Ejemplo n.º 1
0
        protected void ShapeGlyphRuns(TextAnalyzer textAnalyzer)
        {
            // Shapes all the glyph runs in the layout.

            // Estimate the maximum number of glyph indices needed to hold a string.
            int estimatedGlyphCount = EstimateGlyphCount(text_.Length);

            glyphIndices_  = new short[estimatedGlyphCount];
            glyphOffsets_  = new GlyphOffset[estimatedGlyphCount];
            glyphAdvances_ = new float[estimatedGlyphCount];
            glyphClusters_ = new short[text_.Length];

            int glyphStart = 0;

            // Shape each run separately. This is needed whenever script, locale,
            // or reading direction changes.
            for (int runIndex = 0; runIndex < runs_.Length; runIndex++)
            {
                ShapeGlyphRun(textAnalyzer, runIndex, ref glyphStart);
            }

            short[] resized_glyphIndices_ = new short[glyphStart];
            Array.Copy(glyphIndices_, 0, resized_glyphIndices_, 0, glyphStart);
            glyphIndices_ = resized_glyphIndices_;

            GlyphOffset[] resized_glyphOffsets_ = new GlyphOffset[glyphStart];
            Array.Copy(glyphOffsets_, 0, resized_glyphOffsets_, 0, glyphStart);
            glyphOffsets_ = resized_glyphOffsets_;

            float[] resized_glyphAdvances_ = new float[glyphStart];
            Array.Copy(glyphAdvances_, 0, resized_glyphAdvances_, 0, glyphStart);
            glyphAdvances_ = resized_glyphAdvances_;
        }
Ejemplo n.º 2
0
 public void SetGlyphRun(float x, float y, int glyphCount, short[] glyphIndices, float[] glyphAdvances, GlyphOffset[] glyphOffsets, FontFace fontface, float fontEmSize, int BidiLevel, bool isSideways)
 {
     // Append this glyph run to the list.
     int glyphStart = glyphAdvances_.Count;
     glyphAdvances_.AddRange(glyphAdvances);
     glyphIndices_.AddRange(glyphIndices);
     glyphOffsets_.AddRange(glyphOffsets);
     glyphRuns_.Add(new CustomGlyphRun(fontface, fontEmSize, x, y, glyphStart, glyphCount, BidiLevel, isSideways));
 }
Ejemplo n.º 3
0
        ///
        ///<summary>
        /// Save the collection to a xml node
        /// </summary>
        ///
        public bool Save(XmlWriter xml)
        {
            if (xml == null)
            {
                return(false);
            }

            xml.WriteStartElement(Tag);
            xml.WriteAttributeString("name", Name);



            if (!string.IsNullOrEmpty(TextureName))
            {
            }
            else if (!string.IsNullOrEmpty(TTFFileName))
            {
            }
            else if (!string.IsNullOrEmpty(TileSetName))
            {
                xml.WriteStartElement("tileset");
                xml.WriteAttributeString("name", TileSetName);
                xml.WriteEndElement();
            }


            xml.WriteStartElement("offset");
            xml.WriteAttributeString("value", GlyphOffset.ToString());
            xml.WriteEndElement();

            xml.WriteStartElement("advance");
            xml.WriteAttributeString("value", Advance.ToString());
            xml.WriteEndElement();

            xml.WriteStartElement("interline");
            xml.WriteAttributeString("value", Interline.ToString());
            xml.WriteEndElement();

            xml.WriteEndElement();
            return(false);
        }
Ejemplo n.º 4
0
    internal unsafe void __MarshalFrom(ref __Native @ref)
    {
        FontFace = (@ref.FontFace == IntPtr.Zero) ? null : new IDWriteFontFace(@ref.FontFace);
        if (FontFace != null)
        {
            FontFace.AddRef();
        }

        FontEmSize = @ref.FontEmSize;
        if (@ref.GlyphIndices != IntPtr.Zero)
        {
            Indices = new ushort[@ref.GlyphCount];
            if (@ref.GlyphCount > 0)
            {
                UnsafeUtilities.Read(@ref.GlyphIndices, Indices, @ref.GlyphCount);
            }
        }

        if (@ref.GlyphAdvances != IntPtr.Zero)
        {
            Advances = new float[@ref.GlyphCount];
            if (@ref.GlyphCount > 0)
            {
                UnsafeUtilities.Read(@ref.GlyphAdvances, Advances, @ref.GlyphCount);
            }
        }

        if (@ref.GlyphOffsets != IntPtr.Zero)
        {
            Offsets = new GlyphOffset[@ref.GlyphCount];
            if (@ref.GlyphCount > 0)
            {
                UnsafeUtilities.Read(@ref.GlyphOffsets, Offsets, @ref.GlyphCount);
            }
        }

        IsSideways = @ref.IsSideways;
        BidiLevel  = @ref.BidiLevel;
    }
