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> /// Get distance from the start of main text to the end of marker /// </summary> /// <remarks> /// Positive distance is filtered out. Marker overlapping the main text is not supported. /// </remarks> internal int GetMainTextToMarkerIdealDistance() { if (_markerStore != null) { return(Math.Min(0, TextFormatterImp.RealToIdeal(_markerStore.Pap.TextMarkerProperties.Offset) - _store.Settings.TextIndent)); } return(0); }
/// <summary> /// Set tab stops /// </summary> internal void SetTabs(TextFormatterContext context) { unsafe { ParaProp pap = _store.Pap; FormatSettings settings = _store.Settings; // set up appropriate tab stops int incrementalTab = TextFormatterImp.RealToIdeal(pap.DefaultIncrementalTab); int lsTbdCount = pap.Tabs != null ? pap.Tabs.Count : 0; LsTbd[] lsTbds; if (_markerStore != null) { if (pap.Tabs != null && pap.Tabs.Count > 0) { lsTbdCount = pap.Tabs.Count + 1; lsTbds = new LsTbd[lsTbdCount]; lsTbds[0].ur = settings.TextIndent; // marker requires a tab stop at text start position fixed(LsTbd *plsTbds = &lsTbds[1]) { CreateLsTbds(pap, plsTbds, lsTbdCount - 1); context.SetTabs(incrementalTab, plsTbds - 1, lsTbdCount); } } else { LsTbd markerRequiredLsTbd = new LsTbd(); markerRequiredLsTbd.ur = settings.TextIndent; // marker requires a tab stop at text start position context.SetTabs(incrementalTab, &markerRequiredLsTbd, 1); } } else { if (pap.Tabs != null && pap.Tabs.Count > 0) { lsTbds = new LsTbd[lsTbdCount]; fixed(LsTbd *plsTbds = &lsTbds[0]) { CreateLsTbds(pap, plsTbds, lsTbdCount); context.SetTabs(incrementalTab, plsTbds, lsTbdCount); } } else { // work with only incremental tab context.SetTabs(incrementalTab, null, 0); } } } }
/// <summary> /// Constructing paragraph properties /// </summary> /// <param name="formatter">Text formatter</param> /// <param name="paragraphProperties">paragraph properties</param> /// <param name="optimalBreak">produce optimal break</param> internal ParaProp( TextFormatterImp formatter, TextParagraphProperties paragraphProperties, bool optimalBreak ) { _paragraphProperties = paragraphProperties; _emSize = TextFormatterImp.RealToIdeal(paragraphProperties.DefaultTextRunProperties.FontRenderingEmSize); _indent = TextFormatterImp.RealToIdeal(paragraphProperties.Indent); _paragraphIndent = TextFormatterImp.RealToIdeal(paragraphProperties.ParagraphIndent); _height = TextFormatterImp.RealToIdeal(paragraphProperties.LineHeight); if (_paragraphProperties.FlowDirection == FlowDirection.RightToLeft) { _statusFlags |= StatusFlags.Rtl; } if (optimalBreak) { _statusFlags |= StatusFlags.OptimalBreak; } }
/// <summary> /// Map a UV ideal coordinate to an XY ideal coordinate /// </summary> /// <param name="origin">line drawing origin</param> /// <param name="vectorToOrigin">vector to line origin UV</param> /// <param name="u">ideal distance in text flow direction</param> /// <param name="v">ideal distance in paragraph flow direction</param> /// <param name="line">container line</param> /// <param name="nominalX">ideal X origin</param> /// <param name="nominalY">ideal Y origin</param> internal static void UVToNominalXY( Point origin, Point vectorToOrigin, int u, int v, TextMetrics.FullTextLine line, out int nominalX, out int nominalY ) { origin.Y += vectorToOrigin.Y; if (line.RightToLeft) { nominalX = line.ParagraphWidth - u + TextFormatterImp.RealToIdeal(-vectorToOrigin.X + origin.X); } else { nominalX = u + TextFormatterImp.RealToIdeal(vectorToOrigin.X + origin.X); } nominalY = v + TextFormatterImp.RealToIdeal(origin.Y); }
/// <summary> /// Fill a fixed buffer of LsTbd with /// </summary> private unsafe void CreateLsTbds( ParaProp pap, LsTbd *plsTbds, int lsTbdCount ) { for (int i = 0; i < lsTbdCount; i++) { TextTabProperties tab = (TextTabProperties)pap.Tabs[i]; plsTbds[i].lskt = Convert.LsKTabFromTabAlignment(tab.Alignment); plsTbds[i].ur = TextFormatterImp.RealToIdeal(tab.Location); if (tab.TabLeader != 0) { // Note: LS does not currently support surrogate character as tab leader and aligning character plsTbds[i].wchTabLeader = (char)tab.TabLeader; // tab leader requires state at display time for tab leader width fetching _statusFlags |= StatusFlags.KeepState; } plsTbds[i].wchCharTab = (char)tab.AligningCharacter; } }
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; }
/// <summary> /// Create simple run of text, /// returning null if the specified text run cannot be correctly formatted as simple run /// </summary> static internal SimpleRun CreateSimpleTextRun( CharacterBufferRange charBufferRange, TextRun textRun, TextFormatterImp formatter, int widthLeft, bool emergencyWrap, bool breakOnTabs ) { Invariant.Assert(textRun is TextCharacters); SimpleRun run = new SimpleRun(formatter); run.CharBufferReference = charBufferRange.CharacterBufferReference; run.TextRun = textRun; if (!run.TextRun.Properties.Typeface.CheckFastPathNominalGlyphs( charBufferRange, run.EmSize, 1.0, formatter.IdealToReal(widthLeft), !emergencyWrap, false, CultureMapper.GetSpecificCulture(run.TextRun.Properties.CultureInfo), formatter.TextFormattingMode, false, //No support for isSideways breakOnTabs, out run.Length )) { // Getting nominal glyphs is not supported by the font, // or it is but it results in low typographic quality text // e.g. OpenType support is not utilized. return null; } run.TextRun.Properties.Typeface.GetCharacterNominalWidthsAndIdealWidth( new CharacterBufferRange(run.CharBufferReference, run.Length), run.EmSize, TextFormatterImp.ToIdeal, formatter.TextFormattingMode, false, out run.NominalAdvances, out run.IdealWidth ); return run; }
private double _pixelsPerDip; // PixelsPerDip /// <summary> /// Construct text metrics from full text info /// </summary> /// <remarks> /// /// When the application formats a line of text. It starts from the leading edge of the paragraph - the reference position /// called "Paragraph Start". It gives the width of the paragraph or "Paragraph Width" to TextFormatter as one of the main /// parameters to TextFormatter.FormatLine method. It may also provide additional info about how it wants the line to look /// like. The following are all of such info and how the formatting process is carried on inside TextFormatter. /// /// /// *** Indent/Paragraph Indent *** /// The application may specify "Indent" - the distance from the beginning of the line to the beginning of the text in that /// line. The value is sent to TextFormatter via [TextParagraphProperties.Indent]. It may also specify "Paragraph Indent" /// - the distance from the beginning of the paragraph to the beginning of the line [TextParagraphProperties.ParagraphIndent]. /// The usage of paragraph indent is to offset the beginning of the line relative to the paragraph starting point, while /// indent is to offset the beginning of text realtive to the line starting point. Paragraph indent is not included as part /// of the line width while indent is. /// /// /// *** Text Alignment *** /// "Text Alignment" [TextParagraphProperties.TextAlignment] may be specified to align the leading, center or trailing edge /// of the line to the leading, center or trailing edge of the paragraph excluding paragraph indent. /// /// /// *** Bullet/Auto-numbering *** /// The application may also specify "bullet" (or "marker") for the line. Marker does not affect the layout measurement of the /// line. Line with marker has the same line width with the line that has not. The presence of marker however affects the /// pixel-wise black width of the line. The application specifies the distance from the beginning of the line to the trailing /// edge of the marker symbol via the property [TextMarkerProperties.Offset]. The application can create the visual effect of /// having marker embedded inside the body of paragraph text (so-called "marker inside") by specifying a positive indent so /// that the text starts after the beginning of the line and a positive smaller amount of marker offset to place the marker /// symbol at between the beginning of the line and the beginning of the text. The "marker outside" visual effect can /// also be achieved in a similar manner by specifying zero or positive indent value with negative marker offset value. /// /// /// *** Formatted Line Properties *** /// Once the line formatting process is completed and a line is returned to the application. The application determines the /// distance from the paragraph starting point to the actual beginning of the line by looking at the "Line Start" property of /// the text line [TextLine.Start]. The "Width" of the line can be determined, naturally, from the property [TextLine.Width]. /// The property value [TextLine.OverhangLeading] represents the distance from the beginning of the line, or the line's alignment /// point, to the first leading pixel of that line so-called the "Black Start". The property [TextLine.OverhangTrailing] /// is the distance from the last trailing pixel of the line to the trailing edge alignment point of the line. The application /// uses these "overhang" or "overshoot" values to ensure proper positioning of text that avoids pixel clipping of the /// glyph image. A less sophisticated application may provide reasonable leading and trailing margin around the text line /// and ignores these properties altogether. /// /// /// *** Hit-Testing *** /// The application may also perform hit-testing by calling methods on TextLine. All the distances involved in hit-testing /// operations are distances from the paragraph start, not from the line start. Marker symbol on its own is not hit-testable. /// /// /// *** Tabs *** /// The application may specify tab stops - an array of positions to where text aligns. Each tab stop may have different /// "Tab Alignment". The left, center and right tab alignment aligns the tab stop position to the leading, center and the /// trailing edge of the text following the tab character. "Tab Leader" may also be specified to fill the distance occupied /// by the presence of tab character with the symbol of choice. Tab stops is specified thru the property [TextParagraph.Tabs]. /// In the absence of tab stops, the application may assume an automatic tab stop - so called "Incremental Tab" specified by /// the property [TextParagraphProperties.DefaultIncrementalTab]. The property could be overridden, by default the value /// is set by TextFormatter to 4 em of the paragraph's default font. /// /// /// *** Line Services Properties *** /// TextFormatter relies on LS to calculate the distance from the beginning of the line to the beginning of text or "Text Start" /// and keep it in the private property [this._textStart]. This value is non-zero when 1) the line starts with indentation or /// 2) the line starts with marker - either bullet or auto-numbering symbol. /// /// In case of the line with marker, LS also produces the distance from the beginning of the line to the beginning of the marker /// symbol, but TextFormatter does not retain that distance because marker is outside the line. The application is assumed /// responsibility to make sure the marker symbol is not going to be clipped out. The application achieves that by manipulating /// the indent value along with the marker offset value. /// /// TextFormatter also retains the total "Text Width" value computed by LS in the private property [this._textWidth]. This /// is the distance from the beginning of the text to the end including all trailing whitespaces at the end of the line. The /// similar value but with trailing whitespaces excluded is kept in the private property [this._textWidthAtTrailing]. /// /// TextFormatter starts formatting a LS line by assuming the beginning of the line being at an imaginary origin. It then /// places the starting point of the content depending on whether the line has either marker symbol or indent. The actual /// mechanism for the placement is in FetchLineProps callback where the value [LsLineProps.durLeft] represents the distance /// relative to the line's origin where actual content begins. The distances can either be positive or negative. Negative /// distance runs in the reverse direction from the direction of text flow. When a negative indent or marker offset is /// specified, durLeft is set to negative distance relative to line start. /// /// TextFormatter however does not rely on LS for the whole line's text alignment. It always formats LS as if the line is /// left-aligned. Once the distances of the line are received, it aligns the whole line according to the text alignment setting /// specified by the application, outside the LS call. The result of this aligning process is a distance from the beginning of /// the paragraph to the beginning of text and is kept in a private property [this._paragraphToText]. /// /// </remarks> internal unsafe void Compute( FullTextState fullText, int firstCharIndex, int paragraphWidth, FormattedTextSymbols collapsingSymbol, ref LsLineWidths lineWidths, LsLInfo *plsLineInfo ) { _formatter = fullText.Formatter; TextStore store = fullText.TextStore; _pixelsPerDip = store.Settings.TextSource.PixelsPerDip; // obtain position of important distances _textStart = lineWidths.upStartMainText; _textWidthAtTrailing = lineWidths.upStartTrailing; _textWidth = lineWidths.upLimLine; // append line end collapsing symbol if any if (collapsingSymbol != null) { AppendCollapsingSymbolWidth(TextFormatterImp.RealToIdeal(collapsingSymbol.Width)); } // make all widths relative to text start _textWidth -= _textStart; _textWidthAtTrailing -= _textStart; // keep the newline character count if any _cchNewline = store.CchEol; // count text and dependant characters _lscpLim = plsLineInfo->cpLimToContinue; _lastRun = fullText.CountText(_lscpLim, firstCharIndex, out _cchLength); Debug.Assert(_cchLength > 0); if (plsLineInfo->endr != LsEndRes.endrEndPara && plsLineInfo->endr != LsEndRes.endrSoftCR) { // endrEndPara denotes that the line ends at paragraph end. It is a result of submitting Paragraph Separator to LS. // endrSoftCR denotes end of line but not end of paragraph. This is a result of submitting Line Separator to LS. _cchNewline = 0; if (plsLineInfo->dcpDepend >= 0) { // According to SergeyGe [2/16/2006], dcpDepend reported from LS cannot made precise when considering // the line ending with hyphenation - this is because LS does not have the knowledge about the amount // of text, after the hyphenation point, being examined by its client during the process of finding // the right place to hyphenate. LS client must therefore take into account the number of lookahead // LSCP examined by hyphenator when computing the correct dcpDepend for the line. In our implementation // it would just mean we take the max of the two values. int lscpFirstIndependence = Math.Max( plsLineInfo->cpLimToContinue + plsLineInfo->dcpDepend, fullText.LscpHyphenationLookAhead ); fullText.CountText(lscpFirstIndependence, firstCharIndex, out _cchDepend); _cchDepend -= _cchLength; } } ParaProp pap = store.Pap; if (_height <= 0) { // if height has not been settled, // calculate line height and baseline offset if (pap.LineHeight > 0) { // Host specifies line height, honor it. _height = pap.LineHeight; _baselineOffset = (int)Math.Round( _height * pap.DefaultTypeface.Baseline(pap.EmSize, Constants.DefaultIdealToReal, _pixelsPerDip, fullText.TextFormattingMode) / pap.DefaultTypeface.LineSpacing(pap.EmSize, Constants.DefaultIdealToReal, _pixelsPerDip, fullText.TextFormattingMode) ); } if (plsLineInfo->dvrMultiLineHeight == int.MaxValue) { // 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 _textAscent = (int)Math.Round(pap.DefaultTypeface.Baseline(pap.EmSize, Constants.DefaultIdealToReal, _pixelsPerDip, fullText.TextFormattingMode)); _textHeight = (int)Math.Round(pap.DefaultTypeface.LineSpacing(pap.EmSize, Constants.DefaultIdealToReal, _pixelsPerDip, fullText.TextFormattingMode)); } else { _textAscent = plsLineInfo->dvrAscent; _textHeight = _textAscent + plsLineInfo->dvrDescent; if (fullText.VerticalAdjust) { // Line requires vertical repositioning of text runs store.AdjustRunsVerticalOffset( plsLineInfo->cpLimToContinue - firstCharIndex, _height, _baselineOffset, out _textHeight, out _textAscent ); } } // if the client hasn't specified a line height then the line height and baseline // are the same as the text height and text baseline if (_height <= 0) { _height = _textHeight; _baselineOffset = _textAscent; } } // Text alignment aligns the line to correspondent paragraph alignment start edge switch (pap.Align) { case TextAlignment.Right: // alignment rule: // "The sum of paragraph start to line start and line width is equal to paragraph width" // // PTL + LW = PW // (PTT - LTT) + (LTT + TW) = PW // (thus) PTT = PW - TW _paragraphToText = paragraphWidth - _textWidthAtTrailing; break; case TextAlignment.Center: // alignment rule: // "The sum of paragraph start to line start and half the line width is equal to half the paragraph width" // // PTL + 0.5*LW = 0.5*PW // (PTT - LTT) + 0.5*(LTT + TW) = 0.5*PW // (thus) PTT = 0.5 * (PW + LTT - TW) _paragraphToText = (int)Math.Round((paragraphWidth + _textStart - _textWidthAtTrailing) * 0.5); break; default: // alignment rule: // "Paragraph start to line start is paragraph indent" // // PTL = PI // PTT - LTT = PI // (thus) PTT = PI + LTT _paragraphToText = pap.ParagraphIndent + _textStart; break; } }
/// <summary> /// Construct simple text run /// </summary> /// <param name="length">run length</param> /// <param name="textRun">text run</param> /// <param name="flags">run flags</param> private SimpleRun( int length, TextRun textRun, Flags flags, TextFormatterImp textFormatterImp ) { Length = length; TextRun = textRun; RunFlags = flags; _textFormatterImp = textFormatterImp; }
internal unsafe void Compute( FullTextState fullText, int firstCharIndex, int paragraphWidth, FormattedTextSymbols collapsingSymbol, ref LsLineWidths lineWidths, LsLInfo* plsLineInfo ) { _formatter = fullText.Formatter; TextStore store = fullText.TextStore; // obtain position of important distances _textStart = lineWidths.upStartMainText; _textWidthAtTrailing = lineWidths.upStartTrailing; _textWidth = lineWidths.upLimLine; // append line end collapsing symbol if any if (collapsingSymbol != null) { AppendCollapsingSymbolWidth(TextFormatterImp.RealToIdeal(collapsingSymbol.Width)); } // make all widths relative to text start _textWidth -= _textStart; _textWidthAtTrailing -= _textStart; // keep the newline character count if any _cchNewline = store.CchEol; // count text and dependant characters _lscpLim = plsLineInfo->cpLimToContinue; _lastRun = fullText.CountText(_lscpLim, firstCharIndex, out _cchLength); Debug.Assert(_cchLength > 0); if ( plsLineInfo->endr != LsEndRes.endrEndPara && plsLineInfo->endr != LsEndRes.endrSoftCR) { // endrEndPara denotes that the line ends at paragraph end. It is a result of submitting Paragraph Separator to LS. // endrSoftCR denotes end of line but not end of paragraph. This is a result of submitting Line Separator to LS. _cchNewline = 0; if (plsLineInfo->dcpDepend >= 0) { // According to SergeyGe [2/16/2006], dcpDepend reported from LS cannot made precise when considering // the line ending with hyphenation - this is because LS does not have the knowledge about the amount // of text, after the hyphenation point, being examined by its client during the process of finding // the right place to hyphenate. LS client must therefore take into account the number of lookahead // LSCP examined by hyphenator when computing the correct dcpDepend for the line. In our implementation // it would just mean we take the max of the two values. int lscpFirstIndependence = Math.Max( plsLineInfo->cpLimToContinue + plsLineInfo->dcpDepend, fullText.LscpHyphenationLookAhead ); fullText.CountText(lscpFirstIndependence, firstCharIndex, out _cchDepend); _cchDepend -= _cchLength; } } ParaProp pap = store.Pap; if (_height <= 0) { // if height has not been settled, // calculate line height and baseline offset if(pap.LineHeight > 0) { // Host specifies line height, honor it. _height = pap.LineHeight; _baselineOffset = (int)Math.Round( _height * pap.DefaultTypeface.Baseline(pap.EmSize, Constants.DefaultIdealToReal, Util.PixelsPerDip, fullText.TextFormattingMode) / pap.DefaultTypeface.LineSpacing(pap.EmSize, Constants.DefaultIdealToReal, Util.PixelsPerDip, fullText.TextFormattingMode) ); } if(plsLineInfo->dvrMultiLineHeight == int.MaxValue) { // 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 _textAscent = (int)Math.Round(pap.DefaultTypeface.Baseline(pap.EmSize, Constants.DefaultIdealToReal, Util.PixelsPerDip, fullText.TextFormattingMode)); _textHeight = (int)Math.Round(pap.DefaultTypeface.LineSpacing(pap.EmSize, Constants.DefaultIdealToReal, Util.PixelsPerDip, fullText.TextFormattingMode)); } else { _textAscent = plsLineInfo->dvrAscent; _textHeight = _textAscent + plsLineInfo->dvrDescent; if (fullText.VerticalAdjust) { // Line requires vertical repositioning of text runs store.AdjustRunsVerticalOffset( plsLineInfo->cpLimToContinue - firstCharIndex, _height, _baselineOffset, out _textHeight, out _textAscent ); } } // if the client hasn't specified a line height then the line height and baseline // are the same as the text height and text baseline if (_height <= 0) { _height = _textHeight; _baselineOffset = _textAscent; } } // Text alignment aligns the line to correspondent paragraph alignment start edge switch(pap.Align) { case TextAlignment.Right: // alignment rule: // "The sum of paragraph start to line start and line width is equal to paragraph width" // // PTL + LW = PW // (PTT - LTT) + (LTT + TW) = PW // (thus) PTT = PW - TW _paragraphToText = paragraphWidth - _textWidthAtTrailing; break; case TextAlignment.Center: // alignment rule: // "The sum of paragraph start to line start and half the line width is equal to half the paragraph width" // // PTL + 0.5*LW = 0.5*PW // (PTT - LTT) + 0.5*(LTT + TW) = 0.5*PW // (thus) PTT = 0.5 * (PW + LTT - TW) _paragraphToText = (int)Math.Round((paragraphWidth + _textStart - _textWidthAtTrailing) * 0.5); break; default: // alignment rule: // "Paragraph start to line start is paragraph indent" // // PTL = PI // PTT - LTT = PI // (thus) PTT = PI + LTT _paragraphToText = pap.ParagraphIndent + _textStart; break; } }
/// <summary> /// Scan backward to collect trailing spaces of the run /// </summary> /// <param name="formatter">formatter</param> /// <param name="trailing">trailing spaces</param> /// <param name="trailingSpaceWidth">trailing spaces width</param> /// <returns>continue collecting the previous run?</returns> internal bool CollectTrailingSpaces( TextFormatterImp formatter, ref int trailing, ref int trailingSpaceWidth ) { // As we are collecting trailing space cp, we also collect the trailing space width. // In Full text line, TrailingSpaceWidth = ToReal(Sumof(ToIdeal(glyphsWidths)); // we do the same thing here so that trailing space width is exactly the same // as Full Text Line. if(Ghost) { if(!EOT) { trailing += Length; trailingSpaceWidth += IdealWidth; } return true; } // A Tab does not contribute to trailing space calculations. else if (Tab) { return false; } int offsetToFirstChar = CharBufferReference.OffsetToFirstChar; CharacterBuffer charBuffer = CharBufferReference.CharacterBuffer; int dcp = Length; if (dcp > 0 && IsSpace(charBuffer[offsetToFirstChar + dcp - 1])) { // scan backward to find the first blank following a non-blank while (dcp > 0 && IsSpace(charBuffer[offsetToFirstChar + dcp - 1])) { // summing the ideal value of each glyph trailingSpaceWidth += NominalAdvances[dcp - 1]; dcp--; trailing++; } return dcp == 0; } return false; }
internal SimpleRun(TextFormatterImp textFormatterImp) { _textFormatterImp = textFormatterImp; }
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; }
/// <summary> /// Scanning the run list backward to collect run's trailing spaces. /// </summary> /// <param name="runs">current runs in the line</param> /// <param name="formatter">formatter</param> /// <param name="trailing">trailing spaces</param> /// <param name="trailingSpaceWidth">trailing spaces width in ideal values</param> static private void CollectTrailingSpaces( ArrayList runs, TextFormatterImp formatter, ref int trailing, ref int trailingSpaceWidth ) { int left = runs != null ? runs.Count : 0; SimpleRun run = null; bool continueCollecting = true; while(left > 0 && continueCollecting) { run = (SimpleRun)runs[--left]; continueCollecting = run.CollectTrailingSpaces( formatter, ref trailing, ref trailingSpaceWidth ); } }