private unsafe GlyphRun ComputeUnshapedGlyphRun(
            LSRun               lsrun,              // LSrun used to shape the GlyphRun            
            LsTFlow             textFlow,           // flow direction
            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                 dupRun,             // width of the run
            int                 cchText,            // character count
            char                *pwchText,          // characters for display 
            int                 *piCharAdvances,    // character advance widths,
            bool                justify
            )
        {
            GlyphRun glyphRun = null;
            if (lsrun.Type == Plsrun.Text)
            {
                Debug.Assert(lsrun.Shapeable != null);
                Point runOrigin    = new Point();
                int nominalX = 0;
                int nominalY = 0;

                if (originProvided)
                {                   
                    TextMetrics.FullTextLine currentLine = Draw.CurrentLine;
                    
                    if (textFlow == LsTFlow.lstflowWS)
                    {
                        lsrunOrigin.x -= dupRun;
                    }

                    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[cchText];
                IList<double> charWidths;

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

                if (textFormatterImp.TextFormattingMode == TextFormattingMode.Ideal)
                {
                    charWidths = new ThousandthOfEmRealDoubles(textFormatterImp.IdealToReal(lsrun.EmSize), cchText);
                    for (int i = 0; i < cchText; i++)
                    {
                        charString[i] = pwchText[i];
                        charWidths[i] = textFormatterImp.IdealToReal(piCharAdvances[i]);
                    }
                }
                else
                {
                    if (justify)
                    {
                        AdjustMetricsForDisplayModeJustifiedText(
                            pwchText,
                            piCharAdvances,
                            cchText,
                            isRightToLeft,
                            nominalX,
                            nominalY,
                            out runOrigin,
                            out charWidths
                            );
                    }
                    else
                    {
                        charWidths = new List<double>(cchText);
                        for (int i = 0; i < cchText; i++)
                        {
                            charWidths.Add(textFormatterImp.IdealToReal(piCharAdvances[i]));
                        }
                    }
                    for (int i = 0; i < cchText; i++)
                    {
                        charString[i] = pwchText[i];
                    }
                }

                

                glyphRun = lsrun.Shapeable.ComputeUnshapedGlyphRun(
                    runOrigin,
                    charString,
                    charWidths
                    );
            }            

            return glyphRun;
        } 
        public override IEnumerable<IndexedGlyphRun> GetIndexedGlyphRuns()
        {
            List<IndexedGlyphRun> indexedGlyphRuns = new List<IndexedGlyphRun>(_runs.Length);

            // create each GlyphRun at Point(0, 0)
            Point start = new Point(0, 0);
            int currentCp = _cpFirst;

            foreach(SimpleRun run in _runs)
            {
                if (run.Length > 0 && !run.Ghost)
                {
                    IList<double> displayGlyphAdvances;

                    if (_settings.TextFormattingMode == TextFormattingMode.Ideal)
                    {
                        displayGlyphAdvances = new ThousandthOfEmRealDoubles(run.EmSize, run.NominalAdvances.Length);
                        for (int i = 0; i < displayGlyphAdvances.Count; i++)
                        {
                            // convert ideal glyph advance width to real width for displaying
                            displayGlyphAdvances[i] = _settings.Formatter.IdealToReal(run.NominalAdvances[i]);
                        }
                    }
                    else
                    {
                        displayGlyphAdvances = new List<double>(run.NominalAdvances.Length);
                        for (int i = 0; i < run.NominalAdvances.Length; i++)
                        {
                            // convert ideal glyph advance width to real width for displaying
                            displayGlyphAdvances.Add(_settings.Formatter.IdealToReal(run.NominalAdvances[i]));
                        }
                    }



                    GlyphTypeface glyphTypeface = run.Typeface.TryGetGlyphTypeface();
                    Invariant.Assert(glyphTypeface != null);

                    // this simple run has GlyphRun
                    GlyphRun glyphRun = glyphTypeface.ComputeUnshapedGlyphRun(
                        start,
                        new CharacterBufferRange(run.CharBufferReference, run.Length),
                        displayGlyphAdvances,
                        run.EmSize,
                        run.TextRun.Properties.FontHintingEmSize,
                        run.Typeface.NullFont,
                        CultureMapper.GetSpecificCulture(run.TextRun.Properties.CultureInfo),
                        null,   // device font name
                        _settings.TextFormattingMode
                        );

                    if (glyphRun != null)
                    {
                        indexedGlyphRuns.Add(
                            new IndexedGlyphRun(
                                currentCp,
                                run.Length,
                                glyphRun
                            )
                         );
                    }
                }

                currentCp += run.Length;
            }

            return indexedGlyphRuns;
        }
        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 Rect Draw(
            DrawingContext      drawingContext,
            double              x,
            double              y,
            bool                visiCodePath
            )
        {
            if (Length <= 0 || this.Ghost)
            {
                return Rect.Empty;  // nothing to draw
            }

            Brush foregroundBrush = TextRun.Properties.ForegroundBrush;

            if(visiCodePath && foregroundBrush is SolidColorBrush)
            {
                Color color = ((SolidColorBrush)foregroundBrush).Color;
                foregroundBrush = new SolidColorBrush(Color.FromArgb(
                    (byte)(color.A>>2), // * 0.25
                    color.R,
                    color.G,
                    color.B
                    ));
            }

            Rect inkBoundingBox;

            IList<double> displayGlyphAdvances;
            if (_textFormatterImp.TextFormattingMode == TextFormattingMode.Ideal)
            {
                displayGlyphAdvances = new ThousandthOfEmRealDoubles(EmSize, NominalAdvances.Length);
                for (int i = 0; i < displayGlyphAdvances.Count; i++)
                {
                    // convert ideal glyph advance width to real width for displaying.
                    displayGlyphAdvances[i] = _textFormatterImp.IdealToReal(NominalAdvances[i]);
                }
            }
            else
            {
                displayGlyphAdvances = new List<double>(NominalAdvances.Length);
                for (int i = 0; i < NominalAdvances.Length; i++)
                {
                    // convert ideal glyph advance width to real width for displaying.
                    displayGlyphAdvances.Add(_textFormatterImp.IdealToReal(NominalAdvances[i]));
                }
            }

            CharacterBufferRange charBufferRange = new CharacterBufferRange(CharBufferReference, Length);

            GlyphTypeface glyphTypeface = Typeface.TryGetGlyphTypeface();
            Invariant.Assert(glyphTypeface != null);

            GlyphRun glyphRun = glyphTypeface.ComputeUnshapedGlyphRun(
                new Point(x, y),
                charBufferRange,
                displayGlyphAdvances,
                EmSize,
                TextRun.Properties.FontHintingEmSize,
                Typeface.NullFont,
                CultureMapper.GetSpecificCulture(TextRun.Properties.CultureInfo),
                null,  // device font name
                _textFormatterImp.TextFormattingMode
              );

            if (glyphRun != null)
            {
                inkBoundingBox = glyphRun.ComputeInkBoundingBox();
            }
            else
            {
                inkBoundingBox = Rect.Empty;
            }

            if (!inkBoundingBox.IsEmpty)
            {
                // glyph run's ink bounding box is relative to its origin
                inkBoundingBox.X += glyphRun.BaselineOrigin.X;
                inkBoundingBox.Y += glyphRun.BaselineOrigin.Y;
            }

            if (drawingContext != null)
            {
                if (glyphRun != null)
                {
                    glyphRun.EmitBackground(drawingContext, TextRun.Properties.BackgroundBrush);
                    drawingContext.DrawGlyphRun(foregroundBrush, glyphRun);
                }


                // draw underline here
                if (Underline != null)
                {
                    // Determine number of characters to underline. We don't underline trailing spaces
                    // if the TrimTrailingUnderline flag is set.
                    int underlineLength = Length;
                    if (TrimTrailingUnderline)
                    {
                        while (underlineLength > 0 && IsSpace(charBufferRange[underlineLength - 1]))
                        {
                            --underlineLength;
                        }
                    }

                    // Determine the width of the underline.
                    double dxUnderline = 0;
                    for (int i = 0; i < underlineLength; ++i)
                    {
                        dxUnderline += _textFormatterImp.IdealToReal(NominalAdvances[i]);
                    }

                    // We know only TextDecoration.Underline will be handled in Simple Path.
                    double offset = -Typeface.UnderlinePosition * EmSize;
                    double penThickness = Typeface.UnderlineThickness * EmSize;

                    Point lineOrigin = new Point(x, y + offset);

                    Rect underlineRect = new Rect(
                            lineOrigin.X,
                            lineOrigin.Y - penThickness * 0.5,
                            dxUnderline,
                            penThickness
                        );

                    // Apply the pair of guidelines: one for baseline and another
                    // for top edge of undelining line. Both will be snapped to pixel grid.
                    // Guideline pairing algorithm detects the case when these two
                    // guidelines happen to be close to one another and provides
                    // synchronous snapping, so that the gap between baseline and
                    // undelining line does not depend on the position of text line.
                    drawingContext.PushGuidelineY2(y, lineOrigin.Y - penThickness * 0.5 - y);

                    try
                    {
                        drawingContext.DrawRectangle(
                            foregroundBrush,
                            null,               // pen
                            underlineRect
                            );
                    }
                    finally
                    {
                        drawingContext.Pop();
                    }

                    // underline pen thickness is always positive in fast path
                    inkBoundingBox.Union(
                        underlineRect
                        );
                }
            }

            return inkBoundingBox;
        }