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)); }
public void LoadSingleGlyphWithInt8Offset_signed_byte() { var writer = new BigEndianBinaryWriter(); writer.WriteUInt16((ushort)CompositeFlags.ArgsAreXYValues); // signed byte writer.WriteUInt16(1); // glyph id writer.WriteInt8(sbyte.MinValue + 1); // dx writer.WriteInt8(sbyte.MinValue + 2); // dy writer.GetReader(); var bounds = new Bounds(0, 0, 100, 100); var glyph = CompositeGlyphLoader.LoadCompositeGlyph(writer.GetReader(), in bounds); var tbl = new GlyphTable(new[] { new SimpleGlyphLoader(bounds), // padding new SimpleGlyphLoader(new short[] { 20 }, new short[] { 21 }, new[] { true }, new ushort[] { 1 }, bounds) }); GlyphVector finalGlyph = glyph.CreateGlyph(tbl); Vector2 point = Assert.Single(finalGlyph.ControlPoints); Assert.Equal(new Vector2(sbyte.MinValue + 1 + 20, sbyte.MinValue + 2 + 21), point); }
public override Glyphs.GlyphVector CreateGlyph(GlyphTable table) { List <Vector2> controlPoints = new List <Vector2>(); List <bool> onCurves = new List <bool>(); List <ushort> endPoints = new List <ushort>(); List <Vector2> minBounds = new List <Vector2>(); List <Vector2> maxBounds = new List <Vector2>(); List <GlyphInstance> parts = new List <GlyphInstance>(); foreach (Composite composite in this.result) { GlyphVector glyph = table.GetGlyph(composite.GlyphIndex); int pointcount = glyph.PointCount; ushort endPointOffset = (ushort)controlPoints.Count; for (int i = 0; i < pointcount; i++) { controlPoints.Add(Vector2.Transform(glyph.ControlPoints[i], composite.Transformation)); onCurves.Add(glyph.OnCurves[i]); } foreach (ushort p in glyph.EndPoints) { endPoints.Add((ushort)(p + endPointOffset)); } } return(new Glyphs.GlyphVector(controlPoints.ToArray(), onCurves.ToArray(), endPoints.ToArray(), this.bounds)); }
public override GlyphVector CreateGlyph(GlyphTable table) { var controlPoints = new List <Vector2>(); var onCurves = new List <bool>(); var endPoints = new List <ushort>(); var minBounds = new List <Vector2>(); var maxBounds = new List <Vector2>(); var parts = new List <GlyphInstance>(); for (int resultIndex = 0; resultIndex < this.result.Length; resultIndex++) { ref Composite composite = ref this.result[resultIndex]; GlyphVector glyph = table.GetGlyph(composite.GlyphIndex); int pointcount = glyph.PointCount; ushort endPointOffset = (ushort)controlPoints.Count; for (int i = 0; i < pointcount; i++) { controlPoints.Add(Vector2.Transform(glyph.ControlPoints[i], composite.Transformation)); onCurves.Add(glyph.OnCurves[i]); } foreach (ushort p in glyph.EndPoints) { endPoints.Add((ushort)(p + endPointOffset)); } }
/// <summary> /// Fixes the shape of letters based on their position. /// </summary> /// <param name="input"></param> /// <param name="output"></param> /// <param name="preserveNumbers"></param> /// <param name="farsi"></param> /// <returns></returns> public static void Fix(FastStringBuilder input, FastStringBuilder output, bool preserveNumbers, bool farsi) { FixYah(input, farsi); output.SetValue(input); for (int i = 0; i < input.Length; i++) { bool skipNext = false; char iChar = input.Get(i); // For special Lam Letter connections. if (iChar == (char)GeneralLetters.Lam) { if (i < input.Length - 1) { skipNext = HandleSpecialLam(input, output, i); if (skipNext) iChar = output.Get(i); } } // We don't want to fix tatweel or zwnj character if (iChar == (int)GeneralLetters.ArabicTatweel || iChar == (int)GeneralLetters.ZeroWidthNoJoiner) { continue; } if (TextUtils.IsRTLCharacter(iChar)) { char converted = GlyphTable.Convert(iChar); if (IsMiddleLetter(input, i)) { output.Set(i, (char)(converted + 3)); } else if (IsFinishingLetter(input, i)) { output.Set(i, (char)(converted + 1)); } else if (IsLeadingLetter(input, i)) { output.Set(i, (char)(converted + 2)); } } // If this letter as Lam and special Lam-Alef connection was made, We want to skip the Alef // (Lam-Alef occupies 1 space) if (skipNext) { i++; } } if (!preserveNumbers) { FixNumbers(output, farsi); } }
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) { }
public void GetGlyphTable() { if (glyphTable == null) { glyphTable = FindObjectOfType <GlyphTable>(); } }
public void LoadSingleGlyphWithUInt8Offset_unsigned_byte() { var writer = new BigEndianBinaryWriter(); writer.WriteUInt16(0); // 8bit unsigned writer.WriteUInt16(1); // glyph id writer.WriteUInt8(sbyte.MaxValue + 1); // dx writer.WriteUInt8(sbyte.MaxValue + 2); // dy writer.GetReader(); var bounds = new Bounds(0, 0, 100, 100); var glyph = CompositeGlyphLoader.LoadCompositeGlyph(writer.GetReader(), in bounds); var tbl = new GlyphTable(new[] { new SimpleGlyphLoader(bounds), // padding new SimpleGlyphLoader(new short[] { 20 }, new short[] { 21 }, new[] { true }, new ushort[] { 1 }, bounds, Array.Empty <byte>()) }); GlyphVector finalGlyph = glyph.CreateGlyph(tbl); Vector2 point = Assert.Single(finalGlyph.GetOutline().ControlPoints.ToArray()); Assert.Equal(new Vector2(sbyte.MaxValue + 1 + 20, sbyte.MaxValue + 2 + 21), point); }
public void LoadSingleGlyphWithInt16Offset_signed_short() { var writer = new BigEndianBinaryWriter(); writer.WriteUInt16((ushort)(CompositeGlyphFlags.Args1And2AreWords /* 16bit */ | CompositeGlyphFlags.ArgsAreXYValues /* signed */)); // flags writer.WriteUInt16(1); // glyph id writer.WriteInt16(short.MinValue + 1); // dx writer.WriteInt16(short.MinValue + 2); // dy writer.GetReader(); var bounds = new Bounds(0, 0, 100, 100); var glyph = CompositeGlyphLoader.LoadCompositeGlyph(writer.GetReader(), in bounds); var tbl = new GlyphTable(new[] { new SimpleGlyphLoader(bounds), // padding new SimpleGlyphLoader(new short[] { 20 }, new short[] { 21 }, new[] { true }, new ushort[] { 1 }, bounds, Array.Empty <byte>()) }); GlyphVector finalGlyph = glyph.CreateGlyph(tbl); Vector2 point = Assert.Single(finalGlyph.GetOutline().ControlPoints.ToArray()); Assert.Equal(new Vector2(short.MinValue + 1 + 20, short.MinValue + 2 + 21), point); }
public void Get(BinaryWriter writer) { Header.Get(writer); Dictionary.Get(writer); GlyphTable.Get(writer); writer.Write(CompressedData); }
/** * 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; }
public void GlyphsCount_WithWoffFormat_EqualsTtf() { var fontReaderWoff = new FontReader(TestFonts.OpensSansWoff1Data()); GlyphTable glyphsWoff = fontReaderWoff.GetTable <GlyphTable>(); var fontReaderTtf = new FontReader(TestFonts.OpenSansTtfData()); GlyphTable glyphsTtf = fontReaderTtf.GetTable <GlyphTable>(); Assert.Equal(glyphsTtf.GlyphCount, glyphsWoff.GlyphCount); }
public override GlyphVector CreateGlyph(GlyphTable table) { GlyphVector glyph = default; for (int resultIndex = 0; resultIndex < this.result.Length; resultIndex++) { ref Composite composite = ref this.result[resultIndex]; glyph = GlyphVector.Append(glyph, GlyphVector.Transform(GlyphVector.DeepClone(table.GetGlyph(composite.GlyphIndex)), composite.Transformation), this.bounds); }
public override Glyphs.GlyphVector CreateGlyph(GlyphTable table) { this.counter++; if (this.counter > 100) { throw new Fonts.Exceptions.FontException("loop detected loading glyphs"); } return(table.GetGlyph(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) { }
public override Glyphs.GlyphVector CreateGlyph(GlyphTable table) { if (this.loop) { if (this.glyph == null) { this.glyph = new GlyphVector(new Vector2[0], new bool[0], new ushort[0], this.fallbackEmptyBounds); } return(this.glyph.Value); } this.loop = true; return(table.GetGlyph(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); }
private void ProcessTables() { // TODO: These may need to be Processed() in a certain order, for example the horizontal metrics table // 'needs' to know the number of metrics from the horizontal header table. // However, it looks like the tables are listed in the proper order, so we get that for free. But that seems awfully fragile.... foreach (var table in Tables) { // Call each table's process method to parse data table.Process(this); switch (table.Type) { case FontTableType.CharacterToGlyphMap: cmap = (CharacterToGlyphTable)table; break; case FontTableType.FontHeader: head = (HeaderTable)table; break; case FontTableType.HorizontalHeader: hhea = (HorizontalHeaderTable)table; break; case FontTableType.HorizontalMetrics: hmtx = (HorizontalMetricsTable)table; break; case FontTableType.Kerning: kern = (KerningTable)table; break; case FontTableType.MaximumProfile: maxp = (MaximumProfileTable)table; break; case FontTableType.IndexToLocation: loca = (IndexToLocationTable)table; break; case FontTableType.GlyphData: glyf = (GlyphTable)table; break; } } }
/// <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); }
public override GlyphVector CreateGlyph(GlyphTable table) { // lets build some shapes ??? here from return(new GlyphVector(Convert(this.xs, this.ys), this.onCurves, this.endPoints, this.bounds)); }
/// <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); }
public abstract GlyphVector CreateGlyph(GlyphTable table);
public override GlyphVector CreateGlyph(GlyphTable table) => new(Convert(this.xs, this.ys), this.onCurves, this.endPoints, this.bounds, this.instructions);