[GTK][WK2] WebKit2 does not build if gtk-unix-printing-3.0 is not available
[WebKit-https.git] / Source / WebKit2 / 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 "WebPage.h"
30 #include "WebPageProxyMessages.h"
31 #include <WebCore/IntRect.h>
32 #include <WebCore/NotImplemented.h>
33 #include <WebCore/PlatformContextCairo.h>
34 #include <WebCore/PrintContext.h>
35 #include <gtk/gtk.h>
36 #include <wtf/Vector.h>
37 #include <wtf/gobject/GOwnPtr.h>
38
39 #ifdef HAVE_GTK_UNIX_PRINTING
40 #include <cairo-pdf.h>
41 #include <cairo-ps.h>
42 #include <gtk/gtkunixprint.h>
43 #endif
44
45 namespace WebKit {
46
47 #ifdef HAVE_GTK_UNIX_PRINTING
48 class WebPrintOperationGtkUnix: public WebPrintOperationGtk {
49 public:
50     WebPrintOperationGtkUnix(WebPage* page, const PrintInfo& printInfo)
51         : WebPrintOperationGtk(page, printInfo)
52         , m_printJob(0)
53     {
54     }
55
56     static gboolean enumeratePrintersFunction(GtkPrinter* printer, WebPrintOperationGtkUnix* printOperation)
57     {
58         const char* printerName = gtk_print_settings_get_printer(printOperation->printSettings());
59         if ((printerName && strcmp(printerName, gtk_printer_get_name(printer)))
60             || (!printerName && !gtk_printer_is_default(printer)))
61             return FALSE;
62
63         static int jobNumber = 0;
64         const char* applicationName = g_get_application_name();
65         GOwnPtr<char>jobName(g_strdup_printf("%s job #%d", applicationName ? applicationName : "WebKit", ++jobNumber));
66         printOperation->m_printJob = adoptGRef(gtk_print_job_new(jobName.get(), printer,
67                                                                  printOperation->printSettings(),
68                                                                  printOperation->pageSetup()));
69         return TRUE;
70     }
71
72     static void enumeratePrintersFinished(WebPrintOperationGtkUnix* printOperation)
73     {
74         if (!printOperation->m_printJob) {
75             // FIXME: Printing error.
76             return;
77         }
78
79         cairo_surface_t* surface = gtk_print_job_get_surface(printOperation->m_printJob.get(), 0);
80         if (!surface) {
81             // FIXME: Printing error.
82             return;
83         }
84
85         int rangesCount;
86         printOperation->m_pageRanges = gtk_print_job_get_page_ranges(printOperation->m_printJob.get(), &rangesCount);
87         printOperation->m_pageRangesCount = rangesCount;
88         printOperation->m_pagesToPrint = gtk_print_job_get_pages(printOperation->m_printJob.get());
89         printOperation->m_needsRotation = gtk_print_job_get_rotate(printOperation->m_printJob.get());
90
91         // Manual capabilities.
92         printOperation->m_numberUp = gtk_print_job_get_n_up(printOperation->m_printJob.get());
93         printOperation->m_numberUpLayout = gtk_print_job_get_n_up_layout(printOperation->m_printJob.get());
94         printOperation->m_pageSet = gtk_print_job_get_page_set(printOperation->m_printJob.get());
95         printOperation->m_reverse = gtk_print_job_get_reverse(printOperation->m_printJob.get());
96         printOperation->m_copies = gtk_print_job_get_num_copies(printOperation->m_printJob.get());
97         printOperation->m_collateCopies = gtk_print_job_get_collate(printOperation->m_printJob.get());
98         printOperation->m_scale = gtk_print_job_get_scale(printOperation->m_printJob.get());
99
100         printOperation->print(surface, 72, 72);
101     }
102
103     void startPrint(WebCore::PrintContext* printContext, uint64_t callbackID)
104     {
105         m_printContext = printContext;
106         m_callbackID = callbackID;
107         gtk_enumerate_printers(reinterpret_cast<GtkPrinterFunc>(enumeratePrintersFunction), this,
108                                reinterpret_cast<GDestroyNotify>(enumeratePrintersFinished), FALSE);
109     }
110
111     void startPage(cairo_t* cr)
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             cairo_pdf_surface_set_size(surface, width, height);
138     }
139
140     void endPage(cairo_t* cr)
141     {
142         if (currentPageIsLastPageOfSheet())
143             cairo_show_page(cr);
144     }
145
146     static void printJobComplete(GtkPrintJob* printJob, WebPrintOperationGtkUnix* printOperation, const GError*)
147     {
148         printOperation->m_printJob = 0;
149     }
150
151     static void printJobFinished(WebPrintOperationGtkUnix* printOperation)
152     {
153         printOperation->deref();
154     }
155
156     void endPrint()
157     {
158         cairo_surface_finish(gtk_print_job_get_surface(m_printJob.get(), 0));
159         // Make sure the operation is alive until the job is sent.
160         ref();
161         gtk_print_job_send(m_printJob.get(), reinterpret_cast<GtkPrintJobCompleteFunc>(printJobComplete), this,
162                            reinterpret_cast<GDestroyNotify>(printJobFinished));
163     }
164
165     GRefPtr<GtkPrintJob> m_printJob;
166 };
167 #endif
168
169 #ifdef G_OS_WIN32
170 class WebPrintOperationGtkWin32: public WebPrintOperationGtk {
171 public:
172     WebPrintOperationGtkWin32(WebPage* page, const PrintInfo& printInfo)
173         : WebPrintOperationGtk(page, printInfo)
174     {
175     }
176
177     void startPrint(WebCore::PrintContext* printContext, uint64_t callbackID)
178     {
179         m_printContext = printContext;
180         m_callbackID = callbackID;
181         notImplemented();
182     }
183
184     void startPage(cairo_t* cr)
185     {
186         notImplemented();
187     }
188
189     void endPage(cairo_t* cr)
190     {
191         notImplemented();
192     }
193
194     void endPrint()
195     {
196         notImplemented();
197     }
198 };
199 #endif
200
201 struct PrintPagesData {
202     PrintPagesData(WebPrintOperationGtk* printOperation)
203         : printOperation(printOperation)
204         , totalPrinted(-1)
205         , pageNumber(0)
206         , sheetNumber(0)
207         , firstSheetNumber(0)
208         , numberOfSheets(0)
209         , firstPagePosition(0)
210         , collated(0)
211         , uncollated(0)
212         , isDone(false)
213     {
214         if (printOperation->collateCopies()) {
215             collatedCopies = printOperation->copies();
216             uncollatedCopies = 1;
217         } else {
218             collatedCopies = 1;
219             uncollatedCopies = printOperation->copies();
220         }
221
222         if (printOperation->pagesToPrint() == GTK_PRINT_PAGES_RANGES) {
223             Vector<GtkPageRange> pageRanges;
224             GtkPageRange* ranges = printOperation->pageRanges();
225             size_t rangesCount = printOperation->pageRangesCount();
226             int pageCount = printOperation->pageCount();
227
228             pageRanges.reserveCapacity(rangesCount);
229             for (size_t i = 0; i < rangesCount; ++i) {
230                 if (ranges[i].start >= 0 && ranges[i].start < pageCount && ranges[i].end >= 0 && ranges[i].end < pageCount)
231                     pageRanges.append(ranges[i]);
232                 else if (ranges[i].start >= 0 && ranges[i].start < pageCount && ranges[i].end >= pageCount) {
233                     pageRanges.append(ranges[i]);
234                     pageRanges.last().end = pageCount - 1;
235                 } else if (ranges[i].end >= 0 && ranges[i].end < pageCount && ranges[i].start < 0) {
236                     pageRanges.append(ranges[i]);
237                     pageRanges.last().start = 0;
238                 }
239             }
240
241             for (size_t i = 0; i < pageRanges.size(); ++i) {
242                 for (int j = pageRanges[i].start; j <= pageRanges[i].end; ++j)
243                     pages.append(j);
244             }
245
246         } else {
247             for (int i = 0; i < printOperation->pageCount(); ++i)
248                 pages.append(i);
249         }
250         printOperation->setNumberOfPagesToPrint(pages.size());
251
252         size_t numberUp = printOperation->numberUp();
253         if (numberUp > 1)
254             numberOfSheets = (pages.size() % numberUp) ? pages.size() / numberUp + 1 : pages.size() / numberUp;
255         else
256             numberOfSheets = pages.size();
257
258         bool reverse = printOperation->reverse();
259         switch (printOperation->pageSet()) {
260         case GTK_PAGE_SET_ODD:
261             if (reverse) {
262                 lastPagePosition = std::min(numberUp - 1, pages.size() - 1);
263                 sheetNumber = (numberOfSheets - 1) - (numberOfSheets - 1) % 2;
264             } else
265                 lastPagePosition = std::min(((numberOfSheets - 1) - ((numberOfSheets - 1) % 2)) * numberUp - 1, pages.size() - 1);
266             break;
267         case GTK_PAGE_SET_EVEN:
268             if (reverse) {
269                 lastPagePosition = std::min(2 * numberUp - 1, pages.size() - 1);
270                 sheetNumber = (numberOfSheets - 1) - (1 - (numberOfSheets - 1) % 2);
271             } else {
272                 lastPagePosition = std::min(((numberOfSheets - 1) - (1 - (numberOfSheets - 1) % 2)) * numberUp - 1, pages.size() - 1);
273                 sheetNumber = numberOfSheets > 1 ? 1 : -1;
274             }
275             break;
276         case GTK_PAGE_SET_ALL:
277             if (reverse) {
278                 lastPagePosition = std::min(numberUp - 1, pages.size() - 1);
279                 sheetNumber = pages.size() - 1;
280             } else
281                 lastPagePosition = pages.size() - 1;
282             break;
283         }
284
285         // FIXME: check pagePostion is between [0..pages.size() - 1]
286         // and cancel the operation otherwise when error reporting
287         // is implemented.
288         printOperation->setPagePosition(sheetNumber * numberUp);
289         pageNumber = pages[printOperation->pagePosition()];
290         firstPagePosition = printOperation->pagePosition();
291         firstSheetNumber = sheetNumber;
292     }
293
294     size_t collatedCopiesLeft()
295     {
296         return collatedCopies > 1 ? collatedCopies - collated - 1 : 0;
297     }
298
299     size_t uncollatedCopiesLeft()
300     {
301         return uncollatedCopies > 1 ? uncollatedCopies - uncollated - 1 : 0;
302     }
303
304     size_t copiesLeft()
305     {
306         return collatedCopiesLeft() + uncollatedCopiesLeft();
307     }
308
309     void incrementPageSequence()
310     {
311         if (totalPrinted == -1) {
312             totalPrinted = 0;
313             return;
314         }
315
316         size_t pagePosition = printOperation->pagePosition();
317         if (pagePosition == lastPagePosition && !copiesLeft()) {
318             isDone = true;
319             return;
320         }
321
322         if (pagePosition == lastPagePosition && uncollatedCopiesLeft()) {
323             pagePosition = firstPagePosition;
324             sheetNumber = firstSheetNumber;
325             uncollated++;
326         } else if (printOperation->currentPageIsLastPageOfSheet()) {
327             if (!collatedCopiesLeft()) {
328                 int step = printOperation->pageSet() == GTK_PAGE_SET_ALL ? 1 : 2;
329                 step *= printOperation->reverse() ? -1 : 1;
330                 sheetNumber += step;
331                 collated = 0;
332             } else
333                 collated++;
334             pagePosition = sheetNumber * printOperation->numberUp();
335         } else
336             pagePosition++;
337         printOperation->setPagePosition(pagePosition);
338
339         if (pagePosition >= pages.size() || sheetNumber >= numberOfSheets) {
340             isDone = true;
341             return;
342         }
343         pageNumber = pages[pagePosition];
344         totalPrinted++;
345     }
346
347     RefPtr<WebPrintOperationGtk> printOperation;
348
349     int totalPrinted;
350     size_t totalToPrint;
351     int pageNumber;
352     Vector<size_t> pages;
353     size_t sheetNumber;
354     size_t firstSheetNumber;
355     size_t numberOfSheets;
356     size_t firstPagePosition;
357     size_t lastPagePosition;
358     size_t collated;
359     size_t uncollated;
360     size_t collatedCopies;
361     size_t uncollatedCopies;
362
363     bool isDone : 1;
364 };
365
366 PassRefPtr<WebPrintOperationGtk> WebPrintOperationGtk::create(WebPage* page, const PrintInfo& printInfo)
367 {
368 #ifdef HAVE_GTK_UNIX_PRINTING
369     return adoptRef(new WebPrintOperationGtkUnix(page, printInfo));
370 #elif defined(G_OS_WIN32)
371     return adoptRef(new WebPrintOperationGtkWin32(page, printInfo));
372 #else
373     return 0;
374 #endif
375 }
376
377 WebPrintOperationGtk::WebPrintOperationGtk(WebPage* page, const PrintInfo& printInfo)
378     : m_webPage(page)
379     , m_printSettings(printInfo.printSettings.get())
380     , m_pageSetup(printInfo.pageSetup.get())
381     , m_printContext(0)
382     , m_callbackID(0)
383     , m_xDPI(1)
384     , m_yDPI(1)
385     , m_printPagesIdleId(0)
386     , m_numberOfPagesToPrint(0)
387     , m_pagesToPrint(GTK_PRINT_PAGES_ALL)
388     , m_pagePosition(0)
389     , m_pageRanges(0)
390     , m_pageRangesCount(0)
391     , m_needsRotation(false)
392     , m_numberUp(1)
393     , m_numberUpLayout(0)
394     , m_pageSet(GTK_PAGE_SET_ALL)
395     , m_reverse(false)
396     , m_copies(1)
397     , m_collateCopies(false)
398     , m_scale(1)
399 {
400 }
401
402 WebPrintOperationGtk::~WebPrintOperationGtk()
403 {
404     if (m_printPagesIdleId)
405         g_source_remove(m_printPagesIdleId);
406 }
407
408 int WebPrintOperationGtk::pageCount() const
409 {
410     return m_printContext ? m_printContext->pageCount() : 0;
411 }
412
413 bool WebPrintOperationGtk::currentPageIsFirstPageOfSheet() const
414 {
415     return (m_numberUp < 2 || !((m_pagePosition) % m_numberUp));
416 }
417
418 bool WebPrintOperationGtk::currentPageIsLastPageOfSheet() const
419 {
420     return (m_numberUp < 2 || !((m_pagePosition + 1) % m_numberUp) || m_pagePosition == m_numberOfPagesToPrint - 1);
421 }
422
423 void WebPrintOperationGtk::rotatePageIfNeeded()
424 {
425     if (!m_needsRotation)
426         return;
427
428     GtkPaperSize* paperSize = gtk_page_setup_get_paper_size(m_pageSetup.get());
429     double width = gtk_paper_size_get_width(paperSize, GTK_UNIT_INCH) * m_xDPI;
430     double height = gtk_paper_size_get_height(paperSize, GTK_UNIT_INCH) * m_yDPI;
431
432     cairo_matrix_t matrix;
433     switch (gtk_page_setup_get_orientation(m_pageSetup.get())) {
434     case GTK_PAGE_ORIENTATION_LANDSCAPE:
435         cairo_translate(m_cairoContext.get(), 0, height);
436         cairo_matrix_init(&matrix, 0, -1, 1, 0, 0, 0);
437         cairo_transform(m_cairoContext.get(), &matrix);
438         break;
439     case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
440         cairo_translate(m_cairoContext.get(), width, height);
441         cairo_matrix_init(&matrix, -1, 0, 0, -1, 0, 0);
442         cairo_transform(m_cairoContext.get(), &matrix);
443         break;
444     case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
445         cairo_translate(m_cairoContext.get(), width, 0);
446         cairo_matrix_init(&matrix, 0, 1, -1, 0, 0, 0);
447         cairo_transform(m_cairoContext.get(), &matrix);
448         break;
449     case GTK_PAGE_ORIENTATION_PORTRAIT:
450     default:
451         break;
452     }
453 }
454
455 void WebPrintOperationGtk::getRowsAndColumnsOfPagesPerSheet(size_t& rows, size_t& columns)
456 {
457     switch (m_numberUp) {
458     default:
459         columns = 1;
460         rows = 1;
461         break;
462     case 2:
463         columns = 2;
464         rows = 1;
465         break;
466     case 4:
467         columns = 2;
468         rows = 2;
469         break;
470     case 6:
471         columns = 3;
472         rows = 2;
473         break;
474     case 9:
475         columns = 3;
476         rows = 3;
477         break;
478     case 16:
479         columns = 4;
480         rows = 4;
481         break;
482     }
483 }
484
485 void WebPrintOperationGtk::getPositionOfPageInSheet(size_t rows, size_t columns, int& x, int&y)
486 {
487     switch (m_numberUpLayout) {
488     case GTK_NUMBER_UP_LAYOUT_LEFT_TO_RIGHT_TOP_TO_BOTTOM:
489         x = m_pagePosition % columns;
490         y = (m_pagePosition / columns) % rows;
491         break;
492     case GTK_NUMBER_UP_LAYOUT_LEFT_TO_RIGHT_BOTTOM_TO_TOP:
493         x = m_pagePosition % columns;
494         y = rows - 1 - (m_pagePosition / columns) % rows;
495         break;
496     case GTK_NUMBER_UP_LAYOUT_RIGHT_TO_LEFT_TOP_TO_BOTTOM:
497         x = columns - 1 - m_pagePosition % columns;
498         y = (m_pagePosition / columns) % rows;
499         break;
500     case GTK_NUMBER_UP_LAYOUT_RIGHT_TO_LEFT_BOTTOM_TO_TOP:
501         x = columns - 1 - m_pagePosition % columns;
502         y = rows - 1 - (m_pagePosition / columns) % rows;
503         break;
504     case GTK_NUMBER_UP_LAYOUT_TOP_TO_BOTTOM_LEFT_TO_RIGHT:
505         x = (m_pagePosition / rows) % columns;
506         y = m_pagePosition % rows;
507         break;
508     case GTK_NUMBER_UP_LAYOUT_TOP_TO_BOTTOM_RIGHT_TO_LEFT:
509         x = columns - 1 - (m_pagePosition / rows) % columns;
510         y = m_pagePosition % rows;
511         break;
512     case GTK_NUMBER_UP_LAYOUT_BOTTOM_TO_TOP_LEFT_TO_RIGHT:
513         x = (m_pagePosition / rows) % columns;
514         y = rows - 1 - m_pagePosition % rows;
515         break;
516     case GTK_NUMBER_UP_LAYOUT_BOTTOM_TO_TOP_RIGHT_TO_LEFT:
517         x = columns - 1 - (m_pagePosition / rows) % columns;
518         y = rows - 1 - m_pagePosition % rows;
519         break;
520     }
521 }
522
523 void WebPrintOperationGtk::prepareContextToDraw()
524 {
525     if (m_numberUp < 2) {
526         double left = gtk_page_setup_get_left_margin(m_pageSetup.get(), GTK_UNIT_INCH);
527         double top = gtk_page_setup_get_top_margin(m_pageSetup.get(), GTK_UNIT_INCH);
528         if (m_scale != 1.0)
529             cairo_scale(m_cairoContext.get(), m_scale, m_scale);
530         rotatePageIfNeeded();
531         cairo_translate(m_cairoContext.get(), left * m_xDPI, top * m_yDPI);
532
533         return;
534     }
535
536     rotatePageIfNeeded();
537
538     // Multiple pages per sheet.
539     double marginLeft = gtk_page_setup_get_left_margin(m_pageSetup.get(), GTK_UNIT_POINTS);
540     double marginRight = gtk_page_setup_get_right_margin(m_pageSetup.get(), GTK_UNIT_POINTS);
541     double marginTop = gtk_page_setup_get_top_margin(m_pageSetup.get(), GTK_UNIT_POINTS);
542     double marginBottom = gtk_page_setup_get_bottom_margin(m_pageSetup.get(), GTK_UNIT_POINTS);
543
544     double paperWidth = gtk_page_setup_get_paper_width(m_pageSetup.get(), GTK_UNIT_POINTS);
545     double paperHeight = gtk_page_setup_get_paper_height(m_pageSetup.get(), GTK_UNIT_POINTS);
546
547     size_t rows, columns;
548     getRowsAndColumnsOfPagesPerSheet(rows, columns);
549
550     GtkPageOrientation orientation = gtk_page_setup_get_orientation(m_pageSetup.get());
551     double pageWidth = 0, pageHeight = 0;
552     switch (orientation) {
553     case GTK_PAGE_ORIENTATION_PORTRAIT:
554     case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
555         pageWidth = paperWidth - (marginLeft + marginRight);
556         pageHeight = paperHeight - (marginTop + marginBottom);
557         cairo_translate(m_cairoContext.get(), marginLeft, marginTop);
558         break;
559     case GTK_PAGE_ORIENTATION_LANDSCAPE:
560     case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
561         pageWidth = paperWidth - (marginTop + marginBottom);
562         pageHeight = paperHeight - (marginLeft + marginRight);
563         cairo_translate(m_cairoContext.get(), marginTop, marginLeft);
564
565         size_t tmp = columns;
566         columns = rows;
567         rows = tmp;
568         break;
569     }
570
571     int x, y;
572     getPositionOfPageInSheet(rows, columns, x, y);
573
574     switch (m_numberUp) {
575     case 4:
576     case 9:
577     case 16: {
578         double scaleX = pageWidth / (columns * paperWidth);
579         double scaleY = pageHeight / (rows * paperHeight);
580         double scale = std::min(scaleX, scaleY);
581
582         double stepX = paperWidth * (scaleX / scale);
583         double stepY = paperHeight * (scaleY / scale);
584
585         double width = gtk_page_setup_get_page_width(m_pageSetup.get(), GTK_UNIT_INCH) * m_xDPI;
586         double height = gtk_page_setup_get_page_height(m_pageSetup.get(), GTK_UNIT_INCH) * m_yDPI;
587
588         double offsetX, offsetY;
589         if (marginLeft + marginRight > 0) {
590             offsetX = marginLeft * (stepX - width) / (marginLeft + marginRight);
591             offsetY = marginTop * (stepY - height) / (marginTop + marginBottom);
592         } else {
593             offsetX = (stepX - width) / 2.0;
594             offsetY = (stepY - height) / 2.0;
595         }
596
597         cairo_scale(m_cairoContext.get(), scale, scale);
598         cairo_translate(m_cairoContext.get(), x * stepX + offsetX, y * stepY + offsetY);
599         if (m_scale != 1.0)
600             cairo_scale(m_cairoContext.get(), m_scale, m_scale);
601         break;
602     }
603     case 2:
604     case 6: {
605         double scaleX = pageHeight / (columns * paperWidth);
606         double scaleY = pageWidth / (rows * paperHeight);
607         double scale = std::min(scaleX, scaleY);
608
609         double stepX = paperWidth * (scaleX / scale);
610         double stepY = paperHeight * (scaleY / scale);
611
612         double offsetX = ((stepX - paperWidth) / 2.0 * columns) - marginRight;
613         double offsetY = ((stepY - paperHeight) / 2.0 * rows) + marginTop;
614
615         cairo_scale(m_cairoContext.get(), scale, scale);
616         cairo_translate(m_cairoContext.get(), y * paperHeight + offsetY, (columns - x) * paperWidth + offsetX);
617         if (m_scale != 1.0)
618             cairo_scale(m_cairoContext.get(), m_scale, m_scale);
619         cairo_rotate(m_cairoContext.get(), -G_PI / 2);
620         break;
621     }
622     default:
623         break;
624     }
625 }
626
627 void WebPrintOperationGtk::renderPage(int pageNumber)
628 {
629     startPage(m_cairoContext.get());
630     cairo_save(m_cairoContext.get());
631
632     prepareContextToDraw();
633
634     double pageWidth = gtk_page_setup_get_page_width(m_pageSetup.get(), GTK_UNIT_INCH) * m_xDPI;
635     WebCore::PlatformContextCairo platformContext(m_cairoContext.get());
636     WebCore::GraphicsContext graphicsContext(&platformContext);
637     m_printContext->spoolPage(graphicsContext, pageNumber, pageWidth / m_scale);
638
639     cairo_restore(m_cairoContext.get());
640     endPage(m_cairoContext.get());
641 }
642
643 gboolean WebPrintOperationGtk::printPagesIdle(gpointer userData)
644 {
645     PrintPagesData* data = static_cast<PrintPagesData*>(userData);
646
647     data->incrementPageSequence();
648     if (data->isDone)
649         return FALSE;
650
651     data->printOperation->renderPage(data->pageNumber);
652     return TRUE;
653 }
654
655 void WebPrintOperationGtk::printPagesIdleDone(gpointer userData)
656 {
657     PrintPagesData* data = static_cast<PrintPagesData*>(userData);
658
659     data->printOperation->printDone();
660     delete data;
661 }
662
663 void WebPrintOperationGtk::printDone()
664 {
665     m_printPagesIdleId = 0;
666
667     endPrint();
668     // Job is now sent to the printer, we can notify the UI process that we are done.
669     m_webPage->send(Messages::WebPageProxy::VoidCallback(m_callbackID));
670     m_cairoContext = 0;
671 }
672
673 void WebPrintOperationGtk::print(cairo_surface_t* surface, double xDPI, double yDPI)
674 {
675     ASSERT(m_printContext);
676
677     m_xDPI = xDPI;
678     m_yDPI = yDPI;
679     m_cairoContext = adoptRef(cairo_create(surface));
680
681     PrintPagesData* data = new PrintPagesData(this);
682     m_printPagesIdleId = gdk_threads_add_idle_full(G_PRIORITY_DEFAULT_IDLE + 10, printPagesIdle,
683                                                    data, printPagesIdleDone);
684 }
685
686 }