Ejemplo n.º 5
0
        protected bool ProduceGlyphRuns(FlowLayoutSink flowSink, ref RectangleF rect, ref ClusterPosition clusterStart, ref ClusterPosition clusterEnd)
        {
            // Produce a series of glyph runs from the given range
            // and send them to the sink. If the entire text fit
            // into the rect, then we'll only pass on a single glyph
            // run.

            ////////////////////////////////////////
            // Figure out how many runs we cross, because this is the number
            // of distinct glyph runs we'll need to reorder visually.

            int runIndexEnd = clusterEnd.runIndex;

            if (clusterEnd.textPosition > runs_[runIndexEnd].textStart)
            {
                ++runIndexEnd; // Only partially cover the run, so round up.
            }
            int[] bidiOrdering = new int[100];
            int   totalRuns    = Math.Min(bidiOrdering.Length, runIndexEnd - clusterStart.runIndex);

            ProduceBidiOrdering(clusterStart.runIndex, totalRuns, bidiOrdering);

            ////////////////////////////////////////
            // Ignore any trailing whitespace

            // Look backward from end until we find non-space.
            int trailingWsPosition;

            for (trailingWsPosition = clusterEnd.textPosition; trailingWsPosition > clusterStart.textPosition; --trailingWsPosition)
            {
                if (!breakpoints_[trailingWsPosition - 1].IsWhitespace)
                {
                    break; // Encountered last significant character.
                }
            }
            // Set the glyph run's ending cluster to the last whitespace.
            ClusterPosition clusterWsEnd = clusterStart;

            SetClusterPosition(ref clusterWsEnd, trailingWsPosition);

            ////////////////////////////////////////
            // Produce justified advances to reduce the jagged edge.
            List <float> justifiedAdvances;

            ProduceJustifiedAdvances(ref rect, ref clusterStart, ref clusterWsEnd, out justifiedAdvances);
            int justificationGlyphStart = GetClusterGlyphStart(ref clusterStart);
            ////////////////////////////////////////
            // Determine starting point for alignment.
            float x = rect.Left;
            float y = rect.Bottom;

            FontMetrics fontMetrics = fontFace_.Metrics;

            float descent = (fontMetrics.Descent * fontEmSize_ / fontMetrics.DesignUnitsPerEm);

            y -= descent;

            if (readingDirection_ == ReadingDirection.RightToLeft)
            {
                // For RTL, we neeed the run width to adjust the origin
                // so it starts on the right side.
                int glyphStart = GetClusterGlyphStart(ref clusterStart);
                int glyphEnd   = GetClusterGlyphStart(ref clusterWsEnd);

                if (glyphStart < glyphEnd)
                {
                    float lineWidth = GetClusterRangeWidth(
                        glyphStart - justificationGlyphStart,
                        glyphEnd - justificationGlyphStart,
                        justifiedAdvances.ToArray()
                        );
                    x = rect.Right - lineWidth;
                }
            }

            ////////////////////////////////////////
            // Send each glyph run to the sink.
            for (int i = 0; i < totalRuns; ++i)
            {
                Run run        = runs_[bidiOrdering[i]];
                int glyphStart = run.glyphStart;
                int glyphEnd   = glyphStart + run.glyphCount;

                // If the run is only partially covered, we'll need to find
                // the subsection of glyphs that were fit.
                if (clusterStart.textPosition > run.textStart)
                {
                    glyphStart = GetClusterGlyphStart(ref clusterStart);
                }
                if (clusterWsEnd.textPosition < run.textStart + run.textLength)
                {
                    glyphEnd = GetClusterGlyphStart(ref clusterWsEnd);
                }
                if ((glyphStart >= glyphEnd) || (run.script.Shapes == ScriptShapes.NoVisual))
                {
                    // The itemizer told us not to draw this character,
                    // either because it was a formatting, control, or other hidden character.
                    continue;
                }

                // The run width is needed to know how far to move forward,
                // and to flip the origin for right-to-left text.
                float runWidth = GetClusterRangeWidth(
                    glyphStart - justificationGlyphStart,
                    glyphEnd - justificationGlyphStart,
                    justifiedAdvances.ToArray()
                    );

                // Flush this glyph run.
                //int glyphCount = glyphEnd - glyphStart;
                int     glyphCount         = justifiedAdvances.Count;
                short[] call_glyphIndices_ = new short[glyphCount];
                Array.Copy(glyphIndices_, glyphStart, call_glyphIndices_, 0, call_glyphIndices_.Length);
                float[] call_justifiedAdvances = new float[justifiedAdvances.Count - (glyphStart - justificationGlyphStart)];
                justifiedAdvances.CopyTo(glyphStart - justificationGlyphStart, call_justifiedAdvances, 0, call_justifiedAdvances.Length);
                GlyphOffset[] call_glyphOffsets_ = new GlyphOffset[glyphCount];
                Array.Copy(glyphOffsets_, glyphStart, call_glyphOffsets_, 0, call_glyphOffsets_.Length);
                flowSink.SetGlyphRun(
                    (run.bidiLevel % 2 != 0) ? (x + runWidth) : (x), // origin starts from right if RTL
                    y,
                    glyphCount,
                    call_glyphIndices_,
                    call_justifiedAdvances,
                    call_glyphOffsets_,
                    fontFace_,
                    fontEmSize_,
                    run.bidiLevel,
                    run.isSideways
                    );

                x += runWidth;
            }
            return(true);
        }
