Bug 1651947 - Scale the page sequence in print preview so that pages don't overflow the viewport horizontally. r=dholbert

This is going to be useful for the new print preview UI, which is in a
doorhanger and thus much more likely to be less than the page size.

We (ab)use the existing print preview scaling mechanism. We only need it
after reflowing all pages, so this works.

This whole scaling mechanism is all-in-all not amazing, but the patch is
less gross than I initially thought. It's nice, actually.

We could put the new behavior behind a pref trivially, if that's wanted,
but I honestly thing this behavior is better even without the doorhanger
ui.

Differential Revision: https://phabricator.services.mozilla.com/D83309
This commit is contained in:
Emilio Cobos Álvarez 2020-07-15 11:16:43 +00:00
parent 375ba2f130
commit b769a57503
7 changed files with 102 additions and 83 deletions

View File

@ -3259,9 +3259,8 @@ nsDocumentViewer::PrintPreviewScrollToPage(int16_t aType, int32_t aPageNum) {
}
// in PP mPrtPreview->mPrintObject->mSeqFrame is null
nsIFrame* seqFrame = nullptr;
int32_t pageCount = 0;
if (NS_FAILED(mPrintJob->GetSeqFrameAndCountPages(seqFrame, pageCount))) {
auto [seqFrame, pageCount] = mPrintJob->GetSeqFrameAndCountPages();
if (!seqFrame) {
return NS_ERROR_FAILURE;
}
@ -3317,7 +3316,7 @@ nsDocumentViewer::PrintPreviewScrollToPage(int16_t aType, int32_t aPageNum) {
}
if (fndPageFrame) {
nscoord newYPosn = nscoord(mPrintJob->GetPrintPreviewScale() *
nscoord newYPosn = nscoord(seqFrame->GetPrintPreviewScale() *
fndPageFrame->GetPosition().y);
sf->ScrollTo(nsPoint(pt.x, newYPosn), ScrollMode::Instant);
}

View File

@ -453,14 +453,19 @@ class nsPresContext : public nsISupports,
void SetPageScale(float aScale) { mPageScale = aScale; }
/**
* Get/set the scaling facor to use when rendering the pages for print
* Get/set the scaling factor to use when rendering the pages for print
* preview. Only safe to get after print preview set up; safe to set anytime.
* This is a scaling factor for the display of the print preview. It
* does not affect layout. It only affects the size of the onscreen pages
* in print preview.
*
* The getter should only be used by the page sequence frame, which is the
* frame responsible for applying the scaling. Other callers should use
* nsPageSequenceFrame::GetPrintPreviewScale() if needed, instead of this API.
*
* XXX Temporary: see http://wiki.mozilla.org/Gecko:PrintPreview
*/
float GetPrintPreviewScale() { return mPPScale; }
float GetPrintPreviewScaleForSequenceFrame() { return mPPScale; }
void SetPrintPreviewScale(float aScale) { mPPScale = aScale; }
nsDeviceContext* DeviceContext() const { return mDeviceContext; }

View File

@ -82,22 +82,39 @@ NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
//----------------------------------------------------------------------
void nsPageSequenceFrame::SetDesiredSize(ReflowOutput& aDesiredSize,
const ReflowInput& aReflowInput,
nscoord aWidth, nscoord aHeight) {
// Aim to fill the whole size of the document, not only so we
// can act as a background in print preview but also handle overflow
// in child page frames correctly.
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);
}
}
return scale;
}
void nsPageSequenceFrame::PopulateReflowOutput(
ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput) {
// Aim to fill the whole available space, not only so we can act as a
// background in print preview but also handle overflow in child page frames
// correctly.
// Use availableISize so we don't cause a needless horizontal scrollbar.
float scale = GetPrintPreviewScale();
WritingMode wm = aReflowInput.GetWritingMode();
nscoord scaledWidth = aWidth * PresContext()->GetPrintPreviewScale();
nscoord scaledHeight = aHeight * PresContext()->GetPrintPreviewScale();
nscoord iSize = wm.IsVertical() ? mSize.Height() : mSize.Width();
nscoord bSize = wm.IsVertical() ? mSize.Width() : mSize.Height();
nscoord scaledISize = (wm.IsVertical() ? scaledHeight : scaledWidth);
nscoord scaledBSize = (wm.IsVertical() ? scaledWidth : scaledHeight);
aDesiredSize.ISize(wm) = std::max(scaledISize, aReflowInput.AvailableISize());
aDesiredSize.BSize(wm) = std::max(scaledBSize, aReflowInput.ComputedBSize());
aDesiredSize.ISize(wm) =
std::max(NSToCoordFloor(iSize * scale), aReflowInput.AvailableISize());
aDesiredSize.BSize(wm) =
std::max(NSToCoordFloor(bSize * scale), aReflowInput.ComputedBSize());
aDesiredSize.SetOverflowAreasToDesiredBounds();
}
// Helper function to compute the offset needed to center a child
@ -113,9 +130,9 @@ nscoord nsPageSequenceFrame::ComputeCenteringMargin(
// print-preview scale factor, via ComputePageSequenceTransform().
// We really want to center *that scaled-up rendering* inside of
// aContainerContentBoxWidth. So, we scale up its margin-box here...
auto ppScale = PresContext()->GetPrintPreviewScale();
float scale = GetPrintPreviewScale();
nscoord scaledChildMarginBoxWidth =
NSToCoordRound(childMarginBoxWidth * ppScale);
NSToCoordRound(childMarginBoxWidth * scale);
// ...and see we how much space is left over, when we subtract that scaled-up
// size from the container width:
@ -131,7 +148,7 @@ nscoord nsPageSequenceFrame::ComputeCenteringMargin(
// of the extra space. And then, we have to scale that space back down, so
// that it'll produce the correct scaled-up amount when we render (because
// rendering will scale it back up):
return NSToCoordRound(scaledExtraSpace * 0.5 / ppScale);
return NSToCoordRound(scaledExtraSpace * 0.5 / scale);
}
/*
@ -151,27 +168,30 @@ void nsPageSequenceFrame::Reflow(nsPresContext* aPresContext,
MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
NS_FRAME_TRACE_REFLOW_IN("nsPageSequenceFrame::Reflow");
auto CenterPages = [&] {
for (nsIFrame* child : mFrames) {
nsMargin pageCSSMargin = child->GetUsedMargin();
nscoord centeringMargin =
ComputeCenteringMargin(aReflowInput.ComputedWidth(),
child->GetRect().Width(), pageCSSMargin);
nscoord newX = pageCSSMargin.left + centeringMargin;
// Adjust the child's x-position:
child->MovePositionBy(nsPoint(newX - child->GetNormalPosition().x, 0));
}
};
mAvailableISize = aReflowInput.AvailableISize();
// Don't do incremental reflow until we've taught tables how to do
// it right in paginated mode.
if (!HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
// Return our desired size
SetDesiredSize(aDesiredSize, aReflowInput, mSize.width, mSize.height);
aDesiredSize.SetOverflowAreasToDesiredBounds();
PopulateReflowOutput(aDesiredSize, aReflowInput);
FinishAndStoreOverflow(&aDesiredSize);
if (GetRect().Width() != aDesiredSize.Width()) {
// Our width is changing; we need to re-center our children (our pages).
for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
nsIFrame* child = e.get();
nsMargin pageCSSMargin = child->GetUsedMargin();
nscoord centeringMargin =
ComputeCenteringMargin(aReflowInput.ComputedWidth(),
child->GetRect().Width(), pageCSSMargin);
nscoord newX = pageCSSMargin.left + centeringMargin;
// Adjust the child's x-position:
child->MovePositionBy(nsPoint(newX - child->GetNormalPosition().x, 0));
}
if (GetSize().Width() != aDesiredSize.Width()) {
CenterPages();
}
return;
}
@ -257,10 +277,6 @@ void nsPageSequenceFrame::Reflow(nsPresContext* aPresContext,
ReflowChild(kidFrame, aPresContext, kidSize, kidReflowInput, x, y,
ReflowChildFlags::Default, status);
// If the page is narrower than our width, then center it horizontally:
x += ComputeCenteringMargin(aReflowInput.ComputedWidth(), kidSize.Width(),
pageCSSMargin);
FinishReflowChild(kidFrame, aPresContext, kidSize, &kidReflowInput, x, y,
ReflowChildFlags::Default);
y += kidSize.Height();
@ -308,18 +324,19 @@ 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);
// Return our desired size
// Adjust the reflow size by PrintPreviewScale so the scrollbars end up the
// correct size
SetDesiredSize(aDesiredSize, aReflowInput, maxXMost, y);
PopulateReflowOutput(aDesiredSize, aReflowInput);
aDesiredSize.SetOverflowAreasToDesiredBounds();
FinishAndStoreOverflow(&aDesiredSize);
// cache the size so we can set the desired size
// for the other reflows that happen
mSize.width = maxXMost;
mSize.height = y;
// Now center our pages.
CenterPages();
NS_FRAME_TRACE_REFLOW_OUT("nsPageSequenceFrame::Reflow", aStatus);
NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
@ -662,9 +679,11 @@ nsresult nsPageSequenceFrame::DoPageEnd() {
return rv;
}
inline gfx::Matrix4x4 ComputePageSequenceTransform(nsIFrame* aFrame,
float aAppUnitsPerPixel) {
float scale = aFrame->PresContext()->GetPrintPreviewScale();
gfx::Matrix4x4 ComputePageSequenceTransform(nsIFrame* aFrame,
float aAppUnitsPerPixel) {
MOZ_ASSERT(aFrame->IsPageSequenceFrame());
float scale =
static_cast<nsPageSequenceFrame*>(aFrame)->GetPrintPreviewScale();
return gfx::Matrix4x4::Scaling(scale, scale, 1);
}
@ -684,7 +703,7 @@ void nsPageSequenceFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
nsIFrame* child = PrincipalChildList().FirstChild();
nsRect visible = aBuilder->GetVisibleRect();
visible.ScaleInverseRoundOut(PresContext()->GetPrintPreviewScale());
visible.ScaleInverseRoundOut(GetPrintPreviewScale());
while (child) {
if (child->GetVisualOverflowRectRelativeToParent().Intersects(visible)) {

View File

@ -73,6 +73,10 @@ class nsPageSequenceFrame final : public nsContainerFrame {
// For Shrink To Fit
float GetSTFPercent() const { return mPageData->mShrinkToFitRatio; }
// Gets the final print preview scale that we're applying to the previewed
// sheets of paper.
float GetPrintPreviewScale() const;
// Async Printing
nsresult StartPrint(nsPresContext* aPresContext,
nsIPrintSettings* aPrintSettings,
@ -112,11 +116,8 @@ class nsPageSequenceFrame final : public nsContainerFrame {
void SetDateTimeStr(const nsAString& aDateTimeStr);
void SetPageNumberFormat(const nsAString& aFormatStr, bool aForPageNumOnly);
// Sets the frame desired size to the size of the viewport, or the given
// nscoords, whichever is larger. Print scaling is applied in this function.
void SetDesiredSize(ReflowOutput& aDesiredSize,
const ReflowInput& aReflowInput, nscoord aWidth,
nscoord aHeight);
// Print scaling is applied in this function.
void PopulateReflowOutput(ReflowOutput&, const ReflowInput&);
// Helper function to compute the offset needed to center a child
// page-frame's margin-box inside our content-box.
@ -130,14 +131,17 @@ class nsPageSequenceFrame final : public nsContainerFrame {
nsMargin mMargin;
nsSize mSize;
nsSharedPageData* mPageData; // data shared by all the nsPageFrames
// Asynch Printing
// Async Printing
int32_t mPageNum;
int32_t mTotalPages;
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;

View File

@ -290,10 +290,11 @@ static nsPrintObject* FindPrintObjectByDOMWin(nsPrintObject* aPO,
return nullptr;
}
static nsresult GetSeqFrameAndCountPagesInternal(
const UniquePtr<nsPrintObject>& aPO, nsIFrame*& aSeqFrame,
int32_t& aCount) {
NS_ENSURE_ARG_POINTER(aPO);
static std::tuple<nsPageSequenceFrame*, int32_t>
GetSeqFrameAndCountPagesInternal(const UniquePtr<nsPrintObject>& aPO) {
if (!aPO) {
return {nullptr, 0};
}
// This is sometimes incorrectly called before the pres shell has been created
// (bug 1141756). MOZ_DIAGNOSTIC_ASSERT so we'll still see the crash in
@ -301,18 +302,16 @@ static nsresult GetSeqFrameAndCountPagesInternal(
if (!aPO->mPresShell) {
MOZ_DIAGNOSTIC_ASSERT(
false, "GetSeqFrameAndCountPages needs a non-null pres shell");
return NS_ERROR_FAILURE;
return {nullptr, 0};
}
aSeqFrame = aPO->mPresShell->GetPageSequenceFrame();
if (!aSeqFrame) {
return NS_ERROR_FAILURE;
nsPageSequenceFrame* seqFrame = aPO->mPresShell->GetPageSequenceFrame();
if (!seqFrame) {
return {nullptr, 0};
}
// count the total number of pages
aCount = aSeqFrame->PrincipalChildList().GetLength();
return NS_OK;
return {seqFrame, seqFrame->PrincipalChildList().GetLength()};
}
/**
@ -546,14 +545,14 @@ void nsPrintJob::SuppressPrintPreviewUserEvents() {
}
//-----------------------------------------------------------------
nsresult nsPrintJob::GetSeqFrameAndCountPages(nsIFrame*& aSeqFrame,
int32_t& aCount) {
std::tuple<nsPageSequenceFrame*, int32_t>
nsPrintJob::GetSeqFrameAndCountPages() {
MOZ_ASSERT(mPrtPreview);
// Guarantee that mPrintPreview->mPrintObject won't be deleted during a call
// of GetSeqFrameAndCountPagesInternal().
RefPtr<nsPrintData> printDataForPrintPreview = mPrtPreview;
return GetSeqFrameAndCountPagesInternal(
printDataForPrintPreview->mPrintObject, aSeqFrame, aCount);
printDataForPrintPreview->mPrintObject);
}
//---------------------------------------------------------------------------------
//-- Done: Methods needed by the DocViewer
@ -1030,13 +1029,9 @@ int32_t nsPrintJob::GetPrintPreviewNumPages() {
if (NS_WARN_IF(!printData)) {
return 0;
}
nsIFrame* seqFrame = nullptr;
int32_t numPages = 0;
nsresult rv = GetSeqFrameAndCountPagesInternal(printData->mPrintObject,
seqFrame, numPages);
if (NS_WARN_IF(NS_FAILED(rv))) {
return 0;
}
auto [seqFrame, numPages] =
GetSeqFrameAndCountPagesInternal(printData->mPrintObject);
Unused << seqFrame;
return numPages;
}

View File

@ -126,7 +126,7 @@ class nsPrintJob final : public nsIObserver,
bool GetIsPrintPreview() { return mIsDoingPrintPreview; }
bool GetIsCreatingPrintPreview() { return mIsCreatingPrintPreview; }
nsresult GetSeqFrameAndCountPages(nsIFrame*& aSeqFrame, int32_t& aCount);
std::tuple<nsPageSequenceFrame*, int32_t> GetSeqFrameAndCountPages();
void TurnScriptingOn(bool aDoTurnOn);
@ -145,10 +145,6 @@ class nsPrintJob final : public nsIObserver,
return mPrtPreview->mPrintObject->mPresShell;
}
float GetPrintPreviewScale() {
return mPrtPreview->mPrintObject->mPresContext->GetPrintPreviewScale();
}
nsresult Cancel();
void Destroy();
void DestroyPrintingData();

View File

@ -303,6 +303,7 @@
background: white;
box-shadow: 5px 5px 8px #202020;
box-decoration-break: clone;
/* TODO: Remove margin if viewport is small or something? */
margin: 0.125in 0.25in;
}