FullTextState.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 / FullTextState.cs / 1 / FullTextState.cs

                            //------------------------------------------------------------------------ 
//
//  Microsoft Windows Client Platform
//  Copyright (C) Microsoft Corporation
// 
//  File:      FullTextState.cs
// 
//  Contents:  Formatting state of full text 
//
//  Created:   2-25-2004 Worachai Chaoweeraprasit (wchao) 
//  History:   1-29-2005 (wchao) refactor drawing state out, keep full text
//             only for formatting state
//
//----------------------------------------------------------------------- 

 
using System; 
using System.Security;
using System.Globalization; 
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Media; 
using System.Windows.Media.TextFormatting;
using MS.Internal.Shaping; 
using MS.Internal.Generic; 

 
namespace MS.Internal.TextFormatting
{
    /// 
    /// Formatting state of full text 
    /// 
    internal sealed class FullTextState 
    { 
        private TextStore           _store;                     // formatting store for main text
        private TextStore           _markerStore;               // store specifically for marker 
        private StatusFlags         _statusFlags;               // status flags
        private int                 _cpMeasured;                // number of CP successfully measured and fit within column width
        private int                 _lscpHyphenationLookAhead;  // LSCP after the last character examined by the hyphenation code.
 

        [Flags] 
        private enum StatusFlags 
        {
            None            = 0, 
            VerticalAdjust  = 0x00000001,   // vertical adjustment on some runs required
            ForceWrap       = 0x00000002,   // force wrap
            KeepState       = 0x00000040,   // state should be kept in the line
        } 

 
        ///  
        /// A word smaller than seven characters does not require hyphenation. This is based on
        /// observation in real Western books and publications. Advanced research on readability 
        /// seems to support this finding. It suggests that the 'fovea' which is the center
        /// point of our vision, can only see three to four letters before and after the center
        /// of a word.
        ///  
        /// 
        /// "It has been known for over 100 years that when we read, our eyes don't move smoothly 
        /// across the page, but rather make discrete jumps from word to word. We fixate on a word 
        /// for a period of time, roughly 200-250ms, then make a ballistic movement to another word.
        /// These movements are called saccades and usually take 20-35ms. Most saccades are forward 
        /// movements from 7 to 9 letters.
        /// ...
        /// During a single fixation, there is a limit to the amount of information that can be
        /// recognized. The fovea, which is the clear center point of our vision, can only see 
        /// three to four letters to the left and right of fixation at normal reading distances."
        /// 
        /// ["The Science of Word Recognition" by Kevin Larson; July 2004] 
        /// 
        private const int MinCchWordToHyphenate = 7; 


        /// 
        /// Create a fulltext state object from formatting info for subsequent fulltext formatting 
        /// e.g. fulltext line, mix/max computation, potential breakpoints for optimal paragraph formatting.
        ///  
        internal static FullTextState Create( 
            FormatSettings      settings,
            int                 cpFirst, 
            int                 finiteFormatWidth
            )
        {
            // prepare text stores 
            TextStore store = new TextStore(
                settings, 
                cpFirst, 
                0,
                settings.GetFormatWidth(finiteFormatWidth) 
                );

            ParaProp pap = settings.Pap;
            TextStore markerStore = null; 

            if(     pap.FirstLineInParagraph 
                &&  pap.TextMarkerProperties != null 
                &&  pap.TextMarkerProperties.TextSource != null)
            { 
                // create text store specifically for marker
                markerStore = new TextStore(
                    // create specialized settings for marker store e.g. with marker text source
                    new FormatSettings( 
                        settings.Formatter,
                        pap.TextMarkerProperties.TextSource, 
                        new TextRunCacheImp(),  // no cross-call run cache available for marker store 
                        pap,                    // marker by default use the same paragraph properties
                        null,                   // not consider previousLineBreak 
                        true                    // isSingleLineFormatting
                        ),
                    0,                          // marker store always started with cp == 0
                    TextStore.LscpFirstMarker,  // first lscp value for marker text 
                    Constants.InfiniteWidth     // formatWidth
                    ); 
            } 

            // construct a new fulltext state object 
            return new FullTextState(store, markerStore);
        }

 
        /// 
        /// Construct full text state for formatting 
        ///  
        private FullTextState(
            TextStore       store, 
            TextStore       markerStore
            )
        {
            _store = store; 
            _markerStore = markerStore;
        } 
 

        ///  
        /// Number of client CP for which we know the nominal widths fit in the
        /// available margin (i.e., because we've measured them).
        /// 
        internal int CpMeasured 
        {
            get 
            { 
                return _cpMeasured;
            } 
            set
            {
                _cpMeasured = value;
            } 
        }
 
 
        /// 
        /// LSCP immediately after the last LSCP examined by hyphenation code while being run. 
        /// This value is used to calculate a more precise DependentLength value for the line
        /// ended by automatic hyphenation.
        /// 
        internal int LscpHyphenationLookAhead 
        {
            get 
            { 
                return _lscpHyphenationLookAhead;
            } 
        }


