private byte[] BuildHmtxTable() { MemoryStream bos = new MemoryStream(); HorizontalHeaderTable h = ttf.HorizontalHeader; HorizontalMetricsTable hm = ttf.HorizontalMetrics; Bytes.Buffer input = ttf.OriginalData; // more info: https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6hmtx.html int lastgid = h.NumberOfHMetrics - 1; // true if lastgid input not in the set: we'll need its width (but not its left side bearing) later bool needLastGidWidth = false; if (glyphIds.LastOrDefault() > lastgid && !glyphIds.Contains(lastgid)) { needLastGidWidth = true; } try { long isResult = input.Skip(hm.Offset); if (isResult.CompareTo(hm.Offset) != 0) { Debug.WriteLine($"debug: Tried skipping {hm.Offset} bytes but only {isResult} bytes skipped"); } long lastOffset = 0; foreach (int glyphId in glyphIds) { // offset in original file long offset; if (glyphId <= lastgid) { // copy width and lsb offset = glyphId * 4L; lastOffset = CopyBytes(input, bos, offset, lastOffset, 4); } else { if (needLastGidWidth) { // one time only: copy width from lastgid, whose width applies // to all later glyphs needLastGidWidth = false; offset = lastgid * 4L; lastOffset = CopyBytes(input, bos, offset, lastOffset, 2); // then go on with lsb from actual glyph (lsb are individual even in monotype fonts) } // copy lsb only, as we are beyond numOfHMetrics offset = h.NumberOfHMetrics * 4L + (glyphId - h.NumberOfHMetrics) * 2L; lastOffset = CopyBytes(input, bos, offset, lastOffset, 2); } } return(bos.ToArray()); } finally { input.Dispose(); } }
/** * 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); }