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)
 {
 }
Exemple #3
0
        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));
        }
Exemple #4
0
        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;
        }
Exemple #6
0
 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)
 {
 }
Exemple #7
0
        /// <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);
        }
Exemple #8
0
        /// <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());
        }
Exemple #10
0
        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);
        }