Ejemplo n.º 6
0
        protected void ShapeGlyphRun(TextAnalyzer textAnalyzer, int runIndex, ref int glyphStart)
        {
            // Shapes a single run of text into glyphs.
            // Alternately, you could iteratively interleave shaping and line
            // breaking to reduce the number glyphs held onto at once. It's simpler
            // for this demostration to just do shaping and line breaking as two
            // separate processes, but realize that this does have the consequence that
            // certain advanced fonts containing line specific features (like Gabriola)
            // will shape as if the line is not broken.

            Run run              = runs_[runIndex];
            int textStart        = run.textStart;
            int textLength       = run.textLength;
            int maxGlyphCount    = glyphIndices_.Length - glyphStart;
            int actualGlyphCount = 0;

            run.glyphStart = glyphStart;
            run.glyphCount = 0;

            if (textLength == 0)
            {
                return;// Nothing to do..
            }
            // Allocate space for shaping to fill with glyphs and other information,
            // with about as many glyphs as there are text characters. We'll actually
            // need more glyphs than codepoints if they are decomposed into separate
            // glyphs, or fewer glyphs than codepoints if multiple are substituted
            // into a single glyph. In any case, the shaping process will need some
            // room to apply those rules to even make that determintation.

            if (textLength > maxGlyphCount)
            {
                maxGlyphCount = EstimateGlyphCount(textLength);
                int     totalGlyphsArrayCount = glyphStart + maxGlyphCount;
                short[] Resized_glyphIndices_ = new short[totalGlyphsArrayCount];
                glyphIndices_.CopyTo(Resized_glyphIndices_, 0);
                glyphIndices_ = Resized_glyphIndices_;
            }


            ShapingTextProperties[]  textProps  = new ShapingTextProperties[textLength];
            ShapingGlyphProperties[] glyphProps = new ShapingGlyphProperties[maxGlyphCount];

            // Get the glyphs from the text, retrying if needed.
            int tries = 0;

            do
            {
                short[] call_glyphClusters_ = new short[glyphClusters_.Length - textStart];
                short[] call_glyphIndices_  = new short[glyphIndices_.Length - glyphStart];

                var result = textAnalyzer.GetGlyphs(
                    text_.Substring(textStart, textLength),
                    textLength,
                    fontFace_,
                    run.isSideways,
                    (run.bidiLevel % 2 == 1),
                    run.script,
                    localName_,
                    run.isNumberSubstituted ? numberSubstitution_ : null,
                    null,
                    null,
                    0,
                    maxGlyphCount,
                    call_glyphClusters_,
                    textProps,
                    call_glyphIndices_,
                    glyphProps,
                    out actualGlyphCount);
                Array.Copy(call_glyphClusters_, 0, glyphClusters_, textStart, call_glyphClusters_.Length);
                Array.Copy(call_glyphIndices_, 0, glyphIndices_, glyphStart, call_glyphIndices_.Length);
                tries++;
                //if (result!=SharpDX.Result.OutOfMemory)
                if (result != SharpDX.Result.Ok)
                {
                    // Try again using a larger buffer.
                    maxGlyphCount = EstimateGlyphCount(maxGlyphCount);
                    int totalGlyphsArrayCount = glyphStart + maxGlyphCount;

                    glyphProps    = new ShapingGlyphProperties[maxGlyphCount];
                    glyphIndices_ = new short[totalGlyphsArrayCount];
                }
                else
                {
                    break;
                }
            }while (tries < 2);// We'll give it two chances.

            // Get the placement of the all the glyphs.
            if (glyphAdvances_.Length < glyphStart + actualGlyphCount)
            {
                float[] Resized_glyphAdvances_ = new float[glyphStart + actualGlyphCount];
                glyphAdvances_.CopyTo(Resized_glyphAdvances_, 0);
                glyphAdvances_ = Resized_glyphAdvances_;
            }
            if (glyphOffsets_.Length < glyphStart + actualGlyphCount)
            {
                GlyphOffset[] Resized_glyphOffsets_ = new GlyphOffset[glyphStart + actualGlyphCount];
                glyphOffsets_.CopyTo(Resized_glyphOffsets_, 0);
                glyphOffsets_ = Resized_glyphOffsets_;
            }

            short[] call2_glyphClusters_ = new short[glyphClusters_.Length - textStart];
            Array.Copy(glyphClusters_, textStart, call2_glyphClusters_, 0, call2_glyphClusters_.Length);
            short[] call2_glyphIndices_ = new short[glyphIndices_.Length - glyphStart];
            Array.Copy(glyphIndices_, glyphStart, call2_glyphIndices_, 0, call2_glyphIndices_.Length);
            float[] call2_glyphAdvances_ = new float[glyphAdvances_.Length - glyphStart];
            Array.Copy(glyphAdvances_, glyphStart, call2_glyphAdvances_, 0, call2_glyphAdvances_.Length);
            GlyphOffset[] call2_glyphOffsets_ = new GlyphOffset[glyphOffsets_.Length - glyphStart];
            Array.Copy(glyphOffsets_, glyphStart, call2_glyphOffsets_, 0, call2_glyphOffsets_.Length);

            var result2 = textAnalyzer.GetGlyphPlacements(
                text_.Substring(textStart, textLength),
                call2_glyphClusters_,
                textProps,
                textLength,
                call2_glyphIndices_,
                glyphProps,
                actualGlyphCount,
                fontFace_,
                fontEmSize_,
                run.isSideways,
                run.bidiLevel % 2 == 1,
                run.script,
                localName_,
                null,
                null,
                0,
                call2_glyphAdvances_,
                call2_glyphOffsets_);

            //call2_glyphClusters_.CopyTo(glyphClusters_, textStart);
            call2_glyphAdvances_.CopyTo(glyphAdvances_, glyphStart);
            call2_glyphOffsets_.CopyTo(glyphOffsets_, glyphStart);

            // Certain fonts, like Batang, contain glyphs for hidden control
            // and formatting characters. So we'll want to explicitly force their
            // advance to zero.
            if (run.script.Shapes == ScriptShapes.NoVisual)
            {
                for (int i = glyphStart; i < glyphStart + actualGlyphCount; i++)
                {
                    glyphAdvances_[i] = 0;
                }
            }

            // Set the final glyph count of this run and advance the starting glyph.
            run.glyphCount  = actualGlyphCount;
            runs_[runIndex] = run;
            glyphStart     += actualGlyphCount;
        }
Ejemplo n.º 7
0
 /// <summary>	
 /// Computes the outline of a run of glyphs by calling back to the outline sink interface. 	
 /// </summary>	
 /// <param name="emSize">The logical size of the font in DIP units. A DIP ("device-independent pixel") equals 1/96 inch. </param>
 /// <param name="glyphIndices">An array of glyph indices. The glyphs are in logical order and the advance direction depends on the isRightToLeft parameter. The array must be allocated and be able to contain the number of elements specified by glyphCount. </param>
 /// <param name="glyphAdvances">An optional array of glyph advances in DIPs. The advance of a glyph is the amount to advance the position (in the direction of the baseline) after drawing the glyph. glyphAdvances contains the number of elements specified by glyphIndices.Length. </param>
 /// <param name="glyphOffsets">An optional array of glyph offsets, each of which specifies the offset along the baseline and offset perpendicular to the baseline of a glyph relative to the current pen position.   glyphOffsets contains the number of elements specified by glyphIndices.Length. </param>
 /// <param name="isSideways">If TRUE, the ascender of the glyph runs alongside the baseline. If FALSE, the glyph ascender runs perpendicular to the baseline. For example, an English alphabet on a vertical baseline would have isSideways set to FALSE. A client can render a vertical run by setting isSideways to TRUE and rotating the resulting geometry 90 degrees to the right using a transform. The isSideways and isRightToLeft parameters cannot both be true. </param>
 /// <param name="isRightToLeft">The visual order of the glyphs. If this parameter is FALSE, then glyph advances are from left to right. If TRUE, the advance direction is right to left. By default, the advance direction is left to right. </param>
 /// <param name="geometrySink">A reference to the interface that is called back to perform outline drawing operations. </param>
 /// <returns>If the method succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code. </returns>
 /// <unmanaged>HRESULT IDWriteFontFace::GetGlyphRunOutline([None] float emSize,[In, Buffer] const short* glyphIndices,[In, Buffer, Optional] const float* glyphAdvances,[In, Buffer, Optional] const DWRITE_GLYPH_OFFSET* glyphOffsets,[None] int glyphCount,[None] BOOL isSideways,[None] BOOL isRightToLeft,[None] IDWriteGeometrySink* geometrySink)</unmanaged>
 public void GetGlyphRunOutline(float emSize, short[] glyphIndices, float[] glyphAdvances, GlyphOffset[] glyphOffsets, bool isSideways, bool isRightToLeft, SimplifiedGeometrySink geometrySink)
 {
     IntPtr ptr;
     if ( geometrySink is GeometrySink)
         ptr = GeometrySinkShadow.ToIntPtr((GeometrySink)geometrySink);
     else
         ptr = SimplifiedGeometrySinkShadow.ToIntPtr(geometrySink);
     GetGlyphRunOutline_(emSize, glyphIndices, glyphAdvances, glyphOffsets, glyphIndices.Length, isSideways, isRightToLeft, ptr);
 }
