Replace 'i < len-1 && func(i+1)' by 'i+1 < len && func(i+1)'
[profile/ivi/qtbase.git] / src / gui / painting / qpdf.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtGui module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
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 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 #endif
1690 #ifndef QT_NO_LPR
1691     } else {
1692         QString pr;
1693         if (!printerName.isEmpty())
1694             pr = printerName;
1695         int fds[2];
1696         if (qt_safe_pipe(fds) != 0) {
1697             qWarning("QPdfPrinter: Could not open pipe to print");
1698             return false;
1699         }
1700
1701         pid_t pid = fork();
1702         if (pid == 0) {       // child process
1703             // if possible, exit quickly, so the actual lp/lpr
1704             // becomes a child of init, and ::waitpid() is
1705             // guaranteed not to wait.
1706             if (fork() > 0) {
1707                 closeAllOpenFds();
1708
1709                 // try to replace this process with "true" - this prevents
1710                 // global destructors from being called (that could possibly
1711                 // do wrong things to the parent process)
1712                 (void)execlp("true", "true", (char *)0);
1713                 (void)execl("/bin/true", "true", (char *)0);
1714                 (void)execl("/usr/bin/true", "true", (char *)0);
1715                 ::_exit(0);
1716             }
1717             qt_safe_dup2(fds[0], 0, 0);
1718
1719             closeAllOpenFds();
1720
1721             if (!printProgram.isEmpty()) {
1722                 if (!selectionOption.isEmpty())
1723                     pr.prepend(selectionOption);
1724                 else
1725                     pr.prepend(QLatin1String("-P"));
1726                 (void)execlp(printProgram.toLocal8Bit().data(), printProgram.toLocal8Bit().data(),
1727                              pr.toLocal8Bit().data(), (char *)0);
1728             } else {
1729                 // if no print program has been specified, be smart
1730                 // about the option string too.
1731                 QList<QByteArray> lprhack;
1732                 QList<QByteArray> lphack;
1733                 QByteArray media;
1734                 if (!pr.isEmpty() || !selectionOption.isEmpty()) {
1735                     if (!selectionOption.isEmpty()) {
1736                         QStringList list = selectionOption.split(QLatin1Char(' '));
1737                         for (int i = 0; i < list.size(); ++i)
1738                             lprhack.append(list.at(i).toLocal8Bit());
1739                         lphack = lprhack;
1740                     } else {
1741                         lprhack.append("-P");
1742                         lphack.append("-d");
1743                     }
1744                     lprhack.append(pr.toLocal8Bit());
1745                     lphack.append(pr.toLocal8Bit());
1746                 }
1747                 lphack.append("-s");
1748
1749                 char ** lpargs = new char *[lphack.size()+6];
1750                 char lp[] = "lp";
1751                 lpargs[0] = lp;
1752                 int i;
1753                 for (i = 0; i < lphack.size(); ++i)
1754                     lpargs[i+1] = (char *)lphack.at(i).constData();
1755 #ifndef Q_OS_OSF
1756                 if (QPdf::paperSizeToString(paperSize)) {
1757                     char dash_o[] = "-o";
1758                     lpargs[++i] = dash_o;
1759                     lpargs[++i] = const_cast<char *>(QPdf::paperSizeToString(paperSize));
1760                     lpargs[++i] = dash_o;
1761                     media = "media=";
1762                     media += QPdf::paperSizeToString(paperSize);
1763                     lpargs[++i] = media.data();
1764                 }
1765 #endif
1766                 lpargs[++i] = 0;
1767                 char **lprargs = new char *[lprhack.size()+2];
1768                 char lpr[] = "lpr";
1769                 lprargs[0] = lpr;
1770                 for (int i = 0; i < lprhack.size(); ++i)
1771                     lprargs[i+1] = (char *)lprhack[i].constData();
1772                 lprargs[lprhack.size() + 1] = 0;
1773                 (void)execvp("lp", lpargs);
1774                 (void)execvp("lpr", lprargs);
1775                 (void)execv("/bin/lp", lpargs);
1776                 (void)execv("/bin/lpr", lprargs);
1777                 (void)execv("/usr/bin/lp", lpargs);
1778                 (void)execv("/usr/bin/lpr", lprargs);
1779
1780                 delete []lpargs;
1781                 delete []lprargs;
1782             }
1783             // if we couldn't exec anything, close the fd,
1784             // wait for a second so the parent process (the
1785             // child of the GUI process) has exited.  then
1786             // exit.
1787             QT_CLOSE(0);
1788             (void)::sleep(1);
1789             ::_exit(0);
1790         }
1791         // parent process
1792         QT_CLOSE(fds[0]);
1793         fd = fds[1];
1794         (void)qt_safe_waitpid(pid, 0, 0);
1795
1796         if (fd < 0)
1797             return false;
1798
1799         outDevice = new QFile();
1800         static_cast<QFile *>(outDevice)->open(fd, QIODevice::WriteOnly);
1801 #endif
1802     }
1803
1804     return true;
1805 }
1806
1807 void QPdfBaseEnginePrivate::closePrintDevice()
1808 {
1809     if (!outDevice)
1810         return;
1811     outDevice->close();
1812     if (fd >= 0)
1813 #if defined(Q_OS_WIN) && defined(_MSC_VER) && _MSC_VER >= 1400
1814         ::_close(fd);
1815 #else
1816         ::close(fd);
1817 #endif
1818     fd = -1;
1819     delete outDevice;
1820     outDevice = 0;
1821
1822 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
1823     if (!cupsTempFile.isEmpty()) {
1824         QString tempFile = cupsTempFile;
1825         cupsTempFile.clear();
1826         QCUPSSupport cups;
1827
1828         // Set up print options.
1829         QByteArray prnName;
1830         QList<QPair<QByteArray, QByteArray> > options;
1831         QVector<cups_option_t> cupsOptStruct;
1832
1833         if (!printerName.isEmpty()) {
1834             prnName = printerName.toLocal8Bit();
1835         } else {
1836             QPrinterInfo def = QPrinterInfo::defaultPrinter();
1837             if (def.isNull()) {
1838                 qWarning("Could not determine printer to print to");
1839                 QFile::remove(tempFile);
1840                 return;
1841             }
1842             prnName = def.printerName().toLocal8Bit();
1843         }
1844
1845         if (!cupsStringPageSize.isEmpty()) {
1846             options.append(QPair<QByteArray, QByteArray>("media", cupsStringPageSize.toLocal8Bit()));
1847         }
1848
1849         if (copies > 1) {
1850             options.append(QPair<QByteArray, QByteArray>("copies", QString::number(copies).toLocal8Bit()));
1851         }
1852
1853         if (collate) {
1854             options.append(QPair<QByteArray, QByteArray>("Collate", "True"));
1855         }
1856
1857         if (duplex != QPrinter::DuplexNone) {
1858             switch(duplex) {
1859             case QPrinter::DuplexNone: break;
1860             case QPrinter::DuplexAuto:
1861                 if (orientation == QPrinter::Portrait)
1862                     options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-long-edge"));
1863                 else
1864                     options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-short-edge"));
1865                 break;
1866             case QPrinter::DuplexLongSide:
1867                 options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-long-edge"));
1868                 break;
1869             case QPrinter::DuplexShortSide:
1870                 options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-short-edge"));
1871                 break;
1872             }
1873         }
1874
1875         if (QCUPSSupport::cupsVersion() >= 10300 && orientation == QPrinter::Landscape) {
1876             options.append(QPair<QByteArray, QByteArray>("landscape", ""));
1877         }
1878
1879         QStringList::const_iterator it = cupsOptions.constBegin();
1880         while (it != cupsOptions.constEnd()) {
1881             options.append(QPair<QByteArray, QByteArray>((*it).toLocal8Bit(), (*(it+1)).toLocal8Bit()));
1882             it += 2;
1883         }
1884
1885         for (int c = 0; c < options.size(); ++c) {
1886             cups_option_t opt;
1887             opt.name = options[c].first.data();
1888             opt.value = options[c].second.data();
1889             cupsOptStruct.append(opt);
1890         }
1891
1892         // Print the file.
1893         cups_option_t* optPtr = cupsOptStruct.size() ? &cupsOptStruct.first() : 0;
1894         cups.printFile(prnName.constData(), tempFile.toLocal8Bit().constData(),
1895                 title.toLocal8Bit().constData(), cupsOptStruct.size(), optPtr);
1896
1897         QFile::remove(tempFile);
1898     }
1899 #endif
1900 }
1901
1902 QPdfBaseEnginePrivate::~QPdfBaseEnginePrivate()
1903 {
1904     qDeleteAll(fonts);
1905     delete currentPage;
1906 }
1907
1908 void QPdfBaseEnginePrivate::drawTextItem(const QPointF &p, const QTextItemInt &ti)
1909 {
1910     Q_Q(QPdfBaseEngine);
1911
1912     QFontEngine *fe = ti.fontEngine;
1913
1914     QFontEngine::FaceId face_id = fe->faceId();
1915     bool noEmbed = false;
1916     if (face_id.filename.isEmpty()
1917         || (!postscript && ((fe->fsType & 0x200) /* bitmap embedding only */
1918                             || (fe->fsType == 2) /* no embedding allowed */))) {
1919         *currentPage << "Q\n";
1920         q->QPaintEngine::drawTextItem(p, ti);
1921         *currentPage << "q\n";
1922         if (face_id.filename.isEmpty())
1923             return;
1924         noEmbed = true;
1925     }
1926
1927     QFontSubset *font = fonts.value(face_id, 0);
1928     if (!font) {
1929         font = new QFontSubset(fe, requestObject());
1930         font->noEmbed = noEmbed;
1931     }
1932     fonts.insert(face_id, font);
1933
1934     if (!currentPage->fonts.contains(font->object_id))
1935         currentPage->fonts.append(font->object_id);
1936
1937     qreal size = ti.fontEngine->fontDef.pixelSize;
1938 #ifdef Q_WS_WIN
1939     if (ti.fontEngine->type() == QFontEngine::Win) {
1940         QFontEngineWin *fe = static_cast<QFontEngineWin *>(ti.fontEngine);
1941         size = fe->tm.tmHeight;
1942     }
1943 #endif
1944
1945     QVarLengthArray<glyph_t> glyphs;
1946     QVarLengthArray<QFixedPoint> positions;
1947     QTransform m = QTransform::fromTranslate(p.x(), p.y());
1948     ti.fontEngine->getGlyphPositions(ti.glyphs, m, ti.flags,
1949                                      glyphs, positions);
1950     if (glyphs.size() == 0)
1951         return;
1952     int synthesized = ti.fontEngine->synthesized();
1953     qreal stretch = synthesized & QFontEngine::SynthesizedStretch ? ti.fontEngine->fontDef.stretch/100. : 1.;
1954
1955     *currentPage << "BT\n"
1956                  << "/F" << font->object_id << size << "Tf "
1957                  << stretch << (synthesized & QFontEngine::SynthesizedItalic
1958                                 ? "0 .3 -1 0 0 Tm\n"
1959                                 : "0 0 -1 0 0 Tm\n");
1960
1961
1962 #if 0
1963     // #### implement actual text for complex languages
1964     const unsigned short *logClusters = ti.logClusters;
1965     int pos = 0;
1966     do {
1967         int end = pos + 1;
1968         while (end < ti.num_chars && logClusters[end] == logClusters[pos])
1969             ++end;
1970         *currentPage << "/Span << /ActualText <FEFF";
1971         for (int i = pos; i < end; ++i) {
1972             s << toHex((ushort)ti.chars[i].unicode(), buf);
1973         }
1974         *currentPage << "> >>\n"
1975             "BDC\n"
1976             "<";
1977         int ge = end == ti.num_chars ? ti.num_glyphs : logClusters[end];
1978         for (int gs = logClusters[pos]; gs < ge; ++gs)
1979             *currentPage << toHex((ushort)ti.glyphs[gs].glyph, buf);
1980         *currentPage << "> Tj\n"
1981             "EMC\n";
1982         pos = end;
1983     } while (pos < ti.num_chars);
1984 #else
1985     qreal last_x = 0.;
1986     qreal last_y = 0.;
1987     for (int i = 0; i < glyphs.size(); ++i) {
1988         qreal x = positions[i].x.toReal();
1989         qreal y = positions[i].y.toReal();
1990         if (synthesized & QFontEngine::SynthesizedItalic)
1991             x += .3*y;
1992         x /= stretch;
1993         char buf[5];
1994         int g = font->addGlyph(glyphs[i]);
1995         *currentPage << x - last_x << last_y - y << "Td <"
1996                      << QPdf::toHex((ushort)g, buf) << "> Tj\n";
1997         last_x = x;
1998         last_y = y;
1999     }
2000     if (synthesized & QFontEngine::SynthesizedBold) {
2001         *currentPage << stretch << (synthesized & QFontEngine::SynthesizedItalic
2002                             ? "0 .3 -1 0 0 Tm\n"
2003                             : "0 0 -1 0 0 Tm\n");
2004         *currentPage << "/Span << /ActualText <> >> BDC\n";
2005         last_x = 0.5*fe->lineThickness().toReal();
2006         last_y = 0.;
2007         for (int i = 0; i < glyphs.size(); ++i) {
2008             qreal x = positions[i].x.toReal();
2009             qreal y = positions[i].y.toReal();
2010             if (synthesized & QFontEngine::SynthesizedItalic)
2011                 x += .3*y;
2012             x /= stretch;
2013             char buf[5];
2014             int g = font->addGlyph(glyphs[i]);
2015             *currentPage << x - last_x << last_y - y << "Td <"
2016                         << QPdf::toHex((ushort)g, buf) << "> Tj\n";
2017             last_x = x;
2018             last_y = y;
2019         }
2020         *currentPage << "EMC\n";
2021     }
2022 #endif
2023
2024     *currentPage << "ET\n";
2025 }
2026
2027 QRect QPdfBaseEnginePrivate::paperRect() const
2028 {
2029     int w;
2030     int h;
2031     if (paperSize == QPrinter::Custom) {
2032         w = qRound(customPaperSize.width()*resolution/72.);
2033         h = qRound(customPaperSize.height()*resolution/72.);
2034     } else {
2035 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
2036         if (QCUPSSupport::isAvailable() && !cupsPaperRect.isNull()) {
2037             QRect r = cupsPaperRect;
2038             w = r.width();
2039             h = r.height();
2040         } else
2041 #endif
2042         {
2043             QPdf::PaperSize s = QPdf::paperSize(paperSize);
2044             w = s.width;
2045             h = s.height;
2046         }
2047         w = qRound(w*resolution/72.);
2048         h = qRound(h*resolution/72.);
2049     }
2050     if (orientation == QPrinter::Portrait)
2051         return QRect(0, 0, w, h);
2052     else
2053         return QRect(0, 0, h, w);
2054 }
2055
2056 QRect QPdfBaseEnginePrivate::pageRect() const
2057 {
2058     if(fullPage)
2059         return paperRect();
2060
2061     QRect r;
2062
2063 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
2064     if (!hasCustomPageMargins && QCUPSSupport::isAvailable() && !cupsPageRect.isNull()) {
2065         r = cupsPageRect;
2066         if (r == cupsPaperRect) {
2067             // if cups doesn't define any margins, give it at least approx 3.5 mm
2068             r = QRect(10, 10, r.width() - 20, r.height() - 20);
2069         }
2070     } else
2071 #endif
2072     {
2073         QPdf::PaperSize s;
2074         if (paperSize == QPrinter::Custom) {
2075             s.width = qRound(customPaperSize.width());
2076             s.height = qRound(customPaperSize.height());
2077         } else {
2078             s = QPdf::paperSize(paperSize);
2079         }
2080         if (hasCustomPageMargins)
2081             r = QRect(0, 0, s.width, s.height);
2082         else
2083             r = QRect(72/3, 72/3, s.width - 2*72/3, s.height - 2*72/3);
2084     }
2085
2086     int x = qRound(r.left()*resolution/72.);
2087     int y = qRound(r.top()*resolution/72.);
2088     int w = qRound(r.width()*resolution/72.);
2089     int h = qRound(r.height()*resolution/72.);
2090     if (orientation == QPrinter::Portrait)
2091         r = QRect(x, y, w, h);
2092     else
2093         r = QRect(y, x, h, w);
2094
2095     if (hasCustomPageMargins) {
2096         r.adjust(qRound(leftMargin*(resolution/72.)),
2097                  qRound(topMargin*(resolution/72.)),
2098                  -qRound(rightMargin*(resolution/72.)),
2099                  -qRound(bottomMargin*(resolution/72.)));
2100     }
2101     return r;
2102 }
2103
2104 #endif
2105
2106 QT_END_NAMESPACE