TextMetrics.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Core / CSharp / MS / Internal / TextFormatting / TextMetrics.cs / 1 / TextMetrics.cs

                            //------------------------------------------------------------------------ 
//
//  Microsoft Windows Client Platform
//  Copyright (C) Microsoft Corporation, 2001
// 
//  File:      TextMetrics.cs
// 
//  Contents:  Full text implementation of ITextMetrics 
//
//  Created:   5-6-2002 Worachai Chaoweeraprasit (wchao) 
//  History:   2-3-2005 (wchao) Refactoring from FullTextLine for optimal paragraph usage.
//
//-----------------------------------------------------------------------
 
using System;
using System.Diagnostics; 
using System.Windows; 
using System.Windows.Media;
using System.Windows.Media.TextFormatting; 
using System.Security;
using System.Security.Permissions;

using SR = MS.Internal.PresentationCore.SR; 
using SRID = MS.Internal.PresentationCore.SRID;
 
 
namespace MS.Internal.TextFormatting
{ 
    internal partial struct TextMetrics : ITextMetrics
    {
        private TextFormatterImp        _formatter;                 // text formatter formatting this metrics
        private int                     _lscpLim;                   // number of LSCP in the line (for boundary condition handling) 
        private int                     _cchLength;                 // actual character count
        private int                     _cchDepend;                 // number of chars after linebreak that triggers reformatting this line 
        private int                     _cchNewline;                // number of chars of newline symbol 
        private int                     _height;                    // line height
        private int                     _textHeight;                // measured height of text within the line 
        private int                     _baselineOffset;            // offset from top of line height to baseline
        private int                     _textAscent;                // offset from top of text height to baseline
        private int                     _textStart;                 // distance from LS origin to text start
        private int                     _textWidth;                 // text start to end 
        private int                     _textWidthAtTrailing;       // text start to end excluding trailing whitespaces
        private int                     _paragraphToText;           // paragraph start to text start 
        private LSRun                   _lastRun;                   // Last Text LSRun 

 
        /// 
        /// Construct text metrics from full text info
        /// 
        ///  
        ///
        /// 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]. 
        ///
        ///  
        [SecurityCritical]
        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(_formatter.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.DefaultTypeface.LineSpacing
                        );
                } 

                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);
                    _textHeight = (int)Math.Round(pap.DefaultTypeface.LineSpacing * pap.EmSize);
                }
                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;
            }
        } 

        ///  
        /// Client to acquire a state at the point where line is broken by line breaking process; 
        /// can be null when the line ends by the ending of the paragraph. Client may pass this
        /// value back to TextFormatter as an input argument to TextFormatter.FormatLine when 
        /// formatting the next line within the same paragraph.
        /// 
        /// 
        /// TextLineBreak is a finalizable object which may contain a reference to an unmanaged 
        /// structure called break record. Break record can be acquired thru ploline. This method
        /// acquire break record only when the passing ploline object is not NULL. 
        /// 
        /// Not all situations requires break record. Single-line formatting without complex text
        /// object does not need it, but optimal break session does. For performance reason, we 
        /// should not produce break record unnecessarily because it makes TextLineBreak become
        /// finalizable, which therefore unnecessarily put additional pressure to GC since each
        /// finalizable object wakes finalizer thread and requires double GC collections.
        ///  
        /// 
        /// Critical - as this calls LoAcquireBreakRecord 
        ///  
        [SecurityCritical]
        internal TextLineBreak GetTextLineBreak(IntPtr ploline) 
        {
            IntPtr pbreakrec = IntPtr.Zero;

            if (ploline != IntPtr.Zero) 
            {
                LsErr lserr = UnsafeNativeMethods.LoAcquireBreakRecord(ploline, out pbreakrec); 
 
                if (lserr != LsErr.None)
                { 
                    TextFormatterContext.ThrowExceptionFromLsError(SR.Get(SRID.AcquireBreakRecordFailure, lserr), lserr);
                }
            }
 
            if (    _lastRun != null
                &&  _lastRun.TextModifierScope != null 
                &&  !(_lastRun.TextRun is TextEndOfParagraph)) 
            {
                return new TextLineBreak( 
                    _lastRun.TextModifierScope,
                    new SecurityCriticalDataForSet(pbreakrec)
                    );
            } 
            return (pbreakrec != IntPtr.Zero) ? new TextLineBreak(null, new SecurityCriticalDataForSet(pbreakrec)) : null;
        } 
 

        ///  
        /// Append the ideal width of line end collapsing symbol
        /// 
        private void AppendCollapsingSymbolWidth(
            int symbolIdealWidth 
            )
        { 
            _textWidth += symbolIdealWidth; 
            _textWidthAtTrailing += symbolIdealWidth;
        } 


        /// 
        /// Client to get the number of text source positions of this line 
        /// 
        public int Length 
        { 
            get { return _cchLength; }
        } 


        /// 
        /// Client to get the number of characters following the last character 
        /// of the line that may trigger reformatting of the current line.
        ///  
        public int DependentLength 
        {
            get { return _cchDepend; } 
        }


        ///  
        /// Client to get the number of newline characters at line end
        ///  
        public int NewlineLength 
        {
            get { return _cchNewline; } 
        }


        ///  
        /// Client to get distance from paragraph start to line start
        ///  
        public double Start 
        {
            get { return _formatter.IdealToReal(_paragraphToText - _textStart); } 
        }


        ///  
        /// Client to get the total width of this line
        ///  
        public double Width 
        {
            get { return _formatter.IdealToReal(_textWidthAtTrailing + _textStart); } 
        }


        ///  
        /// Client to get the total width of this line including width of whitespace characters at the end of the line.
        ///  
        public double WidthIncludingTrailingWhitespace 
        {
            get { return _formatter.IdealToReal(_textWidth + _textStart); } 
        }


        ///  
        /// Client to get the height of the line
        ///  
        public double Height 
        {
            get { return _formatter.IdealToReal(_height); } 
        }


        ///  
        /// Client to get the height of the text (or other content) in the line; this property may differ from the Height
        /// property if the client specified the line height 
        ///  
        public double TextHeight
        { 
            get { return _formatter.IdealToReal(_textHeight); }
        }

 
        /// 
        /// Client to get the distance from top to baseline of this text line 
        ///  
        public double Baseline
        { 
            get { return _formatter.IdealToReal(_baselineOffset); }
        }

 
        /// 
        /// Client to get the distance from the top of the text (or other content) to the baseline of this text line; 
        /// this property may differ from the Baseline property if the client specified the line height 
        /// 
        public double TextBaseline 
        {
            get { return _formatter.IdealToReal(_textAscent); }
        }
 

        ///  
        /// Client to get the distance from the before edge of line height 
        /// to the baseline of marker of the line if any.
        ///  
        public double MarkerBaseline
        {
            get { return Baseline; }
        } 

 
        ///  
        /// Client to get the overall height of the list items marker of the line if any.
        ///  
        public double MarkerHeight
        {
            get { return Height; }
        } 
    }
} 
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------ 
//
//  Microsoft Windows Client Platform
//  Copyright (C) Microsoft Corporation, 2001
// 
//  File:      TextMetrics.cs
// 
//  Contents:  Full text implementation of ITextMetrics 
//
//  Created:   5-6-2002 Worachai Chaoweeraprasit (wchao) 
//  History:   2-3-2005 (wchao) Refactoring from FullTextLine for optimal paragraph usage.
//
//-----------------------------------------------------------------------
 
