Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "nsSimplePageSequenceFrame.h"
7 :
8 : #include "DateTimeFormat.h"
9 : #include "nsCOMPtr.h"
10 : #include "nsDeviceContext.h"
11 : #include "nsPresContext.h"
12 : #include "gfxContext.h"
13 : #include "nsGkAtoms.h"
14 : #include "nsIPresShell.h"
15 : #include "nsIPrintSettings.h"
16 : #include "nsPageFrame.h"
17 : #include "nsSubDocumentFrame.h"
18 : #include "nsRegion.h"
19 : #include "nsCSSFrameConstructor.h"
20 : #include "nsContentUtils.h"
21 : #include "nsDisplayList.h"
22 : #include "nsHTMLCanvasFrame.h"
23 : #include "mozilla/dom/HTMLCanvasElement.h"
24 : #include "nsICanvasRenderingContextInternal.h"
25 : #include "nsServiceManagerUtils.h"
26 : #include <algorithm>
27 :
28 : #define OFFSET_NOT_SET -1
29 :
30 : using namespace mozilla;
31 : using namespace mozilla::dom;
32 :
33 : #include "mozilla/Logging.h"
34 : mozilla::LazyLogModule gLayoutPrintingLog("printing-layout");
35 :
36 : #define PR_PL(_p1) MOZ_LOG(gLayoutPrintingLog, mozilla::LogLevel::Debug, _p1)
37 :
38 : nsSimplePageSequenceFrame*
39 0 : NS_NewSimplePageSequenceFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
40 : {
41 0 : return new (aPresShell) nsSimplePageSequenceFrame(aContext);
42 : }
43 :
44 0 : NS_IMPL_FRAMEARENA_HELPERS(nsSimplePageSequenceFrame)
45 :
46 0 : nsSimplePageSequenceFrame::nsSimplePageSequenceFrame(nsStyleContext* aContext)
47 : : nsContainerFrame(aContext, kClassID)
48 : , mTotalPages(-1)
49 : , mSelectionHeight(-1)
50 : , mYSelOffset(0)
51 : , mCalledBeginPage(false)
52 0 : , mCurrentCanvasListSetup(false)
53 : {
54 0 : nscoord halfInch = PresContext()->CSSTwipsToAppUnits(NS_INCHES_TO_TWIPS(0.5));
55 0 : mMargin.SizeTo(halfInch, halfInch, halfInch, halfInch);
56 :
57 : // XXX Unsafe to assume successful allocation
58 0 : mPageData = new nsSharedPageData();
59 0 : mPageData->mHeadFootFont =
60 0 : *PresContext()->GetDefaultFont(kGenericFont_serif,
61 0 : aContext->StyleFont()->mLanguage);
62 0 : mPageData->mHeadFootFont.size = nsPresContext::CSSPointsToAppUnits(10);
63 :
64 : // Doing this here so we only have to go get these formats once
65 0 : SetPageNumberFormat("pagenumber", "%1$d", true);
66 0 : SetPageNumberFormat("pageofpages", "%1$d of %2$d", false);
67 0 : }
68 :
69 0 : nsSimplePageSequenceFrame::~nsSimplePageSequenceFrame()
70 : {
71 0 : delete mPageData;
72 0 : ResetPrintCanvasList();
73 0 : }
74 :
75 0 : NS_QUERYFRAME_HEAD(nsSimplePageSequenceFrame)
76 0 : NS_QUERYFRAME_ENTRY(nsIPageSequenceFrame)
77 0 : NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
78 :
79 : //----------------------------------------------------------------------
80 :
81 : void
82 0 : nsSimplePageSequenceFrame::SetDesiredSize(ReflowOutput& aDesiredSize,
83 : const ReflowInput& aReflowInput,
84 : nscoord aWidth,
85 : nscoord aHeight)
86 : {
87 : // Aim to fill the whole size of the document, not only so we
88 : // can act as a background in print preview but also handle overflow
89 : // in child page frames correctly.
90 : // Use availableISize so we don't cause a needless horizontal scrollbar.
91 0 : WritingMode wm = aReflowInput.GetWritingMode();
92 0 : nscoord scaledWidth = aWidth * PresContext()->GetPrintPreviewScale();
93 0 : nscoord scaledHeight = aHeight * PresContext()->GetPrintPreviewScale();
94 :
95 0 : nscoord scaledISize = (wm.IsVertical() ? scaledHeight : scaledWidth);
96 0 : nscoord scaledBSize = (wm.IsVertical() ? scaledWidth : scaledHeight);
97 :
98 0 : aDesiredSize.ISize(wm) = std::max(scaledISize, aReflowInput.AvailableISize());
99 0 : aDesiredSize.BSize(wm) = std::max(scaledBSize, aReflowInput.ComputedBSize());
100 0 : }
101 :
102 : // Helper function to compute the offset needed to center a child
103 : // page-frame's margin-box inside our content-box.
104 : nscoord
105 0 : nsSimplePageSequenceFrame::ComputeCenteringMargin(
106 : nscoord aContainerContentBoxWidth,
107 : nscoord aChildPaddingBoxWidth,
108 : const nsMargin& aChildPhysicalMargin)
109 : {
110 : // We'll be centering our child's margin-box, so get the size of that:
111 : nscoord childMarginBoxWidth =
112 0 : aChildPaddingBoxWidth + aChildPhysicalMargin.LeftRight();
113 :
114 : // When rendered, our child's rect will actually be scaled up by the
115 : // print-preview scale factor, via ComputePageSequenceTransform().
116 : // We really want to center *that scaled-up rendering* inside of
117 : // aContainerContentBoxWidth. So, we scale up its margin-box here...
118 0 : auto ppScale = PresContext()->GetPrintPreviewScale();
119 : nscoord scaledChildMarginBoxWidth =
120 0 : NSToCoordRound(childMarginBoxWidth * ppScale);
121 :
122 : // ...and see we how much space is left over, when we subtract that scaled-up
123 : // size from the container width:
124 : nscoord scaledExtraSpace =
125 0 : aContainerContentBoxWidth - scaledChildMarginBoxWidth;
126 :
127 0 : if (scaledExtraSpace <= 0) {
128 : // (Don't bother centering if there's zero/negative space.)
129 0 : return 0;
130 : }
131 :
132 : // To center the child, we want to give it an additional left-margin of half
133 : // of the extra space. And then, we have to scale that space back down, so
134 : // that it'll produce the correct scaled-up amount when we render (because
135 : // rendering will scale it back up):
136 0 : return NSToCoordRound(scaledExtraSpace * 0.5 / ppScale);
137 : }
138 :
139 : /*
140 : * Note: we largely position/size out our children (page frames) using
141 : * \*physical\* x/y/width/height values, because the print preview UI is always
142 : * arranged in the same orientation, regardless of writing mode.
143 : */
144 : void
145 0 : nsSimplePageSequenceFrame::Reflow(nsPresContext* aPresContext,
146 : ReflowOutput& aDesiredSize,
147 : const ReflowInput& aReflowInput,
148 : nsReflowStatus& aStatus)
149 : {
150 0 : MarkInReflow();
151 0 : NS_PRECONDITION(aPresContext->IsRootPaginatedDocument(),
152 : "A Page Sequence is only for real pages");
153 0 : DO_GLOBAL_REFLOW_COUNT("nsSimplePageSequenceFrame");
154 0 : DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
155 0 : NS_FRAME_TRACE_REFLOW_IN("nsSimplePageSequenceFrame::Reflow");
156 :
157 0 : aStatus.Reset(); // we're always complete
158 :
159 : // Don't do incremental reflow until we've taught tables how to do
160 : // it right in paginated mode.
161 0 : if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
162 : // Return our desired size
163 0 : SetDesiredSize(aDesiredSize, aReflowInput, mSize.width, mSize.height);
164 0 : aDesiredSize.SetOverflowAreasToDesiredBounds();
165 0 : FinishAndStoreOverflow(&aDesiredSize);
166 :
167 0 : if (GetRect().Width() != aDesiredSize.Width()) {
168 : // Our width is changing; we need to re-center our children (our pages).
169 0 : for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
170 0 : nsIFrame* child = e.get();
171 0 : nsMargin pageCSSMargin = child->GetUsedMargin();
172 : nscoord centeringMargin =
173 0 : ComputeCenteringMargin(aReflowInput.ComputedWidth(),
174 0 : child->GetRect().Width(),
175 0 : pageCSSMargin);
176 0 : nscoord newX = pageCSSMargin.left + centeringMargin;
177 :
178 : // Adjust the child's x-position:
179 0 : child->MovePositionBy(nsPoint(newX - child->GetNormalPosition().x, 0));
180 : }
181 : }
182 0 : return;
183 : }
184 :
185 : // See if we can get a Print Settings from the Context
186 0 : if (!mPageData->mPrintSettings &&
187 0 : aPresContext->Medium() == nsGkAtoms::print) {
188 0 : mPageData->mPrintSettings = aPresContext->GetPrintSettings();
189 : }
190 :
191 : // now get out margins & edges
192 0 : if (mPageData->mPrintSettings) {
193 0 : nsIntMargin unwriteableTwips;
194 0 : mPageData->mPrintSettings->GetUnwriteableMarginInTwips(unwriteableTwips);
195 0 : NS_ASSERTION(unwriteableTwips.left >= 0 && unwriteableTwips.top >= 0 &&
196 : unwriteableTwips.right >= 0 && unwriteableTwips.bottom >= 0,
197 : "Unwriteable twips should be non-negative");
198 :
199 0 : nsIntMargin marginTwips;
200 0 : mPageData->mPrintSettings->GetMarginInTwips(marginTwips);
201 0 : mMargin = aPresContext->CSSTwipsToAppUnits(marginTwips + unwriteableTwips);
202 :
203 : int16_t printType;
204 0 : mPageData->mPrintSettings->GetPrintRange(&printType);
205 0 : mPrintRangeType = printType;
206 :
207 0 : nsIntMargin edgeTwips;
208 0 : mPageData->mPrintSettings->GetEdgeInTwips(edgeTwips);
209 :
210 : // sanity check the values. three inches are sometimes needed
211 0 : int32_t inchInTwips = NS_INCHES_TO_INT_TWIPS(3.0);
212 0 : edgeTwips.top = clamped(edgeTwips.top, 0, inchInTwips);
213 0 : edgeTwips.bottom = clamped(edgeTwips.bottom, 0, inchInTwips);
214 0 : edgeTwips.left = clamped(edgeTwips.left, 0, inchInTwips);
215 0 : edgeTwips.right = clamped(edgeTwips.right, 0, inchInTwips);
216 :
217 0 : mPageData->mEdgePaperMargin =
218 0 : aPresContext->CSSTwipsToAppUnits(edgeTwips + unwriteableTwips);
219 : }
220 :
221 : // *** Special Override ***
222 : // If this is a sub-sdoc (meaning it doesn't take the whole page)
223 : // and if this Document is in the upper left hand corner
224 : // we need to suppress the top margin or it will reflow too small
225 :
226 0 : nsSize pageSize = aPresContext->GetPageSize();
227 :
228 0 : mPageData->mReflowSize = pageSize;
229 : // If we're printing a selection, we need to reflow with
230 : // unconstrained height, to make sure we'll get to the selection
231 : // even if it's beyond the first page of content.
232 0 : if (nsIPrintSettings::kRangeSelection == mPrintRangeType) {
233 0 : mPageData->mReflowSize.height = NS_UNCONSTRAINEDSIZE;
234 : }
235 0 : mPageData->mReflowMargin = mMargin;
236 :
237 : // We use the CSS "margin" property on the -moz-page pseudoelement
238 : // to determine the space between each page in print preview.
239 : // Keep a running y-offset for each page.
240 0 : nscoord y = 0;
241 0 : nscoord maxXMost = 0;
242 :
243 : // Tile the pages vertically
244 0 : ReflowOutput kidSize(aReflowInput);
245 0 : for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
246 0 : nsIFrame* kidFrame = e.get();
247 : // Set the shared data into the page frame before reflow
248 0 : nsPageFrame * pf = static_cast<nsPageFrame*>(kidFrame);
249 0 : pf->SetSharedPageData(mPageData);
250 :
251 : // Reflow the page
252 : ReflowInput kidReflowInput(aPresContext, aReflowInput, kidFrame,
253 0 : LogicalSize(kidFrame->GetWritingMode(),
254 0 : pageSize));
255 0 : nsReflowStatus status;
256 :
257 0 : kidReflowInput.SetComputedISize(kidReflowInput.AvailableISize());
258 : //kidReflowInput.SetComputedHeight(kidReflowInput.AvailableHeight());
259 0 : PR_PL(("AV ISize: %d BSize: %d\n",
260 : kidReflowInput.AvailableISize(),
261 : kidReflowInput.AvailableBSize()));
262 :
263 0 : nsMargin pageCSSMargin = kidReflowInput.ComputedPhysicalMargin();
264 0 : y += pageCSSMargin.top;
265 :
266 0 : nscoord x = pageCSSMargin.left;
267 :
268 : // Place and size the page.
269 0 : ReflowChild(kidFrame, aPresContext, kidSize, kidReflowInput, x, y, 0, status);
270 :
271 : // If the page is narrower than our width, then center it horizontally:
272 0 : x += ComputeCenteringMargin(aReflowInput.ComputedWidth(),
273 0 : kidSize.Width(), pageCSSMargin);
274 :
275 0 : FinishReflowChild(kidFrame, aPresContext, kidSize, nullptr, x, y, 0);
276 0 : y += kidSize.Height();
277 0 : y += pageCSSMargin.bottom;
278 :
279 0 : maxXMost = std::max(maxXMost, x + kidSize.Width() + pageCSSMargin.right);
280 :
281 : // Is the page complete?
282 0 : nsIFrame* kidNextInFlow = kidFrame->GetNextInFlow();
283 :
284 0 : if (status.IsFullyComplete()) {
285 0 : NS_ASSERTION(!kidNextInFlow, "bad child flow list");
286 0 : } else if (!kidNextInFlow) {
287 : // The page isn't complete and it doesn't have a next-in-flow, so
288 : // create a continuing page.
289 : nsIFrame* continuingPage = aPresContext->PresShell()->FrameConstructor()->
290 0 : CreateContinuingFrame(aPresContext, kidFrame, this);
291 :
292 : // Add it to our child list
293 0 : mFrames.InsertFrame(nullptr, kidFrame, continuingPage);
294 : }
295 : }
296 :
297 : // Get Total Page Count
298 : // XXXdholbert technically we could calculate this in the loop above,
299 : // instead of needing a separate walk.
300 0 : int32_t pageTot = mFrames.GetLength();
301 :
302 : // Set Page Number Info
303 0 : int32_t pageNum = 1;
304 0 : for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
305 0 : MOZ_ASSERT(e.get()->IsPageFrame(),
306 : "only expecting nsPageFrame children. Other children will make "
307 : "this static_cast bogus & probably violate other assumptions");
308 0 : nsPageFrame* pf = static_cast<nsPageFrame*>(e.get());
309 0 : pf->SetPageNumInfo(pageNum, pageTot);
310 0 : pageNum++;
311 : }
312 :
313 0 : nsAutoString formattedDateString;
314 : time_t ltime;
315 0 : time( <ime );
316 0 : if (NS_SUCCEEDED(DateTimeFormat::FormatTime(kDateFormatShort,
317 : kTimeFormatNoSeconds,
318 : ltime,
319 : formattedDateString))) {
320 0 : SetDateTimeStr(formattedDateString);
321 : }
322 :
323 : // Return our desired size
324 : // Adjust the reflow size by PrintPreviewScale so the scrollbars end up the
325 : // correct size
326 0 : SetDesiredSize(aDesiredSize, aReflowInput, maxXMost, y);
327 :
328 0 : aDesiredSize.SetOverflowAreasToDesiredBounds();
329 0 : FinishAndStoreOverflow(&aDesiredSize);
330 :
331 : // cache the size so we can set the desired size
332 : // for the other reflows that happen
333 0 : mSize.width = maxXMost;
334 0 : mSize.height = y;
335 :
336 0 : NS_FRAME_TRACE_REFLOW_OUT("nsSimplePageSequeceFrame::Reflow", aStatus);
337 0 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
338 : }
339 :
340 : //----------------------------------------------------------------------
341 :
342 : #ifdef DEBUG_FRAME_DUMP
343 : nsresult
344 0 : nsSimplePageSequenceFrame::GetFrameName(nsAString& aResult) const
345 : {
346 0 : return MakeFrameName(NS_LITERAL_STRING("SimplePageSequence"), aResult);
347 : }
348 : #endif
349 :
350 : //====================================================================
351 : //== Asynch Printing
352 : //====================================================================
353 : NS_IMETHODIMP
354 0 : nsSimplePageSequenceFrame::GetCurrentPageNum(int32_t* aPageNum)
355 : {
356 0 : NS_ENSURE_ARG_POINTER(aPageNum);
357 :
358 0 : *aPageNum = mPageNum;
359 0 : return NS_OK;
360 : }
361 :
362 : NS_IMETHODIMP
363 0 : nsSimplePageSequenceFrame::GetNumPages(int32_t* aNumPages)
364 : {
365 0 : NS_ENSURE_ARG_POINTER(aNumPages);
366 :
367 0 : *aNumPages = mTotalPages;
368 0 : return NS_OK;
369 : }
370 :
371 : NS_IMETHODIMP
372 0 : nsSimplePageSequenceFrame::IsDoingPrintRange(bool* aDoing)
373 : {
374 0 : NS_ENSURE_ARG_POINTER(aDoing);
375 :
376 0 : *aDoing = mDoingPageRange;
377 0 : return NS_OK;
378 : }
379 :
380 : NS_IMETHODIMP
381 0 : nsSimplePageSequenceFrame::GetPrintRange(int32_t* aFromPage, int32_t* aToPage)
382 : {
383 0 : NS_ENSURE_ARG_POINTER(aFromPage);
384 0 : NS_ENSURE_ARG_POINTER(aToPage);
385 :
386 0 : *aFromPage = mFromPageNum;
387 0 : *aToPage = mToPageNum;
388 0 : return NS_OK;
389 : }
390 :
391 : // Helper Function
392 : void
393 0 : nsSimplePageSequenceFrame::SetPageNumberFormat(const char* aPropName, const char* aDefPropVal, bool aPageNumOnly)
394 : {
395 : // Doing this here so we only have to go get these formats once
396 0 : nsXPIDLString pageNumberFormat;
397 : // Now go get the Localized Page Formating String
398 : nsresult rv =
399 : nsContentUtils::GetLocalizedString(nsContentUtils::ePRINTING_PROPERTIES,
400 0 : aPropName, pageNumberFormat);
401 0 : if (NS_FAILED(rv)) { // back stop formatting
402 0 : pageNumberFormat.AssignASCII(aDefPropVal);
403 : }
404 :
405 0 : SetPageNumberFormat(pageNumberFormat, aPageNumOnly);
406 0 : }
407 :
408 : NS_IMETHODIMP
409 0 : nsSimplePageSequenceFrame::StartPrint(nsPresContext* aPresContext,
410 : nsIPrintSettings* aPrintSettings,
411 : const nsAString& aDocTitle,
412 : const nsAString& aDocURL)
413 : {
414 0 : NS_ENSURE_ARG_POINTER(aPresContext);
415 0 : NS_ENSURE_ARG_POINTER(aPrintSettings);
416 :
417 0 : if (!mPageData->mPrintSettings) {
418 0 : mPageData->mPrintSettings = aPrintSettings;
419 : }
420 :
421 0 : if (!aDocTitle.IsEmpty()) {
422 0 : mPageData->mDocTitle = aDocTitle;
423 : }
424 0 : if (!aDocURL.IsEmpty()) {
425 0 : mPageData->mDocURL = aDocURL;
426 : }
427 :
428 0 : aPrintSettings->GetStartPageRange(&mFromPageNum);
429 0 : aPrintSettings->GetEndPageRange(&mToPageNum);
430 0 : aPrintSettings->GetPageRanges(mPageRanges);
431 :
432 0 : mDoingPageRange = nsIPrintSettings::kRangeSpecifiedPageRange == mPrintRangeType ||
433 0 : nsIPrintSettings::kRangeSelection == mPrintRangeType;
434 :
435 : // If printing a range of pages make sure at least the starting page
436 : // number is valid
437 0 : int32_t totalPages = mFrames.GetLength();
438 :
439 0 : if (mDoingPageRange) {
440 0 : if (mFromPageNum > totalPages) {
441 0 : return NS_ERROR_INVALID_ARG;
442 : }
443 : }
444 :
445 : // Begin printing of the document
446 0 : nsresult rv = NS_OK;
447 :
448 : // Determine if we are rendering only the selection
449 0 : aPresContext->SetIsRenderingOnlySelection(nsIPrintSettings::kRangeSelection == mPrintRangeType);
450 :
451 :
452 0 : if (mDoingPageRange) {
453 : // XXX because of the hack for making the selection all print on one page
454 : // we must make sure that the page is sized correctly before printing.
455 0 : nscoord height = aPresContext->GetPageSize().height;
456 :
457 0 : int32_t pageNum = 1;
458 0 : nscoord y = 0;//mMargin.top;
459 :
460 0 : for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
461 0 : nsIFrame* page = e.get();
462 0 : if (pageNum >= mFromPageNum && pageNum <= mToPageNum) {
463 0 : nsRect rect = page->GetRect();
464 0 : rect.y = y;
465 0 : rect.height = height;
466 0 : page->SetRect(rect);
467 0 : y += rect.height + mMargin.top + mMargin.bottom;
468 : }
469 0 : pageNum++;
470 : }
471 :
472 : // adjust total number of pages
473 0 : if (nsIPrintSettings::kRangeSelection != mPrintRangeType) {
474 0 : totalPages = pageNum - 1;
475 : }
476 : }
477 :
478 0 : mPageNum = 1;
479 :
480 0 : if (mTotalPages == -1) {
481 0 : mTotalPages = totalPages;
482 : }
483 :
484 0 : return rv;
485 : }
486 :
487 : void
488 0 : GetPrintCanvasElementsInFrame(nsIFrame* aFrame, nsTArray<RefPtr<HTMLCanvasElement> >* aArr)
489 : {
490 0 : if (!aFrame) {
491 0 : return;
492 : }
493 0 : for (nsIFrame::ChildListIterator childLists(aFrame);
494 0 : !childLists.IsDone(); childLists.Next()) {
495 :
496 0 : nsFrameList children = childLists.CurrentList();
497 0 : for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) {
498 0 : nsIFrame* child = e.get();
499 :
500 : // Check if child is a nsHTMLCanvasFrame.
501 0 : nsHTMLCanvasFrame* canvasFrame = do_QueryFrame(child);
502 :
503 : // If there is a canvasFrame, try to get actual canvas element.
504 0 : if (canvasFrame) {
505 : HTMLCanvasElement* canvas =
506 0 : HTMLCanvasElement::FromContentOrNull(canvasFrame->GetContent());
507 0 : if (canvas && canvas->GetMozPrintCallback()) {
508 0 : aArr->AppendElement(canvas);
509 0 : continue;
510 : }
511 : }
512 :
513 0 : if (!child->PrincipalChildList().FirstChild()) {
514 0 : nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(child);
515 0 : if (subdocumentFrame) {
516 : // Descend into the subdocument
517 0 : nsIFrame* root = subdocumentFrame->GetSubdocumentRootFrame();
518 0 : child = root;
519 : }
520 : }
521 : // The current child is not a nsHTMLCanvasFrame OR it is but there is
522 : // no HTMLCanvasElement on it. Check if children of `child` might
523 : // contain a HTMLCanvasElement.
524 0 : GetPrintCanvasElementsInFrame(child, aArr);
525 : }
526 : }
527 : }
528 :
529 : void
530 0 : nsSimplePageSequenceFrame::DetermineWhetherToPrintPage()
531 : {
532 : // See whether we should print this page
533 0 : mPrintThisPage = true;
534 : bool printEvenPages, printOddPages;
535 0 : mPageData->mPrintSettings->GetPrintOptions(nsIPrintSettings::kPrintEvenPages, &printEvenPages);
536 0 : mPageData->mPrintSettings->GetPrintOptions(nsIPrintSettings::kPrintOddPages, &printOddPages);
537 :
538 : // If printing a range of pages check whether the page number is in the
539 : // range of pages to print
540 0 : if (mDoingPageRange) {
541 0 : if (mPageNum < mFromPageNum) {
542 0 : mPrintThisPage = false;
543 0 : } else if (mPageNum > mToPageNum) {
544 0 : mPageNum++;
545 0 : mPrintThisPage = false;
546 0 : return;
547 : } else {
548 0 : int32_t length = mPageRanges.Length();
549 :
550 : // Page ranges are pairs (start, end)
551 0 : if (length && (length % 2 == 0)) {
552 0 : mPrintThisPage = false;
553 :
554 : int32_t i;
555 0 : for (i = 0; i < length; i += 2) {
556 0 : if (mPageRanges[i] <= mPageNum && mPageNum <= mPageRanges[i+1]) {
557 0 : mPrintThisPage = true;
558 0 : break;
559 : }
560 : }
561 : }
562 : }
563 : }
564 :
565 : // Check for printing of odd and even pages
566 0 : if (mPageNum & 0x1) {
567 0 : if (!printOddPages) {
568 0 : mPrintThisPage = false; // don't print odd numbered page
569 : }
570 : } else {
571 0 : if (!printEvenPages) {
572 0 : mPrintThisPage = false; // don't print even numbered page
573 : }
574 : }
575 :
576 0 : if (nsIPrintSettings::kRangeSelection == mPrintRangeType) {
577 0 : mPrintThisPage = true;
578 : }
579 : }
580 :
581 : nsIFrame*
582 0 : nsSimplePageSequenceFrame::GetCurrentPageFrame()
583 : {
584 0 : int32_t i = 1;
585 0 : for (nsFrameList::Enumerator childFrames(mFrames); !childFrames.AtEnd();
586 0 : childFrames.Next()) {
587 0 : if (i == mPageNum) {
588 0 : return childFrames.get();
589 : }
590 0 : ++i;
591 : }
592 0 : return nullptr;
593 : }
594 :
595 : NS_IMETHODIMP
596 0 : nsSimplePageSequenceFrame::PrePrintNextPage(nsITimerCallback* aCallback, bool* aDone)
597 : {
598 0 : nsIFrame* currentPage = GetCurrentPageFrame();
599 0 : if (!currentPage) {
600 0 : *aDone = true;
601 0 : return NS_ERROR_FAILURE;
602 : }
603 :
604 0 : DetermineWhetherToPrintPage();
605 : // Nothing to do if the current page doesn't get printed OR rendering to
606 : // preview. For preview, the `CallPrintCallback` is called from within the
607 : // HTMLCanvasElement::HandlePrintCallback.
608 0 : if (!mPrintThisPage || !PresContext()->IsRootPaginatedDocument()) {
609 0 : *aDone = true;
610 0 : return NS_OK;
611 : }
612 :
613 : // If the canvasList is null, then generate it and start the render
614 : // process for all the canvas.
615 0 : if (!mCurrentCanvasListSetup) {
616 0 : mCurrentCanvasListSetup = true;
617 0 : GetPrintCanvasElementsInFrame(currentPage, &mCurrentCanvasList);
618 :
619 0 : if (mCurrentCanvasList.Length() != 0) {
620 0 : nsresult rv = NS_OK;
621 :
622 : // Begin printing of the document
623 0 : nsDeviceContext *dc = PresContext()->DeviceContext();
624 0 : PR_PL(("\n"));
625 0 : PR_PL(("***************** BeginPage *****************\n"));
626 0 : rv = dc->BeginPage();
627 0 : NS_ENSURE_SUCCESS(rv, rv);
628 :
629 0 : mCalledBeginPage = true;
630 :
631 0 : RefPtr<gfxContext> renderingContext = dc->CreateRenderingContext();
632 0 : NS_ENSURE_TRUE(renderingContext, NS_ERROR_OUT_OF_MEMORY);
633 :
634 0 : DrawTarget* drawTarget = renderingContext->GetDrawTarget();
635 0 : if (NS_WARN_IF(!drawTarget)) {
636 0 : return NS_ERROR_FAILURE;
637 : }
638 :
639 0 : for (int32_t i = mCurrentCanvasList.Length() - 1; i >= 0 ; i--) {
640 0 : HTMLCanvasElement* canvas = mCurrentCanvasList[i];
641 0 : nsIntSize size = canvas->GetSize();
642 :
643 : RefPtr<DrawTarget> canvasTarget =
644 0 : drawTarget->CreateSimilarDrawTarget(size, drawTarget->GetFormat());
645 0 : if (!canvasTarget) {
646 0 : continue;
647 : }
648 :
649 0 : nsICanvasRenderingContextInternal* ctx = canvas->GetContextAtIndex(0);
650 0 : if (!ctx) {
651 0 : continue;
652 : }
653 :
654 : // Initialize the context with the new DrawTarget.
655 0 : ctx->InitializeWithDrawTarget(nullptr, WrapNotNull(canvasTarget));
656 :
657 : // Start the rendering process.
658 0 : AutoWeakFrame weakFrame = this;
659 0 : canvas->DispatchPrintCallback(aCallback);
660 0 : NS_ENSURE_STATE(weakFrame.IsAlive());
661 : }
662 : }
663 : }
664 0 : uint32_t doneCounter = 0;
665 0 : for (int32_t i = mCurrentCanvasList.Length() - 1; i >= 0 ; i--) {
666 0 : HTMLCanvasElement* canvas = mCurrentCanvasList[i];
667 :
668 0 : if (canvas->IsPrintCallbackDone()) {
669 0 : doneCounter++;
670 : }
671 : }
672 : // If all canvas have finished rendering, return true, otherwise false.
673 0 : *aDone = doneCounter == mCurrentCanvasList.Length();
674 :
675 0 : return NS_OK;
676 : }
677 :
678 : NS_IMETHODIMP
679 0 : nsSimplePageSequenceFrame::ResetPrintCanvasList()
680 : {
681 0 : for (int32_t i = mCurrentCanvasList.Length() - 1; i >= 0 ; i--) {
682 0 : HTMLCanvasElement* canvas = mCurrentCanvasList[i];
683 0 : canvas->ResetPrintCallback();
684 : }
685 :
686 0 : mCurrentCanvasList.Clear();
687 0 : mCurrentCanvasListSetup = false;
688 0 : return NS_OK;
689 : }
690 :
691 : NS_IMETHODIMP
692 0 : nsSimplePageSequenceFrame::PrintNextPage()
693 : {
694 : // This method would be very straightforward except that the
695 : // "Print Selection Only" functionality (which is a broken mess) is
696 : // integrated here. The thing to understand is that if we're printing a
697 : // selection (which may contain multiple ranges) then we only enter this
698 : // function once since the content to print is laid out as one arbitrarily
699 : // long nsPageFrame instead of multiple nsPageFrames that are sized to fit
700 : // the printer paper size(!). Each of the "pages" between the start and end
701 : // of the selection are printed by offsetting the nsPageContentFrame by the
702 : // index of the page being printed and then drawing the nsPageContentFrame.
703 : // This does not work for IFrames.
704 : //
705 : // Note: When print al the pages or a page range the printed page shows the
706 : // actual page number, when printing selection it prints the page number starting
707 : // with the first page of the selection. For example if the user has a
708 : // selection that starts on page 2 and ends on page 3, the page numbers when
709 : // print are 1 and then two (which is different than printing a page range, where
710 : // the page numbers would have been 2 and then 3)
711 :
712 0 : nsIFrame* currentPageFrame = GetCurrentPageFrame();
713 0 : if (!currentPageFrame) {
714 0 : return NS_ERROR_FAILURE;
715 : }
716 :
717 0 : nsresult rv = NS_OK;
718 :
719 0 : DetermineWhetherToPrintPage();
720 :
721 0 : if (mPrintThisPage) {
722 0 : nsDeviceContext* dc = PresContext()->DeviceContext();
723 :
724 0 : nsPageFrame * pf = static_cast<nsPageFrame*>(currentPageFrame);
725 0 : pf->SetPageNumInfo(mPageNum, mTotalPages);
726 0 : pf->SetSharedPageData(mPageData);
727 :
728 : // Only used if we're printing a selection:
729 0 : nsIFrame* selectionContentFrame = nullptr;
730 : nscoord pageContentHeight =
731 0 : PresContext()->GetPageSize().height - (mMargin.top + mMargin.bottom);
732 0 : nscoord selectionY = pageContentHeight;
733 0 : int32_t selectionCurrentPageNum = 1;
734 0 : bool haveUnfinishedSelectionToPrint = false;
735 :
736 0 : if (mSelectionHeight >= 0) {
737 0 : selectionContentFrame = currentPageFrame->PrincipalChildList().FirstChild();
738 0 : MOZ_ASSERT(selectionContentFrame->IsPageContentFrame() &&
739 : !selectionContentFrame->GetNextSibling(),
740 : "Unexpected frame tree");
741 : // To print a selection we reposition the page content frame for each
742 : // page. We can do this (and not have to bother resetting the position
743 : // after we're done) because we are printing from a static clone document
744 : // that is thrown away after we finish printing.
745 0 : selectionContentFrame->SetPosition(selectionContentFrame->GetPosition() +
746 0 : nsPoint(0, -mYSelOffset));
747 0 : nsContainerFrame::PositionChildViews(selectionContentFrame);
748 : }
749 :
750 0 : do {
751 0 : if (PresContext()->IsRootPaginatedDocument()) {
752 0 : if (!mCalledBeginPage) {
753 : // We must make sure BeginPage() has been called since some printing
754 : // backends can't give us a valid rendering context for a [physical]
755 : // page otherwise.
756 0 : PR_PL(("\n"));
757 0 : PR_PL(("***************** BeginPage *****************\n"));
758 0 : rv = dc->BeginPage();
759 0 : NS_ENSURE_SUCCESS(rv, rv);
760 : }
761 :
762 : // Reset this flag. We reset it early here because if we loop around to
763 : // print another page's worth of selection we need to call BeginPage
764 : // again:
765 0 : mCalledBeginPage = false;
766 : }
767 :
768 0 : PR_PL(("SeqFr::PrintNextPage -> %p PageNo: %d", pf, mPageNum));
769 :
770 : // CreateRenderingContext can fail
771 0 : RefPtr<gfxContext> gCtx = dc->CreateRenderingContext();
772 0 : NS_ENSURE_TRUE(gCtx, NS_ERROR_OUT_OF_MEMORY);
773 :
774 0 : nsRect drawingRect(nsPoint(0, 0), currentPageFrame->GetSize());
775 0 : nsRegion drawingRegion(drawingRect);
776 0 : nsLayoutUtils::PaintFrame(gCtx, currentPageFrame,
777 : drawingRegion, NS_RGBA(0,0,0,0),
778 : nsDisplayListBuilderMode::PAINTING,
779 0 : nsLayoutUtils::PaintFrameFlags::PAINT_SYNC_DECODE_IMAGES);
780 :
781 0 : if (mSelectionHeight >= 0) {
782 0 : haveUnfinishedSelectionToPrint = (selectionY < mSelectionHeight);
783 0 : if (haveUnfinishedSelectionToPrint) {
784 0 : selectionY += pageContentHeight;
785 0 : selectionCurrentPageNum++;
786 0 : pf->SetPageNumInfo(selectionCurrentPageNum, mTotalPages);
787 0 : selectionContentFrame->SetPosition(selectionContentFrame->GetPosition() +
788 0 : nsPoint(0, -pageContentHeight));
789 0 : nsContainerFrame::PositionChildViews(selectionContentFrame);
790 :
791 : // We're going to loop and call BeginPage to print another page's worth
792 : // of selection so we need to call EndPage first. (Otherwise, EndPage
793 : // is called in DoEndPage.)
794 0 : PR_PL(("***************** End Page (PrintNextPage) *****************\n"));
795 0 : rv = dc->EndPage();
796 0 : NS_ENSURE_SUCCESS(rv, rv);
797 : }
798 : }
799 : } while (haveUnfinishedSelectionToPrint);
800 : }
801 0 : return rv;
802 : }
803 :
804 : NS_IMETHODIMP
805 0 : nsSimplePageSequenceFrame::DoPageEnd()
806 : {
807 0 : nsresult rv = NS_OK;
808 0 : if (PresContext()->IsRootPaginatedDocument() && mPrintThisPage) {
809 0 : PR_PL(("***************** End Page (DoPageEnd) *****************\n"));
810 0 : rv = PresContext()->DeviceContext()->EndPage();
811 0 : NS_ENSURE_SUCCESS(rv, rv);
812 : }
813 :
814 0 : ResetPrintCanvasList();
815 :
816 0 : mPageNum++;
817 :
818 0 : return rv;
819 : }
820 :
821 : inline gfx::Matrix4x4
822 0 : ComputePageSequenceTransform(nsIFrame* aFrame, float aAppUnitsPerPixel)
823 : {
824 0 : float scale = aFrame->PresContext()->GetPrintPreviewScale();
825 0 : return gfx::Matrix4x4::Scaling(scale, scale, 1);
826 : }
827 :
828 : void
829 0 : nsSimplePageSequenceFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
830 : const nsRect& aDirtyRect,
831 : const nsDisplayListSet& aLists)
832 : {
833 0 : DisplayBorderBackgroundOutline(aBuilder, aLists);
834 :
835 0 : nsDisplayList content;
836 :
837 : {
838 : // Clear clip state while we construct the children of the
839 : // nsDisplayTransform, since they'll be in a different coordinate system.
840 0 : DisplayListClipState::AutoSaveRestore clipState(aBuilder);
841 0 : clipState.Clear();
842 :
843 0 : nsIFrame* child = PrincipalChildList().FirstChild();
844 0 : nsRect dirty = aDirtyRect;
845 0 : dirty.ScaleInverseRoundOut(PresContext()->GetPrintPreviewScale());
846 :
847 0 : while (child) {
848 0 : if (child->GetVisualOverflowRectRelativeToParent().Intersects(dirty)) {
849 : child->BuildDisplayListForStackingContext(aBuilder,
850 0 : dirty - child->GetPosition(), &content);
851 0 : aBuilder->ResetMarkedFramesForDisplayList();
852 : }
853 0 : child = child->GetNextSibling();
854 : }
855 : }
856 :
857 0 : content.AppendNewToTop(new (aBuilder)
858 0 : nsDisplayTransform(aBuilder, this, &content, content.GetVisibleRect(),
859 0 : ::ComputePageSequenceTransform));
860 :
861 0 : aLists.Content()->AppendToTop(&content);
862 0 : }
863 :
864 : //------------------------------------------------------------------------------
865 : void
866 0 : nsSimplePageSequenceFrame::SetPageNumberFormat(const nsAString& aFormatStr, bool aForPageNumOnly)
867 : {
868 0 : NS_ASSERTION(mPageData != nullptr, "mPageData string cannot be null!");
869 :
870 0 : if (aForPageNumOnly) {
871 0 : mPageData->mPageNumFormat = aFormatStr;
872 : } else {
873 0 : mPageData->mPageNumAndTotalsFormat = aFormatStr;
874 : }
875 0 : }
876 :
877 : //------------------------------------------------------------------------------
878 : void
879 0 : nsSimplePageSequenceFrame::SetDateTimeStr(const nsAString& aDateTimeStr)
880 : {
881 0 : NS_ASSERTION(mPageData != nullptr, "mPageData string cannot be null!");
882 :
883 0 : mPageData->mDateTimeStr = aDateTimeStr;
884 0 : }
885 :
886 : //------------------------------------------------------------------------------
887 : // For Shrink To Fit
888 : //
889 : // Return the percentage that the page needs to shrink to
890 : //
891 : NS_IMETHODIMP
892 0 : nsSimplePageSequenceFrame::GetSTFPercent(float& aSTFPercent)
893 : {
894 0 : NS_ENSURE_TRUE(mPageData, NS_ERROR_UNEXPECTED);
895 0 : aSTFPercent = mPageData->mShrinkToFitRatio;
896 0 : return NS_OK;
897 : }
898 :
899 : void
900 0 : nsSimplePageSequenceFrame::AppendDirectlyOwnedAnonBoxes(
901 : nsTArray<OwnedAnonBox>& aResult)
902 : {
903 0 : if (mFrames.NotEmpty()) {
904 0 : aResult.AppendElement(mFrames.FirstChild());
905 : }
906 0 : }
|