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 }); }