Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / MS / Internal / PtsHost / TextParagraph.cs / 1305600 / TextParagraph.cs
//---------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // // File: ContainerParagraph.cs // // Description: TextParagraph is a Paragraph representing continuous sequence // of lines. // // History: // 05/05/2003 : [....] - moving from Avalon branch. // 10/30/2004 : [....] - ElementReference cleanup. // //--------------------------------------------------------------------------- #pragma warning disable 1634, 1691 // avoid generating warnings about unknown // message numbers and unknown pragmas for PRESharp contol using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Media; using System.Windows.Media.TextFormatting; using System.Windows.Threading; using MS.Internal.Text; using MS.Internal.Documents; using MS.Internal.PtsHost.UnsafeNativeMethods; namespace MS.Internal.PtsHost { ////// TextParagraph is a Paragraph representing continuous sequence of lines /// and it is using built-in PTS text paragraph handler to do formatting. /// internal sealed class TextParagraph : BaseParagraph { //------------------------------------------------------------------- // // Constructors // //------------------------------------------------------------------- #region Constructors ////// Constructor. /// /// /// Element associated with paragraph. /// /// /// Content's structural cache /// internal TextParagraph(DependencyObject element, StructuralCache structuralCache) : base(element, structuralCache) { } #endregion Constructors //-------------------------------------------------------------------- // // IDisposable Methods // //------------------------------------------------------------------- #region IDisposable Methods ////// IDisposable.Dispose /// public override void Dispose() { if(_attachedObjects != null) { foreach(AttachedObject obj in _attachedObjects) { obj.Dispose(); } _attachedObjects = null; } if (_inlineObjects != null) { foreach (InlineObject obj in _inlineObjects) { obj.Dispose(); } _inlineObjects = null; } base.Dispose(); } #endregion IDisposable Methods // ------------------------------------------------------------------ // // PTS callbacks // // ------------------------------------------------------------------ #region PTS callbacks ////// GetParaProperties /// /// /// OUT: paragraph properties /// internal override void GetParaProperties( ref PTS.FSPAP fspap) { // Paragraph associated with the element (SegmentParagraph) will // take care of break control. fspap.fKeepWithNext = PTS.False; fspap.fBreakPageBefore = PTS.False; fspap.fBreakColumnBefore = PTS.False; GetParaProperties(ref fspap, true); // true means ignore element props fspap.idobj = PTS.fsidobjText; } ////// CreateParaclient /// /// /// OUT: Para client handle, opaque to PTS paragraph client /// internal override void CreateParaclient( out IntPtr paraClientHandle) { #pragma warning disable 6518 // Disable PRESharp warning 6518. TextParaClient is an UnmamangedHandle, that adds itself // to HandleMapper that holds a reference to it. PTS manages lifetime of this object, and // calls DestroyParaclient to get rid of it. DestroyParaclient will call Dispose() on the object // and remove it from HandleMapper. TextParaClient paraClient = new TextParaClient(this); paraClientHandle = paraClient.Handle; #pragma warning restore 6518 } ////// GetTextProperties /// /// /// IN: column-span area index /// /// /// OUT: text paragraph properties /// internal void GetTextProperties( int iArea, ref PTS.FSTXTPROPS fstxtprops) { Debug.Assert(iArea == 0); fstxtprops.fswdir = PTS.FlowDirectionToFswdir((FlowDirection)Element.GetValue(FrameworkElement.FlowDirectionProperty)); fstxtprops.dcpStartContent = 0; fstxtprops.fKeepTogether = PTS.FromBoolean(DynamicPropertyReader.GetKeepTogether(Element)); fstxtprops.cMinLinesAfterBreak = DynamicPropertyReader.GetMinOrphanLines(Element); fstxtprops.cMinLinesBeforeBreak = DynamicPropertyReader.GetMinWidowLines(Element); fstxtprops.fDropCap = PTS.False; fstxtprops.fVerticalGrid = PTS.False; fstxtprops.fOptimizeParagraph = PTS.FromBoolean(IsOptimalParagraph); fstxtprops.fAvoidHyphenationAtTrackBottom = PTS.False; fstxtprops.fAvoidHyphenationOnLastChainElement = PTS.False; fstxtprops.cMaxConsecutiveHyphens = int.MaxValue; } ////// CreateOptimalBreakSession /// /// /// Para client /// /// /// Start of line to format /// /// /// dur of track (dur line?) /// /// /// Break record for a given line /// /// /// OUT: Break session /// /// /// OUT: Is paragraph fully justified /// internal void CreateOptimalBreakSession( TextParaClient textParaClient, int dcpStart, int durTrack, LineBreakRecord lineBreakRecord, out OptimalBreakSession optimalBreakSession, out bool isParagraphJustified) { _textRunCache = new TextRunCache(); TextFormatter textFormatter = StructuralCache.TextFormatterHost.TextFormatter; TextLineBreak textLineBreak = lineBreakRecord != null ? lineBreakRecord.TextLineBreak : null; OptimalTextSource optimalTextSource = new OptimalTextSource(StructuralCache.TextFormatterHost, ParagraphStartCharacterPosition, durTrack, textParaClient, _textRunCache); StructuralCache.TextFormatterHost.Context = optimalTextSource; TextParagraphCache paragraphCache = textFormatter.CreateParagraphCache(StructuralCache.TextFormatterHost, dcpStart, TextDpi.FromTextDpi(durTrack), GetLineProperties(true, dcpStart), textLineBreak, _textRunCache); StructuralCache.TextFormatterHost.Context = null; optimalBreakSession = new OptimalBreakSession(this, textParaClient, paragraphCache, optimalTextSource); isParagraphJustified = ((TextAlignment)Element.GetValue(Block.TextAlignmentProperty)) == TextAlignment.Justify; } ////// Get Number Footnotes /// /// /// IN: dcp at the beginning of the range /// /// /// IN: dcp at the end of the range /// /// /// OUT: number of footnote references in the range /// internal void GetNumberFootnotes( int fsdcpStart, int fsdcpLim, out int nFootnote) { nFootnote = 0; } ////// Format Bottom Text /// /// /// IN: column-span area index /// /// /// IN: current direction /// /// /// IN: last formatted line /// /// /// IN: height of last line /// /// /// OUT: margin collapsing state at bottom of text /// internal void FormatBottomText( int iArea, uint fswdir, Line lastLine, int dvrLine, out IntPtr mcsClient) { Invariant.Assert(iArea == 0); // Text paragraph does not handle margin collapsing. Margin collapsing // is done on container paragraph level. mcsClient = IntPtr.Zero; } ////// Determines whether it's desirable or valid to interrupt formatting after this line. /// Returns true if formatting can be interrupted and false if not. /// /// /// dcp of current line /// /// /// vr value of current line /// internal bool InterruptFormatting(int dcpCur, int vrCur) { BackgroundFormatInfo backgroundFormatInfo = StructuralCache.BackgroundFormatInfo; if (!BackgroundFormatInfo.IsBackgroundFormatEnabled) { return false; } if (StructuralCache.CurrentFormatContext.FinitePage) { return false; } // Must format at least this much if (vrCur < TextDpi.ToTextDpi(double.IsPositiveInfinity(backgroundFormatInfo.ViewportHeight) ? 500 : backgroundFormatInfo.ViewportHeight)) { return false; } if (backgroundFormatInfo.BackgroundFormatStopTime > DateTime.UtcNow) { return false; } if (!backgroundFormatInfo.DoesFinalDTRCoverRestOfText) { return false; } if ((dcpCur + ParagraphStartCharacterPosition) <= backgroundFormatInfo.LastCPUninterruptible) { return false; } StructuralCache.BackgroundFormatInfo.CPInterrupted = dcpCur + ParagraphStartCharacterPosition; return true; } ////// FormatLineVariants - Find all possible stopping points for this line /// /// /// IN: Text para client for TextParagraph /// /// /// IN: Paragraph cache /// /// /// IN: Text Source for optimal /// /// /// IN: dcp at the beginning of the line /// /// /// IN: line break record /// /// /// IN: current direction /// /// /// IN: position at the beginning of the line /// /// /// IN: maximum width of line /// /// /// IN: allow hyphenation of the line? /// /// /// IN: is clear on left side /// /// /// IN: is clear on right side /// /// /// IN: treat line as first line in paragraph /// /// /// IN: treat line as last line in paragraph /// /// /// IN: suppress empty space at the top of page /// /// /// IN: line variant restriction handle (pts / ls communication) /// /// /// OUT: index of the bset line variant (pts / ls communication) /// internal System.Collections.Generic.IListFormatLineVariants(TextParaClient textParaClient, TextParagraphCache textParagraphCache, OptimalTextSource optimalTextSource, int dcp, TextLineBreak textLineBreak, uint fswdir, int urStartLine, int durLine, bool allowHyphenation, bool clearOnLeft, bool clearOnRight, bool treatAsFirstInPara, bool treatAsLastInPara, bool suppressTopSpace, IntPtr lineVariantRestriction, out int iLineBestVariant) { StructuralCache.TextFormatterHost.Context = optimalTextSource; System.Collections.Generic.IList textBreakpoints = textParagraphCache.FormatBreakpoints( dcp, textLineBreak, lineVariantRestriction, TextDpi.FromTextDpi(durLine), out iLineBestVariant ); StructuralCache.TextFormatterHost.Context = null; return textBreakpoints; } /// /// ReconstructLineVariant - Reconstruct line variants for a given line. /// /// /// IN: Text para client for TextParagraph /// /// /// IN: column-span area index /// /// /// IN: dcp at the beginning of the line /// /// /// IN: client's line break record /// /// /// IN: dcp of line to format /// /// /// IN: current direction /// /// /// IN: position at the beginning of the line /// /// /// IN: maximum width of line /// /// /// IN: position at the beginning of the track /// /// /// IN: width of track /// /// /// IN: left margin of the page /// /// /// IN: allow hyphenation of the line? /// /// /// IN: is clear on left side /// /// /// IN: is clear on right side /// /// /// IN: treat line as first line in paragraph /// /// /// IN: treat line as last line in paragraph /// /// /// IN: suppress empty space at the top of page /// /// /// OUT: pointer to line created by client /// /// /// OUT: dcp consumed by the line /// /// /// OUT: client's line break record /// /// /// OUT: was line force-broken? /// /// /// OUT: result of formatting /// /// /// OUT: ascent of the line /// /// /// OUT: descent of the line /// /// /// OUT: ur of the line's ink /// /// /// OUT: dur of of the line's ink /// /// /// OUT: number of chars after line break that were considered /// /// /// OUT: should line segments be reformatted? /// internal void ReconstructLineVariant( TextParaClient paraClient, int iArea, int dcp, IntPtr pbrlineIn, int dcpLineIn, uint fswdir, int urStartLine, int durLine, int urStartTrack, int durTrack, int urPageLeftMargin, bool fAllowHyphenation, bool fClearOnLeft, bool fClearOnRight, bool fTreatAsFirstInPara, bool fTreatAsLastInPara, bool fSuppressTopSpace, out IntPtr lineHandle, out int dcpLine, out IntPtr ppbrlineOut, out int fForcedBroken, out PTS.FSFLRES fsflres, out int dvrAscent, out int dvrDescent, out int urBBox, out int durBBox, out int dcpDepend, out int fReformatNeighborsAsLastLine) { // NOTE: there is no empty space added at the top of lines, so fSuppressTopSpace is never used. Invariant.Assert(iArea == 0); StructuralCache.CurrentFormatContext.OnFormatLine(); // Create and format line #pragma warning disable 6518 // Disable PRESharp warning 6518. Line is an UnmamangedHandle, that adds itself // to HandleMapper that holds a reference to it. PTS manages lifetime of this object, and // calls DestroyLine to get rid of it. DestroyLine will call Dispose() on the object // and remove it from HandleMapper. Line line = new Line(StructuralCache.TextFormatterHost, paraClient, ParagraphStartCharacterPosition); #pragma warning restore 6518 Line.FormattingContext ctx = new Line.FormattingContext(true, fClearOnLeft, fClearOnRight, _textRunCache); ctx.LineFormatLengthTarget = dcpLineIn; FormatLineCore(line, pbrlineIn, ctx, dcp, durLine, durTrack, fTreatAsFirstInPara, dcp); // Retrieve line properties lineHandle = line.Handle; dcpLine = line.SafeLength; TextLineBreak textLineBreak = line.GetTextLineBreak(); if(textLineBreak != null) { #pragma warning disable 56518 // Disable PRESharp warning 6518. Line is an UnmamangedHandle, that adds itself // to HandleMapper that holds a reference to it. PTS manages lifetime of this object, and // calls DestroyLineBreakRecord to get rid of it. DestroyLineBreakRecord will call Dispose() on the object // and remove it from HandleMapper. LineBreakRecord lineBreakRecord = new LineBreakRecord(PtsContext, textLineBreak); #pragma warning disable 56518 ppbrlineOut = lineBreakRecord.Handle; } else { ppbrlineOut = IntPtr.Zero; } fForcedBroken = PTS.FromBoolean(line.IsTruncated); fsflres = line.FormattingResult; dvrAscent = line.Baseline; dvrDescent = line.Height - line.Baseline; urBBox = urStartLine + line.Start; durBBox = line.Width; dcpDepend = line.DependantLength; fReformatNeighborsAsLastLine = PTS.False; // NOTE: When LL provides support for line height, following descent // calculation will go away. CalcLineAscentDescent(dcp, ref dvrAscent, ref dvrDescent); // Ensure we don't include the paragraph break synthetic run into our DcpDepend calculation. // We can only trim to total text length, as _cch may not be calculated at this time, and if it's uncalculated, then ParagraphEndCharacterPosition // is potentially incorrect. All of this needs to be reviewed WRT TextSchema. int dcpDependAbsolute = ParagraphStartCharacterPosition + dcp + line.ActualLength + dcpDepend; int textSize = StructuralCache.TextContainer.SymbolCount; if (dcpDependAbsolute > textSize) dcpDependAbsolute = textSize; StructuralCache.CurrentFormatContext.DependentMax = StructuralCache.TextContainer.CreatePointerAtOffset(dcpDependAbsolute, LogicalDirection.Backward); #if TEXTPANELLAYOUTDEBUG if (StructuralCache.CurrentFormatContext.IncrementalUpdate) { TextPanelDebug.Log("TextPara.FormatLine, Start=" + dcp + " Cch=" + dcpLine, TextPanelDebug.Category.ContentChange); } #endif } ////// Format line /// /// /// IN: Text para client for TextParagraph /// /// /// IN: column-span area index /// /// /// IN: dcp at the beginning of the line /// /// /// IN: client's line break record /// /// /// IN: current direction /// /// /// IN: position at the beginning of the line /// /// /// IN: maximum width of line /// /// /// IN: position at the beginning of the track /// /// /// IN: width of track /// /// /// IN: left margin of the page /// /// /// IN: allow hyphenation of the line? /// /// /// IN: is clear on left side /// /// /// IN: is clear on right side /// /// /// IN: treat line as first line in paragraph /// /// /// IN: treat line as last line in paragraph /// /// /// IN: suppress empty space at the top of page /// /// /// OUT: pointer to line created by client /// /// /// OUT: dcp consumed by the line /// /// /// OUT: client's line break record /// /// /// OUT: was line force-broken? /// /// /// OUT: result of formatting /// /// /// OUT: ascent of the line /// /// /// OUT: descent of the line /// /// /// OUT: ur of the line's ink /// /// /// OUT: dur of of the line's ink /// /// /// OUT: number of chars after line break that were considered /// /// /// OUT: should line segments be reformatted? /// internal void FormatLine( TextParaClient paraClient, int iArea, int dcp, IntPtr pbrlineIn, uint fswdir, int urStartLine, int durLine, int urStartTrack, int durTrack, int urPageLeftMargin, bool fAllowHyphenation, bool fClearOnLeft, bool fClearOnRight, bool fTreatAsFirstInPara, bool fTreatAsLastInPara, bool fSuppressTopSpace, out IntPtr lineHandle, out int dcpLine, out IntPtr ppbrlineOut, out int fForcedBroken, out PTS.FSFLRES fsflres, out int dvrAscent, out int dvrDescent, out int urBBox, out int durBBox, out int dcpDepend, out int fReformatNeighborsAsLastLine) { // NOTE: there is no empty space added at the top of lines, so fSuppressTopSpace is never used. Invariant.Assert(iArea == 0); StructuralCache.CurrentFormatContext.OnFormatLine(); // Create and format line #pragma warning disable 6518 // Disable PRESharp warning 6518. Line is an UnmamangedHandle, that adds itself // to HandleMapper that holds a reference to it. PTS manages lifetime of this object, and // calls DestroyLine to get rid of it. DestroyLine will call Dispose() on the object // and remove it from HandleMapper. Line line = new Line(StructuralCache.TextFormatterHost, paraClient, ParagraphStartCharacterPosition); #pragma warning restore 6518 Line.FormattingContext ctx = new Line.FormattingContext(true, fClearOnLeft, fClearOnRight, _textRunCache); FormatLineCore(line, pbrlineIn, ctx, dcp, durLine, durTrack, fTreatAsFirstInPara, dcp); // Retrieve line properties lineHandle = line.Handle; dcpLine = line.SafeLength; TextLineBreak textLineBreak = line.GetTextLineBreak(); if(textLineBreak != null) { #pragma warning disable 56518 // Disable PRESharp warning 6518. Line is an UnmamangedHandle, that adds itself // to HandleMapper that holds a reference to it. PTS manages lifetime of this object, and // calls DestroyLineBreakRecord to get rid of it. DestroyLineBreakRecord will call Dispose() on the object // and remove it from HandleMapper. LineBreakRecord lineBreakRecord = new LineBreakRecord(PtsContext, textLineBreak); #pragma warning restore 56518 ppbrlineOut = lineBreakRecord.Handle; } else { ppbrlineOut = IntPtr.Zero; } fForcedBroken = PTS.FromBoolean(line.IsTruncated); fsflres = line.FormattingResult; dvrAscent = line.Baseline; dvrDescent = line.Height - line.Baseline; urBBox = urStartLine + line.Start; durBBox = line.Width; dcpDepend = line.DependantLength; fReformatNeighborsAsLastLine = PTS.False; // NOTE: When LL provides support for line height, following descent // calculation will go away. CalcLineAscentDescent(dcp, ref dvrAscent, ref dvrDescent); // Ensure we don't include the paragraph break synthetic run into our DcpDepend calculation. // We can only trim to total text length, as _cch may not be calculated at this time, and if it's uncalculated, then ParagraphEndCharacterPosition // is potentially incorrect. All of this needs to be reviewed WRT TextSchema. int dcpDependAbsolute = ParagraphStartCharacterPosition + dcp + line.ActualLength + dcpDepend; int textSize = StructuralCache.TextContainer.SymbolCount; if (dcpDependAbsolute > textSize) dcpDependAbsolute = textSize; StructuralCache.CurrentFormatContext.DependentMax = StructuralCache.TextContainer.CreatePointerAtOffset(dcpDependAbsolute, LogicalDirection.Backward); #if TEXTPANELLAYOUTDEBUG if (StructuralCache.CurrentFormatContext.IncrementalUpdate) { TextPanelDebug.Log("TextPara.FormatLine, Start=" + dcp + " Cch=" + dcpLine, TextPanelDebug.Category.ContentChange); } #endif } ////// UpdGetChangeInText /// /// /// OUT: start of change /// /// /// OUT: number of chars in old range /// /// /// OUT: number of chars in new range /// internal void UpdGetChangeInText( out int dcpStart, out int ddcpOld, out int ddcpNew) { // Get dtr list for the text presenter DtrList dtrs = StructuralCache.DtrsFromRange(ParagraphStartCharacterPosition, LastFormatCch); if (dtrs != null) { // Union all dtrs. Note: there are no overlapping entries in the list of DTRs. dcpStart = dtrs[0].StartIndex - ParagraphStartCharacterPosition; ddcpNew = dtrs[0].PositionsAdded; ddcpOld = dtrs[0].PositionsRemoved; if (dtrs.Length > 1) { for (int i = 1; i < dtrs.Length; i++) { int delta = dtrs[i].StartIndex - dtrs[i-1].StartIndex; ddcpNew += delta + dtrs[i].PositionsAdded; ddcpOld += delta + dtrs[i].PositionsRemoved; } } // Get rid of embedded objects within dirty range and // update dcp of all object which are following dirty range // For finite page paragraph is reformatted from scrach, so all caches // are up to date and PTS needs only info about the change. if (!StructuralCache.CurrentFormatContext.FinitePage) { UpdateEmbeddedObjectsCache(ref _attachedObjects, dcpStart, ddcpOld, ddcpNew - ddcpOld); UpdateEmbeddedObjectsCache(ref _inlineObjects, dcpStart, ddcpOld, ddcpNew - ddcpOld); } Invariant.Assert(dcpStart >= 0 && Cch >= dcpStart && LastFormatCch >= dcpStart); // Max out at possible number of chars in old and new ranges, adding one for EOP dcp. ddcpOld = Math.Min(ddcpOld, (LastFormatCch - dcpStart) + 1); ddcpNew = Math.Min(ddcpNew, (Cch - dcpStart) + 1); } else { // PTS may call this callback for paragraph which has not been changed in // case of complex page layout (with figures). Return 0 to notify that // there is no change. dcpStart = ddcpOld = ddcpNew = 0; } #if TEXTPANELLAYOUTDEBUG if (StructuralCache.CurrentFormatContext.IncrementalUpdate) { TextPanelDebug.Log("TextPara.UpdGetChangeInText, Start=" + dcpStart + " Old=" + ddcpOld + " New=" + ddcpNew, TextPanelDebug.Category.ContentChange); } #endif } ////// Get Dvr Advance /// /// /// IN: dcp at the beginning of the line /// /// /// IN: current direction /// /// /// OUT: advance amount in tight wrap /// internal void GetDvrAdvance( int dcp, uint fswdir, out int dvr) { EnsureLineProperties(); // When tight wrap is enabled, PTS may not fit line that starts at 'dcp' // at the current vertical offset. // In this situation PTS asks the client by how much it needs to // advance in vertical direction to try again. // For optimal results 1px should be enough, but this has performance hit. // Word decide to use height a character in the paragraph font. The similar // logic is used here. Advance by value of the default line height. dvr = TextDpi.ToTextDpi(_lineProperties.CalcLineAdvanceForTextParagraph(this, dcp, _lineProperties.DefaultTextRunProperties.FontRenderingEmSize)); } ////// Walks the text tree from a given dcp and skips over figure and floater elements, returning the dcp after the last one. /// internal int GetLastDcpAttachedObjectBeforeLine(int dcpFirst) { ITextPointer textPointer = TextContainerHelper.GetTextPointerFromCP(StructuralCache.TextContainer, ParagraphStartCharacterPosition + dcpFirst, LogicalDirection.Forward); ITextPointer textPointerContentStart = TextContainerHelper.GetContentStart(StructuralCache.TextContainer, Element); while(textPointer.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart) { TextElement element = ((TextPointer)textPointer).GetAdjacentElementFromOuterPosition(LogicalDirection.Forward); if(!(element is Figure) && !(element is Floater)) { break; } textPointer.MoveByOffset(element.SymbolCount); } return textPointerContentStart.GetOffsetToPosition(textPointer); } ////// Returns the text elements for a given dcp range. /// private ListGetAttachedObjectElements(int dcpFirst, int dcpLast) { List attachedElements = new List (); ITextPointer textPointerContentStart = TextContainerHelper.GetContentStart(StructuralCache.TextContainer, Element); ITextPointer textPointer = TextContainerHelper.GetTextPointerFromCP(StructuralCache.TextContainer, ParagraphStartCharacterPosition + dcpFirst, LogicalDirection.Forward); if(dcpLast > this.Cch) { dcpLast = this.Cch; // Remove end of paragraph run cp. } while(textPointerContentStart.GetOffsetToPosition(textPointer) < dcpLast) { if(textPointer.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart) { TextElement element = ((TextPointer)textPointer).GetAdjacentElementFromOuterPosition(LogicalDirection.Forward); if(element is Figure || element is Floater) { attachedElements.Add(element); textPointer.MoveByOffset(element.SymbolCount); } else { textPointer.MoveToNextContextPosition(LogicalDirection.Forward); } } else { textPointer.MoveToNextContextPosition(LogicalDirection.Forward); } } return attachedElements; } /// /// Returns the count of attached objects over a given dcp range /// internal int GetAttachedObjectCount(int dcpFirst, int dcpLast) { ListtextElements = GetAttachedObjectElements(dcpFirst, dcpLast); if(textElements.Count == 0) { SubmitAttachedObjects(dcpFirst, dcpLast, null); } return textElements.Count; } /// /// Returns the attached object list for a dcp range by wrapping floating text elements in Figure/FloaterObject tags. /// internal ListGetAttachedObjects(int dcpFirst, int dcpLast) { ITextPointer textPointerContentStart = TextContainerHelper.GetContentStart(StructuralCache.TextContainer, Element); List attachedObjects = new List (); List textElements = GetAttachedObjectElements(dcpFirst, dcpLast); for(int index = 0; index < textElements.Count; index++) { TextElement textElement = textElements[index]; if(textElement is Figure && StructuralCache.CurrentFormatContext.FinitePage) { #pragma warning disable 6518 // Disable PRESharp warning 6518. FigureParagraph is passed to attached objects // which will do following: // a) store this object in TextParagraph._floaters collection. Later when // TextParagraph is disposed, all objects in _floaters collection will be // also disposed. // b) call Dispose() on this object, if it already exists in TextParagraph._floaters // collection. // c) call Dipose() on this object during layout pass following removal of floater. FigureParagraph figurePara = new FigureParagraph(textElement, StructuralCache); #pragma warning restore 6518 if (StructuralCache.CurrentFormatContext.IncrementalUpdate) { figurePara.SetUpdateInfo(PTS.FSKCHANGE.fskchNew, false); } FigureObject figureObject = new FigureObject(textPointerContentStart.GetOffsetToPosition(textElement.ElementStart), figurePara); attachedObjects.Add(figureObject); } else { #pragma warning disable 6518 // Disable PRESharp warning 6518. FigureParagraph is passed to attached objects // which will do following: // a) store this object in TextParagraph._floaters collection. Later when // TextParagraph is disposed, all objects in _floaters collection will be // also disposed. // b) call Dispose() on this object, if it already exists in TextParagraph._floaters // collection. // c) call Dipose() on this object during layout pass following removal of floater. FloaterParagraph floaterPara = new FloaterParagraph(textElement, StructuralCache); #pragma warning restore 6518 if (StructuralCache.CurrentFormatContext.IncrementalUpdate) { floaterPara.SetUpdateInfo(PTS.FSKCHANGE.fskchNew, false); } FloaterObject floaterObject = new FloaterObject(textPointerContentStart.GetOffsetToPosition(textElement.ElementStart), floaterPara); attachedObjects.Add(floaterObject); } } // If it were 0, should have been submitted when count was queried. if(attachedObjects.Count != 0) { SubmitAttachedObjects(dcpFirst, dcpLast, attachedObjects); } return attachedObjects; } #endregion PTS callbacks // ----------------------------------------------------------------- // // Internal Methods // // ------------------------------------------------------------------ #region Internal Methods /// /// Submit inline objects for specified range to the cache. All existing /// inline objects in this particular range will be removed. /// /// /// Dcp of the beginning of the range to update. /// /// /// Dcp of the end of the range to update. /// /// /// Array of inline objects. /// internal void SubmitInlineObjects(int dcpStart, int dcpLim, ListinlineObjects) { SubmitEmbeddedObjects(ref _inlineObjects, dcpStart, dcpLim, inlineObjects); } /// /// Submit floaters for specified range to the cache. All existing /// floaters in this particular range will be removed. /// /// /// Dcp of the beginning of the range to update. /// /// /// Dcp of the end of the range to update. /// /// /// Array of attached objects. /// internal void SubmitAttachedObjects(int dcpStart, int dcpLim, ListattachedObjects) { SubmitEmbeddedObjects(ref _attachedObjects, dcpStart, dcpLim, attachedObjects); } /// /// Returns a list of inline object from specifed range /// /// /// Dcp of the beginning of the range. /// /// /// Dcp of the end of the range. /// internal ListInlineObjectsFromRange(int dcpStart, int dcpLast) { List objects = null; if (_inlineObjects != null) { objects = new List (_inlineObjects.Count); for (int i = 0; i < _inlineObjects.Count; i++) { InlineObject obj = _inlineObjects[i]; if (obj.Dcp >= dcpStart && obj.Dcp < dcpLast) { objects.Add(obj); } else if (obj.Dcp >= dcpLast) { // No reason to continue break; } } } if(objects == null || objects.Count == 0) { return null; } return objects; } /// /// Calculate and return line advance distance. This functionality will go away /// when TextFormatter will be able to handle line height/stacking. /// /// /// dcp of the line /// /// /// Calculated dvr ascent /// /// /// Calculated dvr descent /// internal void CalcLineAscentDescent(int dcp, ref int dvrAscent, ref int dvrDescent) { EnsureLineProperties(); int thisLineAdvance = dvrAscent + dvrDescent; int calculatedLineAdvance = TextDpi.ToTextDpi(_lineProperties.CalcLineAdvanceForTextParagraph(this, dcp, TextDpi.FromTextDpi(thisLineAdvance))); if(thisLineAdvance != calculatedLineAdvance) { double scale = (1.0 * calculatedLineAdvance) / (1.0 * thisLineAdvance); dvrAscent = (int) (dvrAscent * scale); dvrDescent = (int) (dvrDescent * scale); } } ////// Set update info. Those flags are used later by PTS to decide /// if paragraph needs to be updated and when to stop asking for /// update information. /// /// /// Type of change within the paragraph. /// /// /// Synchronization point is reached? /// internal override void SetUpdateInfo(PTS.FSKCHANGE fskch, bool stopAsking) { base.SetUpdateInfo(fskch, stopAsking); if (fskch == PTS.FSKCHANGE.fskchInside) { // Update _cch so we always have correct value during update process _textRunCache = new TextRunCache(); _lineProperties = null; } } ////// Clear previously accumulated update info. /// internal override void ClearUpdateInfo() { base.ClearUpdateInfo(); // Clear update info of all floaters and figures if (_attachedObjects != null) { for (int index=0; index < _attachedObjects.Count; index++) { _attachedObjects[index].Para.ClearUpdateInfo(); } } } ////// Invalidate content's structural cache. Returns true if entire paragraph is invalid. /// /// /// Position to start invalidation from. /// internal override bool InvalidateStructure(int startPosition) { // Change must be inside text paragraph. Invariant.Assert(ParagraphEndCharacterPosition >= startPosition); bool invalid = false; // Thre are 2 situations: // 1) the beginning is startPosition, in this case paragraph content is valid only if // the first element in the para is figure/floater and the element owner has not // been changed. // 2) startPosition is in the middle of paragraph, in this case paragraph content is valid. if (ParagraphStartCharacterPosition == startPosition) { // 1) the beginning is startPosition, in this case paragraph content is valid only if // the first element in the para is figure/floater and the element owner has not // been changed. invalid = true; // Get element owner of the first figure or floater, whichever comes first AnchoredBlock objectElement = null; if(_attachedObjects != null && _attachedObjects.Count > 0) { objectElement = (AnchoredBlock)(_attachedObjects[0].Element); } // If figure/floater starts at the beginning of paragraph and element owner did // not change, treat the paragraph as valid. if (objectElement != null) { if (startPosition == objectElement.ElementStartOffset) { StaticTextPointer position = TextContainerHelper.GetStaticTextPointerFromCP(StructuralCache.TextContainer, startPosition); if (position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart) { invalid = (objectElement != position.GetAdjacentElement(LogicalDirection.Forward)); } // else invalid } // else invalid } // else invalid } // else // 2) startPosition is in the middle of paragraph, in this case paragraph content is valid. // Invalidate text format caches. InvalidateTextFormatCache(); // Invalidate structure of floaters and figures if (_attachedObjects != null) { for (int index=0; index < _attachedObjects.Count; index++) { BaseParagraph attachedObjectPara = _attachedObjects[index].Para; if(attachedObjectPara.ParagraphEndCharacterPosition >= startPosition) { attachedObjectPara.InvalidateStructure(startPosition); } } } return invalid; } ////// Invalidate accumulated format caches. /// internal override void InvalidateFormatCache() { InvalidateTextFormatCache(); // Invalidate structure of floaters and figures if (_attachedObjects != null) { for (int index=0; index < _attachedObjects.Count; index++) { _attachedObjects[index].Para.InvalidateFormatCache(); } } } ////// Invalidate format cache. /// internal void InvalidateTextFormatCache() { _textRunCache = new TextRunCache(); _lineProperties = null; } ////// Format text line. /// /// /// Text line to format /// /// /// Break record of line /// /// /// TextFormatter context /// /// /// Dcp of line start /// /// /// Width of line /// /// /// First line in paragraph? /// /// /// Character position where the line starts. /// internal void FormatLineCore(Line line, IntPtr pbrLineIn, Line.FormattingContext ctx, int dcp, int width, bool firstLine, int dcpLine) { FormatLineCore(line, pbrLineIn, ctx, dcp, width, -1, firstLine, dcpLine); } ////// Format text line. Includes track width, needed during Measure to /// size inline elements. /// /// /// Text line to be formatted /// /// /// Break record of line /// /// /// TextFormatter context /// /// /// dcp of line start /// /// /// Line width /// /// /// Requested width of track /// /// /// First line in paragraph? /// /// /// Character position where the line starts. /// internal void FormatLineCore(Line line, IntPtr pbrLineIn, Line.FormattingContext ctx, int dcp, int width, int trackWidth, bool firstLine, int dcpLine) { TextDpi.EnsureValidLineWidth(ref width); _currentLine = line; TextLineBreak textLineBreak = null; if(pbrLineIn != IntPtr.Zero) { LineBreakRecord lineBreakRecord = PtsContext.HandleToObject(pbrLineIn) as LineBreakRecord; PTS.ValidateHandle(lineBreakRecord); textLineBreak = lineBreakRecord.TextLineBreak; } try { line.Format(ctx, dcp, width, trackWidth, GetLineProperties(firstLine, dcpLine), textLineBreak); } finally { _currentLine = null; } } ////// Measure child UIElement and return its size. /// /// /// InlineObjectRun with child UIElement to measure /// internal Size MeasureChild(InlineObjectRun inlineObject) { if(_currentLine == null) { return ((OptimalTextSource)StructuralCache.TextFormatterHost.Context).MeasureChild(inlineObject); } else { return _currentLine.MeasureChild(inlineObject); } } ////// Returns true if there's anything complicated about this para - figures, floaters /// or inline objects. Returns false if para contains simple text. /// internal bool HasFiguresFloatersOrInlineObjects() { if(HasFiguresOrFloaters() || (_inlineObjects != null && _inlineObjects.Count > 0)) { return true; } return false; } ////// Returns true if figures or floaters exist for this para /// internal bool HasFiguresOrFloaters() { return _attachedObjects != null && _attachedObjects.Count > 0; } ////// Updates text content range with attached object list. Subtracts out all of the known figures and floaters /// Ranges, then adds back in the ranges for the para clients. /// internal void UpdateTextContentRangeFromAttachedObjects(TextContentRange textContentRange, int dcpFirst, int dcpLast, PTS.FSATTACHEDOBJECTDESCRIPTION [] arrayAttachedObjectDesc) { int cpCur = dcpFirst; for(int index = 0; _attachedObjects != null && index < _attachedObjects.Count; index++) { AttachedObject attachedObject = _attachedObjects[index]; int startContentPosition = attachedObject.Para.ParagraphStartCharacterPosition; int paraCch = attachedObject.Para.Cch; if(startContentPosition >= cpCur && startContentPosition < dcpLast) { textContentRange.Merge(new TextContentRange(cpCur, startContentPosition, StructuralCache.TextContainer)); cpCur = startContentPosition + paraCch; // Skip past para content range } if(dcpLast < cpCur) { break; } } if(cpCur < dcpLast) { textContentRange.Merge(new TextContentRange(cpCur, dcpLast, StructuralCache.TextContainer)); } for(int index = 0; arrayAttachedObjectDesc != null && index < arrayAttachedObjectDesc.Length; index++) { PTS.FSATTACHEDOBJECTDESCRIPTION attachedObject = arrayAttachedObjectDesc[index]; BaseParaClient paraClient; paraClient = PtsContext.HandleToObject(arrayAttachedObjectDesc[index].pfsparaclient) as BaseParaClient; PTS.ValidateHandle(paraClient); textContentRange.Merge(paraClient.GetTextContentRange()); } } ////// Handler for DesiredSizeChanged raised by UIElement island. /// internal void OnUIElementDesiredSizeChanged(object sender, DesiredSizeChangedEventArgs e) { StructuralCache.FormattingOwner.OnChildDesiredSizeChanged(e.Child); } #endregion Internal Methods // ----------------------------------------------------------------- // // Internal Properties // // ----------------------------------------------------------------- #region Internal Properties ////// Run cache used by text formatter. /// internal TextRunCache TextRunCache { get { return _textRunCache; } } ////// Text paragraph properties. /// internal LineProperties Properties { get { EnsureLineProperties(); return _lineProperties; } } ////// Optimal paragraph flag /// internal bool IsOptimalParagraph { get { return StructuralCache.IsOptimalParagraphEnabled && GetLineProperties(false, 0).TextWrapping != TextWrapping.NoWrap; } } #endregion Internal Properties // ----------------------------------------------------------------- // // Private Methods // // ------------------------------------------------------------------ #region Private Methods ////// Refetch and cache line properties, if needed. /// private void EnsureLineProperties() { if (_lineProperties == null) { // For default text properties always set background to null. // REASON: If element associated with the text run is block element, ignore background // brush, because it is handled by paragraph itself. TextProperties defaultTextProperties = new TextProperties(Element, StaticTextPointer.Null, false /* inline objects */, false /* get background */); _lineProperties = new LineProperties(Element, StructuralCache.FormattingOwner, defaultTextProperties, null); // No marker properties bool isHyphenationEnabled = (bool) Element.GetValue(Block.IsHyphenationEnabledProperty); if(isHyphenationEnabled) { _lineProperties.Hyphenator = StructuralCache.Hyphenator; } } } ////// Submit embedded objects for specified range. All existing objects /// in this particular range will be removed. /// /// /// Array of cached embedded objects. /// /// /// Dcp of the beginning of the range to update. /// /// /// Dcp of the end of the range to update. /// /// /// Array of new embedded objects. /// private void SubmitEmbeddedObjects(ref List objectsCached, int dcpStart, int dcpLim, List objectsNew) where T : EmbeddedObject { ErrorHandler.Assert(objectsNew == null || (objectsNew[0].Dcp >= dcpStart && objectsNew[objectsNew.Count-1].Dcp <= dcpLim), ErrorHandler.SubmitInvalidList); // Make sure that cached objects array exists if (objectsCached == null) { if (objectsNew == null) { // Nothing to do return; } objectsCached = new List (objectsNew.Count); } // Find affected range of cached objects int end = objectsCached.Count; while (end > 0 && objectsCached[end-1].Dcp >= dcpLim) --end; int start = end; while (start > 0 && objectsCached[start-1].Dcp >= dcpStart) --start; // There are 3 situations, which may happen when submitting embedded objects: // (1) Only remove obsolete objects (no objects to add) // (2) Only add new objects (no objects are obsolete) // (3) Merge new objects into existing list (may add and remove objects) if (objectsNew == null) { // (1) Only remove obsolete objects (no objects to add) for (int index = start; index < end; index++) { objectsCached[index].Dispose(); } objectsCached.RemoveRange(start, end - start); } else if (end == start) { // (2) Only add new objects (no objects are obsolete) objectsCached.InsertRange(start, objectsNew); } else { // (3) Merge new objects into existing list (may add and remove objects) int idxNew = 0; while (start < end) { // Iterate through list of existing objects, which are affected by the // change range. There are 2 possibilities: // (1) There is matching object in the list of new objects. // (2) The object is obsolete, it should be removed. T oldEmbeddedObject = objectsCached[start]; int idx = idxNew; while (idx < objectsNew.Count) { T newEmbeddedObject = objectsNew[idx]; if (oldEmbeddedObject.Element == newEmbeddedObject.Element) { // (1) There is matching object in the list of new objects. // In this case: // * insert all preceding objects from new object list // * update object information if (idx > idxNew) { objectsCached.InsertRange(start, objectsNew.GetRange(idxNew, idx - idxNew)); end += idx - idxNew; start += idx - idxNew; } oldEmbeddedObject.Update(newEmbeddedObject); objectsNew[idx] = oldEmbeddedObject; idxNew = idx + 1; ++start; // Dispose unused EmbeddedObject newEmbeddedObject.Dispose(); break; } ++idx; } if (idx >= objectsNew.Count) { // (2) The object is obsolete - remove it. objectsCached[start].Dispose(); objectsCached.RemoveAt(start); --end; } } if (idxNew < objectsNew.Count) { // If we have any objects left in the new object list, insert them. objectsCached.InsertRange(end, objectsNew.GetRange(idxNew, objectsNew.Count - idxNew)); } } } //------------------------------------------------------------------- // //-------------------------------------------------------------------- /// /// Get rid of embedded objects within dirty range and update dcp of /// all object which are following dirty range. /// /// /// array of cached embedded objects /// /// /// dcp of the beginning of the range to update /// /// /// number of characters deleted /// /// /// difference in characters count /// private void UpdateEmbeddedObjectsCache( ref List objectsCached, int dcpStart, int cchDeleted, int cchDiff) where T : EmbeddedObject { if (objectsCached != null) { // Find the first and last affected object int first = 0; while (first < objectsCached.Count && objectsCached[first].Dcp < dcpStart) { ++first; } int last = first; while (last < objectsCached.Count && objectsCached[last].Dcp < dcpStart + cchDeleted) { ++last; } // Remove obsolete embedded objects if (first != last) { for (int index = first; index < last; index++) { objectsCached[index].Dispose(); } objectsCached.RemoveRange(first, last - first); } // Update dcp of all objects following dirty range while (last < objectsCached.Count) { objectsCached[last].Dcp += cchDiff; ++last; } if (objectsCached.Count == 0) { objectsCached = null; } } } /// /// Get line properties /// /// /// First line in paragraph? /// /// /// Character position where line starts. /// private TextParagraphProperties GetLineProperties(bool firstLine, int dcpLine) { EnsureLineProperties(); if (firstLine && _lineProperties.HasFirstLineProperties) { // There are 2 situations, where PTS claims that it is the first line. // a) first complex composite line - only the first element // should be treated as the first line. // b) paragraph nesting - if there are 2 or more TextParagraphs representing // the same Element, only the first one has the first line properties. // In those cases need to ignore first line properties. if (dcpLine != 0) { // a) first complex composite line - only the first element // should be treated as the first line. firstLine = false; } else { // b) paragraph nesting - if there are 2 or more TextParagraphs representing // the same Element, only the first one has the first line properties. int cpElement = TextContainerHelper.GetCPFromElement(StructuralCache.TextContainer, Element, ElementEdge.AfterStart); if (cpElement < this.ParagraphStartCharacterPosition) { firstLine = false; } } // If we still have the first line, use first line properties. if (firstLine) { return _lineProperties.FirstLineProps; } } return _lineProperties; } #endregion Private Methods // ------------------------------------------------------------------ // // Private Fields // // ----------------------------------------------------------------- #region Private Fields ////// List of attached objects. /// private List_attachedObjects; #if DEBUG internal List AttachedObjectDbg { get { return _attachedObjects; } } #endif /// /// List of inline objects. /// private List_inlineObjects; /// /// Line properties /// private LineProperties _lineProperties; ////// Run cache used by text formatter. /// private TextRunCache _textRunCache = new TextRunCache(); ////// Currently formatted line. Valid only during line formatting. /// private Line _currentLine; #endregion Private Fields } } #pragma warning enable 1634, 1691 // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // // File: ContainerParagraph.cs // // Description: TextParagraph is a Paragraph representing continuous sequence // of lines. // // History: // 05/05/2003 : [....] - moving from Avalon branch. // 10/30/2004 : [....] - ElementReference cleanup. // //--------------------------------------------------------------------------- #pragma warning disable 1634, 1691 // avoid generating warnings about unknown // message numbers and unknown pragmas for PRESharp contol using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Media; using System.Windows.Media.TextFormatting; using System.Windows.Threading; using MS.Internal.Text; using MS.Internal.Documents; using MS.Internal.PtsHost.UnsafeNativeMethods; namespace MS.Internal.PtsHost { ////// TextParagraph is a Paragraph representing continuous sequence of lines /// and it is using built-in PTS text paragraph handler to do formatting. /// internal sealed class TextParagraph : BaseParagraph { //------------------------------------------------------------------- // // Constructors // //------------------------------------------------------------------- #region Constructors ////// Constructor. /// /// /// Element associated with paragraph. /// /// /// Content's structural cache /// internal TextParagraph(DependencyObject element, StructuralCache structuralCache) : base(element, structuralCache) { } #endregion Constructors //-------------------------------------------------------------------- // // IDisposable Methods // //------------------------------------------------------------------- #region IDisposable Methods ////// IDisposable.Dispose /// public override void Dispose() { if(_attachedObjects != null) { foreach(AttachedObject obj in _attachedObjects) { obj.Dispose(); } _attachedObjects = null; } if (_inlineObjects != null) { foreach (InlineObject obj in _inlineObjects) { obj.Dispose(); } _inlineObjects = null; } base.Dispose(); } #endregion IDisposable Methods // ------------------------------------------------------------------ // // PTS callbacks // // ------------------------------------------------------------------ #region PTS callbacks ////// GetParaProperties /// /// /// OUT: paragraph properties /// internal override void GetParaProperties( ref PTS.FSPAP fspap) { // Paragraph associated with the element (SegmentParagraph) will // take care of break control. fspap.fKeepWithNext = PTS.False; fspap.fBreakPageBefore = PTS.False; fspap.fBreakColumnBefore = PTS.False; GetParaProperties(ref fspap, true); // true means ignore element props fspap.idobj = PTS.fsidobjText; } ////// CreateParaclient /// /// /// OUT: Para client handle, opaque to PTS paragraph client /// internal override void CreateParaclient( out IntPtr paraClientHandle) { #pragma warning disable 6518 // Disable PRESharp warning 6518. TextParaClient is an UnmamangedHandle, that adds itself // to HandleMapper that holds a reference to it. PTS manages lifetime of this object, and // calls DestroyParaclient to get rid of it. DestroyParaclient will call Dispose() on the object // and remove it from HandleMapper. TextParaClient paraClient = new TextParaClient(this); paraClientHandle = paraClient.Handle; #pragma warning restore 6518 } ////// GetTextProperties /// /// /// IN: column-span area index /// /// /// OUT: text paragraph properties /// internal void GetTextProperties( int iArea, ref PTS.FSTXTPROPS fstxtprops) { Debug.Assert(iArea == 0); fstxtprops.fswdir = PTS.FlowDirectionToFswdir((FlowDirection)Element.GetValue(FrameworkElement.FlowDirectionProperty)); fstxtprops.dcpStartContent = 0; fstxtprops.fKeepTogether = PTS.FromBoolean(DynamicPropertyReader.GetKeepTogether(Element)); fstxtprops.cMinLinesAfterBreak = DynamicPropertyReader.GetMinOrphanLines(Element); fstxtprops.cMinLinesBeforeBreak = DynamicPropertyReader.GetMinWidowLines(Element); fstxtprops.fDropCap = PTS.False; fstxtprops.fVerticalGrid = PTS.False; fstxtprops.fOptimizeParagraph = PTS.FromBoolean(IsOptimalParagraph); fstxtprops.fAvoidHyphenationAtTrackBottom = PTS.False; fstxtprops.fAvoidHyphenationOnLastChainElement = PTS.False; fstxtprops.cMaxConsecutiveHyphens = int.MaxValue; } ////// CreateOptimalBreakSession /// /// /// Para client /// /// /// Start of line to format /// /// /// dur of track (dur line?) /// /// /// Break record for a given line /// /// /// OUT: Break session /// /// /// OUT: Is paragraph fully justified /// internal void CreateOptimalBreakSession( TextParaClient textParaClient, int dcpStart, int durTrack, LineBreakRecord lineBreakRecord, out OptimalBreakSession optimalBreakSession, out bool isParagraphJustified) { _textRunCache = new TextRunCache(); TextFormatter textFormatter = StructuralCache.TextFormatterHost.TextFormatter; TextLineBreak textLineBreak = lineBreakRecord != null ? lineBreakRecord.TextLineBreak : null; OptimalTextSource optimalTextSource = new OptimalTextSource(StructuralCache.TextFormatterHost, ParagraphStartCharacterPosition, durTrack, textParaClient, _textRunCache); StructuralCache.TextFormatterHost.Context = optimalTextSource; TextParagraphCache paragraphCache = textFormatter.CreateParagraphCache(StructuralCache.TextFormatterHost, dcpStart, TextDpi.FromTextDpi(durTrack), GetLineProperties(true, dcpStart), textLineBreak, _textRunCache); StructuralCache.TextFormatterHost.Context = null; optimalBreakSession = new OptimalBreakSession(this, textParaClient, paragraphCache, optimalTextSource); isParagraphJustified = ((TextAlignment)Element.GetValue(Block.TextAlignmentProperty)) == TextAlignment.Justify; } ////// Get Number Footnotes /// /// /// IN: dcp at the beginning of the range /// /// /// IN: dcp at the end of the range /// /// /// OUT: number of footnote references in the range /// internal void GetNumberFootnotes( int fsdcpStart, int fsdcpLim, out int nFootnote) { nFootnote = 0; } ////// Format Bottom Text /// /// /// IN: column-span area index /// /// /// IN: current direction /// /// /// IN: last formatted line /// /// /// IN: height of last line /// /// /// OUT: margin collapsing state at bottom of text /// internal void FormatBottomText( int iArea, uint fswdir, Line lastLine, int dvrLine, out IntPtr mcsClient) { Invariant.Assert(iArea == 0); // Text paragraph does not handle margin collapsing. Margin collapsing // is done on container paragraph level. mcsClient = IntPtr.Zero; } ////// Determines whether it's desirable or valid to interrupt formatting after this line. /// Returns true if formatting can be interrupted and false if not. /// /// /// dcp of current line /// /// /// vr value of current line /// internal bool InterruptFormatting(int dcpCur, int vrCur) { BackgroundFormatInfo backgroundFormatInfo = StructuralCache.BackgroundFormatInfo; if (!BackgroundFormatInfo.IsBackgroundFormatEnabled) { return false; } if (StructuralCache.CurrentFormatContext.FinitePage) { return false; } // Must format at least this much if (vrCur < TextDpi.ToTextDpi(double.IsPositiveInfinity(backgroundFormatInfo.ViewportHeight) ? 500 : backgroundFormatInfo.ViewportHeight)) { return false; } if (backgroundFormatInfo.BackgroundFormatStopTime > DateTime.UtcNow) { return false; } if (!backgroundFormatInfo.DoesFinalDTRCoverRestOfText) { return false; } if ((dcpCur + ParagraphStartCharacterPosition) <= backgroundFormatInfo.LastCPUninterruptible) { return false; } StructuralCache.BackgroundFormatInfo.CPInterrupted = dcpCur + ParagraphStartCharacterPosition; return true; } ////// FormatLineVariants - Find all possible stopping points for this line /// /// /// IN: Text para client for TextParagraph /// /// /// IN: Paragraph cache /// /// /// IN: Text Source for optimal /// /// /// IN: dcp at the beginning of the line /// /// /// IN: line break record /// /// /// IN: current direction /// /// /// IN: position at the beginning of the line /// /// /// IN: maximum width of line /// /// /// IN: allow hyphenation of the line? /// /// /// IN: is clear on left side /// /// /// IN: is clear on right side /// /// /// IN: treat line as first line in paragraph /// /// /// IN: treat line as last line in paragraph /// /// /// IN: suppress empty space at the top of page /// /// /// IN: line variant restriction handle (pts / ls communication) /// /// /// OUT: index of the bset line variant (pts / ls communication) /// internal System.Collections.Generic.IListFormatLineVariants(TextParaClient textParaClient, TextParagraphCache textParagraphCache, OptimalTextSource optimalTextSource, int dcp, TextLineBreak textLineBreak, uint fswdir, int urStartLine, int durLine, bool allowHyphenation, bool clearOnLeft, bool clearOnRight, bool treatAsFirstInPara, bool treatAsLastInPara, bool suppressTopSpace, IntPtr lineVariantRestriction, out int iLineBestVariant) { StructuralCache.TextFormatterHost.Context = optimalTextSource; System.Collections.Generic.IList textBreakpoints = textParagraphCache.FormatBreakpoints( dcp, textLineBreak, lineVariantRestriction, TextDpi.FromTextDpi(durLine), out iLineBestVariant ); StructuralCache.TextFormatterHost.Context = null; return textBreakpoints; } /// /// ReconstructLineVariant - Reconstruct line variants for a given line. /// /// /// IN: Text para client for TextParagraph /// /// /// IN: column-span area index /// /// /// IN: dcp at the beginning of the line /// /// /// IN: client's line break record /// /// /// IN: dcp of line to format /// /// /// IN: current direction /// /// /// IN: position at the beginning of the line /// /// /// IN: maximum width of line /// /// /// IN: position at the beginning of the track /// /// /// IN: width of track /// /// /// IN: left margin of the page /// /// /// IN: allow hyphenation of the line? /// /// /// IN: is clear on left side /// /// /// IN: is clear on right side /// /// /// IN: treat line as first line in paragraph /// /// /// IN: treat line as last line in paragraph /// /// /// IN: suppress empty space at the top of page /// /// /// OUT: pointer to line created by client /// /// /// OUT: dcp consumed by the line /// /// /// OUT: client's line break record /// /// /// OUT: was line force-broken? /// /// /// OUT: result of formatting /// /// /// OUT: ascent of the line /// /// /// OUT: descent of the line /// /// /// OUT: ur of the line's ink /// /// /// OUT: dur of of the line's ink /// /// /// OUT: number of chars after line break that were considered /// /// /// OUT: should line segments be reformatted? /// internal void ReconstructLineVariant( TextParaClient paraClient, int iArea, int dcp, IntPtr pbrlineIn, int dcpLineIn, uint fswdir, int urStartLine, int durLine, int urStartTrack, int durTrack, int urPageLeftMargin, bool fAllowHyphenation, bool fClearOnLeft, bool fClearOnRight, bool fTreatAsFirstInPara, bool fTreatAsLastInPara, bool fSuppressTopSpace, out IntPtr lineHandle, out int dcpLine, out IntPtr ppbrlineOut, out int fForcedBroken, out PTS.FSFLRES fsflres, out int dvrAscent, out int dvrDescent, out int urBBox, out int durBBox, out int dcpDepend, out int fReformatNeighborsAsLastLine) { // NOTE: there is no empty space added at the top of lines, so fSuppressTopSpace is never used. Invariant.Assert(iArea == 0); StructuralCache.CurrentFormatContext.OnFormatLine(); // Create and format line #pragma warning disable 6518 // Disable PRESharp warning 6518. Line is an UnmamangedHandle, that adds itself // to HandleMapper that holds a reference to it. PTS manages lifetime of this object, and // calls DestroyLine to get rid of it. DestroyLine will call Dispose() on the object // and remove it from HandleMapper. Line line = new Line(StructuralCache.TextFormatterHost, paraClient, ParagraphStartCharacterPosition); #pragma warning restore 6518 Line.FormattingContext ctx = new Line.FormattingContext(true, fClearOnLeft, fClearOnRight, _textRunCache); ctx.LineFormatLengthTarget = dcpLineIn; FormatLineCore(line, pbrlineIn, ctx, dcp, durLine, durTrack, fTreatAsFirstInPara, dcp); // Retrieve line properties lineHandle = line.Handle; dcpLine = line.SafeLength; TextLineBreak textLineBreak = line.GetTextLineBreak(); if(textLineBreak != null) { #pragma warning disable 56518 // Disable PRESharp warning 6518. Line is an UnmamangedHandle, that adds itself // to HandleMapper that holds a reference to it. PTS manages lifetime of this object, and // calls DestroyLineBreakRecord to get rid of it. DestroyLineBreakRecord will call Dispose() on the object // and remove it from HandleMapper. LineBreakRecord lineBreakRecord = new LineBreakRecord(PtsContext, textLineBreak); #pragma warning disable 56518 ppbrlineOut = lineBreakRecord.Handle; } else { ppbrlineOut = IntPtr.Zero; } fForcedBroken = PTS.FromBoolean(line.IsTruncated); fsflres = line.FormattingResult; dvrAscent = line.Baseline; dvrDescent = line.Height - line.Baseline; urBBox = urStartLine + line.Start; durBBox = line.Width; dcpDepend = line.DependantLength; fReformatNeighborsAsLastLine = PTS.False; // NOTE: When LL provides support for line height, following descent // calculation will go away. CalcLineAscentDescent(dcp, ref dvrAscent, ref dvrDescent); // Ensure we don't include the paragraph break synthetic run into our DcpDepend calculation. // We can only trim to total text length, as _cch may not be calculated at this time, and if it's uncalculated, then ParagraphEndCharacterPosition // is potentially incorrect. All of this needs to be reviewed WRT TextSchema. int dcpDependAbsolute = ParagraphStartCharacterPosition + dcp + line.ActualLength + dcpDepend; int textSize = StructuralCache.TextContainer.SymbolCount; if (dcpDependAbsolute > textSize) dcpDependAbsolute = textSize; StructuralCache.CurrentFormatContext.DependentMax = StructuralCache.TextContainer.CreatePointerAtOffset(dcpDependAbsolute, LogicalDirection.Backward); #if TEXTPANELLAYOUTDEBUG if (StructuralCache.CurrentFormatContext.IncrementalUpdate) { TextPanelDebug.Log("TextPara.FormatLine, Start=" + dcp + " Cch=" + dcpLine, TextPanelDebug.Category.ContentChange); } #endif } ////// Format line /// /// /// IN: Text para client for TextParagraph /// /// /// IN: column-span area index /// /// /// IN: dcp at the beginning of the line /// /// /// IN: client's line break record /// /// /// IN: current direction /// /// /// IN: position at the beginning of the line /// /// /// IN: maximum width of line /// /// /// IN: position at the beginning of the track /// /// /// IN: width of track /// /// /// IN: left margin of the page /// /// /// IN: allow hyphenation of the line? /// /// /// IN: is clear on left side /// /// /// IN: is clear on right side /// /// /// IN: treat line as first line in paragraph /// /// /// IN: treat line as last line in paragraph /// /// /// IN: suppress empty space at the top of page /// /// /// OUT: pointer to line created by client /// /// /// OUT: dcp consumed by the line /// /// /// OUT: client's line break record /// /// /// OUT: was line force-broken? /// /// /// OUT: result of formatting /// /// /// OUT: ascent of the line /// /// /// OUT: descent of the line /// /// /// OUT: ur of the line's ink /// /// /// OUT: dur of of the line's ink /// /// /// OUT: number of chars after line break that were considered /// /// /// OUT: should line segments be reformatted? /// internal void FormatLine( TextParaClient paraClient, int iArea, int dcp, IntPtr pbrlineIn, uint fswdir, int urStartLine, int durLine, int urStartTrack, int durTrack, int urPageLeftMargin, bool fAllowHyphenation, bool fClearOnLeft, bool fClearOnRight, bool fTreatAsFirstInPara, bool fTreatAsLastInPara, bool fSuppressTopSpace, out IntPtr lineHandle, out int dcpLine, out IntPtr ppbrlineOut, out int fForcedBroken, out PTS.FSFLRES fsflres, out int dvrAscent, out int dvrDescent, out int urBBox, out int durBBox, out int dcpDepend, out int fReformatNeighborsAsLastLine) { // NOTE: there is no empty space added at the top of lines, so fSuppressTopSpace is never used. Invariant.Assert(iArea == 0); StructuralCache.CurrentFormatContext.OnFormatLine(); // Create and format line #pragma warning disable 6518 // Disable PRESharp warning 6518. Line is an UnmamangedHandle, that adds itself // to HandleMapper that holds a reference to it. PTS manages lifetime of this object, and // calls DestroyLine to get rid of it. DestroyLine will call Dispose() on the object // and remove it from HandleMapper. Line line = new Line(StructuralCache.TextFormatterHost, paraClient, ParagraphStartCharacterPosition); #pragma warning restore 6518 Line.FormattingContext ctx = new Line.FormattingContext(true, fClearOnLeft, fClearOnRight, _textRunCache); FormatLineCore(line, pbrlineIn, ctx, dcp, durLine, durTrack, fTreatAsFirstInPara, dcp); // Retrieve line properties lineHandle = line.Handle; dcpLine = line.SafeLength; TextLineBreak textLineBreak = line.GetTextLineBreak(); if(textLineBreak != null) { #pragma warning disable 56518 // Disable PRESharp warning 6518. Line is an UnmamangedHandle, that adds itself // to HandleMapper that holds a reference to it. PTS manages lifetime of this object, and // calls DestroyLineBreakRecord to get rid of it. DestroyLineBreakRecord will call Dispose() on the object // and remove it from HandleMapper. LineBreakRecord lineBreakRecord = new LineBreakRecord(PtsContext, textLineBreak); #pragma warning restore 56518 ppbrlineOut = lineBreakRecord.Handle; } else { ppbrlineOut = IntPtr.Zero; } fForcedBroken = PTS.FromBoolean(line.IsTruncated); fsflres = line.FormattingResult; dvrAscent = line.Baseline; dvrDescent = line.Height - line.Baseline; urBBox = urStartLine + line.Start; durBBox = line.Width; dcpDepend = line.DependantLength; fReformatNeighborsAsLastLine = PTS.False; // NOTE: When LL provides support for line height, following descent // calculation will go away. CalcLineAscentDescent(dcp, ref dvrAscent, ref dvrDescent); // Ensure we don't include the paragraph break synthetic run into our DcpDepend calculation. // We can only trim to total text length, as _cch may not be calculated at this time, and if it's uncalculated, then ParagraphEndCharacterPosition // is potentially incorrect. All of this needs to be reviewed WRT TextSchema. int dcpDependAbsolute = ParagraphStartCharacterPosition + dcp + line.ActualLength + dcpDepend; int textSize = StructuralCache.TextContainer.SymbolCount; if (dcpDependAbsolute > textSize) dcpDependAbsolute = textSize; StructuralCache.CurrentFormatContext.DependentMax = StructuralCache.TextContainer.CreatePointerAtOffset(dcpDependAbsolute, LogicalDirection.Backward); #if TEXTPANELLAYOUTDEBUG if (StructuralCache.CurrentFormatContext.IncrementalUpdate) { TextPanelDebug.Log("TextPara.FormatLine, Start=" + dcp + " Cch=" + dcpLine, TextPanelDebug.Category.ContentChange); } #endif } ////// UpdGetChangeInText /// /// /// OUT: start of change /// /// /// OUT: number of chars in old range /// /// /// OUT: number of chars in new range /// internal void UpdGetChangeInText( out int dcpStart, out int ddcpOld, out int ddcpNew) { // Get dtr list for the text presenter DtrList dtrs = StructuralCache.DtrsFromRange(ParagraphStartCharacterPosition, LastFormatCch); if (dtrs != null) { // Union all dtrs. Note: there are no overlapping entries in the list of DTRs. dcpStart = dtrs[0].StartIndex - ParagraphStartCharacterPosition; ddcpNew = dtrs[0].PositionsAdded; ddcpOld = dtrs[0].PositionsRemoved; if (dtrs.Length > 1) { for (int i = 1; i < dtrs.Length; i++) { int delta = dtrs[i].StartIndex - dtrs[i-1].StartIndex; ddcpNew += delta + dtrs[i].PositionsAdded; ddcpOld += delta + dtrs[i].PositionsRemoved; } } // Get rid of embedded objects within dirty range and // update dcp of all object which are following dirty range // For finite page paragraph is reformatted from scrach, so all caches // are up to date and PTS needs only info about the change. if (!StructuralCache.CurrentFormatContext.FinitePage) { UpdateEmbeddedObjectsCache(ref _attachedObjects, dcpStart, ddcpOld, ddcpNew - ddcpOld); UpdateEmbeddedObjectsCache(ref _inlineObjects, dcpStart, ddcpOld, ddcpNew - ddcpOld); } Invariant.Assert(dcpStart >= 0 && Cch >= dcpStart && LastFormatCch >= dcpStart); // Max out at possible number of chars in old and new ranges, adding one for EOP dcp. ddcpOld = Math.Min(ddcpOld, (LastFormatCch - dcpStart) + 1); ddcpNew = Math.Min(ddcpNew, (Cch - dcpStart) + 1); } else { // PTS may call this callback for paragraph which has not been changed in // case of complex page layout (with figures). Return 0 to notify that // there is no change. dcpStart = ddcpOld = ddcpNew = 0; } #if TEXTPANELLAYOUTDEBUG if (StructuralCache.CurrentFormatContext.IncrementalUpdate) { TextPanelDebug.Log("TextPara.UpdGetChangeInText, Start=" + dcpStart + " Old=" + ddcpOld + " New=" + ddcpNew, TextPanelDebug.Category.ContentChange); } #endif } ////// Get Dvr Advance /// /// /// IN: dcp at the beginning of the line /// /// /// IN: current direction /// /// /// OUT: advance amount in tight wrap /// internal void GetDvrAdvance( int dcp, uint fswdir, out int dvr) { EnsureLineProperties(); // When tight wrap is enabled, PTS may not fit line that starts at 'dcp' // at the current vertical offset. // In this situation PTS asks the client by how much it needs to // advance in vertical direction to try again. // For optimal results 1px should be enough, but this has performance hit. // Word decide to use height a character in the paragraph font. The similar // logic is used here. Advance by value of the default line height. dvr = TextDpi.ToTextDpi(_lineProperties.CalcLineAdvanceForTextParagraph(this, dcp, _lineProperties.DefaultTextRunProperties.FontRenderingEmSize)); } ////// Walks the text tree from a given dcp and skips over figure and floater elements, returning the dcp after the last one. /// internal int GetLastDcpAttachedObjectBeforeLine(int dcpFirst) { ITextPointer textPointer = TextContainerHelper.GetTextPointerFromCP(StructuralCache.TextContainer, ParagraphStartCharacterPosition + dcpFirst, LogicalDirection.Forward); ITextPointer textPointerContentStart = TextContainerHelper.GetContentStart(StructuralCache.TextContainer, Element); while(textPointer.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart) { TextElement element = ((TextPointer)textPointer).GetAdjacentElementFromOuterPosition(LogicalDirection.Forward); if(!(element is Figure) && !(element is Floater)) { break; } textPointer.MoveByOffset(element.SymbolCount); } return textPointerContentStart.GetOffsetToPosition(textPointer); } ////// Returns the text elements for a given dcp range. /// private ListGetAttachedObjectElements(int dcpFirst, int dcpLast) { List attachedElements = new List (); ITextPointer textPointerContentStart = TextContainerHelper.GetContentStart(StructuralCache.TextContainer, Element); ITextPointer textPointer = TextContainerHelper.GetTextPointerFromCP(StructuralCache.TextContainer, ParagraphStartCharacterPosition + dcpFirst, LogicalDirection.Forward); if(dcpLast > this.Cch) { dcpLast = this.Cch; // Remove end of paragraph run cp. } while(textPointerContentStart.GetOffsetToPosition(textPointer) < dcpLast) { if(textPointer.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart) { TextElement element = ((TextPointer)textPointer).GetAdjacentElementFromOuterPosition(LogicalDirection.Forward); if(element is Figure || element is Floater) { attachedElements.Add(element); textPointer.MoveByOffset(element.SymbolCount); } else { textPointer.MoveToNextContextPosition(LogicalDirection.Forward); } } else { textPointer.MoveToNextContextPosition(LogicalDirection.Forward); } } return attachedElements; } /// /// Returns the count of attached objects over a given dcp range /// internal int GetAttachedObjectCount(int dcpFirst, int dcpLast) { ListtextElements = GetAttachedObjectElements(dcpFirst, dcpLast); if(textElements.Count == 0) { SubmitAttachedObjects(dcpFirst, dcpLast, null); } return textElements.Count; } /// /// Returns the attached object list for a dcp range by wrapping floating text elements in Figure/FloaterObject tags. /// internal ListGetAttachedObjects(int dcpFirst, int dcpLast) { ITextPointer textPointerContentStart = TextContainerHelper.GetContentStart(StructuralCache.TextContainer, Element); List attachedObjects = new List (); List textElements = GetAttachedObjectElements(dcpFirst, dcpLast); for(int index = 0; index < textElements.Count; index++) { TextElement textElement = textElements[index]; if(textElement is Figure && StructuralCache.CurrentFormatContext.FinitePage) { #pragma warning disable 6518 // Disable PRESharp warning 6518. FigureParagraph is passed to attached objects // which will do following: // a) store this object in TextParagraph._floaters collection. Later when // TextParagraph is disposed, all objects in _floaters collection will be // also disposed. // b) call Dispose() on this object, if it already exists in TextParagraph._floaters // collection. // c) call Dipose() on this object during layout pass following removal of floater. FigureParagraph figurePara = new FigureParagraph(textElement, StructuralCache); #pragma warning restore 6518 if (StructuralCache.CurrentFormatContext.IncrementalUpdate) { figurePara.SetUpdateInfo(PTS.FSKCHANGE.fskchNew, false); } FigureObject figureObject = new FigureObject(textPointerContentStart.GetOffsetToPosition(textElement.ElementStart), figurePara); attachedObjects.Add(figureObject); } else { #pragma warning disable 6518 // Disable PRESharp warning 6518. FigureParagraph is passed to attached objects // which will do following: // a) store this object in TextParagraph._floaters collection. Later when // TextParagraph is disposed, all objects in _floaters collection will be // also disposed. // b) call Dispose() on this object, if it already exists in TextParagraph._floaters // collection. // c) call Dipose() on this object during layout pass following removal of floater. FloaterParagraph floaterPara = new FloaterParagraph(textElement, StructuralCache); #pragma warning restore 6518 if (StructuralCache.CurrentFormatContext.IncrementalUpdate) { floaterPara.SetUpdateInfo(PTS.FSKCHANGE.fskchNew, false); } FloaterObject floaterObject = new FloaterObject(textPointerContentStart.GetOffsetToPosition(textElement.ElementStart), floaterPara); attachedObjects.Add(floaterObject); } } // If it were 0, should have been submitted when count was queried. if(attachedObjects.Count != 0) { SubmitAttachedObjects(dcpFirst, dcpLast, attachedObjects); } return attachedObjects; } #endregion PTS callbacks // ----------------------------------------------------------------- // // Internal Methods // // ------------------------------------------------------------------ #region Internal Methods /// /// Submit inline objects for specified range to the cache. All existing /// inline objects in this particular range will be removed. /// /// /// Dcp of the beginning of the range to update. /// /// /// Dcp of the end of the range to update. /// /// /// Array of inline objects. /// internal void SubmitInlineObjects(int dcpStart, int dcpLim, ListinlineObjects) { SubmitEmbeddedObjects(ref _inlineObjects, dcpStart, dcpLim, inlineObjects); } /// /// Submit floaters for specified range to the cache. All existing /// floaters in this particular range will be removed. /// /// /// Dcp of the beginning of the range to update. /// /// /// Dcp of the end of the range to update. /// /// /// Array of attached objects. /// internal void SubmitAttachedObjects(int dcpStart, int dcpLim, ListattachedObjects) { SubmitEmbeddedObjects(ref _attachedObjects, dcpStart, dcpLim, attachedObjects); } /// /// Returns a list of inline object from specifed range /// /// /// Dcp of the beginning of the range. /// /// /// Dcp of the end of the range. /// internal ListInlineObjectsFromRange(int dcpStart, int dcpLast) { List objects = null; if (_inlineObjects != null) { objects = new List (_inlineObjects.Count); for (int i = 0; i < _inlineObjects.Count; i++) { InlineObject obj = _inlineObjects[i]; if (obj.Dcp >= dcpStart && obj.Dcp < dcpLast) { objects.Add(obj); } else if (obj.Dcp >= dcpLast) { // No reason to continue break; } } } if(objects == null || objects.Count == 0) { return null; } return objects; } /// /// Calculate and return line advance distance. This functionality will go away /// when TextFormatter will be able to handle line height/stacking. /// /// /// dcp of the line /// /// /// Calculated dvr ascent /// /// /// Calculated dvr descent /// internal void CalcLineAscentDescent(int dcp, ref int dvrAscent, ref int dvrDescent) { EnsureLineProperties(); int thisLineAdvance = dvrAscent + dvrDescent; int calculatedLineAdvance = TextDpi.ToTextDpi(_lineProperties.CalcLineAdvanceForTextParagraph(this, dcp, TextDpi.FromTextDpi(thisLineAdvance))); if(thisLineAdvance != calculatedLineAdvance) { double scale = (1.0 * calculatedLineAdvance) / (1.0 * thisLineAdvance); dvrAscent = (int) (dvrAscent * scale); dvrDescent = (int) (dvrDescent * scale); } } ////// Set update info. Those flags are used later by PTS to decide /// if paragraph needs to be updated and when to stop asking for /// update information. /// /// /// Type of change within the paragraph. /// /// /// Synchronization point is reached? /// internal override void SetUpdateInfo(PTS.FSKCHANGE fskch, bool stopAsking) { base.SetUpdateInfo(fskch, stopAsking); if (fskch == PTS.FSKCHANGE.fskchInside) { // Update _cch so we always have correct value during update process _textRunCache = new TextRunCache(); _lineProperties = null; } } ////// Clear previously accumulated update info. /// internal override void ClearUpdateInfo() { base.ClearUpdateInfo(); // Clear update info of all floaters and figures if (_attachedObjects != null) { for (int index=0; index < _attachedObjects.Count; index++) { _attachedObjects[index].Para.ClearUpdateInfo(); } } } ////// Invalidate content's structural cache. Returns true if entire paragraph is invalid. /// /// /// Position to start invalidation from. /// internal override bool InvalidateStructure(int startPosition) { // Change must be inside text paragraph. Invariant.Assert(ParagraphEndCharacterPosition >= startPosition); bool invalid = false; // Thre are 2 situations: // 1) the beginning is startPosition, in this case paragraph content is valid only if // the first element in the para is figure/floater and the element owner has not // been changed. // 2) startPosition is in the middle of paragraph, in this case paragraph content is valid. if (ParagraphStartCharacterPosition == startPosition) { // 1) the beginning is startPosition, in this case paragraph content is valid only if // the first element in the para is figure/floater and the element owner has not // been changed. invalid = true; // Get element owner of the first figure or floater, whichever comes first AnchoredBlock objectElement = null; if(_attachedObjects != null && _attachedObjects.Count > 0) { objectElement = (AnchoredBlock)(_attachedObjects[0].Element); } // If figure/floater starts at the beginning of paragraph and element owner did // not change, treat the paragraph as valid. if (objectElement != null) { if (startPosition == objectElement.ElementStartOffset) { StaticTextPointer position = TextContainerHelper.GetStaticTextPointerFromCP(StructuralCache.TextContainer, startPosition); if (position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart) { invalid = (objectElement != position.GetAdjacentElement(LogicalDirection.Forward)); } // else invalid } // else invalid } // else invalid } // else // 2) startPosition is in the middle of paragraph, in this case paragraph content is valid. // Invalidate text format caches. InvalidateTextFormatCache(); // Invalidate structure of floaters and figures if (_attachedObjects != null) { for (int index=0; index < _attachedObjects.Count; index++) { BaseParagraph attachedObjectPara = _attachedObjects[index].Para; if(attachedObjectPara.ParagraphEndCharacterPosition >= startPosition) { attachedObjectPara.InvalidateStructure(startPosition); } } } return invalid; } ////// Invalidate accumulated format caches. /// internal override void InvalidateFormatCache() { InvalidateTextFormatCache(); // Invalidate structure of floaters and figures if (_attachedObjects != null) { for (int index=0; index < _attachedObjects.Count; index++) { _attachedObjects[index].Para.InvalidateFormatCache(); } } } ////// Invalidate format cache. /// internal void InvalidateTextFormatCache() { _textRunCache = new TextRunCache(); _lineProperties = null; } ////// Format text line. /// /// /// Text line to format /// /// /// Break record of line /// /// /// TextFormatter context /// /// /// Dcp of line start /// /// /// Width of line /// /// /// First line in paragraph? /// /// /// Character position where the line starts. /// internal void FormatLineCore(Line line, IntPtr pbrLineIn, Line.FormattingContext ctx, int dcp, int width, bool firstLine, int dcpLine) { FormatLineCore(line, pbrLineIn, ctx, dcp, width, -1, firstLine, dcpLine); } ////// Format text line. Includes track width, needed during Measure to /// size inline elements. /// /// /// Text line to be formatted /// /// /// Break record of line /// /// /// TextFormatter context /// /// /// dcp of line start /// /// /// Line width /// /// /// Requested width of track /// /// /// First line in paragraph? /// /// /// Character position where the line starts. /// internal void FormatLineCore(Line line, IntPtr pbrLineIn, Line.FormattingContext ctx, int dcp, int width, int trackWidth, bool firstLine, int dcpLine) { TextDpi.EnsureValidLineWidth(ref width); _currentLine = line; TextLineBreak textLineBreak = null; if(pbrLineIn != IntPtr.Zero) { LineBreakRecord lineBreakRecord = PtsContext.HandleToObject(pbrLineIn) as LineBreakRecord; PTS.ValidateHandle(lineBreakRecord); textLineBreak = lineBreakRecord.TextLineBreak; } try { line.Format(ctx, dcp, width, trackWidth, GetLineProperties(firstLine, dcpLine), textLineBreak); } finally { _currentLine = null; } } ////// Measure child UIElement and return its size. /// /// /// InlineObjectRun with child UIElement to measure /// internal Size MeasureChild(InlineObjectRun inlineObject) { if(_currentLine == null) { return ((OptimalTextSource)StructuralCache.TextFormatterHost.Context).MeasureChild(inlineObject); } else { return _currentLine.MeasureChild(inlineObject); } } ////// Returns true if there's anything complicated about this para - figures, floaters /// or inline objects. Returns false if para contains simple text. /// internal bool HasFiguresFloatersOrInlineObjects() { if(HasFiguresOrFloaters() || (_inlineObjects != null && _inlineObjects.Count > 0)) { return true; } return false; } ////// Returns true if figures or floaters exist for this para /// internal bool HasFiguresOrFloaters() { return _attachedObjects != null && _attachedObjects.Count > 0; } ////// Updates text content range with attached object list. Subtracts out all of the known figures and floaters /// Ranges, then adds back in the ranges for the para clients. /// internal void UpdateTextContentRangeFromAttachedObjects(TextContentRange textContentRange, int dcpFirst, int dcpLast, PTS.FSATTACHEDOBJECTDESCRIPTION [] arrayAttachedObjectDesc) { int cpCur = dcpFirst; for(int index = 0; _attachedObjects != null && index < _attachedObjects.Count; index++) { AttachedObject attachedObject = _attachedObjects[index]; int startContentPosition = attachedObject.Para.ParagraphStartCharacterPosition; int paraCch = attachedObject.Para.Cch; if(startContentPosition >= cpCur && startContentPosition < dcpLast) { textContentRange.Merge(new TextContentRange(cpCur, startContentPosition, StructuralCache.TextContainer)); cpCur = startContentPosition + paraCch; // Skip past para content range } if(dcpLast < cpCur) { break; } } if(cpCur < dcpLast) { textContentRange.Merge(new TextContentRange(cpCur, dcpLast, StructuralCache.TextContainer)); } for(int index = 0; arrayAttachedObjectDesc != null && index < arrayAttachedObjectDesc.Length; index++) { PTS.FSATTACHEDOBJECTDESCRIPTION attachedObject = arrayAttachedObjectDesc[index]; BaseParaClient paraClient; paraClient = PtsContext.HandleToObject(arrayAttachedObjectDesc[index].pfsparaclient) as BaseParaClient; PTS.ValidateHandle(paraClient); textContentRange.Merge(paraClient.GetTextContentRange()); } } ////// Handler for DesiredSizeChanged raised by UIElement island. /// internal void OnUIElementDesiredSizeChanged(object sender, DesiredSizeChangedEventArgs e) { StructuralCache.FormattingOwner.OnChildDesiredSizeChanged(e.Child); } #endregion Internal Methods // ----------------------------------------------------------------- // // Internal Properties // // ----------------------------------------------------------------- #region Internal Properties ////// Run cache used by text formatter. /// internal TextRunCache TextRunCache { get { return _textRunCache; } } ////// Text paragraph properties. /// internal LineProperties Properties { get { EnsureLineProperties(); return _lineProperties; } } ////// Optimal paragraph flag /// internal bool IsOptimalParagraph { get { return StructuralCache.IsOptimalParagraphEnabled && GetLineProperties(false, 0).TextWrapping != TextWrapping.NoWrap; } } #endregion Internal Properties // ----------------------------------------------------------------- // // Private Methods // // ------------------------------------------------------------------ #region Private Methods ////// Refetch and cache line properties, if needed. /// private void EnsureLineProperties() { if (_lineProperties == null) { // For default text properties always set background to null. // REASON: If element associated with the text run is block element, ignore background // brush, because it is handled by paragraph itself. TextProperties defaultTextProperties = new TextProperties(Element, StaticTextPointer.Null, false /* inline objects */, false /* get background */); _lineProperties = new LineProperties(Element, StructuralCache.FormattingOwner, defaultTextProperties, null); // No marker properties bool isHyphenationEnabled = (bool) Element.GetValue(Block.IsHyphenationEnabledProperty); if(isHyphenationEnabled) { _lineProperties.Hyphenator = StructuralCache.Hyphenator; } } } ////// Submit embedded objects for specified range. All existing objects /// in this particular range will be removed. /// /// /// Array of cached embedded objects. /// /// /// Dcp of the beginning of the range to update. /// /// /// Dcp of the end of the range to update. /// /// /// Array of new embedded objects. /// private void SubmitEmbeddedObjects(ref List objectsCached, int dcpStart, int dcpLim, List objectsNew) where T : EmbeddedObject { ErrorHandler.Assert(objectsNew == null || (objectsNew[0].Dcp >= dcpStart && objectsNew[objectsNew.Count-1].Dcp <= dcpLim), ErrorHandler.SubmitInvalidList); // Make sure that cached objects array exists if (objectsCached == null) { if (objectsNew == null) { // Nothing to do return; } objectsCached = new List (objectsNew.Count); } // Find affected range of cached objects int end = objectsCached.Count; while (end > 0 && objectsCached[end-1].Dcp >= dcpLim) --end; int start = end; while (start > 0 && objectsCached[start-1].Dcp >= dcpStart) --start; // There are 3 situations, which may happen when submitting embedded objects: // (1) Only remove obsolete objects (no objects to add) // (2) Only add new objects (no objects are obsolete) // (3) Merge new objects into existing list (may add and remove objects) if (objectsNew == null) { // (1) Only remove obsolete objects (no objects to add) for (int index = start; index < end; index++) { objectsCached[index].Dispose(); } objectsCached.RemoveRange(start, end - start); } else if (end == start) { // (2) Only add new objects (no objects are obsolete) objectsCached.InsertRange(start, objectsNew); } else { // (3) Merge new objects into existing list (may add and remove objects) int idxNew = 0; while (start < end) { // Iterate through list of existing objects, which are affected by the // change range. There are 2 possibilities: // (1) There is matching object in the list of new objects. // (2) The object is obsolete, it should be removed. T oldEmbeddedObject = objectsCached[start]; int idx = idxNew; while (idx < objectsNew.Count) { T newEmbeddedObject = objectsNew[idx]; if (oldEmbeddedObject.Element == newEmbeddedObject.Element) { // (1) There is matching object in the list of new objects. // In this case: // * insert all preceding objects from new object list // * update object information if (idx > idxNew) { objectsCached.InsertRange(start, objectsNew.GetRange(idxNew, idx - idxNew)); end += idx - idxNew; start += idx - idxNew; } oldEmbeddedObject.Update(newEmbeddedObject); objectsNew[idx] = oldEmbeddedObject; idxNew = idx + 1; ++start; // Dispose unused EmbeddedObject newEmbeddedObject.Dispose(); break; } ++idx; } if (idx >= objectsNew.Count) { // (2) The object is obsolete - remove it. objectsCached[start].Dispose(); objectsCached.RemoveAt(start); --end; } } if (idxNew < objectsNew.Count) { // If we have any objects left in the new object list, insert them. objectsCached.InsertRange(end, objectsNew.GetRange(idxNew, objectsNew.Count - idxNew)); } } } //------------------------------------------------------------------- // //-------------------------------------------------------------------- /// /// Get rid of embedded objects within dirty range and update dcp of /// all object which are following dirty range. /// /// /// array of cached embedded objects /// /// /// dcp of the beginning of the range to update /// /// /// number of characters deleted /// /// /// difference in characters count /// private void UpdateEmbeddedObjectsCache( ref List objectsCached, int dcpStart, int cchDeleted, int cchDiff) where T : EmbeddedObject { if (objectsCached != null) { // Find the first and last affected object int first = 0; while (first < objectsCached.Count && objectsCached[first].Dcp < dcpStart) { ++first; } int last = first; while (last < objectsCached.Count && objectsCached[last].Dcp < dcpStart + cchDeleted) { ++last; } // Remove obsolete embedded objects if (first != last) { for (int index = first; index < last; index++) { objectsCached[index].Dispose(); } objectsCached.RemoveRange(first, last - first); } // Update dcp of all objects following dirty range while (last < objectsCached.Count) { objectsCached[last].Dcp += cchDiff; ++last; } if (objectsCached.Count == 0) { objectsCached = null; } } } /// /// Get line properties /// /// /// First line in paragraph? /// /// /// Character position where line starts. /// private TextParagraphProperties GetLineProperties(bool firstLine, int dcpLine) { EnsureLineProperties(); if (firstLine && _lineProperties.HasFirstLineProperties) { // There are 2 situations, where PTS claims that it is the first line. // a) first complex composite line - only the first element // should be treated as the first line. // b) paragraph nesting - if there are 2 or more TextParagraphs representing // the same Element, only the first one has the first line properties. // In those cases need to ignore first line properties. if (dcpLine != 0) { // a) first complex composite line - only the first element // should be treated as the first line. firstLine = false; } else { // b) paragraph nesting - if there are 2 or more TextParagraphs representing // the same Element, only the first one has the first line properties. int cpElement = TextContainerHelper.GetCPFromElement(StructuralCache.TextContainer, Element, ElementEdge.AfterStart); if (cpElement < this.ParagraphStartCharacterPosition) { firstLine = false; } } // If we still have the first line, use first line properties. if (firstLine) { return _lineProperties.FirstLineProps; } } return _lineProperties; } #endregion Private Methods // ------------------------------------------------------------------ // // Private Fields // // ----------------------------------------------------------------- #region Private Fields ////// List of attached objects. /// private List_attachedObjects; #if DEBUG internal List AttachedObjectDbg { get { return _attachedObjects; } } #endif /// /// List of inline objects. /// private List_inlineObjects; /// /// Line properties /// private LineProperties _lineProperties; ////// Run cache used by text formatter. /// private TextRunCache _textRunCache = new TextRunCache(); ////// Currently formatted line. Valid only during line formatting. /// private Line _currentLine; #endregion Private Fields } } #pragma warning enable 1634, 1691 // 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
- BasicSecurityProfileVersion.cs
- InvocationExpression.cs
- DataGridViewCheckBoxColumn.cs
- RuleSetDialog.cs
- XamlClipboardData.cs
- SpellerError.cs
- WebServiceHandlerFactory.cs
- DebuggerAttributes.cs
- ConfigurationValidatorBase.cs
- EpmTargetTree.cs
- Properties.cs
- DetailsViewRowCollection.cs
- ResourceReferenceKeyNotFoundException.cs
- ResourcePermissionBase.cs
- BamlLocalizer.cs
- Keyboard.cs
- PointAnimationBase.cs
- ContentValidator.cs
- URIFormatException.cs
- MulticastIPAddressInformationCollection.cs
- ApplyImportsAction.cs
- Queue.cs
- TextBoxBase.cs
- BehaviorService.cs
- CommandEventArgs.cs
- ConstructorNeedsTagAttribute.cs
- Errors.cs
- ThreadAttributes.cs
- CompositeCollectionView.cs
- PersonalizationAdministration.cs
- RSAPKCS1KeyExchangeDeformatter.cs
- HttpCacheParams.cs
- HtmlContainerControl.cs
- MruCache.cs
- MethodBuilderInstantiation.cs
- Parsers.cs
- RealProxy.cs
- Schema.cs
- SequenceNumber.cs
- Path.cs
- Win32SafeHandles.cs
- EntityConnectionStringBuilder.cs
- TextBreakpoint.cs
- MailWebEventProvider.cs
- ErrorFormatterPage.cs
- RangeValidator.cs
- AsyncResult.cs
- CRYPTPROTECT_PROMPTSTRUCT.cs
- ConfigLoader.cs
- DataGridCellEditEndingEventArgs.cs
- SiteOfOriginContainer.cs
- MenuItemCollection.cs
- LabelAutomationPeer.cs
- DataGridViewControlCollection.cs
- QilXmlWriter.cs
- ExtendedProperty.cs
- SmiMetaDataProperty.cs
- PageCache.cs
- Point3DCollection.cs
- StorageMappingItemCollection.cs
- TraceHandlerErrorFormatter.cs
- FormsAuthenticationEventArgs.cs
- ScaleTransform3D.cs
- SystemUnicastIPAddressInformation.cs
- XPathDescendantIterator.cs
- DbConnectionPoolGroup.cs
- MtomMessageEncodingElement.cs
- WebDisplayNameAttribute.cs
- DataPager.cs
- HyperLinkField.cs
- SevenBitStream.cs
- RequestCacheEntry.cs
- TypeDefinition.cs
- MethodBuilder.cs
- ContainerParagraph.cs
- CompilationUtil.cs
- TextRange.cs
- MappingException.cs
- EventLogTraceListener.cs
- SqlNotificationEventArgs.cs
- InsufficientMemoryException.cs
- Zone.cs
- BackStopAuthenticationModule.cs
- ErrorTableItemStyle.cs
- TableRowGroup.cs
- FontFamily.cs
- ResourcesChangeInfo.cs
- DataServiceQueryException.cs
- DataGridTable.cs
- TailPinnedEventArgs.cs
- PlacementWorkspace.cs
- UInt64Converter.cs
- OptimisticConcurrencyException.cs
- CompilerResults.cs
- CqlLexer.cs
- LinkLabel.cs
- GB18030Encoding.cs
- UInt64.cs
- DataRowIndexBuffer.cs
- DisplayInformation.cs