        ///  
        /// Set tab stops
        ///  
        ///  
        /// Critical - as this calls TextFormatterContext.SetTabs which is Critical in four
        ///            different places. 
        /// Safe - as the buffers are all created in the same function and the count
        ///        passed in the same as used for creating the buffer.
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal void SetTabs(TextFormatterContext context)
        { 
            unsafe 
            {
                ParaProp pap = _store.Pap; 
                FormatSettings settings = _store.Settings;

                // set up appropriate tab stops
                int incrementalTab = Formatter.RealToIdeal(pap.DefaultIncrementalTab); 
                int lsTbdCount = pap.Tabs != null ? pap.Tabs.Count : 0;
                LsTbd[] lsTbds; 
 
                if (_markerStore != null)
                { 
                    if (pap.Tabs != null && pap.Tabs.Count > 0)
                    {
                        lsTbdCount = pap.Tabs.Count + 1;
                        lsTbds = new LsTbd[lsTbdCount]; 
                        lsTbds[0].ur = settings.TextIndent; // marker requires a tab stop at text start position
                        fixed (LsTbd* plsTbds = &lsTbds[1]) 
                        { 
                            CreateLsTbds(pap, plsTbds, lsTbdCount - 1);
                            context.SetTabs(incrementalTab, plsTbds - 1, lsTbdCount); 
                        }
                    }
                    else
                    { 
                        LsTbd markerRequiredLsTbd = new LsTbd();
                        markerRequiredLsTbd.ur = settings.TextIndent; // marker requires a tab stop at text start position 
                        context.SetTabs(incrementalTab, &markerRequiredLsTbd, 1); 
                    }
                } 
                else
                {
                    if (pap.Tabs != null && pap.Tabs.Count > 0)
                    { 
                        lsTbds = new LsTbd[lsTbdCount];
                        fixed (LsTbd* plsTbds = &lsTbds[0]) 
                        { 
                            CreateLsTbds(pap, plsTbds, lsTbdCount);
                            context.SetTabs(incrementalTab, plsTbds, lsTbdCount); 
                        }
                    }
                    else
                    { 
                        // work with only incremental tab
                        context.SetTabs(incrementalTab, null, 0); 
                    } 
                }
            } 
        }


        ///  
        /// Fill a fixed buffer of LsTbd with
        ///  
        ///  
        /// Critical - This method writes into unmanaged array.
        ///  
        [SecurityCritical]
        private unsafe void CreateLsTbds(
            ParaProp        pap,
            LsTbd*          plsTbds, 
            int             lsTbdCount
            ) 
        { 
            for (int i = 0; i < lsTbdCount; i++)
            { 
                TextTabProperties tab = (TextTabProperties)pap.Tabs[i];
                plsTbds[i].lskt = Convert.LsKTabFromTabAlignment(tab.Alignment);
                plsTbds[i].ur = Formatter.RealToIdeal(tab.Location);
 
                if (tab.TabLeader != 0)
                { 
                    // Note: LS does not currently support surrogate character as tab leader and aligning character 
                    plsTbds[i].wchTabLeader = (char)tab.TabLeader;
 
                    // tab leader requires state at display time for tab leader width fetching
                    _statusFlags |= StatusFlags.KeepState;
                }
                plsTbds[i].wchCharTab = (char)tab.AligningCharacter; 
            }
        } 
 

        ///  
        /// Get distance from the start of main text to the end of marker
        /// 
        /// 
        /// Positive distance is filtered out. Marker overlapping the main text is not supported. 
        /// 
        internal int GetMainTextToMarkerIdealDistance() 
        { 
            if (_markerStore != null)
            { 
                return Math.Min(0, Formatter.RealToIdeal(_markerStore.Pap.TextMarkerProperties.Offset) - _store.Settings.TextIndent);
            }
            return 0;
        } 

 
        ///  
        /// Map LSCP to host CP, and return the last LSRun
        /// before the specified limit. 
        /// 
        internal LSRun CountText(
            int         lscpLim,
            int         cpFirst, 
            out int     count
            ) 
        { 
            LSRun lastRun = null;
            count = 0; 

            int lsccp = lscpLim - _store.CpFirst;
            Debug.Assert(lsccp > 0, "Zero-length text line!");
 
            foreach (Span span in _store.PlsrunVector)
            { 
                if (lsccp <= 0) 
                    break;
 
                Plsrun plsrun = (Plsrun)span.element;

                // There should be no marker runs in _plsrunVector.
                Debug.Assert(!TextStore.IsMarker(plsrun)); 

                // Is it a normal, non-static, LSRun? 
                if (plsrun >= Plsrun.FormatAnchor) 
                {
                    // Get the run and remember the last run. 
                    lastRun = _store.GetRun(plsrun);

                    // Accumulate the length.
                    int cpRun = lastRun.Length; 
                    if (cpRun > 0)
                    { 
                        if (lsccp < span.length && cpRun == span.length) 
                        {
                            count += lsccp; 
                            break;
                        }
                        count += cpRun;
                    } 
                }
 
                lsccp -= span.length; 
            }
 
            // make char count relative to cpFirst as the cpFirst of this metrics may not
            // be the same as the cpFirst of the store in optimal paragraph formatting.
            count = count - cpFirst + _store.CpFirst;
 
            return lastRun;
        } 
 

