mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-21 01:05:45 +00:00
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:
parent
375ba2f130
commit
b769a57503
@ -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);
|
||||
}
|
||||
|
@ -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; }
|
||||
|
@ -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)) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user