Ejemplo n.º 8
0
            /// <summary>
            /// Construct a full description of glyph data
            /// </summary>
            internal Glyphs(
                TextShapeableSymbols     shapeable,
                char[]                   charArray,
                int[]                   glyphAdvances,
                ushort[]                 clusterMap,
                ushort[]                 glyphIndices,
                GlyphOffset[]            glyphOffsets,
                double                   scalingFactor
                )
            {
                _shapeable = shapeable;
                _charArray = charArray;

                // create double array for glyph run creation, because Shaping is all done in 
                // ideal units. FormattedTextSymbol is used to draw text collapsing symbols 
                // which usually contains very few glyphs. Using double[] and Point[] directly
                // is more efficient. 
                _glyphAdvances = new double[glyphAdvances.Length];

                double ToReal = 1.0 / scalingFactor;
                
                for (int i = 0; i < glyphAdvances.Length; i++)
                {
                    _glyphAdvances[i] = glyphAdvances[i] * ToReal;
                    _width += _glyphAdvances[i];                
                    
                }

                if (glyphIndices != null)
                {
                    _clusterMap = clusterMap;

                    if (glyphOffsets != null)
                    {
                        _glyphOffsets  = new PartialArray<Point>(new Point[glyphOffsets.Length]);                    
                    
                        for (int i = 0; i < glyphOffsets.Length; i++)
                        {
                            _glyphOffsets[i] = new Point(
                                glyphOffsets[i].du * ToReal,
                                glyphOffsets[i].dv * ToReal
                                );
                        }
                    }

                    Debug.Assert(glyphAdvances.Length <= glyphIndices.Length);

                    if (glyphAdvances.Length != glyphIndices.Length)
                    {
                        _glyphIndices = new ushort[glyphAdvances.Length];

                        for (int i = 0; i < glyphAdvances.Length; i++)
                        {
                            _glyphIndices[i] = glyphIndices[i];
                        }
                    }
                    else
                    {
                        _glyphIndices = glyphIndices;
                    }
                }
            }
Ejemplo n.º 9
0
        protected bool ProduceGlyphRuns(FlowLayoutSink flowSink, ref RectangleF rect, ref ClusterPosition clusterStart, ref ClusterPosition clusterEnd)
        {
            // Produce a series of glyph runs from the given range
            // and send them to the sink. If the entire text fit
            // into the rect, then we'll only pass on a single glyph
            // run.

            ////////////////////////////////////////
            // Figure out how many runs we cross, because this is the number
            // of distinct glyph runs we'll need to reorder visually.

            int runIndexEnd = clusterEnd.runIndex;
            if (clusterEnd.textPosition > runs_[runIndexEnd].textStart)
                ++runIndexEnd; // Only partially cover the run, so round up.

            int[] bidiOrdering = new int[100];
            int totalRuns = Math.Min(bidiOrdering.Length, runIndexEnd - clusterStart.runIndex);
            ProduceBidiOrdering(clusterStart.runIndex, totalRuns, bidiOrdering);

            ////////////////////////////////////////
            // Ignore any trailing whitespace

            // Look backward from end until we find non-space.
            int trailingWsPosition;
            for (trailingWsPosition = clusterEnd.textPosition; trailingWsPosition > clusterStart.textPosition; --trailingWsPosition)
            {
                if (!breakpoints_[trailingWsPosition - 1].IsWhitespace)
                    break; // Encountered last significant character.
            }
            // Set the glyph run's ending cluster to the last whitespace.
            ClusterPosition clusterWsEnd = clusterStart;
            SetClusterPosition(ref clusterWsEnd, trailingWsPosition);

            ////////////////////////////////////////
            // Produce justified advances to reduce the jagged edge.
            List<float> justifiedAdvances;
            ProduceJustifiedAdvances(ref rect, ref clusterStart, ref clusterWsEnd, out justifiedAdvances);
            int justificationGlyphStart = GetClusterGlyphStart(ref clusterStart);
            ////////////////////////////////////////
            // Determine starting point for alignment.
            float x = rect.Left;
            float y = rect.Bottom;

            FontMetrics fontMetrics = fontFace_.Metrics;

            float descent = (fontMetrics.Descent * fontEmSize_ / fontMetrics.DesignUnitsPerEm);
            y -= descent;

            if (readingDirection_ == ReadingDirection.RightToLeft)
            {
                // For RTL, we neeed the run width to adjust the origin
                // so it starts on the right side.
                int glyphStart = GetClusterGlyphStart(ref clusterStart);
                int glyphEnd = GetClusterGlyphStart(ref clusterWsEnd);

                if (glyphStart < glyphEnd)
                {
                    float lineWidth = GetClusterRangeWidth(
                        glyphStart - justificationGlyphStart,
                        glyphEnd - justificationGlyphStart,
                        justifiedAdvances.ToArray()
                        );
                    x = rect.Right - lineWidth;
                }
            }

            ////////////////////////////////////////
            // Send each glyph run to the sink.
            for (int i = 0; i < totalRuns; ++i)
            {
                Run run = runs_[bidiOrdering[i]];
                int glyphStart = run.glyphStart;
                int glyphEnd = glyphStart + run.glyphCount;

                // If the run is only partially covered, we'll need to find
                // the subsection of glyphs that were fit.
                if (clusterStart.textPosition > run.textStart)
                {
                    glyphStart = GetClusterGlyphStart(ref clusterStart);
                }
                if (clusterWsEnd.textPosition < run.textStart + run.textLength)
                {
                    glyphEnd = GetClusterGlyphStart(ref clusterWsEnd);
                }
                if ((glyphStart >= glyphEnd) || (run.script.Shapes == ScriptShapes.NoVisual))
                {
                    // The itemizer told us not to draw this character,
                    // either because it was a formatting, control, or other hidden character.
                    continue;
                }

                // The run width is needed to know how far to move forward,
                // and to flip the origin for right-to-left text.
                float runWidth = GetClusterRangeWidth(
                                    glyphStart - justificationGlyphStart,
                                    glyphEnd - justificationGlyphStart,
                                    justifiedAdvances.ToArray()
                                    );

                // Flush this glyph run.
                //int glyphCount = glyphEnd - glyphStart;
                int glyphCount = justifiedAdvances.Count;
                short[] call_glyphIndices_ = new short[glyphCount];
                Array.Copy(glyphIndices_, glyphStart, call_glyphIndices_, 0, call_glyphIndices_.Length);
                float[] call_justifiedAdvances = new float[justifiedAdvances.Count - (glyphStart - justificationGlyphStart)];
                justifiedAdvances.CopyTo(glyphStart - justificationGlyphStart, call_justifiedAdvances, 0, call_justifiedAdvances.Length);
                GlyphOffset[] call_glyphOffsets_ = new GlyphOffset[glyphCount];
                Array.Copy(glyphOffsets_, glyphStart, call_glyphOffsets_, 0, call_glyphOffsets_.Length);
                flowSink.SetGlyphRun(
                    (run.bidiLevel % 2 != 0) ? (x + runWidth) : (x), // origin starts from right if RTL
                    y,
                    glyphCount,
                    call_glyphIndices_,
                    call_justifiedAdvances,
                    call_glyphOffsets_,
                    fontFace_,
                    fontEmSize_,
                    run.bidiLevel,
                    run.isSideways
                    );

                x += runWidth;
            }
            return true;
        }
