Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Core / CSharp / MS / Internal / TextFormatting / TextFormatterImp.cs / 1600529 / TextFormatterImp.cs
//+------------------------------------------------------------------------ // // Microsoft Windows Client Platform // Copyright (C) Microsoft Corporation // // File: TextFormatterImp.cs // // Contents: Text formatter implementation // // Created: 2-25-2003 Worachai Chaoweeraprasit (wchao) // //----------------------------------------------------------------------- using System; using System.Security; using System.Windows; using System.Windows.Media; using System.Windows.Threading; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Media.TextFormatting; using MS.Utility; using SR=MS.Internal.PresentationCore.SR; using SRID=MS.Internal.PresentationCore.SRID; using MS.Internal.Shaping; using MS.Internal.Text.TextInterface; using MS.Internal.FontCache; #if !OPTIMALBREAK_API using MS.Internal.PresentationCore; #endif namespace MS.Internal.TextFormatting { ////// Implementation of TextFormatter /// internal sealed class TextFormatterImp : TextFormatter { private FrugalStructList_contextList; // LS context free list private bool _multipleContextProhibited; // prohibit multiple contexts within the same formatter private GlyphingCache _glyphingCache; // Glyphing cache for font linking process private TextFormattingMode _textFormattingMode; private TextAnalyzer _textAnalyzer; // TextAnalyzer used for shaping process private const int MaxGlyphingCacheCapacity = 16; /// /// Construct an instance of TextFormatter implementation /// internal TextFormatterImp(TextFormattingMode textFormattingMode) : this(null, textFormattingMode) { } ////// Construct an instance of TextFormatter implementation /// internal TextFormatterImp() : this(null, TextFormattingMode.Ideal) {} ////// Construct an instance of TextFormatter implementation with the specified context /// /// ////// TextFormatter created via this special ctor takes a specified context and uses it as the only known /// context within its entire lifetime. It prohibits reentering of TextFormatter during formatting as only /// one context is allowed. This restriction is critical to the optimal break algorithm supported by the current /// version of PTLS. /// internal TextFormatterImp(TextFormatterContext soleContext, TextFormattingMode textFormattingMode) { _textFormattingMode = textFormattingMode; if (soleContext != null) _contextList.Add(soleContext); _multipleContextProhibited = (_contextList.Count != 0); } ////// Finalizing text formatter /// ~TextFormatterImp() { CleanupInternal(); } ////// Release all unmanaged LS contexts /// public override void Dispose() { CleanupInternal(); base.Dispose(); GC.SuppressFinalize(this); } ////// Release all unmanaged LS contexts /// private void CleanupInternal() { for (int i = 0; i < _contextList.Count; i++) { _contextList[i].Destroy(); } _contextList.Clear(); } ////// Client to format a text line that fills a paragraph in the document. /// /// an object representing text layout clients text source for TextFormatter. /// character index to specify where in the source text the line starts /// width of paragraph in which the line fills /// properties that can change from one paragraph to the next, such as text flow direction, text alignment, or indentation. /// LineBreak property of the previous text line, or null if this is the first line in the paragraph ///object representing a line of text that client interacts with. public override TextLine FormatLine( TextSource textSource, int firstCharIndex, double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak ) { return FormatLineInternal( textSource, firstCharIndex, 0, // lineLength paragraphWidth, paragraphProperties, previousLineBreak, new TextRunCache() // local cache, only live within this call ); } ////// Client to format a text line that fills a paragraph in the document. /// /// an object representing text layout clients text source for TextFormatter. /// character index to specify where in the source text the line starts /// width of paragraph in which the line fills /// properties that can change from one paragraph to the next, such as text flow direction, text alignment, or indentation. /// LineBreak property of the previous text line, or null if this is the first line in the paragraph /// an object representing content cache of the client. ///object representing a line of text that client interacts with. public override TextLine FormatLine( TextSource textSource, int firstCharIndex, double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak, TextRunCache textRunCache ) { return FormatLineInternal( textSource, firstCharIndex, 0, // lineLength paragraphWidth, paragraphProperties, previousLineBreak, textRunCache ); } ////// Client to reconstruct a previously formatted text line /// /// an object representing text layout clients text source for TextFormatter. /// character index to specify where in the source text the line starts /// character length of the line /// width of paragraph in which the line fills /// properties that can change from one paragraph to the next, such as text flow direction, text alignment, or indentation. /// LineBreak property of the previous text line, or null if this is the first line in the paragraph /// an object representing content cache of the client. ///object representing a line of text that client interacts with. #if OPTIMALBREAK_API public override TextLine RecreateLine( #else internal override TextLine RecreateLine( #endif TextSource textSource, int firstCharIndex, int lineLength, double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak, TextRunCache textRunCache ) { return FormatLineInternal( textSource, firstCharIndex, lineLength, paragraphWidth, paragraphProperties, previousLineBreak, textRunCache ); } ////// Format and produce a text line either with or without previously known /// line break point. /// private TextLine FormatLineInternal( TextSource textSource, int firstCharIndex, int lineLength, double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak, TextRunCache textRunCache ) { EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordText, EventTrace.Level.Verbose, EventTrace.Event.WClientStringBegin, "TextFormatterImp.FormatLineInternal Start"); // prepare formatting settings FormatSettings settings = PrepareFormatSettings( textSource, firstCharIndex, paragraphWidth, paragraphProperties, previousLineBreak, textRunCache, (lineLength != 0), // Do optimal break if break is given true, // isSingleLineFormatting _textFormattingMode ); TextLine textLine = null; if ( !settings.Pap.AlwaysCollapsible && previousLineBreak == null && lineLength <= 0 ) { // simple text line. textLine = SimpleTextLine.Create( settings, firstCharIndex, RealToIdealFloor(paragraphWidth) ) as TextLine; } if (textLine == null) { // content is complex, creating complex line textLine = new TextMetrics.FullTextLine( settings, firstCharIndex, lineLength, RealToIdealFloor(paragraphWidth), LineFlags.None ) as TextLine; } EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordText, EventTrace.Level.Verbose, EventTrace.Event.WClientStringEnd, "TextFormatterImp.FormatLineInternal End"); return textLine; } ////// Client to ask for the possible smallest and largest paragraph width that can fully contain the passing text content /// /// an object representing text layout clients text source for TextFormatter. /// character index to specify where in the source text the line starts /// properties that can change from one paragraph to the next, such as text flow direction, text alignment, or indentation. ///min max paragraph width public override MinMaxParagraphWidth FormatMinMaxParagraphWidth( TextSource textSource, int firstCharIndex, TextParagraphProperties paragraphProperties ) { return FormatMinMaxParagraphWidth( textSource, firstCharIndex, paragraphProperties, new TextRunCache() // local cache, only live within this call ); } ////// Client to ask for the possible smallest and largest paragraph width that can fully contain the passing text content /// /// an object representing text layout clients text source for TextFormatter. /// character index to specify where in the source text the line starts /// properties that can change from one paragraph to the next, such as text flow direction, text alignment, or indentation. /// an object representing content cache of the client. ///min max paragraph width public override MinMaxParagraphWidth FormatMinMaxParagraphWidth( TextSource textSource, int firstCharIndex, TextParagraphProperties paragraphProperties, TextRunCache textRunCache ) { // prepare formatting settings FormatSettings settings = PrepareFormatSettings( textSource, firstCharIndex, 0, // infinite paragraphWidth paragraphProperties, null, // always format the whole paragraph - no previousLineBreak textRunCache, false, // optimalBreak true, // isSingleLineFormatting _textFormattingMode ); // create specialized line specifically for min/max calculation TextMetrics.FullTextLine line = new TextMetrics.FullTextLine( settings, firstCharIndex, 0, // lineLength 0, // paragraph width has no significant meaning in min/max calculation (LineFlags.KeepState | LineFlags.MinMax) ); // line width in this case is the width of a line when the entire paragraph is laid out // as a single long line. MinMaxParagraphWidth minMax = new MinMaxParagraphWidth(line.MinWidth, line.Width); line.Dispose(); return minMax; } internal TextFormattingMode TextFormattingMode { get { return _textFormattingMode; } } ////// Client to cache information about a paragraph to be used during optimal paragraph line formatting /// /// an object representing text layout clients text source for TextFormatter. /// character index to specify where in the source text the line starts /// width of paragraph in which the line fills /// properties that can change from one paragraph to the next, such as text flow direction, text alignment, or indentation. /// text formatting state at the point where the previous line in the paragraph /// was broken by the text formatting process, as specified by the TextLine.LineBreak property for the previous /// line; this parameter can be null, and will always be null for the first line in a paragraph. /// an object representing content cache of the client. ///object representing a line of text that client interacts with. #if OPTIMALBREAK_API public override TextParagraphCache CreateParagraphCache( #else internal override TextParagraphCache CreateParagraphCache( #endif TextSource textSource, int firstCharIndex, double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak, TextRunCache textRunCache ) { // prepare formatting settings FormatSettings settings = PrepareFormatSettings( textSource, firstCharIndex, paragraphWidth, paragraphProperties, previousLineBreak, textRunCache, true, // optimalBreak false, // !isSingleLineFormatting _textFormattingMode ); // // Optimal paragraph formatting session specific check // if (!settings.Pap.Wrap && settings.Pap.OptimalBreak) { // Optimal paragraph must wrap. throw new ArgumentException(SR.Get(SRID.OptimalParagraphMustWrap)); } // create paragraph content cache object return new TextParagraphCache( settings, firstCharIndex, RealToIdeal(paragraphWidth) ); } ////// Validate all the relevant text formatting initial settings and package them /// private FormatSettings PrepareFormatSettings( TextSource textSource, int firstCharIndex, double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak, TextRunCache textRunCache, bool useOptimalBreak, bool isSingleLineFormatting, TextFormattingMode textFormattingMode ) { VerifyTextFormattingArguments( textSource, firstCharIndex, paragraphWidth, paragraphProperties, textRunCache ); if (textRunCache.Imp == null) { // No run cache object available, create one textRunCache.Imp = new TextRunCacheImp(); } // initialize formatting settings return new FormatSettings( this, textSource, textRunCache.Imp, new ParaProp(this, paragraphProperties, useOptimalBreak), previousLineBreak, isSingleLineFormatting, textFormattingMode, false ); } ////// Verify all text formatting arguments /// private void VerifyTextFormattingArguments( TextSource textSource, int firstCharIndex, double paragraphWidth, TextParagraphProperties paragraphProperties, TextRunCache textRunCache ) { if (textSource == null) throw new ArgumentNullException("textSource"); if (textRunCache == null) throw new ArgumentNullException("textRunCache"); if (paragraphProperties == null) throw new ArgumentNullException("paragraphProperties"); if (paragraphProperties.DefaultTextRunProperties == null) throw new ArgumentNullException("paragraphProperties.DefaultTextRunProperties"); if (paragraphProperties.DefaultTextRunProperties.Typeface == null) throw new ArgumentNullException("paragraphProperties.DefaultTextRunProperties.Typeface"); if (DoubleUtil.IsNaN(paragraphWidth)) throw new ArgumentOutOfRangeException("paragraphWidth", SR.Get(SRID.ParameterValueCannotBeNaN)); if (double.IsInfinity(paragraphWidth)) throw new ArgumentOutOfRangeException("paragraphWidth", SR.Get(SRID.ParameterValueCannotBeInfinity)); if ( paragraphWidth < 0 || paragraphWidth > Constants.RealInfiniteWidth) { throw new ArgumentOutOfRangeException("paragraphWidth", SR.Get(SRID.ParameterMustBeBetween, 0, Constants.RealInfiniteWidth)); } double realMaxFontRenderingEmSize = Constants.RealInfiniteWidth / Constants.GreatestMutiplierOfEm; if ( paragraphProperties.DefaultTextRunProperties.FontRenderingEmSize < 0 || paragraphProperties.DefaultTextRunProperties.FontRenderingEmSize > realMaxFontRenderingEmSize) { throw new ArgumentOutOfRangeException("paragraphProperties.DefaultTextRunProperties.FontRenderingEmSize", SR.Get(SRID.ParameterMustBeBetween, 0, realMaxFontRenderingEmSize)); } if (paragraphProperties.Indent > Constants.RealInfiniteWidth) throw new ArgumentOutOfRangeException("paragraphProperties.Indent", SR.Get(SRID.ParameterCannotBeGreaterThan, Constants.RealInfiniteWidth)); if (paragraphProperties.LineHeight > Constants.RealInfiniteWidth) throw new ArgumentOutOfRangeException("paragraphProperties.LineHeight", SR.Get(SRID.ParameterCannotBeGreaterThan, Constants.RealInfiniteWidth)); if ( paragraphProperties.DefaultIncrementalTab < 0 || paragraphProperties.DefaultIncrementalTab > Constants.RealInfiniteWidth) { throw new ArgumentOutOfRangeException("paragraphProperties.DefaultIncrementalTab", SR.Get(SRID.ParameterMustBeBetween, 0, Constants.RealInfiniteWidth)); } } ////// Validate the input character hit /// internal static void VerifyCaretCharacterHit( CharacterHit characterHit, int cpFirst, int cchLength ) { if ( characterHit.FirstCharacterIndex < cpFirst || characterHit.FirstCharacterIndex > cpFirst + cchLength) { throw new ArgumentOutOfRangeException("cpFirst", SR.Get(SRID.ParameterMustBeBetween, cpFirst, cpFirst + cchLength)); } if (characterHit.TrailingLength < 0) { throw new ArgumentOutOfRangeException("cchLength", SR.Get(SRID.ParameterCannotBeNegative)); } } ////// Acquire a free TextFormatter context for complex line operation /// /// object that becomes the owner of LS context once acquired /// matching PLOC ///Active LS context ////// Critical - this sets the owner of the context /// Safe - this doesn't expose critical info /// [SecurityCritical, SecurityTreatAsSafe] internal TextFormatterContext AcquireContext( object owner, IntPtr ploc ) { Invariant.Assert(owner != null); TextFormatterContext context = null; int c; int contextCount = _contextList.Count; for (c = 0; c < contextCount; c++) { context = (TextFormatterContext)_contextList[c]; if (ploc == IntPtr.Zero) { if(context.Owner == null) break; } else if (ploc == context.Ploc.Value) { // LS requires that we use the exact same context for line // destruction or hittesting (part of the reason is that LS // actually caches some run info in the context). So here // we use the actual PLSC as the context signature so we // locate the one we want. Debug.Assert(context.Owner == null); break; } } if (c == contextCount) { if (contextCount == 0 || !_multipleContextProhibited) { // no free one exists, create a new one context = new TextFormatterContext(); _contextList.Add(context); } else { // This instance of TextFormatter only allows a single context, reentering the // same TextFormatter in this case is not allowed. // // This requirement is currently enforced only during optimal break computation. // Client implementing nesting of optimal break content inside another must create // a separate TextFormatter instance for each content in different nesting level. throw new InvalidOperationException(SR.Get(SRID.TextFormatterReentranceProhibited)); } } Debug.Assert(context != null); context.Owner = owner; return context; } ////// Create an anti-inversion transform from the inversion flags. /// The result is used to correct glyph bitmap on an output to /// a drawing surface with the specified inversions applied on. /// internal static MatrixTransform CreateAntiInversionTransform( InvertAxes inversion, double paragraphWidth, double lineHeight ) { if (inversion == InvertAxes.None) { // avoid creating unncessary pressure on GC when anti-transform is not needed. return null; } double m11 = 1; double m22 = 1; double offsetX = 0; double offsetY = 0; if ((inversion & InvertAxes.Horizontal) != 0) { m11 = -m11; offsetX = paragraphWidth; } if ((inversion & InvertAxes.Vertical) != 0) { m22 = -m22; offsetY = lineHeight; } return new MatrixTransform(m11, 0, 0, m22, offsetX, offsetY); } ////// Compare text formatter real values - since values are rounded in Display mode, comparison /// must also round and only return true if one rounded value is greater than the other. /// /// First value to compare. /// Second value to compare. /// Text formatting mode. ///1 if x greater than y, -1 if x less than y, 0 if x == y internal static int CompareReal(double x, double y, TextFormattingMode mode) { double xDisplay = x; double yDisplay = y; if (mode == TextFormattingMode.Display) { xDisplay = RoundDipForDisplayMode(x); yDisplay = RoundDipForDisplayMode(y); } if (xDisplay > yDisplay) { return 1; } if (xDisplay < yDisplay) { return -1; } return 0; } internal static double RoundDip(double value, TextFormattingMode textFormattingMode) { if (TextFormattingMode.Display == textFormattingMode) { return RoundDipForDisplayMode(value); } else { return value; } } internal static double RoundDipForDisplayMode(double value) { return RoundDipForDisplayMode(value, MidpointRounding.ToEven); } private static double RoundDipForDisplayMode(double value, MidpointRounding midpointRounding) { return Math.Round(value * Util.PixelsPerDip, midpointRounding) / Util.PixelsPerDip; } ////// The default behavior of Math.Round() leads to undesirable behavior /// When used for display mode justified text, where we can find /// characters belonging to the same word jumping sideways. /// A word can break among several GlyphRuns. So we need consistent /// rounding of the width of the GlyphRuns. If the width of one GlyphRun /// rounds up and the next GlyphRun rounds down then we see characters /// overlapping and so on. /// It is too late to change the behavior of our rounding universally /// so we are making the change targeted to Display mode + Justified text /// /// ///internal static double RoundDipForDisplayModeJustifiedText(double value) { return RoundDipForDisplayMode(value, MidpointRounding.AwayFromZero); } /// /// Scale LS ideal resolution value to real value /// internal static double IdealToRealWithNoRounding(double i) { return i * Constants.DefaultIdealToReal; } ////// Scale LS ideal resolution value to real value /// internal double IdealToReal(double i) { double value = IdealToRealWithNoRounding(i); if (_textFormattingMode == TextFormattingMode.Display) { value = RoundDipForDisplayMode(value); } if (i > 0) { // Non-zero values should not be converted to 0 accidentally through rounding, ensure that at least the min value is returned. value = Math.Max(value, Constants.DefaultIdealToReal); } return value; } ////// Scale real value to LS ideal resolution /// internal static int RealToIdeal(double i) { int value = (int)Math.Round(i * ToIdeal); if (i > 0) { // Non-zero values should not be converted to 0 accidentally through rounding, ensure that at least the min value is returned. value = Math.Max(value, 1); } return value; } ////// Scale the real value to LS ideal resolution /// Use the floor value of the scale value /// ////// Using Math.Round may result in a line larger than /// the actual given paragraph width. For example, /// round tripping 100.112 with factor 300 becomes 100.1133... /// Using floor to ensure we never go beyond paragraph width /// internal static int RealToIdealFloor(double i) { int value = (int)Math.Floor(i * ToIdeal); if (i > 0) { // Non-zero values should not be converted to 0 accidentally through rounding, ensure that at least the min value is returned. value = Math.Max(value, 1); } return value; } ////// Real to ideal value scaling factor /// internal static double ToIdeal { get { return Constants.DefaultRealToIdeal; } } ////// Return the GlyphingCache associated with this TextFormatterImp object. /// GlyphingCache stores the mapping from Unicode scalar value to the physical font that is /// used to display it. /// internal GlyphingCache GlyphingCache { get { if (_glyphingCache == null) { _glyphingCache = new GlyphingCache(MaxGlyphingCacheCapacity); } return _glyphingCache; } } internal TextAnalyzer TextAnalyzer { ////// Return the TextAnalyzer associated with this TextFormatterImp object. /// TextAnalyzer is used in shaping process. /// ////// Critical - Uses security critical DWriteFactory.Instance. /// Safe - Does not expose the Factory instance. /// [SecuritySafeCritical] get { if (_textAnalyzer == null) { _textAnalyzer = DWriteFactory.Instance.CreateTextAnalyzer(); } return _textAnalyzer; } } } } // 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: TextFormatterImp.cs // // Contents: Text formatter implementation // // Created: 2-25-2003 Worachai Chaoweeraprasit (wchao) // //----------------------------------------------------------------------- using System; using System.Security; using System.Windows; using System.Windows.Media; using System.Windows.Threading; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Windows.Media.TextFormatting; using MS.Utility; using SR=MS.Internal.PresentationCore.SR; using SRID=MS.Internal.PresentationCore.SRID; using MS.Internal.Shaping; using MS.Internal.Text.TextInterface; using MS.Internal.FontCache; #if !OPTIMALBREAK_API using MS.Internal.PresentationCore; #endif namespace MS.Internal.TextFormatting { ////// Implementation of TextFormatter /// internal sealed class TextFormatterImp : TextFormatter { private FrugalStructList_contextList; // LS context free list private bool _multipleContextProhibited; // prohibit multiple contexts within the same formatter private GlyphingCache _glyphingCache; // Glyphing cache for font linking process private TextFormattingMode _textFormattingMode; private TextAnalyzer _textAnalyzer; // TextAnalyzer used for shaping process private const int MaxGlyphingCacheCapacity = 16; /// /// Construct an instance of TextFormatter implementation /// internal TextFormatterImp(TextFormattingMode textFormattingMode) : this(null, textFormattingMode) { } ////// Construct an instance of TextFormatter implementation /// internal TextFormatterImp() : this(null, TextFormattingMode.Ideal) {} ////// Construct an instance of TextFormatter implementation with the specified context /// /// ////// TextFormatter created via this special ctor takes a specified context and uses it as the only known /// context within its entire lifetime. It prohibits reentering of TextFormatter during formatting as only /// one context is allowed. This restriction is critical to the optimal break algorithm supported by the current /// version of PTLS. /// internal TextFormatterImp(TextFormatterContext soleContext, TextFormattingMode textFormattingMode) { _textFormattingMode = textFormattingMode; if (soleContext != null) _contextList.Add(soleContext); _multipleContextProhibited = (_contextList.Count != 0); } ////// Finalizing text formatter /// ~TextFormatterImp() { CleanupInternal(); } ////// Release all unmanaged LS contexts /// public override void Dispose() { CleanupInternal(); base.Dispose(); GC.SuppressFinalize(this); } ////// Release all unmanaged LS contexts /// private void CleanupInternal() { for (int i = 0; i < _contextList.Count; i++) { _contextList[i].Destroy(); } _contextList.Clear(); } ////// Client to format a text line that fills a paragraph in the document. /// /// an object representing text layout clients text source for TextFormatter. /// character index to specify where in the source text the line starts /// width of paragraph in which the line fills /// properties that can change from one paragraph to the next, such as text flow direction, text alignment, or indentation. /// LineBreak property of the previous text line, or null if this is the first line in the paragraph ///object representing a line of text that client interacts with. public override TextLine FormatLine( TextSource textSource, int firstCharIndex, double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak ) { return FormatLineInternal( textSource, firstCharIndex, 0, // lineLength paragraphWidth, paragraphProperties, previousLineBreak, new TextRunCache() // local cache, only live within this call ); } ////// Client to format a text line that fills a paragraph in the document. /// /// an object representing text layout clients text source for TextFormatter. /// character index to specify where in the source text the line starts /// width of paragraph in which the line fills /// properties that can change from one paragraph to the next, such as text flow direction, text alignment, or indentation. /// LineBreak property of the previous text line, or null if this is the first line in the paragraph /// an object representing content cache of the client. ///object representing a line of text that client interacts with. public override TextLine FormatLine( TextSource textSource, int firstCharIndex, double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak, TextRunCache textRunCache ) { return FormatLineInternal( textSource, firstCharIndex, 0, // lineLength paragraphWidth, paragraphProperties, previousLineBreak, textRunCache ); } ////// Client to reconstruct a previously formatted text line /// /// an object representing text layout clients text source for TextFormatter. /// character index to specify where in the source text the line starts /// character length of the line /// width of paragraph in which the line fills /// properties that can change from one paragraph to the next, such as text flow direction, text alignment, or indentation. /// LineBreak property of the previous text line, or null if this is the first line in the paragraph /// an object representing content cache of the client. ///object representing a line of text that client interacts with. #if OPTIMALBREAK_API public override TextLine RecreateLine( #else internal override TextLine RecreateLine( #endif TextSource textSource, int firstCharIndex, int lineLength, double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak, TextRunCache textRunCache ) { return FormatLineInternal( textSource, firstCharIndex, lineLength, paragraphWidth, paragraphProperties, previousLineBreak, textRunCache ); } ////// Format and produce a text line either with or without previously known /// line break point. /// private TextLine FormatLineInternal( TextSource textSource, int firstCharIndex, int lineLength, double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak, TextRunCache textRunCache ) { EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordText, EventTrace.Level.Verbose, EventTrace.Event.WClientStringBegin, "TextFormatterImp.FormatLineInternal Start"); // prepare formatting settings FormatSettings settings = PrepareFormatSettings( textSource, firstCharIndex, paragraphWidth, paragraphProperties, previousLineBreak, textRunCache, (lineLength != 0), // Do optimal break if break is given true, // isSingleLineFormatting _textFormattingMode ); TextLine textLine = null; if ( !settings.Pap.AlwaysCollapsible && previousLineBreak == null && lineLength <= 0 ) { // simple text line. textLine = SimpleTextLine.Create( settings, firstCharIndex, RealToIdealFloor(paragraphWidth) ) as TextLine; } if (textLine == null) { // content is complex, creating complex line textLine = new TextMetrics.FullTextLine( settings, firstCharIndex, lineLength, RealToIdealFloor(paragraphWidth), LineFlags.None ) as TextLine; } EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordText, EventTrace.Level.Verbose, EventTrace.Event.WClientStringEnd, "TextFormatterImp.FormatLineInternal End"); return textLine; } ////// Client to ask for the possible smallest and largest paragraph width that can fully contain the passing text content /// /// an object representing text layout clients text source for TextFormatter. /// character index to specify where in the source text the line starts /// properties that can change from one paragraph to the next, such as text flow direction, text alignment, or indentation. ///min max paragraph width public override MinMaxParagraphWidth FormatMinMaxParagraphWidth( TextSource textSource, int firstCharIndex, TextParagraphProperties paragraphProperties ) { return FormatMinMaxParagraphWidth( textSource, firstCharIndex, paragraphProperties, new TextRunCache() // local cache, only live within this call ); } ////// Client to ask for the possible smallest and largest paragraph width that can fully contain the passing text content /// /// an object representing text layout clients text source for TextFormatter. /// character index to specify where in the source text the line starts /// properties that can change from one paragraph to the next, such as text flow direction, text alignment, or indentation. /// an object representing content cache of the client. ///min max paragraph width public override MinMaxParagraphWidth FormatMinMaxParagraphWidth( TextSource textSource, int firstCharIndex, TextParagraphProperties paragraphProperties, TextRunCache textRunCache ) { // prepare formatting settings FormatSettings settings = PrepareFormatSettings( textSource, firstCharIndex, 0, // infinite paragraphWidth paragraphProperties, null, // always format the whole paragraph - no previousLineBreak textRunCache, false, // optimalBreak true, // isSingleLineFormatting _textFormattingMode ); // create specialized line specifically for min/max calculation TextMetrics.FullTextLine line = new TextMetrics.FullTextLine( settings, firstCharIndex, 0, // lineLength 0, // paragraph width has no significant meaning in min/max calculation (LineFlags.KeepState | LineFlags.MinMax) ); // line width in this case is the width of a line when the entire paragraph is laid out // as a single long line. MinMaxParagraphWidth minMax = new MinMaxParagraphWidth(line.MinWidth, line.Width); line.Dispose(); return minMax; } internal TextFormattingMode TextFormattingMode { get { return _textFormattingMode; } } ////// Client to cache information about a paragraph to be used during optimal paragraph line formatting /// /// an object representing text layout clients text source for TextFormatter. /// character index to specify where in the source text the line starts /// width of paragraph in which the line fills /// properties that can change from one paragraph to the next, such as text flow direction, text alignment, or indentation. /// text formatting state at the point where the previous line in the paragraph /// was broken by the text formatting process, as specified by the TextLine.LineBreak property for the previous /// line; this parameter can be null, and will always be null for the first line in a paragraph. /// an object representing content cache of the client. ///object representing a line of text that client interacts with. #if OPTIMALBREAK_API public override TextParagraphCache CreateParagraphCache( #else internal override TextParagraphCache CreateParagraphCache( #endif TextSource textSource, int firstCharIndex, double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak, TextRunCache textRunCache ) { // prepare formatting settings FormatSettings settings = PrepareFormatSettings( textSource, firstCharIndex, paragraphWidth, paragraphProperties, previousLineBreak, textRunCache, true, // optimalBreak false, // !isSingleLineFormatting _textFormattingMode ); // // Optimal paragraph formatting session specific check // if (!settings.Pap.Wrap && settings.Pap.OptimalBreak) { // Optimal paragraph must wrap. throw new ArgumentException(SR.Get(SRID.OptimalParagraphMustWrap)); } // create paragraph content cache object return new TextParagraphCache( settings, firstCharIndex, RealToIdeal(paragraphWidth) ); } ////// Validate all the relevant text formatting initial settings and package them /// private FormatSettings PrepareFormatSettings( TextSource textSource, int firstCharIndex, double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak, TextRunCache textRunCache, bool useOptimalBreak, bool isSingleLineFormatting, TextFormattingMode textFormattingMode ) { VerifyTextFormattingArguments( textSource, firstCharIndex, paragraphWidth, paragraphProperties, textRunCache ); if (textRunCache.Imp == null) { // No run cache object available, create one textRunCache.Imp = new TextRunCacheImp(); } // initialize formatting settings return new FormatSettings( this, textSource, textRunCache.Imp, new ParaProp(this, paragraphProperties, useOptimalBreak), previousLineBreak, isSingleLineFormatting, textFormattingMode, false ); } ////// Verify all text formatting arguments /// private void VerifyTextFormattingArguments( TextSource textSource, int firstCharIndex, double paragraphWidth, TextParagraphProperties paragraphProperties, TextRunCache textRunCache ) { if (textSource == null) throw new ArgumentNullException("textSource"); if (textRunCache == null) throw new ArgumentNullException("textRunCache"); if (paragraphProperties == null) throw new ArgumentNullException("paragraphProperties"); if (paragraphProperties.DefaultTextRunProperties == null) throw new ArgumentNullException("paragraphProperties.DefaultTextRunProperties"); if (paragraphProperties.DefaultTextRunProperties.Typeface == null) throw new ArgumentNullException("paragraphProperties.DefaultTextRunProperties.Typeface"); if (DoubleUtil.IsNaN(paragraphWidth)) throw new ArgumentOutOfRangeException("paragraphWidth", SR.Get(SRID.ParameterValueCannotBeNaN)); if (double.IsInfinity(paragraphWidth)) throw new ArgumentOutOfRangeException("paragraphWidth", SR.Get(SRID.ParameterValueCannotBeInfinity)); if ( paragraphWidth < 0 || paragraphWidth > Constants.RealInfiniteWidth) { throw new ArgumentOutOfRangeException("paragraphWidth", SR.Get(SRID.ParameterMustBeBetween, 0, Constants.RealInfiniteWidth)); } double realMaxFontRenderingEmSize = Constants.RealInfiniteWidth / Constants.GreatestMutiplierOfEm; if ( paragraphProperties.DefaultTextRunProperties.FontRenderingEmSize < 0 || paragraphProperties.DefaultTextRunProperties.FontRenderingEmSize > realMaxFontRenderingEmSize) { throw new ArgumentOutOfRangeException("paragraphProperties.DefaultTextRunProperties.FontRenderingEmSize", SR.Get(SRID.ParameterMustBeBetween, 0, realMaxFontRenderingEmSize)); } if (paragraphProperties.Indent > Constants.RealInfiniteWidth) throw new ArgumentOutOfRangeException("paragraphProperties.Indent", SR.Get(SRID.ParameterCannotBeGreaterThan, Constants.RealInfiniteWidth)); if (paragraphProperties.LineHeight > Constants.RealInfiniteWidth) throw new ArgumentOutOfRangeException("paragraphProperties.LineHeight", SR.Get(SRID.ParameterCannotBeGreaterThan, Constants.RealInfiniteWidth)); if ( paragraphProperties.DefaultIncrementalTab < 0 || paragraphProperties.DefaultIncrementalTab > Constants.RealInfiniteWidth) { throw new ArgumentOutOfRangeException("paragraphProperties.DefaultIncrementalTab", SR.Get(SRID.ParameterMustBeBetween, 0, Constants.RealInfiniteWidth)); } } ////// Validate the input character hit /// internal static void VerifyCaretCharacterHit( CharacterHit characterHit, int cpFirst, int cchLength ) { if ( characterHit.FirstCharacterIndex < cpFirst || characterHit.FirstCharacterIndex > cpFirst + cchLength) { throw new ArgumentOutOfRangeException("cpFirst", SR.Get(SRID.ParameterMustBeBetween, cpFirst, cpFirst + cchLength)); } if (characterHit.TrailingLength < 0) { throw new ArgumentOutOfRangeException("cchLength", SR.Get(SRID.ParameterCannotBeNegative)); } } ////// Acquire a free TextFormatter context for complex line operation /// /// object that becomes the owner of LS context once acquired /// matching PLOC ///Active LS context ////// Critical - this sets the owner of the context /// Safe - this doesn't expose critical info /// [SecurityCritical, SecurityTreatAsSafe] internal TextFormatterContext AcquireContext( object owner, IntPtr ploc ) { Invariant.Assert(owner != null); TextFormatterContext context = null; int c; int contextCount = _contextList.Count; for (c = 0; c < contextCount; c++) { context = (TextFormatterContext)_contextList[c]; if (ploc == IntPtr.Zero) { if(context.Owner == null) break; } else if (ploc == context.Ploc.Value) { // LS requires that we use the exact same context for line // destruction or hittesting (part of the reason is that LS // actually caches some run info in the context). So here // we use the actual PLSC as the context signature so we // locate the one we want. Debug.Assert(context.Owner == null); break; } } if (c == contextCount) { if (contextCount == 0 || !_multipleContextProhibited) { // no free one exists, create a new one context = new TextFormatterContext(); _contextList.Add(context); } else { // This instance of TextFormatter only allows a single context, reentering the // same TextFormatter in this case is not allowed. // // This requirement is currently enforced only during optimal break computation. // Client implementing nesting of optimal break content inside another must create // a separate TextFormatter instance for each content in different nesting level. throw new InvalidOperationException(SR.Get(SRID.TextFormatterReentranceProhibited)); } } Debug.Assert(context != null); context.Owner = owner; return context; } ////// Create an anti-inversion transform from the inversion flags. /// The result is used to correct glyph bitmap on an output to /// a drawing surface with the specified inversions applied on. /// internal static MatrixTransform CreateAntiInversionTransform( InvertAxes inversion, double paragraphWidth, double lineHeight ) { if (inversion == InvertAxes.None) { // avoid creating unncessary pressure on GC when anti-transform is not needed. return null; } double m11 = 1; double m22 = 1; double offsetX = 0; double offsetY = 0; if ((inversion & InvertAxes.Horizontal) != 0) { m11 = -m11; offsetX = paragraphWidth; } if ((inversion & InvertAxes.Vertical) != 0) { m22 = -m22; offsetY = lineHeight; } return new MatrixTransform(m11, 0, 0, m22, offsetX, offsetY); } ////// Compare text formatter real values - since values are rounded in Display mode, comparison /// must also round and only return true if one rounded value is greater than the other. /// /// First value to compare. /// Second value to compare. /// Text formatting mode. ///1 if x greater than y, -1 if x less than y, 0 if x == y internal static int CompareReal(double x, double y, TextFormattingMode mode) { double xDisplay = x; double yDisplay = y; if (mode == TextFormattingMode.Display) { xDisplay = RoundDipForDisplayMode(x); yDisplay = RoundDipForDisplayMode(y); } if (xDisplay > yDisplay) { return 1; } if (xDisplay < yDisplay) { return -1; } return 0; } internal static double RoundDip(double value, TextFormattingMode textFormattingMode) { if (TextFormattingMode.Display == textFormattingMode) { return RoundDipForDisplayMode(value); } else { return value; } } internal static double RoundDipForDisplayMode(double value) { return RoundDipForDisplayMode(value, MidpointRounding.ToEven); } private static double RoundDipForDisplayMode(double value, MidpointRounding midpointRounding) { return Math.Round(value * Util.PixelsPerDip, midpointRounding) / Util.PixelsPerDip; } ////// The default behavior of Math.Round() leads to undesirable behavior /// When used for display mode justified text, where we can find /// characters belonging to the same word jumping sideways. /// A word can break among several GlyphRuns. So we need consistent /// rounding of the width of the GlyphRuns. If the width of one GlyphRun /// rounds up and the next GlyphRun rounds down then we see characters /// overlapping and so on. /// It is too late to change the behavior of our rounding universally /// so we are making the change targeted to Display mode + Justified text /// /// ///internal static double RoundDipForDisplayModeJustifiedText(double value) { return RoundDipForDisplayMode(value, MidpointRounding.AwayFromZero); } /// /// Scale LS ideal resolution value to real value /// internal static double IdealToRealWithNoRounding(double i) { return i * Constants.DefaultIdealToReal; } ////// Scale LS ideal resolution value to real value /// internal double IdealToReal(double i) { double value = IdealToRealWithNoRounding(i); if (_textFormattingMode == TextFormattingMode.Display) { value = RoundDipForDisplayMode(value); } if (i > 0) { // Non-zero values should not be converted to 0 accidentally through rounding, ensure that at least the min value is returned. value = Math.Max(value, Constants.DefaultIdealToReal); } return value; } ////// Scale real value to LS ideal resolution /// internal static int RealToIdeal(double i) { int value = (int)Math.Round(i * ToIdeal); if (i > 0) { // Non-zero values should not be converted to 0 accidentally through rounding, ensure that at least the min value is returned. value = Math.Max(value, 1); } return value; } ////// Scale the real value to LS ideal resolution /// Use the floor value of the scale value /// ////// Using Math.Round may result in a line larger than /// the actual given paragraph width. For example, /// round tripping 100.112 with factor 300 becomes 100.1133... /// Using floor to ensure we never go beyond paragraph width /// internal static int RealToIdealFloor(double i) { int value = (int)Math.Floor(i * ToIdeal); if (i > 0) { // Non-zero values should not be converted to 0 accidentally through rounding, ensure that at least the min value is returned. value = Math.Max(value, 1); } return value; } ////// Real to ideal value scaling factor /// internal static double ToIdeal { get { return Constants.DefaultRealToIdeal; } } ////// Return the GlyphingCache associated with this TextFormatterImp object. /// GlyphingCache stores the mapping from Unicode scalar value to the physical font that is /// used to display it. /// internal GlyphingCache GlyphingCache { get { if (_glyphingCache == null) { _glyphingCache = new GlyphingCache(MaxGlyphingCacheCapacity); } return _glyphingCache; } } internal TextAnalyzer TextAnalyzer { ////// Return the TextAnalyzer associated with this TextFormatterImp object. /// TextAnalyzer is used in shaping process. /// ////// Critical - Uses security critical DWriteFactory.Instance. /// Safe - Does not expose the Factory instance. /// [SecuritySafeCritical] get { if (_textAnalyzer == null) { _textAnalyzer = DWriteFactory.Instance.CreateTextAnalyzer(); } return _textAnalyzer; } } } } // 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
- BasicExpandProvider.cs
- ScaleTransform.cs
- GeometryConverter.cs
- ModelVisual3D.cs
- DefaultEventAttribute.cs
- ExpressionEvaluator.cs
- ComponentManagerBroker.cs
- SemanticBasicElement.cs
- ChannelEndpointElement.cs
- CompilerTypeWithParams.cs
- sqlstateclientmanager.cs
- HandlerBase.cs
- Helpers.cs
- DataChangedEventManager.cs
- ArcSegment.cs
- DataObjectEventArgs.cs
- AbstractExpressions.cs
- RightsManagementManager.cs
- ViewCellRelation.cs
- XamlNamespaceHelper.cs
- ToolBar.cs
- CodeGen.cs
- CodeMemberEvent.cs
- XmlWriterSettings.cs
- WMIInterop.cs
- AspNetSynchronizationContext.cs
- PointCollection.cs
- Point3D.cs
- WebPartCancelEventArgs.cs
- TrackingProfileCache.cs
- PropertyHelper.cs
- CodeConstructor.cs
- _AcceptOverlappedAsyncResult.cs
- DiagnosticStrings.cs
- ModelTypeConverter.cs
- AutomationElementCollection.cs
- Formatter.cs
- RsaSecurityToken.cs
- Region.cs
- QueryOutputWriter.cs
- MasterPageBuildProvider.cs
- UserNameServiceElement.cs
- SettingsSection.cs
- MissingFieldException.cs
- XmlValueConverter.cs
- objectquery_tresulttype.cs
- ProxySimple.cs
- PathTooLongException.cs
- XmlSchemaIdentityConstraint.cs
- basenumberconverter.cs
- WebPartsPersonalizationAuthorization.cs
- WindowVisualStateTracker.cs
- ScriptModule.cs
- ContextBase.cs
- UntrustedRecipientException.cs
- ArithmeticException.cs
- DesignerLabelAdapter.cs
- SamlAssertionKeyIdentifierClause.cs
- WorkflowInlining.cs
- IncrementalReadDecoders.cs
- TableLayoutStyleCollection.cs
- BooleanFunctions.cs
- Faults.cs
- _Win32.cs
- AssemblyBuilder.cs
- PropertyGeneratedEventArgs.cs
- ResolveMatchesMessageCD1.cs
- ConsoleTraceListener.cs
- LinkDescriptor.cs
- CellParaClient.cs
- ControlPager.cs
- OdbcConnection.cs
- CodeArgumentReferenceExpression.cs
- SpellerError.cs
- PieceDirectory.cs
- PanelDesigner.cs
- NativeCppClassAttribute.cs
- TypeExtensionConverter.cs
- MultipartIdentifier.cs
- ClusterUtils.cs
- RemotingException.cs
- ScrollBar.cs
- SortedList.cs
- PathFigureCollectionValueSerializer.cs
- XmlSchema.cs
- Shape.cs
- COM2ExtendedUITypeEditor.cs
- ServiceDescriptions.cs
- KerberosSecurityTokenAuthenticator.cs
- LayoutEngine.cs
- Page.cs
- WS2007HttpBindingCollectionElement.cs
- LongValidatorAttribute.cs
- UserMapPath.cs
- UriTemplate.cs
- PeerNameResolver.cs
- MachineKeyConverter.cs
- XmlDataSourceView.cs
- ObjectSet.cs
- ConfigXmlAttribute.cs