public static void ComposeGlyphs(int glyphIndex, int startPoint, ref Matrix3x2 transform, List<PointF> basePoints, List<int> baseContours, BaseGlyph[] glyphTable) { var glyph = glyphTable[glyphIndex]; var simple = glyph as SimpleGlyph; if (simple != null) { foreach (var endpoint in simple.ContourEndpoints) baseContours.Add(endpoint + startPoint); foreach (var point in simple.Points) basePoints.Add(new PointF(Vector2.TransformNormal((Vector2)point, transform), point.Type)); } else { // otherwise, we have a composite glyph var composite = (CompositeGlyph)glyph; foreach (var subglyph in composite.Subglyphs) { // if we have a scale, update the local transform var local = transform; bool haveScale = (subglyph.Flags & (CompositeGlyphFlags.HaveScale | CompositeGlyphFlags.HaveXYScale | CompositeGlyphFlags.HaveTransform)) != 0; if (haveScale) local = transform * subglyph.Transform; // recursively compose the subglyph into our lists int currentPoints = basePoints.Count; ComposeGlyphs(subglyph.Index, currentPoints, ref local, basePoints, baseContours, glyphTable); // calculate the offset for the subglyph. we have to do offsetting after composing all subglyphs, // because we might need to find the offset based on previously composed points by index Vector2 offset; if ((subglyph.Flags & CompositeGlyphFlags.ArgsAreXYValues) != 0) { offset = (Vector2)new Point((FUnit)subglyph.Arg1, (FUnit)subglyph.Arg2); if (haveScale && (subglyph.Flags & CompositeGlyphFlags.ScaledComponentOffset) != 0) offset = Vector2.TransformNormal(offset, local); else offset = Vector2.TransformNormal(offset, transform); // if the RoundXYToGrid flag is set, round the offset components if ((subglyph.Flags & CompositeGlyphFlags.RoundXYToGrid) != 0) offset = new Vector2((float)Math.Round(offset.X), (float)Math.Round(offset.Y)); } else { // if the offsets are not given in FUnits, then they are point indices // in the currently composed base glyph that we should match up var p1 = basePoints[(int)((uint)subglyph.Arg1 + startPoint)]; var p2 = basePoints[(int)((uint)subglyph.Arg2 + currentPoints)]; offset = p1.P - p2.P; } // translate all child points if (offset != Vector2.Zero) { for (int i = currentPoints; i < basePoints.Count; i++) basePoints[i] = basePoints[i].Offset(offset); } } } }
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; }