Example #1
0
        /// <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>();
        }
Example #2
0
        /// <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);
        }