Bug 1833244 p1. Create infrastructure to pass page dimensions to PrintTarget::BeginPage. r=dholbert,geckoview-reviewers,jonalmeida

OS print drivers/devices know nothing about page dimensions unless we tell
them. Previously, the physical page dimensions (including orientation) have
always been the same, so communicating their dimensions once at the start of
a print has been enough. In preparation for supporting different "physical"
page dimensions (in the immediate future only different page orientations) when
we save to PDF, we need to have the infrastructure to pass dimensions through
on a page-by-page basis. This patch adds that.

None of the PrintTarget subclasses do anything with this extra information yet,
but in a follow-up patch PrintTargetPDF will use this information to create
PDFs with mixed page orientations.

Differential Revision: https://phabricator.services.mozilla.com/D179423
This commit is contained in:
Jonathan Watt 2023-07-10 14:26:12 +00:00
parent 5c71a93e59
commit 07bafd2423
26 changed files with 118 additions and 41 deletions

View file

@ -327,14 +327,14 @@ nsresult nsDeviceContext::AbortDocument() {
return rv;
}
nsresult nsDeviceContext::BeginPage() {
nsresult nsDeviceContext::BeginPage(const IntSize& aSizeInPoints) {
MOZ_DIAGNOSTIC_ASSERT(!mIsCurrentlyPrintingDoc || mPrintTarget,
"What nulled out our print target while printing?");
if (mDeviceContextSpec) {
MOZ_TRY(mDeviceContextSpec->BeginPage());
MOZ_TRY(mDeviceContextSpec->BeginPage(aSizeInPoints));
}
if (mPrintTarget) {
MOZ_TRY(mPrintTarget->BeginPage());
MOZ_TRY(mPrintTarget->BeginPage(aSizeInPoints));
}
return NS_OK;
}

View file

@ -20,6 +20,7 @@
#include "nscore.h" // for char16_t, nsAString
#include "mozilla/AppUnits.h" // for AppUnits
#include "nsFontMetrics.h" // for nsFontMetrics::Params
#include "mozilla/gfx/Point.h" // for IntSize
#include "mozilla/gfx/PrintTarget.h" // for PrintTarget::PageDoneCallback
#include "mozilla/gfx/PrintPromise.h"
@ -48,7 +49,8 @@ class Screen;
class nsDeviceContext final {
public:
typedef mozilla::gfx::PrintTarget PrintTarget;
using IntSize = mozilla::gfx::IntSize;
using PrintTarget = mozilla::gfx::PrintTarget;
nsDeviceContext();
@ -223,9 +225,17 @@ class nsDeviceContext final {
* Inform the output device that output of a page is beginning
* Used for print related device contexts. Must be matched 1:1 with
* EndPage() and within a BeginDocument()/EndDocument() pair.
*
* @param aSizeInPoints - The physical dimensions of the page in points.
* Currently only supported (used) by print-to-PDF
* print targets, and then only to switch the
* orientation for a specific page (arbitrary page
* sizes are not supported by the Core Graphics print-
* to-PDF APIs, for example).
*
* @return error status
*/
nsresult BeginPage();
nsresult BeginPage(const IntSize& aSizeInPoints);
/**
* Inform the output device that output of a page is ending

View file

@ -43,7 +43,11 @@ class PrintTarget {
#endif
return NS_OK;
}
virtual nsresult BeginPage() {
/**
* Note: not all print devices implement mixed page sizing. Most PrintTarget
* subclasses will ignore `aSizeInPoints`.
*/
virtual nsresult BeginPage(const IntSize& aSizeInPoints) {
#ifdef DEBUG
MOZ_ASSERT(!mHasActivePage, "Missing EndPage() call");
mHasActivePage = true;

View file

@ -28,7 +28,7 @@ class PrintTargetCG final : public PrintTarget {
int32_t aEndPage) final;
nsresult EndPrinting() final;
nsresult AbortPrinting() final;
nsresult BeginPage() final;
nsresult BeginPage(const IntSize& aSizeInPoints) final;
nsresult EndPage() final;
already_AddRefed<DrawTarget> GetReferenceDrawTarget() final;

View file

@ -210,7 +210,7 @@ nsresult PrintTargetCG::AbortPrinting() {
return EndPrinting();
}
nsresult PrintTargetCG::BeginPage() {
nsresult PrintTargetCG::BeginPage(const IntSize& aSizeInPoints) {
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
CGContextRef context;
@ -249,7 +249,7 @@ nsresult PrintTargetCG::BeginPage() {
mCairoSurface = surface;
return PrintTarget::BeginPage();
return PrintTarget::BeginPage(aSizeInPoints);
NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
}

View file

@ -54,10 +54,11 @@ nsresult PrintTargetSkPDF::BeginPrinting(const nsAString& aTitle,
return mPDFDoc ? NS_OK : NS_ERROR_FAILURE;
}
nsresult PrintTargetSkPDF::BeginPage() {
nsresult PrintTargetSkPDF::BeginPage(const IntSize& aSizeInPoints) {
mPageCanvas = mPDFDoc->beginPage(mSize.width, mSize.height);
return !mPageCanvas ? NS_ERROR_FAILURE : PrintTarget::BeginPage();
return !mPageCanvas ? NS_ERROR_FAILURE
: PrintTarget::BeginPage(aSizeInPoints);
}
nsresult PrintTargetSkPDF::EndPage() {

View file

@ -33,7 +33,7 @@ class PrintTargetSkPDF final : public PrintTarget {
nsresult EndPrinting() override;
void Finish() override;
nsresult BeginPage() override;
nsresult BeginPage(const IntSize& aSizeInPoints) override;
nsresult EndPage() override;
already_AddRefed<DrawTarget> MakeDrawTarget(

View file

@ -79,7 +79,7 @@ nsresult PrintTargetThebes::AbortPrinting() {
return mGfxSurface->AbortPrinting();
}
nsresult PrintTargetThebes::BeginPage() {
nsresult PrintTargetThebes::BeginPage(const IntSize& aSizeInPoints) {
#ifdef DEBUG
mHasActivePage = true;
#endif

View file

@ -33,7 +33,7 @@ class PrintTargetThebes final : public PrintTarget {
int32_t aEndPage) override;
nsresult EndPrinting() override;
nsresult AbortPrinting() override;
nsresult BeginPage() override;
nsresult BeginPage(const IntSize& aSizeInPoints) override;
nsresult EndPage() override;
void Finish() override;

View file

@ -103,8 +103,8 @@ nsresult PrintTargetWindows::AbortPrinting() {
return (result <= 0) ? NS_ERROR_FAILURE : NS_OK;
}
nsresult PrintTargetWindows::BeginPage() {
PrintTarget::BeginPage();
nsresult PrintTargetWindows::BeginPage(const IntSize& aSizeInPoints) {
PrintTarget::BeginPage(aSizeInPoints);
int result = ::StartPage(mDC);
return (result <= 0) ? NS_ERROR_FAILURE : NS_OK;
}

View file

@ -26,7 +26,7 @@ class PrintTargetWindows final : public PrintTarget {
int32_t aEndPage) override;
nsresult EndPrinting() override;
nsresult AbortPrinting() override;
nsresult BeginPage() override;
nsresult BeginPage(const IntSize& aSizeInPoints) override;
nsresult EndPage() override;
private:

View file

@ -298,6 +298,16 @@ void PrintedSheetFrame::ComputePagesPerSheetGridMetrics(
mGridCellHeight = availSpaceOnSheet.height / nscoord(numRows);
}
gfx::IntSize PrintedSheetFrame::GetPrintTargetSizeInPoints(
const int32_t aAppUnitsPerPhysicalInch) const {
const auto size = GetSize();
MOZ_ASSERT(size.width > 0 && size.height > 0);
const float pointsPerAppUnit =
POINTS_PER_INCH_FLOAT / float(aAppUnitsPerPhysicalInch);
return IntSize::Ceil(float(size.width) * pointsPerAppUnit,
float(size.height) * pointsPerAppUnit);
}
#ifdef DEBUG_FRAME_DUMP
nsresult PrintedSheetFrame::GetFrameName(nsAString& aResult) const {
return MakeFrameName(u"PrintedSheet"_ns, aResult);

View file

@ -9,6 +9,7 @@
#ifndef LAYOUT_GENERIC_PRINTEDSHEETFRAME_H_
#define LAYOUT_GENERIC_PRINTEDSHEETFRAME_H_
#include "mozilla/gfx/Point.h"
#include "nsContainerFrame.h"
#include "nsHTMLParts.h"
@ -18,6 +19,8 @@ namespace mozilla {
class PrintedSheetFrame final : public nsContainerFrame {
public:
using IntSize = mozilla::gfx::IntSize;
NS_DECL_QUERYFRAME
NS_DECL_FRAMEARENA_HELPERS(PrintedSheetFrame)
@ -71,6 +74,21 @@ class PrintedSheetFrame final : public nsContainerFrame {
nsSize PrecomputeSheetSize(const nsPresContext* aPresContext);
nsSize GetPrecomputedSheetSize() const { return mPrecomputedSize; }
/**
* This method returns the dimensions of the physical page that the target
* [pseudo-]printer should create. This may be different from our own
* dimensions in the case where CSS `page-orientation` causes us to be
* rotated, but we only support that if the PrintTarget backend supports
* different page sizes/orientations. That's only the case for our Save-to-PDF
* backends (possibly other save-to-file outputs in future).
*
* The dimensions returned are expected to be passed to
* nsDeviceContext::BeginPage, which will pass them on to
* PrintTarget::BeginPage to use as the physical dimensions of the page.
*/
IntSize GetPrintTargetSizeInPoints(
const int32_t aAppUnitsPerPhysicalInch) const;
private:
// Private construtor & destructor, to avoid accidental (non-FrameArena)
// instantiation/deletion:

View file

@ -11,6 +11,7 @@
#include "mozilla/PresShell.h"
#include "mozilla/PrintedSheetFrame.h"
#include "mozilla/dom/HTMLCanvasElement.h"
#include "mozilla/gfx/Point.h"
#include "mozilla/StaticPresData.h"
#include "nsCOMPtr.h"
@ -584,7 +585,10 @@ nsresult nsPageSequenceFrame::PrePrintNextSheet(nsITimerCallback* aCallback,
nsDeviceContext* dc = PresContext()->DeviceContext();
PR_PL(("\n"));
PR_PL(("***************** BeginPage *****************\n"));
rv = dc->BeginPage();
const gfx::IntSize sizeInPoints =
currentSheet->GetPrintTargetSizeInPoints(
dc->AppUnitsPerPhysicalInch());
rv = dc->BeginPage(sizeInPoints);
NS_ENSURE_SUCCESS(rv, rv);
mCalledBeginPage = true;
@ -675,7 +679,10 @@ nsresult nsPageSequenceFrame::PrintNextSheet() {
// page otherwise.
PR_PL(("\n"));
PR_PL(("***************** BeginPage *****************\n"));
rv = dc->BeginPage();
const gfx::IntSize sizeInPoints =
currentSheetFrame->GetPrintTargetSizeInPoints(
dc->AppUnitsPerPhysicalInch());
rv = dc->BeginPage(sizeInPoints);
NS_ENSURE_SUCCESS(rv, rv);
}
}

View file

@ -25,7 +25,7 @@ parent:
// Translate the page recording writen into |fd| and play back the events to
// the real print device.
async ProcessPage(uint64_t[] deps);
async ProcessPage(int32_t aWidthInPoints, int32_t aHeightInPoints, uint64_t[] aDeps);
// This informs the real print device that we've finished, so it can trigger
// the actual print.

View file

@ -56,12 +56,14 @@ void RemotePrintJobChild::SetNextPageFD(
mNextPageFD = PR_ImportFile(PROsfd(handle.release()));
}
void RemotePrintJobChild::ProcessPage(nsTArray<uint64_t>&& aDeps) {
void RemotePrintJobChild::ProcessPage(const IntSize& aSizeInPoints,
nsTArray<uint64_t>&& aDeps) {
MOZ_ASSERT(mPagePrintTimer);
mPagePrintTimer->WaitForRemotePrint();
if (!mDestroyed) {
Unused << SendProcessPage(std::move(aDeps));
Unused << SendProcessPage(aSizeInPoints.width, aSizeInPoints.height,
std::move(aDeps));
}
}

View file

@ -10,6 +10,7 @@
#include "mozilla/layout/PRemotePrintJobChild.h"
#include "mozilla/RefPtr.h"
#include "mozilla/gfx/Point.h"
#include "nsIWebProgressListener.h"
class nsPagePrintTimer;
@ -21,6 +22,8 @@ namespace layout {
class RemotePrintJobChild final : public PRemotePrintJobChild,
public nsIWebProgressListener {
public:
using IntSize = mozilla::gfx::IntSize;
NS_DECL_ISUPPORTS
NS_DECL_NSIWEBPROGRESSLISTENER
@ -34,7 +37,7 @@ class RemotePrintJobChild final : public PRemotePrintJobChild,
mozilla::ipc::IPCResult RecvPrintInitializationResult(
const nsresult& aRv, const FileDescriptor& aFd) final;
void ProcessPage(nsTArray<uint64_t>&& aDeps);
void ProcessPage(const IntSize& aSizeInPoints, nsTArray<uint64_t>&& aDeps);
mozilla::ipc::IPCResult RecvPageProcessed(const FileDescriptor& aFd) final;

View file

@ -106,6 +106,7 @@ nsresult RemotePrintJobParent::PrepareNextPageFD(FileDescriptor* aFd) {
}
mozilla::ipc::IPCResult RemotePrintJobParent::RecvProcessPage(
const int32_t& aWidthInPoints, const int32_t& aHeightInPoints,
nsTArray<uint64_t>&& aDeps) {
if (!mCurrentPageStream.IsOpen()) {
Unused << SendAbortPrint(NS_ERROR_FAILURE);
@ -113,8 +114,10 @@ mozilla::ipc::IPCResult RemotePrintJobParent::RecvProcessPage(
}
mCurrentPageStream.Seek(0, PR_SEEK_SET);
gfx::IntSize pageSizeInPoints(aWidthInPoints, aHeightInPoints);
if (aDeps.IsEmpty()) {
FinishProcessingPage();
FinishProcessingPage(pageSizeInPoints);
return IPC_OK();
}
@ -126,20 +129,21 @@ mozilla::ipc::IPCResult RemotePrintJobParent::RecvProcessPage(
gfx::CrossProcessPaint::Start(std::move(deps))
->Then(
GetCurrentSerialEventTarget(), __func__,
[self = RefPtr{this}](
[self = RefPtr{this}, pageSizeInPoints](
gfx::CrossProcessPaint::ResolvedFragmentMap&& aFragments) {
self->FinishProcessingPage(&aFragments);
self->FinishProcessingPage(pageSizeInPoints, &aFragments);
},
[self = RefPtr{this}](const nsresult& aRv) {
self->FinishProcessingPage();
[self = RefPtr{this}, pageSizeInPoints](const nsresult& aRv) {
self->FinishProcessingPage(pageSizeInPoints);
});
return IPC_OK();
}
void RemotePrintJobParent::FinishProcessingPage(
const gfx::IntSize& aSizeInPoints,
gfx::CrossProcessPaint::ResolvedFragmentMap* aFragments) {
nsresult rv = PrintPage(mCurrentPageStream, aFragments);
nsresult rv = PrintPage(aSizeInPoints, mCurrentPageStream, aFragments);
mCurrentPageStream.Close();
@ -147,11 +151,11 @@ void RemotePrintJobParent::FinishProcessingPage(
}
nsresult RemotePrintJobParent::PrintPage(
PRFileDescStream& aRecording,
const gfx::IntSize& aSizeInPoints, PRFileDescStream& aRecording,
gfx::CrossProcessPaint::ResolvedFragmentMap* aFragments) {
MOZ_ASSERT(mPrintDeviceContext);
nsresult rv = mPrintDeviceContext->BeginPage();
nsresult rv = mPrintDeviceContext->BeginPage(aSizeInPoints);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}

View file

@ -14,6 +14,7 @@
#include "nsCOMPtr.h"
#include "mozilla/RefPtr.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/gfx/Point.h"
#include "mozilla/gfx/RecordedEvent.h"
#include "mozilla/gfx/CrossProcessPaint.h"
@ -38,7 +39,9 @@ class RemotePrintJobParent final : public PRemotePrintJobParent {
const int32_t& aStartPage,
const int32_t& aEndPage) final;
mozilla::ipc::IPCResult RecvProcessPage(nsTArray<uint64_t>&& aDeps) final;
mozilla::ipc::IPCResult RecvProcessPage(const int32_t& aWidthInPoints,
const int32_t& aHeightInPoints,
nsTArray<uint64_t>&& aDeps) final;
mozilla::ipc::IPCResult RecvFinalizePrint() final;
@ -72,9 +75,10 @@ class RemotePrintJobParent final : public PRemotePrintJobParent {
nsresult PrepareNextPageFD(FileDescriptor* aFd);
nsresult PrintPage(
PRFileDescStream& aRecording,
const gfx::IntSize& aSizeInPoints, PRFileDescStream& aRecording,
gfx::CrossProcessPaint::ResolvedFragmentMap* aFragments = nullptr);
void FinishProcessingPage(
const gfx::IntSize& aSizeInPoints,
gfx::CrossProcessPaint::ResolvedFragmentMap* aFragments = nullptr);
/**

View file

@ -7,6 +7,7 @@
#include "nsIDeviceContextSpec.h"
#include "nsCOMPtr.h"
#include "mozilla/gfx/Point.h"
#include "mozilla/gfx/PrintPromise.h"
class nsDeviceContextSpecAndroid final : public nsIDeviceContextSpec {
@ -14,6 +15,8 @@ class nsDeviceContextSpecAndroid final : public nsIDeviceContextSpec {
virtual ~nsDeviceContextSpecAndroid();
public:
using IntSize = mozilla::gfx::IntSize;
NS_DECL_ISUPPORTS
already_AddRefed<PrintTarget> MakePrintTarget() final;
@ -23,7 +26,7 @@ class nsDeviceContextSpecAndroid final : public nsIDeviceContextSpec {
const nsAString& aPrintToFileName,
int32_t aStartPage, int32_t aEndPage) override;
RefPtr<mozilla::gfx::PrintEndDocumentPromise> EndDocument() override;
NS_IMETHOD BeginPage() override { return NS_OK; }
NS_IMETHOD BeginPage(const IntSize& aSizeInPoints) override { return NS_OK; }
NS_IMETHOD EndPage() override { return NS_OK; }
private:

View file

@ -28,7 +28,7 @@ class nsDeviceContextSpecX : public nsIDeviceContextSpec {
const nsAString& aPrintToFileName,
int32_t aStartPage, int32_t aEndPage) override;
RefPtr<mozilla::gfx::PrintEndDocumentPromise> EndDocument() override;
NS_IMETHOD BeginPage() override { return NS_OK; };
NS_IMETHOD BeginPage(const IntSize& aSizeInPoints) override { return NS_OK; };
NS_IMETHOD EndPage() override { return NS_OK; };
void GetPaperRect(double* aTop, double* aLeft, double* aBottom,

View file

@ -39,7 +39,7 @@ class nsDeviceContextSpecGTK : public nsIDeviceContextSpec {
const nsAString& aPrintToFileName,
int32_t aStartPage, int32_t aEndPage) override;
RefPtr<mozilla::gfx::PrintEndDocumentPromise> EndDocument() override;
NS_IMETHOD BeginPage() override { return NS_OK; }
NS_IMETHOD BeginPage(const IntSize& aSizeInPoints) override { return NS_OK; }
NS_IMETHOD EndPage() override { return NS_OK; }
protected:

View file

@ -130,13 +130,14 @@ nsDeviceContextSpecProxy::EndDocument() {
}
NS_IMETHODIMP
nsDeviceContextSpecProxy::BeginPage() {
nsDeviceContextSpecProxy::BeginPage(const IntSize& aSizeInPoints) {
if (!mRemotePrintJob || mRemotePrintJob->IsDestroyed()) {
mRemotePrintJob = nullptr;
return NS_ERROR_NOT_AVAILABLE;
}
mRecorder->OpenFD(mRemotePrintJob->GetNextPageFD());
mCurrentPageSizeInPoints = aSizeInPoints;
return NS_OK;
}
@ -150,7 +151,8 @@ nsDeviceContextSpecProxy::EndPage() {
// Send the page recording to the parent.
mRecorder->Close();
mRemotePrintJob->ProcessPage(std::move(mRecorder->TakeDependentSurfaces()));
mRemotePrintJob->ProcessPage(mCurrentPageSizeInPoints,
std::move(mRecorder->TakeDependentSurfaces()));
return NS_OK;
}

View file

@ -24,6 +24,7 @@ class RemotePrintJobChild;
class nsDeviceContextSpecProxy final : public nsIDeviceContextSpec {
public:
using IntSize = mozilla::gfx::IntSize;
using RemotePrintJobChild = mozilla::layout::RemotePrintJobChild;
explicit nsDeviceContextSpecProxy(RemotePrintJobChild* aRemotePrintJob);
@ -43,7 +44,7 @@ class nsDeviceContextSpecProxy final : public nsIDeviceContextSpec {
RefPtr<mozilla::gfx::PrintEndDocumentPromise> EndDocument() final;
NS_IMETHOD BeginPage() final;
NS_IMETHOD BeginPage(const IntSize& aSizeInPoints) final;
NS_IMETHOD EndPage() final;
@ -52,6 +53,7 @@ class nsDeviceContextSpecProxy final : public nsIDeviceContextSpec {
RefPtr<RemotePrintJobChild> mRemotePrintJob;
RefPtr<mozilla::layout::DrawEventRecorderPRFileDesc> mRecorder;
IntSize mCurrentPageSizeInPoints;
};
#endif // nsDeviceContextSpecProxy_h

View file

@ -9,6 +9,7 @@
#include "gfxPoint.h"
#include "nsISupports.h"
#include "mozilla/StaticPrefs_print.h"
#include "mozilla/gfx/Point.h"
#include "mozilla/gfx/PrintPromise.h"
#include "mozilla/MoveOnlyFunction.h"
@ -32,6 +33,7 @@ class PrintTarget;
class nsIDeviceContextSpec : public nsISupports {
public:
typedef mozilla::gfx::PrintTarget PrintTarget;
using IntSize = mozilla::gfx::IntSize;
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IDEVICE_CONTEXT_SPEC_IID)
@ -79,7 +81,12 @@ class nsIDeviceContextSpec : public nsISupports {
int32_t aStartPage, int32_t aEndPage) = 0;
virtual RefPtr<mozilla::gfx::PrintEndDocumentPromise> EndDocument() = 0;
NS_IMETHOD BeginPage() = 0;
/**
* Note: not all print devices implement mixed page sizing. Internally,
* aSizeInPoints gets handed off to a PrintTarget, and most PrintTarget
* subclasses will ignore `aSizeInPoints`.
*/
NS_IMETHOD BeginPage(const IntSize& aSizeInPoints) = 0;
NS_IMETHOD EndPage() = 0;
protected:

View file

@ -31,7 +31,7 @@ class nsDeviceContextSpecWin : public nsIDeviceContextSpec {
return NS_OK;
}
RefPtr<mozilla::gfx::PrintEndDocumentPromise> EndDocument() override;
NS_IMETHOD BeginPage() override { return NS_OK; }
NS_IMETHOD BeginPage(const IntSize& aSizeInPoints) override { return NS_OK; }
NS_IMETHOD EndPage() override { return NS_OK; }
NS_IMETHOD Init(nsIPrintSettings* aPS, bool aIsPrintPreview) override;