/// <summary> /// Constructor. /// </summary> /// <param name="glyph">The glyph being constructed.</param> /// <param name="local">Local subroutines.</param> /// <param name="global">Global subroutines.</param> public ExecContext( Font.Glyph glyph, Dictionary <int, Type2Charstring> local, Dictionary <int, Type2Charstring> global) { this.atStart = true; this.glyph = glyph; this.contour = null; this.pos = Vector2.zero; this.local = local; this.global = global; this.ended = false; this.processedFirstMT = false; this.width = 0.0f; this.hstems = new List <float>(); this.vstems = new List <float>(); this.hintMasks = null; this.cntrMasks = null; this.stack = new List <Operand>(); }
/// <summary> /// Given a font glyph, generate a formal path for it. /// </summary> /// <param name="glyph">The glyph to turn into a path.</param> /// <param name="l">The layer to create the shape in.</param> /// <param name="offset">The offset to translate the glyph.</param> /// <param name="scale">The scale of the glyph, with 1.0 being the /// default size.</param> /// <returns>The created path. If the glyph has multiple /// contours, they will be created as individual loops.</returns> public static BShape GenerateGlyph( Font.Glyph glyph, Layer l, Vector2 offset, float scale) { BShape shapeLetter = new BShape(Vector2.zero, 0.0f); shapeLetter.layer = l; if (l != null) { l.shapes.Add(shapeLetter); } // Generate each contour in the glyph. When we're iterating through the glyph points, // we need to remember we're dealing with two possible conventions at once - TTF/OTF and // CFF. // // Remember TTF/OTF uses quadratic Beziers and the control flags. // // While CCF uses cubic Beziers and the point tangents and tangent flags. for (int j = 0; j < glyph.contours.Count; ++j) { BLoop loopCont = new BLoop(shapeLetter); //https://stackoverflow.com/questions/20733790/truetype-fonts-glyph-are-made-of-quadratic-bezier-why-do-more-than-one-consecu Font.Contour cont = glyph.contours[j]; for (int k = 0; k < cont.points.Count; ++k) { int nextId = (k + 1) % cont.points.Count; // If two control points are next to each other, there's an implied // point in between them at their average. The easiest way to handle // that is to make a representation where we manually inserted them // to define them explicitly. // // NOTE: We should probably just directly "sanitize" this information // when it's first loaded. if (cont.points[k].isControl == true && cont.points[nextId].isControl == true) { Font.Point pt = new Font.Point(); pt.isControl = false; pt.position = (cont.points[k].position + cont.points[nextId].position) * 0.5f; // Things that process this data may want to know it's implied, especially // diagnostic tools. pt.implied = true; cont.points.Insert(nextId, pt); ++k; } } BNode firstNode = null; // Used to know what to link the last node to when we're done looping. BNode prevNode = null; // Used to have a record of the last node when we're done looping. Vector2?lastTan = null; // The last used tangent when dealing with control points. // Point are now either points, or curve controls surrounded by points - // or it's a CFF and we don't actually care about control points since we have // explicitly defined tangents. // // The code is written to handle both without explicitly knowing which system is being used. for (int k = 0; k < cont.points.Count; ++k) { Vector2 ptpos = cont.points[k].position * scale + offset; if (cont.points[k].isControl == false) { BNode node = new BNode(loopCont, ptpos); loopCont.nodes.Add(node); if (lastTan.HasValue == true) { node.UseTanIn = true; node.TanIn = (lastTan.Value - ptpos) * (2.0f / 3.0f) * scale; } lastTan = null; if (prevNode != null) { node.prev = prevNode; prevNode.next = node; } if (firstNode == null) { firstNode = node; } int kPrev = (k - 1 + cont.points.Count) % cont.points.Count; if (k != 0 && cont.points[kPrev].isControl == false && cont.points[kPrev].useTangentOut == false) { prevNode.UseTanOut = false; node.UseTanIn = false; } if (cont.points[k].useTangentIn == true) { node.UseTanIn = true; node.TanIn = cont.points[k].tangentIn; } if (cont.points[k].useTangentOut == true) { node.UseTanOut = true; node.TanOut = cont.points[k].tangentOut; } node.FlagDirty(); prevNode = node; } else // if (cont.points[k].control == true) { lastTan = ptpos; if (prevNode != null) { prevNode.UseTanOut = true; prevNode.TanOut = (ptpos - prevNode.Pos) * (2.0f / 3.0f) * scale; } } } if (firstNode != null) { prevNode.next = firstNode; firstNode.prev = prevNode; if ( cont.points[0].isControl == false && cont.points[0].useTangentIn == false && cont.points[cont.points.Count - 1].isControl == false && cont.points[cont.points.Count - 1].useTangentOut == false) { firstNode.UseTanIn = false; prevNode.UseTanOut = false; } if (lastTan.HasValue == true) { firstNode.UseTanIn = true; firstNode.TanIn = (lastTan.Value - firstNode.Pos) * (2.0f / 3.0f) * scale; } } } return(shapeLetter); }