public static void ReadPost(DataReader reader, TableRecord[] tables, ref FaceHeader header) { if (!SeekToTable(reader, tables, FourCC.Post)) { return; } // skip over version and italicAngle reader.Skip(sizeof(int) * 2); header.UnderlinePosition = reader.ReadInt16BE(); header.UnderlineThickness = reader.ReadInt16BE(); header.IsFixedPitch = reader.ReadUInt32BE() != 0; }
public static MetricsHeader ReadMetricsHeader(DataReader reader) { // skip over version reader.Skip(sizeof(int)); var header = new MetricsHeader { Ascender = reader.ReadInt16BE(), Descender = reader.ReadInt16BE(), LineGap = reader.ReadInt16BE() }; // skip over advanceWidthMax, minLsb, minRsb, xMaxExtent, caretSlopeRise, // caretSlopeRun, caretOffset, 4 reserved entries, and metricDataFormat reader.Skip(sizeof(short) * 12); header.MetricCount = reader.ReadUInt16BE(); return(header); }
public static OS2Data ReadOS2(DataReader reader, TableRecord[] tables) { SeekToTable(reader, tables, FourCC.OS_2, required: true); // skip over version, xAvgCharWidth reader.Skip(sizeof(short) * 2); var result = new OS2Data { Weight = (FontWeight)reader.ReadUInt16BE(), Stretch = (FontStretch)reader.ReadUInt16BE() }; // skip over fsType, ySubscriptXSize, ySubscriptYSize, ySubscriptXOffset, ySubscriptYOffset, // ySuperscriptXSize, ySuperscriptYSize, ySuperscriptXOffset, ySuperscriptXOffset reader.Skip(sizeof(short) * 9); result.StrikeoutSize = reader.ReadInt16BE(); result.StrikeoutPosition = reader.ReadInt16BE(); // skip over sFamilyClass, panose[10], ulUnicodeRange1-4, achVendID[4] reader.Skip(sizeof(short) + sizeof(int) * 4 + 14); // check various style flags var fsSelection = (FsSelectionFlags)reader.ReadUInt16BE(); result.Style = (fsSelection & FsSelectionFlags.Italic) != 0 ? FontStyle.Italic : (fsSelection & FsSelectionFlags.Bold) != 0 ? FontStyle.Bold : (fsSelection & FsSelectionFlags.Oblique) != 0 ? FontStyle.Oblique : FontStyle.Regular; result.IsWWSFont = (fsSelection & FsSelectionFlags.WWS) != 0; result.UseTypographicMetrics = (fsSelection & FsSelectionFlags.UseTypoMetrics) != 0; // skip over usFirstCharIndex, usLastCharIndex reader.Skip(sizeof(short) * 2); result.TypographicAscender = reader.ReadInt16BE(); result.TypographicDescender = reader.ReadInt16BE(); result.TypographicLineGap = reader.ReadInt16BE(); result.WinAscent = reader.ReadUInt16BE(); result.WinDescent = reader.ReadUInt16BE(); // skip over ulCodePageRange1-2 reader.Skip(sizeof(int) * 2); result.XHeight = reader.ReadInt16BE(); result.CapHeight = reader.ReadInt16BE(); return(result); }
public static FUnit[] ReadCvt(DataReader reader, TableRecord[] tables) { var index = FindTable(tables, FourCC.Cvt); if (index == -1) { return(null); } reader.Seek(tables[index].Offset); var results = new FUnit[tables[index].Length / sizeof(short)]; for (int i = 0; i < results.Length; i++) { results[i] = (FUnit)reader.ReadInt16BE(); } return(results); }
public static void ReadHead(DataReader reader, TableRecord[] tables, out FaceHeader header) { SeekToTable(reader, tables, FourCC.Head, required: true); // 'head' table contains global information for the font face // we only care about a few fields in it reader.Skip(sizeof(int) * 4); // version, revision, checksum, magic number header = new FaceHeader { Flags = (HeadFlags)reader.ReadUInt16BE(), UnitsPerEm = reader.ReadUInt16BE() }; if (header.UnitsPerEm == 0) throw new InvalidFontException("Invalid 'head' table."); // skip over created and modified times, bounding box, // deprecated style bits, direction hints, and size hints reader.Skip(sizeof(long) * 2 + sizeof(short) * 7); header.IndexFormat = (IndexFormat)reader.ReadInt16BE(); }
public static void ReadHead(DataReader reader, TableRecord[] tables, out FaceHeader header) { SeekToTable(reader, tables, FourCC.Head, required: true); // 'head' table contains global information for the font face // we only care about a few fields in it reader.Skip(sizeof(int) * 4); // version, revision, checksum, magic number header = new FaceHeader { Flags = (HeadFlags)reader.ReadUInt16BE(), UnitsPerEm = reader.ReadUInt16BE() }; if (header.UnitsPerEm == 0) { throw new InvalidFontException("Invalid 'head' table."); } // skip over created and modified times, bounding box, // deprecated style bits, direction hints, and size hints reader.Skip(sizeof(long) * 2 + sizeof(short) * 7); header.IndexFormat = (IndexFormat)reader.ReadInt16BE(); }
static CompositeGlyph ReadCompositeGlyph(DataReader reader) { // we need to keep reading glyphs for as long as // our flags tell us that there are more to read var subglyphs = new List <Subglyph>(); CompositeGlyphFlags flags; do { flags = (CompositeGlyphFlags)reader.ReadUInt16BE(); var subglyph = new Subglyph { Flags = flags }; subglyph.Index = reader.ReadUInt16BE(); // read in args; they vary in size based on flags if ((flags & CompositeGlyphFlags.ArgsAreWords) != 0) { subglyph.Arg1 = reader.ReadInt16BE(); subglyph.Arg2 = reader.ReadInt16BE(); } else { subglyph.Arg1 = reader.ReadSByte(); subglyph.Arg2 = reader.ReadSByte(); } // figure out the transform; we can either have no scale, a uniform // scale, two independent scales, or a full 2x2 transform matrix // transform components are in 2.14 fixed point format var transform = Matrix3x2.Identity; if ((flags & CompositeGlyphFlags.HaveScale) != 0) { var scale = reader.ReadInt16BE() / F2Dot14ToFloat; transform.M11 = scale; transform.M22 = scale; } else if ((flags & CompositeGlyphFlags.HaveXYScale) != 0) { transform.M11 = reader.ReadInt16BE() / F2Dot14ToFloat; transform.M22 = reader.ReadInt16BE() / F2Dot14ToFloat; } else if ((flags & CompositeGlyphFlags.HaveTransform) != 0) { transform.M11 = reader.ReadInt16BE() / F2Dot14ToFloat; transform.M12 = reader.ReadInt16BE() / F2Dot14ToFloat; transform.M21 = reader.ReadInt16BE() / F2Dot14ToFloat; transform.M22 = reader.ReadInt16BE() / F2Dot14ToFloat; } subglyph.Transform = transform; subglyphs.Add(subglyph); } while ((flags & CompositeGlyphFlags.MoreComponents) != 0); var result = new CompositeGlyph { Subglyphs = subglyphs.ToArray() }; // if we have instructions, read them now if ((flags & CompositeGlyphFlags.HaveInstructions) != 0) { var instructionLength = reader.ReadUInt16BE(); result.Instructions = reader.ReadBytes(instructionLength); } return(result); }
public static KerningTable ReadKern(DataReader reader, TableRecord[] tables) { // kern table is optional if (!SfntTables.SeekToTable(reader, tables, FourCC.Kern)) { return(null); } // skip version reader.Skip(sizeof(short)); // read each subtable and accumulate kerning values var tableData = new Dictionary <uint, int>(); var subtableCount = reader.ReadUInt16BE(); for (int i = 0; i < subtableCount; i++) { // skip version var currentOffset = reader.Position; reader.Skip(sizeof(short)); var length = reader.ReadUInt16BE(); var coverage = reader.ReadUInt16BE(); // we (and Windows) only support Format 0 tables // only care about tables with horizontal kerning data var kc = (KernCoverage)coverage; if ((coverage & FormatMask) == 0 && (kc & KernCoverage.Horizontal) != 0 && (kc & KernCoverage.CrossStream) == 0) { // read the number of entries; skip over the rest of the header var entryCount = reader.ReadUInt16BE(); reader.Skip(sizeof(short) * 3); var isMin = (kc & KernCoverage.Minimum) != 0; var isOverride = (kc & KernCoverage.Override) != 0; // read in each entry and accumulate its kerning data for (int j = 0; j < entryCount; j++) { var left = reader.ReadUInt16BE(); var right = reader.ReadUInt16BE(); var value = reader.ReadInt16BE(); // look up the current value, if we have one; if not, start at zero int current = 0; var key = ((uint)left << 16) | right; tableData.TryGetValue(key, out current); if (isMin) { if (current < value) { tableData[key] = value; } } else if (isOverride) { tableData[key] = value; } else { tableData[key] = current + value; } } } // jump to the next subtable reader.Seek(currentOffset + length); } return(new KerningTable(tableData)); }
public static void ReadGlyph( DataReader reader, int glyphIndex, int recursionDepth, BaseGlyph[] glyphTable, uint glyfOffset, uint glyfLength, uint *loca ) { // check if this glyph has already been loaded; this can happen // if we're recursively loading subglyphs as part of a composite if (glyphTable[glyphIndex] != null) { return; } // prevent bad font data from causing infinite recursion if (recursionDepth > MaxRecursion) { throw new InvalidFontException("Bad font data; infinite composite recursion."); } // check if this glyph doesn't have any actual data GlyphHeader header; var offset = loca[glyphIndex]; if ((glyphIndex < glyphTable.Length - 1 && offset == loca[glyphIndex + 1]) || offset >= glyfLength) { // this is an empty glyph, so synthesize a header header = default(GlyphHeader); } else { // seek to the right spot and load the header reader.Seek(glyfOffset + loca[glyphIndex]); header = new GlyphHeader { ContourCount = reader.ReadInt16BE(), MinX = reader.ReadInt16BE(), MinY = reader.ReadInt16BE(), MaxX = reader.ReadInt16BE(), MaxY = reader.ReadInt16BE() }; if (header.ContourCount < -1 || header.ContourCount > MaxContours) { throw new InvalidFontException("Invalid number of contours for glyph."); } } if (header.ContourCount > 0) { // positive contours means a simple glyph glyphTable[glyphIndex] = ReadSimpleGlyph(reader, header.ContourCount); } else if (header.ContourCount == -1) { // -1 means composite glyph var composite = ReadCompositeGlyph(reader); var subglyphs = composite.Subglyphs; // read each subglyph recrusively for (int i = 0; i < subglyphs.Length; i++) { ReadGlyph(reader, subglyphs[i].Index, recursionDepth + 1, glyphTable, glyfOffset, glyfLength, loca); } glyphTable[glyphIndex] = composite; } else { // no data, so synthesize an empty glyph glyphTable[glyphIndex] = new SimpleGlyph { Points = new Point[0], ContourEndpoints = new int[0] }; } // save bounding box var glyph = glyphTable[glyphIndex]; glyph.MinX = header.MinX; glyph.MinY = header.MinY; glyph.MaxX = header.MaxX; glyph.MaxY = header.MaxY; }
static SimpleGlyph ReadSimpleGlyph(DataReader reader, int contourCount) { // read contour endpoints var contours = new int[contourCount]; var lastEndpoint = reader.ReadUInt16BE(); contours[0] = lastEndpoint; for (int i = 1; i < contours.Length; i++) { var endpoint = reader.ReadUInt16BE(); contours[i] = endpoint; if (contours[i] <= lastEndpoint) { throw new InvalidFontException("Glyph contour endpoints are unordered."); } lastEndpoint = endpoint; } // the last contour's endpoint is the number of points in the glyph var pointCount = lastEndpoint + 1; var points = new Point[pointCount]; // read instruction data var instructionLength = reader.ReadUInt16BE(); var instructions = reader.ReadBytes(instructionLength); // read flags var flags = new SimpleGlyphFlags[pointCount]; int flagIndex = 0; while (flagIndex < flags.Length) { var f = (SimpleGlyphFlags)reader.ReadByte(); flags[flagIndex++] = f; // if Repeat is set, this flag data is repeated n more times if ((f & SimpleGlyphFlags.Repeat) != 0) { var count = reader.ReadByte(); for (int i = 0; i < count; i++) { flags[flagIndex++] = f; } } } // Read points, first doing all X coordinates and then all Y coordinates. // The point packing is insane; coords are either 1 byte or 2; they're // deltas from previous point, and flags let you repeat identical points. var x = 0; for (int i = 0; i < points.Length; i++) { var f = flags[i]; var delta = 0; if ((f & SimpleGlyphFlags.ShortX) != 0) { delta = reader.ReadByte(); if ((f & SimpleGlyphFlags.SameX) == 0) { delta = -delta; } } else if ((f & SimpleGlyphFlags.SameX) == 0) { delta = reader.ReadInt16BE(); } x += delta; points[i].X = (FUnit)x; } var y = 0; for (int i = 0; i < points.Length; i++) { var f = flags[i]; var delta = 0; if ((f & SimpleGlyphFlags.ShortY) != 0) { delta = reader.ReadByte(); if ((f & SimpleGlyphFlags.SameY) == 0) { delta = -delta; } } else if ((f & SimpleGlyphFlags.SameY) == 0) { delta = reader.ReadInt16BE(); } y += delta; points[i].Y = (FUnit)y; points[i].Type = (f & SimpleGlyphFlags.OnCurve) != 0 ? PointType.OnCurve : PointType.Quadratic; } return(new SimpleGlyph { Points = points, ContourEndpoints = contours, Instructions = instructions }); }
public static OS2Data ReadOS2(DataReader reader, TableRecord[] tables) { SeekToTable(reader, tables, FourCC.OS_2, required: true); // skip over version, xAvgCharWidth reader.Skip(sizeof(short) * 2); var result = new OS2Data { Weight = (FontWeight)reader.ReadUInt16BE(), Stretch = (FontStretch)reader.ReadUInt16BE() }; // skip over fsType, ySubscriptXSize, ySubscriptYSize, ySubscriptXOffset, ySubscriptYOffset, // ySuperscriptXSize, ySuperscriptYSize, ySuperscriptXOffset, ySuperscriptXOffset reader.Skip(sizeof(short) * 9); result.StrikeoutSize = reader.ReadInt16BE(); result.StrikeoutPosition = reader.ReadInt16BE(); // skip over sFamilyClass, panose[10], ulUnicodeRange1-4, achVendID[4] reader.Skip(sizeof(short) + sizeof(int) * 4 + 14); // check various style flags var fsSelection = (FsSelectionFlags)reader.ReadUInt16BE(); result.Style = (fsSelection & FsSelectionFlags.Italic) != 0 ? FontStyle.Italic : (fsSelection & FsSelectionFlags.Bold) != 0 ? FontStyle.Bold : (fsSelection & FsSelectionFlags.Oblique) != 0 ? FontStyle.Oblique : FontStyle.Regular; result.IsWWSFont = (fsSelection & FsSelectionFlags.WWS) != 0; result.UseTypographicMetrics = (fsSelection & FsSelectionFlags.UseTypoMetrics) != 0; // skip over usFirstCharIndex, usLastCharIndex reader.Skip(sizeof(short) * 2); result.TypographicAscender = reader.ReadInt16BE(); result.TypographicDescender = reader.ReadInt16BE(); result.TypographicLineGap = reader.ReadInt16BE(); result.WinAscent = reader.ReadUInt16BE(); result.WinDescent = reader.ReadUInt16BE(); // skip over ulCodePageRange1-2 reader.Skip(sizeof(int) * 2); result.XHeight = reader.ReadInt16BE(); result.CapHeight = reader.ReadInt16BE(); return result; }
public static MetricsEntry[] ReadMetricsTable(DataReader reader, int glyphCount, int metricCount) { var results = new MetricsEntry[glyphCount]; for (int i = 0; i < metricCount; i++) { results[i] = new MetricsEntry { Advance = reader.ReadUInt16BE(), FrontSideBearing = reader.ReadInt16BE() }; } // there might be an additional array of fsb-only entries var extraCount = glyphCount - metricCount; var lastAdvance = results[metricCount - 1].Advance; for (int i = 0; i < extraCount; i++) { results[i + metricCount] = new MetricsEntry { Advance = lastAdvance, FrontSideBearing = reader.ReadInt16BE() }; } return results; }
unsafe static CharacterMap ReadCmapFormat4(DataReader reader) { // skip over length and language reader.Skip(sizeof(short) * 2); // figure out how many segments we have var segmentCount = reader.ReadUInt16BE() / 2; if (segmentCount > MaxSegments) { throw new Exception("Too many cmap segments."); } // skip over searchRange, entrySelector, and rangeShift reader.Skip(sizeof(short) * 3); // read in segment ranges var endCount = stackalloc int[segmentCount]; for (int i = 0; i < segmentCount; i++) { endCount[i] = reader.ReadUInt16BE(); } reader.Skip(sizeof(short)); // padding var startCount = stackalloc int[segmentCount]; for (int i = 0; i < segmentCount; i++) { startCount[i] = reader.ReadUInt16BE(); } var idDelta = stackalloc int[segmentCount]; for (int i = 0; i < segmentCount; i++) { idDelta[i] = reader.ReadInt16BE(); } // build table from each segment var table = new Dictionary <CodePoint, int>(); for (int i = 0; i < segmentCount; i++) { // read the "idRangeOffset" for the current segment // if nonzero, we need to jump into the glyphIdArray to figure out the mapping // the layout is bizarre; see the OpenType spec for details var idRangeOffset = reader.ReadUInt16BE(); if (idRangeOffset != 0) { var currentOffset = reader.Position; reader.Seek(currentOffset + idRangeOffset - sizeof(ushort)); var end = endCount[i]; var delta = idDelta[i]; for (var codepoint = startCount[i]; codepoint <= end; codepoint++) { var glyphId = reader.ReadUInt16BE(); if (glyphId != 0) { var glyphIndex = (glyphId + delta) & 0xFFFF; if (glyphIndex != 0) { table.Add((CodePoint)codepoint, glyphIndex); } } } reader.Seek(currentOffset); } else { // otherwise, do a straight iteration through the segment var end = endCount[i]; var delta = idDelta[i]; for (var codepoint = startCount[i]; codepoint <= end; codepoint++) { var glyphIndex = (codepoint + delta) & 0xFFFF; if (glyphIndex != 0) { table.Add((CodePoint)codepoint, glyphIndex); } } } } return(new CharacterMap(table)); }
public static KerningTable ReadKern(DataReader reader, TableRecord[] tables) { // kern table is optional if (!SfntTables.SeekToTable(reader, tables, FourCC.Kern)) return null; // skip version reader.Skip(sizeof(short)); // read each subtable and accumulate kerning values var tableData = new Dictionary<uint, int>(); var subtableCount = reader.ReadUInt16BE(); for (int i = 0; i < subtableCount; i++) { // skip version var currentOffset = reader.Position; reader.Skip(sizeof(short)); var length = reader.ReadUInt16BE(); var coverage = reader.ReadUInt16BE(); // we (and Windows) only support Format 0 tables // only care about tables with horizontal kerning data var kc = (KernCoverage)coverage; if ((coverage & FormatMask) == 0 && (kc & KernCoverage.Horizontal) != 0 && (kc & KernCoverage.CrossStream) == 0) { // read the number of entries; skip over the rest of the header var entryCount = reader.ReadUInt16BE(); reader.Skip(sizeof(short) * 3); var isMin = (kc & KernCoverage.Minimum) != 0; var isOverride = (kc & KernCoverage.Override) != 0; // read in each entry and accumulate its kerning data for (int j = 0; j < entryCount; j++) { var left = reader.ReadUInt16BE(); var right = reader.ReadUInt16BE(); var value = reader.ReadInt16BE(); // look up the current value, if we have one; if not, start at zero int current = 0; var key = ((uint)left << 16) | right; tableData.TryGetValue(key, out current); if (isMin) { if (current < value) tableData[key] = value; } else if (isOverride) tableData[key] = value; else tableData[key] = current + value; } } // jump to the next subtable reader.Seek(currentOffset + length); } return new KerningTable(tableData); }
public static void ReadPost(DataReader reader, TableRecord[] tables, ref FaceHeader header) { if (!SeekToTable(reader, tables, FourCC.Post)) return; // skip over version and italicAngle reader.Skip(sizeof(int) * 2); header.UnderlinePosition = reader.ReadInt16BE(); header.UnderlineThickness = reader.ReadInt16BE(); header.IsFixedPitch = reader.ReadUInt32BE() != 0; }
static unsafe CharacterMap ReadCmapFormat4(DataReader reader) { // skip over length and language reader.Skip(sizeof(short) * 2); // figure out how many segments we have var segmentCount = reader.ReadUInt16BE() / 2; if (segmentCount > MaxSegments) throw new Exception("Too many cmap segments."); // skip over searchRange, entrySelector, and rangeShift reader.Skip(sizeof(short) * 3); // read in segment ranges var endCount = stackalloc int[segmentCount]; for (int i = 0; i < segmentCount; i++) endCount[i] = reader.ReadUInt16BE(); reader.Skip(sizeof(short)); // padding var startCount = stackalloc int[segmentCount]; for (int i = 0; i < segmentCount; i++) startCount[i] = reader.ReadUInt16BE(); var idDelta = stackalloc int[segmentCount]; for (int i = 0; i < segmentCount; i++) idDelta[i] = reader.ReadInt16BE(); // build table from each segment var table = new Dictionary<CodePoint, int>(); for (int i = 0; i < segmentCount; i++) { // read the "idRangeOffset" for the current segment // if nonzero, we need to jump into the glyphIdArray to figure out the mapping // the layout is bizarre; see the OpenType spec for details var idRangeOffset = reader.ReadUInt16BE(); if (idRangeOffset != 0) { var currentOffset = reader.Position; reader.Seek(currentOffset + idRangeOffset - sizeof(ushort)); var end = endCount[i]; var delta = idDelta[i]; for (var codepoint = startCount[i]; codepoint <= end; codepoint++) { var glyphId = reader.ReadUInt16BE(); if (glyphId != 0) { var glyphIndex = (glyphId + delta) & 0xFFFF; if (glyphIndex != 0) table.Add((CodePoint)codepoint, glyphIndex); } } reader.Seek(currentOffset); } else { // otherwise, do a straight iteration through the segment var end = endCount[i]; var delta = idDelta[i]; for (var codepoint = startCount[i]; codepoint <= end; codepoint++) { var glyphIndex = (codepoint + delta) & 0xFFFF; if (glyphIndex != 0) table.Add((CodePoint)codepoint, glyphIndex); } } } return new CharacterMap(table); }
public static MetricsHeader ReadMetricsHeader(DataReader reader) { // skip over version reader.Skip(sizeof(int)); var header = new MetricsHeader { Ascender = reader.ReadInt16BE(), Descender = reader.ReadInt16BE(), LineGap = reader.ReadInt16BE() }; // skip over advanceWidthMax, minLsb, minRsb, xMaxExtent, caretSlopeRise, // caretSlopeRun, caretOffset, 4 reserved entries, and metricDataFormat reader.Skip(sizeof(short) * 12); header.MetricCount = reader.ReadUInt16BE(); return header; }
public static FUnit[] ReadCvt(DataReader reader, TableRecord[] tables) { var index = FindTable(tables, FourCC.Cvt); if (index == -1) return null; reader.Seek(tables[index].Offset); var results = new FUnit[tables[index].Length / sizeof(short)]; for (int i = 0; i < results.Length; i++) results[i] = (FUnit)reader.ReadInt16BE(); return results; }
static CompositeGlyph ReadCompositeGlyph(DataReader reader) { // we need to keep reading glyphs for as long as // our flags tell us that there are more to read var subglyphs = new List<Subglyph>(); CompositeGlyphFlags flags; do { flags = (CompositeGlyphFlags)reader.ReadUInt16BE(); var subglyph = new Subglyph { Flags = flags }; subglyph.Index = reader.ReadUInt16BE(); // read in args; they vary in size based on flags if ((flags & CompositeGlyphFlags.ArgsAreWords) != 0) { subglyph.Arg1 = reader.ReadInt16BE(); subglyph.Arg2 = reader.ReadInt16BE(); } else { subglyph.Arg1 = reader.ReadSByte(); subglyph.Arg2 = reader.ReadSByte(); } // figure out the transform; we can either have no scale, a uniform // scale, two independent scales, or a full 2x2 transform matrix // transform components are in 2.14 fixed point format var transform = Matrix3x2.Identity; if ((flags & CompositeGlyphFlags.HaveScale) != 0) { var scale = reader.ReadInt16BE() / F2Dot14ToFloat; transform.M11 = scale; transform.M22 = scale; } else if ((flags & CompositeGlyphFlags.HaveXYScale) != 0) { transform.M11 = reader.ReadInt16BE() / F2Dot14ToFloat; transform.M22 = reader.ReadInt16BE() / F2Dot14ToFloat; } else if ((flags & CompositeGlyphFlags.HaveTransform) != 0) { transform.M11 = reader.ReadInt16BE() / F2Dot14ToFloat; transform.M12 = reader.ReadInt16BE() / F2Dot14ToFloat; transform.M21 = reader.ReadInt16BE() / F2Dot14ToFloat; transform.M22 = reader.ReadInt16BE() / F2Dot14ToFloat; } subglyph.Transform = transform; subglyphs.Add(subglyph); } while ((flags & CompositeGlyphFlags.MoreComponents) != 0); var result = new CompositeGlyph { Subglyphs = subglyphs.ToArray() }; // if we have instructions, read them now if ((flags & CompositeGlyphFlags.HaveInstructions) != 0) { var instructionLength = reader.ReadUInt16BE(); result.Instructions = reader.ReadBytes(instructionLength); } return result; }
static SimpleGlyph ReadSimpleGlyph(DataReader reader, int contourCount) { // read contour endpoints var contours = new int[contourCount]; var lastEndpoint = reader.ReadUInt16BE(); contours[0] = lastEndpoint; for (int i = 1; i < contours.Length; i++) { var endpoint = reader.ReadUInt16BE(); contours[i] = endpoint; if (contours[i] <= lastEndpoint) throw new InvalidFontException("Glyph contour endpoints are unordered."); lastEndpoint = endpoint; } // the last contour's endpoint is the number of points in the glyph var pointCount = lastEndpoint + 1; var points = new Point[pointCount]; // read instruction data var instructionLength = reader.ReadUInt16BE(); var instructions = reader.ReadBytes(instructionLength); // read flags var flags = new SimpleGlyphFlags[pointCount]; int flagIndex = 0; while (flagIndex < flags.Length) { var f = (SimpleGlyphFlags)reader.ReadByte(); flags[flagIndex++] = f; // if Repeat is set, this flag data is repeated n more times if ((f & SimpleGlyphFlags.Repeat) != 0) { var count = reader.ReadByte(); for (int i = 0; i < count; i++) flags[flagIndex++] = f; } } // Read points, first doing all X coordinates and then all Y coordinates. // The point packing is insane; coords are either 1 byte or 2; they're // deltas from previous point, and flags let you repeat identical points. var x = 0; for (int i = 0; i < points.Length; i++) { var f = flags[i]; var delta = 0; if ((f & SimpleGlyphFlags.ShortX) != 0) { delta = reader.ReadByte(); if ((f & SimpleGlyphFlags.SameX) == 0) delta = -delta; } else if ((f & SimpleGlyphFlags.SameX) == 0) delta = reader.ReadInt16BE(); x += delta; points[i].X = (FUnit)x; } var y = 0; for (int i = 0; i < points.Length; i++) { var f = flags[i]; var delta = 0; if ((f & SimpleGlyphFlags.ShortY) != 0) { delta = reader.ReadByte(); if ((f & SimpleGlyphFlags.SameY) == 0) delta = -delta; } else if ((f & SimpleGlyphFlags.SameY) == 0) delta = reader.ReadInt16BE(); y += delta; points[i].Y = (FUnit)y; points[i].Type = (f & SimpleGlyphFlags.OnCurve) != 0 ? PointType.OnCurve : PointType.Quadratic; } return new SimpleGlyph { Points = points, ContourEndpoints = contours, Instructions = instructions }; }
public static void ReadGlyph( DataReader reader, int glyphIndex, int recursionDepth, BaseGlyph[] glyphTable, uint glyfOffset, uint glyfLength, uint* loca ) { // check if this glyph has already been loaded; this can happen // if we're recursively loading subglyphs as part of a composite if (glyphTable[glyphIndex] != null) return; // prevent bad font data from causing infinite recursion if (recursionDepth > MaxRecursion) throw new InvalidFontException("Bad font data; infinite composite recursion."); // check if this glyph doesn't have any actual data GlyphHeader header; var offset = loca[glyphIndex]; if ((glyphIndex < glyphTable.Length - 1 && offset == loca[glyphIndex + 1]) || offset >= glyfLength) { // this is an empty glyph, so synthesize a header header = default(GlyphHeader); } else { // seek to the right spot and load the header reader.Seek(glyfOffset + loca[glyphIndex]); header = new GlyphHeader { ContourCount = reader.ReadInt16BE(), MinX = reader.ReadInt16BE(), MinY = reader.ReadInt16BE(), MaxX = reader.ReadInt16BE(), MaxY = reader.ReadInt16BE() }; if (header.ContourCount < -1 || header.ContourCount > MaxContours) throw new InvalidFontException("Invalid number of contours for glyph."); } if (header.ContourCount > 0) { // positive contours means a simple glyph glyphTable[glyphIndex] = ReadSimpleGlyph(reader, header.ContourCount); } else if (header.ContourCount == -1) { // -1 means composite glyph var composite = ReadCompositeGlyph(reader); var subglyphs = composite.Subglyphs; // read each subglyph recrusively for (int i = 0; i < subglyphs.Length; i++) ReadGlyph(reader, subglyphs[i].Index, recursionDepth + 1, glyphTable, glyfOffset, glyfLength, loca); glyphTable[glyphIndex] = composite; } else { // no data, so synthesize an empty glyph glyphTable[glyphIndex] = new SimpleGlyph { Points = new Point[0], ContourEndpoints = new int[0] }; } // save bounding box var glyph = glyphTable[glyphIndex]; glyph.MinX = header.MinX; glyph.MinY = header.MinY; glyph.MaxX = header.MaxX; glyph.MaxY = header.MaxY; }