private PTS.FSRECT GetRect() { PTS.FSRECT rect; // There are 3 cases when calculating page rect: // (1) PTS page is not created - return empty rect. // (2) PTS page - use page PTS APIs to get page rectangle. // (3) PTS subpage - use subpage PTS APIs to get page rectangle. if (IsEmpty) { // (1) PTS page is not created - empty rect. rect = new PTS.FSRECT(); } else { // (2) PTS page - use page PTS APIs to get page rectangle. PTS.FSPAGEDETAILS pageDetails; PTS.Validate(PTS.FsQueryPageDetails(PtsContext.Context, _ptsPage.Value, out pageDetails)); // There are 2 different types of PTS page and calculated rectangle depends on it: // (a) simple page (contains only one track) - get rectanglefrom the track. // (b) complex page (contains header, page body, footnotes and footer) - get bounding // box of each segment and union them. if (PTS.ToBoolean(pageDetails.fSimple)) { // (a) simple page (contains only one track) - get rectanglefrom the track. rect = pageDetails.u.simple.trackdescr.fsrc; } else { // (b) complex page (contains header, page body, footnotes and footer) - get rectangle // of each segment and union them. //ErrorHandler.Assert(!PTS.ToBoolean(pageDetails.u.complex.fTopBottomHeaderFooter) || this.PresenterCache.CurrentPresenter.IsBottomless, ErrorHandler.NotSupportedHeadersFooters); //ErrorHandler.Assert(!PTS.ToBoolean(pageDetails.u.complex.fJustified) || this.PresenterCache.CurrentPresenter.IsBottomless, ErrorHandler.NotSupportedVerticalJustify); ErrorHandler.Assert(pageDetails.u.complex.cFootnoteColumns == 0, ErrorHandler.NotSupportedFootnotes); // Since header/footer and footnotes are not supported yet, use page body rectangle rect = pageDetails.u.complex.fsrcPageBody; } } return rect; }
internal override void UpdateViewport(ref PTS.FSRECT viewport) { // Query subpage details PTS.FSSUBPAGEDETAILS subpageDetails; PTS.Validate(PTS.FsQuerySubpageDetails(PtsContext.Context, _paraHandle.Value, out subpageDetails)); PTS.FSRECT viewportSubpage = new PTS.FSRECT(); viewportSubpage.u = viewport.u - ContentRect.u; viewportSubpage.v = viewport.v - ContentRect.v; viewportSubpage.du = viewport.du; viewportSubpage.dv = viewport.dv; // Subpage content may be simple or complex - // depending of set of features used in the content of the subpage. // (1) simple subpage (contains only one track) // (2) complex subpage (contains columns) if (PTS.ToBoolean(subpageDetails.fSimple)) { PtsHelper.UpdateViewportTrack(PtsContext, ref subpageDetails.u.simple.trackdescr, ref viewportSubpage); } else { // (2) complex page (contains columns) // cBasicColumns == 0, means that subpage content is empty bool emptySubpage = (subpageDetails.u.complex.cBasicColumns == 0); if (!emptySubpage) { // Retrieve description for each column. PTS.FSTRACKDESCRIPTION[] arrayColumnDesc; PtsHelper.TrackListFromSubpage(PtsContext, _paraHandle.Value, ref subpageDetails, out arrayColumnDesc); emptySubpage = (arrayColumnDesc.Length == 0); if (!emptySubpage) { for (int index = 0; index < arrayColumnDesc.Length; index++) { PtsHelper.UpdateViewportTrack(PtsContext, ref arrayColumnDesc[index], ref viewportSubpage); } } } } }
internal override void ValidateVisual(PTS.FSKUPDATE fskupdInherited) { // Floater is always reported as NEW. Override PTS inherited value. fskupdInherited = PTS.FSKUPDATE.fskupdNew; // Query subpage details PTS.FSSUBPAGEDETAILS subpageDetails; PTS.Validate(PTS.FsQuerySubpageDetails(PtsContext.Context, _paraHandle.Value, out subpageDetails)); // Obtain all mbd info MbpInfo mbp = MbpInfo.FromElement(Paragraph.Element); if(ThisFlowDirection != PageFlowDirection) { mbp.MirrorBP(); } Brush backgroundBrush = (Brush)Paragraph.Element.GetValue(TextElement.BackgroundProperty); Visual.DrawBackgroundAndBorder(backgroundBrush, mbp.BorderBrush, mbp.Border, _rect.FromTextDpi(), IsFirstChunk, IsLastChunk); ContainerVisual pageContentVisual; ContainerVisual floatingElementsVisual; if(_visual.Children.Count != 2) { _visual.Children.Clear(); _visual.Children.Add(new ContainerVisual()); _visual.Children.Add(new ContainerVisual()); } pageContentVisual = (ContainerVisual)_visual.Children[0]; floatingElementsVisual = (ContainerVisual)_visual.Children[1]; // Subpage content may be simple or complex - // depending of set of features used in the content of the subpage. // (1) simple subpage (contains only one track) // (2) complex subpage (contains columns) if (PTS.ToBoolean(subpageDetails.fSimple)) { // (1) simple subpage (contains only one track) PTS.FSKUPDATE fskupd = subpageDetails.u.simple.trackdescr.fsupdinf.fskupd; if (fskupd == PTS.FSKUPDATE.fskupdInherited) { fskupd = fskupdInherited; } VisualCollection visualChildren = pageContentVisual.Children; if (fskupd == PTS.FSKUPDATE.fskupdNew) { visualChildren.Clear(); visualChildren.Add(new ContainerVisual()); } // For complex subpage SectionVisual is added. So, when morphing // complex subpage to simple one, remove SectionVisual. else if (visualChildren.Count == 1 && !(visualChildren[0] is ContainerVisual)) { visualChildren.Clear(); visualChildren.Add(new ContainerVisual()); } Debug.Assert(visualChildren.Count == 1 && visualChildren[0] is ContainerVisual); ContainerVisual trackVisual = (ContainerVisual)visualChildren[0]; PtsHelper.UpdateTrackVisuals(PtsContext, trackVisual.Children, fskupdInherited, ref subpageDetails.u.simple.trackdescr); } else { // (2) complex page (contains columns) // cBasicColumns == 0, means that subpage content is empty bool emptySubpage = (subpageDetails.u.complex.cBasicColumns == 0); if (!emptySubpage) { // Retrieve description for each column. PTS.FSTRACKDESCRIPTION[] arrayColumnDesc; PtsHelper.TrackListFromSubpage(PtsContext, _paraHandle.Value, ref subpageDetails, out arrayColumnDesc); emptySubpage = (arrayColumnDesc.Length == 0); if (!emptySubpage) { PTS.FSKUPDATE fskupd = fskupdInherited; ErrorHandler.Assert(fskupd != PTS.FSKUPDATE.fskupdShifted, ErrorHandler.UpdateShiftedNotValid); Debug.Assert(fskupd != PTS.FSKUPDATE.fskupdNoChange); // For complex subpage SectionVisual is added. So, when morphing // simple subpage to complex one, remove ParagraphVisual. VisualCollection visualChildren = pageContentVisual.Children; if (visualChildren.Count == 0) { visualChildren.Add(new SectionVisual()); } else if (!(visualChildren[0] is SectionVisual)) { visualChildren.Clear(); visualChildren.Add(new SectionVisual()); } Debug.Assert(visualChildren.Count == 1 && visualChildren[0] is SectionVisual); SectionVisual sectionVisual = (SectionVisual)visualChildren[0]; // Draw column rules. ColumnPropertiesGroup columnProperties = new ColumnPropertiesGroup(Paragraph.Element); sectionVisual.DrawColumnRules(ref arrayColumnDesc, TextDpi.FromTextDpi(subpageDetails.u.complex.fsrc.v), TextDpi.FromTextDpi(subpageDetails.u.complex.fsrc.dv), columnProperties); visualChildren = sectionVisual.Children; if (fskupd == PTS.FSKUPDATE.fskupdNew) { visualChildren.Clear(); for (int index = 0; index < arrayColumnDesc.Length; index++) { visualChildren.Add(new ContainerVisual()); } } ErrorHandler.Assert(visualChildren.Count == arrayColumnDesc.Length, ErrorHandler.ColumnVisualCountMismatch); for (int index = 0; index < arrayColumnDesc.Length; index++) { ContainerVisual trackVisual = (ContainerVisual)visualChildren[index]; PtsHelper.UpdateTrackVisuals(PtsContext, trackVisual.Children, fskupdInherited, ref arrayColumnDesc[index]); } } } if (emptySubpage) { // There is no content, remove all existing visuals. _visual.Children.Clear(); } } pageContentVisual.Offset = new PTS.FSVECTOR(ContentRect.u, ContentRect.v).FromTextDpi(); floatingElementsVisual.Offset = new PTS.FSVECTOR(ContentRect.u, ContentRect.v).FromTextDpi(); PTS.FSRECT clipRect = new PTS.FSRECT(_paddingRect.u - _contentRect.u, _paddingRect.v - _contentRect.v, _paddingRect.du, _paddingRect.dv); PtsHelper.ClipChildrenToRect(_visual, clipRect.FromTextDpi()); PtsHelper.UpdateFloatingElementVisuals(floatingElementsVisual, _pageContextOfThisPage.FloatingElementList); }
internal void GetFigureProperties( FigureParaClient paraClient, // IN: int fInTextLine, // IN: it is attached to text line uint fswdir, // IN: current direction int fBottomUndefined, // IN: bottom of page is not defined out int dur, // OUT: width of figure out int dvr, // OUT: height of figure out PTS.FSFIGUREPROPS fsfigprops, // OUT: figure attributes out int cPolygons, // OUT: number of polygons out int cVertices, // OUT: total number of vertices in all polygons out int durDistTextLeft, // OUT: distance to text from MinU side out int durDistTextRight, // OUT: distance to text from MaxU side out int dvrDistTextTop, // OUT: distance to text from MinV side out int dvrDistTextBottom) // OUT: distance to text from MaxV side { Invariant.Assert(StructuralCache.CurrentFormatContext.FinitePage); uint fswdirPara = PTS.FlowDirectionToFswdir(((FlowDirection)Element.GetValue(FrameworkElement.FlowDirectionProperty))); IntPtr pfsFigureContent; PTS.FSBBOX fsbbox; int cColumns; int dvrTopSpace; PTS.FSCOLUMNINFO[] columnInfoCollection; IntPtr pmcsclientOut; MbpInfo mbp; Figure element = (Figure)Element; // Initialize the subpage size. PTS subpage margin is always set to 0 for Figures. // If width on figure is specified, use the specified value. // Border and padding of the figure is extracted from available subpage width. // We use StructuralCache.CurrentFormatContext's page dimensions as limiting values for figure MBP mbp = MbpInfo.FromElement(Element); // We do not mirror margin as it's used to dist text left and right, and is unnecessary. durDistTextLeft = durDistTextRight = dvrDistTextTop = dvrDistTextBottom = 0; // Calculate specified width. IsAuto flag is needed because Auto is formatted the same way as column and will // not return Double.NaN. bool isWidthAuto; double specifiedWidth = FigureHelper.CalculateFigureWidth(StructuralCache, element, element.Width, out isWidthAuto); double anchorLimitedWidth = LimitTotalWidthFromAnchor(specifiedWidth, TextDpi.FromTextDpi(mbp.MarginLeft + mbp.MarginRight)); int subpageWidth = Math.Max(1, TextDpi.ToTextDpi(anchorLimitedWidth) - (mbp.BPLeft + mbp.BPRight)); // Calculate figure height, IsAuto flag is used as specifiedHeight will never be NaN. bool isHeightAuto; double specifiedHeight = FigureHelper.CalculateFigureHeight(StructuralCache, element, element.Height, out isHeightAuto); double anchorLimitedHeight = LimitTotalHeightFromAnchor(specifiedHeight, TextDpi.FromTextDpi(mbp.MarginTop + mbp.MarginBottom)); int subpageHeight = Math.Max(1, TextDpi.ToTextDpi(anchorLimitedHeight) - (mbp.BPTop + mbp.BPBottom)); // Initialize column info. Figure always has just 1 column. cColumns = 1; columnInfoCollection = new PTS.FSCOLUMNINFO[cColumns]; columnInfoCollection[0].durBefore = 0; columnInfoCollection[0].durWidth = subpageWidth; // Create subpage { PTS.FSFMTR fsfmtr; IntPtr brParaOut; PTS.FSRECT marginRect = new PTS.FSRECT(0, 0, subpageWidth, subpageHeight); CreateSubpageFiniteHelper(PtsContext, IntPtr.Zero, PTS.False, _mainTextSegment.Handle, IntPtr.Zero, PTS.False, PTS.True, fswdir, subpageWidth, subpageHeight, ref marginRect, cColumns, columnInfoCollection, PTS.False, out fsfmtr, out pfsFigureContent, out brParaOut, out dvr, out fsbbox, out pmcsclientOut, out dvrTopSpace); if(brParaOut != IntPtr.Zero) { PTS.Validate(PTS.FsDestroySubpageBreakRecord(PtsContext.Context, brParaOut)); } } // PTS subpage does not support autosizing, but Figure needs to autosize to its // content. To workaround this problem, second format of subpage is performed, if // necessary. It means that if the width of bounding box is smaller than subpage's // width, second formatting is performed. if(PTS.ToBoolean(fsbbox.fDefined)) { if (fsbbox.fsrc.du < subpageWidth && isWidthAuto) { // There is a need to reformat PTS subpage, so destroy any resourcces allocated by PTS // during previous formatting. if (pfsFigureContent != IntPtr.Zero) { PTS.Validate(PTS.FsDestroySubpage(PtsContext.Context, pfsFigureContent), PtsContext); } if (pmcsclientOut != IntPtr.Zero) { MarginCollapsingState mcs = PtsContext.HandleToObject(pmcsclientOut) as MarginCollapsingState; PTS.ValidateHandle(mcs); mcs.Dispose(); pmcsclientOut = IntPtr.Zero; } // Create subpage with new width. subpageWidth = fsbbox.fsrc.du + 1; // add 1/300px to avoid rounding errors columnInfoCollection[0].durWidth = subpageWidth; // Create subpage PTS.FSFMTR fsfmtr; IntPtr brParaOut; PTS.FSRECT marginRect = new PTS.FSRECT(0, 0, subpageWidth, subpageHeight); CreateSubpageFiniteHelper(PtsContext, IntPtr.Zero, PTS.False, _mainTextSegment.Handle, IntPtr.Zero, PTS.False, PTS.True, fswdir, subpageWidth, subpageHeight, ref marginRect, cColumns, columnInfoCollection, PTS.False, out fsfmtr, out pfsFigureContent, out brParaOut, out dvr, out fsbbox, out pmcsclientOut, out dvrTopSpace); if(brParaOut != IntPtr.Zero) { PTS.Validate(PTS.FsDestroySubpageBreakRecord(PtsContext.Context, brParaOut)); } } } else { subpageWidth = TextDpi.ToTextDpi(TextDpi.MinWidth); } // Get the size of the figure. For height PTS already reports calculated value. // But width is the same as subpage width. Include margins in dur since we are not using // distance to text anymore. dur = subpageWidth + mbp.MBPLeft + mbp.MBPRight; // Destroy objects created by PTS, but not used here. if (pmcsclientOut != IntPtr.Zero) { MarginCollapsingState mcs = PtsContext.HandleToObject(pmcsclientOut) as MarginCollapsingState; PTS.ValidateHandle(mcs); mcs.Dispose(); pmcsclientOut = IntPtr.Zero; } dvr += mbp.MBPTop + mbp.MBPBottom; if(!isHeightAuto) { // Replace height with explicit height if specified, adding margins in addition to height // Border and padding are included in specified height but margins are external dvr = TextDpi.ToTextDpi(anchorLimitedHeight) + mbp.MarginTop + mbp.MarginBottom; } FigureHorizontalAnchor horzAnchor = element.HorizontalAnchor; FigureVerticalAnchor vertAnchor = element.VerticalAnchor; fsfigprops.fskrefU = (PTS.FSKREF)(((int)horzAnchor) / 3); fsfigprops.fskrefV = (PTS.FSKREF)(((int)vertAnchor) / 3); fsfigprops.fskalfU = (PTS.FSKALIGNFIG)(((int)horzAnchor) % 3); fsfigprops.fskalfV = (PTS.FSKALIGNFIG)(((int)vertAnchor) % 3); // PTS does not allow to anchor delayed figures to 'Character' if (!PTS.ToBoolean(fInTextLine)) { if (fsfigprops.fskrefU == PTS.FSKREF.fskrefChar) { fsfigprops.fskrefU = PTS.FSKREF.fskrefMargin; fsfigprops.fskalfU = PTS.FSKALIGNFIG.fskalfMin; } if (fsfigprops.fskrefV == PTS.FSKREF.fskrefChar) { fsfigprops.fskrefV = PTS.FSKREF.fskrefMargin; fsfigprops.fskalfV = PTS.FSKALIGNFIG.fskalfMin; } } // Always wrap text on both sides of the floater. fsfigprops.fskwrap = PTS.WrapDirectionToFskwrap(element.WrapDirection); fsfigprops.fNonTextPlane = PTS.False; fsfigprops.fAllowOverlap = PTS.False; fsfigprops.fDelayable = PTS.FromBoolean(element.CanDelayPlacement); // Tight wrap is disabled for now. cPolygons = cVertices = 0; // Update handle to PTS subpage. ((FigureParaClient)paraClient).SubpageHandle = pfsFigureContent; }
internal int CalcFigurePosition( IntPtr pfsclient, // IN: client opaque data IntPtr pfsparaclientFigure, // IN: IntPtr nmpFigure, // IN: figure's name uint fswdir, // IN: current direction ref PTS.FSRECT fsrcPage, // IN: page rectangle ref PTS.FSRECT fsrcMargin, // IN: rectangle within page margins ref PTS.FSRECT fsrcTrack, // IN: track rectangle ref PTS.FSRECT fsrcFigurePreliminary,// IN: prelim figure rect calculated from figure props int fMustPosition, // IN: must find position in this track? int fInTextLine, // IN: it is attached to text line out int fPushToNextTrack, // OUT: push to next track? out PTS.FSRECT fsrcFlow, // OUT: FlowAround rectangle out PTS.FSRECT fsrcOverlap, // OUT: Overlap rectangle out PTS.FSBBOX fsbbox, // OUT: bbox out PTS.FSRECT fsrcSearch) // OUT: search area for overlap { int fserr = PTS.fserrNone; try { FigureParagraph para = PtsContext.HandleToObject(nmpFigure) as FigureParagraph; PTS.ValidateHandle(para); FigureParaClient paraClient = PtsContext.HandleToObject(pfsparaclientFigure) as FigureParaClient; PTS.ValidateHandle(paraClient); para.CalcFigurePosition(paraClient, fswdir, ref fsrcPage, ref fsrcMargin, ref fsrcTrack, ref fsrcFigurePreliminary, fMustPosition, fInTextLine, out fPushToNextTrack, out fsrcFlow, out fsrcOverlap, out fsbbox, out fsrcSearch); } catch (Exception e) { fPushToNextTrack = 0; fsrcFlow = fsrcOverlap = fsrcSearch = new PTS.FSRECT(); fsbbox = new PTS.FSBBOX(); PtsContext.CallbackException = e; fserr = PTS.fserrCallbackException; } catch { fPushToNextTrack = 0; fsrcFlow = fsrcOverlap = fsrcSearch = new PTS.FSRECT(); fsbbox = new PTS.FSBBOX(); PtsContext.CallbackException = new System.Exception("Caught a non CLS Exception"); fserr = PTS.fserrCallbackException; } return fserr; }
internal override List<Rect> GetRectangles(ContentElement e, int start, int length) { List<Rect> rectangles = new List<Rect>(); Debug.Assert(Paragraph.Element as ContentElement != e); PTS.FSTEXTDETAILS textDetails; PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails)); // There are 3 different types of text paragraphs: // (a) full with simple lines // (b) full with composite lines - when figures/floaters are present // (c) cached - when using ParaChache if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull) { // Check figures and floaters if (textDetails.u.full.cAttachedObjects > 0) { PTS.FSATTACHEDOBJECTDESCRIPTION[] arrayAttachedObjectDesc; PtsHelper.AttachedObjectListFromParagraph(PtsContext, _paraHandle.Value, textDetails.u.full.cAttachedObjects, out arrayAttachedObjectDesc); for (int index = 0; index < arrayAttachedObjectDesc.Length; index++) { PTS.FSATTACHEDOBJECTDESCRIPTION attachedObjectDesc = arrayAttachedObjectDesc[index]; BaseParaClient paraClient = PtsContext.HandleToObject(attachedObjectDesc.pfsparaclient) as BaseParaClient; PTS.ValidateHandle(paraClient); if (start < paraClient.Paragraph.ParagraphEndCharacterPosition) { rectangles = paraClient.GetRectangles(e, start, length); Invariant.Assert(rectangles != null); if (rectangles.Count != 0) { break; } } } } // If no success with figures and floaters, check in line if (rectangles.Count == 0 && textDetails.u.full.cLines > 0) { if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite)) { // (a) full with simple lines rectangles = GetRectanglesInSimpleLines(e, start, length, ref textDetails.u.full); } else { // (b) full with complex lines rectangles = GetRectanglesInCompositeLines(e, start, length, ref textDetails.u.full); } // Ensure these are specified in page coordinates. if(rectangles.Count > 0 && ThisFlowDirection != PageFlowDirection) { PTS.FSRECT pageRect = _pageContext.PageRect; for(int index = 0; index < rectangles.Count; index++) { PTS.FSRECT rectTransform = new PTS.FSRECT(rectangles[index]); PTS.Validate(PTS.FsTransformRectangle(PTS.FlowDirectionToFswdir(ThisFlowDirection), ref pageRect, ref rectTransform, PTS.FlowDirectionToFswdir(PageFlowDirection), out rectTransform)); rectangles[index] = rectTransform.FromTextDpi(); } } } } else { // (c) cached - when using ParaChache Debug.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached); Debug.Assert(false, "Should not get here. ParaCache is not currently used."); } Invariant.Assert(rectangles != null); return rectangles; }
internal Rect GetRectangleFromTextPosition(ITextPointer position) { Rect rect = System.Windows.Rect.Empty; int cp = Paragraph.StructuralCache.TextContainer.Start.GetOffsetToPosition((TextPointer)position); int dcp = cp - Paragraph.ParagraphStartCharacterPosition; int originalDcp = dcp; if (position.LogicalDirection == LogicalDirection.Backward && dcp > 0) { --dcp; } // Query paragraph details PTS.FSTEXTDETAILS textDetails; PTS.Validate(PTS.FsQueryTextDetails(PtsContext.Context, _paraHandle.Value, out textDetails)); // There are 3 different types of text paragraphs: // (a) full with simple lines // (b) full with composite lines - when figures/floaters are present // (c) cached - when using ParaChache if (textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdFull) { if (textDetails.u.full.cLines > 0) { int vrBaseline = 0; if (!PTS.ToBoolean(textDetails.u.full.fLinesComposite)) { // (a) full with simple lines RectFromDcpSimpleLines(dcp, originalDcp, position.LogicalDirection, position.GetPointerContext(position.LogicalDirection), ref textDetails.u.full, ref rect, ref vrBaseline); } else { // (b) full with composite lines - when figures/floaters are present RectFromDcpCompositeLines(dcp, originalDcp, position.LogicalDirection, position.GetPointerContext(position.LogicalDirection), ref textDetails.u.full, ref rect, ref vrBaseline); } } } else { // (c) cached - when using ParaChache Debug.Assert(textDetails.fsktd == PTS.FSKTEXTDETAILS.fsktdCached); Debug.Assert(false, "Should not get here. ParaCache is not currently used."); } // Mirror back to page flow direction if(ThisFlowDirection != PageFlowDirection) { PTS.FSRECT pageRect = _pageContext.PageRect; PTS.FSRECT rectTransform = new PTS.FSRECT(rect); PTS.Validate(PTS.FsTransformRectangle(PTS.FlowDirectionToFswdir(ThisFlowDirection), ref pageRect, ref rectTransform, PTS.FlowDirectionToFswdir(PageFlowDirection), out rectTransform)); rect = rectTransform.FromTextDpi(); } return rect; }
private ReadOnlyCollection<LineResult> LineResultsFromCompositeLines(ref PTS.FSTEXTDETAILSFULL textDetails) { ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); // Get list of complex composite lines. PTS.FSLINEDESCRIPTIONCOMPOSITE [] arrayLineDesc; PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); List<LineResult> lines = new List<LineResult>(arrayLineDesc.Length); // Get lines information for (int index = 0; index < arrayLineDesc.Length; index++) { PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; if (lineDesc.cElements == 0) { continue; } // Get list of line elements. PTS.FSLINEELEMENT [] arrayLineElement; PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++) { PTS.FSLINEELEMENT element = arrayLineElement[elIndex]; // Create line info Rect lbox = new Rect(TextDpi.FromTextDpi(element.urBBox), TextDpi.FromTextDpi(lineDesc.vrStart), TextDpi.FromTextDpi(element.durBBox), TextDpi.FromTextDpi(element.dvrAscent + element.dvrDescent)); // Mirror layout box to page flow direction if(ThisFlowDirection != PageFlowDirection) { PTS.FSRECT pageRect = _pageContext.PageRect; PTS.FSRECT rectTransform = new PTS.FSRECT(lbox); PTS.Validate(PTS.FsTransformRectangle(PTS.FlowDirectionToFswdir(ThisFlowDirection), ref pageRect, ref rectTransform, PTS.FlowDirectionToFswdir(PageFlowDirection), out rectTransform)); lbox = rectTransform.FromTextDpi(); } lines.Add(new TextParaLineResult(this, element.dcpFirst, element.dcpLim - element.dcpFirst, lbox, TextDpi.FromTextDpi(element.dvrAscent))); } } if (lines.Count != 0) { // Hide EOP character TextParaLineResult lastLineResult = (TextParaLineResult)lines[lines.Count - 1]; if (HasEOP && lastLineResult.DcpLast > Paragraph.Cch) { ErrorHandler.Assert(lastLineResult.DcpLast - Line.SyntheticCharacterLength == Paragraph.Cch, ErrorHandler.ParagraphCharacterCountMismatch); lastLineResult.DcpLast -= Line.SyntheticCharacterLength; } } return (lines.Count > 0) ? new ReadOnlyCollection<LineResult>(lines) : null; }
// ------------------------------------------------------------------ // Returns rectangles for a single composite line correcsponding to the // given dcp range. Includes trailing whitespaces. // Params: // dcpRangeStart - range's cp start position. Adjusted for // line's cp range. // cchRange - nuber of cps in the range. // lineTopSpace - the value that line's height should // be extended to at the top. // lineRightSpace - the value that line's width should // be extended to at the right. // lineDesc - line description. // lineIndex - line index. // elemDesc - element description. // visibleRect - visibility rectangle. It is Ok to return // null if the line is not visible. // hasAttachedObjects- Attached objects are present // Returns: // null - if line is not visible // rectangles - otherwise. // ----------------------------------------------------------------- private List<Rect> RectanglesFromDcpRangeOfCompositeLineElement( int dcpRangeStart, int cchRange, double lineTopSpace, double lineRightSpace, ref PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc, int lineIndex, ref PTS.FSLINEELEMENT elemDesc, int elemIndex, Rect visibleRect) { List<Rect> rectangles = null; Rect elementRect = new PTS.FSRECT(elemDesc.urBBox, lineDesc.vrStart, elemDesc.durBBox, lineDesc.dvrAscent + lineDesc.dvrDescent).FromTextDpi(); // width has to be adjusted to include trailing whitespaces... LineVisual lineVisual = FetchLineVisualComposite(lineIndex, elemIndex); if (lineVisual != null) { elementRect.Width = Math.Max(lineVisual.WidthIncludingTrailingWhitespace, 0); } elementRect.Y = elementRect.Y - lineTopSpace; elementRect.Height = elementRect.Height + lineTopSpace; elementRect.Width = elementRect.Width + lineRightSpace; // Ignore horizontal offset because TextBox page width != extent width. // It's ok to include content that doesn't strictly intersect -- this // is a perf optimization and the edge cases won't significantly hurt us. Rect testRect = elementRect; testRect.X = visibleRect.X; if (testRect.IntersectsWith(visibleRect)) { // Check whether the line is fully selected - we don't need to reformat it in this case if (dcpRangeStart == elemDesc.dcpFirst && elemDesc.dcpLim <= (dcpRangeStart + cchRange)) { rectangles = new List<Rect>(1); rectangles.Add(elementRect); } else { // Create and format line Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(elemDesc.fClearOnLeft), PTS.ToBoolean(elemDesc.fClearOnRight), TextParagraph.TextRunCache); if (IsOptimalParagraph) { ctx.LineFormatLengthTarget = elemDesc.dcpLim - elemDesc.dcpFirst; } TextParagraph.FormatLineCore(line, elemDesc.pfsbreakreclineclient, ctx, elemDesc.dcpFirst, elemDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), elemDesc.dcpFirst); Invariant.Assert(line.SafeLength == elemDesc.dcpLim - elemDesc.dcpFirst, "Line length is out of [....]"); double duOffset = TextDpi.FromTextDpi(elemDesc.urStart); double dvOffset = TextDpi.FromTextDpi(lineDesc.vrStart); rectangles = line.GetRangeBounds(dcpRangeStart, cchRange, duOffset, dvOffset); if (!DoubleUtil.IsZero(lineTopSpace)) { for (int i = 0, count = rectangles.Count; i < count; ++i) { Rect r = rectangles[i]; r.Y = r.Y - lineTopSpace; r.Height = r.Height + lineTopSpace; rectangles[i] = r; } } if (!DoubleUtil.IsZero(lineRightSpace)) { // add the rect representing end-of-line / end-of-para rectangles.Add( new Rect( duOffset + TextDpi.FromTextDpi(line.Start + line.Width), dvOffset - lineTopSpace, lineRightSpace, TextDpi.FromTextDpi(line.Height) + lineTopSpace ) ); } // dispose of the line line.Dispose(); } } return (rectangles); }
/// <summary> /// Arranges content. /// </summary> /// <param name="arrangeSize">Size that element should use to arrange itself and its children.</param> /// <param name="viewport">Viewport for visible content.</param> internal void Arrange(Size arrangeSize, Rect viewport) { Invariant.Assert(_document.StructuralCache.DtrList == null || _document.StructuralCache.DtrList.Length == 0 || (_document.StructuralCache.DtrList.Length == 1 && _document.StructuralCache.BackgroundFormatInfo.DoesFinalDTRCoverRestOfText)); // Arrange the content and create visual tree. _documentPage.Arrange(arrangeSize); _documentPage.EnsureValidVisuals(); _arrangedAfterFormat = true; // Render content only for the current viewport. if (viewport.IsEmpty) { viewport = new Rect(0, 0, arrangeSize.Width, _document.StructuralCache.BackgroundFormatInfo.ViewportHeight); } PTS.FSRECT fsrectViewport = new PTS.FSRECT(viewport); _documentPage.UpdateViewport(ref fsrectViewport, true); _isContentFormatValid = true; }
internal void FormatCellFinite(Size subpageSize, IntPtr breakRecordIn, bool isEmptyOk, uint fswdir, PTS.FSKSUPPRESSHARDBREAKBEFOREFIRSTPARA fsksuppresshardbreakbeforefirstparaIn, out PTS.FSFMTR fsfmtr, out int dvrUsed, out IntPtr breakRecordOut) { IntPtr pfspara; PTS.FSBBOX fsbbox; IntPtr pmcsclientOut; PTS.FSKCLEAR fskclearOut; int dvrTopSpace; PTS.FSPAP fspap; if(CellParagraph.StructuralCache.DtrList != null && breakRecordIn != null) { CellParagraph.InvalidateStructure(TextContainerHelper.GetCPFromElement(CellParagraph.StructuralCache.TextContainer, CellParagraph.Element, ElementEdge.BeforeStart)); } // Ensures segment is created for paragraph fspap = new PTS.FSPAP(); CellParagraph.GetParaProperties(ref fspap); PTS.FSRECT rectCell; rectCell = new PTS.FSRECT(); rectCell.u = rectCell.v = 0; rectCell.du = TextDpi.ToTextDpi(subpageSize.Width); rectCell.dv = TextDpi.ToTextDpi(subpageSize.Height); // Suppress top space if cell is broken, but not otherwise bool suppressTopSpace = (breakRecordIn != IntPtr.Zero) ? true : false; CellParagraph.FormatParaFinite(this, breakRecordIn, PTS.FromBoolean(true), IntPtr.Zero, PTS.FromBoolean(isEmptyOk), PTS.FromBoolean(suppressTopSpace), fswdir, ref rectCell, null, PTS.FSKCLEAR.fskclearNone, fsksuppresshardbreakbeforefirstparaIn, out fsfmtr, out pfspara, out breakRecordOut, out dvrUsed, out fsbbox, out pmcsclientOut, out fskclearOut, out dvrTopSpace); if (pmcsclientOut != IntPtr.Zero) { MarginCollapsingState mcs = PtsContext.HandleToObject(pmcsclientOut) as MarginCollapsingState; PTS.ValidateHandle(mcs); dvrUsed += mcs.Margin; mcs.Dispose(); pmcsclientOut = IntPtr.Zero; } _paraHandle.Value = pfspara; }
internal unsafe void UpdateBottomlessPara( IntPtr pfspara, // IN: pointer to the para data SubpageParaClient paraClient, // IN: int fSuppressTopSpace, // IN: suppress empty space at the top of the page uint fswdir, // IN: current direction int urTrack, // IN: u of bootomless rectangle to fill int durTrack, // IN: du of bootomless rectangle to fill int vrTrack, // IN: v of bootomless rectangle to fill MarginCollapsingState mcs, // IN: input margin collapsing state PTS.FSKCLEAR fskclearIn, // IN: clear property that must be satisfied int fInterruptable, // IN: formatting can be interrupted out PTS.FSFMTRBL fsfmtrbl, // OUT: result of formatting the paragraph out int dvrUsed, // OUT: vertical space used by the para out PTS.FSBBOX fsbbox, // OUT: para BBox out IntPtr pmcsclientOut, // OUT: margin collapsing state at the bottom out PTS.FSKCLEAR fskclearOut, // OUT: ClearIn for the next paragraph out int dvrTopSpace, // OUT: top space due to collapsed margin out int fPageBecomesUninterruptable)// OUT: interruption is prohibited from now on { int subpageWidth, urSubpageMargin, durSubpageMargin, vrSubpageMargin; int cColumns; int marginTop, marginBottom; MbpInfo mbp; MarginCollapsingState mcsSubpage, mcsBottom; PTS.FSCOLUMNINFO[] columnInfoCollection; uint fswdirSubpage = PTS.FlowDirectionToFswdir(((FlowDirection)Element.GetValue(FrameworkElement.FlowDirectionProperty))); // Initialize the subpage size and its margin. subpageWidth = durTrack; urSubpageMargin = vrSubpageMargin = 0; // Set clear property Invariant.Assert(Element is TableCell || Element is AnchoredBlock); fskclearIn = PTS.WrapDirectionToFskclear((WrapDirection)Element.GetValue(Block.ClearFloatersProperty)); // Take into account MBPs and modify subpage metrics, // and make sure that subpage is at least 1 unit wide (cannot measure at width <= 0) // NOTE: Do not suppress top space for bottomles pages. mbp = MbpInfo.FromElement(Element); if(fswdirSubpage != fswdir) { PTS.FSRECT fsrcToFillSubpage = new PTS.FSRECT(urTrack, 0, durTrack, 0); PTS.FSRECT pageRect = StructuralCache.CurrentFormatContext.PageRect; PTS.Validate(PTS.FsTransformRectangle(fswdir, ref pageRect, ref fsrcToFillSubpage, fswdirSubpage, out fsrcToFillSubpage)); urTrack = fsrcToFillSubpage.u; durTrack = fsrcToFillSubpage.du; mbp.MirrorMargin(); } subpageWidth = Math.Max(1, subpageWidth - (mbp.MBPLeft + mbp.MBPRight)); MarginCollapsingState.CollapseTopMargin(PtsContext, mbp, mcs, out mcsSubpage, out marginTop); // Destroy top margin collapsing state (not needed anymore). if (mcsSubpage != null) { mcsSubpage.Dispose(); mcsSubpage = null; } durSubpageMargin = subpageWidth; // Initialize column info // For bottomles spara, limit line height to the height of the current format context in structural cache. ColumnPropertiesGroup columnProperties = new ColumnPropertiesGroup(_element); double lineHeight = DynamicPropertyReader.GetLineHeightValue(_element); double pageFontSize = (double)_structuralCache.PropertyOwner.GetValue(Block.FontSizeProperty); FontFamily pageFontFamily = (FontFamily)_structuralCache.PropertyOwner.GetValue(Block.FontFamilyProperty); // Get columns info, setting ownerIsFlowDocument flag to false. A flow document should not be formatted as a subpage and we // do not want default column widths to be set on TableCells and floaters cColumns = PtsHelper.CalculateColumnCount(columnProperties, lineHeight, TextDpi.FromTextDpi(subpageWidth), pageFontSize, pageFontFamily, false); columnInfoCollection = new PTS.FSCOLUMNINFO[cColumns]; fixed (PTS.FSCOLUMNINFO* rgColumnInfo = columnInfoCollection) { PtsHelper.GetColumnsInfo(columnProperties, lineHeight, TextDpi.FromTextDpi(subpageWidth), pageFontSize, pageFontFamily, cColumns, rgColumnInfo, false); } StructuralCache.CurrentFormatContext.PushNewPageData(new Size(TextDpi.FromTextDpi(subpageWidth), TextDpi.MaxWidth), new Thickness(), true, false); // Create subpage fixed (PTS.FSCOLUMNINFO* rgColumnInfo = columnInfoCollection) { PTS.Validate(PTS.FsUpdateBottomlessSubpage(PtsContext.Context, pfspara, _mainTextSegment.Handle, fSuppressTopSpace, fswdir, subpageWidth, urSubpageMargin, durSubpageMargin, vrSubpageMargin, cColumns, rgColumnInfo, 0, null, null, 0, null, null, PTS.FromBoolean(true), out fsfmtrbl, out dvrUsed, out fsbbox, out pmcsclientOut, out dvrTopSpace, out fPageBecomesUninterruptable), PtsContext); } StructuralCache.CurrentFormatContext.PopPageData(); fskclearOut = PTS.FSKCLEAR.fskclearNone; if (fsfmtrbl != PTS.FSFMTRBL.fmtrblCollision) { // Bottom margin collapsing: // (1) retrieve mcs from the subtrack // (2) do margin collapsing; create a new margin collapsing state if (pmcsclientOut != IntPtr.Zero) { mcsSubpage = PtsContext.HandleToObject(pmcsclientOut) as MarginCollapsingState; PTS.ValidateHandle(mcsSubpage); pmcsclientOut = IntPtr.Zero; } MarginCollapsingState.CollapseBottomMargin(PtsContext, mbp, mcsSubpage, out mcsBottom, out marginBottom); pmcsclientOut = (mcsBottom != null) ? mcsBottom.Handle : IntPtr.Zero; // Since MCS returned by PTS is never passed back, destroy MCS provided by PTS. // If necessary, new MCS is created and passed back to PTS. if (mcsSubpage != null) { mcsSubpage.Dispose(); mcsSubpage = null; } if (PTS.ToBoolean(fsbbox.fDefined)) { // Workaround for PTS bug 860: get max of the page rect and // bounding box of the page. dvrUsed = Math.Max(dvrUsed, fsbbox.fsrc.dv + fsbbox.fsrc.v); durTrack = Math.Max(durTrack, fsbbox.fsrc.du + fsbbox.fsrc.u); } // Take into account MBPs and modify subtrack metrics dvrTopSpace = (mbp.BPTop != 0) ? marginTop : dvrTopSpace; dvrUsed += (marginTop + mbp.BPTop) + (marginBottom + mbp.BPBottom); // Update bounding box fsbbox.fsrc.u = urTrack + mbp.MarginLeft; fsbbox.fsrc.v = vrTrack + dvrTopSpace; fsbbox.fsrc.du = Math.Max(durTrack - (mbp.MarginLeft + mbp.MarginRight), 0); fsbbox.fsrc.dv = Math.Max(dvrUsed - dvrTopSpace, 0); } else { Debug.Assert(pmcsclientOut == IntPtr.Zero); pfspara = IntPtr.Zero; dvrUsed = dvrTopSpace = 0; } if(fswdirSubpage != fswdir) { PTS.FSRECT pageRect = StructuralCache.CurrentFormatContext.PageRect; PTS.Validate(PTS.FsTransformBbox(fswdirSubpage, ref pageRect, ref fsbbox, fswdir, out fsbbox)); } // Update information about first/last chunk. In bottomless scenario // paragraph is not broken, so there is only one chunk. paraClient.SetChunkInfo(true, true); }
internal unsafe void FormatParaFinite( SubpageParaClient paraClient, // IN: IntPtr pbrkrecIn, // IN: break record---use if !NULL int fBRFromPreviousPage, // IN: break record was created on previous page IntPtr footnoteRejector, // IN: int fEmptyOk, // IN: is it OK not to add anything? int fSuppressTopSpace, // IN: suppress empty space at the top of the page uint fswdir, // IN: current direction ref PTS.FSRECT fsrcToFill, // IN: rectangle to fill MarginCollapsingState mcs, // IN: input margin collapsing state PTS.FSKCLEAR fskclearIn, // IN: clear property that must be satisfied PTS.FSKSUPPRESSHARDBREAKBEFOREFIRSTPARA fsksuppresshardbreakbeforefirstparaIn, // IN: suppress breaks at track start? out PTS.FSFMTR fsfmtr, // OUT: result of formatting the paragraph out IntPtr pfspara, // OUT: pointer to the para data out IntPtr pbrkrecOut, // OUT: pointer to the para break record out int dvrUsed, // OUT: vertical space used by the para out PTS.FSBBOX fsbbox, // OUT: para BBox out IntPtr pmcsclientOut, // OUT: margin collapsing state at the bottom out PTS.FSKCLEAR fskclearOut, // OUT: ClearIn for the next paragraph out int dvrTopSpace) // OUT: top space due to collapsed margin { uint fswdirSubpage = PTS.FlowDirectionToFswdir(((FlowDirection)Element.GetValue(FrameworkElement.FlowDirectionProperty))); int subpageWidth, subpageHeight; int cColumns; int marginTop, marginBottom; MbpInfo mbp; MarginCollapsingState mcsSubpage, mcsBottom; PTS.FSRECT fsrcSubpageMargin; PTS.FSCOLUMNINFO [] columnInfoCollection; // Currently it is possible to get MCS and BR in following situation: // At the end of the page there is a paragraph with delayed figure, so the figure // gets delayed to the next page. But part of the next paragraph fits in the page, // so it gets broken. PTS creates BR with delayed figure and broken para. // PTS will format the next page starting from delayed figure, which can produce MCS. // So when the next paragraph is continued from BR, it has MCS. // This problem is currently investigated by PTS team: PTSLS bug 915. // For now, MCS gets ignored here. //Debug.Assert(pbrkrecIn == IntPtr.Zero || mcs == null, "Broken paragraph cannot have margin collapsing state."); if (mcs != null && pbrkrecIn != IntPtr.Zero) { mcs = null; } // Initialize the subpage size and its margin. fsrcSubpageMargin = new PTS.FSRECT(); subpageWidth = fsrcToFill.du; subpageHeight = fsrcToFill.dv; // Set clear property Invariant.Assert(Element is TableCell || Element is AnchoredBlock); fskclearIn = PTS.WrapDirectionToFskclear((WrapDirection)Element.GetValue(Block.ClearFloatersProperty)); // Take into account MBPs and modify subpage metrics, // and make sure that subpage is at least 1 unit wide (cannot measure at width <= 0) marginTop = 0; mcsSubpage = null; // Get MBP info. Since subpage height and width must be at least 1, the max size for MBP is subpage dimensions less 1 mbp = MbpInfo.FromElement(Element); if(fswdirSubpage != fswdir) { PTS.FSRECT pageRect = StructuralCache.CurrentFormatContext.PageRect; PTS.Validate(PTS.FsTransformRectangle(fswdir, ref pageRect, ref fsrcToFill, fswdirSubpage, out fsrcToFill)); mbp.MirrorMargin(); } subpageWidth = Math.Max(1, subpageWidth - (mbp.MBPLeft + mbp.MBPRight)); if (pbrkrecIn == IntPtr.Zero) { // Top margin collapsing. If suppresing top space, top margin is always 0. MarginCollapsingState.CollapseTopMargin(PtsContext, mbp, mcs, out mcsSubpage, out marginTop); if (PTS.ToBoolean(fSuppressTopSpace)) { marginTop = 0; } subpageHeight = Math.Max(1, subpageHeight - (marginTop + mbp.BPTop)); // Destroy top margin collapsing state (not needed anymore). if (mcsSubpage != null) { mcsSubpage.Dispose(); mcsSubpage = null; } } else { Debug.Assert(fSuppressTopSpace == 1, "Top space should be always suppressed at the top of broken paragraph."); } fsrcSubpageMargin.du = subpageWidth; fsrcSubpageMargin.dv = subpageHeight; // Initialize column info ColumnPropertiesGroup columnProperties = new ColumnPropertiesGroup(_element); double lineHeight = DynamicPropertyReader.GetLineHeightValue(_element); double pageFontSize = (double)_structuralCache.PropertyOwner.GetValue(Block.FontSizeProperty); FontFamily pageFontFamily = (FontFamily)_structuralCache.PropertyOwner.GetValue(Block.FontFamilyProperty); // Get columns info, setting ownerIsFlowDocument flag to false. A flow document should not be formatted as a subpage and we // do not want default column widths to be set on TableCells and floaters cColumns = PtsHelper.CalculateColumnCount(columnProperties, lineHeight, TextDpi.FromTextDpi(subpageWidth), pageFontSize, pageFontFamily, false); columnInfoCollection = new PTS.FSCOLUMNINFO[cColumns]; fixed (PTS.FSCOLUMNINFO* rgColumnInfo = columnInfoCollection) { PtsHelper.GetColumnsInfo(columnProperties, lineHeight, TextDpi.FromTextDpi(subpageWidth), pageFontSize, pageFontFamily, cColumns, rgColumnInfo, false); } // Format subpage StructuralCache.CurrentFormatContext.PushNewPageData(new Size(TextDpi.FromTextDpi(subpageWidth), TextDpi.FromTextDpi(subpageHeight)), new Thickness(), false, true); fixed (PTS.FSCOLUMNINFO* rgColumnInfo = columnInfoCollection) { PTS.Validate(PTS.FsCreateSubpageFinite(PtsContext.Context, pbrkrecIn, fBRFromPreviousPage, _mainTextSegment.Handle, footnoteRejector, fEmptyOk, fSuppressTopSpace, fswdir, subpageWidth, subpageHeight, ref fsrcSubpageMargin, cColumns, rgColumnInfo, PTS.False, 0, null, null, 0, null, null, PTS.FromBoolean(false), fsksuppresshardbreakbeforefirstparaIn, out fsfmtr, out pfspara, out pbrkrecOut, out dvrUsed, out fsbbox, out pmcsclientOut, out dvrTopSpace), PtsContext); } StructuralCache.CurrentFormatContext.PopPageData(); fskclearOut = PTS.FSKCLEAR.fskclearNone; if (PTS.ToBoolean(fsbbox.fDefined)) { // Workaround for PTS bug 860: get max of the page rect and // bounding box of the page. dvrUsed = Math.Max(dvrUsed, fsbbox.fsrc.dv + fsbbox.fsrc.v); fsrcToFill.du = Math.Max(fsrcToFill.du, fsbbox.fsrc.du + fsbbox.fsrc.u); } if (pbrkrecIn == IntPtr.Zero) // if first chunk { // Take into account MBPs and modify subpage metrics dvrTopSpace = (mbp.BPTop != 0) ? marginTop : dvrTopSpace; dvrUsed += (marginTop + mbp.BPTop); } if (pmcsclientOut != IntPtr.Zero) { mcsSubpage = PtsContext.HandleToObject(pmcsclientOut) as MarginCollapsingState; PTS.ValidateHandle(mcsSubpage); pmcsclientOut = IntPtr.Zero; } // Initialize subpage metrics if (fsfmtr.kstop >= PTS.FSFMTRKSTOP.fmtrNoProgressOutOfSpace) // No progress or collision { dvrUsed = dvrTopSpace = 0; //pbrkrecOut = IntPtr.Zero; //pfspara = IntPtr.Zero; //pmcsclientOut = IntPtr.Zero; } else { if (fsfmtr.kstop == PTS.FSFMTRKSTOP.fmtrGoalReached) { // Bottom margin collapsing: // (a) retrieve mcs from the subpage // (b) do margin collapsing; create a new margin collapsing state // There is no bottom margin collapsing if paragraph will be continued (output break record is not null). MarginCollapsingState.CollapseBottomMargin(PtsContext, mbp, mcsSubpage, out mcsBottom, out marginBottom); pmcsclientOut = (mcsBottom != null) ? mcsBottom.Handle : IntPtr.Zero; if (pmcsclientOut == IntPtr.Zero) // if last chunk dvrUsed += marginBottom + mbp.BPBottom; } // Update bounding box fsbbox.fsrc.u = fsrcToFill.u + mbp.MarginLeft; fsbbox.fsrc.v = fsrcToFill.v + dvrTopSpace; fsbbox.fsrc.du = Math.Max(fsrcToFill.du - (mbp.MarginLeft + mbp.MarginRight), 0); fsbbox.fsrc.dv = Math.Max(dvrUsed - dvrTopSpace, 0); } if(fswdirSubpage != fswdir) { PTS.FSRECT pageRect = StructuralCache.CurrentFormatContext.PageRect; PTS.Validate(PTS.FsTransformBbox(fswdirSubpage, ref pageRect, ref fsbbox, fswdir, out fsbbox)); } // Since MCS returned by PTS is never passed back, destroy MCS provided by PTS. // If necessary, new MCS is created and passed back to PTS (see above). if (mcsSubpage != null) { mcsSubpage.Dispose(); mcsSubpage = null; } // Update information about first/last chunk paraClient.SetChunkInfo(pbrkrecIn == IntPtr.Zero, pbrkrecOut == IntPtr.Zero); }
//------------------------------------------------------------------- // Update the viewport //------------------------------------------------------------------- internal void UpdateViewport(ref PTS.FSRECT viewport, bool drawBackground) { Rect contentViewport; // Transform point to PtsPage coordinate system. // NOTE: TransformToAncestor is safe (will never throw an exception). GeneralTransform transform = this.PageVisual.Child.TransformToAncestor(this.PageVisual); transform = transform.Inverse; contentViewport = viewport.FromTextDpi(); if (transform != null) { contentViewport = transform.TransformBounds(contentViewport); } if(!IsDisposed) { // Draw background if (drawBackground) { this.PageVisual.DrawBackground((Brush)_structuralCache.PropertyOwner.GetValue(FlowDocument.BackgroundProperty), contentViewport); } using (_structuralCache.SetDocumentVisualValidationContext(this)) { PTS.FSRECT contentViewportTextDpi = new PTS.FSRECT(contentViewport); _ptsPage.UpdateViewport(ref contentViewportTextDpi); _structuralCache.DetectInvalidOperation(); } ValidateTextView(); } }
internal override void FormatFloaterContentFinite( FloaterBaseParaClient paraClient, // IN: IntPtr pbrkrecIn, // IN: break record---use if !IntPtr.Zero int fBRFromPreviousPage, // IN: break record was created on previous page IntPtr footnoteRejector, // IN: int fEmptyOk, // IN: is it OK not to add anything? int fSuppressTopSpace, // IN: suppress empty space at the top of the page uint fswdir, // IN: direction of Track int fAtMaxWidth, // IN: formating is at full width of column int durAvailable, // IN: width of available space int dvrAvailable, // IN: height of available space PTS.FSKSUPPRESSHARDBREAKBEFOREFIRSTPARA fsksuppresshardbreakbeforefirstparaIn, // IN: suppress breaks at track start? out PTS.FSFMTR fsfmtr, // OUT: result of formatting out IntPtr pfsFloatContent, // OUT: opaque for PTS pointer pointer to formatted content out IntPtr pbrkrecOut, // OUT: pointer to the floater content break record out int durFloaterWidth, // OUT: floater width out int dvrFloaterHeight, // OUT: floater height out PTS.FSBBOX fsbbox, // OUT: floater bbox out int cPolygons, // OUT: number of polygons out int cVertices) // OUT: total number of vertices in all polygons { uint fswdirPara = PTS.FlowDirectionToFswdir(((FlowDirection)Element.GetValue(FrameworkElement.FlowDirectionProperty))); int subpageWidth, subpageHeight; int dvrTopSpace; int cColumns; PTS.FSRECT fsrcSubpageMargin; PTS.FSCOLUMNINFO[] columnInfoCollection; IntPtr pmcsclientOut; double specifiedWidth; MbpInfo mbp; Invariant.Assert(paraClient is FloaterParaClient); // If horizontal alignment is Stretch and we are not formatting at max width, // we cannot proceed. if (IsFloaterRejected(PTS.ToBoolean(fAtMaxWidth), TextDpi.FromTextDpi(durAvailable))) { durFloaterWidth = dvrFloaterHeight = 0; cPolygons = cVertices = 0; fsfmtr = new PTS.FSFMTR(); fsfmtr.kstop = PTS.FSFMTRKSTOP.fmtrNoProgressOutOfSpace; fsfmtr.fContainsItemThatStoppedBeforeFootnote = PTS.False; fsfmtr.fForcedProgress = PTS.False; fsbbox = new PTS.FSBBOX(); fsbbox.fDefined = PTS.False; pbrkrecOut = IntPtr.Zero; pfsFloatContent = IntPtr.Zero; } else { // When formatting bottomless page, PTS may format paragraphs as finite. This happens // in case of multiple columns. In this case make sure that height is not too big. if (!StructuralCache.CurrentFormatContext.FinitePage) { if (Double.IsInfinity(StructuralCache.CurrentFormatContext.PageHeight)) { if (dvrAvailable > PTS.dvBottomUndefined / 2) { dvrAvailable = Math.Min(dvrAvailable, PTS.dvBottomUndefined / 2); fEmptyOk = PTS.False; } } else { dvrAvailable = Math.Min(dvrAvailable, TextDpi.ToTextDpi(StructuralCache.CurrentFormatContext.PageHeight)); } } // Initialize the subpage size. PTS subpage margin is always set to 0 for Floaters. // If width on floater is specified, use the specified value. // Margin, border and padding of the floater is extracted from available subpage height mbp = MbpInfo.FromElement(Element); // We do not mirror margin as it's used to dist text left and right, and is unnecessary. // Clip Floater.Width to available width specifiedWidth = CalculateWidth(TextDpi.FromTextDpi(durAvailable)); AdjustDurAvailable(specifiedWidth, ref durAvailable, out subpageWidth); subpageHeight = Math.Max(1, dvrAvailable - (mbp.MBPTop + mbp.MBPBottom)); fsrcSubpageMargin = new PTS.FSRECT(); fsrcSubpageMargin.du = subpageWidth; fsrcSubpageMargin.dv = subpageHeight; // Initialize column info. Floater always has just 1 column. cColumns = 1; columnInfoCollection = new PTS.FSCOLUMNINFO[cColumns]; columnInfoCollection[0].durBefore = 0; columnInfoCollection[0].durWidth = subpageWidth; // Format subpage CreateSubpageFiniteHelper(PtsContext, pbrkrecIn, fBRFromPreviousPage, _mainTextSegment.Handle, footnoteRejector, fEmptyOk, PTS.True, fswdir, subpageWidth, subpageHeight, ref fsrcSubpageMargin, cColumns, columnInfoCollection, PTS.False, fsksuppresshardbreakbeforefirstparaIn, out fsfmtr, out pfsFloatContent, out pbrkrecOut, out dvrFloaterHeight, out fsbbox, out pmcsclientOut, out dvrTopSpace); // Initialize subpage metrics if (fsfmtr.kstop >= PTS.FSFMTRKSTOP.fmtrNoProgressOutOfSpace) // No progress or collision { Debug.Assert(pmcsclientOut == IntPtr.Zero); durFloaterWidth = dvrFloaterHeight = 0; cPolygons = cVertices = 0; //pbrkrecpara = IntPtr.Zero; } else { // PTS subpage does not support autosizing, but Floater needs to autosize to its // content. To workaround this problem, second format of subpage is performed, if // necessary. It means that if the width of bounding box is smaller than subpage's // width, second formatting is performed. // However, if HorizontalAlignment is set to Stretch we should not reformat because floater // should be at max width if (PTS.ToBoolean(fsbbox.fDefined)) { if(fsbbox.fsrc.du < subpageWidth && Double.IsNaN(specifiedWidth) && HorizontalAlignment != HorizontalAlignment.Stretch) { // There is a need to reformat PTS subpage, so destroy any resourcces allocated by PTS // during previous formatting. if (pfsFloatContent != IntPtr.Zero) { PTS.Validate(PTS.FsDestroySubpage(PtsContext.Context, pfsFloatContent), PtsContext); pfsFloatContent = IntPtr.Zero; } if (pbrkrecOut != IntPtr.Zero) { PTS.Validate(PTS.FsDestroySubpageBreakRecord(PtsContext.Context, pbrkrecOut), PtsContext); pbrkrecOut = IntPtr.Zero; } if (pmcsclientOut != IntPtr.Zero) { MarginCollapsingState mcs = PtsContext.HandleToObject(pmcsclientOut) as MarginCollapsingState; PTS.ValidateHandle(mcs); mcs.Dispose(); pmcsclientOut = IntPtr.Zero; } // Create subpage with new width. subpageWidth = fsbbox.fsrc.du + 1; // add 1/300px to avoid rounding errors fsrcSubpageMargin.du = subpageWidth; fsrcSubpageMargin.dv = subpageHeight; columnInfoCollection[0].durWidth = subpageWidth; CreateSubpageFiniteHelper(PtsContext, pbrkrecIn, fBRFromPreviousPage, _mainTextSegment.Handle, footnoteRejector, fEmptyOk, PTS.True, fswdir, subpageWidth, subpageHeight, ref fsrcSubpageMargin, cColumns, columnInfoCollection, PTS.False, fsksuppresshardbreakbeforefirstparaIn, out fsfmtr, out pfsFloatContent, out pbrkrecOut, out dvrFloaterHeight, out fsbbox, out pmcsclientOut, out dvrTopSpace); } } else { subpageWidth = TextDpi.ToTextDpi(TextDpi.MinWidth); } // Destroy objects created by PTS, but not used here. if (pmcsclientOut != IntPtr.Zero) { MarginCollapsingState mcs = PtsContext.HandleToObject(pmcsclientOut) as MarginCollapsingState; PTS.ValidateHandle(mcs); mcs.Dispose(); pmcsclientOut = IntPtr.Zero; } // Get the size of the floater. For height PTS already reports calculated value. // But width is the same as subpage width. Add margin values here since we do not use // distance to text anymore durFloaterWidth = subpageWidth + mbp.MBPLeft + mbp.MBPRight; // Add back all MBP values since we do not use dist to text dvrFloaterHeight += mbp.MBPTop + mbp.MBPBottom; // Check if floater width fits in available width. It may exceed available width because borders and // padding are added. fsbbox.fsrc.u = 0; fsbbox.fsrc.v = 0; fsbbox.fsrc.du = durFloaterWidth; fsbbox.fsrc.dv = dvrFloaterHeight; fsbbox.fDefined = PTS.True; // Tight wrap is disabled for now. cPolygons = cVertices = 0; if(durFloaterWidth > durAvailable || dvrFloaterHeight > dvrAvailable) { if(PTS.ToBoolean(fEmptyOk)) { // Get rid of any previous formatting if (pfsFloatContent != IntPtr.Zero) { PTS.Validate(PTS.FsDestroySubpage(PtsContext.Context, pfsFloatContent), PtsContext); pfsFloatContent = IntPtr.Zero; } if (pbrkrecOut != IntPtr.Zero) { PTS.Validate(PTS.FsDestroySubpageBreakRecord(PtsContext.Context, pbrkrecOut), PtsContext); pbrkrecOut = IntPtr.Zero; } cPolygons = cVertices = 0; fsfmtr.kstop = PTS.FSFMTRKSTOP.fmtrNoProgressOutOfSpace; } else { fsfmtr.fForcedProgress = PTS.True; } } } } // Update handle to PTS subpage. ((FloaterParaClient)paraClient).SubpageHandle = pfsFloatContent; }
internal void UpdateBottomlessPara( IntPtr pfspara, // IN: pointer to the para data ContainerParaClient paraClient, // IN: int iArea, // IN: column-span area index IntPtr pfsgeom, // IN: pointer to geometry int fSuppressTopSpace, // IN: suppress empty space at the top of the page uint fswdir, // IN: current direction int urTrack, // IN: u of bootomless rectangle to fill int durTrack, // IN: du of bootomless rectangle to fill int vrTrack, // IN: v of bootomless rectangle to fill MarginCollapsingState mcs, // IN: input margin collapsing state PTS.FSKCLEAR fskclearIn, // IN: clear property that must be satisfied int fInterruptable, // IN: formatting can be interrupted out PTS.FSFMTRBL fsfmtrbl, // OUT: result of formatting the paragraph out int dvrUsed, // OUT: vertical space used by the para out PTS.FSBBOX fsbbox, // OUT: para BBox out IntPtr pmcsclientOut, // OUT: margin collapsing state at the bottom out PTS.FSKCLEAR fskclearOut, // OUT: ClearIn for the next paragraph out int dvrTopSpace, // OUT: top space due to collapsed margin out int fPageBecomesUninterruptable)// OUT: interruption is prohibited from now on { uint fswdirSubtrack = PTS.FlowDirectionToFswdir(((FlowDirection)Element.GetValue(FrameworkElement.FlowDirectionProperty))); Debug.Assert(iArea == 0); // Top margin collapsing. int marginTop; MarginCollapsingState mcsContainer; MbpInfo mbp = MbpInfo.FromElement(Element); MarginCollapsingState.CollapseTopMargin(PtsContext, mbp, mcs, out mcsContainer, out marginTop); if (PTS.ToBoolean(fSuppressTopSpace)) { marginTop = 0; } // Set clear property Invariant.Assert(Element is Block || Element is ListItem); fskclearIn = PTS.WrapDirectionToFskclear((WrapDirection)Element.GetValue(Block.ClearFloatersProperty)); if(fswdirSubtrack != fswdir) { PTS.FSRECT fsrcToFillSubtrack = new PTS.FSRECT(urTrack, 0, durTrack, 0); PTS.FSRECT pageRect = StructuralCache.CurrentFormatContext.PageRect; PTS.Validate(PTS.FsTransformRectangle(fswdir, ref pageRect, ref fsrcToFillSubtrack, fswdirSubtrack, out fsrcToFillSubtrack)); urTrack = fsrcToFillSubtrack.u; durTrack = fsrcToFillSubtrack.du; mbp.MirrorMargin(); } // Take into accound MBPs and modify subtrack metrics, // and make sure that subtrack is at least 1 unit wide (cannot measure at width <= 0) int urSubtrack, durSubtrack, vrSubtrack; int dvrSubTrackTopSpace = 0; urSubtrack = Math.Max(Math.Min(urTrack + mbp.MBPLeft, urTrack + durTrack - 1), urTrack); durSubtrack = Math.Max(durTrack - (mbp.MBPLeft + mbp.MBPRight), 0); vrSubtrack = vrTrack + (marginTop + mbp.BPTop); // Format subtrack try { PTS.Validate(PTS.FsUpdateBottomlessSubtrack(PtsContext.Context, pfspara, this.Handle, iArea, pfsgeom, fSuppressTopSpace, fswdirSubtrack, urSubtrack, durSubtrack, vrSubtrack, (mcsContainer != null) ? mcsContainer.Handle : IntPtr.Zero, fskclearIn, fInterruptable, out fsfmtrbl, out dvrUsed, out fsbbox, out pmcsclientOut, out fskclearOut, out dvrSubTrackTopSpace, out fPageBecomesUninterruptable), PtsContext); } finally { // Destroy top margin collapsing state (not needed anymore). if (mcsContainer != null) { mcsContainer.Dispose(); mcsContainer = null; } } if (fsfmtrbl != PTS.FSFMTRBL.fmtrblCollision) { // Bottom margin collapsing: // (1) retrieve mcs from the subtrack // (2) do margin collapsing; create a new margin collapsing state if (pmcsclientOut != IntPtr.Zero) { mcsContainer = PtsContext.HandleToObject(pmcsclientOut) as MarginCollapsingState; PTS.ValidateHandle(mcsContainer); pmcsclientOut = IntPtr.Zero; } int marginBottom; MarginCollapsingState mcsNew; MarginCollapsingState.CollapseBottomMargin(PtsContext, mbp, mcsContainer, out mcsNew, out marginBottom); pmcsclientOut = (mcsNew != null) ? mcsNew.Handle : IntPtr.Zero; // Since MCS returned by PTS is never passed back, destroy MCS provided by PTS. // If necessary, new MCS is created and passed back to PTS. if (mcsContainer != null) { mcsContainer.Dispose(); mcsContainer = null; } // Take into accound MBPs and modify subtrack metrics dvrTopSpace = (mbp.BPTop != 0) ? marginTop : dvrSubTrackTopSpace; dvrUsed += (vrSubtrack - vrTrack) + marginBottom + mbp.BPBottom; } else { Debug.Assert(pmcsclientOut == IntPtr.Zero); pfspara = IntPtr.Zero; dvrTopSpace = 0; } // Adjust fsbbox to account for margins fsbbox.fsrc.u -= mbp.MBPLeft; fsbbox.fsrc.du += mbp.MBPLeft + mbp.MBPRight; if(fswdirSubtrack != fswdir) { PTS.FSRECT pageRect = StructuralCache.CurrentFormatContext.PageRect; PTS.Validate(PTS.FsTransformBbox(fswdirSubtrack, ref pageRect, ref fsbbox, fswdir, out fsbbox)); } // Update information about first/last chunk. In bottomless scenario // paragraph is not broken, so there is only one chunk. paraClient.SetChunkInfo(true, true); }