Exemple #1
0
        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;
            }
        }
Exemple #2
0
        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;
            }
        }
Exemple #3
0
 /// <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);
 }
Exemple #4
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);
                    }
                }
            }
        }
Exemple #5
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; 
            }
        } 
Exemple #6
0
        /// <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);
        }
Exemple #7
0
        /// <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;
            }
        }
Exemple #8
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;
            }
        }
        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;
        }
Exemple #11
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;
            }
        }
 /// <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
                    );
            }
        }