Ejemplo n.º 10
0
        protected void ShapeGlyphRun(TextAnalyzer textAnalyzer, int runIndex, ref int glyphStart)
        {
            // Shapes a single run of text into glyphs.
            // Alternately, you could iteratively interleave shaping and line
            // breaking to reduce the number glyphs held onto at once. It's simpler
            // for this demostration to just do shaping and line breaking as two
            // separate processes, but realize that this does have the consequence that
            // certain advanced fonts containing line specific features (like Gabriola)
            // will shape as if the line is not broken.

            Run run = runs_[runIndex];
            int textStart = run.textStart;
            int textLength = run.textLength;
            int maxGlyphCount = glyphIndices_.Length - glyphStart;
            int actualGlyphCount = 0;

            run.glyphStart = glyphStart;
            run.glyphCount = 0;

            if (textLength == 0)
                return;// Nothing to do..

            // Allocate space for shaping to fill with glyphs and other information,
            // with about as many glyphs as there are text characters. We'll actually
            // need more glyphs than codepoints if they are decomposed into separate
            // glyphs, or fewer glyphs than codepoints if multiple are substituted
            // into a single glyph. In any case, the shaping process will need some
            // room to apply those rules to even make that determintation.

            if (textLength > maxGlyphCount)
            {
                maxGlyphCount = EstimateGlyphCount(textLength);
                int totalGlyphsArrayCount = glyphStart + maxGlyphCount;
                short[] Resized_glyphIndices_ = new short[totalGlyphsArrayCount];
                glyphIndices_.CopyTo(Resized_glyphIndices_, 0);
                glyphIndices_ = Resized_glyphIndices_;
            }


            ShapingTextProperties[] textProps = new ShapingTextProperties[textLength];
            ShapingGlyphProperties[] glyphProps = new ShapingGlyphProperties[maxGlyphCount];

            // Get the glyphs from the text, retrying if needed.
            int tries = 0;
            while (tries < 2)// We'll give it two chances.
            {
                short[] call_glyphClusters_ = new short[glyphClusters_.Length - textStart];
                short[] call_glyphIndices_ = new short[glyphIndices_.Length - glyphStart];

                bool isDone = false;
                try
                {
                    textAnalyzer.GetGlyphs(
                        text_.Substring(textStart, textLength),
                        textLength,
                        fontFace_,
                        run.isSideways,
                        (run.bidiLevel % 2 == 1),
                        run.script,
                        localName_,
                        run.isNumberSubstituted ? numberSubstitution_ : null,
                        null,
                        null,
                        maxGlyphCount,
                        call_glyphClusters_,
                        textProps,
                        call_glyphIndices_,
                        glyphProps,
                        out actualGlyphCount);
                    Array.Copy(call_glyphClusters_, 0, glyphClusters_, textStart, call_glyphClusters_.Length);
                    Array.Copy(call_glyphIndices_, 0, glyphIndices_, glyphStart, call_glyphIndices_.Length);
                    isDone = true;
                }
                finally
                {
                    tries++;
                    // Try again using a larger buffer.
                    maxGlyphCount = EstimateGlyphCount(maxGlyphCount);
                    int totalGlyphsArrayCount = glyphStart + maxGlyphCount;

                    glyphProps = new ShapingGlyphProperties[maxGlyphCount];
                    glyphIndices_ = new short[totalGlyphsArrayCount];
                }
                if (isDone)
                    break;
            }

            // Get the placement of the all the glyphs.
            if (glyphAdvances_.Length < glyphStart + actualGlyphCount)
            {
                float[] Resized_glyphAdvances_ = new float[glyphStart + actualGlyphCount];
                glyphAdvances_.CopyTo(Resized_glyphAdvances_, 0);
                glyphAdvances_ = Resized_glyphAdvances_;
            }
            if (glyphOffsets_.Length < glyphStart + actualGlyphCount)
            {
                GlyphOffset[] Resized_glyphOffsets_ = new GlyphOffset[glyphStart + actualGlyphCount];
                glyphOffsets_.CopyTo(Resized_glyphOffsets_, 0);
                glyphOffsets_ = Resized_glyphOffsets_;
            }

            short[] call2_glyphClusters_ = new short[glyphClusters_.Length - textStart];
            Array.Copy(glyphClusters_, textStart, call2_glyphClusters_, 0, call2_glyphClusters_.Length);
            short[] call2_glyphIndices_ = new short[glyphIndices_.Length - glyphStart];
            Array.Copy(glyphIndices_, glyphStart, call2_glyphIndices_, 0, call2_glyphIndices_.Length);
            float[] call2_glyphAdvances_ = new float[glyphAdvances_.Length - glyphStart];
            Array.Copy(glyphAdvances_, glyphStart, call2_glyphAdvances_, 0, call2_glyphAdvances_.Length);
            GlyphOffset[] call2_glyphOffsets_ = new GlyphOffset[glyphOffsets_.Length - glyphStart];
            Array.Copy(glyphOffsets_, glyphStart, call2_glyphOffsets_, 0, call2_glyphOffsets_.Length);

            textAnalyzer.GetGlyphPlacements(
                text_.Substring(textStart, textLength),
                call2_glyphClusters_,
                textProps,
                textLength,
                call2_glyphIndices_,
                glyphProps,
                actualGlyphCount,
                fontFace_,
                fontEmSize_,
                run.isSideways,
                run.bidiLevel % 2 == 1,
                run.script,
                localName_,
                null,
                null,
                call2_glyphAdvances_,
                call2_glyphOffsets_);
            //call2_glyphClusters_.CopyTo(glyphClusters_, textStart);
            call2_glyphAdvances_.CopyTo(glyphAdvances_, glyphStart);
            call2_glyphOffsets_.CopyTo(glyphOffsets_, glyphStart);

            // Certain fonts, like Batang, contain glyphs for hidden control
            // and formatting characters. So we'll want to explicitly force their
            // advance to zero.
            if (run.script.Shapes == ScriptShapes.NoVisual)
            {
                for (int i = glyphStart; i < glyphStart + actualGlyphCount; i++)
                    glyphAdvances_[i] = 0;
            }

            // Set the final glyph count of this run and advance the starting glyph.
            run.glyphCount = actualGlyphCount;
            runs_[runIndex] = run;
            glyphStart += actualGlyphCount;
        }
