Bug 1658173 part 2: Scale down print-preview so that every sheet fits into the scrollport, in both dimensions (not just the inline axis). r=emilio

Note: Previously we were scaling based on the available ISize (and we were
ignoring the BSize for scaling purposes).  Now we want to consider the BSize as
well, so the obvious next step would be to scale based on the available BSize -
but that doesn't work, because the available BSize is unconstrained here. (It's
unconstrained, even though we're fragmenting, because thankfully we don't
attempt to fragment the nsPageSequenceFrame itself -- we only fragment at a
level further down in the frame tree.)

So: this patch changes us to instead scale down based on the *computed* size
(rather than the available size), such that both dimensions fit.  The computed
size here is the size of the scrollport; and, importantly, it has a finite
value in the block axis (unlike the *available* BSize which is unconstrained
here).

Note that the available ISize and the computed ISize are the same in this case
(they're both the ISize of the scrollport), so there's no behavior-change here
for the computation of the inline-axis-based scale factor.

Differential Revision: https://phabricator.services.mozilla.com/D87021
This commit is contained in:
Daniel Holbert 2020-08-17 21:08:37 +00:00
parent 1cc2b45cc6
commit 4e78d2ce23
2 changed files with 76 additions and 17 deletions

View File

@ -50,6 +50,8 @@ NS_IMPL_FRAMEARENA_HELPERS(nsPageSequenceFrame)
nsPageSequenceFrame::nsPageSequenceFrame(ComputedStyle* aStyle,
nsPresContext* aPresContext)
: nsContainerFrame(aStyle, aPresContext, kClassID),
mMaxSheetSize(mWritingMode),
mScrollportSize(mWritingMode),
mTotalPages(-1),
mCalledBeginPage(false),
mCurrentCanvasListSetup(false) {
@ -79,16 +81,34 @@ NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
//----------------------------------------------------------------------
float nsPageSequenceFrame::GetPrintPreviewScale() const {
MOZ_DIAGNOSTIC_ASSERT(mAvailableISize >= 0, "Unset available width?");
nsPresContext* pc = PresContext();
float scale = pc->GetPrintPreviewScaleForSequenceFrame();
if (pc->IsScreen()) {
// For print preview, scale to the available size if needed.
nscoord iSize = GetWritingMode().IsVertical() ? mSize.height : mSize.width;
nscoord scaledISize = NSToCoordCeil(iSize * scale);
if (scaledISize > mAvailableISize) {
scale *= float(mAvailableISize) / float(scaledISize);
WritingMode wm = GetWritingMode();
if (pc->IsScreen() && MOZ_LIKELY(mScrollportSize.ISize(wm) > 0 &&
mScrollportSize.BSize(wm) > 0)) {
// For print preview, scale down as-needed to ensure that each of our
// sheets will fit in the the scrollport.
// Check if the current scale is sufficient for our sheets to fit in inline
// axis (and if not, reduce the scale so that it will fit).
nscoord scaledISize = NSToCoordCeil(mMaxSheetSize.ISize(wm) * scale);
if (scaledISize > mScrollportSize.ISize(wm)) {
scale *= float(mScrollportSize.ISize(wm)) / float(scaledISize);
}
// Further reduce the scale (if needed) to be sure each sheet will fit in
// block axis, too.
// NOTE: in general, a scrollport's BSize *could* be unconstrained,
// i.e. sized to its contents. If that happens, then shrinking the contents
// to fit the scrollport is not a meaningful operation in this axis, so we
// skip over this. But we can be pretty sure that the print-preview UI
// will have given the scrollport a fixed size; hence the MOZ_LIKELY here.
if (MOZ_LIKELY(mScrollportSize.BSize(wm) != NS_UNCONSTRAINEDSIZE)) {
nscoord scaledBSize = NSToCoordCeil(mMaxSheetSize.BSize(wm) * scale);
if (scaledBSize > mScrollportSize.BSize(wm)) {
scale *= float(mScrollportSize.BSize(wm)) / float(scaledBSize);
}
}
}
return scale;
@ -177,7 +197,12 @@ void nsPageSequenceFrame::Reflow(nsPresContext* aPresContext,
}
};
mAvailableISize = aReflowInput.AvailableISize();
if (aPresContext->IsScreen()) {
// When we're displayed on-screen, the computed size that we're given is
// the size of our scrollport. We need to save this for use in
// GetPrintPreviewScale.
mScrollportSize = aReflowInput.ComputedSize();
}
// Don't do incremental reflow until we've taught tables how to do
// it right in paginated mode.
@ -243,7 +268,11 @@ void nsPageSequenceFrame::Reflow(nsPresContext* aPresContext,
// to determine the space between each printed sheet in print preview.
// Keep a running y-offset for each printed sheet.
nscoord y = 0;
nscoord maxXMost = 0;
// These represent the maximum sheet size across all our sheets (in each
// axis), inflated a bit to account for the -moz-printed-sheet 'margin'.
nscoord maxInflatedSheetWidth = 0;
nscoord maxInflatedSheetHeight = 0;
// Tile the sheets vertically
for (nsIFrame* kidFrame : mFrames) {
@ -279,8 +308,12 @@ void nsPageSequenceFrame::Reflow(nsPresContext* aPresContext,
y += kidReflowOutput.Height();
y += pageCSSMargin.bottom;
maxXMost =
std::max(maxXMost, x + kidReflowOutput.Width() + pageCSSMargin.right);
maxInflatedSheetWidth =
std::max(maxInflatedSheetWidth,
kidReflowOutput.Width() + pageCSSMargin.LeftRight());
maxInflatedSheetHeight =
std::max(maxInflatedSheetHeight,
kidReflowOutput.Height() + pageCSSMargin.TopBottom());
// Is the sheet complete?
nsIFrame* kidNextInFlow = kidFrame->GetNextInFlow();
@ -331,9 +364,22 @@ void nsPageSequenceFrame::Reflow(nsPresContext* aPresContext,
SetDateTimeStr(formattedDateString);
}
// cache the size so we can set the desired size
// for the other reflows that happen
mSize = nsSize(maxXMost, y);
// cache the size so we can set the desired size for the other reflows that
// happen. Since we're tiling our sheets vertically: in the x axis, we are
// as wide as our widest sheet (inflated via "margin"); and in the y axis,
// we're as tall as the sum of our sheets' inflated heights, which the 'y'
// variable is conveniently storing at this point.
mSize = nsSize(maxInflatedSheetWidth, y);
if (aPresContext->IsScreen()) {
// Also cache the maximum size of all our sheets, to use together with the
// scrollport size (available as our computed size, and captured higher up
// in this function), so that we can scale to ensure that every sheet will
// fit in the scrollport.
WritingMode wm = aReflowInput.GetWritingMode();
mMaxSheetSize =
LogicalSize(wm, nsSize(maxInflatedSheetWidth, maxInflatedSheetHeight));
}
// Return our desired size
// Adjust the reflow size by PrintPreviewScale so the scrollbars end up the

View File

@ -136,6 +136,20 @@ class nsPageSequenceFrame final : public nsContainerFrame {
nsSize mSize;
// These next two LogicalSize members are used when we're in print-preview to
// ensure that each previewed sheet will fit in the print-preview scrollport:
// -------
// Each component of this LogicalSize represents the maximum length of all
// our print-previewed sheets in that axis, plus a little extra for the
// print-preview margin. Note that this LogicalSize doesn't necessarily
// correspond to any one particular sheet's size (especially if our sheets
// have different sizes), since the components are tracked independently such
// that we end up storing the maximum in each dimension.
LogicalSize mMaxSheetSize;
// The size of the scrollport where we're print-previewing sheets.
LogicalSize mScrollportSize;
// Data shared by all the nsPageFrames:
mozilla::UniquePtr<nsSharedPageData> mPageData;
@ -145,8 +159,7 @@ class nsPageSequenceFrame final : public nsContainerFrame {
int32_t mPrintRangeType;
int32_t mFromPageNum;
int32_t mToPageNum;
// The size we need to shrink-to-fit our previewed sheets of paper against.
nscoord mAvailableISize = -1;
nsTArray<int32_t> mPageRanges;
nsTArray<RefPtr<mozilla::dom::HTMLCanvasElement> > mCurrentCanvasList;