Make QRegion not need to be friends with QVector
[profile/ivi/qtbase.git] / src / gui / painting / qpdf.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
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 <qnumeric.h>
48 #include "private/qfont_p.h"
49 #include <qimagewriter.h>
50 #include "qbuffer.h"
51 #include "QtCore/qdatetime.h"
52
53 #ifndef QT_NO_COMPRESS
54 #include <zlib.h>
55 #endif
56
57 #ifdef QT_NO_COMPRESS
58 static const bool do_compress = false;
59 #else
60 static const bool do_compress = true;
61 #endif
62
63 // might be helpful for smooth transforms of images
64 // Can't use it though, as gs generates completely wrong images if this is true.
65 static const bool interpolateImages = false;
66
67 QT_BEGIN_NAMESPACE
68
69 inline QPaintEngine::PaintEngineFeatures qt_pdf_decide_features()
70 {
71     QPaintEngine::PaintEngineFeatures f = QPaintEngine::AllFeatures;
72     f &= ~(QPaintEngine::PorterDuff | QPaintEngine::PerspectiveTransform
73            | QPaintEngine::ObjectBoundingModeGradients
74 #ifndef USE_NATIVE_GRADIENTS
75            | QPaintEngine::LinearGradientFill
76 #endif
77            | QPaintEngine::RadialGradientFill
78            | QPaintEngine::ConicalGradientFill);
79     return f;
80 }
81
82
83
84 /* also adds a space at the end of the number */
85 const char *qt_real_to_string(qreal val, char *buf) {
86     const char *ret = buf;
87
88     if (qIsNaN(val)) {
89         *(buf++) = '0';
90         *(buf++) = ' ';
91         *buf = 0;
92         return ret;
93     }
94
95     if (val < 0) {
96         *(buf++) = '-';
97         val = -val;
98     }
99     unsigned int ival = (unsigned int) val;
100     qreal frac = val - (qreal)ival;
101
102     int ifrac = (int)(frac * 1000000000);
103     if (ifrac == 1000000000) {
104         ++ival;
105         ifrac = 0;
106     }
107     char output[256];
108     int i = 0;
109     while (ival) {
110         output[i] = '0' + (ival % 10);
111         ++i;
112         ival /= 10;
113     }
114     int fact = 100000000;
115     if (i == 0) {
116         *(buf++) = '0';
117     } else {
118         while (i) {
119             *(buf++) = output[--i];
120             fact /= 10;
121             ifrac /= 10;
122         }
123     }
124
125     if (ifrac) {
126         *(buf++) =  '.';
127         while (fact) {
128             *(buf++) = '0' + ((ifrac/fact) % 10);
129             fact /= 10;
130         }
131     }
132     *(buf++) = ' ';
133     *buf = 0;
134     return ret;
135 }
136
137 const char *qt_int_to_string(int val, char *buf) {
138     const char *ret = buf;
139     if (val < 0) {
140         *(buf++) = '-';
141         val = -val;
142     }
143     char output[256];
144     int i = 0;
145     while (val) {
146         output[i] = '0' + (val % 10);
147         ++i;
148         val /= 10;
149     }
150     if (i == 0) {
151         *(buf++) = '0';
152     } else {
153         while (i)
154             *(buf++) = output[--i];
155     }
156     *(buf++) = ' ';
157     *buf = 0;
158     return ret;
159 }
160
161
162 namespace QPdf {
163     ByteStream::ByteStream(QByteArray *byteArray, bool fileBacking)
164             : dev(new QBuffer(byteArray)),
165             fileBackingEnabled(fileBacking),
166             fileBackingActive(false),
167             handleDirty(false)
168     {
169         dev->open(QIODevice::ReadWrite | QIODevice::Append);
170     }
171
172     ByteStream::ByteStream(bool fileBacking)
173             : dev(new QBuffer(&ba)),
174             fileBackingEnabled(fileBacking),
175             fileBackingActive(false),
176             handleDirty(false)
177     {
178         dev->open(QIODevice::ReadWrite);
179     }
180
181     ByteStream::~ByteStream()
182     {
183         delete dev;
184     }
185
186     ByteStream &ByteStream::operator <<(char chr)
187     {
188         if (handleDirty) prepareBuffer();
189         dev->write(&chr, 1);
190         return *this;
191     }
192
193     ByteStream &ByteStream::operator <<(const char *str)
194     {
195         if (handleDirty) prepareBuffer();
196         dev->write(str, strlen(str));
197         return *this;
198     }
199
200     ByteStream &ByteStream::operator <<(const QByteArray &str)
201     {
202         if (handleDirty) prepareBuffer();
203         dev->write(str);
204         return *this;
205     }
206
207     ByteStream &ByteStream::operator <<(const ByteStream &src)
208     {
209         Q_ASSERT(!src.dev->isSequential());
210         if (handleDirty) prepareBuffer();
211         // We do play nice here, even though it looks ugly.
212         // We save the position and restore it afterwards.
213         ByteStream &s = const_cast<ByteStream&>(src);
214         qint64 pos = s.dev->pos();
215         s.dev->reset();
216         while (!s.dev->atEnd()) {
217             QByteArray buf = s.dev->read(chunkSize());
218             dev->write(buf);
219         }
220         s.dev->seek(pos);
221         return *this;
222     }
223
224     ByteStream &ByteStream::operator <<(qreal val) {
225         char buf[256];
226         qt_real_to_string(val, buf);
227         *this << buf;
228         return *this;
229     }
230
231     ByteStream &ByteStream::operator <<(int val) {
232         char buf[256];
233         qt_int_to_string(val, buf);
234         *this << buf;
235         return *this;
236     }
237
238     ByteStream &ByteStream::operator <<(const QPointF &p) {
239         char buf[256];
240         qt_real_to_string(p.x(), buf);
241         *this << buf;
242         qt_real_to_string(p.y(), buf);
243         *this << buf;
244         return *this;
245     }
246
247     QIODevice *ByteStream::stream()
248     {
249         dev->reset();
250         handleDirty = true;
251         return dev;
252     }
253
254     void ByteStream::clear()
255     {
256         dev->open(QIODevice::ReadWrite | QIODevice::Truncate);
257     }
258
259     void ByteStream::constructor_helper(QByteArray *ba)
260     {
261         delete dev;
262         dev = new QBuffer(ba);
263         dev->open(QIODevice::ReadWrite);
264     }
265
266     void ByteStream::prepareBuffer()
267     {
268         Q_ASSERT(!dev->isSequential());
269         qint64 size = dev->size();
270         if (fileBackingEnabled && !fileBackingActive
271                 && size > maxMemorySize()) {
272             // Switch to file backing.
273             QTemporaryFile *newFile = new QTemporaryFile;
274             newFile->open();
275             dev->reset();
276             while (!dev->atEnd()) {
277                 QByteArray buf = dev->read(chunkSize());
278                 newFile->write(buf);
279             }
280             delete dev;
281             dev = newFile;
282             ba.clear();
283             fileBackingActive = true;
284         }
285         if (dev->pos() != size) {
286             dev->seek(size);
287             handleDirty = false;
288         }
289     }
290 }
291
292 #define QT_PATH_ELEMENT(elm)
293
294 QByteArray QPdf::generatePath(const QPainterPath &path, const QTransform &matrix, PathFlags flags)
295 {
296     QByteArray result;
297     if (!path.elementCount())
298         return result;
299
300     ByteStream s(&result);
301
302     int start = -1;
303     for (int i = 0; i < path.elementCount(); ++i) {
304         const QPainterPath::Element &elm = path.elementAt(i);
305         switch (elm.type) {
306         case QPainterPath::MoveToElement:
307             if (start >= 0
308                 && path.elementAt(start).x == path.elementAt(i-1).x
309                 && path.elementAt(start).y == path.elementAt(i-1).y)
310                 s << "h\n";
311             s << matrix.map(QPointF(elm.x, elm.y)) << "m\n";
312             start = i;
313                 break;
314         case QPainterPath::LineToElement:
315             s << matrix.map(QPointF(elm.x, elm.y)) << "l\n";
316             break;
317         case QPainterPath::CurveToElement:
318             Q_ASSERT(path.elementAt(i+1).type == QPainterPath::CurveToDataElement);
319             Q_ASSERT(path.elementAt(i+2).type == QPainterPath::CurveToDataElement);
320             s << matrix.map(QPointF(elm.x, elm.y))
321               << matrix.map(QPointF(path.elementAt(i+1).x, path.elementAt(i+1).y))
322               << matrix.map(QPointF(path.elementAt(i+2).x, path.elementAt(i+2).y))
323               << "c\n";
324             i += 2;
325             break;
326         default:
327             qFatal("QPdf::generatePath(), unhandled type: %d", elm.type);
328         }
329     }
330     if (start >= 0
331         && path.elementAt(start).x == path.elementAt(path.elementCount()-1).x
332         && path.elementAt(start).y == path.elementAt(path.elementCount()-1).y)
333         s << "h\n";
334
335     Qt::FillRule fillRule = path.fillRule();
336
337     const char *op = "";
338     switch (flags) {
339     case ClipPath:
340         op = (fillRule == Qt::WindingFill) ? "W n\n" : "W* n\n";
341         break;
342     case FillPath:
343         op = (fillRule == Qt::WindingFill) ? "f\n" : "f*\n";
344         break;
345     case StrokePath:
346         op = "S\n";
347         break;
348     case FillAndStrokePath:
349         op = (fillRule == Qt::WindingFill) ? "B\n" : "B*\n";
350         break;
351     }
352     s << op;
353     return result;
354 }
355
356 QByteArray QPdf::generateMatrix(const QTransform &matrix)
357 {
358     QByteArray result;
359     ByteStream s(&result);
360     s << matrix.m11()
361       << matrix.m12()
362       << matrix.m21()
363       << matrix.m22()
364       << matrix.dx()
365       << matrix.dy()
366       << "cm\n";
367     return result;
368 }
369
370 QByteArray QPdf::generateDashes(const QPen &pen)
371 {
372     QByteArray result;
373     ByteStream s(&result);
374     s << '[';
375
376     QVector<qreal> dasharray = pen.dashPattern();
377     qreal w = pen.widthF();
378     if (w < 0.001)
379         w = 1;
380     for (int i = 0; i < dasharray.size(); ++i) {
381         qreal dw = dasharray.at(i)*w;
382         if (dw < 0.0001) dw = 0.0001;
383         s << dw;
384     }
385     s << ']';
386     //qDebug() << "dasharray: pen has" << dasharray;
387     //qDebug() << "  => " << result;
388     return result;
389 }
390
391
392
393 static const char* pattern_for_brush[] = {
394     0, // NoBrush
395     0, // SolidPattern
396     "0 J\n"
397     "6 w\n"
398     "[] 0 d\n"
399     "4 0 m\n"
400     "4 8 l\n"
401     "0 4 m\n"
402     "8 4 l\n"
403     "S\n", // Dense1Pattern
404
405     "0 J\n"
406     "2 w\n"
407     "[6 2] 1 d\n"
408     "0 0 m\n"
409     "0 8 l\n"
410     "8 0 m\n"
411     "8 8 l\n"
412     "S\n"
413     "[] 0 d\n"
414     "2 0 m\n"
415     "2 8 l\n"
416     "6 0 m\n"
417     "6 8 l\n"
418     "S\n"
419     "[6 2] -3 d\n"
420     "4 0 m\n"
421     "4 8 l\n"
422     "S\n", // Dense2Pattern
423
424     "0 J\n"
425     "2 w\n"
426     "[6 2] 1 d\n"
427     "0 0 m\n"
428     "0 8 l\n"
429     "8 0 m\n"
430     "8 8 l\n"
431     "S\n"
432     "[2 2] -1 d\n"
433     "2 0 m\n"
434     "2 8 l\n"
435     "6 0 m\n"
436     "6 8 l\n"
437     "S\n"
438     "[6 2] -3 d\n"
439     "4 0 m\n"
440     "4 8 l\n"
441     "S\n", // Dense3Pattern
442
443     "0 J\n"
444     "2 w\n"
445     "[2 2] 1 d\n"
446     "0 0 m\n"
447     "0 8 l\n"
448     "8 0 m\n"
449     "8 8 l\n"
450     "S\n"
451     "[2 2] -1 d\n"
452     "2 0 m\n"
453     "2 8 l\n"
454     "6 0 m\n"
455     "6 8 l\n"
456     "S\n"
457     "[2 2] 1 d\n"
458     "4 0 m\n"
459     "4 8 l\n"
460     "S\n", // Dense4Pattern
461
462     "0 J\n"
463     "2 w\n"
464     "[2 6] -1 d\n"
465     "0 0 m\n"
466     "0 8 l\n"
467     "8 0 m\n"
468     "8 8 l\n"
469     "S\n"
470     "[2 2] 1 d\n"
471     "2 0 m\n"
472     "2 8 l\n"
473     "6 0 m\n"
474     "6 8 l\n"
475     "S\n"
476     "[2 6] 3 d\n"
477     "4 0 m\n"
478     "4 8 l\n"
479     "S\n", // Dense5Pattern
480
481     "0 J\n"
482     "2 w\n"
483     "[2 6] -1 d\n"
484     "0 0 m\n"
485     "0 8 l\n"
486     "8 0 m\n"
487     "8 8 l\n"
488     "S\n"
489     "[2 6] 3 d\n"
490     "4 0 m\n"
491     "4 8 l\n"
492     "S\n", // Dense6Pattern
493
494     "0 J\n"
495     "2 w\n"
496     "[2 6] -1 d\n"
497     "0 0 m\n"
498     "0 8 l\n"
499     "8 0 m\n"
500     "8 8 l\n"
501     "S\n", // Dense7Pattern
502
503     "1 w\n"
504     "0 4 m\n"
505     "8 4 l\n"
506     "S\n", // HorPattern
507
508     "1 w\n"
509     "4 0 m\n"
510     "4 8 l\n"
511     "S\n", // VerPattern
512
513     "1 w\n"
514     "4 0 m\n"
515     "4 8 l\n"
516     "0 4 m\n"
517     "8 4 l\n"
518     "S\n", // CrossPattern
519
520     "1 w\n"
521     "-1 5 m\n"
522     "5 -1 l\n"
523     "3 9 m\n"
524     "9 3 l\n"
525     "S\n", // BDiagPattern
526
527     "1 w\n"
528     "-1 3 m\n"
529     "5 9 l\n"
530     "3 -1 m\n"
531     "9 5 l\n"
532     "S\n", // FDiagPattern
533
534     "1 w\n"
535     "-1 3 m\n"
536     "5 9 l\n"
537     "3 -1 m\n"
538     "9 5 l\n"
539     "-1 5 m\n"
540     "5 -1 l\n"
541     "3 9 m\n"
542     "9 3 l\n"
543     "S\n", // DiagCrossPattern
544 };
545
546 QByteArray QPdf::patternForBrush(const QBrush &b)
547 {
548     int style = b.style();
549     if (style > Qt::DiagCrossPattern)
550         return QByteArray();
551     return pattern_for_brush[style];
552 }
553
554 #ifdef USE_NATIVE_GRADIENTS
555 static void writeTriangleLine(uchar *&data, int xpos, int ypos, int xoff, int yoff, uint rgb, uchar flag, bool alpha)
556 {
557     data[0] =  flag;
558     data[1] = (uchar)(xpos >> 16);
559     data[2] = (uchar)(xpos >> 8);
560     data[3] = (uchar)(xpos >> 0);
561     data[4] = (uchar)(ypos >> 16);
562     data[5] = (uchar)(ypos >> 8);
563     data[6] = (uchar)(ypos >> 0);
564     data += 7;
565     if (alpha) {
566         *data++ = (uchar)qAlpha(rgb);
567     } else {
568         *data++ = (uchar)qRed(rgb);
569         *data++ = (uchar)qGreen(rgb);
570         *data++ = (uchar)qBlue(rgb);
571     }
572     xpos += xoff;
573     ypos += yoff;
574     data[0] =  flag;
575     data[1] = (uchar)(xpos >> 16);
576     data[2] = (uchar)(xpos >> 8);
577     data[3] = (uchar)(xpos >> 0);
578     data[4] = (uchar)(ypos >> 16);
579     data[5] = (uchar)(ypos >> 8);
580     data[6] = (uchar)(ypos >> 0);
581     data += 7;
582     if (alpha) {
583         *data++ = (uchar)qAlpha(rgb);
584     } else {
585         *data++ = (uchar)qRed(rgb);
586         *data++ = (uchar)qGreen(rgb);
587         *data++ = (uchar)qBlue(rgb);
588     }
589 }
590
591
592 QByteArray QPdf::generateLinearGradientShader(const QLinearGradient *gradient, const QPointF *page_rect, bool alpha)
593 {
594     // generate list of triangles with colors
595     QPointF start = gradient->start();
596     QPointF stop = gradient->finalStop();
597     QGradientStops stops = gradient->stops();
598     QPointF offset = stop - start;
599     QGradient::Spread spread = gradient->spread();
600
601     if (gradient->spread() == QGradient::ReflectSpread) {
602         offset *= 2;
603         for (int i = stops.size() - 2; i >= 0; --i) {
604             QGradientStop stop = stops.at(i);
605             stop.first = 2. - stop.first;
606             stops.append(stop);
607         }
608         for (int i = 0 ; i < stops.size(); ++i)
609             stops[i].first /= 2.;
610     }
611
612     QPointF orthogonal(offset.y(), -offset.x());
613     qreal length = offset.x()*offset.x() + offset.y()*offset.y();
614
615     // find the max and min values in offset and orth direction that are needed to cover
616     // the whole page
617     int off_min = INT_MAX;
618     int off_max = INT_MIN;
619     qreal ort_min = INT_MAX;
620     qreal ort_max = INT_MIN;
621     for (int i = 0; i < 4; ++i) {
622         qreal off = ((page_rect[i].x() - start.x()) * offset.x() + (page_rect[i].y() - start.y()) * offset.y())/length;
623         qreal ort = ((page_rect[i].x() - start.x()) * orthogonal.x() + (page_rect[i].y() - start.y()) * orthogonal.y())/length;
624         off_min = qMin(off_min, qFloor(off));
625         off_max = qMax(off_max, qCeil(off));
626         ort_min = qMin(ort_min, ort);
627         ort_max = qMax(ort_max, ort);
628     }
629     ort_min -= 1;
630     ort_max += 1;
631
632     start += off_min * offset + ort_min * orthogonal;
633     orthogonal *= (ort_max - ort_min);
634     int num = off_max - off_min;
635
636     QPointF gradient_rect[4] = { start,
637                                  start + orthogonal,
638                                  start + num*offset,
639                                  start + num*offset + orthogonal };
640     qreal xmin = gradient_rect[0].x();
641     qreal xmax = gradient_rect[0].x();
642     qreal ymin = gradient_rect[0].y();
643     qreal ymax = gradient_rect[0].y();
644     for (int i = 1; i < 4; ++i) {
645         xmin = qMin(xmin, gradient_rect[i].x());
646         xmax = qMax(xmax, gradient_rect[i].x());
647         ymin = qMin(ymin, gradient_rect[i].y());
648         ymax = qMax(ymax, gradient_rect[i].y());
649     }
650     xmin -= 1000;
651     xmax += 1000;
652     ymin -= 1000;
653     ymax += 1000;
654     start -= QPointF(xmin, ymin);
655     qreal factor_x = qreal(1<<24)/(xmax - xmin);
656     qreal factor_y = qreal(1<<24)/(ymax - ymin);
657     int xoff = (int)(orthogonal.x()*factor_x);
658     int yoff = (int)(orthogonal.y()*factor_y);
659
660     QByteArray triangles;
661     triangles.resize(spread == QGradient::PadSpread ? 20*(stops.size()+2) : 20*num*stops.size());
662     uchar *data = (uchar *) triangles.data();
663     if (spread == QGradient::PadSpread) {
664         if (off_min > 0 || off_max < 1) {
665             // linear gradient outside of page
666             const QGradientStop &current_stop = off_min > 0 ? stops.at(stops.size()-1) : stops.at(0);
667             uint rgb = current_stop.second.rgba();
668             int xpos = (int)(start.x()*factor_x);
669             int ypos = (int)(start.y()*factor_y);
670             writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, 0, alpha);
671             start += num*offset;
672             xpos = (int)(start.x()*factor_x);
673             ypos = (int)(start.y()*factor_y);
674             writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, 1, alpha);
675         } else {
676             int flag = 0;
677             if (off_min < 0) {
678                 uint rgb = stops.at(0).second.rgba();
679                 int xpos = (int)(start.x()*factor_x);
680                 int ypos = (int)(start.y()*factor_y);
681                 writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
682                 start -= off_min*offset;
683                 flag = 1;
684             }
685             for (int s = 0; s < stops.size(); ++s) {
686                 const QGradientStop &current_stop = stops.at(s);
687                 uint rgb = current_stop.second.rgba();
688                 int xpos = (int)(start.x()*factor_x);
689                 int ypos = (int)(start.y()*factor_y);
690                 writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
691                 if (s < stops.size()-1)
692                     start += offset*(stops.at(s+1).first - stops.at(s).first);
693                 flag = 1;
694             }
695             if (off_max > 1) {
696                 start += (off_max - 1)*offset;
697                 uint rgb = stops.at(stops.size()-1).second.rgba();
698                 int xpos = (int)(start.x()*factor_x);
699                 int ypos = (int)(start.y()*factor_y);
700                 writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
701             }
702         }
703     } else {
704         for (int i = 0; i < num; ++i) {
705             uchar flag = 0;
706             for (int s = 0; s < stops.size(); ++s) {
707                 uint rgb = stops.at(s).second.rgba();
708                 int xpos = (int)(start.x()*factor_x);
709                 int ypos = (int)(start.y()*factor_y);
710                 writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
711                 if (s < stops.size()-1)
712                     start += offset*(stops.at(s+1).first - stops.at(s).first);
713                 flag = 1;
714             }
715         }
716     }
717     triangles.resize((char *)data - triangles.constData());
718
719     QByteArray shader;
720     QPdf::ByteStream s(&shader);
721     s << "<<\n"
722         "/ShadingType 4\n"
723         "/ColorSpace " << (alpha ? "/DeviceGray\n" : "/DeviceRGB\n") <<
724         "/AntiAlias true\n"
725         "/BitsPerCoordinate 24\n"
726         "/BitsPerComponent 8\n"
727         "/BitsPerFlag 8\n"
728         "/Decode [" << xmin << xmax << ymin << ymax << (alpha ? "0 1]\n" : "0 1 0 1 0 1]\n") <<
729         "/AntiAlias true\n"
730         "/Length " << triangles.length() << "\n"
731         ">>\n"
732         "stream\n" << triangles << "endstream\n"
733         "endobj\n";
734     return shader;
735 }
736 #endif
737
738 static void moveToHook(qfixed x, qfixed y, void *data)
739 {
740     QPdf::Stroker *t = (QPdf::Stroker *)data;
741     if (!t->first)
742         *t->stream << "h\n";
743     if (!t->cosmeticPen)
744         t->matrix.map(x, y, &x, &y);
745     *t->stream << x << y << "m\n";
746     t->first = false;
747 }
748
749 static void lineToHook(qfixed x, qfixed y, void *data)
750 {
751     QPdf::Stroker *t = (QPdf::Stroker *)data;
752     if (!t->cosmeticPen)
753         t->matrix.map(x, y, &x, &y);
754     *t->stream << x << y << "l\n";
755 }
756
757 static void cubicToHook(qfixed c1x, qfixed c1y,
758                         qfixed c2x, qfixed c2y,
759                         qfixed ex, qfixed ey,
760                         void *data)
761 {
762     QPdf::Stroker *t = (QPdf::Stroker *)data;
763     if (!t->cosmeticPen) {
764         t->matrix.map(c1x, c1y, &c1x, &c1y);
765         t->matrix.map(c2x, c2y, &c2x, &c2y);
766         t->matrix.map(ex, ey, &ex, &ey);
767     }
768     *t->stream << c1x << c1y
769                << c2x << c2y
770                << ex << ey
771                << "c\n";
772 }
773
774 QPdf::Stroker::Stroker()
775     : stream(0),
776     first(true),
777     dashStroker(&basicStroker)
778 {
779     stroker = &basicStroker;
780     basicStroker.setMoveToHook(moveToHook);
781     basicStroker.setLineToHook(lineToHook);
782     basicStroker.setCubicToHook(cubicToHook);
783     cosmeticPen = true;
784     basicStroker.setStrokeWidth(.1);
785 }
786
787 void QPdf::Stroker::setPen(const QPen &pen)
788 {
789     if (pen.style() == Qt::NoPen) {
790         stroker = 0;
791         return;
792     }
793     qreal w = pen.widthF();
794     bool zeroWidth = w < 0.0001;
795     cosmeticPen = pen.isCosmetic();
796     if (zeroWidth)
797         w = .1;
798
799     basicStroker.setStrokeWidth(w);
800     basicStroker.setCapStyle(pen.capStyle());
801     basicStroker.setJoinStyle(pen.joinStyle());
802     basicStroker.setMiterLimit(pen.miterLimit());
803
804     QVector<qreal> dashpattern = pen.dashPattern();
805     if (zeroWidth) {
806         for (int i = 0; i < dashpattern.size(); ++i)
807             dashpattern[i] *= 10.;
808     }
809     if (!dashpattern.isEmpty()) {
810         dashStroker.setDashPattern(dashpattern);
811         dashStroker.setDashOffset(pen.dashOffset());
812         stroker = &dashStroker;
813     } else {
814         stroker = &basicStroker;
815     }
816 }
817
818 void QPdf::Stroker::strokePath(const QPainterPath &path)
819 {
820     if (!stroker)
821         return;
822     first = true;
823
824     stroker->strokePath(path, this, cosmeticPen ? matrix : QTransform());
825     *stream << "h f\n";
826 }
827
828 QByteArray QPdf::ascii85Encode(const QByteArray &input)
829 {
830     int isize = input.size()/4*4;
831     QByteArray output;
832     output.resize(input.size()*5/4+7);
833     char *out = output.data();
834     const uchar *in = (const uchar *)input.constData();
835     for (int i = 0; i < isize; i += 4) {
836         uint val = (((uint)in[i])<<24) + (((uint)in[i+1])<<16) + (((uint)in[i+2])<<8) + (uint)in[i+3];
837         if (val == 0) {
838             *out = 'z';
839             ++out;
840         } else {
841             char base[5];
842             base[4] = val % 85;
843             val /= 85;
844             base[3] = val % 85;
845             val /= 85;
846             base[2] = val % 85;
847             val /= 85;
848             base[1] = val % 85;
849             val /= 85;
850             base[0] = val % 85;
851             *(out++) = base[0] + '!';
852             *(out++) = base[1] + '!';
853             *(out++) = base[2] + '!';
854             *(out++) = base[3] + '!';
855             *(out++) = base[4] + '!';
856         }
857     }
858     //write the last few bytes
859     int remaining = input.size() - isize;
860     if (remaining) {
861         uint val = 0;
862         for (int i = isize; i < input.size(); ++i)
863             val = (val << 8) + in[i];
864         val <<= 8*(4-remaining);
865         char base[5];
866         base[4] = val % 85;
867         val /= 85;
868         base[3] = val % 85;
869         val /= 85;
870         base[2] = val % 85;
871         val /= 85;
872         base[1] = val % 85;
873         val /= 85;
874         base[0] = val % 85;
875         for (int i = 0; i < remaining+1; ++i)
876             *(out++) = base[i] + '!';
877     }
878     *(out++) = '~';
879     *(out++) = '>';
880     output.resize(out-output.data());
881     return output;
882 }
883
884 const char *QPdf::toHex(ushort u, char *buffer)
885 {
886     int i = 3;
887     while (i >= 0) {
888         ushort hex = (u & 0x000f);
889         if (hex < 0x0a)
890             buffer[i] = '0'+hex;
891         else
892             buffer[i] = 'A'+(hex-0x0a);
893         u = u >> 4;
894         i--;
895     }
896     buffer[4] = '\0';
897     return buffer;
898 }
899
900 const char *QPdf::toHex(uchar u, char *buffer)
901 {
902     int i = 1;
903     while (i >= 0) {
904         ushort hex = (u & 0x000f);
905         if (hex < 0x0a)
906             buffer[i] = '0'+hex;
907         else
908             buffer[i] = 'A'+(hex-0x0a);
909         u = u >> 4;
910         i--;
911     }
912     buffer[2] = '\0';
913     return buffer;
914 }
915
916
917 QPdfPage::QPdfPage()
918     : QPdf::ByteStream(true) // Enable file backing
919 {
920 }
921
922 void QPdfPage::streamImage(int w, int h, int object)
923 {
924     *this << w << "0 0 " << -h << "0 " << h << "cm /Im" << object << " Do\n";
925     if (!images.contains(object))
926         images.append(object);
927 }
928
929
930 QPdfEngine::QPdfEngine(QPdfEnginePrivate &dd)
931     : QPaintEngine(dd, qt_pdf_decide_features())
932 {
933 }
934
935 QPdfEngine::QPdfEngine()
936     : QPaintEngine(*new QPdfEnginePrivate(), qt_pdf_decide_features())
937 {
938 }
939
940 void QPdfEngine::setOutputFilename(const QString &filename)
941 {
942     Q_D(QPdfEngine);
943     d->outputFileName = filename;
944 }
945
946
947 void QPdfEngine::drawPoints (const QPointF *points, int pointCount)
948 {
949     if (!points)
950         return;
951
952     Q_D(QPdfEngine);
953     QPainterPath p;
954     for (int i=0; i!=pointCount;++i) {
955         p.moveTo(points[i]);
956         p.lineTo(points[i] + QPointF(0, 0.001));
957     }
958
959     bool hadBrush = d->hasBrush;
960     d->hasBrush = false;
961     drawPath(p);
962     d->hasBrush = hadBrush;
963 }
964
965 void QPdfEngine::drawLines (const QLineF *lines, int lineCount)
966 {
967     if (!lines)
968         return;
969
970     Q_D(QPdfEngine);
971     QPainterPath p;
972     for (int i=0; i!=lineCount;++i) {
973         p.moveTo(lines[i].p1());
974         p.lineTo(lines[i].p2());
975     }
976     bool hadBrush = d->hasBrush;
977     d->hasBrush = false;
978     drawPath(p);
979     d->hasBrush = hadBrush;
980 }
981
982 void QPdfEngine::drawRects (const QRectF *rects, int rectCount)
983 {
984     if (!rects)
985         return;
986
987     Q_D(QPdfEngine);
988
989     if (d->clipEnabled && d->allClipped)
990         return;
991     if (!d->hasPen && !d->hasBrush)
992         return;
993
994     QBrush penBrush = d->pen.brush();
995     if (d->simplePen || !d->hasPen) {
996         // draw strokes natively in this case for better output
997         if(!d->simplePen && !d->stroker.matrix.isIdentity())
998             *d->currentPage << "q\n" << QPdf::generateMatrix(d->stroker.matrix);
999         for (int i = 0; i < rectCount; ++i)
1000             *d->currentPage << rects[i].x() << rects[i].y() << rects[i].width() << rects[i].height() << "re\n";
1001         *d->currentPage << (d->hasPen ? (d->hasBrush ? "B\n" : "S\n") : "f\n");
1002         if(!d->simplePen && !d->stroker.matrix.isIdentity())
1003             *d->currentPage << "Q\n";
1004     } else {
1005         QPainterPath p;
1006         for (int i=0; i!=rectCount; ++i)
1007             p.addRect(rects[i]);
1008         drawPath(p);
1009     }
1010 }
1011
1012 void QPdfEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
1013 {
1014     Q_D(QPdfEngine);
1015
1016     if (!points || !pointCount)
1017         return;
1018
1019     bool hb = d->hasBrush;
1020     QPainterPath p;
1021
1022     switch(mode) {
1023         case OddEvenMode:
1024             p.setFillRule(Qt::OddEvenFill);
1025             break;
1026         case ConvexMode:
1027         case WindingMode:
1028             p.setFillRule(Qt::WindingFill);
1029             break;
1030         case PolylineMode:
1031             d->hasBrush = false;
1032             break;
1033         default:
1034             break;
1035     }
1036
1037     p.moveTo(points[0]);
1038     for (int i = 1; i < pointCount; ++i)
1039         p.lineTo(points[i]);
1040
1041     if (mode != PolylineMode)
1042         p.closeSubpath();
1043     drawPath(p);
1044
1045     d->hasBrush = hb;
1046 }
1047
1048 void QPdfEngine::drawPath (const QPainterPath &p)
1049 {
1050     Q_D(QPdfEngine);
1051
1052     if (d->clipEnabled && d->allClipped)
1053         return;
1054     if (!d->hasPen && !d->hasBrush)
1055         return;
1056
1057     if (d->simplePen) {
1058         // draw strokes natively in this case for better output
1059         *d->currentPage << QPdf::generatePath(p, QTransform(), d->hasBrush ? QPdf::FillAndStrokePath : QPdf::StrokePath);
1060     } else {
1061         if (d->hasBrush)
1062             *d->currentPage << QPdf::generatePath(p, d->stroker.matrix, QPdf::FillPath);
1063         if (d->hasPen) {
1064             *d->currentPage << "q\n";
1065             QBrush b = d->brush;
1066             d->brush = d->pen.brush();
1067             setBrush();
1068             d->stroker.strokePath(p);
1069             *d->currentPage << "Q\n";
1070             d->brush = b;
1071         }
1072     }
1073 }
1074
1075 void QPdfEngine::drawPixmap (const QRectF &rectangle, const QPixmap &pixmap, const QRectF &sr)
1076 {
1077     if (sr.isEmpty() || rectangle.isEmpty() || pixmap.isNull())
1078         return;
1079     Q_D(QPdfEngine);
1080
1081     QBrush b = d->brush;
1082
1083     QRect sourceRect = sr.toRect();
1084     QPixmap pm = sourceRect != pixmap.rect() ? pixmap.copy(sourceRect) : pixmap;
1085     QImage image = pm.toImage();
1086     bool bitmap = true;
1087     const int object = d->addImage(image, &bitmap, pm.cacheKey());
1088     if (object < 0)
1089         return;
1090
1091     *d->currentPage << "q\n/GSa gs\n";
1092     *d->currentPage
1093         << QPdf::generateMatrix(QTransform(rectangle.width() / sr.width(), 0, 0, rectangle.height() / sr.height(),
1094                                            rectangle.x(), rectangle.y()) * (d->simplePen ? QTransform() : d->stroker.matrix));
1095     if (bitmap) {
1096         // set current pen as d->brush
1097         d->brush = d->pen.brush();
1098     }
1099     setBrush();
1100     d->currentPage->streamImage(image.width(), image.height(), object);
1101     *d->currentPage << "Q\n";
1102
1103     d->brush = b;
1104 }
1105
1106 void QPdfEngine::drawImage(const QRectF &rectangle, const QImage &image, const QRectF &sr, Qt::ImageConversionFlags)
1107 {
1108     if (sr.isEmpty() || rectangle.isEmpty() || image.isNull())
1109         return;
1110     Q_D(QPdfEngine);
1111
1112     QRect sourceRect = sr.toRect();
1113     QImage im = sourceRect != image.rect() ? image.copy(sourceRect) : image;
1114     bool bitmap = true;
1115     const int object = d->addImage(im, &bitmap, im.cacheKey());
1116     if (object < 0)
1117         return;
1118
1119     *d->currentPage << "q\n/GSa gs\n";
1120     *d->currentPage
1121         << QPdf::generateMatrix(QTransform(rectangle.width() / sr.width(), 0, 0, rectangle.height() / sr.height(),
1122                                            rectangle.x(), rectangle.y()) * (d->simplePen ? QTransform() : d->stroker.matrix));
1123     setBrush();
1124     d->currentPage->streamImage(im.width(), im.height(), object);
1125     *d->currentPage << "Q\n";
1126 }
1127
1128 void QPdfEngine::drawTiledPixmap (const QRectF &rectangle, const QPixmap &pixmap, const QPointF &point)
1129 {
1130     Q_D(QPdfEngine);
1131
1132     bool bitmap = (pixmap.depth() == 1);
1133     QBrush b = d->brush;
1134     QPointF bo = d->brushOrigin;
1135     bool hp = d->hasPen;
1136     d->hasPen = false;
1137     bool hb = d->hasBrush;
1138     d->hasBrush = true;
1139
1140     d->brush = QBrush(pixmap);
1141     if (bitmap)
1142         // #### fix bitmap case where we have a brush pen
1143         d->brush.setColor(d->pen.color());
1144
1145     d->brushOrigin = -point;
1146     *d->currentPage << "q\n";
1147     setBrush();
1148
1149     drawRects(&rectangle, 1);
1150     *d->currentPage << "Q\n";
1151
1152     d->hasPen = hp;
1153     d->hasBrush = hb;
1154     d->brush = b;
1155     d->brushOrigin = bo;
1156 }
1157
1158 void QPdfEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
1159 {
1160     Q_D(QPdfEngine);
1161
1162     if (!d->hasPen || (d->clipEnabled && d->allClipped))
1163         return;
1164
1165     if (d->stroker.matrix.type() >= QTransform::TxProject) {
1166         QPaintEngine::drawTextItem(p, textItem);
1167         return;
1168     }
1169
1170     *d->currentPage << "q\n";
1171     if(!d->simplePen)
1172         *d->currentPage << QPdf::generateMatrix(d->stroker.matrix);
1173
1174     bool hp = d->hasPen;
1175     d->hasPen = false;
1176     QBrush b = d->brush;
1177     d->brush = d->pen.brush();
1178     setBrush();
1179
1180     const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
1181     Q_ASSERT(ti.fontEngine->type() != QFontEngine::Multi);
1182     d->drawTextItem(p, ti);
1183     d->hasPen = hp;
1184     d->brush = b;
1185     *d->currentPage << "Q\n";
1186 }
1187
1188
1189 void QPdfEngine::updateState(const QPaintEngineState &state)
1190 {
1191     Q_D(QPdfEngine);
1192
1193     QPaintEngine::DirtyFlags flags = state.state();
1194
1195     if (flags & DirtyTransform)
1196         d->stroker.matrix = state.transform();
1197
1198     if (flags & DirtyPen) {
1199         d->pen = state.pen();
1200         d->hasPen = d->pen.style() != Qt::NoPen;
1201         d->stroker.setPen(d->pen);
1202         QBrush penBrush = d->pen.brush();
1203         bool oldSimple = d->simplePen;
1204         d->simplePen = (d->hasPen && (penBrush.style() == Qt::SolidPattern) && penBrush.isOpaque());
1205         if (oldSimple != d->simplePen)
1206             flags |= DirtyTransform;
1207     }
1208     if (flags & DirtyBrush) {
1209         d->brush = state.brush();
1210         if (d->brush.color().alpha() == 0 && d->brush.style() == Qt::SolidPattern)
1211             d->brush.setStyle(Qt::NoBrush);
1212         d->hasBrush = d->brush.style() != Qt::NoBrush;
1213     }
1214     if (flags & DirtyBrushOrigin) {
1215         d->brushOrigin = state.brushOrigin();
1216         flags |= DirtyBrush;
1217     }
1218     if (flags & DirtyOpacity)
1219         d->opacity = state.opacity();
1220
1221     bool ce = d->clipEnabled;
1222     if (flags & DirtyClipPath) {
1223         d->clipEnabled = true;
1224         updateClipPath(state.clipPath(), state.clipOperation());
1225     } else if (flags & DirtyClipRegion) {
1226         d->clipEnabled = true;
1227         QPainterPath path;
1228         QVector<QRect> rects = state.clipRegion().rects();
1229         for (int i = 0; i < rects.size(); ++i)
1230             path.addRect(rects.at(i));
1231         updateClipPath(path, state.clipOperation());
1232         flags |= DirtyClipPath;
1233     } else if (flags & DirtyClipEnabled) {
1234         d->clipEnabled = state.isClipEnabled();
1235     }
1236
1237     if (ce != d->clipEnabled)
1238         flags |= DirtyClipPath;
1239     else if (!d->clipEnabled)
1240         flags &= ~DirtyClipPath;
1241
1242     setupGraphicsState(flags);
1243 }
1244
1245 void QPdfEngine::setupGraphicsState(QPaintEngine::DirtyFlags flags)
1246 {
1247     Q_D(QPdfEngine);
1248     if (flags & DirtyClipPath)
1249         flags |= DirtyTransform|DirtyPen|DirtyBrush;
1250
1251     if (flags & DirtyTransform) {
1252         *d->currentPage << "Q\n";
1253         flags |= DirtyPen|DirtyBrush;
1254     }
1255
1256     if (flags & DirtyClipPath) {
1257         *d->currentPage << "Q q\n";
1258
1259         d->allClipped = false;
1260         if (d->clipEnabled && !d->clips.isEmpty()) {
1261             for (int i = 0; i < d->clips.size(); ++i) {
1262                 if (d->clips.at(i).isEmpty()) {
1263                     d->allClipped = true;
1264                     break;
1265                 }
1266             }
1267             if (!d->allClipped) {
1268                 for (int i = 0; i < d->clips.size(); ++i) {
1269                     *d->currentPage << QPdf::generatePath(d->clips.at(i), QTransform(), QPdf::ClipPath);
1270                 }
1271             }
1272         }
1273     }
1274
1275     if (flags & DirtyTransform) {
1276         *d->currentPage << "q\n";
1277         if (d->simplePen && !d->stroker.matrix.isIdentity())
1278             *d->currentPage << QPdf::generateMatrix(d->stroker.matrix);
1279     }
1280     if (flags & DirtyBrush)
1281         setBrush();
1282     if (d->simplePen && (flags & DirtyPen))
1283         setPen();
1284 }
1285
1286 extern QPainterPath qt_regionToPath(const QRegion &region);
1287
1288 void QPdfEngine::updateClipPath(const QPainterPath &p, Qt::ClipOperation op)
1289 {
1290     Q_D(QPdfEngine);
1291     QPainterPath path = d->stroker.matrix.map(p);
1292     //qDebug() << "updateClipPath: " << d->stroker.matrix << p.boundingRect() << path.boundingRect() << op;
1293
1294     if (op == Qt::NoClip) {
1295         d->clipEnabled = false;
1296         d->clips.clear();
1297     } else if (op == Qt::ReplaceClip) {
1298         d->clips.clear();
1299         d->clips.append(path);
1300     } else if (op == Qt::IntersectClip) {
1301         d->clips.append(path);
1302     } else { // UniteClip
1303         // ask the painter for the current clipping path. that's the easiest solution
1304         path = painter()->clipPath();
1305         path = d->stroker.matrix.map(path);
1306         d->clips.clear();
1307         d->clips.append(path);
1308     }
1309 }
1310
1311 void QPdfEngine::setPen()
1312 {
1313     Q_D(QPdfEngine);
1314     if (d->pen.style() == Qt::NoPen)
1315         return;
1316     QBrush b = d->pen.brush();
1317     Q_ASSERT(b.style() == Qt::SolidPattern && b.isOpaque());
1318
1319     QColor rgba = b.color();
1320     if (d->grayscale) {
1321         qreal gray = qGray(rgba.rgba())/255.;
1322         *d->currentPage << gray << gray << gray;
1323     } else {
1324         *d->currentPage << rgba.redF()
1325                         << rgba.greenF()
1326                         << rgba.blueF();
1327     }
1328     *d->currentPage << "SCN\n";
1329
1330     *d->currentPage << d->pen.widthF() << "w ";
1331
1332     int pdfCapStyle = 0;
1333     switch(d->pen.capStyle()) {
1334     case Qt::FlatCap:
1335         pdfCapStyle = 0;
1336         break;
1337     case Qt::SquareCap:
1338         pdfCapStyle = 2;
1339         break;
1340     case Qt::RoundCap:
1341         pdfCapStyle = 1;
1342         break;
1343     default:
1344         break;
1345     }
1346     *d->currentPage << pdfCapStyle << "J ";
1347
1348     int pdfJoinStyle = 0;
1349     switch(d->pen.joinStyle()) {
1350     case Qt::MiterJoin:
1351         pdfJoinStyle = 0;
1352         break;
1353     case Qt::BevelJoin:
1354         pdfJoinStyle = 2;
1355         break;
1356     case Qt::RoundJoin:
1357         pdfJoinStyle = 1;
1358         break;
1359     default:
1360         break;
1361     }
1362     *d->currentPage << pdfJoinStyle << "j ";
1363
1364     *d->currentPage << QPdf::generateDashes(d->pen) << " 0 d\n";
1365 }
1366
1367
1368 void QPdfEngine::setBrush()
1369 {
1370     Q_D(QPdfEngine);
1371     Qt::BrushStyle style = d->brush.style();
1372     if (style == Qt::NoBrush)
1373         return;
1374
1375     bool specifyColor;
1376     int gStateObject = 0;
1377     int patternObject = d->addBrushPattern(d->stroker.matrix, &specifyColor, &gStateObject);
1378
1379     *d->currentPage << (patternObject ? "/PCSp cs " : "/CSp cs ");
1380     if (specifyColor) {
1381         QColor rgba = d->brush.color();
1382         if (d->grayscale) {
1383             qreal gray = qGray(rgba.rgba())/255.;
1384             *d->currentPage << gray << gray << gray;
1385         } else {
1386             *d->currentPage << rgba.redF()
1387                             << rgba.greenF()
1388                             << rgba.blueF();
1389         }
1390     }
1391     if (patternObject)
1392         *d->currentPage << "/Pat" << patternObject;
1393     *d->currentPage << "scn\n";
1394
1395     if (gStateObject)
1396         *d->currentPage << "/GState" << gStateObject << "gs\n";
1397     else
1398         *d->currentPage << "/GSa gs\n";
1399 }
1400
1401
1402 bool QPdfEngine::newPage()
1403 {
1404     Q_D(QPdfEngine);
1405     if (!isActive())
1406         return false;
1407     d->newPage();
1408
1409     setupGraphicsState(DirtyBrush|DirtyPen|DirtyClipPath);
1410     QFile *outfile = qobject_cast<QFile*> (d->outDevice);
1411     if (outfile && outfile->error() != QFile::NoError)
1412         return false;
1413     return true;
1414 }
1415
1416 QPaintEngine::Type QPdfEngine::type() const
1417 {
1418     return QPaintEngine::Pdf;
1419 }
1420
1421
1422
1423 int QPdfEngine::metric(QPaintDevice::PaintDeviceMetric metricType) const
1424 {
1425     Q_D(const QPdfEngine);
1426     int val;
1427     QRect r = d->pageRect();
1428     switch (metricType) {
1429     case QPaintDevice::PdmWidth:
1430         val = r.width();
1431         break;
1432     case QPaintDevice::PdmHeight:
1433         val = r.height();
1434         break;
1435     case QPaintDevice::PdmDpiX:
1436     case QPaintDevice::PdmDpiY:
1437         val = d->resolution;
1438         break;
1439     case QPaintDevice::PdmPhysicalDpiX:
1440     case QPaintDevice::PdmPhysicalDpiY:
1441         val = 1200;
1442         break;
1443     case QPaintDevice::PdmWidthMM:
1444         val = qRound(r.width()*25.4/d->resolution);
1445         break;
1446     case QPaintDevice::PdmHeightMM:
1447         val = qRound(r.height()*25.4/d->resolution);
1448         break;
1449     case QPaintDevice::PdmNumColors:
1450         val = INT_MAX;
1451         break;
1452     case QPaintDevice::PdmDepth:
1453         val = 32;
1454         break;
1455     default:
1456         qWarning("QPdfWriter::metric: Invalid metric command");
1457         return 0;
1458     }
1459     return val;
1460 }
1461
1462 #define Q_MM(n) int((n * 720 + 127) / 254)
1463
1464 QPdfEnginePrivate::QPdfEnginePrivate()
1465     : clipEnabled(false), allClipped(false), hasPen(true), hasBrush(false), simplePen(false),
1466       outDevice(0), ownsDevice(false),
1467       fullPage(false), embedFonts(true),
1468       landscape(false),
1469       grayscale(false),
1470       paperSize(Q_MM(210), Q_MM(297)), // A4
1471       leftMargin(10), topMargin(10), rightMargin(10), bottomMargin(10) // ~3.5 mm
1472 {
1473     resolution = 1200;
1474     postscript = false;
1475     currentObject = 1;
1476     currentPage = 0;
1477     stroker.stream = 0;
1478
1479     streampos = 0;
1480
1481     stream = new QDataStream;
1482 }
1483
1484 bool QPdfEngine::begin(QPaintDevice *pdev)
1485 {
1486     Q_D(QPdfEngine);
1487     d->pdev = pdev;
1488
1489     if (!d->outDevice) {
1490         if (!d->outputFileName.isEmpty()) {
1491             QFile *file = new QFile(d->outputFileName);
1492             if (!file->open(QFile::WriteOnly|QFile::Truncate)) {
1493                 delete file;
1494                 return false;
1495             }
1496             d->outDevice = file;
1497         } else {
1498             return false;
1499         }
1500         d->ownsDevice = true;
1501     }
1502
1503     d->postscript = false;
1504     d->currentObject = 1;
1505
1506     d->currentPage = new QPdfPage;
1507     d->stroker.stream = d->currentPage;
1508     d->opacity = 1.0;
1509
1510     d->stream->setDevice(d->outDevice);
1511
1512     d->streampos = 0;
1513     d->hasPen = true;
1514     d->hasBrush = false;
1515     d->clipEnabled = false;
1516     d->allClipped = false;
1517
1518     d->xrefPositions.clear();
1519     d->pageRoot = 0;
1520     d->catalog = 0;
1521     d->info = 0;
1522     d->graphicsState = 0;
1523     d->patternColorSpace = 0;
1524
1525     d->pages.clear();
1526     d->imageCache.clear();
1527     d->alphaCache.clear();
1528
1529     setActive(true);
1530     d->writeHeader();
1531     newPage();
1532
1533     return true;
1534 }
1535
1536 bool QPdfEngine::end()
1537 {
1538     Q_D(QPdfEngine);
1539     d->writeTail();
1540
1541     d->stream->unsetDevice();
1542
1543     qDeleteAll(d->fonts);
1544     d->fonts.clear();
1545     delete d->currentPage;
1546     d->currentPage = 0;
1547
1548     if (d->outDevice && d->ownsDevice) {
1549         d->outDevice->close();
1550         delete d->outDevice;
1551         d->outDevice = 0;
1552     }
1553
1554     setActive(false);
1555     return true;
1556 }
1557
1558 QPdfEnginePrivate::~QPdfEnginePrivate()
1559 {
1560     qDeleteAll(fonts);
1561     delete currentPage;
1562     delete stream;
1563 }
1564
1565 QRect QPdfEnginePrivate::paperRect() const
1566 {
1567     int w = qRound(paperSize.width()*resolution/72.);
1568     int h = qRound(paperSize.height()*resolution/72.);
1569
1570     if (!landscape)
1571         return QRect(0, 0, w, h);
1572     else
1573         return QRect(0, 0, h, w);
1574 }
1575
1576 QRect QPdfEnginePrivate::pageRect() const
1577 {
1578     QRect r = paperRect();
1579
1580     if(!fullPage)
1581         r.adjust(qRound(leftMargin*(resolution/72.)),
1582                  qRound(topMargin*(resolution/72.)),
1583                  -qRound(rightMargin*(resolution/72.)),
1584                  -qRound(bottomMargin*(resolution/72.)));
1585
1586     return r;
1587 }
1588
1589
1590 void QPdfEnginePrivate::writeHeader()
1591 {
1592     addXrefEntry(0,false);
1593
1594     xprintf("%%PDF-1.4\n");
1595
1596     writeInfo();
1597
1598     catalog = addXrefEntry(-1);
1599     pageRoot = requestObject();
1600     xprintf("<<\n"
1601             "/Type /Catalog\n"
1602             "/Pages %d 0 R\n"
1603             ">>\n"
1604             "endobj\n", pageRoot);
1605
1606     // graphics state
1607     graphicsState = addXrefEntry(-1);
1608     xprintf("<<\n"
1609             "/Type /ExtGState\n"
1610             "/SA true\n"
1611             "/SM 0.02\n"
1612             "/ca 1.0\n"
1613             "/CA 1.0\n"
1614             "/AIS false\n"
1615             "/SMask /None"
1616             ">>\n"
1617             "endobj\n");
1618
1619     // color space for pattern
1620     patternColorSpace = addXrefEntry(-1);
1621     xprintf("[/Pattern /DeviceRGB]\n"
1622             "endobj\n");
1623 }
1624
1625 void QPdfEnginePrivate::writeInfo()
1626 {
1627     info = addXrefEntry(-1);
1628     xprintf("<<\n/Title ");
1629     printString(title);
1630     xprintf("\n/Creator ");
1631     printString(creator);
1632     xprintf("\n/Producer ");
1633     printString(QString::fromLatin1("Qt " QT_VERSION_STR " (C) 2011 Nokia Corporation and/or its subsidiary(-ies)"));
1634     QDateTime now = QDateTime::currentDateTime().toUTC();
1635     QTime t = now.time();
1636     QDate d = now.date();
1637     xprintf("\n/CreationDate (D:%d%02d%02d%02d%02d%02d)\n",
1638             d.year(),
1639             d.month(),
1640             d.day(),
1641             t.hour(),
1642             t.minute(),
1643             t.second());
1644     xprintf(">>\n"
1645             "endobj\n");
1646 }
1647
1648 void QPdfEnginePrivate::writePageRoot()
1649 {
1650     addXrefEntry(pageRoot);
1651
1652     xprintf("<<\n"
1653             "/Type /Pages\n"
1654             "/Kids \n"
1655             "[\n");
1656     int size = pages.size();
1657     for (int i = 0; i < size; ++i)
1658         xprintf("%d 0 R\n", pages[i]);
1659     xprintf("]\n");
1660
1661     //xprintf("/Group <</S /Transparency /I true /K false>>\n");
1662     xprintf("/Count %d\n", pages.size());
1663
1664     xprintf("/ProcSet [/PDF /Text /ImageB /ImageC]\n"
1665             ">>\n"
1666             "endobj\n");
1667 }
1668
1669
1670 void QPdfEnginePrivate::embedFont(QFontSubset *font)
1671 {
1672     //qDebug() << "embedFont" << font->object_id;
1673     int fontObject = font->object_id;
1674     QByteArray fontData = font->toTruetype();
1675 #ifdef FONT_DUMP
1676     static int i = 0;
1677     QString fileName("font%1.ttf");
1678     fileName = fileName.arg(i++);
1679     QFile ff(fileName);
1680     ff.open(QFile::WriteOnly);
1681     ff.write(fontData);
1682     ff.close();
1683 #endif
1684
1685     int fontDescriptor = requestObject();
1686     int fontstream = requestObject();
1687     int cidfont = requestObject();
1688     int toUnicode = requestObject();
1689
1690     QFontEngine::Properties properties = font->fontEngine->properties();
1691
1692     {
1693         qreal scale = 1000/properties.emSquare.toReal();
1694         addXrefEntry(fontDescriptor);
1695         QByteArray descriptor;
1696         QPdf::ByteStream s(&descriptor);
1697         s << "<< /Type /FontDescriptor\n"
1698             "/FontName /Q";
1699         int tag = fontDescriptor;
1700         for (int i = 0; i < 5; ++i) {
1701             s << (char)('A' + (tag % 26));
1702             tag /= 26;
1703         }
1704         s <<  '+' << properties.postscriptName << "\n"
1705             "/Flags " << 4 << "\n"
1706             "/FontBBox ["
1707           << properties.boundingBox.x()*scale
1708           << -(properties.boundingBox.y() + properties.boundingBox.height())*scale
1709           << (properties.boundingBox.x() + properties.boundingBox.width())*scale
1710           << -properties.boundingBox.y()*scale  << "]\n"
1711             "/ItalicAngle " << properties.italicAngle.toReal() << "\n"
1712             "/Ascent " << properties.ascent.toReal()*scale << "\n"
1713             "/Descent " << -properties.descent.toReal()*scale << "\n"
1714             "/CapHeight " << properties.capHeight.toReal()*scale << "\n"
1715             "/StemV " << properties.lineWidth.toReal()*scale << "\n"
1716             "/FontFile2 " << fontstream << "0 R\n"
1717             ">> endobj\n";
1718         write(descriptor);
1719     }
1720     {
1721         addXrefEntry(fontstream);
1722         QByteArray header;
1723         QPdf::ByteStream s(&header);
1724
1725         int length_object = requestObject();
1726         s << "<<\n"
1727             "/Length1 " << fontData.size() << "\n"
1728             "/Length " << length_object << "0 R\n";
1729         if (do_compress)
1730             s << "/Filter /FlateDecode\n";
1731         s << ">>\n"
1732             "stream\n";
1733         write(header);
1734         int len = writeCompressed(fontData);
1735         write("endstream\n"
1736               "endobj\n");
1737         addXrefEntry(length_object);
1738         xprintf("%d\n"
1739                 "endobj\n", len);
1740     }
1741     {
1742         addXrefEntry(cidfont);
1743         QByteArray cid;
1744         QPdf::ByteStream s(&cid);
1745         s << "<< /Type /Font\n"
1746             "/Subtype /CIDFontType2\n"
1747             "/BaseFont /" << properties.postscriptName << "\n"
1748             "/CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >>\n"
1749             "/FontDescriptor " << fontDescriptor << "0 R\n"
1750             "/CIDToGIDMap /Identity\n"
1751           << font->widthArray() <<
1752             ">>\n"
1753             "endobj\n";
1754         write(cid);
1755     }
1756     {
1757         addXrefEntry(toUnicode);
1758         QByteArray touc = font->createToUnicodeMap();
1759         xprintf("<< /Length %d >>\n"
1760                 "stream\n", touc.length());
1761         write(touc);
1762         write("endstream\n"
1763               "endobj\n");
1764     }
1765     {
1766         addXrefEntry(fontObject);
1767         QByteArray font;
1768         QPdf::ByteStream s(&font);
1769         s << "<< /Type /Font\n"
1770             "/Subtype /Type0\n"
1771             "/BaseFont /" << properties.postscriptName << "\n"
1772             "/Encoding /Identity-H\n"
1773             "/DescendantFonts [" << cidfont << "0 R]\n"
1774             "/ToUnicode " << toUnicode << "0 R"
1775             ">>\n"
1776             "endobj\n";
1777         write(font);
1778     }
1779 }
1780
1781
1782 void QPdfEnginePrivate::writeFonts()
1783 {
1784     for (QHash<QFontEngine::FaceId, QFontSubset *>::iterator it = fonts.begin(); it != fonts.end(); ++it) {
1785         embedFont(*it);
1786         delete *it;
1787     }
1788     fonts.clear();
1789 }
1790
1791 void QPdfEnginePrivate::writePage()
1792 {
1793     if (pages.empty())
1794         return;
1795
1796     *currentPage << "Q Q\n";
1797
1798     uint pageStream = requestObject();
1799     uint pageStreamLength = requestObject();
1800     uint resources = requestObject();
1801     uint annots = requestObject();
1802
1803     addXrefEntry(pages.last());
1804     xprintf("<<\n"
1805             "/Type /Page\n"
1806             "/Parent %d 0 R\n"
1807             "/Contents %d 0 R\n"
1808             "/Resources %d 0 R\n"
1809             "/Annots %d 0 R\n"
1810             "/MediaBox [0 0 %d %d]\n"
1811             ">>\n"
1812             "endobj\n",
1813             pageRoot, pageStream, resources, annots,
1814             // make sure we use the pagesize from when we started the page, since the user may have changed it
1815             currentPage->pageSize.width(), currentPage->pageSize.height());
1816
1817     addXrefEntry(resources);
1818     xprintf("<<\n"
1819             "/ColorSpace <<\n"
1820             "/PCSp %d 0 R\n"
1821             "/CSp /DeviceRGB\n"
1822             "/CSpg /DeviceGray\n"
1823             ">>\n"
1824             "/ExtGState <<\n"
1825             "/GSa %d 0 R\n",
1826             patternColorSpace, graphicsState);
1827
1828     for (int i = 0; i < currentPage->graphicStates.size(); ++i)
1829         xprintf("/GState%d %d 0 R\n", currentPage->graphicStates.at(i), currentPage->graphicStates.at(i));
1830     xprintf(">>\n");
1831
1832     xprintf("/Pattern <<\n");
1833     for (int i = 0; i < currentPage->patterns.size(); ++i)
1834         xprintf("/Pat%d %d 0 R\n", currentPage->patterns.at(i), currentPage->patterns.at(i));
1835     xprintf(">>\n");
1836
1837     xprintf("/Font <<\n");
1838     for (int i = 0; i < currentPage->fonts.size();++i)
1839         xprintf("/F%d %d 0 R\n", currentPage->fonts[i], currentPage->fonts[i]);
1840     xprintf(">>\n");
1841
1842     xprintf("/XObject <<\n");
1843     for (int i = 0; i<currentPage->images.size(); ++i) {
1844         xprintf("/Im%d %d 0 R\n", currentPage->images.at(i), currentPage->images.at(i));
1845     }
1846     xprintf(">>\n");
1847
1848     xprintf(">>\n"
1849             "endobj\n");
1850
1851     addXrefEntry(annots);
1852     xprintf("[ ");
1853     for (int i = 0; i<currentPage->annotations.size(); ++i) {
1854         xprintf("%d 0 R ", currentPage->annotations.at(i));
1855     }
1856     xprintf("]\nendobj\n");
1857
1858     addXrefEntry(pageStream);
1859     xprintf("<<\n"
1860             "/Length %d 0 R\n", pageStreamLength); // object number for stream length object
1861     if (do_compress)
1862         xprintf("/Filter /FlateDecode\n");
1863
1864     xprintf(">>\n");
1865     xprintf("stream\n");
1866     QIODevice *content = currentPage->stream();
1867     int len = writeCompressed(content);
1868     xprintf("endstream\n"
1869             "endobj\n");
1870
1871     addXrefEntry(pageStreamLength);
1872     xprintf("%d\nendobj\n",len);
1873 }
1874
1875 void QPdfEnginePrivate::writeTail()
1876 {
1877     writePage();
1878     writeFonts();
1879     writePageRoot();
1880     addXrefEntry(xrefPositions.size(),false);
1881     xprintf("xref\n"
1882             "0 %d\n"
1883             "%010d 65535 f \n", xrefPositions.size()-1, xrefPositions[0]);
1884
1885     for (int i = 1; i < xrefPositions.size()-1; ++i)
1886         xprintf("%010d 00000 n \n", xrefPositions[i]);
1887
1888     xprintf("trailer\n"
1889             "<<\n"
1890             "/Size %d\n"
1891             "/Info %d 0 R\n"
1892             "/Root %d 0 R\n"
1893             ">>\n"
1894             "startxref\n%d\n"
1895             "%%%%EOF\n",
1896             xrefPositions.size()-1, info, catalog, xrefPositions.last());
1897 }
1898
1899 int QPdfEnginePrivate::addXrefEntry(int object, bool printostr)
1900 {
1901     if (object < 0)
1902         object = requestObject();
1903
1904     if (object>=xrefPositions.size())
1905         xrefPositions.resize(object+1);
1906
1907     xrefPositions[object] = streampos;
1908     if (printostr)
1909         xprintf("%d 0 obj\n",object);
1910
1911     return object;
1912 }
1913
1914 void QPdfEnginePrivate::printString(const QString &string) {
1915     // The 'text string' type in PDF is encoded either as PDFDocEncoding, or
1916     // Unicode UTF-16 with a Unicode byte order mark as the first character
1917     // (0xfeff), with the high-order byte first.
1918     QByteArray array("(\xfe\xff");
1919     const ushort *utf16 = string.utf16();
1920
1921     for (int i=0; i < string.size(); ++i) {
1922         char part[2] = {char((*(utf16 + i)) >> 8), char((*(utf16 + i)) & 0xff)};
1923         for(int j=0; j < 2; ++j) {
1924             if (part[j] == '(' || part[j] == ')' || part[j] == '\\')
1925                 array.append('\\');
1926             array.append(part[j]);
1927         }
1928     }
1929     array.append(")");
1930     write(array);
1931 }
1932
1933
1934 // For strings up to 10000 bytes only !
1935 void QPdfEnginePrivate::xprintf(const char* fmt, ...)
1936 {
1937     if (!stream)
1938         return;
1939
1940     const int msize = 10000;
1941     char buf[msize];
1942
1943     va_list args;
1944     va_start(args, fmt);
1945     int bufsize = qvsnprintf(buf, msize, fmt, args);
1946
1947     Q_ASSERT(bufsize<msize);
1948
1949     va_end(args);
1950
1951     stream->writeRawData(buf, bufsize);
1952     streampos += bufsize;
1953 }
1954
1955 int QPdfEnginePrivate::writeCompressed(QIODevice *dev)
1956 {
1957 #ifndef QT_NO_COMPRESS
1958     if (do_compress) {
1959         int size = QPdfPage::chunkSize();
1960         int sum = 0;
1961         ::z_stream zStruct;
1962         zStruct.zalloc = Z_NULL;
1963         zStruct.zfree = Z_NULL;
1964         zStruct.opaque = Z_NULL;
1965         if (::deflateInit(&zStruct, Z_DEFAULT_COMPRESSION) != Z_OK) {
1966             qWarning("QPdfStream::writeCompressed: Error in deflateInit()");
1967             return sum;
1968         }
1969         zStruct.avail_in = 0;
1970         QByteArray in, out;
1971         out.resize(size);
1972         while (!dev->atEnd() || zStruct.avail_in != 0) {
1973             if (zStruct.avail_in == 0) {
1974                 in = dev->read(size);
1975                 zStruct.avail_in = in.size();
1976                 zStruct.next_in = reinterpret_cast<unsigned char*>(in.data());
1977                 if (in.size() <= 0) {
1978                     qWarning("QPdfStream::writeCompressed: Error in read()");
1979                     ::deflateEnd(&zStruct);
1980                     return sum;
1981                 }
1982             }
1983             zStruct.next_out = reinterpret_cast<unsigned char*>(out.data());
1984             zStruct.avail_out = out.size();
1985             if (::deflate(&zStruct, 0) != Z_OK) {
1986                 qWarning("QPdfStream::writeCompressed: Error in deflate()");
1987                 ::deflateEnd(&zStruct);
1988                 return sum;
1989             }
1990             int written = out.size() - zStruct.avail_out;
1991             stream->writeRawData(out.constData(), written);
1992             streampos += written;
1993             sum += written;
1994         }
1995         int ret;
1996         do {
1997             zStruct.next_out = reinterpret_cast<unsigned char*>(out.data());
1998             zStruct.avail_out = out.size();
1999             ret = ::deflate(&zStruct, Z_FINISH);
2000             if (ret != Z_OK && ret != Z_STREAM_END) {
2001                 qWarning("QPdfStream::writeCompressed: Error in deflate()");
2002                 ::deflateEnd(&zStruct);
2003                 return sum;
2004             }
2005             int written = out.size() - zStruct.avail_out;
2006             stream->writeRawData(out.constData(), written);
2007             streampos += written;
2008             sum += written;
2009         } while (ret == Z_OK);
2010
2011         ::deflateEnd(&zStruct);
2012
2013         return sum;
2014     } else
2015 #endif
2016     {
2017         QByteArray arr;
2018         int sum = 0;
2019         while (!dev->atEnd()) {
2020             arr = dev->read(QPdfPage::chunkSize());
2021             stream->writeRawData(arr.constData(), arr.size());
2022             streampos += arr.size();
2023             sum += arr.size();
2024         }
2025         return sum;
2026     }
2027 }
2028
2029 int QPdfEnginePrivate::writeCompressed(const char *src, int len)
2030 {
2031 #ifndef QT_NO_COMPRESS
2032     if(do_compress) {
2033         uLongf destLen = len + len/100 + 13; // zlib requirement
2034         Bytef* dest = new Bytef[destLen];
2035         if (Z_OK == ::compress(dest, &destLen, (const Bytef*) src, (uLongf)len)) {
2036             stream->writeRawData((const char*)dest, destLen);
2037         } else {
2038             qWarning("QPdfStream::writeCompressed: Error in compress()");
2039             destLen = 0;
2040         }
2041         delete [] dest;
2042         len = destLen;
2043     } else
2044 #endif
2045     {
2046         stream->writeRawData(src,len);
2047     }
2048     streampos += len;
2049     return len;
2050 }
2051
2052 int QPdfEnginePrivate::writeImage(const QByteArray &data, int width, int height, int depth,
2053                                   int maskObject, int softMaskObject, bool dct)
2054 {
2055     int image = addXrefEntry(-1);
2056     xprintf("<<\n"
2057             "/Type /XObject\n"
2058             "/Subtype /Image\n"
2059             "/Width %d\n"
2060             "/Height %d\n", width, height);
2061
2062     if (depth == 1) {
2063         xprintf("/ImageMask true\n"
2064                 "/Decode [1 0]\n");
2065     } else {
2066         xprintf("/BitsPerComponent 8\n"
2067                 "/ColorSpace %s\n", (depth == 32) ? "/DeviceRGB" : "/DeviceGray");
2068     }
2069     if (maskObject > 0)
2070         xprintf("/Mask %d 0 R\n", maskObject);
2071     if (softMaskObject > 0)
2072         xprintf("/SMask %d 0 R\n", softMaskObject);
2073
2074     int lenobj = requestObject();
2075     xprintf("/Length %d 0 R\n", lenobj);
2076     if (interpolateImages)
2077         xprintf("/Interpolate true\n");
2078     int len = 0;
2079     if (dct) {
2080         //qDebug() << "DCT";
2081         xprintf("/Filter /DCTDecode\n>>\nstream\n");
2082         write(data);
2083         len = data.length();
2084     } else {
2085         if (do_compress)
2086             xprintf("/Filter /FlateDecode\n>>\nstream\n");
2087         else
2088             xprintf(">>\nstream\n");
2089         len = writeCompressed(data);
2090     }
2091     xprintf("endstream\n"
2092             "endobj\n");
2093     addXrefEntry(lenobj);
2094     xprintf("%d\n"
2095             "endobj\n", len);
2096     return image;
2097 }
2098
2099 #ifdef USE_NATIVE_GRADIENTS
2100 int QPdfEnginePrivate::gradientBrush(const QBrush &b, const QMatrix &matrix, int *gStateObject)
2101 {
2102     const QGradient *gradient = b.gradient();
2103     if (!gradient)
2104         return 0;
2105
2106     QTransform inv = matrix.inverted();
2107     QPointF page_rect[4] = { inv.map(QPointF(0, 0)),
2108                              inv.map(QPointF(width_, 0)),
2109                              inv.map(QPointF(0, height_)),
2110                              inv.map(QPointF(width_, height_)) };
2111
2112     bool opaque = b.isOpaque();
2113
2114     QByteArray shader;
2115     QByteArray alphaShader;
2116     if (gradient->type() == QGradient::LinearGradient) {
2117         const QLinearGradient *lg = static_cast<const QLinearGradient *>(gradient);
2118         shader = QPdf::generateLinearGradientShader(lg, page_rect);
2119         if (!opaque)
2120             alphaShader = QPdf::generateLinearGradientShader(lg, page_rect, true);
2121     } else {
2122         // #############
2123         return 0;
2124     }
2125     int shaderObject = addXrefEntry(-1);
2126     write(shader);
2127
2128     QByteArray str;
2129     QPdf::ByteStream s(&str);
2130     s << "<<\n"
2131         "/Type /Pattern\n"
2132         "/PatternType 2\n"
2133         "/Shading " << shaderObject << "0 R\n"
2134         "/Matrix ["
2135       << matrix.m11()
2136       << matrix.m12()
2137       << matrix.m21()
2138       << matrix.m22()
2139       << matrix.dx()
2140       << matrix.dy() << "]\n";
2141     s << ">>\n"
2142         "endobj\n";
2143
2144     int patternObj = addXrefEntry(-1);
2145     write(str);
2146     currentPage->patterns.append(patternObj);
2147
2148     if (!opaque) {
2149         bool ca = true;
2150         QGradientStops stops = gradient->stops();
2151         int a = stops.at(0).second.alpha();
2152         for (int i = 1; i < stops.size(); ++i) {
2153             if (stops.at(i).second.alpha() != a) {
2154                 ca = false;
2155                 break;
2156             }
2157         }
2158         if (ca) {
2159             *gStateObject = addConstantAlphaObject(stops.at(0).second.alpha());
2160         } else {
2161             int alphaShaderObject = addXrefEntry(-1);
2162             write(alphaShader);
2163
2164             QByteArray content;
2165             QPdf::ByteStream c(&content);
2166             c << "/Shader" << alphaShaderObject << "sh\n";
2167
2168             QByteArray form;
2169             QPdf::ByteStream f(&form);
2170             f << "<<\n"
2171                 "/Type /XObject\n"
2172                 "/Subtype /Form\n"
2173                 "/BBox [0 0 " << width_ << height_ << "]\n"
2174                 "/Group <</S /Transparency >>\n"
2175                 "/Resources <<\n"
2176                 "/Shading << /Shader" << alphaShaderObject << alphaShaderObject << "0 R >>\n"
2177                 ">>\n";
2178
2179             f << "/Length " << content.length() << "\n"
2180                 ">>\n"
2181                 "stream\n"
2182               << content
2183               << "endstream\n"
2184                 "endobj\n";
2185
2186             int softMaskFormObject = addXrefEntry(-1);
2187             write(form);
2188             *gStateObject = addXrefEntry(-1);
2189             xprintf("<< /SMask << /S /Alpha /G %d 0 R >> >>\n"
2190                     "endobj\n", softMaskFormObject);
2191             currentPage->graphicStates.append(*gStateObject);
2192         }
2193     }
2194
2195     return patternObj;
2196 }
2197 #endif
2198
2199 int QPdfEnginePrivate::addConstantAlphaObject(int brushAlpha, int penAlpha)
2200 {
2201     if (brushAlpha == 255 && penAlpha == 255)
2202         return 0;
2203     int object = alphaCache.value(QPair<uint, uint>(brushAlpha, penAlpha), 0);
2204     if (!object) {
2205         object = addXrefEntry(-1);
2206         QByteArray alphaDef;
2207         QPdf::ByteStream s(&alphaDef);
2208         s << "<<\n/ca " << (brushAlpha/qreal(255.)) << '\n';
2209         s << "/CA " << (penAlpha/qreal(255.)) << "\n>>";
2210         xprintf("%s\nendobj\n", alphaDef.constData());
2211         alphaCache.insert(QPair<uint, uint>(brushAlpha, penAlpha), object);
2212     }
2213     if (currentPage->graphicStates.indexOf(object) < 0)
2214         currentPage->graphicStates.append(object);
2215
2216     return object;
2217 }
2218
2219 int QPdfEnginePrivate::addBrushPattern(const QTransform &m, bool *specifyColor, int *gStateObject)
2220 {
2221     int paintType = 2; // Uncolored tiling
2222     int w = 8;
2223     int h = 8;
2224
2225     *specifyColor = true;
2226     *gStateObject = 0;
2227
2228     QTransform matrix = m;
2229     matrix.translate(brushOrigin.x(), brushOrigin.y());
2230     matrix = matrix * pageMatrix();
2231     //qDebug() << brushOrigin << matrix;
2232
2233     Qt::BrushStyle style = brush.style();
2234     if (style == Qt::LinearGradientPattern) {// && style <= Qt::ConicalGradientPattern) {
2235 #ifdef USE_NATIVE_GRADIENTS
2236         *specifyColor = false;
2237         return gradientBrush(b, matrix, gStateObject);
2238 #else
2239         return 0;
2240 #endif
2241     }
2242
2243     if ((!brush.isOpaque() && brush.style() < Qt::LinearGradientPattern) || opacity != 1.0)
2244         *gStateObject = addConstantAlphaObject(qRound(brush.color().alpha() * opacity),
2245                                                qRound(pen.color().alpha() * opacity));
2246
2247     int imageObject = -1;
2248     QByteArray pattern = QPdf::patternForBrush(brush);
2249     if (pattern.isEmpty()) {
2250         if (brush.style() != Qt::TexturePattern)
2251             return 0;
2252         QImage image = brush.texture().toImage();
2253         bool bitmap = true;
2254         imageObject = addImage(image, &bitmap, brush.texture().cacheKey());
2255         if (imageObject != -1) {
2256             QImage::Format f = image.format();
2257             if (f != QImage::Format_MonoLSB && f != QImage::Format_Mono) {
2258                 paintType = 1; // Colored tiling
2259                 *specifyColor = false;
2260             }
2261             w = image.width();
2262             h = image.height();
2263             QTransform m(w, 0, 0, -h, 0, h);
2264             QPdf::ByteStream s(&pattern);
2265             s << QPdf::generateMatrix(m);
2266             s << "/Im" << imageObject << " Do\n";
2267         }
2268     }
2269
2270     QByteArray str;
2271     QPdf::ByteStream s(&str);
2272     s << "<<\n"
2273         "/Type /Pattern\n"
2274         "/PatternType 1\n"
2275         "/PaintType " << paintType << "\n"
2276         "/TilingType 1\n"
2277         "/BBox [0 0 " << w << h << "]\n"
2278         "/XStep " << w << "\n"
2279         "/YStep " << h << "\n"
2280         "/Matrix ["
2281       << matrix.m11()
2282       << matrix.m12()
2283       << matrix.m21()
2284       << matrix.m22()
2285       << matrix.dx()
2286       << matrix.dy() << "]\n"
2287         "/Resources \n<< "; // open resource tree
2288     if (imageObject > 0) {
2289         s << "/XObject << /Im" << imageObject << ' ' << imageObject << "0 R >> ";
2290     }
2291     s << ">>\n"
2292         "/Length " << pattern.length() << "\n"
2293         ">>\n"
2294         "stream\n"
2295       << pattern
2296       << "endstream\n"
2297         "endobj\n";
2298
2299     int patternObj = addXrefEntry(-1);
2300     write(str);
2301     currentPage->patterns.append(patternObj);
2302     return patternObj;
2303 }
2304
2305 /*!
2306  * Adds an image to the pdf and return the pdf-object id. Returns -1 if adding the image failed.
2307  */
2308 int QPdfEnginePrivate::addImage(const QImage &img, bool *bitmap, qint64 serial_no)
2309 {
2310     if (img.isNull())
2311         return -1;
2312
2313     int object = imageCache.value(serial_no);
2314     if(object)
2315         return object;
2316
2317     QImage image = img;
2318     QImage::Format format = image.format();
2319     if (image.depth() == 1 && *bitmap && img.colorTable().size() == 2
2320         && img.colorTable().at(0) == QColor(Qt::black).rgba()
2321         && img.colorTable().at(1) == QColor(Qt::white).rgba())
2322     {
2323         if (format == QImage::Format_MonoLSB)
2324             image = image.convertToFormat(QImage::Format_Mono);
2325         format = QImage::Format_Mono;
2326     } else {
2327         *bitmap = false;
2328         if (format != QImage::Format_RGB32 && format != QImage::Format_ARGB32) {
2329             image = image.convertToFormat(QImage::Format_ARGB32);
2330             format = QImage::Format_ARGB32;
2331         }
2332     }
2333
2334     int w = image.width();
2335     int h = image.height();
2336     int d = image.depth();
2337
2338     if (format == QImage::Format_Mono) {
2339         int bytesPerLine = (w + 7) >> 3;
2340         QByteArray data;
2341         data.resize(bytesPerLine * h);
2342         char *rawdata = data.data();
2343         for (int y = 0; y < h; ++y) {
2344             memcpy(rawdata, image.scanLine(y), bytesPerLine);
2345             rawdata += bytesPerLine;
2346         }
2347         object = writeImage(data, w, h, d, 0, 0);
2348     } else {
2349         QByteArray softMaskData;
2350         bool dct = false;
2351         QByteArray imageData;
2352         bool hasAlpha = false;
2353         bool hasMask = false;
2354
2355         if (QImageWriter::supportedImageFormats().contains("jpeg") && !grayscale) {
2356             QBuffer buffer(&imageData);
2357             QImageWriter writer(&buffer, "jpeg");
2358             writer.setQuality(94);
2359             writer.write(image);
2360             dct = true;
2361
2362             if (format != QImage::Format_RGB32) {
2363                 softMaskData.resize(w * h);
2364                 uchar *sdata = (uchar *)softMaskData.data();
2365                 for (int y = 0; y < h; ++y) {
2366                     const QRgb *rgb = (const QRgb *)image.scanLine(y);
2367                     for (int x = 0; x < w; ++x) {
2368                         uchar alpha = qAlpha(*rgb);
2369                         *sdata++ = alpha;
2370                         hasMask |= (alpha < 255);
2371                         hasAlpha |= (alpha != 0 && alpha != 255);
2372                         ++rgb;
2373                     }
2374                 }
2375             }
2376         } else {
2377             imageData.resize(grayscale ? w * h : 3 * w * h);
2378             uchar *data = (uchar *)imageData.data();
2379             softMaskData.resize(w * h);
2380             uchar *sdata = (uchar *)softMaskData.data();
2381             for (int y = 0; y < h; ++y) {
2382                 const QRgb *rgb = (const QRgb *)image.scanLine(y);
2383                 if (grayscale) {
2384                     for (int x = 0; x < w; ++x) {
2385                         *(data++) = qGray(*rgb);
2386                         uchar alpha = qAlpha(*rgb);
2387                         *sdata++ = alpha;
2388                         hasMask |= (alpha < 255);
2389                         hasAlpha |= (alpha != 0 && alpha != 255);
2390                         ++rgb;
2391                     }
2392                 } else {
2393                     for (int x = 0; x < w; ++x) {
2394                         *(data++) = qRed(*rgb);
2395                         *(data++) = qGreen(*rgb);
2396                         *(data++) = qBlue(*rgb);
2397                         uchar alpha = qAlpha(*rgb);
2398                         *sdata++ = alpha;
2399                         hasMask |= (alpha < 255);
2400                         hasAlpha |= (alpha != 0 && alpha != 255);
2401                         ++rgb;
2402                     }
2403                 }
2404             }
2405             if (format == QImage::Format_RGB32)
2406                 hasAlpha = hasMask = false;
2407         }
2408         int maskObject = 0;
2409         int softMaskObject = 0;
2410         if (hasAlpha) {
2411             softMaskObject = writeImage(softMaskData, w, h, 8, 0, 0);
2412         } else if (hasMask) {
2413             // dither the soft mask to 1bit and add it. This also helps PDF viewers
2414             // without transparency support
2415             int bytesPerLine = (w + 7) >> 3;
2416             QByteArray mask(bytesPerLine * h, 0);
2417             uchar *mdata = (uchar *)mask.data();
2418             const uchar *sdata = (const uchar *)softMaskData.constData();
2419             for (int y = 0; y < h; ++y) {
2420                 for (int x = 0; x < w; ++x) {
2421                     if (*sdata)
2422                         mdata[x>>3] |= (0x80 >> (x&7));
2423                     ++sdata;
2424                 }
2425                 mdata += bytesPerLine;
2426             }
2427             maskObject = writeImage(mask, w, h, 1, 0, 0);
2428         }
2429         object = writeImage(imageData, w, h, grayscale ? 8 : 32,
2430                             maskObject, softMaskObject, dct);
2431     }
2432     imageCache.insert(serial_no, object);
2433     return object;
2434 }
2435
2436 void QPdfEnginePrivate::drawTextItem(const QPointF &p, const QTextItemInt &ti)
2437 {
2438     Q_Q(QPdfEngine);
2439
2440     if (ti.charFormat.isAnchor()) {
2441         qreal size = ti.fontEngine->fontDef.pixelSize;
2442         int synthesized = ti.fontEngine->synthesized();
2443         qreal stretch = synthesized & QFontEngine::SynthesizedStretch ? ti.fontEngine->fontDef.stretch/100. : 1.;
2444
2445         QTransform trans;
2446         // Build text rendering matrix (Trm). We need it to map the text area to user
2447         // space units on the PDF page.
2448         trans = QTransform(size*stretch, 0, 0, size, 0, 0);
2449         // Apply text matrix (Tm).
2450         trans *= QTransform(1,0,0,-1,p.x(),p.y());
2451         // Apply page displacement (Identity for first page).
2452         trans *= stroker.matrix;
2453         // Apply Current Transformation Matrix (CTM)
2454         trans *= pageMatrix();
2455         qreal x1, y1, x2, y2;
2456         trans.map(0, 0, &x1, &y1);
2457         trans.map(ti.width.toReal()/size, (ti.ascent.toReal()-ti.descent.toReal())/size, &x2, &y2);
2458
2459         uint annot = addXrefEntry(-1);
2460 #ifdef Q_DEBUG_PDF_LINKS
2461         xprintf("<<\n/Type /Annot\n/Subtype /Link\n/Rect [%f %f %f %f]\n/Border [16 16 1]\n/A <<\n",
2462 #else
2463         xprintf("<<\n/Type /Annot\n/Subtype /Link\n/Rect [%f %f %f %f]\n/Border [0 0 0]\n/A <<\n",
2464 #endif
2465                 static_cast<double>(x1),
2466                 static_cast<double>(y1),
2467                 static_cast<double>(x2),
2468                 static_cast<double>(y2));
2469         xprintf("/Type /Action\n/S /URI\n/URI (%s)\n",
2470                 ti.charFormat.anchorHref().toLatin1().constData());
2471         xprintf(">>\n>>\n");
2472         xprintf("endobj\n");
2473
2474         if (!currentPage->annotations.contains(annot)) {
2475             currentPage->annotations.append(annot);
2476         }
2477     }
2478
2479     QFontEngine *fe = ti.fontEngine;
2480
2481     QFontEngine::FaceId face_id = fe->faceId();
2482     bool noEmbed = false;
2483     if (face_id.filename.isEmpty()
2484         || (!postscript && ((fe->fsType & 0x200) /* bitmap embedding only */
2485                             || (fe->fsType == 2) /* no embedding allowed */))) {
2486         *currentPage << "Q\n";
2487         q->QPaintEngine::drawTextItem(p, ti);
2488         *currentPage << "q\n";
2489         if (face_id.filename.isEmpty())
2490             return;
2491         noEmbed = true;
2492     }
2493
2494     QFontSubset *font = fonts.value(face_id, 0);
2495     if (!font) {
2496         font = new QFontSubset(fe, requestObject());
2497         font->noEmbed = noEmbed;
2498     }
2499     fonts.insert(face_id, font);
2500
2501     if (!currentPage->fonts.contains(font->object_id))
2502         currentPage->fonts.append(font->object_id);
2503
2504     qreal size = ti.fontEngine->fontDef.pixelSize;
2505
2506     QVarLengthArray<glyph_t> glyphs;
2507     QVarLengthArray<QFixedPoint> positions;
2508     QTransform m = QTransform::fromTranslate(p.x(), p.y());
2509     ti.fontEngine->getGlyphPositions(ti.glyphs, m, ti.flags,
2510                                      glyphs, positions);
2511     if (glyphs.size() == 0)
2512         return;
2513     int synthesized = ti.fontEngine->synthesized();
2514     qreal stretch = synthesized & QFontEngine::SynthesizedStretch ? ti.fontEngine->fontDef.stretch/100. : 1.;
2515
2516     *currentPage << "BT\n"
2517                  << "/F" << font->object_id << size << "Tf "
2518                  << stretch << (synthesized & QFontEngine::SynthesizedItalic
2519                                 ? "0 .3 -1 0 0 Tm\n"
2520                                 : "0 0 -1 0 0 Tm\n");
2521
2522
2523 #if 0
2524     // #### implement actual text for complex languages
2525     const unsigned short *logClusters = ti.logClusters;
2526     int pos = 0;
2527     do {
2528         int end = pos + 1;
2529         while (end < ti.num_chars && logClusters[end] == logClusters[pos])
2530             ++end;
2531         *currentPage << "/Span << /ActualText <FEFF";
2532         for (int i = pos; i < end; ++i) {
2533             s << toHex((ushort)ti.chars[i].unicode(), buf);
2534         }
2535         *currentPage << "> >>\n"
2536             "BDC\n"
2537             "<";
2538         int ge = end == ti.num_chars ? ti.num_glyphs : logClusters[end];
2539         for (int gs = logClusters[pos]; gs < ge; ++gs)
2540             *currentPage << toHex((ushort)ti.glyphs[gs].glyph, buf);
2541         *currentPage << "> Tj\n"
2542             "EMC\n";
2543         pos = end;
2544     } while (pos < ti.num_chars);
2545 #else
2546     qreal last_x = 0.;
2547     qreal last_y = 0.;
2548     for (int i = 0; i < glyphs.size(); ++i) {
2549         qreal x = positions[i].x.toReal();
2550         qreal y = positions[i].y.toReal();
2551         if (synthesized & QFontEngine::SynthesizedItalic)
2552             x += .3*y;
2553         x /= stretch;
2554         char buf[5];
2555         int g = font->addGlyph(glyphs[i]);
2556         *currentPage << x - last_x << last_y - y << "Td <"
2557                      << QPdf::toHex((ushort)g, buf) << "> Tj\n";
2558         last_x = x;
2559         last_y = y;
2560     }
2561     if (synthesized & QFontEngine::SynthesizedBold) {
2562         *currentPage << stretch << (synthesized & QFontEngine::SynthesizedItalic
2563                             ? "0 .3 -1 0 0 Tm\n"
2564                             : "0 0 -1 0 0 Tm\n");
2565         *currentPage << "/Span << /ActualText <> >> BDC\n";
2566         last_x = 0.5*fe->lineThickness().toReal();
2567         last_y = 0.;
2568         for (int i = 0; i < glyphs.size(); ++i) {
2569             qreal x = positions[i].x.toReal();
2570             qreal y = positions[i].y.toReal();
2571             if (synthesized & QFontEngine::SynthesizedItalic)
2572                 x += .3*y;
2573             x /= stretch;
2574             char buf[5];
2575             int g = font->addGlyph(glyphs[i]);
2576             *currentPage << x - last_x << last_y - y << "Td <"
2577                         << QPdf::toHex((ushort)g, buf) << "> Tj\n";
2578             last_x = x;
2579             last_y = y;
2580         }
2581         *currentPage << "EMC\n";
2582     }
2583 #endif
2584
2585     *currentPage << "ET\n";
2586 }
2587
2588 QTransform QPdfEnginePrivate::pageMatrix() const
2589 {
2590     qreal scale = 72./resolution;
2591     QTransform tmp(scale, 0.0, 0.0, -scale, 0.0, height());
2592     if (!fullPage) {
2593         QRect r = pageRect();
2594         tmp.translate(r.left(), r.top());
2595     }
2596     return tmp;
2597 }
2598
2599 void QPdfEnginePrivate::newPage()
2600 {
2601     if (currentPage && currentPage->pageSize.isEmpty())
2602         currentPage->pageSize = QSize(width(), height());
2603     writePage();
2604
2605     delete currentPage;
2606     currentPage = new QPdfPage;
2607     currentPage->pageSize = QSize(width(), height());
2608     stroker.stream = currentPage;
2609     pages.append(requestObject());
2610
2611     *currentPage << "/GSa gs /CSp cs /CSp CS\n"
2612                  << QPdf::generateMatrix(pageMatrix())
2613                  << "q q\n";
2614 }
2615
2616
2617 QT_END_NAMESPACE