Ejemplo n.º 11
0
        protected void ShapeGlyphRuns(TextAnalyzer textAnalyzer)
        {
            // Shapes all the glyph runs in the layout.

            // Estimate the maximum number of glyph indices needed to hold a string.
            int estimatedGlyphCount = EstimateGlyphCount(text_.Length);

            glyphIndices_ = new short[estimatedGlyphCount];
            glyphOffsets_ = new GlyphOffset[estimatedGlyphCount];
            glyphAdvances_ = new float[estimatedGlyphCount];
            glyphClusters_ = new short[text_.Length];

            int glyphStart = 0;

            // Shape each run separately. This is needed whenever script, locale,
            // or reading direction changes.
            for (int runIndex = 0; runIndex < runs_.Length; runIndex++)
            {
                ShapeGlyphRun(textAnalyzer, runIndex, ref glyphStart);
            }

            short[] resized_glyphIndices_ = new short[glyphStart];
            Array.Copy(glyphIndices_, 0, resized_glyphIndices_, 0, glyphStart);
            glyphIndices_ = resized_glyphIndices_;

            GlyphOffset[] resized_glyphOffsets_ = new GlyphOffset[glyphStart];
            Array.Copy(glyphOffsets_, 0, resized_glyphOffsets_, 0, glyphStart);
            glyphOffsets_ = resized_glyphOffsets_;

            float[] resized_glyphAdvances_ = new float[glyphStart];
            Array.Copy(glyphAdvances_, 0, resized_glyphAdvances_, 0, glyphStart);
            glyphAdvances_ = resized_glyphAdvances_;

        }
        private unsafe GlyphRun ComputeShapedGlyphRun(
            LSRun                   lsrun,                      // ls run
            TextFormatterImp        textFormatterImp,           // The TextFormatter Implementation
            bool                    originProvided,             // flag indicate whether the origin of the run is provided                        
            LSPOINT                 lsrunOrigin,                // physical start of the run
            int                     charCount,                  // characters count
            char                    *pwchText,                  // characters for the GlyphRun
            ushort                  *puClusterMap,              // cluster map
            int                     glyphCount,                 // glyph count
            ushort                  *puGlyphs,                  // glyph indices
            int                     *piJustifiedGlyphAdvances,  // glyph advances
            GlyphOffset             *piiGlyphOffsets,           // glyph offsets
            bool                    justify
            )
        {
            TextMetrics.FullTextLine currentLine = Draw.CurrentLine;

            Point runOrigin = new Point();
            int nominalX = 0;
            int nominalY = 0;

            if (originProvided)
            {   
                if (currentLine.RightToLeft)
                {





                    lsrunOrigin.x = -lsrunOrigin.x;
                }

                if (textFormatterImp.TextFormattingMode == TextFormattingMode.Display && justify)
                {
                    LSRun.UVToNominalXY(
                        Draw.LineOrigin,
                        Draw.VectorToLineOrigin,
                        currentLine.LSLineUToParagraphU(lsrunOrigin.x),
                        lsrunOrigin.y + lsrun.BaselineMoveOffset,
                        currentLine,
                        out nominalX,
                        out nominalY
                        );
                }
                else
                {
                    runOrigin = LSRun.UVToXY(
                        Draw.LineOrigin,
                        Draw.VectorToLineOrigin,
                        currentLine.LSLineUToParagraphU(lsrunOrigin.x),
                        lsrunOrigin.y + lsrun.BaselineMoveOffset,
                        currentLine
                        );
                }
            }





            char[] charString = new char[charCount];
            ushort[] clusterMap = new ushort[charCount];

            for (int i = 0; i < charCount; i++)
            {
                charString[i] = pwchText[i];
                clusterMap[i] = puClusterMap[i];
            }

            ushort[] glyphIndices = new ushort[glyphCount];            
            IList<double> glyphAdvances;
            IList<Point> glyphOffsets;

            bool isRightToLeft = (lsrun.BidiLevel & 1) != 0;

            if (textFormatterImp.TextFormattingMode == TextFormattingMode.Ideal)
            {
                glyphAdvances = new ThousandthOfEmRealDoubles(textFormatterImp.IdealToReal(lsrun.EmSize), glyphCount);
                glyphOffsets = new ThousandthOfEmRealPoints(textFormatterImp.IdealToReal(lsrun.EmSize), glyphCount);

                for (int i = 0; i < glyphCount; i++)
                {
                    glyphIndices[i] = puGlyphs[i];
                    glyphAdvances[i] = textFormatterImp.IdealToReal(piJustifiedGlyphAdvances[i]);
                    glyphOffsets[i] = new Point(
                        textFormatterImp.IdealToReal(piiGlyphOffsets[i].du),
                        textFormatterImp.IdealToReal(piiGlyphOffsets[i].dv)
                        );
                }
            }
            else
            {
                if (justify)
                {
                    AdjustMetricsForDisplayModeJustifiedText(
                        pwchText,
                        piJustifiedGlyphAdvances,
                        glyphCount,
                        isRightToLeft,
                        nominalX,
                        nominalY,
                        out runOrigin,
                        out glyphAdvances
                        );
                }
                else
                {
                    glyphAdvances = new List<double>(glyphCount);
                    for (int i = 0; i < glyphCount; i++)
                    {
                        glyphAdvances.Add(textFormatterImp.IdealToReal(piJustifiedGlyphAdvances[i]));
                    }
                }
                glyphOffsets  = new List<Point>(glyphCount);
                for (int i = 0; i < glyphCount; i++)
                {
                    glyphIndices[i] = puGlyphs[i];
                    glyphOffsets.Add(new Point(
                            textFormatterImp.IdealToReal(piiGlyphOffsets[i].du),
                            textFormatterImp.IdealToReal(piiGlyphOffsets[i].dv)
                            ));
                }
            }

#if CHECK_GLYPHS
            if (   lsrun._glyphs != null
                && glyphCount <= lsrun._glyphs.Length)
            {
                for (int i = 0; i < glyphCount; i++)
                {
                    Debug.Assert(glyphIndices[i] == lsrun._glyphs[i], "Corrupted glyphs");
                }
            }
#endif            

            GlyphRun glyphRun = lsrun.Shapeable.ComputeShapedGlyphRun(
                runOrigin,
                charString, 
                clusterMap, 
                glyphIndices, 
                glyphAdvances, 
                glyphOffsets,
                isRightToLeft, 
                false   // no sideway support yet
                );

            return glyphRun;
        }
        internal unsafe LsErr EnumText(        
            IntPtr                      pols,                           // ls context
            Plsrun                      plsrun,                         // plsrun
            int                         cpFirst,                        // first cp of the ls dnode
            int                         dcp,                            // dcp of the dnode
            char                        *pwchText,                      // characters for glyph run
            int                         cchText,                        // length of characters 
            LsTFlow                     lstFlow,                        // flow direction
            int                         fReverseOrder,                  // flag for reverse order enumeration
            int                         fGeometryProvided,              // flag for providing geometry 
            ref LSPOINT                 pptStart,                       // [in] logical start of the run
            ref LsHeights               pheights,                       // [in] height (iff geometryProvided)
            int                         dupRun,                         // width of the run
            int                         glyphBaseRun,                   // flag for glyph based run
            int                         *piCharAdvances,                // character advance widths (iff !glyphBaseRun)
            ushort                      *puClusterMap,                  // cluster map (iff glyphBaseRun)
            ushort                      *characterProperties,           // character properties (iff glyphBaseRun)
            ushort                      *puGlyphs,                      // glyph indices (iff glyphBaseRun)
            int                         *piJustifiedGlyphAdvances,      // glyph advances (iff glyphBaseRun)
            GlyphOffset                 *piiGlyphOffsets,               // glyph offsets (iff glyphBaseRun)
            uint                        *piGlyphProperties,             // glyph properties (iff glyphProperties)
            int                         glyphCount                      // glyph count
            )
        {
            Debug.Assert(fGeometryProvided == 0, "Line enumeration doesn't need geometry information");

            if (cpFirst < 0)
            {

                return LsErr.None;
            }
            
            LsErr lserr = LsErr.None;
            LSRun lsrun = null;

            try                 
            {   
                TextMetrics.FullTextLine currentLine = Draw.CurrentLine;
                lsrun = currentLine.GetRun(plsrun);
                GlyphRun glyphRun = null;
                if (glyphBaseRun != 0)
                {

                    if (glyphCount > 0)
                    {

                        glyphRun = ComputeShapedGlyphRun(
                            lsrun, 
                            currentLine.Formatter,
                            false,      // glyph run origin not provided
                            pptStart, 
                            cchText, 
                            pwchText, 
                            puClusterMap, 
                            glyphCount, 
                            puGlyphs,
                            piJustifiedGlyphAdvances, 
                            piiGlyphOffsets,
                            currentLine.IsJustified
                            );
                    }
                }
                else if (cchText > 0)
                {

                    dupRun = 0;
                    for (int i = 0; i < cchText; i++)
                    {
                        dupRun += piCharAdvances[i];
                    }

                    

                    glyphRun = ComputeUnshapedGlyphRun(
                        lsrun, 
                        lstFlow, 
                        currentLine.Formatter,
                        false,      // glyph run origin not provided at enumeration
                        pptStart, 
                        dupRun, 
                        cchText, 
                        pwchText, 
                        piCharAdvances,
                        currentLine.IsJustified
                        );
                }
                
                if (glyphRun != null)
                {





                    IndexedGlyphRuns.Add(
                        new IndexedGlyphRun(
                           currentLine.GetExternalCp(cpFirst),
                           dcp,
                           glyphRun
                           )
                    );
                }                   
            }
            catch (Exception e)
            {
                SaveException(e, plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            catch
            {
                SaveNonCLSException("EnumText", plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            
            return lserr;
        }
        internal unsafe LsErr DrawGlyphs(
            IntPtr                      pols,                       // Line Layout context
            Plsrun                      plsrun,                     // plsrun
            char*                       pwchText,                   // character string
            ushort*                     puClusterMap,               // character-to-cluster map
            ushort*                     puCharProperties,           // character properties
            int                         charCount,                  // character count
            ushort*                     puGlyphs,                   // glyph indices
            int*                        piJustifiedGlyphAdvances,   // justified glyph advances
            int*                        piGlyphAdvances,            // original ideal glyph advances
            GlyphOffset*                piiGlyphOffsets,            // glyph offsets
            uint*                       piGlyphProperties,          // glyph properties
            LsExpType*                  plsExpType,                 // glyph expansion types
            int                         glyphCount,                 // glyph count
            LsTFlow                     textFlow,                   // text flow
            uint                        displayMode,                // draw transparent or opaque
            ref LSPOINT                 ptRun,                      // [in] display position (at baseline)
            ref LsHeights               lsHeights,                  // [in] run height metrics
            int                         runWidth,                   // run overall advance width
            ref LSRECT                  clippingRect                // [in] clipping rectangle if any applied
            )
        {
            LsErr lserr = LsErr.None;
            LSRun lsrun = null;

            try
            {
                TextMetrics.FullTextLine currentLine = Draw.CurrentLine;
                lsrun = currentLine.GetRun(plsrun);

                Debug.Assert(TextStore.IsContent(plsrun) && lsrun.Shapeable != null);

                GlyphRun glyphRun = ComputeShapedGlyphRun(
                    lsrun,
                    currentLine.Formatter,
                    true,           // origin of the glyph run provided at drawing time
                    ptRun,
                    charCount,
                    pwchText,
                    puClusterMap,
                    glyphCount,
                    puGlyphs,
                    piJustifiedGlyphAdvances,
                    piiGlyphOffsets,
                    currentLine.IsJustified
                    );

                if (glyphRun != null)
                {
                    DrawingContext drawingContext = Draw.DrawingContext;

                    Draw.SetGuidelineY(glyphRun.BaselineOrigin.Y);

                    try 
                    {
                        _boundingBox.Union(
                            lsrun.DrawGlyphRun(
                                drawingContext, 
                                null,     // draw with the run's foreground
                                glyphRun
                                )
                            );
                    }
                    finally 
                    {
                        Draw.UnsetGuidelineY();
                    }

                }
            }
            catch (Exception e)
            {
                SaveException(e, plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            catch
            {
                SaveNonCLSException("DrawGlyphs", plsrun, lsrun);
                lserr = LsErr.ClientAbort;
            }
            return lserr;
        }
        internal unsafe LsErr GetGlyphPositions(
            IntPtr                      pols,               // Line Layout context
            IntPtr*                     plsplsruns,         // array of plsruns
            int*                        pcchPlsrun,         // array of character count per run
            int                         plsrunCount,        // number of runs
            LsDevice                    device,             // on reference or presentation device
            char*                       pwchText,           // character string
            ushort*                     puClusterMap,       // character-to-glyph cluster map
            ushort*                     puCharProperties,   // character properties
            int                         cchText,            // character count
            ushort*                     puGlyphs,           // glyph indices
            uint*                       piGlyphProperties,  // glyph properties
            int                         glyphCount,         // glyph count
            LsTFlow                     textFlow,           // text flow direction
            int*                        piGlyphAdvances,    // [out] glyph advances
            GlyphOffset*                piiGlyphOffsets     // [out] glyph offsets
            )
        {            
            LsErr lserr = LsErr.None;
            LSRun lsrunFirst = null;

            try
            {
                LSRun[] lsruns = RemapLSRuns(plsplsruns, plsrunCount);
                lsrunFirst = lsruns[0];

                bool isRightToLeft = ((lsrunFirst.BidiLevel & 1) != 0);

                GlyphOffset[] glyphOffset;

                GlyphTypeface glyphTypeface = lsrunFirst.Shapeable.GlyphTypeFace;

                DWriteFontFeature[][] fontFeatures;
                uint[] fontFeatureRanges;
                LSRun.CompileFeatureSet(lsruns, pcchPlsrun, checked((uint)cchText), out fontFeatures, out fontFeatureRanges);

                
                FullText.Formatter.TextAnalyzer.GetGlyphPlacements(
                    (ushort*)pwchText,
                    puClusterMap,
                    (ushort*)puCharProperties,
                    (uint)cchText,
                    puGlyphs,
                    piGlyphProperties,
                    (uint)glyphCount,
                    glyphTypeface.FontDWrite,
                    lsrunFirst.Shapeable.EmSize,
                    TextFormatterImp.ToIdeal,
                    false,
                    isRightToLeft,
                    lsrunFirst.RunProp.CultureInfo,
                    fontFeatures,
                    fontFeatureRanges,
                    FullText.TextFormattingMode,
                    lsrunFirst.Shapeable.ItemProps,
                    Util.PixelsPerDip,
                    piGlyphAdvances,
                    out glyphOffset
                    );

                for (int i = 0; i < glyphCount; ++i)
                {
                    piiGlyphOffsets[i].du = glyphOffset[i].du;
                    piiGlyphOffsets[i].dv = glyphOffset[i].dv;
                }                
                 
            }
            catch (Exception e)
            {
                SaveException(e, (Plsrun)(plsplsruns[0]), lsrunFirst);
                lserr = LsErr.ClientAbort;
            }
            catch
            {
                SaveNonCLSException("GetGlyphPositions", (Plsrun)(plsplsruns[0]), lsrunFirst);
                lserr = LsErr.ClientAbort;
            }
            return lserr;
            
        }
Ejemplo n.º 16
0
        /// <summary>
        ///   This method retrieves the geometry for a formatted cluster.
        /// </summary>
        /// <param name="clusterIndex"> This parameter indicates the formatted cluster index. </param>
        /// <param name="bidiLevel"> This parameter indicates the bidi level of the cluster. </param>
        /// <param name="font"> This parameter references the font for the cluster. </param>
        /// <param name="input"> This parameter references the formatted text output. </param>
        /// <returns> This method returns the geometry for the formatted cluster if available; otherwise, this method returns <c>null</c> . </returns>
        public Shape Retrieve(
			IndexedRange glyphRange,
			bool isVertical,
			bool isRightToLeft,
			FontHandle font,
			List<Formatting.TextShaper.Glyph> glyphs)
        {
            ResizeInternalBuffers(glyphRange.Length);

            int index = 0;

            foreach(int itemIndex in glyphRange)
            {
                _GlyphAdvances[index] = glyphs[itemIndex].Advance;
                _GlyphIndices[index] = glyphs[itemIndex].Index;

                _GlyphOffsets[index] = new GlyphOffset
                {
                    AdvanceOffset = glyphs[itemIndex].Offset.Width,
                    AscenderOffset = glyphs[itemIndex].Offset.Height
                };

                ++index;
            }

            TextGeometryKey key;

            key.Advances = _GlyphAdvances;
            key.Indices = _GlyphIndices;
            key.Offsets = _GlyphOffsets;

            Dictionary<TextGeometryKey, Shape> cacheForFont;

            if(_Cache.TryGetValue(font, out cacheForFont))
            {
                Shape result;

                if(cacheForFont.TryGetValue(key, out result))
                {
                    return result;
                }
            }
            else
            {
                cacheForFont = new Dictionary<TextGeometryKey, Shape>();

                _Cache.Add(font, cacheForFont);
            }

            Shape geometry = _Sink.CreateGeometry(
                key, isRightToLeft, isVertical, font);

            TextGeometryKey newKey;

            newKey.Advances = (float[])key.Advances.Clone();
            newKey.Indices = (short[])key.Indices.Clone();
            newKey.Offsets = (GlyphOffset[])key.Offsets.Clone();

            cacheForFont.Add(newKey, geometry);

            return geometry;
        }