        ///  
        /// Convert the specified external CP to LSCP corresponding to a possible break position for optimal break.
        /// 
        /// 
        /// There is a generic issue that one CP could map to multiple LSCPs when it comes to 
        /// lsrun that occupies no actual CP. Such lsrun is generated by line layout during
        /// formatting to accomplsih specific purpose i.e. run representing open and close 
        /// reverse object or a fake-linebreak lsrun. 
        ///
        /// According to SergeyGe and Antons, LS will never breaks immediately after open reverse 
        /// or immediately before close reverse. It also never break before a linebreak character.
        ///
        /// Therefore, it is safe to make an assumption here that one CP will indeed map to one
        /// LSCP given being the LSCP before the open reverse and the LSCP after the close reverse. 
        /// Never the vice-versa.
        /// 
        /// This is the reason why the loop inside this method may overread the PLSRUN span by 
        /// one span at the end. The loop is designed to skip over all the lsruns with zero CP
        /// which is not the open reverse. 
        ///
        /// This logic is exactly the same as the one used by FullTextLine.PrefetchLSRuns. Any
        /// attempt to change it needs to be thoroughly reviewed. The same logic is to be applied
        /// accordingly on PrefetchLSRuns. 
        ///
        /// [Wchao, 5-24-2005] 
        /// 
        /// 
        internal int GetBreakpointInternalCp(int cp) 
        {
            int ccp = cp - _store.CpFirst;
            int lscp = _store.CpFirst;
            int ccpCurrent = 0; 

            SpanVector plsrunVector = _store.PlsrunVector; 
            LSRun lsrun; 
            int i = 0;
 
            int lastSpanLength = 0;
            int lastRunLength = 0;

            do 
            {
                Span span = plsrunVector[i]; 
                Plsrun plsrun = (Plsrun)span.element; 
                lsrun = _store.GetRun(plsrun);
 
                if (ccp == ccpCurrent && lsrun.Type == Plsrun.Reverse)
                {
                    break;
                } 

                lastSpanLength = span.length; 
                lastRunLength = (plsrun >= Plsrun.FormatAnchor ? lsrun.Length : 0); 

                lscp += lastSpanLength; 
                ccpCurrent += lastRunLength;

            } while (   ++i < plsrunVector.Count
                    &&  lsrun.Type != Plsrun.ParaBreak 
                    &&  ccp >= ccpCurrent
                ); 
 
            // Since we may overread the span vector by one span,
            // we need to subtract the accumulated lscp by the number of cp we may have overread. 

            if (ccpCurrent == ccp || lastSpanLength == lastRunLength)
                return lscp - ccpCurrent + ccp;
 
            Invariant.Assert(ccpCurrent - ccp == lastRunLength);
            return lscp - lastSpanLength; 
        } 

 
        /// 
        /// Find the hyphen break following or preceding the specified current LSCP
        /// 
        ///  
        /// This method never checks whether the specified current LSCP is already right
        /// at a hyphen break. It either finds the next or the previous break regardless. 
        /// 
        /// A negative lscchLim param value indicates the caller finds the hyphen immediately
        /// before the specified character index. 
        /// 
        /// the current LSCP
        /// the number of LSCP to search for break
        /// flag indicates whether lscpCurrent is the beginning of the word to hyphenate 
        /// LSCP of the hyphen
        /// Hyphen properties 
        internal bool FindNextHyphenBreak( 
            int         lscpCurrent,
            int         lscchLim, 
            bool        isCurrentAtWordStart,
            ref int     lscpHyphen,
            ref LsHyph  lshyph
            ) 
        {
            lshyph = new LsHyph();  // no additional hyphen properties for now 
 
            if (_store.Pap.Hyphenator != null)
            { 
                int lscpChunk;
                int lscchChunk;

                LexicalChunk chunk = GetChunk( 
                    _store.Pap.Hyphenator,
                    lscpCurrent, 
                    lscchLim, 
                    isCurrentAtWordStart,
                    out lscpChunk, 
                    out lscchChunk
                    );

                _lscpHyphenationLookAhead = lscpChunk + lscchChunk; 

                if (!chunk.IsNoBreak) 
                { 
                    int ichCurrent = chunk.LSCPToCharacterIndex(lscpCurrent - lscpChunk);
                    int ichLim = chunk.LSCPToCharacterIndex(lscpCurrent + lscchLim - lscpChunk); 

                    if (lscchLim >= 0)
                    {
                        int ichNext = chunk.Breaks.GetNextBreak(ichCurrent); 

                        if (ichNext >= 0 && ichNext > ichCurrent && ichNext <= ichLim) 
                        { 
                            // -1 because ichNext is the character index where break occurs in front of it,
                            // while LSCP is the position where break occurs after it. 
                            lscpHyphen = chunk.CharacterIndexToLSCP(ichNext - 1) + lscpChunk;
                            return true;
                        }
                    } 
                    else
                    { 
                        int ichPrev = chunk.Breaks.GetPreviousBreak(ichCurrent); 

                        if (ichPrev >= 0 && ichPrev <= ichCurrent && ichPrev > ichLim) 
                        {
                            // -1 because ichPrev is the character index where break occurs in front of it,
                            // while LSCP is the position where break occurs after it.
                            lscpHyphen = chunk.CharacterIndexToLSCP(ichPrev - 1) + lscpChunk; 
                            return true;
                        } 
                    } 
                }
            } 
            return false;
        }

 
        /// 
        /// Get the lexical chunk the specified current LSCP is within. 
        ///  
        private LexicalChunk GetChunk(
            TextLexicalService      lexicalService, 
            int                     lscpCurrent,
            int                     lscchLim,
            bool                    isCurrentAtWordStart,
            out int                 lscpChunk, 
            out int                 lscchChunk
            ) 
        { 
            int lscpStart = lscpCurrent;
            int lscpLim = lscpStart + lscchLim; 

            int cpFirst = _store.CpFirst;

            if (lscpStart > lscpLim) 
            {
                // Start is always before limit 
                lscpStart = lscpLim; 
                lscpLim = lscpCurrent;
            } 

            LexicalChunk chunk = new LexicalChunk();
            int cchWordMax;
            CultureInfo textCulture; 
            SpanVector textVector;
 
            char[] rawText = _store.CollectRawWord( 
                lscpStart,
                isCurrentAtWordStart, 
                out lscpChunk,
                out lscchChunk,
                out textCulture,
                out cchWordMax, 
                out textVector
                ); 
 
            if (    rawText != null
                &&  cchWordMax >= MinCchWordToHyphenate 
                &&  lscpLim < lscpChunk + lscchChunk
                &&  textCulture != null
                &&  lexicalService != null
                &&  lexicalService.IsCultureSupported(textCulture) 
                )
            { 
                // analyze the chunk and produce the lexical chunk to cache 
                TextLexicalBreaks breaks = lexicalService.AnalyzeText(
                    rawText, 
                    rawText.Length,
                    textCulture
                    );
 
                if (breaks != null)
                { 
                    chunk = new LexicalChunk(breaks, textVector); 
                }
            } 

            return chunk;
        }
 

