/// <summary> /// Map LSCP to host CP, and return the last LSRun /// before the specified limit. /// </summary> internal LSRun CountText( int lscpLim, int cpFirst, out int count ) { LSRun lastRun = null; count = 0; int lsccp = lscpLim - _store.CpFirst; Debug.Assert(lsccp > 0, "Zero-length text line!"); foreach (Span span in _store.PlsrunVector) { if (lsccp <= 0) { break; } Plsrun plsrun = (Plsrun)span.element; // There should be no marker runs in _plsrunVector. Debug.Assert(!TextStore.IsMarker(plsrun)); // Is it a normal, non-static, LSRun? if (plsrun >= Plsrun.FormatAnchor) { // Get the run and remember the last run. lastRun = _store.GetRun(plsrun); // Accumulate the length. int cpRun = lastRun.Length; if (cpRun > 0) { if (lsccp < span.length && cpRun == span.length) { count += lsccp; break; } count += cpRun; } } lsccp -= span.length; } // make char count relative to cpFirst as the cpFirst of this metrics may not // be the same as the cpFirst of the store in optimal paragraph formatting. count = count - cpFirst + _store.CpFirst; return(lastRun); }
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; } }
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; } }
public FormattedTextSymbols( GlyphingCache glyphingCache, TextRun textSymbols, bool rightToLeft, double scalingFactor, float pixelsPerDip, TextFormattingMode textFormattingMode, bool isSideways ) { _textFormattingMode = textFormattingMode; _isSideways = isSideways; ITextSymbols symbols = textSymbols as ITextSymbols; Debug.Assert(symbols != null); // break down a single text run into pieces IList <TextShapeableSymbols> shapeables = symbols.GetTextShapeableSymbols( glyphingCache, textSymbols.CharacterBufferReference, textSymbols.Length, rightToLeft, // This is a bool indicating the RTL // based on the bidi level of text (if applicable). // For FormattedTextSymbols it is equal to paragraph flow direction. rightToLeft, // This is the flow direction of the paragraph as // specified by the user. DWrite needs the paragraph // flow direction of the paragraph // while WPF algorithms need the RTL of the text based on // Bidi if possible. null, // cultureInfo null, // textModifierScope _textFormattingMode, _isSideways ); Debug.Assert(shapeables != null && shapeables.Count > 0); _rightToLeft = rightToLeft; _glyphs = new Glyphs[shapeables.Count]; CharacterBuffer charBuffer = textSymbols.CharacterBufferReference.CharacterBuffer; int offsetToFirstChar = textSymbols.CharacterBufferReference.OffsetToFirstChar; int i = 0; int ich = 0; while (i < shapeables.Count) { TextShapeableSymbols current = shapeables[i] as TextShapeableSymbols; Debug.Assert(current != null); int cch = current.Length; int j; // make a separate character buffer for glyphrun persistence char[] charArray = new char[cch]; for (j = 0; j < cch; j++) { charArray[j] = charBuffer[offsetToFirstChar + ich + j]; } if (current.IsShapingRequired) { ushort[] clusterMap; ushort[] glyphIndices; int[] glyphAdvances; GlyphOffset[] glyphOffsets; // unsafe { fixed(char *fixedCharArray = &charArray[0]) { MS.Internal.Text.TextInterface.TextAnalyzer textAnalyzer = MS.Internal.FontCache.DWriteFactory.Instance.CreateTextAnalyzer(); GlyphTypeface glyphTypeface = current.GlyphTypeFace; DWriteFontFeature[][] fontFeatures; uint[] fontFeatureRanges; uint unsignedCch = checked ((uint)cch); LSRun.CompileFeatureSet(current.Properties.TypographyProperties, unsignedCch, out fontFeatures, out fontFeatureRanges); textAnalyzer.GetGlyphsAndTheirPlacements( (ushort *)fixedCharArray, unsignedCch, glyphTypeface.FontDWrite, glyphTypeface.BlankGlyphIndex, false, // no sideway support yet /************************************************************************************************/ // rightToLeft, current.Properties.CultureInfo, /************************************************************************************************/ fontFeatures, fontFeatureRanges, current.Properties.FontRenderingEmSize, scalingFactor, pixelsPerDip, _textFormattingMode, current.ItemProps, out clusterMap, out glyphIndices, out glyphAdvances, out glyphOffsets ); } _glyphs[i] = new Glyphs( current, charArray, glyphAdvances, clusterMap, glyphIndices, glyphOffsets, scalingFactor ); } } else { // shaping not required, // bypass glyphing process altogether int[] nominalAdvances = new int[charArray.Length]; unsafe { fixed(char *fixedCharArray = &charArray[0]) fixed(int *fixedNominalAdvances = &nominalAdvances[0]) { current.GetAdvanceWidthsUnshaped( fixedCharArray, cch, scalingFactor, // format resolution specified per em, fixedNominalAdvances ); } } _glyphs[i] = new Glyphs( current, charArray, nominalAdvances, scalingFactor ); } i++; ich += cch; } }
public ExceptionContext(object innerContext, string stackTraceOrMethodName, Plsrun plsrun, LSRun lsrun) { _stackTraceOrMethodName = stackTraceOrMethodName; _plsrun = (uint)plsrun; _lsrun = lsrun; _innerContext = innerContext; }
private void GetLSRunStrikethroughMetrics( LSRun lsrun, out double strikeThroughPositionInEm, out double strikeThroughThicknessInEm ) { if (lsrun.Shapeable != null) { strikeThroughPositionInEm = lsrun.Shapeable.StrikethroughPosition; strikeThroughThicknessInEm = lsrun.Shapeable.StrikethroughThickness; } else { strikeThroughPositionInEm = lsrun.RunProp.Typeface.StrikethroughPosition; strikeThroughThicknessInEm = lsrun.RunProp.Typeface.StrikethroughThickness; } }
private void SaveException(Exception e, Plsrun plsrun, LSRun lsrun) { e.Data[ExceptionContext.Key] = new ExceptionContext(e.Data[ExceptionContext.Key], e.StackTrace, plsrun, lsrun); _exception = e; }
private void SaveNonCLSException(string methodName, Plsrun plsrun, LSRun lsrun) { Exception e = new System.Exception(SR.Get(SRID.NonCLSException)); e.Data[ExceptionContext.Key] = new ExceptionContext(null, methodName, plsrun, lsrun); _exception = e; }
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; }
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; }
private unsafe LSRun[] RemapLSRuns( IntPtr* plsplsruns, int plsrunCount ) { LSRun[] lsruns = new LSRun[plsrunCount]; TextStore store = FullText.StoreFrom((Plsrun)(*plsplsruns)); for (int i = 0; i < lsruns.Length; i++) { Plsrun plsrun = (Plsrun)plsplsruns[i]; lsruns[i] = store.GetRun(plsrun); Debug.Assert(TextStore.IsContent(plsrun) && lsruns[i] != null); } return lsruns; }
private Rect DrawTextDecoration( LSRun lsrun, // lsrun Brush foregroundBrush, // default brush if text decoration has no pen LSPOINT ptOrigin, // drawing origin int ulLength, // underline length int ulThickness, // underline thickness LsTFlow textFlow, // text flow direction TextDecoration textDecoration //TextDecoration to be draw (add to sublinecollection ) { switch (textFlow) { case LsTFlow.lstflowWS: case LsTFlow.lstflowNE: case LsTFlow.lstflowNW: ptOrigin.x -= ulLength; break; } TextMetrics.FullTextLine currentLine = Draw.CurrentLine; if (currentLine.RightToLeft) { ptOrigin.x = -ptOrigin.x; } int u = currentLine.LSLineUToParagraphU(ptOrigin.x); Point baselineOrigin = LSRun.UVToXY( Draw.LineOrigin, Draw.VectorToLineOrigin, u, currentLine.BaselineOffset, currentLine ); Point lineOrigin = LSRun.UVToXY( Draw.LineOrigin, Draw.VectorToLineOrigin, u, ptOrigin.y + lsrun.BaselineMoveOffset, currentLine ); double penThickness = 1.0; if (textDecoration.Pen != null) { penThickness = textDecoration.Pen.Thickness; } switch (textDecoration.PenThicknessUnit) { case TextDecorationUnit.FontRecommended: penThickness = currentLine.Formatter.IdealToReal(ulThickness * penThickness); break; case TextDecorationUnit.FontRenderingEmSize: penThickness = currentLine.Formatter.IdealToReal(penThickness * lsrun.EmSize); break; case TextDecorationUnit.Pixel: break; default: Debug.Assert(false, "Not supported TextDecorationUnit"); break; } penThickness = Math.Abs(penThickness); double unitValue = 1.0; switch (textDecoration.PenOffsetUnit) { case TextDecorationUnit.FontRecommended: unitValue = (lineOrigin.Y - baselineOrigin.Y); break; case TextDecorationUnit.FontRenderingEmSize: unitValue = currentLine.Formatter.IdealToReal(lsrun.EmSize); break; case TextDecorationUnit.Pixel: unitValue = 1.0; break; default: Debug.Assert(false, "Not supported TextDecorationUnit"); break; } double lineLength = currentLine.Formatter.IdealToReal(ulLength); DrawingContext drawingContext = Draw.DrawingContext; if (drawingContext != null) { double drawingPenThickness = penThickness; Point drawingLineOrigin = lineOrigin; bool animated = !textDecoration.CanFreeze && (unitValue != 0); int pushCount = 0; // counter for the number of explicit DrawingContext.Push() Draw.SetGuidelineY(baselineOrigin.Y); try { if (animated) { ScaleTransform scaleTransform = new ScaleTransform( 1.0, // X scale unitValue, // y scale drawingLineOrigin.X, // reference point of scaling drawingLineOrigin.Y // reference point of scaling ); TranslateTransform yTranslate = new TranslateTransform( 0, // x translate textDecoration.PenOffset // y translate ); drawingPenThickness = drawingPenThickness / Math.Abs(unitValue); drawingContext.PushTransform(scaleTransform); pushCount++; drawingContext.PushTransform(yTranslate); pushCount++; } else { drawingLineOrigin.Y += unitValue * textDecoration.PenOffset; } drawingContext.PushGuidelineY2(baselineOrigin.Y, drawingLineOrigin.Y - drawingPenThickness * 0.5 - baselineOrigin.Y); pushCount++; if (textDecoration.Pen == null) { drawingContext.DrawRectangle( foregroundBrush, // fill using foreground null, // null pen for rectangle stroke new Rect( drawingLineOrigin.X, drawingLineOrigin.Y - drawingPenThickness * 0.5, lineLength, drawingPenThickness ) ); } else { Pen textDecorationPen = textDecoration.Pen.CloneCurrentValue(); if (Object.ReferenceEquals(textDecoration.Pen, textDecorationPen)) { textDecorationPen = textDecoration.Pen.Clone(); } textDecorationPen.Thickness = drawingPenThickness; drawingContext.DrawLine( textDecorationPen, drawingLineOrigin, new Point(drawingLineOrigin.X + lineLength, drawingLineOrigin.Y) ); } } finally { for (int i = 0; i < pushCount; i++) { drawingContext.Pop(); } Draw.UnsetGuidelineY(); } } return new Rect( lineOrigin.X, lineOrigin.Y + unitValue * textDecoration.PenOffset - penThickness * 0.5, lineLength, penThickness ); }
private void DrawTextDecorationCollection( LSRun lsrun, uint locationMask, TextDecorationCollection textDecorations, Brush foregroundBrush, int left, int underlineTop, int overlineTop, int strikethroughTop, int baselineTop, int length, int thickness, LsTFlow textFlow ) { Invariant.Assert(textDecorations != null); foreach (TextDecoration td in textDecorations) { if (((1U << (int)td.Location) & locationMask) != 0) { switch (td.Location) { case TextDecorationLocation.Underline: _boundingBox.Union( DrawTextDecoration( lsrun, foregroundBrush, new LSPOINT(left, underlineTop), length, thickness, textFlow, td ) ); break; case TextDecorationLocation.OverLine: _boundingBox.Union( DrawTextDecoration( lsrun, foregroundBrush, new LSPOINT(left, overlineTop), length, thickness, textFlow, td ) ); break; case TextDecorationLocation.Strikethrough: _boundingBox.Union( DrawTextDecoration( lsrun, foregroundBrush, new LSPOINT(left, strikethroughTop), length, thickness, textFlow, td ) ); break; case TextDecorationLocation.Baseline: _boundingBox.Union( DrawTextDecoration( lsrun, foregroundBrush, new LSPOINT(left, baselineTop), length, thickness, textFlow, td ) ); break; } } } }
private void DrawTextDecorations( LSRun lsrun, uint locationMask, int left, int underlineTop, int overlineTop, int strikethroughTop, int baselineTop, int length, int thickness, LsTFlow textFlow ) { TextMetrics.FullTextLine currentLine = Draw.CurrentLine; TextDecorationCollection textDecorations = currentLine.TextDecorations; if (textDecorations != null) { DrawTextDecorationCollection( lsrun, locationMask, textDecorations, currentLine.DefaultTextDecorationsBrush, left, underlineTop, overlineTop, strikethroughTop, baselineTop, length, thickness, textFlow ); } textDecorations = lsrun.RunProp.TextDecorations; if (textDecorations != null) { DrawTextDecorationCollection( lsrun, locationMask, textDecorations, lsrun.RunProp.ForegroundBrush, left, underlineTop, overlineTop, strikethroughTop, baselineTop, length, thickness, textFlow ); } }
internal static unsafe void CompileFeatureSet( LSRun[] lsruns, int* pcchRuns, uint totalLength, out DWriteFontFeature[][] fontFeatures, out uint[] fontFeatureRanges ) { Debug.Assert(lsruns != null && lsruns.Length > 0 && lsruns[0] != null); // // Quick check for null properties // Run properties should be all null or all not null // if (lsruns[0].RunProp.TypographyProperties == null) { for (int i = 1; i < lsruns.Length; i++) { if (lsruns[i].RunProp.TypographyProperties != null) { throw new ArgumentException(SR.Get(SRID.CompileFeatureSet_InvalidTypographyProperties)); } } fontFeatures = null; fontFeatureRanges = null; return; } //End of quick check. We will process custom features now. fontFeatures = new DWriteFontFeature[lsruns.Length][]; fontFeatureRanges = new uint[lsruns.Length]; for (int i = 0; i < lsruns.Length; i++) { TextRunTypographyProperties properties = lsruns[i].RunProp.TypographyProperties; fontFeatures[i] = CreateDWriteFontFeatures(properties); fontFeatureRanges[i] = checked((uint)pcchRuns[i]); } }