public TextLine FormatLine(TextSource textSource, int firstCharIndex, double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak)
        {
            var runs = new List <Tuple <TextRun, GlyphRun, int, double> >();

            int    index = firstCharIndex;
            double x = paragraphProperties.Indent, height = 0, baseline = 0;
            double trailWhitespaceWidth = 0;

            while (true)
            {
                var run       = textSource.GetTextRun(index);
                var textProps = run.Properties ?? paragraphProperties.DefaultTextRunProperties;
                var fontSize  = textProps.FontRenderingEmSize;
                var len       = run.Length;
                if (textProps != null)
                {
                    height   = Math.Max(height, (int)(textProps.Typeface.FontFamily.LineSpacing * fontSize));
                    baseline = Math.Max(baseline, (int)(textProps.Typeface.FontFamily.Baseline * fontSize));
                }

                if (run is TextEndOfLine || run == null)
                {
                    index += len;
                    runs.Add(Tuple.Create(run, (GlyphRun)null, 0, 0.0));
                    break;
                }
                else if (run is TextCharacters)
                {
                    var chrs       = (TextCharacters)run;
                    var charBuf    = getCharBuf(chrs.CharacterBufferReference);
                    var charOffset = getCharOffset(chrs.CharacterBufferReference);

                    GlyphTypeface gl;
                    if (!textProps.Typeface.TryGetGlyphTypeface(out gl))
                    {
                        throw new Exception("GlyphTypeface does not exists for font '" + textProps.Typeface.FontFamily + "'.");
                    }

                    ushort[] glyphIndexes  = new ushort[len];
                    double[] advanceWidths = new double[len];

                    double totalWidth      = 0;
                    int    trailWhitespace = 0;
                    trailWhitespaceWidth = 0;
                    for (int n = 0; n < len; n++)
                    {
                        var c = charBuf[charOffset + n];

                        ushort glyphIndex;
                        double width;

                        if (c == '\t')
                        {
                            glyphIndex = gl.CharacterToGlyphMap[' '];
                            width      = paragraphProperties.DefaultIncrementalTab - x % paragraphProperties.DefaultIncrementalTab;
                        }
                        else
                        {
                            if (!gl.CharacterToGlyphMap.TryGetValue(c, out glyphIndex))
                            {
                                glyphIndex = gl.CharacterToGlyphMap['?'];
                            }
                            width = gl.AdvanceWidths[glyphIndex] * fontSize;
                        }

                        glyphIndexes[n]  = glyphIndex;
                        advanceWidths[n] = width;

                        if (char.IsWhiteSpace(c))
                        {
                            trailWhitespace++;
                            trailWhitespaceWidth += width;
                        }
                        else
                        {
                            totalWidth          += trailWhitespaceWidth + width;
                            trailWhitespaceWidth = 0;
                            trailWhitespace      = 0;
                        }
                    }
                    var origin = new Point(x, 0);

                    var glyphRun = new GlyphRun(
                        gl, 0, false, fontSize, glyphIndexes, origin, advanceWidths,
                        null, null, null, null, null, null);
                    runs.Add(Tuple.Create(run, glyphRun, trailWhitespace, trailWhitespaceWidth));

                    x += totalWidth + trailWhitespaceWidth;

                    index += len;
                }
                else if (run is TextEmbeddedObject)
                {
                    var obj     = (TextEmbeddedObject)run;
                    var metrics = obj.Format(paragraphWidth - x);
                    runs.Add(Tuple.Create(run, (GlyphRun)null, 0, metrics.Width));

                    height = Math.Max(height, obj.Format(paragraphWidth - x).Height);
                    x     += metrics.Width;

                    index += len;
                }
            }

            return(new GlyphRunLine {
                entries = runs.ToArray(),
                baseline = baseline,
                width = x - trailWhitespaceWidth,
                height = height,
                mode = mode
            });
        }