        ///  
        /// Get a text store containing the specified plsrun 
        /// 
        internal TextStore StoreFrom(Plsrun plsrun) 
        {
            return TextStore.IsMarker(plsrun) ? _markerStore : _store;
        }
 

        ///  
        /// Get a text store containing the specified lscp 
        /// 
        internal TextStore StoreFrom(int lscp) 
        {
            return lscp < 0 ? _markerStore : _store;
        }
 

        ///  
        /// Flag indicating whether vertical adjustment of some runs is required 
        /// 
        internal bool VerticalAdjust 
        {
            get { return (_statusFlags & StatusFlags.VerticalAdjust) != 0; }
            set
            { 
                if (value)
                    _statusFlags |= StatusFlags.VerticalAdjust; 
                else 
                    _statusFlags &= ~StatusFlags.VerticalAdjust;
            } 
        }


        ///  
        /// Flag indicating whether force wrap is required
        ///  
        internal bool ForceWrap 
        {
            get { return (_statusFlags & StatusFlags.ForceWrap) != 0; } 
            set
            {
                if (value)
                    _statusFlags |= StatusFlags.ForceWrap; 
                else
                    _statusFlags &= ~StatusFlags.ForceWrap; 
            } 
        }
 

        /// 
        /// Flag indicating whether state should be kept in the line
        ///  
        internal bool KeepState
        { 
            get { return (_statusFlags & StatusFlags.KeepState) != 0; } 
        }
 

        /// 
        /// Formatting store for main text
        ///  
        internal TextStore TextStore
        { 
            get { return _store; } 
        }
 

        /// 
        /// Formatting store for marker text
        ///  
        internal TextStore TextMarkerStore
        { 
            get { return _markerStore; } 
        }
 

        /// 
        /// Current formatter
        ///  
        internal TextFormatterImp Formatter
        { 
            get { return _store.Settings.Formatter; } 
        }
 

