/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsSimplePageSequenceFrame.h" #include "nsCOMPtr.h" #include "nsDeviceContext.h" #include "nsPresContext.h" #include "gfxContext.h" #include "nsRenderingContext.h" #include "nsGkAtoms.h" #include "nsIPresShell.h" #include "nsIPrintSettings.h" #include "nsPageFrame.h" #include "nsSubDocumentFrame.h" #include "nsRegion.h" #include "nsCSSFrameConstructor.h" #include "nsContentUtils.h" #include "nsDisplayList.h" #include "nsHTMLCanvasFrame.h" #include "mozilla/dom/HTMLCanvasElement.h" #include "nsICanvasRenderingContextInternal.h" #include "nsIDateTimeFormat.h" #include "nsServiceManagerUtils.h" #include #define OFFSET_NOT_SET -1 using namespace mozilla; using namespace mozilla::dom; #include "mozilla/Logging.h" mozilla::LazyLogModule gLayoutPrintingLog("printing-layout"); #define PR_PL(_p1) MOZ_LOG(gLayoutPrintingLog, mozilla::LogLevel::Debug, _p1) nsSimplePageSequenceFrame* NS_NewSimplePageSequenceFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { return new (aPresShell) nsSimplePageSequenceFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsSimplePageSequenceFrame) nsSimplePageSequenceFrame::nsSimplePageSequenceFrame(nsStyleContext* aContext) : nsContainerFrame(aContext), mTotalPages(-1), mSelectionHeight(-1), mYSelOffset(0), mCalledBeginPage(false), mCurrentCanvasListSetup(false) { nscoord halfInch = PresContext()->CSSTwipsToAppUnits(NS_INCHES_TO_TWIPS(0.5)); mMargin.SizeTo(halfInch, halfInch, halfInch, halfInch); // XXX Unsafe to assume successful allocation mPageData = new nsSharedPageData(); mPageData->mHeadFootFont = *PresContext()->GetDefaultFont(kGenericFont_serif, aContext->StyleFont()->mLanguage); mPageData->mHeadFootFont.size = nsPresContext::CSSPointsToAppUnits(10); // Doing this here so we only have to go get these formats once SetPageNumberFormat("pagenumber", "%1$d", true); SetPageNumberFormat("pageofpages", "%1$d of %2$d", false); } nsSimplePageSequenceFrame::~nsSimplePageSequenceFrame() { delete mPageData; ResetPrintCanvasList(); } NS_QUERYFRAME_HEAD(nsSimplePageSequenceFrame) NS_QUERYFRAME_ENTRY(nsIPageSequenceFrame) NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) //---------------------------------------------------------------------- void nsSimplePageSequenceFrame::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. // Use availableWidth so we don't cause a needless horizontal scrollbar. aDesiredSize.Width() = std::max(aReflowInput.AvailableWidth(), nscoord(aWidth * PresContext()->GetPrintPreviewScale())); aDesiredSize.Height() = std::max(aReflowInput.ComputedHeight(), nscoord(aHeight * PresContext()->GetPrintPreviewScale())); } // Helper function to compute the offset needed to center a child // page-frame's margin-box inside our content-box. nscoord nsSimplePageSequenceFrame::ComputeCenteringMargin( nscoord aContainerContentBoxWidth, nscoord aChildPaddingBoxWidth, const nsMargin& aChildPhysicalMargin) { // We'll be centering our child's margin-box, so get the size of that: nscoord childMarginBoxWidth = aChildPaddingBoxWidth + aChildPhysicalMargin.LeftRight(); // When rendered, our child's rect will actually be scaled up by the // 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(); nscoord scaledChildMarginBoxWidth = NSToCoordRound(childMarginBoxWidth * ppScale); // ...and see we how much space is left over, when we subtract that scaled-up // size from the container width: nscoord scaledExtraSpace = aContainerContentBoxWidth - scaledChildMarginBoxWidth; if (scaledExtraSpace <= 0) { // (Don't bother centering if there's zero/negative space.) return 0; } // To center the child, we want to give it an additional left-margin of half // 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); } void nsSimplePageSequenceFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) { MarkInReflow(); NS_PRECONDITION(aPresContext->IsRootPaginatedDocument(), "A Page Sequence is only for real pages"); DO_GLOBAL_REFLOW_COUNT("nsSimplePageSequenceFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); NS_FRAME_TRACE_REFLOW_IN("nsSimplePageSequenceFrame::Reflow"); aStatus = NS_FRAME_COMPLETE; // we're always complete // Don't do incremental reflow until we've taught tables how to do // it right in paginated mode. if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) { // Return our desired size SetDesiredSize(aDesiredSize, aReflowInput, mSize.width, mSize.height); aDesiredSize.SetOverflowAreasToDesiredBounds(); 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)); } } return; } // See if we can get a Print Settings from the Context if (!mPageData->mPrintSettings && aPresContext->Medium() == nsGkAtoms::print) { mPageData->mPrintSettings = aPresContext->GetPrintSettings(); } // now get out margins & edges if (mPageData->mPrintSettings) { nsIntMargin unwriteableTwips; mPageData->mPrintSettings->GetUnwriteableMarginInTwips(unwriteableTwips); NS_ASSERTION(unwriteableTwips.left >= 0 && unwriteableTwips.top >= 0 && unwriteableTwips.right >= 0 && unwriteableTwips.bottom >= 0, "Unwriteable twips should be non-negative"); nsIntMargin marginTwips; mPageData->mPrintSettings->GetMarginInTwips(marginTwips); mMargin = aPresContext->CSSTwipsToAppUnits(marginTwips + unwriteableTwips); int16_t printType; mPageData->mPrintSettings->GetPrintRange(&printType); mPrintRangeType = printType; nsIntMargin edgeTwips; mPageData->mPrintSettings->GetEdgeInTwips(edgeTwips); // sanity check the values. three inches are sometimes needed int32_t inchInTwips = NS_INCHES_TO_INT_TWIPS(3.0); edgeTwips.top = clamped(edgeTwips.top, 0, inchInTwips); edgeTwips.bottom = clamped(edgeTwips.bottom, 0, inchInTwips); edgeTwips.left = clamped(edgeTwips.left, 0, inchInTwips); edgeTwips.right = clamped(edgeTwips.right, 0, inchInTwips); mPageData->mEdgePaperMargin = aPresContext->CSSTwipsToAppUnits(edgeTwips + unwriteableTwips); } // *** Special Override *** // If this is a sub-sdoc (meaning it doesn't take the whole page) // and if this Document is in the upper left hand corner // we need to suppress the top margin or it will reflow too small nsSize pageSize = aPresContext->GetPageSize(); mPageData->mReflowSize = pageSize; // If we're printing a selection, we need to reflow with // unconstrained height, to make sure we'll get to the selection // even if it's beyond the first page of content. if (nsIPrintSettings::kRangeSelection == mPrintRangeType) { mPageData->mReflowSize.height = NS_UNCONSTRAINEDSIZE; } mPageData->mReflowMargin = mMargin; // We use the CSS "margin" property on the -moz-page pseudoelement // to determine the space between each page in print preview. // Keep a running y-offset for each page. nscoord y = 0; nscoord maxXMost = 0; // Tile the pages vertically ReflowOutput kidSize(aReflowInput); for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) { nsIFrame* kidFrame = e.get(); // Set the shared data into the page frame before reflow nsPageFrame * pf = static_cast(kidFrame); pf->SetSharedPageData(mPageData); // Reflow the page ReflowInput kidReflowInput(aPresContext, aReflowInput, kidFrame, LogicalSize(kidFrame->GetWritingMode(), pageSize)); nsReflowStatus status; kidReflowInput.SetComputedWidth(kidReflowInput.AvailableWidth()); //kidReflowInput.SetComputedHeight(kidReflowInput.AvailableHeight()); PR_PL(("AV W: %d H: %d\n", kidReflowInput.AvailableWidth(), kidReflowInput.AvailableHeight())); nsMargin pageCSSMargin = kidReflowInput.ComputedPhysicalMargin(); y += pageCSSMargin.top; nscoord x = pageCSSMargin.left; // Place and size the page. ReflowChild(kidFrame, aPresContext, kidSize, kidReflowInput, x, y, 0, status); // If the page is narrower than our width, then center it horizontally: x += ComputeCenteringMargin(aReflowInput.ComputedWidth(), kidSize.Width(), pageCSSMargin); FinishReflowChild(kidFrame, aPresContext, kidSize, nullptr, x, y, 0); y += kidSize.Height(); y += pageCSSMargin.bottom; maxXMost = std::max(maxXMost, x + kidSize.Width() + pageCSSMargin.right); // Is the page complete? nsIFrame* kidNextInFlow = kidFrame->GetNextInFlow(); if (NS_FRAME_IS_FULLY_COMPLETE(status)) { NS_ASSERTION(!kidNextInFlow, "bad child flow list"); } else if (!kidNextInFlow) { // The page isn't complete and it doesn't have a next-in-flow, so // create a continuing page. nsIFrame* continuingPage = aPresContext->PresShell()->FrameConstructor()-> CreateContinuingFrame(aPresContext, kidFrame, this); // Add it to our child list mFrames.InsertFrame(nullptr, kidFrame, continuingPage); } } // Get Total Page Count // XXXdholbert technically we could calculate this in the loop above, // instead of needing a separate walk. int32_t pageTot = mFrames.GetLength(); // Set Page Number Info int32_t pageNum = 1; for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) { MOZ_ASSERT(e.get()->GetType() == nsGkAtoms::pageFrame, "only expecting nsPageFrame children. Other children will make " "this static_cast bogus & probably violate other assumptions"); nsPageFrame* pf = static_cast(e.get()); pf->SetPageNumInfo(pageNum, pageTot); pageNum++; } // Create current Date/Time String if (!mDateFormatter) { mDateFormatter = nsIDateTimeFormat::Create(); } if (!mDateFormatter) { return; } nsAutoString formattedDateString; time_t ltime; time( <ime ); if (NS_SUCCEEDED(mDateFormatter->FormatTime(nullptr /* nsILocale* locale */, kDateFormatShort, kTimeFormatNoSeconds, ltime, formattedDateString))) { SetDateTimeStr(formattedDateString); } // Return our desired size // Adjust the reflow size by PrintPreviewScale so the scrollbars end up the // correct size SetDesiredSize(aDesiredSize, aReflowInput, maxXMost, y); 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; NS_FRAME_TRACE_REFLOW_OUT("nsSimplePageSequeceFrame::Reflow", aStatus); NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize); } //---------------------------------------------------------------------- #ifdef DEBUG_FRAME_DUMP nsresult nsSimplePageSequenceFrame::GetFrameName(nsAString& aResult) const { return MakeFrameName(NS_LITERAL_STRING("SimplePageSequence"), aResult); } #endif //==================================================================== //== Asynch Printing //==================================================================== NS_IMETHODIMP nsSimplePageSequenceFrame::GetCurrentPageNum(int32_t* aPageNum) { NS_ENSURE_ARG_POINTER(aPageNum); *aPageNum = mPageNum; return NS_OK; } NS_IMETHODIMP nsSimplePageSequenceFrame::GetNumPages(int32_t* aNumPages) { NS_ENSURE_ARG_POINTER(aNumPages); *aNumPages = mTotalPages; return NS_OK; } NS_IMETHODIMP nsSimplePageSequenceFrame::IsDoingPrintRange(bool* aDoing) { NS_ENSURE_ARG_POINTER(aDoing); *aDoing = mDoingPageRange; return NS_OK; } NS_IMETHODIMP nsSimplePageSequenceFrame::GetPrintRange(int32_t* aFromPage, int32_t* aToPage) { NS_ENSURE_ARG_POINTER(aFromPage); NS_ENSURE_ARG_POINTER(aToPage); *aFromPage = mFromPageNum; *aToPage = mToPageNum; return NS_OK; } // Helper Function void nsSimplePageSequenceFrame::SetPageNumberFormat(const char* aPropName, const char* aDefPropVal, bool aPageNumOnly) { // Doing this here so we only have to go get these formats once nsXPIDLString pageNumberFormat; // Now go get the Localized Page Formating String nsresult rv = nsContentUtils::GetLocalizedString(nsContentUtils::ePRINTING_PROPERTIES, aPropName, pageNumberFormat); if (NS_FAILED(rv)) { // back stop formatting pageNumberFormat.AssignASCII(aDefPropVal); } SetPageNumberFormat(pageNumberFormat, aPageNumOnly); } NS_IMETHODIMP nsSimplePageSequenceFrame::StartPrint(nsPresContext* aPresContext, nsIPrintSettings* aPrintSettings, const nsAString& aDocTitle, const nsAString& aDocURL) { NS_ENSURE_ARG_POINTER(aPresContext); NS_ENSURE_ARG_POINTER(aPrintSettings); if (!mPageData->mPrintSettings) { mPageData->mPrintSettings = aPrintSettings; } if (!aDocTitle.IsEmpty()) { mPageData->mDocTitle = aDocTitle; } if (!aDocURL.IsEmpty()) { mPageData->mDocURL = aDocURL; } aPrintSettings->GetStartPageRange(&mFromPageNum); aPrintSettings->GetEndPageRange(&mToPageNum); aPrintSettings->GetPageRanges(mPageRanges); mDoingPageRange = nsIPrintSettings::kRangeSpecifiedPageRange == mPrintRangeType || nsIPrintSettings::kRangeSelection == mPrintRangeType; // If printing a range of pages make sure at least the starting page // number is valid int32_t totalPages = mFrames.GetLength(); if (mDoingPageRange) { if (mFromPageNum > totalPages) { return NS_ERROR_INVALID_ARG; } } // Begin printing of the document nsresult rv = NS_OK; // Determine if we are rendering only the selection aPresContext->SetIsRenderingOnlySelection(nsIPrintSettings::kRangeSelection == mPrintRangeType); if (mDoingPageRange) { // XXX because of the hack for making the selection all print on one page // we must make sure that the page is sized correctly before printing. nscoord height = aPresContext->GetPageSize().height; int32_t pageNum = 1; nscoord y = 0;//mMargin.top; for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) { nsIFrame* page = e.get(); if (pageNum >= mFromPageNum && pageNum <= mToPageNum) { nsRect rect = page->GetRect(); rect.y = y; rect.height = height; page->SetRect(rect); y += rect.height + mMargin.top + mMargin.bottom; } pageNum++; } // adjust total number of pages if (nsIPrintSettings::kRangeSelection != mPrintRangeType) { totalPages = pageNum - 1; } } mPageNum = 1; if (mTotalPages == -1) { mTotalPages = totalPages; } return rv; } void GetPrintCanvasElementsInFrame(nsIFrame* aFrame, nsTArray >* aArr) { if (!aFrame) { return; } for (nsIFrame::ChildListIterator childLists(aFrame); !childLists.IsDone(); childLists.Next()) { nsFrameList children = childLists.CurrentList(); for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) { nsIFrame* child = e.get(); // Check if child is a nsHTMLCanvasFrame. nsHTMLCanvasFrame* canvasFrame = do_QueryFrame(child); // If there is a canvasFrame, try to get actual canvas element. if (canvasFrame) { HTMLCanvasElement* canvas = HTMLCanvasElement::FromContentOrNull(canvasFrame->GetContent()); if (canvas && canvas->GetMozPrintCallback()) { aArr->AppendElement(canvas); continue; } } if (!child->PrincipalChildList().FirstChild()) { nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(child); if (subdocumentFrame) { // Descend into the subdocument nsIFrame* root = subdocumentFrame->GetSubdocumentRootFrame(); child = root; } } // The current child is not a nsHTMLCanvasFrame OR it is but there is // no HTMLCanvasElement on it. Check if children of `child` might // contain a HTMLCanvasElement. GetPrintCanvasElementsInFrame(child, aArr); } } } void nsSimplePageSequenceFrame::DetermineWhetherToPrintPage() { // See whether we should print this page mPrintThisPage = true; bool printEvenPages, printOddPages; mPageData->mPrintSettings->GetPrintOptions(nsIPrintSettings::kPrintEvenPages, &printEvenPages); mPageData->mPrintSettings->GetPrintOptions(nsIPrintSettings::kPrintOddPages, &printOddPages); // If printing a range of pages check whether the page number is in the // range of pages to print if (mDoingPageRange) { if (mPageNum < mFromPageNum) { mPrintThisPage = false; } else if (mPageNum > mToPageNum) { mPageNum++; mPrintThisPage = false; return; } else { int32_t length = mPageRanges.Length(); // Page ranges are pairs (start, end) if (length && (length % 2 == 0)) { mPrintThisPage = false; int32_t i; for (i = 0; i < length; i += 2) { if (mPageRanges[i] <= mPageNum && mPageNum <= mPageRanges[i+1]) { mPrintThisPage = true; break; } } } } } // Check for printing of odd and even pages if (mPageNum & 0x1) { if (!printOddPages) { mPrintThisPage = false; // don't print odd numbered page } } else { if (!printEvenPages) { mPrintThisPage = false; // don't print even numbered page } } if (nsIPrintSettings::kRangeSelection == mPrintRangeType) { mPrintThisPage = true; } } nsIFrame* nsSimplePageSequenceFrame::GetCurrentPageFrame() { int32_t i = 1; for (nsFrameList::Enumerator childFrames(mFrames); !childFrames.AtEnd(); childFrames.Next()) { if (i == mPageNum) { return childFrames.get(); } ++i; } return nullptr; } NS_IMETHODIMP nsSimplePageSequenceFrame::PrePrintNextPage(nsITimerCallback* aCallback, bool* aDone) { nsIFrame* currentPage = GetCurrentPageFrame(); if (!currentPage) { *aDone = true; return NS_ERROR_FAILURE; } DetermineWhetherToPrintPage(); // Nothing to do if the current page doesn't get printed OR rendering to // preview. For preview, the `CallPrintCallback` is called from within the // HTMLCanvasElement::HandlePrintCallback. if (!mPrintThisPage || !PresContext()->IsRootPaginatedDocument()) { *aDone = true; return NS_OK; } // If the canvasList is null, then generate it and start the render // process for all the canvas. if (!mCurrentCanvasListSetup) { mCurrentCanvasListSetup = true; GetPrintCanvasElementsInFrame(currentPage, &mCurrentCanvasList); if (mCurrentCanvasList.Length() != 0) { nsresult rv = NS_OK; // Begin printing of the document nsDeviceContext *dc = PresContext()->DeviceContext(); PR_PL(("\n")); PR_PL(("***************** BeginPage *****************\n")); rv = dc->BeginPage(); NS_ENSURE_SUCCESS(rv, rv); mCalledBeginPage = true; RefPtr renderingContext = dc->CreateRenderingContext(); NS_ENSURE_TRUE(renderingContext, NS_ERROR_OUT_OF_MEMORY); DrawTarget* drawTarget = renderingContext->GetDrawTarget(); if (NS_WARN_IF(!drawTarget)) { return NS_ERROR_FAILURE; } for (int32_t i = mCurrentCanvasList.Length() - 1; i >= 0 ; i--) { HTMLCanvasElement* canvas = mCurrentCanvasList[i]; nsIntSize size = canvas->GetSize(); RefPtr canvasTarget = drawTarget->CreateSimilarDrawTarget(size, drawTarget->GetFormat()); if (!canvasTarget) { continue; } nsICanvasRenderingContextInternal* ctx = canvas->GetContextAtIndex(0); if (!ctx) { continue; } // Initialize the context with the new DrawTarget. ctx->InitializeWithDrawTarget(nullptr, WrapNotNull(canvasTarget)); // Start the rendering process. nsWeakFrame weakFrame = this; canvas->DispatchPrintCallback(aCallback); NS_ENSURE_STATE(weakFrame.IsAlive()); } } } uint32_t doneCounter = 0; for (int32_t i = mCurrentCanvasList.Length() - 1; i >= 0 ; i--) { HTMLCanvasElement* canvas = mCurrentCanvasList[i]; if (canvas->IsPrintCallbackDone()) { doneCounter++; } } // If all canvas have finished rendering, return true, otherwise false. *aDone = doneCounter == mCurrentCanvasList.Length(); return NS_OK; } NS_IMETHODIMP nsSimplePageSequenceFrame::ResetPrintCanvasList() { for (int32_t i = mCurrentCanvasList.Length() - 1; i >= 0 ; i--) { HTMLCanvasElement* canvas = mCurrentCanvasList[i]; canvas->ResetPrintCallback(); } mCurrentCanvasList.Clear(); mCurrentCanvasListSetup = false; return NS_OK; } NS_IMETHODIMP nsSimplePageSequenceFrame::PrintNextPage() { // Print each specified page // pageNum keeps track of the current page and what pages are printing // // printedPageNum keeps track of the current page number to be printed // Note: When print al the pages or a page range the printed page shows the // actual page number, when printing selection it prints the page number starting // with the first page of the selection. For example if the user has a // selection that starts on page 2 and ends on page 3, the page numbers when // print are 1 and then two (which is different than printing a page range, where // the page numbers would have been 2 and then 3) nsIFrame* currentPage = GetCurrentPageFrame(); if (!currentPage) { return NS_ERROR_FAILURE; } nsresult rv = NS_OK; DetermineWhetherToPrintPage(); if (mPrintThisPage) { // Begin printing of the document nsDeviceContext* dc = PresContext()->DeviceContext(); // XXX This is temporary fix for printing more than one page of a selection // This does a poor man's "dump" pagination (see Bug 89353) // It has laid out as one long page and now we are just moving or view up/down // one page at a time and printing the contents of what is exposed by the rect. // currently this does not work for IFrames // I will soon improve this to work with IFrames bool continuePrinting = true; nscoord width, height; width = PresContext()->GetPageSize().width; height = PresContext()->GetPageSize().height; height -= mMargin.top + mMargin.bottom; width -= mMargin.left + mMargin.right; nscoord selectionY = height; nsIFrame* conFrame = currentPage->PrincipalChildList().FirstChild(); if (mSelectionHeight >= 0) { conFrame->SetPosition(conFrame->GetPosition() + nsPoint(0, -mYSelOffset)); nsContainerFrame::PositionChildViews(conFrame); } // cast the frame to be a page frame nsPageFrame * pf = static_cast(currentPage); pf->SetPageNumInfo(mPageNum, mTotalPages); pf->SetSharedPageData(mPageData); int32_t printedPageNum = 1; while (continuePrinting) { if (PresContext()->IsRootPaginatedDocument()) { if (!mCalledBeginPage) { PR_PL(("\n")); PR_PL(("***************** BeginPage *****************\n")); rv = dc->BeginPage(); NS_ENSURE_SUCCESS(rv, rv); } else { mCalledBeginPage = false; } } PR_PL(("SeqFr::PrintNextPage -> %p PageNo: %d", pf, mPageNum)); // CreateRenderingContext can fail RefPtr gCtx = dc->CreateRenderingContext(); NS_ENSURE_TRUE(gCtx, NS_ERROR_OUT_OF_MEMORY); nsRenderingContext renderingContext(gCtx); nsRect drawingRect(nsPoint(0, 0), currentPage->GetSize()); nsRegion drawingRegion(drawingRect); nsLayoutUtils::PaintFrame(&renderingContext, currentPage, drawingRegion, NS_RGBA(0,0,0,0), nsDisplayListBuilderMode::PAINTING, nsLayoutUtils::PaintFrameFlags::PAINT_SYNC_DECODE_IMAGES); if (mSelectionHeight >= 0 && selectionY < mSelectionHeight) { selectionY += height; printedPageNum++; pf->SetPageNumInfo(printedPageNum, mTotalPages); conFrame->SetPosition(conFrame->GetPosition() + nsPoint(0, -height)); nsContainerFrame::PositionChildViews(conFrame); PR_PL(("***************** End Page (PrintNextPage) *****************\n")); rv = dc->EndPage(); NS_ENSURE_SUCCESS(rv, rv); } else { continuePrinting = false; } } } return rv; } NS_IMETHODIMP nsSimplePageSequenceFrame::DoPageEnd() { nsresult rv = NS_OK; if (PresContext()->IsRootPaginatedDocument() && mPrintThisPage) { PR_PL(("***************** End Page (DoPageEnd) *****************\n")); rv = PresContext()->DeviceContext()->EndPage(); NS_ENSURE_SUCCESS(rv, rv); } ResetPrintCanvasList(); mPageNum++; return rv; } inline gfx::Matrix4x4 ComputePageSequenceTransform(nsIFrame* aFrame, float aAppUnitsPerPixel) { float scale = aFrame->PresContext()->GetPrintPreviewScale(); return gfx::Matrix4x4::Scaling(scale, scale, 1); } void nsSimplePageSequenceFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) { DisplayBorderBackgroundOutline(aBuilder, aLists); nsDisplayList content; { // Clear clip state while we construct the children of the // nsDisplayTransform, since they'll be in a different coordinate system. DisplayListClipState::AutoSaveRestore clipState(aBuilder); clipState.Clear(); nsIFrame* child = PrincipalChildList().FirstChild(); nsRect dirty = aBuilder->GetDirtyRect(); dirty.ScaleInverseRoundOut(PresContext()->GetPrintPreviewScale()); while (child) { if (child->GetVisualOverflowRectRelativeToParent().Intersects(dirty)) { nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild(aBuilder, child, dirty - child->GetPosition(), aBuilder->IsAtRootOfPseudoStackingContext()); child->BuildDisplayListForStackingContext(aBuilder, &content); aBuilder->ResetMarkedFramesForDisplayList(); } child = child->GetNextSibling(); } } content.AppendNewToTop(new (aBuilder) nsDisplayTransform(aBuilder, this, &content, content.GetVisibleRect(), ::ComputePageSequenceTransform)); aLists.Content()->AppendToTop(&content); } nsIAtom* nsSimplePageSequenceFrame::GetType() const { return nsGkAtoms::sequenceFrame; } //------------------------------------------------------------------------------ void nsSimplePageSequenceFrame::SetPageNumberFormat(const nsAString& aFormatStr, bool aForPageNumOnly) { NS_ASSERTION(mPageData != nullptr, "mPageData string cannot be null!"); if (aForPageNumOnly) { mPageData->mPageNumFormat = aFormatStr; } else { mPageData->mPageNumAndTotalsFormat = aFormatStr; } } //------------------------------------------------------------------------------ void nsSimplePageSequenceFrame::SetDateTimeStr(const nsAString& aDateTimeStr) { NS_ASSERTION(mPageData != nullptr, "mPageData string cannot be null!"); mPageData->mDateTimeStr = aDateTimeStr; } //------------------------------------------------------------------------------ // For Shrink To Fit // // Return the percentage that the page needs to shrink to // NS_IMETHODIMP nsSimplePageSequenceFrame::GetSTFPercent(float& aSTFPercent) { NS_ENSURE_TRUE(mPageData, NS_ERROR_UNEXPECTED); aSTFPercent = mPageData->mShrinkToFitRatio; return NS_OK; }