예제 #1
0
        /// <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);
        }
예제 #2
0
        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;
            }
        }
예제 #3
0
        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;
            }
        }
예제 #4
0
        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]);
            }            
        }