1/*
2 * Copyright (C) 2012 Igalia S.L.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "WebPrintOperationGtk.h"
28
29#include "WebCoreArgumentCoders.h"
30#include "WebErrors.h"
31#include "WebPage.h"
32#include "WebPageProxyMessages.h"
33#include "WebProcess.h"
34#include <WebCore/DocumentLoader.h>
35#include <WebCore/Frame.h>
36#include <WebCore/FrameLoader.h>
37#include <WebCore/GraphicsContextImplCairo.h>
38#include <WebCore/IntRect.h>
39#include <WebCore/NotImplemented.h>
40#include <WebCore/PlatformContextCairo.h>
41#include <WebCore/PrintContext.h>
42#include <WebCore/ResourceError.h>
43#include <gtk/gtk.h>
44#include <memory>
45#include <wtf/URL.h>
46#include <wtf/Vector.h>
47#include <wtf/glib/GUniquePtr.h>
48
49#if HAVE(GTK_UNIX_PRINTING)
50#include "PrinterListGtk.h"
51#include <cairo-pdf.h>
52#include <cairo-ps.h>
53#include <gtk/gtkunixprint.h>
54#endif
55
56namespace WebKit {
57
58#if HAVE(GTK_UNIX_PRINTING)
59class WebPrintOperationGtkUnix final: public WebPrintOperationGtk {
60public:
61 WebPrintOperationGtkUnix(WebPage* page, const PrintInfo& printInfo)
62 : WebPrintOperationGtk(page, printInfo)
63 , m_printJob(0)
64 {
65 }
66
67 void startPrint(WebCore::PrintContext* printContext, CallbackID callbackID) override
68 {
69 m_printContext = printContext;
70 m_callbackID = callbackID;
71
72 RefPtr<PrinterListGtk> printerList = PrinterListGtk::getOrCreate();
73 ASSERT(printerList);
74 const char* printerName = gtk_print_settings_get_printer(m_printSettings.get());
75 GtkPrinter* printer = printerName ? printerList->findPrinter(printerName) : printerList->defaultPrinter();
76 if (!printer) {
77 printDone(printerNotFoundError(frameURL()));
78 return;
79 }
80
81 static int jobNumber = 0;
82 const char* applicationName = g_get_application_name();
83 GUniquePtr<char>jobName(g_strdup_printf("%s job #%d", applicationName ? applicationName : "WebKit", ++jobNumber));
84 m_printJob = adoptGRef(gtk_print_job_new(jobName.get(), printer, m_printSettings.get(), m_pageSetup.get()));
85
86 GUniqueOutPtr<GError> error;
87 cairo_surface_t* surface = gtk_print_job_get_surface(m_printJob.get(), &error.outPtr());
88 if (!surface) {
89 printDone(printError(frameURL(), error->message));
90 return;
91 }
92
93 int rangesCount;
94 m_pageRanges = gtk_print_job_get_page_ranges(m_printJob.get(), &rangesCount);
95 m_pageRangesCount = rangesCount;
96 m_pagesToPrint = gtk_print_job_get_pages(m_printJob.get());
97 m_needsRotation = gtk_print_job_get_rotate(m_printJob.get());
98
99 // Manual capabilities.
100 m_numberUp = gtk_print_job_get_n_up(m_printJob.get());
101 m_numberUpLayout = gtk_print_job_get_n_up_layout(m_printJob.get());
102 m_pageSet = gtk_print_job_get_page_set(m_printJob.get());
103 m_reverse = gtk_print_job_get_reverse(m_printJob.get());
104 m_copies = gtk_print_job_get_num_copies(m_printJob.get());
105 m_collateCopies = gtk_print_job_get_collate(m_printJob.get());
106 m_scale = gtk_print_job_get_scale(m_printJob.get());
107
108 print(surface, 72, 72);
109 }
110
111 void startPage(cairo_t*) override
112 {
113 if (!currentPageIsFirstPageOfSheet())
114 return;
115
116 GtkPaperSize* paperSize = gtk_page_setup_get_paper_size(m_pageSetup.get());
117 double width = gtk_paper_size_get_width(paperSize, GTK_UNIT_POINTS);
118 double height = gtk_paper_size_get_height(paperSize, GTK_UNIT_POINTS);
119
120 cairo_surface_t* surface = gtk_print_job_get_surface(m_printJob.get(), 0);
121 cairo_surface_type_t surfaceType = cairo_surface_get_type(surface);
122 if (surfaceType == CAIRO_SURFACE_TYPE_PS) {
123 cairo_ps_surface_set_size(surface, width, height);
124 cairo_ps_surface_dsc_begin_page_setup(surface);
125
126 switch (gtk_page_setup_get_orientation(m_pageSetup.get())) {
127 case GTK_PAGE_ORIENTATION_PORTRAIT:
128 case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
129 cairo_ps_surface_dsc_comment(surface, "%%PageOrientation: Portrait");
130 break;
131 case GTK_PAGE_ORIENTATION_LANDSCAPE:
132 case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
133 cairo_ps_surface_dsc_comment(surface, "%%PageOrientation: Landscape");
134 break;
135 }
136 } else if (surfaceType == CAIRO_SURFACE_TYPE_PDF)
137 switch (gtk_page_setup_get_orientation(m_pageSetup.get())) {
138 case GTK_PAGE_ORIENTATION_PORTRAIT:
139 case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
140 cairo_pdf_surface_set_size(surface, width, height);
141 break;
142 case GTK_PAGE_ORIENTATION_LANDSCAPE:
143 case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
144 cairo_pdf_surface_set_size(surface, height, width);
145 break;
146 }
147 }
148
149 void endPage(cairo_t* cr) override
150 {
151 if (currentPageIsLastPageOfSheet())
152 cairo_show_page(cr);
153 }
154
155 static void printJobComplete(GtkPrintJob*, WebPrintOperationGtkUnix* printOperation, const GError* error)
156 {
157 printOperation->printDone(error ? printError(printOperation->frameURL(), error->message) : WebCore::ResourceError());
158 printOperation->m_printJob = 0;
159 }
160
161 static void printJobFinished(WebPrintOperationGtkUnix* printOperation)
162 {
163 printOperation->deref();
164 WebProcess::singleton().enableTermination();
165 }
166
167 void endPrint() override
168 {
169 // Disable web process termination until the print job finishes.
170 WebProcess::singleton().disableTermination();
171
172 cairo_surface_finish(gtk_print_job_get_surface(m_printJob.get(), 0));
173 // Make sure the operation is alive until the job is sent.
174 ref();
175 gtk_print_job_send(m_printJob.get(), reinterpret_cast<GtkPrintJobCompleteFunc>(printJobComplete), this,
176 reinterpret_cast<GDestroyNotify>(printJobFinished));
177 }
178
179 GRefPtr<GtkPrintJob> m_printJob;
180};
181#endif
182
183#ifdef G_OS_WIN32
184class WebPrintOperationGtkWin32 final: public WebPrintOperationGtk {
185public:
186 WebPrintOperationGtkWin32(WebPage* page, const PrintInfo& printInfo)
187 : WebPrintOperationGtk(page, printInfo)
188 {
189 }
190
191 void startPrint(WebCore::PrintContext* printContext, CallbackID callbackID) override
192 {
193 m_printContext = printContext;
194 m_callbackID = callbackID;
195 notImplemented();
196 }
197
198 void startPage(cairo_t* cr) override
199 {
200 notImplemented();
201 }
202
203 void endPage(cairo_t* cr) override
204 {
205 notImplemented();
206 }
207
208 void endPrint() override
209 {
210 notImplemented();
211 }
212};
213#endif
214
215struct PrintPagesData {
216 PrintPagesData(WebPrintOperationGtk* printOperation)
217 : printOperation(printOperation)
218 , totalPrinted(-1)
219 , pageNumber(0)
220 , sheetNumber(0)
221 , firstSheetNumber(0)
222 , numberOfSheets(0)
223 , firstPagePosition(0)
224 , lastPagePosition(0)
225 , collated(0)
226 , uncollated(0)
227 , isDone(false)
228 , isValid(true)
229 {
230 if (printOperation->printMode() == PrintInfo::PrintModeSync)
231 mainLoop = adoptGRef(g_main_loop_new(0, FALSE));
232
233 if (printOperation->collateCopies()) {
234 collatedCopies = printOperation->copies();
235 uncollatedCopies = 1;
236 } else {
237 collatedCopies = 1;
238 uncollatedCopies = printOperation->copies();
239 }
240
241 if (printOperation->pagesToPrint() == GTK_PRINT_PAGES_RANGES) {
242 Vector<GtkPageRange> pageRanges;
243 GtkPageRange* ranges = printOperation->pageRanges();
244 size_t rangesCount = printOperation->pageRangesCount();
245 int pageCount = printOperation->pageCount();
246
247 pageRanges.reserveCapacity(rangesCount);
248 for (size_t i = 0; i < rangesCount; ++i) {
249 if (ranges[i].start >= 0 && ranges[i].start < pageCount && ranges[i].end >= 0 && ranges[i].end < pageCount)
250 pageRanges.append(ranges[i]);
251 else if (ranges[i].start >= 0 && ranges[i].start < pageCount && ranges[i].end >= pageCount) {
252 pageRanges.append(ranges[i]);
253 pageRanges.last().end = pageCount - 1;
254 } else if (ranges[i].end >= 0 && ranges[i].end < pageCount && ranges[i].start < 0) {
255 pageRanges.append(ranges[i]);
256 pageRanges.last().start = 0;
257 }
258 }
259
260 for (size_t i = 0; i < pageRanges.size(); ++i) {
261 for (int j = pageRanges[i].start; j <= pageRanges[i].end; ++j)
262 pages.append(j);
263 }
264
265 } else {
266 for (int i = 0; i < printOperation->pageCount(); ++i)
267 pages.append(i);
268 }
269
270 if (!pages.size()) {
271 isValid = false;
272 return;
273 }
274 printOperation->setNumberOfPagesToPrint(pages.size());
275
276 size_t numberUp = printOperation->numberUp();
277 if (numberUp > 1)
278 numberOfSheets = (pages.size() % numberUp) ? pages.size() / numberUp + 1 : pages.size() / numberUp;
279 else
280 numberOfSheets = pages.size();
281
282 bool reverse = printOperation->reverse();
283 switch (printOperation->pageSet()) {
284 case GTK_PAGE_SET_ODD:
285 if (reverse) {
286 lastPagePosition = std::min(numberUp - 1, pages.size() - 1);
287 sheetNumber = (numberOfSheets - 1) - (numberOfSheets - 1) % 2;
288 } else
289 lastPagePosition = std::min(((numberOfSheets - 1) - ((numberOfSheets - 1) % 2)) * numberUp - 1, pages.size() - 1);
290 break;
291 case GTK_PAGE_SET_EVEN:
292 if (reverse) {
293 lastPagePosition = std::min(2 * numberUp - 1, pages.size() - 1);
294 sheetNumber = (numberOfSheets - 1) - (1 - (numberOfSheets - 1) % 2);
295 } else {
296 lastPagePosition = std::min(((numberOfSheets - 1) - (1 - (numberOfSheets - 1) % 2)) * numberUp - 1, pages.size() - 1);
297 sheetNumber = numberOfSheets > 1 ? 1 : -1;
298 }
299 break;
300 case GTK_PAGE_SET_ALL:
301 if (reverse) {
302 lastPagePosition = std::min(numberUp - 1, pages.size() - 1);
303 sheetNumber = pages.size() - 1;
304 } else
305 lastPagePosition = pages.size() - 1;
306 break;
307 }
308
309 if (sheetNumber * numberUp >= pages.size()) {
310 isValid = false;
311 return;
312 }
313
314 printOperation->setPagePosition(sheetNumber * numberUp);
315 pageNumber = pages[printOperation->pagePosition()];
316 firstPagePosition = printOperation->pagePosition();
317 firstSheetNumber = sheetNumber;
318 }
319
320 size_t collatedCopiesLeft()
321 {
322 return collatedCopies > 1 ? collatedCopies - collated - 1 : 0;
323 }
324
325 size_t uncollatedCopiesLeft()
326 {
327 return uncollatedCopies > 1 ? uncollatedCopies - uncollated - 1 : 0;
328 }
329
330 size_t copiesLeft()
331 {
332 return collatedCopiesLeft() + uncollatedCopiesLeft();
333 }
334
335 void incrementPageSequence()
336 {
337 if (totalPrinted == -1) {
338 totalPrinted = 0;
339 return;
340 }
341
342 size_t pagePosition = printOperation->pagePosition();
343 if (pagePosition == lastPagePosition && !copiesLeft()) {
344 isDone = true;
345 return;
346 }
347
348 if (pagePosition == lastPagePosition && uncollatedCopiesLeft()) {
349 pagePosition = firstPagePosition;
350 sheetNumber = firstSheetNumber;
351 uncollated++;
352 } else if (printOperation->currentPageIsLastPageOfSheet()) {
353 if (!collatedCopiesLeft()) {
354 int step = printOperation->pageSet() == GTK_PAGE_SET_ALL ? 1 : 2;
355 step *= printOperation->reverse() ? -1 : 1;
356 sheetNumber += step;
357 collated = 0;
358 } else
359 collated++;
360 pagePosition = sheetNumber * printOperation->numberUp();
361 } else
362 pagePosition++;
363 printOperation->setPagePosition(pagePosition);
364
365 if (pagePosition >= pages.size() || sheetNumber >= numberOfSheets) {
366 isDone = true;
367 return;
368 }
369 pageNumber = pages[pagePosition];
370 totalPrinted++;
371 }
372
373 RefPtr<WebPrintOperationGtk> printOperation;
374 GRefPtr<GMainLoop> mainLoop;
375
376 int totalPrinted;
377 int pageNumber;
378 Vector<size_t> pages;
379 size_t sheetNumber;
380 size_t firstSheetNumber;
381 size_t numberOfSheets;
382 size_t firstPagePosition;
383 size_t lastPagePosition;
384 size_t collated;
385 size_t uncollated;
386 size_t collatedCopies;
387 size_t uncollatedCopies;
388
389 bool isDone : 1;
390 bool isValid : 1;
391};
392
393RefPtr<WebPrintOperationGtk> WebPrintOperationGtk::create(WebPage* page, const PrintInfo& printInfo)
394{
395#if HAVE(GTK_UNIX_PRINTING)
396 return adoptRef(new WebPrintOperationGtkUnix(page, printInfo));
397#elif defined(G_OS_WIN32)
398 return adoptRef(new WebPrintOperationGtkWin32(page, printInfo));
399#else
400 UNUSED_PARAM(page);
401 UNUSED_PARAM(printInfo);
402 return nullptr;
403#endif
404}
405
406WebPrintOperationGtk::WebPrintOperationGtk(WebPage* page, const PrintInfo& printInfo)
407 : m_webPage(page)
408 , m_printSettings(printInfo.printSettings.get())
409 , m_pageSetup(printInfo.pageSetup.get())
410 , m_printMode(printInfo.printMode)
411 , m_printContext(0)
412 , m_xDPI(1)
413 , m_yDPI(1)
414 , m_printPagesIdleId(0)
415 , m_numberOfPagesToPrint(0)
416 , m_pagesToPrint(GTK_PRINT_PAGES_ALL)
417 , m_pagePosition(0)
418 , m_pageRanges(0)
419 , m_pageRangesCount(0)
420 , m_needsRotation(false)
421 , m_numberUp(1)
422 , m_numberUpLayout(0)
423 , m_pageSet(GTK_PAGE_SET_ALL)
424 , m_reverse(false)
425 , m_copies(1)
426 , m_collateCopies(false)
427 , m_scale(1)
428{
429}
430
431WebPrintOperationGtk::~WebPrintOperationGtk()
432{
433 if (m_printPagesIdleId)
434 g_source_remove(m_printPagesIdleId);
435}
436
437void WebPrintOperationGtk::disconnectFromPage()
438{
439 m_webPage = nullptr;
440}
441
442int WebPrintOperationGtk::pageCount() const
443{
444 return m_printContext ? m_printContext->pageCount() : 0;
445}
446
447bool WebPrintOperationGtk::currentPageIsFirstPageOfSheet() const
448{
449 return (m_numberUp < 2 || !((m_pagePosition) % m_numberUp));
450}
451
452bool WebPrintOperationGtk::currentPageIsLastPageOfSheet() const
453{
454 return (m_numberUp < 2 || !((m_pagePosition + 1) % m_numberUp) || m_pagePosition == m_numberOfPagesToPrint - 1);
455}
456
457URL WebPrintOperationGtk::frameURL() const
458{
459 if (!m_printContext)
460 return URL();
461
462 WebCore::DocumentLoader* documentLoader = m_printContext->frame()->loader().documentLoader();
463 return documentLoader ? documentLoader->url() : URL();
464}
465
466void WebPrintOperationGtk::rotatePageIfNeeded()
467{
468 if (!m_needsRotation)
469 return;
470
471 GtkPaperSize* paperSize = gtk_page_setup_get_paper_size(m_pageSetup.get());
472 double width = gtk_paper_size_get_width(paperSize, GTK_UNIT_INCH) * m_xDPI;
473 double height = gtk_paper_size_get_height(paperSize, GTK_UNIT_INCH) * m_yDPI;
474
475 cairo_matrix_t matrix;
476 switch (gtk_page_setup_get_orientation(m_pageSetup.get())) {
477 case GTK_PAGE_ORIENTATION_LANDSCAPE:
478 cairo_translate(m_cairoContext.get(), 0, height);
479 cairo_matrix_init(&matrix, 0, -1, 1, 0, 0, 0);
480 cairo_transform(m_cairoContext.get(), &matrix);
481 break;
482 case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
483 cairo_translate(m_cairoContext.get(), width, height);
484 cairo_matrix_init(&matrix, -1, 0, 0, -1, 0, 0);
485 cairo_transform(m_cairoContext.get(), &matrix);
486 break;
487 case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
488 cairo_translate(m_cairoContext.get(), width, 0);
489 cairo_matrix_init(&matrix, 0, 1, -1, 0, 0, 0);
490 cairo_transform(m_cairoContext.get(), &matrix);
491 break;
492 case GTK_PAGE_ORIENTATION_PORTRAIT:
493 default:
494 break;
495 }
496}
497
498void WebPrintOperationGtk::getRowsAndColumnsOfPagesPerSheet(size_t& rows, size_t& columns)
499{
500 switch (m_numberUp) {
501 default:
502 columns = 1;
503 rows = 1;
504 break;
505 case 2:
506 columns = 2;
507 rows = 1;
508 break;
509 case 4:
510 columns = 2;
511 rows = 2;
512 break;
513 case 6:
514 columns = 3;
515 rows = 2;
516 break;
517 case 9:
518 columns = 3;
519 rows = 3;
520 break;
521 case 16:
522 columns = 4;
523 rows = 4;
524 break;
525 }
526}
527
528void WebPrintOperationGtk::getPositionOfPageInSheet(size_t rows, size_t columns, int& x, int&y)
529{
530 switch (m_numberUpLayout) {
531 case GTK_NUMBER_UP_LAYOUT_LEFT_TO_RIGHT_TOP_TO_BOTTOM:
532 x = m_pagePosition % columns;
533 y = (m_pagePosition / columns) % rows;
534 break;
535 case GTK_NUMBER_UP_LAYOUT_LEFT_TO_RIGHT_BOTTOM_TO_TOP:
536 x = m_pagePosition % columns;
537 y = rows - 1 - (m_pagePosition / columns) % rows;
538 break;
539 case GTK_NUMBER_UP_LAYOUT_RIGHT_TO_LEFT_TOP_TO_BOTTOM:
540 x = columns - 1 - m_pagePosition % columns;
541 y = (m_pagePosition / columns) % rows;
542 break;
543 case GTK_NUMBER_UP_LAYOUT_RIGHT_TO_LEFT_BOTTOM_TO_TOP:
544 x = columns - 1 - m_pagePosition % columns;
545 y = rows - 1 - (m_pagePosition / columns) % rows;
546 break;
547 case GTK_NUMBER_UP_LAYOUT_TOP_TO_BOTTOM_LEFT_TO_RIGHT:
548 x = (m_pagePosition / rows) % columns;
549 y = m_pagePosition % rows;
550 break;
551 case GTK_NUMBER_UP_LAYOUT_TOP_TO_BOTTOM_RIGHT_TO_LEFT:
552 x = columns - 1 - (m_pagePosition / rows) % columns;
553 y = m_pagePosition % rows;
554 break;
555 case GTK_NUMBER_UP_LAYOUT_BOTTOM_TO_TOP_LEFT_TO_RIGHT:
556 x = (m_pagePosition / rows) % columns;
557 y = rows - 1 - m_pagePosition % rows;
558 break;
559 case GTK_NUMBER_UP_LAYOUT_BOTTOM_TO_TOP_RIGHT_TO_LEFT:
560 x = columns - 1 - (m_pagePosition / rows) % columns;
561 y = rows - 1 - m_pagePosition % rows;
562 break;
563 }
564}
565
566void WebPrintOperationGtk::prepareContextToDraw()
567{
568 if (m_numberUp < 2) {
569 double left = gtk_page_setup_get_left_margin(m_pageSetup.get(), GTK_UNIT_INCH);
570 double top = gtk_page_setup_get_top_margin(m_pageSetup.get(), GTK_UNIT_INCH);
571 if (m_scale != 1.0)
572 cairo_scale(m_cairoContext.get(), m_scale, m_scale);
573 rotatePageIfNeeded();
574 cairo_translate(m_cairoContext.get(), left * m_xDPI, top * m_yDPI);
575
576 return;
577 }
578
579 rotatePageIfNeeded();
580
581 // Multiple pages per sheet.
582 double marginLeft = gtk_page_setup_get_left_margin(m_pageSetup.get(), GTK_UNIT_POINTS);
583 double marginRight = gtk_page_setup_get_right_margin(m_pageSetup.get(), GTK_UNIT_POINTS);
584 double marginTop = gtk_page_setup_get_top_margin(m_pageSetup.get(), GTK_UNIT_POINTS);
585 double marginBottom = gtk_page_setup_get_bottom_margin(m_pageSetup.get(), GTK_UNIT_POINTS);
586
587 double paperWidth = gtk_page_setup_get_paper_width(m_pageSetup.get(), GTK_UNIT_POINTS);
588 double paperHeight = gtk_page_setup_get_paper_height(m_pageSetup.get(), GTK_UNIT_POINTS);
589
590 size_t rows, columns;
591 getRowsAndColumnsOfPagesPerSheet(rows, columns);
592
593 GtkPageOrientation orientation = gtk_page_setup_get_orientation(m_pageSetup.get());
594 double pageWidth = 0, pageHeight = 0;
595 switch (orientation) {
596 case GTK_PAGE_ORIENTATION_PORTRAIT:
597 case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
598 pageWidth = paperWidth - (marginLeft + marginRight);
599 pageHeight = paperHeight - (marginTop + marginBottom);
600 cairo_translate(m_cairoContext.get(), marginLeft, marginTop);
601 break;
602 case GTK_PAGE_ORIENTATION_LANDSCAPE:
603 case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
604 pageWidth = paperWidth - (marginTop + marginBottom);
605 pageHeight = paperHeight - (marginLeft + marginRight);
606 cairo_translate(m_cairoContext.get(), marginTop, marginLeft);
607
608 size_t tmp = columns;
609 columns = rows;
610 rows = tmp;
611 break;
612 }
613
614 int x, y;
615 getPositionOfPageInSheet(rows, columns, x, y);
616
617 switch (m_numberUp) {
618 case 4:
619 case 9:
620 case 16: {
621 double scaleX = pageWidth / (columns * paperWidth);
622 double scaleY = pageHeight / (rows * paperHeight);
623 double scale = std::min(scaleX, scaleY);
624
625 double stepX = paperWidth * (scaleX / scale);
626 double stepY = paperHeight * (scaleY / scale);
627
628 double width = gtk_page_setup_get_page_width(m_pageSetup.get(), GTK_UNIT_INCH) * m_xDPI;
629 double height = gtk_page_setup_get_page_height(m_pageSetup.get(), GTK_UNIT_INCH) * m_yDPI;
630
631 double offsetX, offsetY;
632 if (marginLeft + marginRight > 0) {
633 offsetX = marginLeft * (stepX - width) / (marginLeft + marginRight);
634 offsetY = marginTop * (stepY - height) / (marginTop + marginBottom);
635 } else {
636 offsetX = (stepX - width) / 2.0;
637 offsetY = (stepY - height) / 2.0;
638 }
639
640 cairo_scale(m_cairoContext.get(), scale, scale);
641 cairo_translate(m_cairoContext.get(), x * stepX + offsetX, y * stepY + offsetY);
642 if (m_scale != 1.0)
643 cairo_scale(m_cairoContext.get(), m_scale, m_scale);
644 break;
645 }
646 case 2:
647 case 6: {
648 double scaleX = pageHeight / (columns * paperWidth);
649 double scaleY = pageWidth / (rows * paperHeight);
650 double scale = std::min(scaleX, scaleY);
651
652 double stepX = paperWidth * (scaleX / scale);
653 double stepY = paperHeight * (scaleY / scale);
654
655 double offsetX = ((stepX - paperWidth) / 2.0 * columns) - marginRight;
656 double offsetY = ((stepY - paperHeight) / 2.0 * rows) + marginTop;
657
658 cairo_scale(m_cairoContext.get(), scale, scale);
659 cairo_translate(m_cairoContext.get(), y * paperHeight + offsetY, (columns - x) * paperWidth + offsetX);
660 if (m_scale != 1.0)
661 cairo_scale(m_cairoContext.get(), m_scale, m_scale);
662 cairo_rotate(m_cairoContext.get(), -G_PI / 2);
663 break;
664 }
665 default:
666 break;
667 }
668}
669
670void WebPrintOperationGtk::renderPage(int pageNumber)
671{
672 startPage(m_cairoContext.get());
673 cairo_save(m_cairoContext.get());
674
675 prepareContextToDraw();
676
677 double pageWidth = gtk_page_setup_get_page_width(m_pageSetup.get(), GTK_UNIT_INCH) * m_xDPI;
678 WebCore::GraphicsContext graphicsContext(WebCore::GraphicsContextImplCairo::createFactory(m_cairoContext.get()));
679 m_printContext->spoolPage(graphicsContext, pageNumber, pageWidth / m_scale);
680
681 cairo_restore(m_cairoContext.get());
682 endPage(m_cairoContext.get());
683}
684
685gboolean WebPrintOperationGtk::printPagesIdle(gpointer userData)
686{
687 PrintPagesData* data = static_cast<PrintPagesData*>(userData);
688
689 data->incrementPageSequence();
690 if (data->isDone)
691 return FALSE;
692
693 data->printOperation->renderPage(data->pageNumber);
694 return TRUE;
695}
696
697void WebPrintOperationGtk::printPagesIdleDone(gpointer userData)
698{
699 PrintPagesData* data = static_cast<PrintPagesData*>(userData);
700 if (data->mainLoop)
701 g_main_loop_quit(data->mainLoop.get());
702
703 data->printOperation->printPagesDone();
704 delete data;
705}
706
707void WebPrintOperationGtk::printPagesDone()
708{
709 m_printPagesIdleId = 0;
710 endPrint();
711 m_cairoContext = nullptr;
712}
713
714void WebPrintOperationGtk::printDone(const WebCore::ResourceError& error)
715{
716 if (m_printPagesIdleId)
717 g_source_remove(m_printPagesIdleId);
718 m_printPagesIdleId = 0;
719
720 // Print finished or failed, notify the UI process that we are done if the page hasn't been closed.
721 if (m_webPage)
722 m_webPage->didFinishPrintOperation(error, m_callbackID);
723}
724
725void WebPrintOperationGtk::print(cairo_surface_t* surface, double xDPI, double yDPI)
726{
727 ASSERT(m_printContext);
728
729 auto data = std::make_unique<PrintPagesData>(this);
730 if (!data->isValid) {
731 cairo_surface_finish(surface);
732 printDone(invalidPageRangeToPrint(frameURL()));
733 return;
734 }
735
736 m_xDPI = xDPI;
737 m_yDPI = yDPI;
738 m_cairoContext = adoptRef(cairo_create(surface));
739
740 // Make sure the print pages idle has more priority than IPC messages comming from
741 // the IO thread, so that the EndPrinting message is always handled once the print
742 // operation has finished. See https://bugs.webkit.org/show_bug.cgi?id=122801.
743 unsigned idlePriority = m_printMode == PrintInfo::PrintModeSync ? G_PRIORITY_DEFAULT - 10 : G_PRIORITY_DEFAULT_IDLE + 10;
744 GMainLoop* mainLoop = data->mainLoop.get();
745 m_printPagesIdleId = gdk_threads_add_idle_full(idlePriority, printPagesIdle, data.release(), printPagesIdleDone);
746 if (m_printMode == PrintInfo::PrintModeSync) {
747 ASSERT(mainLoop);
748 g_main_loop_run(mainLoop);
749 }
750}
751
752}
753