| File: MS\Internal\TextFormatting\FullTextBreakpoint.cs | Web Access |
| Project: src\wpf\src\Microsoft.DotNet.Wpf\src\PresentationCore\PresentationCore.csproj (PresentationCore) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Windows.Media.TextFormatting; namespace MS.Internal.TextFormatting { /// <summary> /// Full text implementation of the specialized text line representing state /// of line up to the point where line break may occur. Unlike a more tangible /// type like text line , breakpoint could not draw or performs hit-testing /// operation. It could only reflect formatting result back to the client. /// </summary> internal sealed class FullTextBreakpoint : TextBreakpoint { private TextMetrics _metrics; // full text metrics private IntPtr _ploline; // native object representing this break private IntPtr _penaltyResource; // unsafe handle to the internal factors used to determines penalty of the break. By default, the lifetime of this resource is managed by _ploline. private bool _isDisposed; // flag indicates whether this object is disposed private bool _isLineTruncated; // flag indicates whether the line produced at this breakpoint is truncated. /// <summary> /// Construct a list of potential breakpoints starting from the specified firstCharIndex /// </summary> /// <param name="paragraphCache">cache of paragraph content of all possible breaks within</param> /// <param name="firstCharIndex">index to first character where formatting starts</param> /// <param name="maxLineWidth">max format ideal width of the line being built</param> /// <param name="previousLineBreak">LineBreak property of the previous text line, or null if this is the first line in the paragraph</param> /// <param name="penaltyRestriction">constraint on what breakpoint is returned based on its implied calculated penalty</param> /// <param name="bestFitIndex">index of the best fit breakpoint in the returned collection</param> /// <returns>a list of potential breakpoints starting from firstCharIndex</returns> internal static IList<TextBreakpoint> CreateMultiple( TextParagraphCache paragraphCache, int firstCharIndex, int maxLineWidth, TextLineBreak previousLineBreak, IntPtr penaltyRestriction, out int bestFitIndex ) { Invariant.Assert(paragraphCache != null); // grab full text state from paragraph cache FullTextState fullText = paragraphCache.FullText; Invariant.Assert(fullText != null); FormatSettings settings = fullText.TextStore.Settings; Invariant.Assert(settings != null); // update formatting parameters at line start settings.UpdateSettingsForCurrentLine( maxLineWidth, previousLineBreak, (firstCharIndex == fullText.TextStore.CpFirst) ); Invariant.Assert(settings.Formatter != null); // acquiring LS context TextFormatterContext context = settings.Formatter.AcquireContext(fullText, IntPtr.Zero); IntPtr previousBreakRecord = IntPtr.Zero; if (settings.PreviousLineBreak != null) previousBreakRecord = settings.PreviousLineBreak.BreakRecord; // need not consider marker as tab since marker does not affect line metrics and it wasnt drawn. fullText.SetTabs(context); LsBreaks lsbreaks = new LsBreaks(); LsErr lserr = context.CreateBreaks( fullText.GetBreakpointInternalCp(firstCharIndex), previousBreakRecord, paragraphCache.Ploparabreak, // para breaking session penaltyRestriction, ref lsbreaks, out bestFitIndex ); // get the exception in context before it is released Exception callbackException = context.CallbackException; // release the context context.Release(); if(lserr != LsErr.None) { if(callbackException != null) { // rethrow exception thrown in callbacks throw callbackException; } else { // throw with LS error codes TextFormatterContext.ThrowExceptionFromLsError(SR.Format(SR.CreateBreaksFailure, lserr), lserr); } } // keep context alive at least till here GC.KeepAlive(context); TextBreakpoint[] breakpoints = new TextBreakpoint[lsbreaks.cBreaks]; for (int i = 0; i < lsbreaks.cBreaks; i++) { breakpoints[i] = new FullTextBreakpoint( fullText, firstCharIndex, maxLineWidth, ref lsbreaks, i // the current break ); } return breakpoints; } /// <summary> /// Construct breakpoint from full text info /// </summary> private FullTextBreakpoint( FullTextState fullText, int firstCharIndex, int maxLineWidth, ref LsBreaks lsbreaks, int breakIndex ) : this() { // According to antons: PTS only uses the width of a feasible break to avoid // clipping in subpage. At the moment, there is no good solution as of how // PTS client would be able to compute this width efficiently using LS. // The work around - although could be conceived would simply be too slow. // The width should therefore be set to the paragraph width for the time being. // // Client of text formatter would simply pass the value of TextBreakpoint.Width // back to PTS pfnFormatLineVariants call. LsLineWidths lineWidths = new LsLineWidths { upLimLine = maxLineWidth, upStartMainText = fullText.TextStore.Settings.TextIndent }; lineWidths.upStartMarker = lineWidths.upStartMainText; lineWidths.upStartTrailing = lineWidths.upLimLine; lineWidths.upMinStartTrailing = lineWidths.upStartTrailing; // construct the correspondent text metrics unsafe { _metrics.Compute( fullText, firstCharIndex, maxLineWidth, null, // collapsingSymbol ref lineWidths, &lsbreaks.plslinfoArray[breakIndex] ); _ploline = lsbreaks.pplolineArray[breakIndex]; // keep the line penalty handle _penaltyResource = lsbreaks.plinepenaltyArray[breakIndex]; if (lsbreaks.plslinfoArray[breakIndex].fForcedBreak != 0) _isLineTruncated = true; } } /// <summary> /// Empty private constructor /// </summary> private FullTextBreakpoint() { _metrics = new TextMetrics(); } /// <summary> /// Finalizing the break /// </summary> ~FullTextBreakpoint() { Dispose(false); } /// <summary> /// Disposing LS unmanaged memory for text line /// </summary> protected override void Dispose(bool disposing) { if(_ploline != IntPtr.Zero) { UnsafeNativeMethods.LoDisposeLine(_ploline, !disposing); _ploline = IntPtr.Zero; _penaltyResource = IntPtr.Zero; _isDisposed = true; GC.KeepAlive(this); } } #region TextBreakpoint /// <summary> /// Client to acquire a state at the point where breakpoint is determined by line breaking process; /// can be null when the line ends by the ending of the paragraph. Client may pass this /// value back to TextFormatter as an input argument to TextFormatter.FormatParagraphBreakpoints when /// formatting the next set of breakpoints within the same paragraph. /// </summary> public override TextLineBreak GetTextLineBreak() { if (_isDisposed) { throw new ObjectDisposedException(SR.TextBreakpointHasBeenDisposed); } return _metrics.GetTextLineBreak(_ploline); } /// <summary> /// Client to get the handle of the internal factors that are used to determine penalty of this breakpoint. /// </summary> /// <remarks> /// Calling this method means that the client will now manage the lifetime of this unmanaged resource themselves using unsafe penalty handler. /// We would make a correspondent call to notify our unmanaged wrapper to release them from duty of managing this /// resource. /// </remarks> internal override IntPtr GetTextPenaltyResource() { if (_isDisposed) { throw new ObjectDisposedException(SR.TextBreakpointHasBeenDisposed); } LsErr lserr = UnsafeNativeMethods.LoRelievePenaltyResource(_ploline); if (lserr != LsErr.None) { TextFormatterContext.ThrowExceptionFromLsError(SR.Format(SR.RelievePenaltyResourceFailure, lserr), lserr); } return _penaltyResource; } /// <summary> /// Client to get a Boolean flag indicating whether the line is truncated in the /// middle of a word. This flag is set only when TextParagraphProperties.TextWrapping /// is set to TextWrapping.Wrap and a single word is longer than the formatting /// paragraph width. In such situation, TextFormatter truncates the line in the middle /// of the word to honor the desired behavior specified by TextWrapping.Wrap setting. /// </summary> public override bool IsTruncated { get { return _isLineTruncated; } } #endregion #region TextMetrics /// <summary> /// Client to get the number of text source positions of this line /// </summary> public override int Length { get { return _metrics.Length; } } /// <summary> /// Client to get the number of characters following the last character /// of the line that may trigger reformatting of the current line. /// </summary> public override int DependentLength { get { return _metrics.DependentLength; } } /// <summary> /// Client to get the number of newline characters at line end /// </summary> public override int NewlineLength { get { return _metrics.NewlineLength; } } /// <summary> /// Client to get distance from paragraph start to line start /// </summary> public override double Start { get { return _metrics.Start; } } /// <summary> /// Client to get the total width of this line /// </summary> public override double Width { get { return _metrics.Width; } } /// <summary> /// Client to get the total width of this line including width of whitespace characters at the end of the line. /// </summary> public override double WidthIncludingTrailingWhitespace { get { return _metrics.WidthIncludingTrailingWhitespace; } } /// <summary> /// Client to get the height of the line /// </summary> public override double Height { get { return _metrics.Height; } } /// <summary> /// Client to get the height of the text (or other content) in the line; this property may differ from the Height /// property if the client specified the line height /// </summary> public override double TextHeight { get { return _metrics.TextHeight; } } /// <summary> /// Client to get the distance from top to baseline of this text line /// </summary> public override double Baseline { get { return _metrics.Baseline; } } /// <summary> /// Client to get the distance from the top of the text (or other content) to the baseline of this text line; /// this property may differ from the Baseline property if the client specified the line height /// </summary> public override double TextBaseline { get { return _metrics.TextBaseline; } } /// <summary> /// Client to get the distance from the before edge of line height /// to the baseline of marker of the line if any. /// </summary> public override double MarkerBaseline { get { return _metrics.MarkerBaseline; } } /// <summary> /// Client to get the overall height of the list items marker of the line if any. /// </summary> public override double MarkerHeight { get { return _metrics.MarkerHeight; } } #endregion } }