internal FormatSettings( TextFormatterImp formatter, TextSource textSource, TextRunCacheImp runCache, ParaProp pap, TextLineBreak previousLineBreak, bool isSingleLineFormatting, TextFormattingMode textFormattingMode, bool isSideways ) { _isSideways = isSideways; _textFormattingMode = textFormattingMode; _formatter = formatter; _textSource = textSource; _runCache = runCache; _pap = pap; _digitState = new DigitState(); _previousLineBreak = previousLineBreak; _maxLineWidth = Constants.IdealInfiniteWidth; if (isSingleLineFormatting) { // Apply text indent on each line in single line mode _textIndent = _pap.Indent; } }
/// <summary> /// Create a fulltext state object from formatting info for subsequent fulltext formatting /// e.g. fulltext line, mix/max computation, potential breakpoints for optimal paragraph formatting. /// </summary> internal static FullTextState Create( FormatSettings settings, int cpFirst, int finiteFormatWidth ) { // prepare text stores TextStore store = new TextStore( settings, cpFirst, 0, settings.GetFormatWidth(finiteFormatWidth) ); ParaProp pap = settings.Pap; TextStore markerStore = null; if (pap.FirstLineInParagraph && pap.TextMarkerProperties != null && pap.TextMarkerProperties.TextSource != null) { // create text store specifically for marker markerStore = new TextStore( // create specialized settings for marker store e.g. with marker text source new FormatSettings( settings.Formatter, pap.TextMarkerProperties.TextSource, new TextRunCacheImp(), // no cross-call run cache available for marker store pap, // marker by default use the same paragraph properties null, // not consider previousLineBreak true, // isSingleLineFormatting settings.TextFormattingMode, settings.IsSideways ), 0, // marker store always started with cp == 0 TextStore.LscpFirstMarker, // first lscp value for marker text Constants.IdealInfiniteWidth // formatWidth ); } // construct a new fulltext state object return(new FullTextState(store, markerStore, settings.IsSideways)); }
/// <summary> /// format text line using LS /// </summary> /// <param name="fullText">state of the full text backing store</param> /// <param name="cpFirst">first cp to format</param> /// <param name="lineLength">character length of the line</param> /// <param name="formatWidth">width used to format</param> /// <param name="finiteFormatWidth">width used to detect overflowing of format result</param> /// <param name="paragraphWidth">paragraph width</param> /// <param name="lineFlags">line formatting control flags</param> /// <param name="collapsingSymbol">line end collapsing symbol</param> private void FormatLine( FullTextState fullText, int cpFirst, int lineLength, int formatWidth, int finiteFormatWidth, int paragraphWidth, LineFlags lineFlags, FormattedTextSymbols collapsingSymbol ) { _fullText = fullText; _paragraphWidth = paragraphWidth; TextStore store = fullText.TextStore; FormatSettings settings = store.Settings; ParaProp pap = settings.Pap; _paragraphTextDecorations = pap.TextDecorations; if (_paragraphTextDecorations != null) { if (_paragraphTextDecorations.Count != 0) { _defaultTextDecorationsBrush = pap.DefaultTextDecorationsBrush; } else { _paragraphTextDecorations = null; } } _metrics = GetLineMetrics(fullText, cpFirst, lineLength, formatWidth, finiteFormatWidth, paragraphWidth, lineFlags, collapsingSymbol); lineLength = _metrics._cchLength; if (lineLength > _metrics._cchNewline) { var lineBreakpoints = store.FindLineBreakpoints(cpFirst, lineLength); // check for line wrap if (pap.Wrap && _metrics._textStart + _metrics._textWidthAtTrailing > finiteFormatWidth) { for (int i = lineLength - 1; i > 0; i--) { if (lineBreakpoints.GetBreakConditionBefore(i + cpFirst) == DWriteBreakCondition.CanBreak) { var trailingWhitespace = lineBreakpoints.WhitespaceLengthBefore(i + cpFirst); var trimmedMetrics = GetLineMetrics(fullText, cpFirst, i - trailingWhitespace, formatWidth, finiteFormatWidth, paragraphWidth, lineFlags, collapsingSymbol); if (trimmedMetrics._textStart + trimmedMetrics._textWidthAtTrailing <= finiteFormatWidth) { _metrics = GetLineMetrics(fullText, cpFirst, i, formatWidth, finiteFormatWidth, paragraphWidth, lineFlags, collapsingSymbol); _metrics._cchTrailing = trailingWhitespace; _metrics._textWidthAtTrailing = trimmedMetrics._textWidth; break; } } } } } if (collapsingSymbol == null) { // overflow detection for potential collapsible line if (_metrics._textStart + _metrics._textWidthAtTrailing > finiteFormatWidth) { bool hasOverflowed = true; if (_textFormattingMode == TextFormattingMode.Display) { // apply display-mode rounding before checking for overflow double realWidth = Width; double realFormatWidth = _metrics._formatter.IdealToReal(finiteFormatWidth, PixelsPerDip); hasOverflowed = (TextFormatterImp.CompareReal(realWidth, realFormatWidth, PixelsPerDip, _textFormattingMode) > 0); } if (hasOverflowed) { // line has overflowed _statusFlags |= StatusFlags.HasOverflowed; } } } }
// For a given line length, return TextMetrics for as manu characters fit in the width private TextMetrics GetLineMetrics( FullTextState fullText, int cpFirst, int lineLength, int formatWidth, int finiteFormatWidth, int paragraphWidth, LineFlags lineFlags, FormattedTextSymbols collapsingSymbol ) { TextMetrics result = new TextMetrics(); result._formatter = fullText.Formatter; Debug.Assert(result._formatter != null); TextStore store = fullText.TextStore; FormatSettings settings = store.Settings; ParaProp pap = settings.Pap; var pixelsPerDip = settings.TextSource.PixelsPerDip; int pos = cpFirst; int content_ascent = 0; int content_descent = 0; while (lineLength <= 0 || cpFirst + lineLength > pos) { TextRun textRun; int runLength; CharacterBufferRange chars = settings.FetchTextRun(pos, cpFirst, out textRun, out runLength); if (lineLength > 0 && pos + runLength > cpFirst + lineLength) { runLength = cpFirst + lineLength - pos; chars = new CharacterBufferRange(chars, 0, runLength); } if (textRun is TextEndOfParagraph || textRun is TextEndOfLine) { pos += runLength; result._cchNewline = runLength; break; } TextMetrics runMetrics = GetRunMetrics(fullText, textRun, chars); if (content_ascent < runMetrics._textAscent) { content_ascent = runMetrics._textAscent; } if (content_descent < runMetrics._textHeight - runMetrics._textAscent) { content_descent = runMetrics._textHeight - runMetrics._textAscent; } result._textWidth += runMetrics._textWidth; pos += runLength; } result._pixelsPerDip = pixelsPerDip; result._cchLength = pos - cpFirst; result._textWidthAtTrailing = result._textWidth; // will be set later by FormatLine if (pap.LineHeight > 0) { // Host specifies line height, honor it. result._height = pap.LineHeight; result._baselineOffset = (int)Math.Round( result._height * pap.DefaultTypeface.Baseline(pap.EmSize, Constants.DefaultIdealToReal, pixelsPerDip, _textFormattingMode) / pap.DefaultTypeface.LineSpacing(pap.EmSize, Constants.DefaultIdealToReal, pixelsPerDip, _textFormattingMode) ); } if (content_ascent > 0) { result._textAscent = content_ascent; result._textHeight = content_ascent + content_descent; // TODO: VerticalAdjust } else { // Line is empty so text height and text baseline are based on the default typeface; // it doesn't make sense even for an emtpy line to have zero text height result._textAscent = (int)Math.Round(pap.DefaultTypeface.Baseline(pap.EmSize, Constants.DefaultIdealToReal, pixelsPerDip, _textFormattingMode)); result._textHeight = (int)Math.Round(pap.DefaultTypeface.LineSpacing(pap.EmSize, Constants.DefaultIdealToReal, pixelsPerDip, _textFormattingMode)); } if (result._height <= 0) { result._height = result._textHeight; result._baselineOffset = result._textAscent; } return(result); }