        /// 
        /// formattng ideal width
        ///  
        internal int FormatWidth
        { 
            get { return _store.FormatWidth; } 
        }
    } 
}

// 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
// 
//  File:      FullTextState.cs
// 
//  Contents:  Formatting state of full text 
//
//  Created:   2-25-2004 Worachai Chaoweeraprasit (wchao) 
//  History:   1-29-2005 (wchao) refactor drawing state out, keep full text
//             only for formatting state
//
//----------------------------------------------------------------------- 

 
using System; 
using System.Security;
using System.Globalization; 
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Media; 
using System.Windows.Media.TextFormatting;
using MS.Internal.Shaping; 
using MS.Internal.Generic; 

 
namespace MS.Internal.TextFormatting
{
    /// 
    /// Formatting state of full text 
    /// 
    internal sealed class FullTextState 
    { 
        private TextStore           _store;                     // formatting store for main text
        private TextStore           _markerStore;               // store specifically for marker 
        private StatusFlags         _statusFlags;               // status flags
        private int                 _cpMeasured;                // number of CP successfully measured and fit within column width
        private int                 _lscpHyphenationLookAhead;  // LSCP after the last character examined by the hyphenation code.
 

        [Flags] 
        private enum StatusFlags 
        {
            None            = 0, 
            VerticalAdjust  = 0x00000001,   // vertical adjustment on some runs required
            ForceWrap       = 0x00000002,   // force wrap
            KeepState       = 0x00000040,   // state should be kept in the line
        } 

 
        ///  
        /// A word smaller than seven characters does not require hyphenation. This is based on
        /// observation in real Western books and publications. Advanced research on readability 
        /// seems to support this finding. It suggests that the 'fovea' which is the center
        /// point of our vision, can only see three to four letters before and after the center
        /// of a word.
        ///  
        /// 
        /// "It has been known for over 100 years that when we read, our eyes don't move smoothly 
        /// across the page, but rather make discrete jumps from word to word. We fixate on a word 
        /// for a period of time, roughly 200-250ms, then make a ballistic movement to another word.
        /// These movements are called saccades and usually take 20-35ms. Most saccades are forward 
        /// movements from 7 to 9 letters.
        /// ...
        /// During a single fixation, there is a limit to the amount of information that can be
        /// recognized. The fovea, which is the clear center point of our vision, can only see 
        /// three to four letters to the left and right of fixation at normal reading distances."
        /// 
        /// ["The Science of Word Recognition" by Kevin Larson; July 2004] 
        /// 
        private const int MinCchWordToHyphenate = 7; 


        /// 
        /// Create a fulltext state object from formatting info for subsequent fulltext formatting 
        /// e.g. fulltext line, mix/max computation, potential breakpoints for optimal paragraph formatting.
        ///  
        internal static FullTextState Create( 
            FormatSettings      settings,
            int                 cpFirst, 
            int                 finiteFormatWidth
            )
        {
            // prepare text stores 
            TextStore store = new TextStore(
                settings, 
                cpFirst, 
                0,
                settings.GetFormatWidth(finiteFormatWidth) 
                );

            ParaProp pap = settings.Pap;
            TextStore markerStore = null; 

            if(     pap.FirstLineInParagraph 
                &&  pap.TextMarkerProperties != null 
                &&  pap.TextMarkerProperties.TextSource != null)
            { 
                // create text store specifically for marker
                markerStore = new TextStore(
                    // create specialized settings for marker store e.g. with marker text source
                    new FormatSettings( 
                        settings.Formatter,
                        pap.TextMarkerProperties.TextSource, 
                        new TextRunCacheImp(),  // no cross-call run cache available for marker store 
                        pap,                    // marker by default use the same paragraph properties
                        null,                   // not consider previousLineBreak 
                        true                    // isSingleLineFormatting
                        ),
                    0,                          // marker store always started with cp == 0
                    TextStore.LscpFirstMarker,  // first lscp value for marker text 
                    Constants.InfiniteWidth     // formatWidth
                    ); 
            } 

            // construct a new fulltext state object 
            return new FullTextState(store, markerStore);
        }

 
        /// 
        /// Construct full text state for formatting 
        ///  
        private FullTextState(
            TextStore       store, 
            TextStore       markerStore
            )
        {
            _store = store; 
            _markerStore = markerStore;
        } 
 

        ///  
        /// Number of client CP for which we know the nominal widths fit in the
        /// available margin (i.e., because we've measured them).
        /// 
        internal int CpMeasured 
        {
            get 
            { 
                return _cpMeasured;
            } 
            set
            {
                _cpMeasured = value;
            } 
        }
 
 
        /// 
        /// LSCP immediately after the last LSCP examined by hyphenation code while being run. 
        /// This value is used to calculate a more precise DependentLength value for the line
        /// ended by automatic hyphenation.
        /// 
        internal int LscpHyphenationLookAhead 
        {
            get 
            { 
                return _lscpHyphenationLookAhead;
            } 
        }


