// Parses font's offset table and alters OffsetTable object appropriately. public OffsetTable ParseOffsetTable(byte[] fontContent) { OffsetTable offsetTable = new OffsetTable(); uint offset = 0; offsetTable.tag = ReadDataAndIncreaseIndex(fontContent, ref offset, TagLength); offsetTable.numTables = ReadDataAndIncreaseIndex <ushort>(fontContent, ref offset, sizeof(ushort)); offsetTable.searchRange = ReadDataAndIncreaseIndex <ushort>(fontContent, ref offset, sizeof(ushort)); offsetTable.entrySelector = ReadDataAndIncreaseIndex <ushort>(fontContent, ref offset, sizeof(ushort)); offsetTable.rangeShift = ReadDataAndIncreaseIndex <ushort>(fontContent, ref offset, sizeof(ushort)); start = offset; return(offsetTable); }
// Updates OffsetTable object and the offset table in font to reflect a table being added. public OffsetTable UpdateOffsetTable(OffsetTable offsetTable, ref byte[] source) { OffsetTable newOffsetTable = offsetTable; newOffsetTable.numTables += 1; byte[] newNumTables = GetBytesBigEndian(offsetTable.numTables); int offsetTableNumTablesOffset = TagLength; // Advance past the initial tag of the offset table. Array.Copy(newNumTables, 0, source, offsetTableNumTablesOffset, sizeof(ushort)); // Overwrite numTables with numTables + 1. // Calculate searchRange, entrySelector and rangeShift as outlined in the Offset Table section of the OpenType spec. ushort searchRange = 1, entrySelector = 0; while ((searchRange << 1) < offsetTable.numTables) { searchRange <<= 1; entrySelector++; } searchRange <<= 4; // multiply by 16 newOffsetTable.searchRange = searchRange; newOffsetTable.entrySelector = entrySelector; newOffsetTable.rangeShift = (ushort)((offsetTable.numTables * 16) - offsetTable.searchRange); byte[] newSearchRange = GetBytesBigEndian(offsetTable.searchRange); byte[] newEntrySelector = GetBytesBigEndian(offsetTable.entrySelector); byte[] newRangeShift = GetBytesBigEndian(offsetTable.rangeShift); // Write the newly calculated values of searchRange, entrySelector and rangeShift to source. int sourceIndex = sizeof(uint) + sizeof(ushort); Array.Copy(newSearchRange, 0, source, sourceIndex, sizeof(ushort)); sourceIndex += sizeof(ushort); Array.Copy(newEntrySelector, 0, source, sourceIndex, sizeof(ushort)); sourceIndex += sizeof(ushort); Array.Copy(newRangeShift, 0, source, sourceIndex, sizeof(ushort)); return(newOffsetTable); }
public async Task LoadDataAsync(StorageFile file) { parser.Start = 0; parser.Current = 0; FontFileName = file.Name; // Read contents of file and return a buffer. IBuffer buffer = await FileIO.ReadBufferAsync(file); source = new byte[buffer.Length]; // Put buffer byte info into source. using (DataReader reader = DataReader.FromBuffer(buffer)) { reader.ReadBytes(source); } offsetTable = parser.ParseOffsetTable(source); TableRecords = new TableRecord[offsetTable.numTables]; for (int i = 0; i < TableRecords.Length; i++) { TableRecords[i] = parser.ReadTableRecord(source); } // Create a temporary list to contain the updated GlyphModel objects. List <GlyphModel> tempList = new List <GlyphModel>(); foreach (TableRecord record in TableRecords) { if (record.tag == "cmap") { // Only supports cmap tables of format 0 or format 4. foreach (GlyphModel currentGlyph in parser.ParseCMap(record, source)) { // If codepoint matches known whitespace, do not append to preview list. uint codepoint = currentGlyph.CodePoint; if (0x0 <= codepoint && codepoint <= 0x001F || 0x7F <= codepoint && codepoint <= 0xA0 || 0x2000 <= codepoint && codepoint <= 0x200F || codepoint == 0x202F || codepoint == 0x205F || codepoint == 0x3000 || codepoint == 0xFEFF || codepoint == 0x20) { continue; } else { tempList.Add(currentGlyph); } } } else if (record.tag == "name") { FamilyName = parser.GetFamilyName(record, source); // If no FamilyName is found, do not load the font. if (FamilyName == null) { return; } } } // Create local font saved in temp folder. LocalFont = "ms-appdata:///temp/" + FontFileName + "#" + FamilyName; // Assign local font to each GlyphModel. foreach (GlyphModel glyph in tempList) { glyph.FontFamily = LocalFont; } // Repopulate the list of all GlyphModel objects. AllGlyphs.Clear(); foreach (GlyphModel glyph in tempList) { AllGlyphs.Add(glyph); } }
public async Task AddSvgAsync(ushort glyphID, StorageFile svgFile) { // Add SVG data into a buffer. IBuffer buffer = await FileIO.ReadBufferAsync(svgFile); byte[] svgContent = new byte[buffer.Length]; // Read buffer into bytes in svgContent. using (DataReader reader = DataReader.FromBuffer(buffer)) { reader.ReadBytes(svgContent); } // Tweak the SVG: Add glyph ID and adjust the X/Y origin in accordance with OpenType spec. ChangeSvgOriginID(ref svgContent, glyphID); for (int i = 0; i < TableRecords.Length; i++) { if (TableRecords[i].tag == "SVG ") { parser.AssembleSvgContent(ref TableRecords[i], ref source, svgContent, glyphID, this); break; } // Checks if SVG table is not present else if (String.Compare(TableRecords[i].tag, "SVG ", StringComparison.Ordinal) > 0) { // Changes offset table to reflect the addition of a new table offsetTable = parser.UpdateOffsetTable(offsetTable, ref source); // Changes the offsets contained in all of the table records in order to reflect the addition of a table record parser.ChangeTableRecOffsets(TableRecords, ref source, ((sizeof(uint) * 4) * -1), 0); byte[] newSource = new byte[source.Length + (sizeof(uint) * 6) + (sizeof(ushort) * 2)]; // Copies from the beginning of the source array to the end of the table record preceeding the location of the new SVG table record Array.Copy(source, 0, newSource, 0, (int)(TableRecords[i].offsetOfOffset - (sizeof(uint) * 2))); string tag = "SVG "; byte[] svgTag = Encoding.ASCII.GetBytes((string)tag); // Adds SVG tag for the table record Array.Copy(svgTag, 0, newSource, (int)(TableRecords[i].offsetOfOffset - (sizeof(uint) * 2)), sizeof(uint)); byte[] checksum = BitConverter.GetBytes((uint)0); Array.Reverse(checksum); // Adds the checksum for the table record Array.Copy(checksum, 0, newSource, (int)(TableRecords[i].offsetOfOffset - sizeof(uint)), sizeof(uint)); byte[] offset = BitConverter.GetBytes((uint)(source.Length + (sizeof(uint) * 4))); Array.Reverse(offset); // Adds the offset for the table record Array.Copy(offset, 0, newSource, (int)(TableRecords[i].offsetOfOffset), sizeof(uint)); byte[] length = BitConverter.GetBytes((uint)((sizeof(ushort) + sizeof(uint)) * 2)); Array.Reverse(length); // Adds the length for the table record Array.Copy(length, 0, newSource, (int)(TableRecords[i].offsetOfOffset + sizeof(uint)), sizeof(uint)); // Copies from the beginning of the table record following the new SVG table record in the source array to the end of the source array Array.Copy(source, (int)(TableRecords[i].offsetOfOffset - (sizeof(uint) * 2)), newSource, (int)(TableRecords[i].offsetOfOffset + (sizeof(uint) * 2)), (int)(source.Length - (TableRecords[i].offsetOfOffset - (sizeof(uint) * 2)))); byte[] version = BitConverter.GetBytes((ushort)0); Array.Reverse(version); // Adds the SVG table at the very end of the font file // Copies the version number to the newSource array (0) Array.Copy(version, 0, newSource, (int)(source.Length + (sizeof(uint) * 4)), sizeof(ushort)); byte[] svgDocIndexOffset = BitConverter.GetBytes((uint)(sizeof(ushort) + (sizeof(uint) * 2))); Array.Reverse(svgDocIndexOffset); // Copies the SVG Document Index Offset to the newSource array Array.Copy(svgDocIndexOffset, 0, newSource, (int)(source.Length + (sizeof(uint) * 4) + sizeof(ushort)), sizeof(uint)); byte[] reserved = BitConverter.GetBytes((uint)0); Array.Reverse(reserved); // Copies reserved to th enewSource array Array.Copy(reserved, 0, newSource, (int)(source.Length + (sizeof(uint) * 5) + sizeof(ushort)), sizeof(uint)); byte[] numEntries = BitConverter.GetBytes((ushort)0); Array.Reverse(numEntries); // Copies the number of SVG Document Index Entries to the newSource array (in this case just 0 because it is a new table) Array.Copy(numEntries, 0, newSource, (int)(source.Length + (sizeof(uint) * 6) + sizeof(ushort)), sizeof(ushort)); TableRecord svg = new TableRecord(); svg.tag = "SVG "; svg.checksum = 0; svg.offsetOfOffset = TableRecords[i].offsetOfOffset; svg.offset = (uint)(source.Length + (sizeof(uint) * 4)); svg.length = (uint)((sizeof(ushort) + sizeof(uint)) * 2); source = newSource; parser.Start = 12; // Start is 12 because the offset table is always before the table records and is always 12 bytes in length parser.Current = 12; TableRecords = new TableRecord[offsetTable.numTables]; for (int j = 0; j < TableRecords.Length; j++) { TableRecords[j] = parser.ReadTableRecord(source); } parser.AssembleSvgContent(ref svg, ref source, svgContent, glyphID, this); break; } } // Create an updated font file in temp folder. StorageFolder tempFolder = ApplicationData.Current.TemporaryFolder; StorageFile newFontFile = await tempFolder.CreateFileAsync(FontFileName, CreationCollisionOption.GenerateUniqueName); await FileIO.WriteBytesAsync(newFontFile, source); // Hang on to the new font file. temporaryFontFile = newFontFile; }