FullTextState.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 / FullTextState.cs / 1305600 / 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.
        private bool                _isSideways; 

 
        [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
                        settings.TextFormattingMode,
                        settings.IsSideways 
                        ),
                    0,                           // marker store always started with cp == 0 
                    TextStore.LscpFirstMarker,   // first lscp value for marker text 
                    Constants.IdealInfiniteWidth // formatWidth
                    ); 
            }

            // construct a new fulltext state object
            return new FullTextState(store, markerStore, settings.IsSideways); 
        }
 
 
        /// 
        /// Construct full text state for formatting 
        /// 
        private FullTextState(
            TextStore       store,
            TextStore       markerStore, 
            bool isSideways
            ) 
        { 
            _isSideways = isSideways;
            _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; 
            }
        } 
 
        internal TextFormattingMode TextFormattingMode
        { 
            get
            {
                return Formatter.TextFormattingMode;
            } 
        }
 
        internal bool IsSideways 
        {
            get 
            {
                return _isSideways;
            }
        } 

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

        ///  
        /// 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 = TextFormatterImp.RealToIdeal(tab.Location);
 
                if (tab.TabLeader != 0) 
                {
                    // Note: LS does not currently support surrogate character as tab leader and aligning character 
                    plsTbds[i].wchTabLeader = (char)tab.TabLeader;

                    // tab leader requires state at display time for tab leader width fetching
                    _statusFlags |= StatusFlags.KeepState; 
                }
                plsTbds[i].wchCharTab = (char)tab.AligningCharacter; 
            } 
        }
 

        /// 
        /// 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, TextFormatterImp.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,
                _isSideways,
                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