/** * Constructor. * * @param bais the stream to be read * @param glyphTable the Glyphtable containing all glyphs * @ is thrown if something went wrong */ public GlyfCompositeDescript(TTFDataStream bais, GlyphTable glyphTable) : base((short)-1, bais) { this.glyphTable = glyphTable; // Get all of the composite components GlyfCompositeComp comp; do { comp = new GlyfCompositeComp(bais); components.Add(comp); }while ((comp.Flags & GlyfCompositeComp.MORE_COMPONENTS) != 0); // Are there hinting instructions to read? if ((comp.Flags & GlyfCompositeComp.WE_HAVE_INSTRUCTIONS) != 0) { ReadInstructions(bais, (bais.ReadUnsignedShort())); } InitDescriptions(); }
/** * This will read the required data from the stream. * * @param glyphTable The glyph table this glyph belongs to. * @param data The stream to read the data from. * @param leftSideBearing The left side bearing for this glyph. * @ If there is an error reading the data. */ public void InitData(GlyphTable glyphTable, TTFDataStream data, int leftSideBearing) { numberOfContours = data.ReadSignedShort(); xMin = data.ReadSignedShort(); yMin = data.ReadSignedShort(); xMax = data.ReadSignedShort(); yMax = data.ReadSignedShort(); boundingBox = new SKRect(xMin, yMin, xMax, yMax); if (numberOfContours >= 0) { // create a simple glyph short x0 = (short)(leftSideBearing - xMin); glyphDescription = new GlyfSimpleDescript(numberOfContours, data, x0); } else { // create a composite glyph glyphDescription = new GlyfCompositeDescript(data, glyphTable); } }
// never returns null private byte[] BuildGlyfTable(long[] newOffsets) { GlyphTable g = ttf.Glyph; long[] offsets = ttf.IndexToLocation.Offsets; MemoryStream bos = new MemoryStream(); using (Bytes.Buffer input = ttf.OriginalData) { long isResult = input.Skip(g.Offset); if (isResult.CompareTo(g.Offset) != 0) { Debug.WriteLine($"debug: Tried skipping {g.Offset} bytes but skipped only {isResult} bytes"); } long prevEnd = 0; // previously read glyph offset long newOffset = 0; // new offset for the glyph in the subset font int newGid = 0; // new GID in subset font // for each glyph in the subset foreach (int gid in glyphIds) { long offset = offsets[gid]; long length = offsets[gid + 1] - offset; newOffsets[newGid++] = newOffset; isResult = input.Skip(offset - prevEnd); if (isResult.CompareTo(offset - prevEnd) != 0) { Debug.WriteLine($"debug: Tried skipping {(offset - prevEnd)} bytes but skipped only {isResult} bytes"); } sbyte[] buf = new sbyte[(int)length]; isResult = input.Read(buf); if (isResult.CompareTo(length) != 0) { Debug.WriteLine($"debug: Tried reading {length} bytes but only {isResult} bytes read"); } // detect glyph type if (buf.Length >= 2 && buf[0] == -1 && buf[1] == -1) { // compound glyph int off = 2 * 5; int flags; do { // flags flags = (buf[off] & 0xff) << 8 | buf[off + 1] & 0xff; off += 2; // glyphIndex int componentGid = (buf[off] & 0xff) << 8 | buf[off + 1] & 0xff; if (!glyphIds.Contains(componentGid)) { glyphIds.Add(componentGid); } int newComponentGid = GetNewGlyphId(componentGid); buf[off] = (sbyte)((uint)(newComponentGid) >> 8); buf[off + 1] = (sbyte)newComponentGid; off += 2; // ARG_1_AND_2_ARE_WORDS if ((flags & 1 << 0) != 0) { off += 2 * 2; } else { off += 2; } // WE_HAVE_A_TWO_BY_TWO if ((flags & 1 << 7) != 0) { off += 2 * 4; } // WE_HAVE_AN_X_AND_Y_SCALE else if ((flags & 1 << 6) != 0) { off += 2 * 2; } // WE_HAVE_A_SCALE else if ((flags & 1 << 3) != 0) { off += 2; } }while ((flags & 1 << 5) != 0); // MORE_COMPONENTS // WE_HAVE_INSTRUCTIONS if ((flags & 0x0100) == 0x0100) { // USHORT numInstr int numInstr = (buf[off] & 0xff) << 8 | buf[off + 1] & 0xff; off += 2; // BYTE instr[numInstr] off += numInstr; } // write the compound glyph bos.Write((byte[])(Array)buf, 0, off); // offset to start next glyph newOffset += off; } else if (buf.Length > 0) { // copy the entire glyph bos.Write((byte[])(Array)buf, 0, buf.Length); // offset to start next glyph newOffset += buf.Length; } // 4-byte alignment if (newOffset % 4 != 0) { int len = 4 - (int)(newOffset % 4); bos.Write(PAD_BUF, 0, len); newOffset += len; } prevEnd = offset + length; } newOffsets[newGid++] = newOffset; } return(bos.ToArray()); }
/** * Resolve compound glyph references. */ private void AddCompoundReferences() { if (hasAddedCompoundReferences) { return; } hasAddedCompoundReferences = true; bool hasNested; do { GlyphTable g = ttf.Glyph; long[] offsets = ttf.IndexToLocation.Offsets; Bytes.Buffer input = ttf.OriginalData; ISet <int> glyphIdsToAdd = null; try { long isResult = input.Skip(g.Offset); if (isResult.CompareTo(g.Offset) != 0) { Debug.WriteLine($"debug: Tried skipping {g.Offset} bytes but skipped only {isResult} bytes"); } long lastOff = 0L; foreach (int glyphId in glyphIds) { long offset = offsets[glyphId]; long len = offsets[glyphId + 1] - offset; isResult = input.Skip(offset - lastOff); if (isResult.CompareTo(offset - lastOff) != 0) { Debug.WriteLine($"debug: Tried skipping {(offset - lastOff)} bytes but skipped only {isResult} bytes"); } sbyte[] buf = new sbyte[(int)len]; isResult = input.Read(buf); if (isResult.CompareTo(len) != 0) { Debug.WriteLine($"debug: Tried reading {len} bytes but only {isResult} bytes read"); } // rewrite glyphIds for compound glyphs if (buf.Length >= 2 && buf[0] == -1 && buf[1] == -1) { int off = 2 * 5; int flags; do { flags = (buf[off] & 0xff) << 8 | buf[off + 1] & 0xff; off += 2; int ogid = (buf[off] & 0xff) << 8 | buf[off + 1] & 0xff; if (!glyphIds.Contains(ogid)) { if (glyphIdsToAdd == null) { glyphIdsToAdd = new HashSet <int>(); } glyphIdsToAdd.Add(ogid); } off += 2; // ARG_1_AND_2_ARE_WORDS if ((flags & 1 << 0) != 0) { off += 2 * 2; } else { off += 2; } // WE_HAVE_A_TWO_BY_TWO if ((flags & 1 << 7) != 0) { off += 2 * 4; } // WE_HAVE_AN_X_AND_Y_SCALE else if ((flags & 1 << 6) != 0) { off += 2 * 2; } // WE_HAVE_A_SCALE else if ((flags & 1 << 3) != 0) { off += 2; } }while ((flags & 1 << 5) != 0); // MORE_COMPONENTS } lastOff = offsets[glyphId + 1]; } } finally { input.Dispose(); } if (glyphIdsToAdd != null) { glyphIds.AddAll(glyphIdsToAdd); } hasNested = glyphIdsToAdd != null; }while (hasNested); }
private TTFTable ReadTableDirectory(TrueTypeFont font, TTFDataStream raf) { TTFTable table; string tag = raf.ReadString(4); switch (tag) { case CmapTable.TAG: table = new CmapTable(font); break; case GlyphTable.TAG: table = new GlyphTable(font); break; case HeaderTable.TAG: table = new HeaderTable(font); break; case HorizontalHeaderTable.TAG: table = new HorizontalHeaderTable(font); break; case HorizontalMetricsTable.TAG: table = new HorizontalMetricsTable(font); break; case IndexToLocationTable.TAG: table = new IndexToLocationTable(font); break; case MaximumProfileTable.TAG: table = new MaximumProfileTable(font); break; case NamingTable.TAG: table = new NamingTable(font); break; case OS2WindowsMetricsTable.TAG: table = new OS2WindowsMetricsTable(font); break; case PostScriptTable.TAG: table = new PostScriptTable(font); break; case DigitalSignatureTable.TAG: table = new DigitalSignatureTable(font); break; case KerningTable.TAG: table = new KerningTable(font); break; case VerticalHeaderTable.TAG: table = new VerticalHeaderTable(font); break; case VerticalMetricsTable.TAG: table = new VerticalMetricsTable(font); break; case VerticalOriginTable.TAG: table = new VerticalOriginTable(font); break; case GlyphSubstitutionTable.TAG: table = new GlyphSubstitutionTable(font); break; default: table = ReadTable(font, tag); break; } table.Tag = tag; table.CheckSum = raf.ReadUnsignedInt(); table.Offset = raf.ReadUnsignedInt(); table.Length = raf.ReadUnsignedInt(); // skip tables with zero length (except glyf) if (table.Length == 0 && !tag.Equals(GlyphTable.TAG, StringComparison.Ordinal)) { return(null); } return(table); }