/** * Parse a file and get a true type font. * * @param raf The TTF file. * @return A TrueType font. * @ If there is an error parsing the TrueType font. */ public TrueTypeFont Parse(TTFDataStream raf, string fontName = null) { if (string.Equals(raf.ReadString(4), TrueTypeCollection.TAG, StringComparison.Ordinal)) { raf.Seek(raf.CurrentPosition - 4); TrueTypeCollection fontCollection = new TrueTypeCollection(raf); var nameFont = fontCollection.GetFontByName(fontName); if (nameFont == null) { nameFont = fontCollection.GetFontAtIndex(0); } return(nameFont); } raf.Seek(raf.CurrentPosition - 4); TrueTypeFont font = NewFont(raf); font.Version = raf.Read32Fixed(); int numberOfTables = raf.ReadUnsignedShort(); int searchRange = raf.ReadUnsignedShort(); int entrySelector = raf.ReadUnsignedShort(); int rangeShift = raf.ReadUnsignedShort(); for (int i = 0; i < numberOfTables; i++) { TTFTable table = ReadTableDirectory(font, raf); // skip tables with zero length if (table != null) { font.AddTable(table); } } // parse tables if wanted if (!parseOnDemandOnly) { ParseTables(font); } return(font); }
/** * Write the subfont to the given output stream. * * @param os the stream used for writing. It will be closed by this method. * @ if something went wrong. * @throws IllegalStateException if the subset input empty. */ public void WriteToStream(Stream os) { if (glyphIds.Count == 0 && uniToGID.Count == 0) { Debug.WriteLine("info: font subset input empty"); } AddCompoundReferences(); using (var output = new BinaryWriter(os, Charset.ASCII, true)) { long[] newLoca = new long[glyphIds.Count + 1]; // generate tables in dependency order byte[] head = buildHeadTable(); byte[] hhea = buildHheaTable(); byte[] maxp = BuildMaxpTable(); byte[] name = BuildNameTable(); byte[] os2 = BuildOS2Table(); byte[] glyf = BuildGlyfTable(newLoca); byte[] loca = BuildLocaTable(newLoca); byte[] cmap = BuildCmapTable(); byte[] hmtx = BuildHmtxTable(); byte[] post = BuildPostTable(); // save to TTF in optimized order Dictionary <string, byte[]> tables = new Dictionary <string, byte[]>(StringComparer.Ordinal); if (os2 != null) { tables["OS/2"] = os2; } if (cmap != null) { tables["cmap"] = cmap; } tables["glyf"] = glyf; tables["head"] = head; tables["hhea"] = hhea; tables["hmtx"] = hmtx; tables["loca"] = loca; tables["maxp"] = maxp; if (name != null) { tables["name"] = name; } if (post != null) { tables["post"] = post; } // copy all other tables foreach (KeyValuePair <string, TTFTable> entry in ttf.TableMap) { string tag = entry.Key; TTFTable table = entry.Value; if (!tables.ContainsKey(tag) && (keepTables == null || keepTables.Contains(tag, StringComparer.Ordinal))) { tables[tag] = ttf.GetTableBytes(table); } } // calculate checksum long checksum = WriteFileHeader(output, tables.Count); long offset = 12L + 16L * tables.Count; foreach (var entry in tables) { checksum += WriteTableHeader(output, entry.Key, offset, entry.Value); offset += (entry.Value.Length + 3) / 4 * 4; } checksum = 0xB1B0AFBAL - (checksum & 0xffffffffL); // update checksumAdjustment in 'head' table head[8] = (byte)(((uint)checksum) >> 24); head[9] = (byte)(((uint)checksum) >> 16); head[10] = (byte)(((uint)checksum) >> 8); head[11] = (byte)checksum; foreach (byte[] bytes in tables.Values) { WriteTableBody(output, bytes); } } }