Code:
/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Core / 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 [....] ([....]) // History: 1-29-2005 ([....]) 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. /// /// [[....], 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; SpanVectortextVector; 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
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- DocumentReferenceCollection.cs
- ImageMap.cs
- RectAnimation.cs
- PointLight.cs
- FormViewUpdateEventArgs.cs
- CheckBoxPopupAdapter.cs
- EntityDataSourceDesigner.cs
- elementinformation.cs
- CodeDOMUtility.cs
- InvalidFilterCriteriaException.cs
- TextSerializer.cs
- PerspectiveCamera.cs
- UndirectedGraph.cs
- DataGridViewRowConverter.cs
- NavigationFailedEventArgs.cs
- XmlMapping.cs
- StdValidatorsAndConverters.cs
- RepeatInfo.cs
- SystemIcons.cs
- StringReader.cs
- HTTPNotFoundHandler.cs
- ContourSegment.cs
- XmlSortKey.cs
- Imaging.cs
- WebServiceResponseDesigner.cs
- Context.cs
- XsltConvert.cs
- WindowsGraphics.cs
- Hashtable.cs
- BindingList.cs
- sqlpipe.cs
- BuildProviderCollection.cs
- TableDetailsCollection.cs
- DataKey.cs
- TextShapeableCharacters.cs
- TagPrefixAttribute.cs
- BindingOperations.cs
- IdentityHolder.cs
- ProcessHostMapPath.cs
- DetailsViewRow.cs
- Binding.cs
- AsyncDataRequest.cs
- MsmqTransportReceiveParameters.cs
- WindowsGraphicsCacheManager.cs
- AttributeCollection.cs
- InkCanvas.cs
- SettingsBindableAttribute.cs
- BaseParser.cs
- SafeSecurityHelper.cs
- documentsequencetextpointer.cs
- HostExecutionContextManager.cs
- FlowDocumentPage.cs
- ConnectionStringSettingsCollection.cs
- DataGridViewSelectedColumnCollection.cs
- SqlRowUpdatingEvent.cs
- EntityReference.cs
- HttpChannelFactory.cs
- BinaryFormatterWriter.cs
- XmlSchemaSet.cs
- ResXDataNode.cs
- PingOptions.cs
- TransformerTypeCollection.cs
- DataTable.cs
- AttributeProviderAttribute.cs
- StringFunctions.cs
- IERequestCache.cs
- SecurityUtils.cs
- MenuItemBindingCollection.cs
- CellTreeNode.cs
- DataListItem.cs
- DataGridViewTextBoxEditingControl.cs
- DataControlFieldHeaderCell.cs
- ListViewItemSelectionChangedEvent.cs
- XamlGridLengthSerializer.cs
- PnrpPeerResolver.cs
- QilCloneVisitor.cs
- HtmlMeta.cs
- SqlCacheDependencySection.cs
- XmlCharType.cs
- arc.cs
- XmlNavigatorFilter.cs
- PageParser.cs
- XmlSchemaSequence.cs
- BasicHttpMessageSecurity.cs
- CommandConverter.cs
- EntityWithChangeTrackerStrategy.cs
- ProfileBuildProvider.cs
- DebugInfo.cs
- Activator.cs
- TimelineGroup.cs
- XmlName.cs
- XPathNode.cs
- ResourceReferenceExpressionConverter.cs
- WebBrowserHelper.cs
- WebPartConnection.cs
- Paragraph.cs
- FunctionImportMapping.cs
- NetCodeGroup.cs
- SqlClientWrapperSmiStream.cs
- CodeAttributeDeclarationCollection.cs