        ///  
        /// Set tab stops
        ///  
        ///  
        /// Critical - as this calls TextFormatterContext.SetTabs which is Critical in four
        ///            different places. 
        /// Safe - as the buffers are all created in the same function and the count
        ///        passed in the same as used for creating the buffer.
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal void SetTabs(TextFormatterContext context)
        { 
            unsafe 
            {
                ParaProp pap = _store.Pap; 
                FormatSettings settings = _store.Settings;

                // set up appropriate tab stops
                int incrementalTab = Formatter.RealToIdeal(pap.DefaultIncrementalTab); 
                int lsTbdCount = pap.Tabs != null ? pap.Tabs.Count : 0;
                LsTbd[] lsTbds; 
 
                if (_markerStore != null)
                { 
                    if (pap.Tabs != null && pap.Tabs.Count > 0)
                    {
                        lsTbdCount = pap.Tabs.Count + 1;
                        lsTbds = new LsTbd[lsTbdCount]; 
                        lsTbds[0].ur = settings.TextIndent; // marker requires a tab stop at text start position
                        fixed (LsTbd* plsTbds = &lsTbds[1]) 
                        { 
                            CreateLsTbds(pap, plsTbds, lsTbdCount - 1);
                            context.SetTabs(incrementalTab, plsTbds - 1, lsTbdCount); 
                        }
                    }
                    else
                    { 
                        LsTbd markerRequiredLsTbd = new LsTbd();
                        markerRequiredLsTbd.ur = settings.TextIndent; // marker requires a tab stop at text start position 
                        context.SetTabs(incrementalTab, &markerRequiredLsTbd, 1); 
                    }
                } 
                else
                {
                    if (pap.Tabs != null && pap.Tabs.Count > 0)
                    { 
                        lsTbds = new LsTbd[lsTbdCount];
                        fixed (LsTbd* plsTbds = &lsTbds[0]) 
                        { 
                            CreateLsTbds(pap, plsTbds, lsTbdCount);
                            context.SetTabs(incrementalTab, plsTbds, lsTbdCount); 
                        }
                    }
                    else
                    { 
                        // work with only incremental tab
                        context.SetTabs(incrementalTab, null, 0); 
                    } 
                }
            } 
        }


        ///  
        /// Fill a fixed buffer of LsTbd with
        ///  
        ///  
        /// Critical - This method writes into unmanaged array.
        ///  
        [SecurityCritical]
        private unsafe void CreateLsTbds(
            ParaProp        pap,
            LsTbd*          plsTbds, 
            int             lsTbdCount
            ) 
        { 
            for (int i = 0; i < lsTbdCount; i++)
            { 
                TextTabProperties tab = (TextTabProperties)pap.Tabs[i];
                plsTbds[i].lskt = Convert.LsKTabFromTabAlignment(tab.Alignment);
                plsTbds[i].ur = Formatter.RealToIdeal(tab.Location);
 
                if (tab.TabLeader != 0)
                { 
                    // Note: LS does not currently support surrogate character as tab leader and aligning character 
                    plsTbds[i].wchTabLeader = (char)tab.TabLeader;
 
                    // tab leader requires state at display time for tab leader width fetching
                    _statusFlags |= StatusFlags.KeepState;
                }
                plsTbds[i].wchCharTab = (char)tab.AligningCharacter; 
            }
        } 
 

        ///  
        /// Get distance from the start of main text to the end of marker
        /// 
        /// 
        /// Positive distance is filtered out. Marker overlapping the main text is not supported. 
        /// 
        internal int GetMainTextToMarkerIdealDistance() 
        { 
            if (_markerStore != null)
            { 
                return Math.Min(0, Formatter.RealToIdeal(_markerStore.Pap.TextMarkerProperties.Offset) - _store.Settings.TextIndent);
            }
            return 0;
        } 

 
        ///  
        /// Map LSCP to host CP, and return the last LSRun
        /// before the specified limit. 
        /// 
        internal LSRun CountText(
            int         lscpLim,
            int         cpFirst, 
            out int     count
            ) 
        { 
            LSRun lastRun = null;
            count = 0; 

            int lsccp = lscpLim - _store.CpFirst;
            Debug.Assert(lsccp > 0, "Zero-length text line!");
 
            foreach (Span span in _store.PlsrunVector)
            { 
                if (lsccp <= 0) 
                    break;
 
                Plsrun plsrun = (Plsrun)span.element;

                // There should be no marker runs in _plsrunVector.
                Debug.Assert(!TextStore.IsMarker(plsrun)); 

                // Is it a normal, non-static, LSRun? 
                if (plsrun >= Plsrun.FormatAnchor) 
                {
                    // Get the run and remember the last run. 
                    lastRun = _store.GetRun(plsrun);

                    // Accumulate the length.
                    int cpRun = lastRun.Length; 
                    if (cpRun > 0)
                    { 
                        if (lsccp < span.length && cpRun == span.length) 
                        {
                            count += lsccp; 
                            break;
                        }
                        count += cpRun;
                    } 
                }
 
                lsccp -= span.length; 
            }
 
            // make char count relative to cpFirst as the cpFirst of this metrics may not
            // be the same as the cpFirst of the store in optimal paragraph formatting.
            count = count - cpFirst + _store.CpFirst;
 
            return lastRun;
        } 
 

