/// <summary> /// Assess how to handle a page break. /// </summary> public override PageDisposition AssessPageBreak(Rectangle bodyBox) { using (new TraceContextPusher(_generator, _traceContext)) { Trace("_bounds={0} bodyBox={1}", _bounds, bodyBox); PageDisposition disposition = PageDisposition.ThisPage; // We never split the bullet, so if the bullet doesn't fit on the // page then we need to move entirely to the next page disposition = _bulletLayout.AssessPageBreak(bodyBox); if (disposition != PageDisposition.ThisPage) { return(PageDisposition.NewPage); } // We're happy to split our content and duplicate the bullet disposition = _contentLayout.AssessPageBreak(bodyBox); return(disposition); } // ListItemLayout doesn't have to worry about keep-with-next because // keep-with-next is implemented by the container layout, and the // list item layout doesn't really have sublayouts. The bullet and // content layouts are technically sublayouts but they don't function // as such. }
protected override PageDisposition SetPageSplitIndex(Rectangle bodyBox, ref bool honourKeepWithNext) { using (new TraceContextPusher(_generator, _traceContext)) { PageDisposition disposition = PageDisposition.ThisPage; if (_bounds.Bottom < bodyBox.Bottom) { disposition = PageDisposition.Overflow; } // Our bottom padding has already been factored in by Layout.AssessPageBreak return(disposition); } }
protected override PageDisposition SetPageSplitIndex(Rectangle bodyBox, ref bool honourKeepWithNext) { using (new TraceContextPusher(_generator, _traceContext)) { PageDisposition disposition = base.SetPageSplitIndex(bodyBox, ref honourKeepWithNext); // Don't split the header rows if (_pageSplitIndex < _headerRows) { _pageSplitIndex = _headerRows; } return(disposition); } }
public override PageDisposition AssessPageBreak(Rectangle bodyBox) { using (new TraceContextPusher(_generator, _traceContext)) { Trace("_bounds={0} bodyBox={1}", _bounds, bodyBox); PageDisposition disposition = PageDisposition.ThisPage; // If we don't fit on this page then split if (_bounds.Bottom < bodyBox.Bottom) { disposition = PageDisposition.Split; } Trace("return {0}, split index {1}", disposition, _pageSplitIndex); return(disposition); } }
public override PageDisposition AssessPageBreak(Rectangle bodyBox) { // We override this method here because a table row's sublayouts // are arranged horizontally, not vertically, and so their // individual dispositions interact with each other. For example, // if one cell wants to split but another refuses, then the row // cannot split. Or if any one cell wants to go on the next page // then the entire row goes on the next page. // // Keep-with-next is meaningless in a table row because the next // sublayout is another cell on the same row, and by definition // all cells start on the same page. using (new TraceContextPusher(_generator, _traceContext)) { Trace("_bounds={0} bodyBox={1}", _bounds, bodyBox); PageDisposition disposition = PageDisposition.ThisPage; // If we've got a new-page rule then put ourself on the next page if (_pageBreakRules?.NewPage ?? false) { Trace("page break rules say new-page, so set new-page disposition"); disposition = PageDisposition.NewPage; } if (disposition == PageDisposition.ThisPage) { // If we've got a max-position rule then check whether we're past // that position. And if we don't have a max-position rule then // assume a max position of 1.0 so that we don't position ourself // off the bottom of the page. float maxPosition = _pageBreakRules?.MaximumPosition ?? 1.0f; if (maxPosition > 1.0f) { maxPosition = 1.0f; } // Express the distance from the top of the body box to the top // of ourself, as a percentage of the body box height float position = ((float)(bodyBox.Top - _bounds.Top)) / (float)bodyBox.Height; if (position > maxPosition) { Trace( "max-position {0} rule is violated by bounds {1}, so set overflow disposition", maxPosition, _bounds); disposition = PageDisposition.Overflow; } } // If it looks like we can start on this page then ask our sublayouts // what they want to do. Even if we think that we can fit entirely on // this page, a sublayout could have rules that cause it to move to the // next page and that would cause us to split. // Min-lines is evaluated on splitting. if (disposition == PageDisposition.ThisPage) { List <PageDisposition> sublayoutDispositions = new List <PageDisposition>(); foreach (Layout sublayout in _subLayouts) { PageDisposition sublayoutDisposition = sublayout.AssessPageBreak(bodyBox); sublayoutDispositions.Add(sublayoutDisposition); } // If any one cell said next-page then put the entire row on the next page if (sublayoutDispositions.Contains(PageDisposition.NewPage)) { Trace("at least one cell says next-page, so set new-page disposition"); disposition = PageDisposition.NewPage; } else if (sublayoutDispositions.Contains(PageDisposition.Split)) { // If no cell said next-page, and any one said split, then split. // (If any cell couldn't fit on this page but wasn't prepared to // split then it would have said next-page. So we know that all // cells are content to split.) Trace("at least one cell says split, so set split disposition"); bool canSplit = CanSplit(bodyBox); if (canSplit) { disposition = PageDisposition.Split; } else { Trace("need to split but can't, so set overflow disposition"); _pageSplitIndex = 0; disposition = PageDisposition.Overflow; } } } Trace("return {0}, split index {1}", disposition, _pageSplitIndex); return(disposition); } }
private PageLayout ApplyPageBreak() { // Collapse any space layouts at the top of the page. Space layouts are // intended to provide space _between_ layouts and thus are not appropriate // at the top of a page. We collapse top space after drafting so that we know // where the space layouts fall on this page, but before applying the next // page break because lower layouts might now fit on this page. CollapseTopSpace(); PageDisposition disposition = AssessPageBreak(_bodyBox); // Avoid infinite overflow. If any layout starts at the top of the page and // is longer than the page, then it will return disposition overflow or split // and its page split index will be zero. Splitting at index zero is the same // thing as overflowing. Overflowing a full page will cause this page to move // all of its content onto a new page, leaving itself empty, and this will lead // to infinite overflow. So just stop the overflow right now. This will give us // an oversized page, but that's better than making a report with an infinite // number of empty pages. (In the current implementation, AssessPageBreak here on // a page layout will never return overflow because SetPageSplitIndex always // converts overflow to split, and then because a page can split it doesn't convert // it back to overflow. But for semantic clarity we still check for overflow here.) if ((disposition == PageDisposition.Overflow) || (disposition == PageDisposition.Split && _pageSplitIndex == 0)) { // If we can split as requested then do so. Otherwise bump the page split // index from zero to one - this will leave the oversized layout on this // page but overflow subsequent layouts to the next. If it's not possible // to bump the index then return an empty page to stop the overflow. Bump bump = BumpPageSplitIndex(); switch (bump) { case Bump.Bumped: case Bump.Unnecessary: // The split index has been moved beyond zero (or was already there) // and so we can now do the normal page break. This may still leave // the oversized layout on this page, but later layouts are moved // to the next page. break; case Bump.Impossible: // Couldn't fix the situation by splitting further down, so // just accept the oversized layout return((PageLayout)ShallowCopy()); } } PageLayout overflow = (PageLayout)DoPageBreak(); // This page now contains only the content that can fit in it. Content // that must be moved to the next page, or a later page, is now contained // in the overflow page. If the overflow page is empty then this is the last // page. // Redraft this page to set the layouts' bottoms so that they can // draw borders correctly, and redraft the new page so that its contents // start at the top of the page. Redraft(_bodyBox.Top); overflow?.Redraft(overflow._bodyBox.Top); return(overflow); }