private void EnsureHmtxTable() { if (hmtx == null) { hmtx = (HorizontalMetricsTable)GetTable(TableNames.Hmtx); } }
internal FakeFontInstance( NameTable nameTable, MaximumProfileTable maxpTable, CMapTable cmap, GlyphTable glyphs, OS2Table os2, HorizontalHeadTable horizontalHeadTable, HorizontalMetricsTable horizontalMetrics, VerticalHeadTable verticalHeadTable, VerticalMetricsTable verticalMetrics, HeadTable head, KerningTable kern) : base( nameTable, maxpTable, cmap, glyphs, os2, horizontalHeadTable, horizontalMetrics, verticalHeadTable, verticalMetrics, head, kern, null, null, null, null, null, null, null, null) { }
internal static FontInstance LoadFont(FontReader reader) { // https://www.microsoft.com/typography/otspec/recom.htm#TableOrdering // recomended order HeadTable head = reader.GetTable <HeadTable>(); // head - not saving but loading in suggested order reader.GetTable <HoizontalHeadTable>(); // hhea reader.GetTable <MaximumProfileTable>(); // maxp OS2Table os2 = reader.GetTable <OS2Table>(); // OS/2 HorizontalMetricsTable horizontalMetrics = reader.GetTable <HorizontalMetricsTable>(); // hmtx // LTSH - Linear threshold data // VDMX - Vertical device metrics // hdmx - Horizontal device metrics CMapTable cmap = reader.GetTable <CMapTable>(); // cmap // fpgm - Font Program // prep - Control Value Program // cvt - Control Value Table reader.GetTable <IndexLocationTable>(); // loca GlyphTable glyphs = reader.GetTable <GlyphTable>(); // glyf KerningTable kern = reader.GetTable <KerningTable>(); // kern - Kerning NameTable nameTable = reader.GetTable <NameTable>(); // name // post - PostScript information // gasp - Grid-fitting/Scan-conversion (optional table) // PCLT - PCL 5 data // DSIG - Digital signature return(new FontInstance(nameTable, cmap, glyphs, os2, horizontalMetrics, head, kern)); }
private static void OptionallyParseTables(IReadOnlyDictionary <string, TrueTypeHeaderTable> tables, TrueTypeDataBytes data, TableRegister.Builder tableRegister) { // cmap if (tables.TryGetValue(TrueTypeHeaderTable.Cmap, out var cmap)) { tableRegister.CMapTable = CMapTable.Load(data, cmap, tableRegister); } // hmtx if (tables.TryGetValue(TrueTypeHeaderTable.Hmtx, out var hmtxHeaderTable)) { tableRegister.HorizontalMetricsTable = HorizontalMetricsTable.Load(data, hmtxHeaderTable, tableRegister); } // name if (tables.TryGetValue(TrueTypeHeaderTable.Name, out var nameHeaderTable)) { // TODO: Not important } // os2 // kern if (tables.TryGetValue(TrueTypeHeaderTable.Kern, out var kernHeaderTable)) { tableRegister.KerningTable = KerningTable.Load(data, kernHeaderTable); } }
/** * Builds vertical metrics with a custom CIDToGIDMap (for embedding font subset). */ private void BuildVerticalMetrics(Dictionary <int, int> cidToGid) { // The "vhea" and "vmtx" tables that specify vertical metrics shall never be used by a conforming // reader. The only way to specify vertical metrics in PDF shall be by means of the DW2 and W2 // entries in a CIDFont dictionary. if (!BuildVerticalHeader(cidFont)) { return; } float scaling = 1000f / ttf.Header.UnitsPerEm; VerticalHeaderTable vhea = ttf.VerticalHeader; VerticalMetricsTable vmtx = ttf.VerticalMetrics; GlyphTable glyf = ttf.Glyph; HorizontalMetricsTable hmtx = ttf.HorizontalMetrics; long v_y = (long)Math.Round(vhea.Ascender * scaling); long w1 = (long)Math.Round(-vhea.AdvanceHeightMax * scaling); PdfArray heights = new PdfArray(); PdfArray w2 = new PdfArray(); int prev = int.MinValue; // Use a sorted list to get an optimal width array ISet <int> keys = new HashSet <int>(cidToGid.Keys); foreach (int cid in keys) { // Unlike buildWidths, we look up with cid (not gid) here because this is // the original TTF, not the rebuilt one. GlyphData glyph = glyf.GetGlyph(cid); if (glyph == null) { continue; } long height = (long)Math.Round((glyph.YMaximum + vmtx.GetTopSideBearing(cid)) * scaling); long advance = (long)Math.Round(-vmtx.GetAdvanceHeight(cid) * scaling); if (height == v_y && advance == w1) { // skip default metrics continue; } // c [w1_1y v_1x v_1y w1_2y v_2x v_2y ... w1_ny v_nx v_ny] if (prev != cid - 1) { w2 = new PdfArray(); heights.Add(PdfInteger.Get(cid)); // c heights.Add(w2); } w2.Add(PdfInteger.Get(advance)); // w1_iy long width = (long)Math.Round(hmtx.GetAdvanceWidth(cid) * scaling); w2.Add(PdfInteger.Get(width / 2)); // v_ix w2.Add(PdfInteger.Get(height)); // v_iy prev = cid; } cidFont[PdfName.W2] = heights; }
internal FakeFontInstance( NameTable nameTable, CMapTable cmap, GlyphTable glyphs, OS2Table os2, HorizontalHeadTable horizontalHeadTable, HorizontalMetricsTable horizontalMetrics, HeadTable head, KerningTable kern) : base(nameTable, cmap, glyphs, os2, horizontalHeadTable, horizontalMetrics, head, kern, null, null) { }
/// <summary> /// Initializes a new instance of the <see cref="FontDescription" /> class. /// </summary> /// <param name="nameTable">The name table.</param> /// <param name="cmap">The cmap.</param> /// <param name="glyphs">The glyphs.</param> /// <param name="os2">The os2.</param> /// <param name="horizontalMetrics">The horizontal metrics.</param> /// <param name="head">The head.</param> /// <param name="kern">The kern.</param> internal FontInstance(NameTable nameTable, CMapTable cmap, GlyphTable glyphs, OS2Table os2, HorizontalMetricsTable horizontalMetrics, HeadTable head, KerningTable kern) { this.cmap = cmap; this.os2 = os2; this.glyphs = glyphs; this.horizontalMetrics = horizontalMetrics; this.head = head; this.glyphCache = new GlyphInstance[this.glyphs.GlyphCount]; // https://www.microsoft.com/typography/otspec/recom.htm#tad this.LineHeight = os2.TypoAscender - os2.TypoDescender + os2.TypoLineGap; this.EmSize = this.head.UnitsPerEm; this.kerning = kern; this.Description = new FontDescription(nameTable, os2, head); }
/// <summary> /// Initializes a new instance of the <see cref="FontInstance"/> class. /// </summary> /// <param name="nameTable">The name table.</param> /// <param name="cmap">The cmap.</param> /// <param name="glyphs">The glyphs.</param> /// <param name="os2">The os2.</param> /// <param name="horizontalHeadTable">The horizontal head table.</param> /// <param name="horizontalMetrics">The horizontal metrics.</param> /// <param name="head">The head.</param> /// <param name="kern">The kern.</param> /// <param name="colrTable">The COLR table</param> /// <param name="cpalTable">The CPAL table</param> internal FontInstance( NameTable nameTable, CMapTable cmap, GlyphTable glyphs, OS2Table os2, HorizontalHeadTable horizontalHeadTable, HorizontalMetricsTable horizontalMetrics, HeadTable head, KerningTable kern, ColrTable?colrTable, CpalTable?cpalTable) { this.cmap = cmap; this.os2 = os2; this.glyphs = glyphs; this.horizontalMetrics = horizontalMetrics; this.head = head; this.glyphCache = new GlyphInstance[this.glyphs.GlyphCount]; if (!(colrTable is null)) { this.colorGlyphCache = new GlyphInstance[this.glyphs.GlyphCount][]; } bool useTypoMetrics = os2.FontStyle.HasFlag(OS2Table.FontStyleSelection.USE_TYPO_METRICS); // https://www.microsoft.com/typography/otspec/recom.htm#tad this.Ascender = useTypoMetrics ? os2.TypoAscender : horizontalHeadTable.Ascender; this.Descender = useTypoMetrics ? os2.TypoDescender : horizontalHeadTable.Descender; this.LineGap = useTypoMetrics ? os2.TypoLineGap : horizontalHeadTable.LineGap; this.LineHeight = this.Ascender - this.Descender + this.LineGap; this.EmSize = this.head.UnitsPerEm; this.kerning = kern; this.colrTable = colrTable; this.cpalTable = cpalTable; this.Description = new FontDescription(nameTable, os2, head); }
/** * Sets the glyph widths in the font dictionary. */ private void SetWidths(PdfDictionary font, GlyphMapping glyphList) { float scaling = 1000f / ttf.Header.UnitsPerEm; HorizontalMetricsTable hmtx = ttf.HorizontalMetrics; Dictionary <int, string> codeToName = FontEncoding.CodeToNameMap; int firstChar = codeToName.Keys.Min(); int lastChar = codeToName.Keys.Max(); List <int> widths = new List <int>(lastChar - firstChar + 1); for (int i = 0; i < lastChar - firstChar + 1; i++) { widths.Add(0); } // a character code is mapped to a glyph name via the provided font encoding // afterwards, the glyph name is translated to a glyph ID. foreach (KeyValuePair <int, string> entry in codeToName) { int code = entry.Key; string name = entry.Value; if (code >= firstChar && code <= lastChar) { var charCode = glyphList.ToUnicode(name) ?? 0; int gid = cmapLookup.GetGlyphId(charCode); widths[entry.Key - firstChar] = (int)Math.Round(hmtx.GetAdvanceWidth(gid) * scaling); } } font[PdfName.FirstChar] = PdfInteger.Get(firstChar); font[PdfName.LastChar] = PdfInteger.Get(lastChar); font[PdfName.Widths] = new PdfArray(widths.Select(p => PdfInteger.Get(p)).ToList()); }
public static Font ReadFromStream(Stream stream) { var font = new Font(); var reader = new BinaryReader(stream); // Offset Table font.OffsetTable = OffsetTable.Read(reader); // Head var headTableRecord = font.OffsetTable.TableRecords.Single(x => x.TableTag == HeadTable.Tag); stream.Seek(headTableRecord.Offset, SeekOrigin.Begin); font.HeadTable = HeadTable.Read(reader); // Horizontal Header var horizontalHeaderTableRecord = font.OffsetTable.TableRecords.Single(x => x.TableTag == HorizontalHeaderTable.Tag); stream.Seek(horizontalHeaderTableRecord.Offset, SeekOrigin.Begin); font.HorizontalHeaderTable = HorizontalHeaderTable.Read(reader); // Maximum Profile var maximumProfileTableRecord = font.OffsetTable.TableRecords.Single(x => x.TableTag == MaximumProfileTableBase.Tag); stream.Seek(maximumProfileTableRecord.Offset, SeekOrigin.Begin); var maximumProfileTableVersion = Math.Round(reader.ReadFixedPointNumber(), 2); if (maximumProfileTableVersion == 0.5m) { font.MaximumProfileTable = MaximumProfileTableV0_5.Read(reader, maximumProfileTableVersion); } else if (maximumProfileTableVersion == 1.0m) { font.MaximumProfileTable = MaximumProfileTableV1.Read(reader, maximumProfileTableVersion); } // Horizontal Metrics var horizontalMetricsTableRecord = font.OffsetTable.TableRecords.Single(x => x.TableTag == HorizontalMetricsTable.Tag); stream.Seek(horizontalMetricsTableRecord.Offset, SeekOrigin.Begin); font.HorizontalMetricsTable = HorizontalMetricsTable.Read(reader, font.HorizontalHeaderTable.NumberOfHMetrics, font.MaximumProfileTable.NumGlyphs); // Glyph var glyphTableRecord = font.OffsetTable.TableRecords.Single(x => x.TableTag == GlyphTable.Tag); font.GlyphTable = new GlyphTable(reader, glyphTableRecord.Offset); // IndexLocation var indexLocationTableRecord = font.OffsetTable.TableRecords.Single(x => x.TableTag == IndexLocationTableBase.Tag); stream.Seek(indexLocationTableRecord.Offset, SeekOrigin.Begin); if (font.HeadTable.IndexToLocFormat == 0) { font.IndexLocationTable = IndexLocationShortFormatTable.Read(reader, font.MaximumProfileTable.NumGlyphs); } else { font.IndexLocationTable = IndexLocationLongFormatTable.Read(reader, font.MaximumProfileTable.NumGlyphs); } // PostScript var postScriptTableRecord = font.OffsetTable.TableRecords.Single(x => x.TableTag == PostScriptTable.Tag); stream.Seek(postScriptTableRecord.Offset, SeekOrigin.Begin); font.PostScriptTable = PostScriptTable.Read(reader); // OS/2 and Windows var os2TableRecord = font.OffsetTable.TableRecords.Single(x => x.TableTag == Os2Table.Tag); stream.Seek(os2TableRecord.Offset, SeekOrigin.Begin); font.Os2Table = Os2Table.Read(reader); // CharacterMap var characterMapTableRecord = font.OffsetTable.TableRecords.Single(x => x.TableTag == CharacterMapTable.Tag); stream.Seek(characterMapTableRecord.Offset, SeekOrigin.Begin); font.CharacterMapTable = CharacterMapTable.Read(reader); return(font); }
/// <summary> /// Initializes a new instance of the <see cref="StreamFontMetrics"/> class. /// </summary> /// <param name="nameTable">The name table.</param> /// <param name="maximumProfileTable">The maximum profile table.</param> /// <param name="cmap">The cmap table.</param> /// <param name="glyphs">The glyph table.</param> /// <param name="os2">The os2 table.</param> /// <param name="horizontalHeadTable">The horizontal head table.</param> /// <param name="horizontalMetrics">The horizontal metrics table.</param> /// <param name="verticalHeadTable">The vertical head table.</param> /// <param name="verticalMetrics">The vertical metrics table.</param> /// <param name="head">The head table.</param> /// <param name="kern">The kerning table.</param> /// <param name="gSubTable">The glyph substitution table.</param> /// <param name="gPosTable">The glyph positioning table.</param> /// <param name="colrTable">The COLR table</param> /// <param name="cpalTable">The CPAL table</param> /// <param name="fpgm">The font program table.</param> /// <param name="cvt">The control value table.</param> /// <param name="prep">The control value program table.</param> /// <param name="glyphDefinitionTable">The glyph definition table.</param> internal StreamFontMetrics( NameTable nameTable, MaximumProfileTable maximumProfileTable, CMapTable cmap, GlyphTable glyphs, OS2Table os2, HorizontalHeadTable horizontalHeadTable, HorizontalMetricsTable horizontalMetrics, VerticalHeadTable?verticalHeadTable, VerticalMetricsTable?verticalMetrics, HeadTable head, KerningTable kern, GSubTable?gSubTable, GPosTable?gPosTable, ColrTable?colrTable, CpalTable?cpalTable, FpgmTable?fpgm, CvtTable?cvt, PrepTable?prep, GlyphDefinitionTable?glyphDefinitionTable) { this.maximumProfileTable = maximumProfileTable; this.cmap = cmap; this.os2 = os2; this.glyphs = glyphs; this.horizontalMetrics = horizontalMetrics; this.verticalMetricsTable = verticalMetrics; this.head = head; this.glyphCache = new GlyphMetrics[this.glyphs.GlyphCount][]; if (colrTable is not null) { this.colorGlyphCache = new GlyphMetrics[this.glyphs.GlyphCount][]; } // https://www.microsoft.com/typography/otspec/recom.htm#tad // We use the same approach as FreeType for calculating the the global ascender, descender, and // height of OpenType fonts for consistency. // // 1.If the OS/ 2 table exists and the fsSelection bit 7 is set (USE_TYPO_METRICS), trust the font // and use the Typo* metrics. // 2.Otherwise, use the HorizontalHeadTable "hhea" table's metrics. // 3.If they are zero and the OS/ 2 table exists, // - Use the OS/ 2 table's sTypo* metrics if they are non-zero. // - Otherwise, use the OS / 2 table's usWin* metrics. bool useTypoMetrics = os2.FontStyle.HasFlag(OS2Table.FontStyleSelection.USE_TYPO_METRICS); if (useTypoMetrics) { this.Ascender = os2.TypoAscender; this.Descender = os2.TypoDescender; this.LineGap = os2.TypoLineGap; this.LineHeight = (short)(this.Ascender - this.Descender + this.LineGap); } else { this.Ascender = horizontalHeadTable.Ascender; this.Descender = horizontalHeadTable.Descender; this.LineGap = horizontalHeadTable.LineGap; this.LineHeight = (short)(this.Ascender - this.Descender + this.LineGap); } if (this.Ascender == 0 || this.Descender == 0) { if (os2.TypoAscender != 0 || os2.TypoDescender != 0) { this.Ascender = os2.TypoAscender; this.Descender = os2.TypoDescender; this.LineGap = os2.TypoLineGap; this.LineHeight = (short)(this.Ascender - this.Descender + this.LineGap); } else { this.Ascender = (short)os2.WinAscent; this.Descender = (short)-os2.WinDescent; this.LineHeight = (short)(this.Ascender - this.Descender); } } this.UnitsPerEm = this.head.UnitsPerEm; // 72 * UnitsPerEm means 1pt = 1px this.ScaleFactor = this.UnitsPerEm * 72F; this.AdvanceWidthMax = (short)horizontalHeadTable.AdvanceWidthMax; this.AdvanceHeightMax = verticalHeadTable == null ? this.LineHeight : verticalHeadTable.AdvanceHeightMax; this.kerningTable = kern; this.gSubTable = gSubTable; this.gPosTable = gPosTable; this.colrTable = colrTable; this.cpalTable = cpalTable; this.fpgm = fpgm; this.cvt = cvt; this.prep = prep; this.glyphDefinitionTable = glyphDefinitionTable; this.Description = new FontDescription(nameTable, os2, head); }