        ///  
        /// Convert the specified external CP to LSCP corresponding to a possible break position for optimal break.
        /// 
        /// 
        /// There is a generic issue that one CP could map to multiple LSCPs when it comes to 
        /// lsrun that occupies no actual CP. Such lsrun is generated by line layout during
        /// formatting to accomplsih specific purpose i.e. run representing open and close 
        /// reverse object or a fake-linebreak lsrun. 
        ///
        /// According to SergeyGe and Antons, LS will never breaks immediately after open reverse 
        /// or immediately before close reverse. It also never break before a linebreak character.
        ///
        /// Therefore, it is safe to make an assumption here that one CP will indeed map to one
        /// LSCP given being the LSCP before the open reverse and the LSCP after the close reverse. 
        /// Never the vice-versa.
        /// 
        /// This is the reason why the loop inside this method may overread the PLSRUN span by 
        /// one span at the end. The loop is designed to skip over all the lsruns with zero CP
        /// which is not the open reverse. 
        ///
        /// This logic is exactly the same as the one used by FullTextLine.PrefetchLSRuns. Any
        /// attempt to change it needs to be thoroughly reviewed. The same logic is to be applied
        /// accordingly on PrefetchLSRuns. 
        ///
        /// [Wchao, 5-24-2005] 
        /// 
        /// 
        internal int GetBreakpointInternalCp(int cp) 
        {
            int ccp = cp - _store.CpFirst;
            int lscp = _store.CpFirst;
            int ccpCurrent = 0; 

            SpanVector plsrunVector = _store.PlsrunVector; 
            LSRun lsrun; 
            int i = 0;
 
            int lastSpanLength = 0;
            int lastRunLength = 0;

            do 
            {
                Span span = plsrunVector[i]; 
                Plsrun plsrun = (Plsrun)span.element; 
                lsrun = _store.GetRun(plsrun);
 
                if (ccp == ccpCurrent && lsrun.Type == Plsrun.Reverse)
                {
                    break;
                } 

                lastSpanLength = span.length; 
                lastRunLength = (plsrun >= Plsrun.FormatAnchor ? lsrun.Length : 0); 

                lscp += lastSpanLength; 
                ccpCurrent += lastRunLength;

            } while (   ++i < plsrunVector.Count
                    &&  lsrun.Type != Plsrun.ParaBreak 
                    &&  ccp >= ccpCurrent
                ); 
 
            // Since we may overread the span vector by one span,
            // we need to subtract the accumulated lscp by the number of cp we may have overread. 

            if (ccpCurrent == ccp || lastSpanLength == lastRunLength)
                return lscp - ccpCurrent + ccp;
 
            Invariant.Assert(ccpCurrent - ccp == lastRunLength);
            return lscp - lastSpanLength; 
        } 

 
        /// 
        /// Find the hyphen break following or preceding the specified current LSCP
        /// 
        ///  
        /// This method never checks whether the specified current LSCP is already right
        /// at a hyphen break. It either finds the next or the previous break regardless. 
        /// 
        /// A negative lscchLim param value indicates the caller finds the hyphen immediately
        /// before the specified character index. 
        /// 
        /// the current LSCP
        /// the number of LSCP to search for break
        /// flag indicates whether lscpCurrent is the beginning of the word to hyphenate 
        /// LSCP of the hyphen
        /// Hyphen properties 
        internal bool FindNextHyphenBreak( 
            int         lscpCurrent,
            int         lscchLim, 
            bool        isCurrentAtWordStart,
            ref int     lscpHyphen,
            ref LsHyph  lshyph
            ) 
        {
            lshyph = new LsHyph();  // no additional hyphen properties for now 
 
            if (_store.Pap.Hyphenator != null)
            { 
                int lscpChunk;
                int lscchChunk;

                LexicalChunk chunk = GetChunk( 
                    _store.Pap.Hyphenator,
                    lscpCurrent, 
                    lscchLim, 
                    isCurrentAtWordStart,
                    out lscpChunk, 
                    out lscchChunk
                    );

                _lscpHyphenationLookAhead = lscpChunk + lscchChunk; 

                if (!chunk.IsNoBreak) 
                { 
                    int ichCurrent = chunk.LSCPToCharacterIndex(lscpCurrent - lscpChunk);
                    int ichLim = chunk.LSCPToCharacterIndex(lscpCurrent + lscchLim - lscpChunk); 

                    if (lscchLim >= 0)
                    {
                        int ichNext = chunk.Breaks.GetNextBreak(ichCurrent); 

                        if (ichNext >= 0 && ichNext > ichCurrent && ichNext <= ichLim) 
                        { 
                            // -1 because ichNext is the character index where break occurs in front of it,
                            // while LSCP is the position where break occurs after it. 
                            lscpHyphen = chunk.CharacterIndexToLSCP(ichNext - 1) + lscpChunk;
                            return true;
                        }
                    } 
                    else
                    { 
                        int ichPrev = chunk.Breaks.GetPreviousBreak(ichCurrent); 

                        if (ichPrev >= 0 && ichPrev <= ichCurrent && ichPrev > ichLim) 
                        {
                            // -1 because ichPrev is the character index where break occurs in front of it,
                            // while LSCP is the position where break occurs after it.
                            lscpHyphen = chunk.CharacterIndexToLSCP(ichPrev - 1) + lscpChunk; 
                            return true;
                        } 
                    } 
                }
            } 
            return false;
        }

 
        /// 
        /// Get the lexical chunk the specified current LSCP is within. 
        ///  
        private LexicalChunk GetChunk(
            TextLexicalService      lexicalService, 
            int                     lscpCurrent,
            int                     lscchLim,
            bool                    isCurrentAtWordStart,
            out int                 lscpChunk, 
            out int                 lscchChunk
            ) 
        { 
            int lscpStart = lscpCurrent;
            int lscpLim = lscpStart + lscchLim; 

            int cpFirst = _store.CpFirst;

            if (lscpStart > lscpLim) 
            {
                // Start is always before limit 
                lscpStart = lscpLim; 
                lscpLim = lscpCurrent;
            } 

            LexicalChunk chunk = new LexicalChunk();
            int cchWordMax;
            CultureInfo textCulture; 
            SpanVector textVector;
 
            char[] rawText = _store.CollectRawWord( 
                lscpStart,
                isCurrentAtWordStart, 
                out lscpChunk,
                out lscchChunk,
                out textCulture,
                out cchWordMax, 
                out textVector
                ); 
 
            if (    rawText != null
                &&  cchWordMax >= MinCchWordToHyphenate 
                &&  lscpLim < lscpChunk + lscchChunk
                &&  textCulture != null
                &&  lexicalService != null
                &&  lexicalService.IsCultureSupported(textCulture) 
                )
            { 
                // analyze the chunk and produce the lexical chunk to cache 
                TextLexicalBreaks breaks = lexicalService.AnalyzeText(
                    rawText, 
                    rawText.Length,
                    textCulture
                    );
 
                if (breaks != null)
                { 
                    chunk = new LexicalChunk(breaks, textVector); 
                }
            } 

            return chunk;
        }
 

