printsupport: set duplex setting correctly to CUPS printer
[qt:qt.git] / src / gui / painting / qpdf.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 #include "qplatformdefs.h"
42 #include <qdebug.h>
43 #include "qpdf_p.h"
44 #include <qfile.h>
45 #include <qtemporaryfile.h>
46 #include <private/qmath_p.h>
47 #include "private/qcups_p.h"
48 #include "qprinterinfo.h"
49 #include <qnumeric.h>
50 #include "private/qfont_p.h"
51
52 #ifdef Q_OS_UNIX
53 #include "private/qcore_unix_p.h" // overrides QT_OPEN
54 #endif
55
56 QT_BEGIN_NAMESPACE
57
58 #ifndef QT_NO_PRINTER
59
60 extern QSizeF qt_paperSizeToQSizeF(QPrinter::PaperSize size);
61
62 /* also adds a space at the end of the number */
63 const char *qt_real_to_string(qreal val, char *buf) {
64     const char *ret = buf;
65
66     if (qIsNaN(val)) {
67         *(buf++) = '0';
68         *(buf++) = ' ';
69         *buf = 0;
70         return ret;
71     }
72
73     if (val < 0) {
74         *(buf++) = '-';
75         val = -val;
76     }
77     unsigned int ival = (unsigned int) val;
78     qreal frac = val - (qreal)ival;
79
80     int ifrac = (int)(frac * 1000000000);
81     if (ifrac == 1000000000) {
82         ++ival;
83         ifrac = 0;
84     }
85     char output[256];
86     int i = 0;
87     while (ival) {
88         output[i] = '0' + (ival % 10);
89         ++i;
90         ival /= 10;
91     }
92     int fact = 100000000;
93     if (i == 0) {
94         *(buf++) = '0';
95     } else {
96         while (i) {
97             *(buf++) = output[--i];
98             fact /= 10;
99             ifrac /= 10;
100         }
101     }
102
103     if (ifrac) {
104         *(buf++) =  '.';
105         while (fact) {
106             *(buf++) = '0' + ((ifrac/fact) % 10);
107             fact /= 10;
108         }
109     }
110     *(buf++) = ' ';
111     *buf = 0;
112     return ret;
113 }
114
115 const char *qt_int_to_string(int val, char *buf) {
116     const char *ret = buf;
117     if (val < 0) {
118         *(buf++) = '-';
119         val = -val;
120     }
121     char output[256];
122     int i = 0;
123     while (val) {
124         output[i] = '0' + (val % 10);
125         ++i;
126         val /= 10;
127     }
128     if (i == 0) {
129         *(buf++) = '0';
130     } else {
131         while (i)
132             *(buf++) = output[--i];
133     }
134     *(buf++) = ' ';
135     *buf = 0;
136     return ret;
137 }
138
139
140 namespace QPdf {
141     ByteStream::ByteStream(QByteArray *byteArray, bool fileBacking)
142             : dev(new QBuffer(byteArray)),
143             fileBackingEnabled(fileBacking),
144             fileBackingActive(false),
145             handleDirty(false)
146     {
147         dev->open(QIODevice::ReadWrite | QIODevice::Append);
148     }
149
150     ByteStream::ByteStream(bool fileBacking)
151             : dev(new QBuffer(&ba)),
152             fileBackingEnabled(fileBacking),
153             fileBackingActive(false),
154             handleDirty(false)
155     {
156         dev->open(QIODevice::ReadWrite);
157     }
158
159     ByteStream::~ByteStream()
160     {
161         delete dev;
162     }
163
164     ByteStream &ByteStream::operator <<(char chr)
165     {
166         if (handleDirty) prepareBuffer();
167         dev->write(&chr, 1);
168         return *this;
169     }
170
171     ByteStream &ByteStream::operator <<(const char *str)
172     {
173         if (handleDirty) prepareBuffer();
174         dev->write(str, strlen(str));
175         return *this;
176     }
177
178     ByteStream &ByteStream::operator <<(const QByteArray &str)
179     {
180         if (handleDirty) prepareBuffer();
181         dev->write(str);
182         return *this;
183     }
184
185     ByteStream &ByteStream::operator <<(const ByteStream &src)
186     {
187         Q_ASSERT(!src.dev->isSequential());
188         if (handleDirty) prepareBuffer();
189         // We do play nice here, even though it looks ugly.
190         // We save the position and restore it afterwards.
191         ByteStream &s = const_cast<ByteStream&>(src);
192         qint64 pos = s.dev->pos();
193         s.dev->reset();
194         while (!s.dev->atEnd()) {
195             QByteArray buf = s.dev->read(chunkSize());
196             dev->write(buf);
197         }
198         s.dev->seek(pos);
199         return *this;
200     }
201
202     ByteStream &ByteStream::operator <<(qreal val) {
203         char buf[256];
204         qt_real_to_string(val, buf);
205         *this << buf;
206         return *this;
207     }
208
209     ByteStream &ByteStream::operator <<(int val) {
210         char buf[256];
211         qt_int_to_string(val, buf);
212         *this << buf;
213         return *this;
214     }
215
216     ByteStream &ByteStream::operator <<(const QPointF &p) {
217         char buf[256];
218         qt_real_to_string(p.x(), buf);
219         *this << buf;
220         qt_real_to_string(p.y(), buf);
221         *this << buf;
222         return *this;
223     }
224
225     QIODevice *ByteStream::stream()
226     {
227         dev->reset();
228         handleDirty = true;
229         return dev;
230     }
231
232     void ByteStream::clear()
233     {
234         dev->open(QIODevice::ReadWrite | QIODevice::Truncate);
235     }
236
237     void ByteStream::constructor_helper(QByteArray *ba)
238     {
239         delete dev;
240         dev = new QBuffer(ba);
241         dev->open(QIODevice::ReadWrite);
242     }
243
244     void ByteStream::prepareBuffer()
245     {
246         Q_ASSERT(!dev->isSequential());
247         qint64 size = dev->size();
248         if (fileBackingEnabled && !fileBackingActive
249                 && size > maxMemorySize()) {
250             // Switch to file backing.
251             QTemporaryFile *newFile = new QTemporaryFile;
252             newFile->open();
253             dev->reset();
254             while (!dev->atEnd()) {
255                 QByteArray buf = dev->read(chunkSize());
256                 newFile->write(buf);
257             }
258             delete dev;
259             dev = newFile;
260             ba.clear();
261             fileBackingActive = true;
262         }
263         if (dev->pos() != size) {
264             dev->seek(size);
265             handleDirty = false;
266         }
267     }
268 }
269
270 #define QT_PATH_ELEMENT(elm)
271
272 QByteArray QPdf::generatePath(const QPainterPath &path, const QTransform &matrix, PathFlags flags)
273 {
274     QByteArray result;
275     if (!path.elementCount())
276         return result;
277
278     ByteStream s(&result);
279
280     int start = -1;
281     for (int i = 0; i < path.elementCount(); ++i) {
282         const QPainterPath::Element &elm = path.elementAt(i);
283         switch (elm.type) {
284         case QPainterPath::MoveToElement:
285             if (start >= 0
286                 && path.elementAt(start).x == path.elementAt(i-1).x
287                 && path.elementAt(start).y == path.elementAt(i-1).y)
288                 s << "h\n";
289             s << matrix.map(QPointF(elm.x, elm.y)) << "m\n";
290             start = i;
291                 break;
292         case QPainterPath::LineToElement:
293             s << matrix.map(QPointF(elm.x, elm.y)) << "l\n";
294             break;
295         case QPainterPath::CurveToElement:
296             Q_ASSERT(path.elementAt(i+1).type == QPainterPath::CurveToDataElement);
297             Q_ASSERT(path.elementAt(i+2).type == QPainterPath::CurveToDataElement);
298             s << matrix.map(QPointF(elm.x, elm.y))
299               << matrix.map(QPointF(path.elementAt(i+1).x, path.elementAt(i+1).y))
300               << matrix.map(QPointF(path.elementAt(i+2).x, path.elementAt(i+2).y))
301               << "c\n";
302             i += 2;
303             break;
304         default:
305             qFatal("QPdf::generatePath(), unhandled type: %d", elm.type);
306         }
307     }
308     if (start >= 0
309         && path.elementAt(start).x == path.elementAt(path.elementCount()-1).x
310         && path.elementAt(start).y == path.elementAt(path.elementCount()-1).y)
311         s << "h\n";
312
313     Qt::FillRule fillRule = path.fillRule();
314
315     const char *op = "";
316     switch (flags) {
317     case ClipPath:
318         op = (fillRule == Qt::WindingFill) ? "W n\n" : "W* n\n";
319         break;
320     case FillPath:
321         op = (fillRule == Qt::WindingFill) ? "f\n" : "f*\n";
322         break;
323     case StrokePath:
324         op = "S\n";
325         break;
326     case FillAndStrokePath:
327         op = (fillRule == Qt::WindingFill) ? "B\n" : "B*\n";
328         break;
329     }
330     s << op;
331     return result;
332 }
333
334 QByteArray QPdf::generateMatrix(const QTransform &matrix)
335 {
336     QByteArray result;
337     ByteStream s(&result);
338     s << matrix.m11()
339       << matrix.m12()
340       << matrix.m21()
341       << matrix.m22()
342       << matrix.dx()
343       << matrix.dy()
344       << "cm\n";
345     return result;
346 }
347
348 QByteArray QPdf::generateDashes(const QPen &pen)
349 {
350     QByteArray result;
351     ByteStream s(&result);
352     s << '[';
353
354     QVector<qreal> dasharray = pen.dashPattern();
355     qreal w = pen.widthF();
356     if (w < 0.001)
357         w = 1;
358     for (int i = 0; i < dasharray.size(); ++i) {
359         qreal dw = dasharray.at(i)*w;
360         if (dw < 0.0001) dw = 0.0001;
361         s << dw;
362     }
363     s << ']';
364     //qDebug() << "dasharray: pen has" << dasharray;
365     //qDebug() << "  => " << result;
366     return result;
367 }
368
369
370
371 static const char* pattern_for_brush[] = {
372     0, // NoBrush
373     0, // SolidPattern
374     "0 J\n"
375     "6 w\n"
376     "[] 0 d\n"
377     "4 0 m\n"
378     "4 8 l\n"
379     "0 4 m\n"
380     "8 4 l\n"
381     "S\n", // Dense1Pattern
382
383     "0 J\n"
384     "2 w\n"
385     "[6 2] 1 d\n"
386     "0 0 m\n"
387     "0 8 l\n"
388     "8 0 m\n"
389     "8 8 l\n"
390     "S\n"
391     "[] 0 d\n"
392     "2 0 m\n"
393     "2 8 l\n"
394     "6 0 m\n"
395     "6 8 l\n"
396     "S\n"
397     "[6 2] -3 d\n"
398     "4 0 m\n"
399     "4 8 l\n"
400     "S\n", // Dense2Pattern
401
402     "0 J\n"
403     "2 w\n"
404     "[6 2] 1 d\n"
405     "0 0 m\n"
406     "0 8 l\n"
407     "8 0 m\n"
408     "8 8 l\n"
409     "S\n"
410     "[2 2] -1 d\n"
411     "2 0 m\n"
412     "2 8 l\n"
413     "6 0 m\n"
414     "6 8 l\n"
415     "S\n"
416     "[6 2] -3 d\n"
417     "4 0 m\n"
418     "4 8 l\n"
419     "S\n", // Dense3Pattern
420
421     "0 J\n"
422     "2 w\n"
423     "[2 2] 1 d\n"
424     "0 0 m\n"
425     "0 8 l\n"
426     "8 0 m\n"
427     "8 8 l\n"
428     "S\n"
429     "[2 2] -1 d\n"
430     "2 0 m\n"
431     "2 8 l\n"
432     "6 0 m\n"
433     "6 8 l\n"
434     "S\n"
435     "[2 2] 1 d\n"
436     "4 0 m\n"
437     "4 8 l\n"
438     "S\n", // Dense4Pattern
439
440     "0 J\n"
441     "2 w\n"
442     "[2 6] -1 d\n"
443     "0 0 m\n"
444     "0 8 l\n"
445     "8 0 m\n"
446     "8 8 l\n"
447     "S\n"
448     "[2 2] 1 d\n"
449     "2 0 m\n"
450     "2 8 l\n"
451     "6 0 m\n"
452     "6 8 l\n"
453     "S\n"
454     "[2 6] 3 d\n"
455     "4 0 m\n"
456     "4 8 l\n"
457     "S\n", // Dense5Pattern
458
459     "0 J\n"
460     "2 w\n"
461     "[2 6] -1 d\n"
462     "0 0 m\n"
463     "0 8 l\n"
464     "8 0 m\n"
465     "8 8 l\n"
466     "S\n"
467     "[2 6] 3 d\n"
468     "4 0 m\n"
469     "4 8 l\n"
470     "S\n", // Dense6Pattern
471
472     "0 J\n"
473     "2 w\n"
474     "[2 6] -1 d\n"
475     "0 0 m\n"
476     "0 8 l\n"
477     "8 0 m\n"
478     "8 8 l\n"
479     "S\n", // Dense7Pattern
480
481     "1 w\n"
482     "0 4 m\n"
483     "8 4 l\n"
484     "S\n", // HorPattern
485
486     "1 w\n"
487     "4 0 m\n"
488     "4 8 l\n"
489     "S\n", // VerPattern
490
491     "1 w\n"
492     "4 0 m\n"
493     "4 8 l\n"
494     "0 4 m\n"
495     "8 4 l\n"
496     "S\n", // CrossPattern
497
498     "1 w\n"
499     "-1 5 m\n"
500     "5 -1 l\n"
501     "3 9 m\n"
502     "9 3 l\n"
503     "S\n", // BDiagPattern
504
505     "1 w\n"
506     "-1 3 m\n"
507     "5 9 l\n"
508     "3 -1 m\n"
509     "9 5 l\n"
510     "S\n", // FDiagPattern
511
512     "1 w\n"
513     "-1 3 m\n"
514     "5 9 l\n"
515     "3 -1 m\n"
516     "9 5 l\n"
517     "-1 5 m\n"
518     "5 -1 l\n"
519     "3 9 m\n"
520     "9 3 l\n"
521     "S\n", // DiagCrossPattern
522 };
523
524 QByteArray QPdf::patternForBrush(const QBrush &b)
525 {
526     int style = b.style();
527     if (style > Qt::DiagCrossPattern)
528         return QByteArray();
529     return pattern_for_brush[style];
530 }
531
532 #ifdef USE_NATIVE_GRADIENTS
533 static void writeTriangleLine(uchar *&data, int xpos, int ypos, int xoff, int yoff, uint rgb, uchar flag, bool alpha)
534 {
535     data[0] =  flag;
536     data[1] = (uchar)(xpos >> 16);
537     data[2] = (uchar)(xpos >> 8);
538     data[3] = (uchar)(xpos >> 0);
539     data[4] = (uchar)(ypos >> 16);
540     data[5] = (uchar)(ypos >> 8);
541     data[6] = (uchar)(ypos >> 0);
542     data += 7;
543     if (alpha) {
544         *data++ = (uchar)qAlpha(rgb);
545     } else {
546         *data++ = (uchar)qRed(rgb);
547         *data++ = (uchar)qGreen(rgb);
548         *data++ = (uchar)qBlue(rgb);
549     }
550     xpos += xoff;
551     ypos += yoff;
552     data[0] =  flag;
553     data[1] = (uchar)(xpos >> 16);
554     data[2] = (uchar)(xpos >> 8);
555     data[3] = (uchar)(xpos >> 0);
556     data[4] = (uchar)(ypos >> 16);
557     data[5] = (uchar)(ypos >> 8);
558     data[6] = (uchar)(ypos >> 0);
559     data += 7;
560     if (alpha) {
561         *data++ = (uchar)qAlpha(rgb);
562     } else {
563         *data++ = (uchar)qRed(rgb);
564         *data++ = (uchar)qGreen(rgb);
565         *data++ = (uchar)qBlue(rgb);
566     }
567 }
568
569
570 QByteArray QPdf::generateLinearGradientShader(const QLinearGradient *gradient, const QPointF *page_rect, bool alpha)
571 {
572     // generate list of triangles with colors
573     QPointF start = gradient->start();
574     QPointF stop = gradient->finalStop();
575     QGradientStops stops = gradient->stops();
576     QPointF offset = stop - start;
577     QGradient::Spread spread = gradient->spread();
578
579     if (gradient->spread() == QGradient::ReflectSpread) {
580         offset *= 2;
581         for (int i = stops.size() - 2; i >= 0; --i) {
582             QGradientStop stop = stops.at(i);
583             stop.first = 2. - stop.first;
584             stops.append(stop);
585         }
586         for (int i = 0 ; i < stops.size(); ++i)
587             stops[i].first /= 2.;
588     }
589
590     QPointF orthogonal(offset.y(), -offset.x());
591     qreal length = offset.x()*offset.x() + offset.y()*offset.y();
592
593     // find the max and min values in offset and orth direction that are needed to cover
594     // the whole page
595     int off_min = INT_MAX;
596     int off_max = INT_MIN;
597     qreal ort_min = INT_MAX;
598     qreal ort_max = INT_MIN;
599     for (int i = 0; i < 4; ++i) {
600         qreal off = ((page_rect[i].x() - start.x()) * offset.x() + (page_rect[i].y() - start.y()) * offset.y())/length;
601         qreal ort = ((page_rect[i].x() - start.x()) * orthogonal.x() + (page_rect[i].y() - start.y()) * orthogonal.y())/length;
602         off_min = qMin(off_min, qFloor(off));
603         off_max = qMax(off_max, qCeil(off));
604         ort_min = qMin(ort_min, ort);
605         ort_max = qMax(ort_max, ort);
606     }
607     ort_min -= 1;
608     ort_max += 1;
609
610     start += off_min * offset + ort_min * orthogonal;
611     orthogonal *= (ort_max - ort_min);
612     int num = off_max - off_min;
613
614     QPointF gradient_rect[4] = { start,
615                                  start + orthogonal,
616                                  start + num*offset,
617                                  start + num*offset + orthogonal };
618     qreal xmin = gradient_rect[0].x();
619     qreal xmax = gradient_rect[0].x();
620     qreal ymin = gradient_rect[0].y();
621     qreal ymax = gradient_rect[0].y();
622     for (int i = 1; i < 4; ++i) {
623         xmin = qMin(xmin, gradient_rect[i].x());
624         xmax = qMax(xmax, gradient_rect[i].x());
625         ymin = qMin(ymin, gradient_rect[i].y());
626         ymax = qMax(ymax, gradient_rect[i].y());
627     }
628     xmin -= 1000;
629     xmax += 1000;
630     ymin -= 1000;
631     ymax += 1000;
632     start -= QPointF(xmin, ymin);
633     qreal factor_x = qreal(1<<24)/(xmax - xmin);
634     qreal factor_y = qreal(1<<24)/(ymax - ymin);
635     int xoff = (int)(orthogonal.x()*factor_x);
636     int yoff = (int)(orthogonal.y()*factor_y);
637
638     QByteArray triangles;
639     triangles.resize(spread == QGradient::PadSpread ? 20*(stops.size()+2) : 20*num*stops.size());
640     uchar *data = (uchar *) triangles.data();
641     if (spread == QGradient::PadSpread) {
642         if (off_min > 0 || off_max < 1) {
643             // linear gradient outside of page
644             const QGradientStop &current_stop = off_min > 0 ? stops.at(stops.size()-1) : stops.at(0);
645             uint rgb = current_stop.second.rgba();
646             int xpos = (int)(start.x()*factor_x);
647             int ypos = (int)(start.y()*factor_y);
648             writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, 0, alpha);
649             start += num*offset;
650             xpos = (int)(start.x()*factor_x);
651             ypos = (int)(start.y()*factor_y);
652             writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, 1, alpha);
653         } else {
654             int flag = 0;
655             if (off_min < 0) {
656                 uint rgb = stops.at(0).second.rgba();
657                 int xpos = (int)(start.x()*factor_x);
658                 int ypos = (int)(start.y()*factor_y);
659                 writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
660                 start -= off_min*offset;
661                 flag = 1;
662             }
663             for (int s = 0; s < stops.size(); ++s) {
664                 const QGradientStop &current_stop = stops.at(s);
665                 uint rgb = current_stop.second.rgba();
666                 int xpos = (int)(start.x()*factor_x);
667                 int ypos = (int)(start.y()*factor_y);
668                 writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
669                 if (s < stops.size()-1)
670                     start += offset*(stops.at(s+1).first - stops.at(s).first);
671                 flag = 1;
672             }
673             if (off_max > 1) {
674                 start += (off_max - 1)*offset;
675                 uint rgb = stops.at(stops.size()-1).second.rgba();
676                 int xpos = (int)(start.x()*factor_x);
677                 int ypos = (int)(start.y()*factor_y);
678                 writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
679             }
680         }
681     } else {
682         for (int i = 0; i < num; ++i) {
683             uchar flag = 0;
684             for (int s = 0; s < stops.size(); ++s) {
685                 uint rgb = stops.at(s).second.rgba();
686                 int xpos = (int)(start.x()*factor_x);
687                 int ypos = (int)(start.y()*factor_y);
688                 writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
689                 if (s < stops.size()-1)
690                     start += offset*(stops.at(s+1).first - stops.at(s).first);
691                 flag = 1;
692             }
693         }
694     }
695     triangles.resize((char *)data - triangles.constData());
696
697     QByteArray shader;
698     QPdf::ByteStream s(&shader);
699     s << "<<\n"
700         "/ShadingType 4\n"
701         "/ColorSpace " << (alpha ? "/DeviceGray\n" : "/DeviceRGB\n") <<
702         "/AntiAlias true\n"
703         "/BitsPerCoordinate 24\n"
704         "/BitsPerComponent 8\n"
705         "/BitsPerFlag 8\n"
706         "/Decode [" << xmin << xmax << ymin << ymax << (alpha ? "0 1]\n" : "0 1 0 1 0 1]\n") <<
707         "/AntiAlias true\n"
708         "/Length " << triangles.length() << "\n"
709         ">>\n"
710         "stream\n" << triangles << "endstream\n"
711         "endobj\n";
712     return shader;
713 }
714 #endif
715
716 static void moveToHook(qfixed x, qfixed y, void *data)
717 {
718     QPdf::Stroker *t = (QPdf::Stroker *)data;
719     if (!t->first)
720         *t->stream << "h\n";
721     if (!t->cosmeticPen)
722         t->matrix.map(x, y, &x, &y);
723     *t->stream << x << y << "m\n";
724     t->first = false;
725 }
726
727 static void lineToHook(qfixed x, qfixed y, void *data)
728 {
729     QPdf::Stroker *t = (QPdf::Stroker *)data;
730     if (!t->cosmeticPen)
731         t->matrix.map(x, y, &x, &y);
732     *t->stream << x << y << "l\n";
733 }
734
735 static void cubicToHook(qfixed c1x, qfixed c1y,
736                         qfixed c2x, qfixed c2y,
737                         qfixed ex, qfixed ey,
738                         void *data)
739 {
740     QPdf::Stroker *t = (QPdf::Stroker *)data;
741     if (!t->cosmeticPen) {
742         t->matrix.map(c1x, c1y, &c1x, &c1y);
743         t->matrix.map(c2x, c2y, &c2x, &c2y);
744         t->matrix.map(ex, ey, &ex, &ey);
745     }
746     *t->stream << c1x << c1y
747                << c2x << c2y
748                << ex << ey
749                << "c\n";
750 }
751
752 QPdf::Stroker::Stroker()
753     : stream(0),
754     first(true),
755     dashStroker(&basicStroker)
756 {
757     stroker = &basicStroker;
758     basicStroker.setMoveToHook(moveToHook);
759     basicStroker.setLineToHook(lineToHook);
760     basicStroker.setCubicToHook(cubicToHook);
761     cosmeticPen = true;
762     basicStroker.setStrokeWidth(.1);
763 }
764
765 void QPdf::Stroker::setPen(const QPen &pen)
766 {
767     if (pen.style() == Qt::NoPen) {
768         stroker = 0;
769         return;
770     }
771     qreal w = pen.widthF();
772     bool zeroWidth = w < 0.0001;
773     cosmeticPen = pen.isCosmetic();
774     if (zeroWidth)
775         w = .1;
776
777     basicStroker.setStrokeWidth(w);
778     basicStroker.setCapStyle(pen.capStyle());
779     basicStroker.setJoinStyle(pen.joinStyle());
780     basicStroker.setMiterLimit(pen.miterLimit());
781
782     QVector<qreal> dashpattern = pen.dashPattern();
783     if (zeroWidth) {
784         for (int i = 0; i < dashpattern.size(); ++i)
785             dashpattern[i] *= 10.;
786     }
787     if (!dashpattern.isEmpty()) {
788         dashStroker.setDashPattern(dashpattern);
789         dashStroker.setDashOffset(pen.dashOffset());
790         stroker = &dashStroker;
791     } else {
792         stroker = &basicStroker;
793     }
794 }
795
796 void QPdf::Stroker::strokePath(const QPainterPath &path)
797 {
798     if (!stroker)
799         return;
800     first = true;
801
802     stroker->strokePath(path, this, cosmeticPen ? matrix : QTransform());
803     *stream << "h f\n";
804 }
805
806 QByteArray QPdf::ascii85Encode(const QByteArray &input)
807 {
808     int isize = input.size()/4*4;
809     QByteArray output;
810     output.resize(input.size()*5/4+7);
811     char *out = output.data();
812     const uchar *in = (const uchar *)input.constData();
813     for (int i = 0; i < isize; i += 4) {
814         uint val = (((uint)in[i])<<24) + (((uint)in[i+1])<<16) + (((uint)in[i+2])<<8) + (uint)in[i+3];
815         if (val == 0) {
816             *out = 'z';
817             ++out;
818         } else {
819             char base[5];
820             base[4] = val % 85;
821             val /= 85;
822             base[3] = val % 85;
823             val /= 85;
824             base[2] = val % 85;
825             val /= 85;
826             base[1] = val % 85;
827             val /= 85;
828             base[0] = val % 85;
829             *(out++) = base[0] + '!';
830             *(out++) = base[1] + '!';
831             *(out++) = base[2] + '!';
832             *(out++) = base[3] + '!';
833             *(out++) = base[4] + '!';
834         }
835     }
836     //write the last few bytes
837     int remaining = input.size() - isize;
838     if (remaining) {
839         uint val = 0;
840         for (int i = isize; i < input.size(); ++i)
841             val = (val << 8) + in[i];
842         val <<= 8*(4-remaining);
843         char base[5];
844         base[4] = val % 85;
845         val /= 85;
846         base[3] = val % 85;
847         val /= 85;
848         base[2] = val % 85;
849         val /= 85;
850         base[1] = val % 85;
851         val /= 85;
852         base[0] = val % 85;
853         for (int i = 0; i < remaining+1; ++i)
854             *(out++) = base[i] + '!';
855     }
856     *(out++) = '~';
857     *(out++) = '>';
858     output.resize(out-output.data());
859     return output;
860 }
861
862 const char *QPdf::toHex(ushort u, char *buffer)
863 {
864     int i = 3;
865     while (i >= 0) {
866         ushort hex = (u & 0x000f);
867         if (hex < 0x0a)
868             buffer[i] = '0'+hex;
869         else
870             buffer[i] = 'A'+(hex-0x0a);
871         u = u >> 4;
872         i--;
873     }
874     buffer[4] = '\0';
875     return buffer;
876 }
877
878 const char *QPdf::toHex(uchar u, char *buffer)
879 {
880     int i = 1;
881     while (i >= 0) {
882         ushort hex = (u & 0x000f);
883         if (hex < 0x0a)
884             buffer[i] = '0'+hex;
885         else
886             buffer[i] = 'A'+(hex-0x0a);
887         u = u >> 4;
888         i--;
889     }
890     buffer[2] = '\0';
891     return buffer;
892 }
893
894 #define Q_MM(n) int((n * 720 + 127) / 254)
895 #define Q_IN(n) int(n * 72)
896
897 static const char * const psToStr[QPrinter::NPaperSize+1] =
898 {
899     "A4", "B5", "Letter", "Legal", "Executive",
900     "A0", "A1", "A2", "A3", "A5", "A6", "A7", "A8", "A9", "B0", "B1",
901     "B10", "B2", "B3", "B4", "B6", "B7", "B8", "B9", "C5E", "Comm10E",
902     "DLE", "Folio", "Ledger", "Tabloid", 0
903 };
904
905 QPdf::PaperSize QPdf::paperSize(QPrinter::PaperSize paperSize)
906 {
907     QSizeF s = qt_paperSizeToQSizeF(paperSize);
908     PaperSize p = { Q_MM(s.width()), Q_MM(s.height()) };
909     return p;
910 }
911
912 const char *QPdf::paperSizeToString(QPrinter::PaperSize paperSize)
913 {
914     return psToStr[paperSize];
915 }
916
917 // -------------------------- base engine, shared code between PS and PDF -----------------------
918
919 QPdfBaseEngine::QPdfBaseEngine(QPdfBaseEnginePrivate &dd, PaintEngineFeatures f)
920     : QAlphaPaintEngine(dd, f)
921 {
922     Q_D(QPdfBaseEngine);
923 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
924     if (QCUPSSupport::isAvailable()) {
925         QCUPSSupport cups;
926         const cups_dest_t* printers = cups.availablePrinters();
927         int prnCount = cups.availablePrintersCount();
928
929         for (int i = 0; i <  prnCount; ++i) {
930             if (printers[i].is_default) {
931                 d->printerName = QString::fromLocal8Bit(printers[i].name);
932                 break;
933             }
934         }
935
936     } else
937 #endif
938     {
939         d->printerName = QString::fromLocal8Bit(qgetenv("PRINTER"));
940         if (d->printerName.isEmpty())
941             d->printerName = QString::fromLocal8Bit(qgetenv("LPDEST"));
942         if (d->printerName.isEmpty())
943             d->printerName = QString::fromLocal8Bit(qgetenv("NPRINTER"));
944         if (d->printerName.isEmpty())
945             d->printerName = QString::fromLocal8Bit(qgetenv("NGPRINTER"));
946     }
947 }
948
949 void QPdfBaseEngine::drawPoints (const QPointF *points, int pointCount)
950 {
951     if (!points)
952         return;
953
954     Q_D(QPdfBaseEngine);
955     QPainterPath p;
956     for (int i=0; i!=pointCount;++i) {
957         p.moveTo(points[i]);
958         p.lineTo(points[i] + QPointF(0, 0.001));
959     }
960
961     bool hadBrush = d->hasBrush;
962     d->hasBrush = false;
963     drawPath(p);
964     d->hasBrush = hadBrush;
965 }
966
967 void QPdfBaseEngine::drawLines (const QLineF *lines, int lineCount)
968 {
969     if (!lines)
970         return;
971
972     Q_D(QPdfBaseEngine);
973     QPainterPath p;
974     for (int i=0; i!=lineCount;++i) {
975         p.moveTo(lines[i].p1());
976         p.lineTo(lines[i].p2());
977     }
978     bool hadBrush = d->hasBrush;
979     d->hasBrush = false;
980     drawPath(p);
981     d->hasBrush = hadBrush;
982 }
983
984 void QPdfBaseEngine::drawRects (const QRectF *rects, int rectCount)
985 {
986     if (!rects)
987         return;
988
989     Q_D(QPdfBaseEngine);
990     if (d->useAlphaEngine) {
991         QAlphaPaintEngine::drawRects(rects, rectCount);
992         if (!continueCall())
993             return;
994     }
995
996     if (d->clipEnabled && d->allClipped)
997         return;
998     if (!d->hasPen && !d->hasBrush)
999         return;
1000
1001     QBrush penBrush = d->pen.brush();
1002     if (d->simplePen || !d->hasPen) {
1003         // draw strokes natively in this case for better output
1004         if(!d->simplePen && !d->stroker.matrix.isIdentity())
1005             *d->currentPage << "q\n" << QPdf::generateMatrix(d->stroker.matrix);
1006         for (int i = 0; i < rectCount; ++i)
1007             *d->currentPage << rects[i].x() << rects[i].y() << rects[i].width() << rects[i].height() << "re\n";
1008         *d->currentPage << (d->hasPen ? (d->hasBrush ? "B\n" : "S\n") : "f\n");
1009         if(!d->simplePen && !d->stroker.matrix.isIdentity())
1010             *d->currentPage << "Q\n";
1011     } else {
1012         QPainterPath p;
1013         for (int i=0; i!=rectCount; ++i)
1014             p.addRect(rects[i]);
1015         drawPath(p);
1016     }
1017 }
1018
1019 void QPdfBaseEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1020 {
1021     Q_D(QPdfBaseEngine);
1022
1023     if (d->useAlphaEngine) {
1024         QAlphaPaintEngine::drawPolygon(points, pointCount, mode);
1025         if (!continueCall())
1026             return;
1027     }
1028
1029     if (!points || !pointCount)
1030         return;
1031
1032     bool hb = d->hasBrush;
1033     QPainterPath p;
1034
1035     switch(mode) {
1036         case OddEvenMode:
1037             p.setFillRule(Qt::OddEvenFill);
1038             break;
1039         case ConvexMode:
1040         case WindingMode:
1041             p.setFillRule(Qt::WindingFill);
1042             break;
1043         case PolylineMode:
1044             d->hasBrush = false;
1045             break;
1046         default:
1047             break;
1048     }
1049
1050     p.moveTo(points[0]);
1051     for (int i = 1; i < pointCount; ++i)
1052         p.lineTo(points[i]);
1053
1054     if (mode != PolylineMode)
1055         p.closeSubpath();
1056     drawPath(p);
1057
1058     d->hasBrush = hb;
1059 }
1060
1061 void QPdfBaseEngine::drawPath (const QPainterPath &p)
1062 {
1063     Q_D(QPdfBaseEngine);
1064
1065     if (d->useAlphaEngine) {
1066         QAlphaPaintEngine::drawPath(p);
1067         if (!continueCall())
1068             return;
1069     }
1070
1071     if (d->clipEnabled && d->allClipped)
1072         return;
1073     if (!d->hasPen && !d->hasBrush)
1074         return;
1075
1076     if (d->simplePen) {
1077         // draw strokes natively in this case for better output
1078         *d->currentPage << QPdf::generatePath(p, QTransform(), d->hasBrush ? QPdf::FillAndStrokePath : QPdf::StrokePath);
1079     } else {
1080         if (d->hasBrush)
1081             *d->currentPage << QPdf::generatePath(p, d->stroker.matrix, QPdf::FillPath);
1082         if (d->hasPen) {
1083             *d->currentPage << "q\n";
1084             QBrush b = d->brush;
1085             d->brush = d->pen.brush();
1086             setBrush();
1087             d->stroker.strokePath(p);
1088             *d->currentPage << "Q\n";
1089             d->brush = b;
1090         }
1091     }
1092 }
1093
1094 void QPdfBaseEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
1095 {
1096     Q_D(QPdfBaseEngine);
1097
1098     if (d->useAlphaEngine) {
1099         QAlphaPaintEngine::drawTextItem(p, textItem);
1100         if (!continueCall())
1101             return;
1102     }
1103
1104     if (!d->hasPen || (d->clipEnabled && d->allClipped))
1105         return;
1106
1107     if (d->stroker.matrix.type() >= QTransform::TxProject) {
1108         QPaintEngine::drawTextItem(p, textItem);
1109         return;
1110     }
1111
1112     *d->currentPage << "q\n";
1113     if(!d->simplePen)
1114         *d->currentPage << QPdf::generateMatrix(d->stroker.matrix);
1115
1116     bool hp = d->hasPen;
1117     d->hasPen = false;
1118     QBrush b = d->brush;
1119     d->brush = d->pen.brush();
1120     setBrush();
1121
1122     const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
1123     Q_ASSERT(ti.fontEngine->type() != QFontEngine::Multi);
1124     d->drawTextItem(p, ti);
1125     d->hasPen = hp;
1126     d->brush = b;
1127     *d->currentPage << "Q\n";
1128 }
1129
1130
1131 void QPdfBaseEngine::updateState(const QPaintEngineState &state)
1132 {
1133     Q_D(QPdfBaseEngine);
1134
1135     if (d->useAlphaEngine) {
1136         QAlphaPaintEngine::updateState(state);
1137         if (!continueCall())
1138             return;
1139     }
1140
1141     QPaintEngine::DirtyFlags flags = state.state();
1142
1143     if (flags & DirtyTransform)
1144         d->stroker.matrix = state.transform();
1145
1146     if (flags & DirtyPen) {
1147         d->pen = state.pen();
1148         d->hasPen = d->pen.style() != Qt::NoPen;
1149         d->stroker.setPen(d->pen);
1150         QBrush penBrush = d->pen.brush();
1151         bool oldSimple = d->simplePen;
1152         d->simplePen = (d->hasPen && (penBrush.style() == Qt::SolidPattern) && penBrush.isOpaque());
1153         if (oldSimple != d->simplePen)
1154             flags |= DirtyTransform;
1155     }
1156     if (flags & DirtyBrush) {
1157         d->brush = state.brush();
1158         if (d->brush.color().alpha() == 0 && d->brush.style() == Qt::SolidPattern)
1159             d->brush.setStyle(Qt::NoBrush);
1160         d->hasBrush = d->brush.style() != Qt::NoBrush;
1161     }
1162     if (flags & DirtyBrushOrigin) {
1163         d->brushOrigin = state.brushOrigin();
1164         flags |= DirtyBrush;
1165     }
1166     if (flags & DirtyOpacity)
1167         d->opacity = state.opacity();
1168
1169     bool ce = d->clipEnabled;
1170     if (flags & DirtyClipPath) {
1171         d->clipEnabled = true;
1172         updateClipPath(state.clipPath(), state.clipOperation());
1173     } else if (flags & DirtyClipRegion) {
1174         d->clipEnabled = true;
1175         QPainterPath path;
1176         QVector<QRect> rects = state.clipRegion().rects();
1177         for (int i = 0; i < rects.size(); ++i)
1178             path.addRect(rects.at(i));
1179         updateClipPath(path, state.clipOperation());
1180         flags |= DirtyClipPath;
1181     } else if (flags & DirtyClipEnabled) {
1182         d->clipEnabled = state.isClipEnabled();
1183     }
1184
1185     if (ce != d->clipEnabled)
1186         flags |= DirtyClipPath;
1187     else if (!d->clipEnabled)
1188         flags &= ~DirtyClipPath;
1189
1190     setupGraphicsState(flags);
1191 }
1192
1193 void QPdfBaseEngine::setupGraphicsState(QPaintEngine::DirtyFlags flags)
1194 {
1195     Q_D(QPdfBaseEngine);
1196     if (flags & DirtyClipPath)
1197         flags |= DirtyTransform|DirtyPen|DirtyBrush;
1198
1199     if (flags & DirtyTransform) {
1200         *d->currentPage << "Q\n";
1201         flags |= DirtyPen|DirtyBrush;
1202     }
1203
1204     if (flags & DirtyClipPath) {
1205         *d->currentPage << "Q q\n";
1206
1207         d->allClipped = false;
1208         if (d->clipEnabled && !d->clips.isEmpty()) {
1209             for (int i = 0; i < d->clips.size(); ++i) {
1210                 if (d->clips.at(i).isEmpty()) {
1211                     d->allClipped = true;
1212                     break;
1213                 }
1214             }
1215             if (!d->allClipped) {
1216                 for (int i = 0; i < d->clips.size(); ++i) {
1217                     *d->currentPage << QPdf::generatePath(d->clips.at(i), QTransform(), QPdf::ClipPath);
1218                 }
1219             }
1220         }
1221     }
1222
1223     if (flags & DirtyTransform) {
1224         *d->currentPage << "q\n";
1225         if (d->simplePen && !d->stroker.matrix.isIdentity())
1226             *d->currentPage << QPdf::generateMatrix(d->stroker.matrix);
1227     }
1228     if (flags & DirtyBrush)
1229         setBrush();
1230     if (d->simplePen && (flags & DirtyPen))
1231         setPen();
1232 }
1233
1234 extern Q_AUTOTEST_EXPORT QPainterPath qt_regionToPath(const QRegion &region);
1235
1236 void QPdfBaseEngine::updateClipPath(const QPainterPath &p, Qt::ClipOperation op)
1237 {
1238     Q_D(QPdfBaseEngine);
1239     QPainterPath path = d->stroker.matrix.map(p);
1240     //qDebug() << "updateClipPath: " << d->stroker.matrix << p.boundingRect() << path.boundingRect() << op;
1241
1242     if (op == Qt::NoClip) {
1243         d->clipEnabled = false;
1244         d->clips.clear();
1245     } else if (op == Qt::ReplaceClip) {
1246         d->clips.clear();
1247         d->clips.append(path);
1248     } else if (op == Qt::IntersectClip) {
1249         d->clips.append(path);
1250     } else { // UniteClip
1251         // ask the painter for the current clipping path. that's the easiest solution
1252         path = painter()->clipPath();
1253         path = d->stroker.matrix.map(path);
1254         d->clips.clear();
1255         d->clips.append(path);
1256     }
1257
1258     if (d->useAlphaEngine) {
1259         // if we have an alpha region, we have to subtract that from the
1260         // any existing clip region since that region will be filled in
1261         // later with images
1262         QPainterPath alphaClip = qt_regionToPath(alphaClipping());
1263         if (!alphaClip.isEmpty()) {
1264             if (!d->clipEnabled) {
1265                 QRect r = d->fullPage ? d->paperRect() : d->pageRect();
1266                 QPainterPath dev;
1267                 dev.addRect(QRect(0, 0, r.width(), r.height()));
1268                 if (path.isEmpty())
1269                     path = dev;
1270                 else
1271                     path = path.intersected(dev);
1272                 d->clipEnabled = true;
1273             } else {
1274                 path = painter()->clipPath();
1275                 path = d->stroker.matrix.map(path);
1276             }
1277             path = path.subtracted(alphaClip);
1278             d->clips.clear();
1279             d->clips.append(path);
1280         }
1281     }
1282 }
1283
1284 void QPdfBaseEngine::setPen()
1285 {
1286     Q_D(QPdfBaseEngine);
1287     if (d->pen.style() == Qt::NoPen)
1288         return;
1289     QBrush b = d->pen.brush();
1290     Q_ASSERT(b.style() == Qt::SolidPattern && b.isOpaque());
1291
1292     QColor rgba = b.color();
1293     if (d->colorMode == QPrinter::GrayScale) {
1294         qreal gray = qGray(rgba.rgba())/255.;
1295         *d->currentPage << gray << gray << gray;
1296     } else {
1297         *d->currentPage << rgba.redF()
1298                         << rgba.greenF()
1299                         << rgba.blueF();
1300     }
1301     *d->currentPage << "SCN\n";
1302
1303     *d->currentPage << d->pen.widthF() << "w ";
1304
1305     int pdfCapStyle = 0;
1306     switch(d->pen.capStyle()) {
1307     case Qt::FlatCap:
1308         pdfCapStyle = 0;
1309         break;
1310     case Qt::SquareCap:
1311         pdfCapStyle = 2;
1312         break;
1313     case Qt::RoundCap:
1314         pdfCapStyle = 1;
1315         break;
1316     default:
1317         break;
1318     }
1319     *d->currentPage << pdfCapStyle << "J ";
1320
1321     int pdfJoinStyle = 0;
1322     switch(d->pen.joinStyle()) {
1323     case Qt::MiterJoin:
1324         pdfJoinStyle = 0;
1325         break;
1326     case Qt::BevelJoin:
1327         pdfJoinStyle = 2;
1328         break;
1329     case Qt::RoundJoin:
1330         pdfJoinStyle = 1;
1331         break;
1332     default:
1333         break;
1334     }
1335     *d->currentPage << pdfJoinStyle << "j ";
1336
1337     *d->currentPage << QPdf::generateDashes(d->pen) << " 0 d\n";
1338 }
1339
1340 bool QPdfBaseEngine::newPage()
1341 {
1342     Q_D(QPdfBaseEngine);
1343     setupGraphicsState(DirtyBrush|DirtyPen|DirtyClipPath);
1344     QFile *outfile = qobject_cast<QFile*> (d->outDevice);
1345     if (outfile && outfile->error() != QFile::NoError)
1346         return false;
1347     return true;
1348 }
1349
1350
1351 int QPdfBaseEngine::metric(QPaintDevice::PaintDeviceMetric metricType) const
1352 {
1353     Q_D(const QPdfBaseEngine);
1354     int val;
1355     QRect r = d->fullPage ? d->paperRect() : d->pageRect();
1356     switch (metricType) {
1357     case QPaintDevice::PdmWidth:
1358         val = r.width();
1359         break;
1360     case QPaintDevice::PdmHeight:
1361         val = r.height();
1362         break;
1363     case QPaintDevice::PdmDpiX:
1364     case QPaintDevice::PdmDpiY:
1365         val = d->resolution;
1366         break;
1367     case QPaintDevice::PdmPhysicalDpiX:
1368     case QPaintDevice::PdmPhysicalDpiY:
1369         val = 1200;
1370         break;
1371     case QPaintDevice::PdmWidthMM:
1372         val = qRound(r.width()*25.4/d->resolution);
1373         break;
1374     case QPaintDevice::PdmHeightMM:
1375         val = qRound(r.height()*25.4/d->resolution);
1376         break;
1377     case QPaintDevice::PdmNumColors:
1378         val = INT_MAX;
1379         break;
1380     case QPaintDevice::PdmDepth:
1381         val = 32;
1382         break;
1383     default:
1384         qWarning("QPrinter::metric: Invalid metric command");
1385         return 0;
1386     }
1387     return val;
1388 }
1389
1390 void QPdfBaseEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value)
1391 {
1392     Q_D(QPdfBaseEngine);
1393     switch (int(key)) {
1394     case PPK_CollateCopies:
1395         d->collate = value.toBool();
1396         break;
1397     case PPK_ColorMode:
1398         d->colorMode = QPrinter::ColorMode(value.toInt());
1399         break;
1400     case PPK_Creator:
1401         d->creator = value.toString();
1402         break;
1403     case PPK_DocumentName:
1404         d->title = value.toString();
1405         break;
1406     case PPK_FullPage:
1407         d->fullPage = value.toBool();
1408         break;
1409     case PPK_CopyCount: // fallthrough
1410     case PPK_NumberOfCopies:
1411         d->copies = value.toInt();
1412         break;
1413     case PPK_Orientation:
1414         d->orientation = QPrinter::Orientation(value.toInt());
1415         break;
1416     case PPK_OutputFileName:
1417         d->outputFileName = value.toString();
1418         break;
1419     case PPK_PageOrder:
1420         d->pageOrder = QPrinter::PageOrder(value.toInt());
1421         break;
1422     case PPK_PaperSize:
1423         d->paperSize = QPrinter::PaperSize(value.toInt());
1424         break;
1425     case PPK_PaperSource:
1426         d->paperSource = QPrinter::PaperSource(value.toInt());
1427         break;
1428     case PPK_PrinterName:
1429         d->printerName = value.toString();
1430         break;
1431     case PPK_PrinterProgram:
1432         d->printProgram = value.toString();
1433         break;
1434     case PPK_Resolution:
1435         d->resolution = value.toInt();
1436         break;
1437     case PPK_SelectionOption:
1438         d->selectionOption = value.toString();
1439         break;
1440     case PPK_FontEmbedding:
1441         d->embedFonts = value.toBool();
1442         break;
1443     case PPK_Duplex:
1444         d->duplex = static_cast<QPrinter::DuplexMode> (value.toInt());
1445         break;
1446     case PPK_CupsPageRect:
1447         d->cupsPageRect = value.toRect();
1448         break;
1449     case PPK_CupsPaperRect:
1450         d->cupsPaperRect = value.toRect();
1451         break;
1452     case PPK_CupsOptions:
1453         d->cupsOptions = value.toStringList();
1454         break;
1455     case PPK_CupsStringPageSize:
1456         d->cupsStringPageSize = value.toString();
1457         break;
1458     case PPK_CustomPaperSize:
1459         d->paperSize = QPrinter::Custom;
1460         d->customPaperSize = value.toSizeF();
1461         break;
1462     case PPK_PageMargins:
1463     {
1464         QList<QVariant> margins(value.toList());
1465         Q_ASSERT(margins.size() == 4);
1466         d->leftMargin = margins.at(0).toReal();
1467         d->topMargin = margins.at(1).toReal();
1468         d->rightMargin = margins.at(2).toReal();
1469         d->bottomMargin = margins.at(3).toReal();
1470         d->hasCustomPageMargins = true;
1471         break;
1472     }
1473     default:
1474         break;
1475     }
1476 }
1477
1478 QVariant QPdfBaseEngine::property(PrintEnginePropertyKey key) const
1479 {
1480     Q_D(const QPdfBaseEngine);
1481
1482     QVariant ret;
1483     switch (int(key)) {
1484     case PPK_CollateCopies:
1485         ret = d->collate;
1486         break;
1487     case PPK_ColorMode:
1488         ret = d->colorMode;
1489         break;
1490     case PPK_Creator:
1491         ret = d->creator;
1492         break;
1493     case PPK_DocumentName:
1494         ret = d->title;
1495         break;
1496     case PPK_FullPage:
1497         ret = d->fullPage;
1498         break;
1499     case PPK_CopyCount:
1500         ret = d->copies;
1501         break;
1502     case PPK_SupportsMultipleCopies:
1503 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
1504         if (QCUPSSupport::isAvailable())
1505             ret = true;
1506         else
1507 #endif
1508             ret = false;
1509         break;
1510     case PPK_NumberOfCopies:
1511 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
1512         if (QCUPSSupport::isAvailable())
1513             ret = 1;
1514         else
1515 #endif
1516             ret = d->copies;
1517         break;
1518     case PPK_Orientation:
1519         ret = d->orientation;
1520         break;
1521     case PPK_OutputFileName:
1522         ret = d->outputFileName;
1523         break;
1524     case PPK_PageOrder:
1525         ret = d->pageOrder;
1526         break;
1527     case PPK_PaperSize:
1528         ret = d->paperSize;
1529         break;
1530     case PPK_PaperSource:
1531         ret = d->paperSource;
1532         break;
1533     case PPK_PrinterName:
1534         ret = d->printerName;
1535         break;
1536     case PPK_PrinterProgram:
1537         ret = d->printProgram;
1538         break;
1539     case PPK_Resolution:
1540         ret = d->resolution;
1541         break;
1542     case PPK_SupportedResolutions:
1543         ret = QList<QVariant>() << 72;
1544         break;
1545     case PPK_PaperRect:
1546         ret = d->paperRect();
1547         break;
1548     case PPK_PageRect:
1549         ret = d->pageRect();
1550         break;
1551     case PPK_SelectionOption:
1552         ret = d->selectionOption;
1553         break;
1554     case PPK_FontEmbedding:
1555         ret = d->embedFonts;
1556         break;
1557     case PPK_Duplex:
1558         ret = d->duplex;
1559         break;
1560     case PPK_CupsPageRect:
1561         ret = d->cupsPageRect;
1562         break;
1563     case PPK_CupsPaperRect:
1564         ret = d->cupsPaperRect;
1565         break;
1566     case PPK_CupsOptions:
1567         ret = d->cupsOptions;
1568         break;
1569     case PPK_CupsStringPageSize:
1570         ret = d->cupsStringPageSize;
1571         break;
1572     case PPK_CustomPaperSize:
1573         ret = d->customPaperSize;
1574         break;
1575     case PPK_PageMargins:
1576     {
1577         QList<QVariant> margins;
1578         if (d->hasCustomPageMargins) {
1579             margins << d->leftMargin << d->topMargin
1580                     << d->rightMargin << d->bottomMargin;
1581         } else {
1582             const qreal defaultMargin = 10; // ~3.5 mm
1583             margins << defaultMargin << defaultMargin
1584                     << defaultMargin << defaultMargin;
1585         }
1586         ret = margins;
1587         break;
1588     }
1589     default:
1590         break;
1591     }
1592     return ret;
1593 }
1594
1595 QPdfBaseEnginePrivate::QPdfBaseEnginePrivate(QPrinter::PrinterMode m)
1596     : clipEnabled(false), allClipped(false), hasPen(true), hasBrush(false), simplePen(false),
1597       useAlphaEngine(false),
1598       outDevice(0), fd(-1),
1599       duplex(QPrinter::DuplexNone), collate(false), fullPage(false), embedFonts(true), copies(1),
1600       pageOrder(QPrinter::FirstPageFirst), orientation(QPrinter::Portrait),
1601       paperSize(QPrinter::A4), colorMode(QPrinter::Color), paperSource(QPrinter::Auto),
1602       hasCustomPageMargins(false),
1603       leftMargin(0), topMargin(0), rightMargin(0), bottomMargin(0)
1604 {
1605     resolution = 72;
1606     if (m == QPrinter::HighResolution)
1607         resolution = 1200;
1608     else if (m == QPrinter::ScreenResolution)
1609         resolution = qt_defaultDpi();
1610
1611     postscript = false;
1612     currentObject = 1;
1613     currentPage = 0;
1614     stroker.stream = 0;
1615 }
1616
1617 bool QPdfBaseEngine::begin(QPaintDevice *pdev)
1618 {
1619     Q_D(QPdfBaseEngine);
1620     d->pdev = pdev;
1621
1622     d->postscript = false;
1623     d->currentObject = 1;
1624
1625     d->currentPage = new QPdfPage;
1626     d->stroker.stream = d->currentPage;
1627     d->opacity = 1.0;
1628
1629     return d->openPrintDevice();
1630 }
1631
1632 bool QPdfBaseEngine::end()
1633 {
1634     Q_D(QPdfBaseEngine);
1635     qDeleteAll(d->fonts);
1636     d->fonts.clear();
1637     delete d->currentPage;
1638     d->currentPage = 0;
1639
1640     d->closePrintDevice();
1641     return true;
1642 }
1643
1644 #ifndef QT_NO_LPR
1645 static void closeAllOpenFds()
1646 {
1647     // hack time... getting the maximum number of open
1648     // files, if possible.  if not we assume it's the
1649     // larger of 256 and the fd we got
1650     int i;
1651 #if defined(_SC_OPEN_MAX)
1652     i = (int)sysconf(_SC_OPEN_MAX);
1653 #elif defined(_POSIX_OPEN_MAX)
1654     i = (int)_POSIX_OPEN_MAX;
1655 #elif defined(OPEN_MAX)
1656     i = (int)OPEN_MAX;
1657 #else
1658     i = 256;
1659 #endif
1660     // leave stdin/out/err untouched
1661     while(--i > 2)
1662         QT_CLOSE(i);
1663 }
1664 #endif
1665
1666 bool QPdfBaseEnginePrivate::openPrintDevice()
1667 {
1668     if(outDevice)
1669         return false;
1670
1671     if (!outputFileName.isEmpty()) {
1672         QFile *file = new QFile(outputFileName);
1673         if (! file->open(QFile::WriteOnly|QFile::Truncate)) {
1674             delete file;
1675             return false;
1676         }
1677         outDevice = file;
1678 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
1679     } else if (QCUPSSupport::isAvailable()) {
1680         QCUPSSupport cups;
1681         QPair<int, QString> ret = cups.tempFd();
1682         if (ret.first < 0) {
1683             qWarning("QPdfPrinter: Could not open temporary file to print");
1684             return false;
1685         }
1686         cupsTempFile = ret.second;
1687         outDevice = new QFile();
1688         static_cast<QFile *>(outDevice)->open(ret.first, QIODevice::WriteOnly);
1689         fd = ret.first;
1690 #endif
1691 #ifndef QT_NO_LPR
1692     } else {
1693         QString pr;
1694         if (!printerName.isEmpty())
1695             pr = printerName;
1696         int fds[2];
1697         if (qt_safe_pipe(fds) != 0) {
1698             qWarning("QPdfPrinter: Could not open pipe to print");
1699             return false;
1700         }
1701
1702         pid_t pid = fork();
1703         if (pid == 0) {       // child process
1704             // if possible, exit quickly, so the actual lp/lpr
1705             // becomes a child of init, and ::waitpid() is
1706             // guaranteed not to wait.
1707             if (fork() > 0) {
1708                 closeAllOpenFds();
1709
1710                 // try to replace this process with "true" - this prevents
1711                 // global destructors from being called (that could possibly
1712                 // do wrong things to the parent process)
1713                 (void)execlp("true", "true", (char *)0);
1714                 (void)execl("/bin/true", "true", (char *)0);
1715                 (void)execl("/usr/bin/true", "true", (char *)0);
1716                 ::_exit(0);
1717             }
1718             qt_safe_dup2(fds[0], 0, 0);
1719
1720             closeAllOpenFds();
1721
1722             if (!printProgram.isEmpty()) {
1723                 if (!selectionOption.isEmpty())
1724                     pr.prepend(selectionOption);
1725                 else
1726                     pr.prepend(QLatin1String("-P"));
1727                 (void)execlp(printProgram.toLocal8Bit().data(), printProgram.toLocal8Bit().data(),
1728                              pr.toLocal8Bit().data(), (char *)0);
1729             } else {
1730                 // if no print program has been specified, be smart
1731                 // about the option string too.
1732                 QList<QByteArray> lprhack;
1733                 QList<QByteArray> lphack;
1734                 QByteArray media;
1735                 if (!pr.isEmpty() || !selectionOption.isEmpty()) {
1736                     if (!selectionOption.isEmpty()) {
1737                         QStringList list = selectionOption.split(QLatin1Char(' '));
1738                         for (int i = 0; i < list.size(); ++i)
1739                             lprhack.append(list.at(i).toLocal8Bit());
1740                         lphack = lprhack;
1741                     } else {
1742                         lprhack.append("-P");
1743                         lphack.append("-d");
1744                     }
1745                     lprhack.append(pr.toLocal8Bit());
1746                     lphack.append(pr.toLocal8Bit());
1747                 }
1748                 lphack.append("-s");
1749
1750                 char ** lpargs = new char *[lphack.size()+6];
1751                 char lp[] = "lp";
1752                 lpargs[0] = lp;
1753                 int i;
1754                 for (i = 0; i < lphack.size(); ++i)
1755                     lpargs[i+1] = (char *)lphack.at(i).constData();
1756 #ifndef Q_OS_OSF
1757                 char dash_o[] = "-o";
1758                 if (QPdf::paperSizeToString(paperSize)) {
1759                     lpargs[++i] = dash_o;
1760                     lpargs[++i] = const_cast<char *>(QPdf::paperSizeToString(paperSize));
1761                     lpargs[++i] = dash_o;
1762                     media = "media=";
1763                     media += QPdf::paperSizeToString(paperSize);
1764                     lpargs[++i] = media.data();
1765                 }
1766 #endif
1767                 lpargs[++i] = 0;
1768                 char **lprargs = new char *[lprhack.size()+2];
1769                 char lpr[] = "lpr";
1770                 lprargs[0] = lpr;
1771                 for (int i = 0; i < lprhack.size(); ++i)
1772                     lprargs[i+1] = (char *)lprhack[i].constData();
1773                 lprargs[lprhack.size() + 1] = 0;
1774                 (void)execvp("lp", lpargs);
1775                 (void)execvp("lpr", lprargs);
1776                 (void)execv("/bin/lp", lpargs);
1777                 (void)execv("/bin/lpr", lprargs);
1778                 (void)execv("/usr/bin/lp", lpargs);
1779                 (void)execv("/usr/bin/lpr", lprargs);
1780
1781                 delete []lpargs;
1782                 delete []lprargs;
1783             }
1784             // if we couldn't exec anything, close the fd,
1785             // wait for a second so the parent process (the
1786             // child of the GUI process) has exited.  then
1787             // exit.
1788             QT_CLOSE(0);
1789             (void)::sleep(1);
1790             ::_exit(0);
1791         }
1792         // parent process
1793         QT_CLOSE(fds[0]);
1794         fd = fds[1];
1795         (void)qt_safe_waitpid(pid, 0, 0);
1796
1797         if (fd < 0)
1798             return false;
1799
1800         outDevice = new QFile();
1801         static_cast<QFile *>(outDevice)->open(fd, QIODevice::WriteOnly);
1802 #endif
1803     }
1804
1805     return true;
1806 }
1807
1808 void QPdfBaseEnginePrivate::closePrintDevice()
1809 {
1810     if (!outDevice)
1811         return;
1812     outDevice->close();
1813     if (fd >= 0)
1814 #if defined(Q_OS_WIN) && defined(_MSC_VER) && _MSC_VER >= 1400
1815         ::_close(fd);
1816 #else
1817         ::close(fd);
1818 #endif
1819     fd = -1;
1820     delete outDevice;
1821     outDevice = 0;
1822
1823 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
1824     if (!cupsTempFile.isEmpty()) {
1825         QString tempFile = cupsTempFile;
1826         cupsTempFile.clear();
1827         QCUPSSupport cups;
1828
1829         // Set up print options.
1830         QByteArray prnName;
1831         QList<QPair<QByteArray, QByteArray> > options;
1832         QVector<cups_option_t> cupsOptStruct;
1833
1834         if (!printerName.isEmpty()) {
1835             prnName = printerName.toLocal8Bit();
1836         } else {
1837             QPrinterInfo def = QPrinterInfo::defaultPrinter();
1838             if (def.isNull()) {
1839                 qWarning("Could not determine printer to print to");
1840                 QFile::remove(tempFile);
1841                 return;
1842             }
1843             prnName = def.printerName().toLocal8Bit();
1844         }
1845
1846         if (!cupsStringPageSize.isEmpty()) {
1847             options.append(QPair<QByteArray, QByteArray>("media", cupsStringPageSize.toLocal8Bit()));
1848         }
1849
1850         if (copies > 1) {
1851             options.append(QPair<QByteArray, QByteArray>("copies", QString::number(copies).toLocal8Bit()));
1852         }
1853
1854         if (collate) {
1855             options.append(QPair<QByteArray, QByteArray>("Collate", "True"));
1856         }
1857
1858         switch (duplex) {
1859         case QPrinter::DuplexNone:
1860             options.append(QPair<QByteArray, QByteArray>("sides", "one-sided"));
1861             break;
1862         case QPrinter::DuplexAuto:
1863             if (orientation == QPrinter::Portrait)
1864                 options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-long-edge"));
1865             else
1866                 options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-short-edge"));
1867             break;
1868         case QPrinter::DuplexLongSide:
1869             options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-long-edge"));
1870             break;
1871         case QPrinter::DuplexShortSide:
1872             options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-short-edge"));
1873             break;
1874         }
1875
1876         if (QCUPSSupport::cupsVersion() >= 10300 && orientation == QPrinter::Landscape) {
1877             options.append(QPair<QByteArray, QByteArray>("landscape", ""));
1878         }
1879
1880         QStringList::const_iterator it = cupsOptions.constBegin();
1881         while (it != cupsOptions.constEnd()) {
1882             options.append(QPair<QByteArray, QByteArray>((*it).toLocal8Bit(), (*(it+1)).toLocal8Bit()));
1883             it += 2;
1884         }
1885
1886         for (int c = 0; c < options.size(); ++c) {
1887             cups_option_t opt;
1888             opt.name = options[c].first.data();
1889             opt.value = options[c].second.data();
1890             cupsOptStruct.append(opt);
1891         }
1892
1893         // Print the file.
1894         cups_option_t* optPtr = cupsOptStruct.size() ? &cupsOptStruct.first() : 0;
1895         cups.printFile(prnName.constData(), tempFile.toLocal8Bit().constData(),
1896                 title.toLocal8Bit().constData(), cupsOptStruct.size(), optPtr);
1897
1898         QFile::remove(tempFile);
1899     }
1900 #endif
1901 }
1902
1903 QPdfBaseEnginePrivate::~QPdfBaseEnginePrivate()
1904 {
1905     qDeleteAll(fonts);
1906     delete currentPage;
1907 }
1908
1909 void QPdfBaseEnginePrivate::drawTextItem(const QPointF &p, const QTextItemInt &ti)
1910 {
1911     Q_Q(QPdfBaseEngine);
1912
1913     QFontEngine *fe = ti.fontEngine;
1914
1915     QFontEngine::FaceId face_id = fe->faceId();
1916     bool noEmbed = false;
1917     if (face_id.filename.isEmpty()
1918         || (!postscript && ((fe->fsType & 0x200) /* bitmap embedding only */
1919                             || (fe->fsType == 2) /* no embedding allowed */))) {
1920         *currentPage << "Q\n";
1921         q->QPaintEngine::drawTextItem(p, ti);
1922         *currentPage << "q\n";
1923         if (face_id.filename.isEmpty())
1924             return;
1925         noEmbed = true;
1926     }
1927
1928     QFontSubset *font = fonts.value(face_id, 0);
1929     if (!font) {
1930         font = new QFontSubset(fe, requestObject());
1931         font->noEmbed = noEmbed;
1932     }
1933     fonts.insert(face_id, font);
1934
1935     if (!currentPage->fonts.contains(font->object_id))
1936         currentPage->fonts.append(font->object_id);
1937
1938     qreal size = ti.fontEngine->fontDef.pixelSize;
1939 #ifdef Q_WS_WIN
1940     if (ti.fontEngine->type() == QFontEngine::Win) {
1941         QFontEngineWin *fe = static_cast<QFontEngineWin *>(ti.fontEngine);
1942         size = fe->tm.tmHeight;
1943     }
1944 #endif
1945
1946     QVarLengthArray<glyph_t> glyphs;
1947     QVarLengthArray<QFixedPoint> positions;
1948     QTransform m = QTransform::fromTranslate(p.x(), p.y());
1949     ti.fontEngine->getGlyphPositions(ti.glyphs, m, ti.flags,
1950                                      glyphs, positions);
1951     if (glyphs.size() == 0)
1952         return;
1953     int synthesized = ti.fontEngine->synthesized();
1954     qreal stretch = synthesized & QFontEngine::SynthesizedStretch ? ti.fontEngine->fontDef.stretch/100. : 1.;
1955
1956     *currentPage << "BT\n"
1957                  << "/F" << font->object_id << size << "Tf "
1958                  << stretch << (synthesized & QFontEngine::SynthesizedItalic
1959                                 ? "0 .3 -1 0 0 Tm\n"
1960                                 : "0 0 -1 0 0 Tm\n");
1961
1962
1963 #if 0
1964     // #### implement actual text for complex languages
1965     const unsigned short *logClusters = ti.logClusters;
1966     int pos = 0;
1967     do {
1968         int end = pos + 1;
1969         while (end < ti.num_chars && logClusters[end] == logClusters[pos])
1970             ++end;
1971         *currentPage << "/Span << /ActualText <FEFF";
1972         for (int i = pos; i < end; ++i) {
1973             s << toHex((ushort)ti.chars[i].unicode(), buf);
1974         }
1975         *currentPage << "> >>\n"
1976             "BDC\n"
1977             "<";
1978         int ge = end == ti.num_chars ? ti.num_glyphs : logClusters[end];
1979         for (int gs = logClusters[pos]; gs < ge; ++gs)
1980             *currentPage << toHex((ushort)ti.glyphs[gs].glyph, buf);
1981         *currentPage << "> Tj\n"
1982             "EMC\n";
1983         pos = end;
1984     } while (pos < ti.num_chars);
1985 #else
1986     qreal last_x = 0.;
1987     qreal last_y = 0.;
1988     for (int i = 0; i < glyphs.size(); ++i) {
1989         qreal x = positions[i].x.toReal();
1990         qreal y = positions[i].y.toReal();
1991         if (synthesized & QFontEngine::SynthesizedItalic)
1992             x += .3*y;
1993         x /= stretch;
1994         char buf[5];
1995         int g = font->addGlyph(glyphs[i]);
1996         *currentPage << x - last_x << last_y - y << "Td <"
1997                      << QPdf::toHex((ushort)g, buf) << "> Tj\n";
1998         last_x = x;
1999         last_y = y;
2000     }
2001     if (synthesized & QFontEngine::SynthesizedBold) {
2002         *currentPage << stretch << (synthesized & QFontEngine::SynthesizedItalic
2003                             ? "0 .3 -1 0 0 Tm\n"
2004                             : "0 0 -1 0 0 Tm\n");
2005         *currentPage << "/Span << /ActualText <> >> BDC\n";
2006         last_x = 0.5*fe->lineThickness().toReal();
2007         last_y = 0.;
2008         for (int i = 0; i < glyphs.size(); ++i) {
2009             qreal x = positions[i].x.toReal();
2010             qreal y = positions[i].y.toReal();
2011             if (synthesized & QFontEngine::SynthesizedItalic)
2012                 x += .3*y;
2013             x /= stretch;
2014             char buf[5];
2015             int g = font->addGlyph(glyphs[i]);
2016             *currentPage << x - last_x << last_y - y << "Td <"
2017                         << QPdf::toHex((ushort)g, buf) << "> Tj\n";
2018             last_x = x;
2019             last_y = y;
2020         }
2021         *currentPage << "EMC\n";
2022     }
2023 #endif
2024
2025     *currentPage << "ET\n";
2026 }
2027
2028 QRect QPdfBaseEnginePrivate::paperRect() const
2029 {
2030     int w;
2031     int h;
2032     if (paperSize == QPrinter::Custom) {
2033         w = qRound(customPaperSize.width()*resolution/72.);
2034         h = qRound(customPaperSize.height()*resolution/72.);
2035     } else {
2036 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
2037         if (QCUPSSupport::isAvailable() && !cupsPaperRect.isNull()) {
2038             QRect r = cupsPaperRect;
2039             w = r.width();
2040             h = r.height();
2041         } else
2042 #endif
2043         {
2044             QPdf::PaperSize s = QPdf::paperSize(paperSize);
2045             w = s.width;
2046             h = s.height;
2047         }
2048         w = qRound(w*resolution/72.);
2049         h = qRound(h*resolution/72.);
2050     }
2051     if (orientation == QPrinter::Portrait)
2052         return QRect(0, 0, w, h);
2053     else
2054         return QRect(0, 0, h, w);
2055 }
2056
2057 QRect QPdfBaseEnginePrivate::pageRect() const
2058 {
2059     if(fullPage)
2060         return paperRect();
2061
2062     QRect r;
2063
2064 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
2065     if (!hasCustomPageMargins && QCUPSSupport::isAvailable() && !cupsPageRect.isNull()) {
2066         r = cupsPageRect;
2067         if (r == cupsPaperRect) {
2068             // if cups doesn't define any margins, give it at least approx 3.5 mm
2069             r = QRect(10, 10, r.width() - 20, r.height() - 20);
2070         }
2071     } else
2072 #endif
2073     {
2074         QPdf::PaperSize s;
2075         if (paperSize == QPrinter::Custom) {
2076             s.width = qRound(customPaperSize.width());
2077             s.height = qRound(customPaperSize.height());
2078         } else {
2079             s = QPdf::paperSize(paperSize);
2080         }
2081         if (hasCustomPageMargins)
2082             r = QRect(0, 0, s.width, s.height);
2083         else
2084             r = QRect(72/3, 72/3, s.width - 2*72/3, s.height - 2*72/3);
2085     }
2086
2087     int x = qRound(r.left()*resolution/72.);
2088     int y = qRound(r.top()*resolution/72.);
2089     int w = qRound(r.width()*resolution/72.);
2090     int h = qRound(r.height()*resolution/72.);
2091     if (orientation == QPrinter::Portrait)
2092         r = QRect(x, y, w, h);
2093     else
2094         r = QRect(y, x, h, w);
2095
2096     if (hasCustomPageMargins) {
2097         r.adjust(qRound(leftMargin*(resolution/72.)),
2098                  qRound(topMargin*(resolution/72.)),
2099                  -qRound(rightMargin*(resolution/72.)),
2100                  -qRound(bottomMargin*(resolution/72.)));
2101     }
2102     return r;
2103 }
2104
2105 #endif
2106
2107 QT_END_NAMESPACE