73566a13385de3aac646fb9496bddafcb5af46b6
[WebKit-https.git] / Source / WebKit / WebProcess / WebPage / gtk / WebPrintOperationGtk.cpp
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
56 namespace WebKit {
57
58 #if HAVE(GTK_UNIX_PRINTING)
59 class WebPrintOperationGtkUnix final: public WebPrintOperationGtk {
60 public:
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
184 class WebPrintOperationGtkWin32 final: public WebPrintOperationGtk {
185 public:
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
215 struct 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
393 RefPtr<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
406 WebPrintOperationGtk::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
431 WebPrintOperationGtk::~WebPrintOperationGtk()
432 {
433     if (m_printPagesIdleId)
434         g_source_remove(m_printPagesIdleId);
435 }
436
437 void WebPrintOperationGtk::disconnectFromPage()
438 {
439     m_webPage = nullptr;
440 }
441
442 int WebPrintOperationGtk::pageCount() const
443 {
444     return m_printContext ? m_printContext->pageCount() : 0;
445 }
446
447 bool WebPrintOperationGtk::currentPageIsFirstPageOfSheet() const
448 {
449     return (m_numberUp < 2 || !((m_pagePosition) % m_numberUp));
450 }
451
452 bool WebPrintOperationGtk::currentPageIsLastPageOfSheet() const
453 {
454     return (m_numberUp < 2 || !((m_pagePosition + 1) % m_numberUp) || m_pagePosition == m_numberOfPagesToPrint - 1);
455 }
456
457 URL 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
466 void 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
498 void 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
528 void 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
566 void 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
670 void 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
685 gboolean 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
697 void 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
707 void WebPrintOperationGtk::printPagesDone()
708 {
709     m_printPagesIdleId = 0;
710     endPrint();
711     m_cairoContext = nullptr;
712 }
713
714 void 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
725 void 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 }