using System;
using System.Diagnostics; 
using System.Windows; 
using System.Windows.Media;
using System.Windows.Media.TextFormatting; 
using System.Security;
using System.Security.Permissions;

using SR = MS.Internal.PresentationCore.SR; 
using SRID = MS.Internal.PresentationCore.SRID;
 
 
namespace MS.Internal.TextFormatting
{ 
    internal partial struct TextMetrics : ITextMetrics
    {
        private TextFormatterImp        _formatter;                 // text formatter formatting this metrics
        private int                     _lscpLim;                   // number of LSCP in the line (for boundary condition handling) 
        private int                     _cchLength;                 // actual character count
        private int                     _cchDepend;                 // number of chars after linebreak that triggers reformatting this line 
        private int                     _cchNewline;                // number of chars of newline symbol 
        private int                     _height;                    // line height
        private int                     _textHeight;                // measured height of text within the line 
        private int                     _baselineOffset;            // offset from top of line height to baseline
        private int                     _textAscent;                // offset from top of text height to baseline
        private int                     _textStart;                 // distance from LS origin to text start
        private int                     _textWidth;                 // text start to end 
        private int                     _textWidthAtTrailing;       // text start to end excluding trailing whitespaces
        private int                     _paragraphToText;           // paragraph start to text start 
        private LSRun                   _lastRun;                   // Last Text LSRun 

 
        /// 
        /// Construct text metrics from full text info
        /// 
        ///  
        ///
        /// 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]. 
        ///
        ///  
        [SecurityCritical]
        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(_formatter.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.DefaultTypeface.LineSpacing
                        );
                } 

                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);
                    _textHeight = (int)Math.Round(pap.DefaultTypeface.LineSpacing * pap.EmSize);
                }
                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;
            }
        } 

        ///  
        /// Client to acquire a state at the point where line is broken by line breaking process; 
        /// can be null when the line ends by the ending of the paragraph. Client may pass this
        /// value back to TextFormatter as an input argument to TextFormatter.FormatLine when 
        /// formatting the next line within the same paragraph.
        /// 
        /// 
        /// TextLineBreak is a finalizable object which may contain a reference to an unmanaged 
        /// structure called break record. Break record can be acquired thru ploline. This method
        /// acquire break record only when the passing ploline object is not NULL. 
        /// 
        /// Not all situations requires break record. Single-line formatting without complex text
        /// object does not need it, but optimal break session does. For performance reason, we 
        /// should not produce break record unnecessarily because it makes TextLineBreak become
        /// finalizable, which therefore unnecessarily put additional pressure to GC since each
        /// finalizable object wakes finalizer thread and requires double GC collections.
        ///  
        /// 
        /// Critical - as this calls LoAcquireBreakRecord 
        ///  
        [SecurityCritical]
        internal TextLineBreak GetTextLineBreak(IntPtr ploline) 
        {
            IntPtr pbreakrec = IntPtr.Zero;

            if (ploline != IntPtr.Zero) 
            {
                LsErr lserr = UnsafeNativeMethods.LoAcquireBreakRecord(ploline, out pbreakrec); 
 
                if (lserr != LsErr.None)
                { 
                    TextFormatterContext.ThrowExceptionFromLsError(SR.Get(SRID.AcquireBreakRecordFailure, lserr), lserr);
                }
            }
 
            if (    _lastRun != null
                &&  _lastRun.TextModifierScope != null 
                &&  !(_lastRun.TextRun is TextEndOfParagraph)) 
            {
                return new TextLineBreak( 
                    _lastRun.TextModifierScope,
                    new SecurityCriticalDataForSet(pbreakrec)
                    );
            } 
            return (pbreakrec != IntPtr.Zero) ? new TextLineBreak(null, new SecurityCriticalDataForSet(pbreakrec)) : null;
        } 
 

        ///  
        /// Append the ideal width of line end collapsing symbol
        /// 
        private void AppendCollapsingSymbolWidth(
            int symbolIdealWidth 
            )
        { 
            _textWidth += symbolIdealWidth; 
            _textWidthAtTrailing += symbolIdealWidth;
        } 


        /// 
        /// Client to get the number of text source positions of this line 
        /// 
        public int Length 
        { 
            get { return _cchLength; }
        } 


        /// 
        /// Client to get the number of characters following the last character 
        /// of the line that may trigger reformatting of the current line.
        ///  
        public int DependentLength 
        {
            get { return _cchDepend; } 
        }


        ///  
        /// Client to get the number of newline characters at line end
        ///  
        public int NewlineLength 
        {
            get { return _cchNewline; } 
        }


        ///  
        /// Client to get distance from paragraph start to line start
        ///  
        public double Start 
        {
            get { return _formatter.IdealToReal(_paragraphToText - _textStart); } 
        }


        ///  
        /// Client to get the total width of this line
        ///  
        public double Width 
        {
            get { return _formatter.IdealToReal(_textWidthAtTrailing + _textStart); } 
        }


        ///  
        /// Client to get the total width of this line including width of whitespace characters at the end of the line.
        ///  
        public double WidthIncludingTrailingWhitespace 
        {
            get { return _formatter.IdealToReal(_textWidth + _textStart); } 
        }


        ///  
        /// Client to get the height of the line
        ///  
        public double Height 
        {
            get { return _formatter.IdealToReal(_height); } 
        }


        ///  
        /// Client to get the height of the text (or other content) in the line; this property may differ from the Height
        /// property if the client specified the line height 
        ///  
        public double TextHeight
        { 
            get { return _formatter.IdealToReal(_textHeight); }
        }

 
        /// 
        /// Client to get the distance from top to baseline of this text line 
        ///  
        public double Baseline
        { 
            get { return _formatter.IdealToReal(_baselineOffset); }
        }

 
        /// 
        /// Client to get the distance from the top of the text (or other content) to the baseline of this text line; 
        /// this property may differ from the Baseline property if the client specified the line height 
        /// 
        public double TextBaseline 
        {
            get { return _formatter.IdealToReal(_textAscent); }
        }
 

        ///  
        /// Client to get the distance from the before edge of line height 
        /// to the baseline of marker of the line if any.
        ///  
        public double MarkerBaseline
        {
            get { return Baseline; }
        } 

 
        ///  
        /// Client to get the overall height of the list items marker of the line if any.
        ///  
        public double MarkerHeight
        {
            get { return Height; }
        } 
    }
} 
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK