SimpleTextLine.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Core / CSharp / MS / Internal / TextFormatting / SimpleTextLine.cs / 1477649 / SimpleTextLine.cs

                            //------------------------------------------------------------------------ 
//
//  Microsoft Windows Client Platform
//  Copyright (C) Microsoft Corporation, 2001
// 
//  File:      SimpleTextLine.cs
// 
//  Contents:  Light-weight implementation of TextLine 
//
//  Created:   11-7-2001 Worachai Chaoweeraprasit (wchao) 
//
//-----------------------------------------------------------------------

using System; 
using System.Security;
using System.Windows; 
using System.Windows.Media; 
using System.Windows.Media.Animation;
using System.Windows.Media.TextFormatting; 
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using MS.Internal.Shaping; 
using MS.Internal.FontCache;
 
using SR=MS.Internal.PresentationCore.SR; 
using SRID=MS.Internal.PresentationCore.SRID;
 
namespace MS.Internal.TextFormatting
{
    /// 
    /// Light-weight implementation of TextLine 
    ///
    /// Support following functionalities 
    ///    o    Non-complex script text metrics through font CMAP/HMTX 
    ///    o    Multiple character formats, each limited to single font face
    ///    o    Simple text underlining for individual run (no averaging) 
    ///
    /// In the event that either the incoming text or formatting is more
    /// complicated than what this implementation can handle. The .ctor
    /// simply stops and leaves this.Valid flag unset. The caller examines 
    /// this flag and lets the full path takes over if needed.
    ///  
    internal class SimpleTextLine : TextLine 
    {
        private SimpleRun[]             _runs;                  // contained runs 
        private int                     _cpFirst;               // line first cp
        private int                     _cpLength;              // all characters
        private int                     _cpLengthEOT;           // newline characters
        private double                  _widthAtTrailing;       // width excluding trailing space 
        private double                  _width;                 // whole width
        private double                  _paragraphWidth;        // paragraph width 
        private double                  _height;                // line height 
        private double                  _offset;                // offset to the first character
        private double                  _baselineOffset;        // offset to baseline 
        private int                     _trailing;              // trailing spaces
        private Rect                    _boundingBox;           // line bounding rectangle
        private StatusFlags             _statusFlags;           // status flags
        private FormatSettings          _settings;              // formatting settings (only kept in an overflowed line for collapsing purpose only) 

 
        [Flags] 
        private enum StatusFlags
        { 
            None                = 0,
            BoundingBoxComputed = 0x00000001,   // bounding box has been computed
            HasOverflowed       = 0x00000002,   // line width overflows paragraph width
        } 

 
 
        /// 
        /// Creating a lightweight text line 
        /// 
        /// text formatting settings
        /// First cp of the line
        /// paragraph width 
        /// TextLine instance
        ///  
        /// This method breaks line using Ideal width such that it will be 
        /// consistent with FullTextLine
        ///  
        static public TextLine  Create(
            FormatSettings          settings,
            int                     cpFirst,
            int                     paragraphWidth 
            )
        { 
            ParaProp pap = settings.Pap; 

            if(    pap.RightToLeft 
                || pap.Justify
                || (   pap.FirstLineInParagraph
                    && pap.TextMarkerProperties != null)
                || settings.TextIndent != 0 
                || pap.ParagraphIndent != 0
                || pap.LineHeight > 0 
                || pap.AlwaysCollapsible 
                || (pap.TextDecorations != null && pap.TextDecorations.Count != 0)
                ) 
            {
                // unsupported paragraph properties
                return null;
            } 

            int cp = cpFirst; 
 
            // paragraphWidth == 0 means the format width is unlimited.
            int widthLeft = (pap.Wrap && paragraphWidth > 0) ? paragraphWidth : int.MaxValue; 

            SimpleRun prev = null;

            SimpleRun run = SimpleRun.Create( 
                settings,
                cp, 
                cpFirst, 
                widthLeft,
                paragraphWidth 
                );


            if(run == null) 
            {
                // fail to create run e.g. complex content encountered 
                return null; 
            }
            else if(!run.EOT && run.IdealWidth <= widthLeft) 
            {
                // create next run
                cp += run.Length;
                widthLeft -= run.IdealWidth; 
                prev = run;
 
                run = SimpleRun.Create( 
                    settings,
                    cp, 
                    cpFirst,
                    widthLeft,
                    paragraphWidth
                    ); 

                if(run == null) 
                { 
                    return null;
                } 
            }


            int trailing = 0; 
            ArrayList runs = new ArrayList(2);
 
            if(prev != null) 
            {
                AddRun(runs, prev, null); 
            }

            do
            { 
                if(!run.EOT && run.IdealWidth > widthLeft)
                { 
                    // linebreaking required, even simple text requires classification-based linebreaking, 
                    // we'll now let LS handle this line.
                    return null; 
                }

                AddRun(runs, run, null);
 
                prev = run;
                cp += run.Length; 
                widthLeft -= run.IdealWidth; 

                if(run.EOT) 
                {
                    // we're done
                    break;
                } 

                run = SimpleRun.Create( 
                    settings, 
                    cp,
                    cpFirst, 
                    widthLeft,
                    paragraphWidth
                    );
 
                if(    run == null
                    || (   run.Underline != null 
                        && prev != null 
                        && prev.Underline != null
                        && !prev.IsUnderlineCompatible(run)) 
                    )
                {
                    // fail to create run or
                    // runs cannot support averaging underline 
                    return null;
                } 
 
            } while(true);
 
            int trailingSpaceWidth = 0;

            CollectTrailingSpaces(
                runs, 
                settings.Formatter,
                ref trailing, 
                ref trailingSpaceWidth 
                );
 
            // create a simple line
            return new SimpleTextLine(
                settings,
                cpFirst, 
                paragraphWidth,
                runs, 
                ref trailing, 
                ref trailingSpaceWidth
                ) as TextLine; 
        }


 
        /// 
        /// Constructing a lightweight text line 
        ///  
        /// text formatting settings
        /// line first cp 
        /// paragraph width
        /// collection of simple runs
        /// line trailing spaces
        /// line trailing spaces width 
        /// 
        /// SimpleTextLine is constructed with Ideal width such that the line breaking 
        /// behavior is consistent with the FullTextLine 
        /// 
        public SimpleTextLine( 
            FormatSettings          settings,
            int                     cpFirst,
            int                     paragraphWidth,
            ArrayList               runs, 
            ref int                 trailing,
            ref int                 trailingSpaceWidth 
            ) 
        {
            // Compute line metrics 
            int count = 0;

            _settings = settings;
 
            double realAscent = 0;
            double realDescent = 0; 
            double realHeight = 0; 

            ParaProp pap = settings.Pap; 
            TextFormatterImp formatter = settings.Formatter;

            int idealWidth = 0;
            while(count < runs.Count) 
            {
                SimpleRun run = (SimpleRun)runs[count]; 
 
                if(run.Length > 0)
                { 
                    if(run.EOT)
                    {
                        // EOT run has no effect on height, it is part of trailing spaces
                        trailing += run.Length; 
                        _cpLengthEOT += run.Length;
                    } 
                    else 
                    {
                        realHeight = Math.Max(realHeight, run.Height); 
                        realAscent = Math.Max(realAscent, run.Baseline);
                        realDescent = Math.Max(realDescent, run.Height - run.Baseline);
                    }
 
                    _cpLength += run.Length;
                    idealWidth += run.IdealWidth; 
                } 
                count++;
            } 

            // Roundtrip run baseline and height to take its precision back to the specified formatting resolution.
            //
            // We have to do this to guarantee sameness of line alignment metrics produced by fast and full path. 
            // This is critical for TextBlock/TextFlow. They rely on the fact that line created during Measure must
            // yield the same metrics as one created during Render, while there is no guarantee that the paragraph 
            // properties of that same line remains the same in both timings e.g. Measure may not specify 
            // justification (which results in us formatting the line in fast path), while Render might
            // (which results in us formatting that same line in full path). 

            _baselineOffset = formatter.IdealToReal(TextFormatterImp.RealToIdeal(realAscent));

            if (realAscent + realDescent == realHeight) 
            {
                _height = formatter.IdealToReal(TextFormatterImp.RealToIdeal(realHeight)); 
            } 
            else
            { 
                _height = formatter.IdealToReal(TextFormatterImp.RealToIdeal(realAscent) + TextFormatterImp.RealToIdeal(realDescent));
            }

            if(_height <= 0) 
            {
                //  line is empty (containing only EOP) 
                //  we need to work out the line height 

                // It needs to be exactly the same as in full path. 
                _height = formatter.IdealToReal((int)Math.Round(pap.DefaultTypeface.LineSpacing(pap.EmSize, Constants.DefaultIdealToReal, Util.PixelsPerDip, _settings.TextFormattingMode)));
                _baselineOffset = formatter.IdealToReal((int)Math.Round(pap.DefaultTypeface.Baseline(pap.EmSize, Constants.DefaultIdealToReal, Util.PixelsPerDip, _settings.TextFormattingMode)));
            }
 
            // Initialize the array of runs and set the TrimTrailingUnderline flag
            // for runs that contain trailing spaces at the end of the line. 
            _runs = new SimpleRun[count]; 
            for(int i = count - 1, t = trailing; i >= 0; --i)
            { 
                SimpleRun run = (SimpleRun)runs[i];

                if (t > 0)
                { 
                    run.TrimTrailingUnderline = true;
                    t -= run.Length; 
                } 

                _runs[i] = run; 
            }

            _cpFirst = cpFirst;
            _trailing = trailing; 

            int idealWidthAtTrailing = idealWidth - trailingSpaceWidth; 
 
            if(pap.Align != TextAlignment.Left)
            { 
                switch(pap.Align)
                {
                    case TextAlignment.Right:
                        _offset = formatter.IdealToReal(paragraphWidth - idealWidthAtTrailing); 
                        break;
                    case TextAlignment.Center: 
                        // exactly consistent with FullTextLine 
                        _offset = formatter.IdealToReal((int) Math.Round((paragraphWidth - idealWidthAtTrailing) * 0.5));
                        break; 
                }
            }

            // converting all the ideal values to real values 
            _width = formatter.IdealToReal(idealWidth);
            _widthAtTrailing = formatter.IdealToReal(idealWidthAtTrailing); 
            _paragraphWidth = formatter.IdealToReal(paragraphWidth); 

            // paragraphWidth == 0 means format width is unlimited and hence not overflowable. 
            // we keep paragraphWidth for alignment calculation
            if (paragraphWidth > 0 && _widthAtTrailing > _paragraphWidth)
            {
                _statusFlags |= StatusFlags.HasOverflowed; 
            }
        } 
 

        ///  
        /// Nothing to release
        /// 
        public override void Dispose() {}
 

        ///  
        /// Scanning the run list backward to collect run's trailing spaces. 
        /// 
        /// current runs in the line 
        /// formatter
        /// trailing spaces
        /// trailing spaces width in ideal values
        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
                    ); 
            } 
        }
 

        /// 
        /// Collecting glyph runs
        ///  
        static private void AddRun(
            ArrayList       runs, 
            SimpleRun       run, 
            SimpleRun       prev
            ) 
        {
            Invariant.Assert(
                prev == null || (runs.Count > 0 && prev == runs[runs.Count - 1]),
                "Trailing space run is not after the last existing run!" 
                );
 
            if(run.Length > 0) 
            {
                // dont add 0-length run 
                runs.Add(run);
            }
        }
 

 
        ///  
        /// Get distance from line start to the specified cp
        ///  
        private double DistanceFromCp(int currentIndex)
        {
            Invariant.Assert(currentIndex >= _cpFirst);
 
            double advance = 0;
            int dcp = currentIndex - _cpFirst; 
 
            foreach(SimpleRun run in _runs)
            { 
                advance += run.DistanceFromDcp(dcp);

                if(dcp <= run.Length)
                { 
                    break;
                } 
 
                dcp -= run.Length;
            } 

            return advance + _offset;
        }
 

 
        ///  
        /// Draw line
        ///  
        /// drawing context
        /// drawing origin
        /// indicate the inversion of the drawing surface
        public override void Draw( 
            DrawingContext      drawingContext,
            Point               origin, 
            InvertAxes          inversion 
            )
        { 
            if (drawingContext == null)
            {
                throw new ArgumentNullException("drawingContext");
            } 

            MatrixTransform antiInversion = TextFormatterImp.CreateAntiInversionTransform( 
                inversion, 
                _paragraphWidth,
                _height 
                );

            if (antiInversion == null)
            { 
                DrawTextLine(drawingContext, origin);
            } 
            else 
            {
                // Apply anti-inversion transform to correct the visual 
                drawingContext.PushTransform(antiInversion);
                try
                {
                    DrawTextLine(drawingContext, origin); 
                }
                finally 
                { 
                    drawingContext.Pop();
                } 
            }
        }

 

        ///  
        /// Client to collapse the line to fit for display 
        /// 
        /// a list of collapsing properties 
        public override TextLine Collapse(
            params TextCollapsingProperties[]   collapsingPropertiesList
            )
        { 
            if (!HasOverflowed)
                return this; 
 
            Invariant.Assert(_settings != null);
 
            // instantiate a collapsible full text line, collapse it and return the collapsed line
            TextMetrics.FullTextLine textLine = new TextMetrics.FullTextLine(
                _settings,
                _cpFirst, 
                0,  // lineLength
                TextFormatterImp.RealToIdeal(_paragraphWidth), 
                LineFlags.None 
                );
 
            Invariant.Assert(textLine.HasOverflowed);
            TextLine collapsedTextLine = textLine.Collapse(collapsingPropertiesList);
            if (collapsedTextLine != textLine)
            { 
                // if collapsed line is genuinely new,
                // Dispose its maker as we no longer need it around, dispose it explicitly 
                // to reduce unnecessary finalization of this intermediate line. 
                textLine.Dispose();
            } 
            return collapsedTextLine;
        }

 
        /// 
        /// Make sure the bounding box is calculated 
        ///  
        private void CheckBoundingBox()
        { 
            if ((_statusFlags & StatusFlags.BoundingBoxComputed) == 0)
            {
                DrawTextLine(null, new Point(0, 0));
            } 
            Debug.Assert((_statusFlags & StatusFlags.BoundingBoxComputed) != 0);
        } 
 

        ///  
        /// Draw a simple text line
        /// 
        /// a drawing bounding box
        private void DrawTextLine( 
            DrawingContext drawingContext,
            Point          origin 
            ) 
        {
            if (_runs.Length <= 0) 
            {
                _boundingBox = Rect.Empty;
                _statusFlags |= StatusFlags.BoundingBoxComputed;
                return; 
            }
 
            double x = origin.X + _offset; 
            double y = origin.Y + Baseline;
 
            if (drawingContext != null)
            {
                drawingContext.PushGuidelineY1(y);
            } 

            Rect boundingBox = Rect.Empty; 
 
            try
            { 
                foreach (SimpleRun run in _runs)
                {
                    boundingBox.Union(
                        run.Draw( 
                            drawingContext,
                            x, 
                            y, 
                            false
                            ) 
                        );

                    x += _settings.Formatter.IdealToReal(run.IdealWidth);
                } 
            }
            finally 
            { 
                if (drawingContext != null)
                { 
                    drawingContext.Pop();
                }
            }
 
            if(boundingBox.IsEmpty)
            { 
                boundingBox = new Rect(Start, 0, 0, 0); 
            }
            else 
            {
                boundingBox.X -= origin.X;
                boundingBox.Y -= origin.Y;
            } 

            _boundingBox = boundingBox; 
            _statusFlags |= StatusFlags.BoundingBoxComputed; 
        }
 


        /// 
        /// Client to get the character hit corresponding to the specified 
        /// distance from the beginning of the line.
        ///  
        /// distance in text flow direction from the beginning of the line 
        /// character hit
        public override CharacterHit GetCharacterHitFromDistance( 
            double      distance
            )
        {
            double advance = distance - _offset; 
            int first = _cpFirst;
 
            if (advance < 0) 
            {
                // hit happens before the line, return the first position 
                return new CharacterHit(_cpFirst, 0);
            }

            // process hit that happens within the line 
            SimpleRun run = null;
            CharacterHit runIndex = new CharacterHit(); 
 
            for(int i = 0; i < _runs.Length;  i++)
            { 
                run = (SimpleRun)_runs[i];

                if (!run.EOT)
                { 
                    // move forward to start of next non-EOT run
                    first += runIndex.TrailingLength; 
                    runIndex = run.DcpFromDistance(advance); 
                    first += runIndex.FirstCharacterIndex;
                } 

                if(advance <= _settings.Formatter.IdealToReal(run.IdealWidth))
                {
                    break; 
                }
 
                advance -= _settings.Formatter.IdealToReal(run.IdealWidth); 
            }
            return new CharacterHit(first, runIndex.TrailingLength); 
        }


        ///  
        /// Client to get the distance from the beginning of the line from the specified
        /// character hit. 
        ///  
        /// character hit of the character to query the distance.
        /// distance in text flow direction from the beginning of the line. 
        public override double GetDistanceFromCharacterHit(
            CharacterHit    characterHit
            )
        { 
            TextFormatterImp.VerifyCaretCharacterHit(characterHit, _cpFirst, _cpLength);
            return DistanceFromCp(characterHit.FirstCharacterIndex + (characterHit.TrailingLength != 0 ? 1 : 0)); 
        } 

 
        /// 
        /// Client to get the next character hit for caret navigation
        /// 
        /// the current character hit 
        /// the next character hit
        public override CharacterHit GetNextCaretCharacterHit( 
            CharacterHit    characterHit 
            )
        { 
            TextFormatterImp.VerifyCaretCharacterHit(characterHit, _cpFirst, _cpLength);

            int nextVisisbleCp;
            bool navigableCpFound; 
            if (characterHit.TrailingLength == 0)
            { 
                navigableCpFound = FindNextVisibleCp(characterHit.FirstCharacterIndex, out nextVisisbleCp); 
                if (navigableCpFound)
                { 
                    // Move from leading to trailing edge
                    return new CharacterHit(nextVisisbleCp, 1);
                }
            } 

            navigableCpFound = FindNextVisibleCp(characterHit.FirstCharacterIndex + 1, out nextVisisbleCp); 
            if (navigableCpFound) 
            {
                // Move from trailing edge of current character to trailing edge of next 
                return new CharacterHit(nextVisisbleCp, 1);
            }

            // Can't move, we're after the last character 
            return characterHit;
        } 
 

        ///  
        /// Client to get the previous character hit for caret navigation
        /// 
        /// the current character hit
        /// the previous character hit 
        public override CharacterHit GetPreviousCaretCharacterHit(
            CharacterHit    characterHit 
            ) 
        {
            TextFormatterImp.VerifyCaretCharacterHit(characterHit, _cpFirst, _cpLength); 
            int previousVisisbleCp;
            bool navigableCpFound;

            int cpHit = characterHit.FirstCharacterIndex; 
            bool trailingHit = (characterHit.TrailingLength != 0);
 
            // Input can be right after the end of the current line. Snap it to be at the end of the line. 
            if (cpHit >= _cpFirst + _cpLength)
            { 
                cpHit = _cpFirst + _cpLength - 1;
                trailingHit = true;
            }
 
            if (trailingHit)
            { 
                navigableCpFound = FindPreviousVisibleCp(cpHit, out previousVisisbleCp); 
                if (navigableCpFound)
                { 
                    // Move from trailing to leading edge
                    return new CharacterHit(previousVisisbleCp, 0);
                }
            } 

            navigableCpFound = FindPreviousVisibleCp(cpHit - 1, out previousVisisbleCp); 
            if (navigableCpFound) 
            {
                // Move from leading edge of current character to leading edge of previous 
                return new CharacterHit(previousVisisbleCp, 0);
            }

            // Can't move, we're before the first character 
            return characterHit;
        } 
 

        ///  
        /// Client to get the previous character hit after backspacing
        /// 
        /// the current character hit
        /// the character hit after backspacing 
        public override CharacterHit GetBackspaceCaretCharacterHit(
            CharacterHit    characterHit 
            ) 
        {
            // same operation as move-to-previous 
            return GetPreviousCaretCharacterHit(characterHit);
        }

 
        /// 
        /// Client to get an array of bounding rectangles of a range of characters within a text line. 
        ///  
        /// index of first character of specified range
        /// number of characters of the specified range 
        /// an array of bounding rectangles.
        public override IList GetTextBounds(
            int     firstTextSourceCharacterIndex,
            int     textLength 
            )
        { 
            if (textLength == 0) 
            {
                throw new ArgumentOutOfRangeException("textLength", SR.Get(SRID.ParameterMustBeGreaterThanZero)); 
            }

            if (textLength < 0)
            { 
                firstTextSourceCharacterIndex += textLength;
                textLength = -textLength; 
            } 

            if (firstTextSourceCharacterIndex < _cpFirst) 
            {
                textLength += (firstTextSourceCharacterIndex - _cpFirst);
                firstTextSourceCharacterIndex = _cpFirst;
            } 

            if (firstTextSourceCharacterIndex + textLength > _cpFirst + _cpLength) 
            { 
                textLength = _cpFirst + _cpLength - firstTextSourceCharacterIndex;
            } 


            double x1 = GetDistanceFromCharacterHit(
                new CharacterHit(firstTextSourceCharacterIndex, 0) 
                );
 
            double x2 = GetDistanceFromCharacterHit( 
                new CharacterHit(firstTextSourceCharacterIndex + textLength, 0)
                ); 

            IList boundsList = null;
            int dcp = firstTextSourceCharacterIndex - _cpFirst;
            int ich = 0; 

            boundsList = new List(2); 
 
            foreach(SimpleRun run in _runs)
            { 
                if(     !run.EOT
                    &&  !run.Ghost
                    &&  ich + run.Length > dcp)
                { 
                    if(ich >= dcp + textLength)
                        break; 
 
                    int first = Math.Max(ich, dcp) + _cpFirst;
                    int afterLast = Math.Min(ich + run.Length, dcp + textLength) + _cpFirst; 

                    boundsList.Add(
                        new TextRunBounds(
                            new Rect( 
                                new Point(
                                    DistanceFromCp(first), 
                                    _baselineOffset - run.Baseline 
                                    ),
                                new Point( 
                                    DistanceFromCp(afterLast),
                                    _baselineOffset - run.Baseline + run.Height
                                    )
                                ), 
                            first,
                            afterLast, 
                            run.TextRun 
                            )
                        ); 
                }
                ich += run.Length;
            }
 
            return new TextBounds[]
            { 
                new TextBounds( 
                    new Rect(
                        x1, 
                        0,
                        x2 - x1,
                        _height
                        ), 
                    FlowDirection.LeftToRight,
                    (boundsList == null || boundsList.Count == 0 ? null : boundsList) 
                ) 
            };
        } 


        /// 
        /// Client to get a collection of TextRun objects within a line 
        /// 
        public override IList> GetTextRunSpans() 
        { 
            TextSpan[] textRunSpans = new TextSpan[_runs.Length];
 
            for (int i = 0; i < _runs.Length; i++)
            {
                textRunSpans[i] = new TextSpan(_runs[i].Length, _runs[i].TextRun);
            } 

            return textRunSpans; 
        } 

        ///  
        /// Client to get a IEnumerable<IndexedGlyphRun> to enumerate GlyphRuns
        /// within in a line
        /// 
        ///  
        /// Critical - calls critical code, accepts pointer parameters, unsafe code
        ///  
        [SecurityCritical] 
        public override IEnumerable GetIndexedGlyphRuns()
        { 
            List indexedGlyphRuns = new List(_runs.Length);

            // create each GlyphRun at Point(0, 0)
            Point start = new Point(0, 0); 
            int currentCp = _cpFirst;
 
            foreach(SimpleRun run in _runs) 
            {
                if (run.Length > 0 && !run.Ghost) 
                {
                    IList displayGlyphAdvances;

                    if (_settings.TextFormattingMode == TextFormattingMode.Ideal) 
                    {
                        displayGlyphAdvances = new ThousandthOfEmRealDoubles(run.EmSize, run.NominalAdvances.Length); 
                        for (int i = 0; i < displayGlyphAdvances.Count; i++) 
                        {
                            // convert ideal glyph advance width to real width for displaying 
                            displayGlyphAdvances[i] = _settings.Formatter.IdealToReal(run.NominalAdvances[i]);
                        }
                    }
                    else 
                    {
                        displayGlyphAdvances = new List(run.NominalAdvances.Length); 
                        for (int i = 0; i < run.NominalAdvances.Length; i++) 
                        {
                            // convert ideal glyph advance width to real width for displaying 
                            displayGlyphAdvances.Add(_settings.Formatter.IdealToReal(run.NominalAdvances[i]));
                        }
                    }
 

 
                    GlyphTypeface glyphTypeface = run.Typeface.TryGetGlyphTypeface(); 
                    Invariant.Assert(glyphTypeface != null);
 
                    // this simple run has GlyphRun
                    GlyphRun glyphRun = glyphTypeface.ComputeUnshapedGlyphRun(
                        start,
                        new CharacterBufferRange(run.CharBufferReference, run.Length), 
                        displayGlyphAdvances,
                        run.EmSize, 
                        run.TextRun.Properties.FontHintingEmSize, 
                        run.Typeface.NullFont,
                        CultureMapper.GetSpecificCulture(run.TextRun.Properties.CultureInfo), 
                        null,   // device font name
                        _settings.TextFormattingMode
                        );
 
                    if (glyphRun != null)
                    { 
                        indexedGlyphRuns.Add( 
                            new IndexedGlyphRun(
                                currentCp, 
                                run.Length,
                                glyphRun
                            )
                         ); 
                    }
                } 
 
                currentCp += run.Length;
            } 

            return indexedGlyphRuns;
        }
 

        ///  
        /// Client to acquire a settings 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.
        /// 
        public override TextLineBreak GetTextLineBreak()
        { 
            // No line break implemented in simple text
            return null; 
        } 

 
        /// 
        /// Client to get a collection of collapsed cha----r ranges after a line has been collapsed
        /// 
        public override IList GetTextCollapsedRanges() 
        {
            // A collapsed line is never implemented as simple text line 
            Invariant.Assert(!HasCollapsed); 
            return null;
        } 

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

        /// 
        /// Client to get the number of whitespace characters at the end of the line.
        ///  
        public override int TrailingWhitespaceLength
        { 
            get { return _trailing; } 
        }
 

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


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


        /// 
        /// Client to get distance from paragraph start to line start 
        /// 
        public override double Start 
        { 
            get { return _offset; }
        } 


        /// 
        /// Client to get the total width of this line 
        /// 
        public override double Width 
        { 
            get { return _widthAtTrailing; }
        } 


        /// 
        /// Client to get the total width of this line including width of whitespace characters at the end of the line. 
        /// 
        public override double WidthIncludingTrailingWhitespace 
        { 
            get { return _width; }
        } 


        /// 
        /// Client to get the height of the line 
        /// 
        public override double Height 
        { 
            get { return _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 override double TextHeight 
        {
            // simple path assumes no client-specified line height, i.e., TextParagraphProperties.LineHeight <= 0 
            get { return _height; }
        }

 
        /// 
        /// Client to get the height of the actual black of the line 
        ///  
        public override double Extent
        { 
            get
            {
                CheckBoundingBox();
                return _boundingBox.Bottom - _boundingBox.Top; 
            }
        } 
 

        ///  
        /// Client to get the distance from top to baseline of this text line
        /// 
        public override double Baseline
        { 
            get { return _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 override double TextBaseline 
        {
            // simple path assumes no client-specified line height, i.e., TextParagraphProperties.LineHeight <= 0 
            get { return _baselineOffset; } 
        }
 

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


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


        /// 
        /// Client to get the distance covering all black preceding the leading edge of the line. 
        /// 
        public override double OverhangLeading 
        { 
            get
            { 
                CheckBoundingBox();
                return _boundingBox.Left - Start;
            }
        } 

 
        ///  
        /// Client to get the distance covering all black following the trailing edge of the line.
        ///  
        public override double OverhangTrailing
        {
            get
            { 
                CheckBoundingBox();
                return Start + Width - _boundingBox.Right; 
            } 
        }
 

        /// 
        /// Client to get the distance from the after edge of line height to the after edge of the extent of the line.
        ///  
        public override double OverhangAfter
        { 
            get 
            {
                CheckBoundingBox(); 
                return _boundingBox.Bottom - Height;
            }
        }
 

        ///  
        /// Client to get a boolean value indicates whether content of the line overflows 
        /// the specified paragraph width.
        ///  
        public override bool HasOverflowed
        {
            get { return (_statusFlags & StatusFlags.HasOverflowed) != 0; }
        } 

 
        ///  
        /// Client to get a boolean value indicates whether a line has been collapsed
        ///  
        public override bool HasCollapsed
        {
            // A collapsed line is never implemented as simple text line
            get { return false; } 
        }
 
        ///  
        /// Search forward from the given cp index (inclusive) to find the next navigable cp index.
        /// Return true if one such cp is found, false otherwise. 
        /// 
        private bool FindNextVisibleCp(int cp, out int cpVisible)
        {
            cpVisible = cp; 
            if (cp >= _cpFirst + _cpLength)
            { 
                return false; // Cannot go forward anymore 
            }
 
            int cpRunStart, runIndex;
            GetRunIndexAtCp(cp, out runIndex, out cpRunStart);

            while (runIndex < _runs.Length) 
            {
                // When navigating forward, only the trailing edge of visible content is 
                // navigable. 
                if (_runs[runIndex].IsVisible && !_runs[runIndex].EOT)
                { 
                    cpVisible = Math.Max(cpRunStart, cp);
                    return true;
                }
 
                cpRunStart += _runs[runIndex++].Length;
            } 
 
            return false;
        } 

        /// 
        /// Search backward from the given cp index (inclusive) to find the previous navigable cp index.
        /// Return true if one such cp is found, false otherwise. 
        /// 
        private bool FindPreviousVisibleCp(int cp, out int cpVisible) 
        { 
            cpVisible = cp;
            if (cp < _cpFirst) 
            {
                return false; // Cannot go backward anymore.
            }
 
            int cpRunEnd, runIndex;
            // Position the cpRunEnd at the end of the span that contains the given cp 
            GetRunIndexAtCp(cp, out runIndex, out cpRunEnd); 
            cpRunEnd += _runs[runIndex].Length - 1;
 
            while (runIndex >= 0)
            {
                // Visible content has caret stops at its leading edge.
                if (_runs[runIndex].IsVisible && !_runs[runIndex].EOT) 
                {
                    cpVisible = Math.Min(cpRunEnd, cp); 
                    return true; 
                }
 
                // Newline sequence has caret stops at its leading edge.
                if (_runs[runIndex].EOT)
                {
                    // Get the cp index at the beginning of the newline sequence. 
                    cpVisible = cpRunEnd - _runs[runIndex].Length + 1;
                    return true; 
                } 

                cpRunEnd -= _runs[runIndex--].Length; 
            }

            return false;
        } 

        private void GetRunIndexAtCp( 
            int cp, 
            out int runIndex,
            out int cpRunStart 
            )
        {
            Invariant.Assert(cp >= _cpFirst && cp < _cpFirst + _cpLength);
            cpRunStart= _cpFirst; 
            runIndex = 0;
 
            // Find the span that contains the given cp 
            while (runIndex < _runs.Length && cpRunStart + _runs[runIndex].Length <= cp)
            { 
                cpRunStart += _runs[runIndex++].Length;
            }
        }
    } 

 
    ///  
    /// Simple text run
    ///  
    internal sealed class SimpleRun
    {
        public CharacterBufferReference CharBufferReference;    // character buffer reference
        public int                      Length;                 // CP length 
        public int[]                    NominalAdvances;        // nominal glyph advance widths in ideal units
        public int                      IdealWidth;             // Ideal width of the line. Use ideal width to be consistent with FullTextLine in linebreaking 
        public TextRun                  TextRun;                // text run 
        public TextDecoration           Underline;              // only support single underline
        public Flags                    RunFlags;               // run flags 

        private TextFormatterImp _textFormatterImp;

        [Flags] 
        internal enum Flags : ushort
        { 
            None                  = 0, 
            EOT                   = 0x0001,   // end-of-text mark
            Ghost                 = 0x0002,   // non-existence run - only consume cp 
            TrimTrailingUnderline = 0x0004,   // trailing whitespace should not be underlined
        }

        internal bool EOT 
        {
            get { return (RunFlags & Flags.EOT) != 0; } 
        } 

        internal bool Ghost 
        {
            get { return (RunFlags & Flags.Ghost) != 0; }
        }
 
        internal bool TrimTrailingUnderline
        { 
            get { return (RunFlags & Flags.TrimTrailingUnderline) != 0; } 
            set
            { 
                if (value)
                {
                    RunFlags |= Flags.TrimTrailingUnderline;
                } 
                else
                { 
                    RunFlags &= ~Flags.TrimTrailingUnderline; 
                }
            } 
        }

        internal double Baseline
        { 
            get
            { 
                if (Ghost || EOT) 
                    return 0;
 
                return TextRun.Properties.Typeface.Baseline(TextRun.Properties.FontRenderingEmSize, 1, Util.PixelsPerDip, _textFormatterImp.TextFormattingMode);
            }
        }
 
        internal double Height
        { 
            get 
            {
                if (Ghost || EOT) 
                    return 0;

                return TextRun.Properties.Typeface.LineSpacing(TextRun.Properties.FontRenderingEmSize, 1, Util.PixelsPerDip, _textFormatterImp.TextFormattingMode);
            } 
        }
 
        internal Typeface Typeface 
        {
            get { return TextRun.Properties.Typeface; } 
        }

        internal double EmSize
        { 
            get { return TextRun.Properties.FontRenderingEmSize; }
        } 
 
        internal bool IsVisible
        { 
            get { return this.TextRun is TextCharacters; }
        }

        internal SimpleRun(TextFormatterImp textFormatterImp) 
        {
            _textFormatterImp = textFormatterImp; 
        } 

 
        /// 
        /// Creating a simple text run
        /// 
        /// text formatting settings 
        /// first cp of the run
        /// first cp of the line 
        /// maxium run width 
        /// maximum column width
        /// a SimpleRun object 
        static public SimpleRun Create(
            FormatSettings          settings,
            int                     cp,
            int                     cpFirst, 
            int                     widthLeft,
            int                     widthMax 
            ) 
        {
            TextRun textRun; 
            int runLength;

            CharacterBufferRange charBufferRange = settings.FetchTextRun(
                cp, 
                cpFirst,
                out textRun, 
                out runLength 
                );
 
            return Create(
                settings,
                charBufferRange,
                textRun, 
                cp,
                cpFirst, 
                runLength, 
                widthLeft
                ); 
        }


 
        /// 
        /// Creating a simple text run 
        ///  
        /// text formatting settings
        /// character string associated to textrun 
        /// text run
        /// first cp of the run
        /// first cp of the line
        /// run length 
        /// maximum run width
        /// a SimpleRun object 
        static public SimpleRun Create( 
            FormatSettings          settings,
            CharacterBufferRange    charString, 
            TextRun                 textRun,
            int                     cp,
            int                     cpFirst,
            int                     runLength, 
            int                     widthLeft
            ) 
        { 
            SimpleRun run = null;
 
            if (textRun is TextCharacters)
            {
                if (    textRun.Properties.BaselineAlignment != BaselineAlignment.Baseline
                    ||  (textRun.Properties.TextEffects != null && textRun.Properties.TextEffects.Count != 0) 
                    )
                { 
                    // fast path does not handle the following conditions 
                    //  o  non-default baseline alignment
                    //  o  text drawing effect ( 
                    return null;
                }

                TextDecorationCollection textDecorations = textRun.Properties.TextDecorations; 

                if (    textDecorations != null 
                    &&  textDecorations.Count != 0 
                    &&  !textDecorations.ValueEquals(TextDecorations.Underline))
                { 
                    // we only support a single underline
                    return null;
                }
 
                settings.DigitState.SetTextRunProperties(textRun.Properties);
                if (settings.DigitState.RequiresNumberSubstitution) 
                { 
                    // don't support number substitution in fast path
                    return null; 
                }

                if (charString[0] == TextStore.CharCarriageReturn)
                { 
                    // CR in the middle of text stream treated as explicit paragraph break
                    // simple hard line break 
                    runLength = 1; 
                    if (charString.Length > 1 && charString[1] == TextStore.CharLineFeed)
                    { 
                        runLength = 2;
                    }
                    // This path handles the case where the backing store breaks the text run in between
                    // a Carriage Return and a Line Feed. So we fetch the next run to check whether the next 
                    // character is a line feed.
                    else if (charString.Length == 1) 
                    { 
                        // Prefetch to check for line feed.
                        TextRun newRun; 
                        int newRunLength;
                        CharacterBufferRange newBufferRange = settings.FetchTextRun(
                            cp + 1,
                            cpFirst, 
                            out newRun,
                            out newRunLength 
                            ); 

                        if (newBufferRange.Length > 0 && newBufferRange[0] == TextStore.CharLineFeed) 
                        {
                            // Merge the 2 runs.
                            int lengthOfRun = 2;
                            char[] characterArray = new char[lengthOfRun]; 
                            characterArray[0] = TextStore.CharCarriageReturn;
                            characterArray[1] = TextStore.CharLineFeed; 
                            TextRun mergedTextRun = new TextCharacters(characterArray, 0, lengthOfRun, textRun.Properties); 
                            return new SimpleRun(lengthOfRun, mergedTextRun, (Flags.EOT | Flags.Ghost), settings.Formatter);
                        } 

                    }
                    return new SimpleRun(runLength, textRun, (Flags.EOT | Flags.Ghost), settings.Formatter);
                } 
                else if (charString[0] == TextStore.CharLineFeed)
                { 
                    // LF in the middle of text stream treated as explicit paragraph break 
                    // simple hard line break
                    runLength = 1; 
                    return new SimpleRun(runLength, textRun, (Flags.EOT | Flags.Ghost), settings.Formatter);
                }

                // attempt to create a simple run for text 
                run = CreateSimpleTextRun(
                    charString, 
                    textRun, 
                    settings.Formatter,
                    widthLeft, 
                    settings.Pap.EmergencyWrap
                    );

                if (run == null) 
                {
                    // fail to create simple text run, the run content is too complex 
                    return null; 
                }
 
                // Check for underline condition
                if (textDecorations != null && textDecorations.Count == 1 )
                {
                    run.Underline = textDecorations[0]; 
                }
            } 
            else if (textRun is TextEndOfLine) 
            {
                run = new SimpleRun(runLength, textRun, (Flags.EOT | Flags.Ghost), settings.Formatter); 
            }
            else if (textRun is TextHidden)
            {
                // hidden run 
                run = new SimpleRun(runLength, textRun, Flags.Ghost, settings.Formatter);
            } 
 
            return run;
        } 


        /// 
        /// Create simple run of text, 
        /// returning null if the specified text run cannot be correctly formatted as simple run
        ///  
        static internal SimpleRun CreateSimpleTextRun( 
            CharacterBufferRange    charBufferRange,
            TextRun                 textRun, 
            TextFormatterImp        formatter,
            int                     widthLeft,
            bool                    emergencyWrap
            ) 
        {
            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
                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;
        } 
 

 
        /// 
        /// Construct simple text run
        /// 
        /// run length 
        /// text run
        /// run flags 
        private SimpleRun( 
            int              length,
            TextRun          textRun, 
            Flags            flags,
            TextFormatterImp textFormatterImp
            )
        { 
            Length = length;
            TextRun = textRun; 
            RunFlags = flags; 
            _textFormatterImp = textFormatterImp;
        } 


        /// 
        /// Draw a simple run 
        /// 
        /// drawing bounding box 
        ///  
        /// Critical - as this calls critical function ComputeUnshapedGlyphRun.
        /// Safe - as this just draws text and returns a rect. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal Rect Draw(
            DrawingContext      drawingContext, 
            double              x,
            double              y, 
            bool                visiCodePath 
            )
        { 
            if (Length <= 0 || this.Ghost)
            {
                return Rect.Empty;  // nothing to draw
            } 

            Brush foregroundBrush = TextRun.Properties.ForegroundBrush; 
 
            if(visiCodePath && foregroundBrush is SolidColorBrush)
            { 
                Color color = ((SolidColorBrush)foregroundBrush).Color;
                foregroundBrush = new SolidColorBrush(Color.FromArgb(
                    (byte)(color.A>>2), // * 0.25
                    color.R, 
                    color.G,
                    color.B 
                    )); 
            }
 
            Rect inkBoundingBox;

            IList displayGlyphAdvances;
            if (_textFormatterImp.TextFormattingMode == TextFormattingMode.Ideal) 
            {
                displayGlyphAdvances = new ThousandthOfEmRealDoubles(EmSize, NominalAdvances.Length); 
                for (int i = 0; i < displayGlyphAdvances.Count; i++) 
                {
                    // convert ideal glyph advance width to real width for displaying. 
                    displayGlyphAdvances[i] = _textFormatterImp.IdealToReal(NominalAdvances[i]);
                }
            }
            else 
            {
                displayGlyphAdvances = new List(NominalAdvances.Length); 
                for (int i = 0; i < NominalAdvances.Length; i++) 
                {
                    // convert ideal glyph advance width to real width for displaying. 
                    displayGlyphAdvances.Add(_textFormatterImp.IdealToReal(NominalAdvances[i]));
                }
            }
 
            CharacterBufferRange charBufferRange = new CharacterBufferRange(CharBufferReference, Length);
 
            GlyphTypeface glyphTypeface = Typeface.TryGetGlyphTypeface(); 
            Invariant.Assert(glyphTypeface != null);
 
            GlyphRun glyphRun = glyphTypeface.ComputeUnshapedGlyphRun(
                new Point(x, y),
                charBufferRange,
                displayGlyphAdvances, 
                EmSize,
                TextRun.Properties.FontHintingEmSize, 
                Typeface.NullFont, 
                CultureMapper.GetSpecificCulture(TextRun.Properties.CultureInfo),
                null,  // device font name 
                _textFormatterImp.TextFormattingMode
              );

            if (glyphRun != null) 
            {
                inkBoundingBox = glyphRun.ComputeInkBoundingBox(); 
            } 
            else
            { 
                inkBoundingBox = Rect.Empty;
            }

            if (!inkBoundingBox.IsEmpty) 
            {
                // glyph run's ink bounding box is relative to its origin 
                inkBoundingBox.X += glyphRun.BaselineOrigin.X; 
                inkBoundingBox.Y += glyphRun.BaselineOrigin.Y;
            } 

            if (drawingContext != null)
            {
                if (glyphRun != null) 
                {
                    glyphRun.EmitBackground(drawingContext, TextRun.Properties.BackgroundBrush); 
                    drawingContext.DrawGlyphRun(foregroundBrush, glyphRun); 
                }
 

                // draw underline here
                if (Underline != null)
                { 
                    // Determine number of characters to underline. We don't underline trailing spaces
                    // if the TrimTrailingUnderline flag is set. 
                    int underlineLength = Length; 
                    if (TrimTrailingUnderline)
                    { 
                        while (underlineLength > 0 && TextStore.IsSpace(charBufferRange[underlineLength - 1]))
                        {
                            --underlineLength;
                        } 
                    }
 
                    // Determine the width of the underline. 
                    double dxUnderline = 0;
                    for (int i = 0; i < underlineLength; ++i) 
                    {
                        dxUnderline += _textFormatterImp.IdealToReal(NominalAdvances[i]);
                    }
 
                    // We know only TextDecoration.Underline will be handled in Simple Path.
                    double offset = -Typeface.UnderlinePosition * EmSize; 
                    double penThickness = Typeface.UnderlineThickness * EmSize; 

                    Point lineOrigin = new Point(x, y + offset); 

                    Rect underlineRect = new Rect(
                            lineOrigin.X,
                            lineOrigin.Y - penThickness * 0.5, 
                            dxUnderline,
                            penThickness 
                        ); 

                    // Apply the pair of guidelines: one for baseline and another 
                    // for top edge of undelining line. Both will be snapped to pixel grid.
                    // Guideline pairing algorithm detects the case when these two
                    // guidelines happen to be close to one another and provides
                    // synchronous snapping, so that the gap between baseline and 
                    // undelining line does not depend on the position of text line.
                    drawingContext.PushGuidelineY2(y, lineOrigin.Y - penThickness * 0.5 - y); 
 
                    try
                    { 
                        drawingContext.DrawRectangle(
                            foregroundBrush,
                            null,               // pen
                            underlineRect 
                            );
                    } 
                    finally 
                    {
                        drawingContext.Pop(); 
                    }

                    // underline pen thickness is always positive in fast path
                    inkBoundingBox.Union( 
                        underlineRect
                        ); 
                } 
            }
 
            return inkBoundingBox;
        }

 
        /// 
        /// Scan backward to collect trailing spaces of the run 
        ///  
        /// formatter
        /// trailing spaces 
        /// trailing spaces width
        /// continue collecting the previous run?
        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;
            }
 
            int offsetToFirstChar = CharBufferReference.OffsetToFirstChar;
            CharacterBuffer charBuffer = CharBufferReference.CharacterBuffer; 
            int dcp = Length; 

            if (dcp > 0 && TextStore.IsSpace(charBuffer[offsetToFirstChar + dcp - 1])) 
            {
                // scan backward to find the first blank following a non-blank
                while (dcp > 0 && TextStore.IsSpace(charBuffer[offsetToFirstChar + dcp - 1]))
                { 
                    // summing the ideal value of each glyph
                    trailingSpaceWidth += NominalAdvances[dcp - 1]; 
                    dcp--; 
                    trailing++;
                } 

                return dcp == 0;
            }
 
            return false;
        } 
 

        internal bool IsUnderlineCompatible(SimpleRun nextRun) 
        {
            return     Typeface.Equals(nextRun.Typeface)
                    && EmSize == nextRun.EmSize
                    && Baseline == nextRun.Baseline; 
        }
 
 
        internal double DistanceFromDcp(int dcp)
        { 
            if (Ghost)
            {
                return dcp <= 0 ? 0 : _textFormatterImp.IdealToReal(IdealWidth);
            } 

            if (dcp > Length) 
            { 
                dcp = Length;
            } 

            double distance = 0;

            for(int i = 0; i < dcp; i++) 
            {
                distance += _textFormatterImp.IdealToReal(NominalAdvances[i]); 
            } 

            return distance; 
        }


        internal CharacterHit DcpFromDistance(double distance) 
        {
            if (Ghost) 
            { 
                return (EOT || distance <= 0) ? new CharacterHit() : new CharacterHit(Length, 0);
            } 

            if (Length <= 0)
            {
                return new CharacterHit(); 
            }
 
            int dcp = 0; 
            double currentRealAdvance = 0;
            while (dcp < Length && distance > (currentRealAdvance = _textFormatterImp.IdealToReal(NominalAdvances[dcp]))) 
            {
                distance -= currentRealAdvance;
                dcp++;
            } 

            if (dcp < Length) 
            { 
                // hit occurs in this run
                return new CharacterHit(dcp, (distance > currentRealAdvance / 2 ? 1 : 0)); 
            }

            // hit doesn't occur in this run
            return new CharacterHit(Length - 1, 1); 
        }
    } 
} 

// 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