        ///  
        /// Get a text store containing the specified plsrun 
        /// 
        internal TextStore StoreFrom(Plsrun plsrun) 
        {
            return TextStore.IsMarker(plsrun) ? _markerStore : _store;
        }
 

        ///  
        /// Get a text store containing the specified lscp 
        /// 
        internal TextStore StoreFrom(int lscp) 
        {
            return lscp < 0 ? _markerStore : _store;
        }
 

        ///  
        /// Flag indicating whether vertical adjustment of some runs is required 
        /// 
        internal bool VerticalAdjust 
        {
            get { return (_statusFlags & StatusFlags.VerticalAdjust) != 0; }
            set
            { 
                if (value)
                    _statusFlags |= StatusFlags.VerticalAdjust; 
                else 
                    _statusFlags &= ~StatusFlags.VerticalAdjust;
            } 
        }


        ///  
        /// Flag indicating whether force wrap is required
        ///  
        internal bool ForceWrap 
        {
            get { return (_statusFlags & StatusFlags.ForceWrap) != 0; } 
            set
            {
                if (value)
                    _statusFlags |= StatusFlags.ForceWrap; 
                else
                    _statusFlags &= ~StatusFlags.ForceWrap; 
            } 
        }
 

        /// 
        /// Flag indicating whether state should be kept in the line
        ///  
        internal bool KeepState
        { 
            get { return (_statusFlags & StatusFlags.KeepState) != 0; } 
        }
 

        /// 
        /// Formatting store for main text
        ///  
        internal TextStore TextStore
        { 
            get { return _store; } 
        }
 

        /// 
        /// Formatting store for marker text
        ///  
        internal TextStore TextMarkerStore
        { 
            get { return _markerStore; } 
        }
 

        /// 
        /// Current formatter
        ///  
        internal TextFormatterImp Formatter
        { 
            get { return _store.Settings.Formatter; } 
        }
 

        /// 
        /// formattng ideal width
        ///  
        internal int FormatWidth
        { 
            get { return _store.FormatWidth; } 
        }
    } 
}

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