d2aa938aed8dd7d2f311479fe5b6aa02e322cabf
[profile/ivi/qtbase.git] / src / gui / painting / qpdf.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 #include "qplatformdefs.h"
42 #include <qdebug.h>
43 #include "qpdf_p.h"
44 #include <qfile.h>
45 #include <qtemporaryfile.h>
46 #include <private/qmath_p.h>
47 #include <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     d->simplePen = false;
1525
1526     d->pages.clear();
1527     d->imageCache.clear();
1528     d->alphaCache.clear();
1529
1530     setActive(true);
1531     d->writeHeader();
1532     newPage();
1533
1534     return true;
1535 }
1536
1537 bool QPdfEngine::end()
1538 {
1539     Q_D(QPdfEngine);
1540     d->writeTail();
1541
1542     d->stream->unsetDevice();
1543
1544     qDeleteAll(d->fonts);
1545     d->fonts.clear();
1546     delete d->currentPage;
1547     d->currentPage = 0;
1548
1549     if (d->outDevice && d->ownsDevice) {
1550         d->outDevice->close();
1551         delete d->outDevice;
1552         d->outDevice = 0;
1553     }
1554
1555     setActive(false);
1556     return true;
1557 }
1558
1559 QPdfEnginePrivate::~QPdfEnginePrivate()
1560 {
1561     qDeleteAll(fonts);
1562     delete currentPage;
1563     delete stream;
1564 }
1565
1566 QRect QPdfEnginePrivate::paperRect() const
1567 {
1568     int w = qRound(paperSize.width()*resolution/72.);
1569     int h = qRound(paperSize.height()*resolution/72.);
1570
1571     if (!landscape)
1572         return QRect(0, 0, w, h);
1573     else
1574         return QRect(0, 0, h, w);
1575 }
1576
1577 QRect QPdfEnginePrivate::pageRect() const
1578 {
1579     QRect r = paperRect();
1580
1581     if(!fullPage)
1582         r.adjust(qRound(leftMargin*(resolution/72.)),
1583                  qRound(topMargin*(resolution/72.)),
1584                  -qRound(rightMargin*(resolution/72.)),
1585                  -qRound(bottomMargin*(resolution/72.)));
1586
1587     return r;
1588 }
1589
1590
1591 void QPdfEnginePrivate::writeHeader()
1592 {
1593     addXrefEntry(0,false);
1594
1595     xprintf("%%PDF-1.4\n");
1596
1597     writeInfo();
1598
1599     catalog = addXrefEntry(-1);
1600     pageRoot = requestObject();
1601     xprintf("<<\n"
1602             "/Type /Catalog\n"
1603             "/Pages %d 0 R\n"
1604             ">>\n"
1605             "endobj\n", pageRoot);
1606
1607     // graphics state
1608     graphicsState = addXrefEntry(-1);
1609     xprintf("<<\n"
1610             "/Type /ExtGState\n"
1611             "/SA true\n"
1612             "/SM 0.02\n"
1613             "/ca 1.0\n"
1614             "/CA 1.0\n"
1615             "/AIS false\n"
1616             "/SMask /None"
1617             ">>\n"
1618             "endobj\n");
1619
1620     // color space for pattern
1621     patternColorSpace = addXrefEntry(-1);
1622     xprintf("[/Pattern /DeviceRGB]\n"
1623             "endobj\n");
1624 }
1625
1626 void QPdfEnginePrivate::writeInfo()
1627 {
1628     info = addXrefEntry(-1);
1629     xprintf("<<\n/Title ");
1630     printString(title);
1631     xprintf("\n/Creator ");
1632     printString(creator);
1633     xprintf("\n/Producer ");
1634     printString(QString::fromLatin1("Qt " QT_VERSION_STR " (C) 2011 Nokia Corporation and/or its subsidiary(-ies)"));
1635     QDateTime now = QDateTime::currentDateTime().toUTC();
1636     QTime t = now.time();
1637     QDate d = now.date();
1638     xprintf("\n/CreationDate (D:%d%02d%02d%02d%02d%02d)\n",
1639             d.year(),
1640             d.month(),
1641             d.day(),
1642             t.hour(),
1643             t.minute(),
1644             t.second());
1645     xprintf(">>\n"
1646             "endobj\n");
1647 }
1648
1649 void QPdfEnginePrivate::writePageRoot()
1650 {
1651     addXrefEntry(pageRoot);
1652
1653     xprintf("<<\n"
1654             "/Type /Pages\n"
1655             "/Kids \n"
1656             "[\n");
1657     int size = pages.size();
1658     for (int i = 0; i < size; ++i)
1659         xprintf("%d 0 R\n", pages[i]);
1660     xprintf("]\n");
1661
1662     //xprintf("/Group <</S /Transparency /I true /K false>>\n");
1663     xprintf("/Count %d\n", pages.size());
1664
1665     xprintf("/ProcSet [/PDF /Text /ImageB /ImageC]\n"
1666             ">>\n"
1667             "endobj\n");
1668 }
1669
1670
1671 void QPdfEnginePrivate::embedFont(QFontSubset *font)
1672 {
1673     //qDebug() << "embedFont" << font->object_id;
1674     int fontObject = font->object_id;
1675     QByteArray fontData = font->toTruetype();
1676 #ifdef FONT_DUMP
1677     static int i = 0;
1678     QString fileName("font%1.ttf");
1679     fileName = fileName.arg(i++);
1680     QFile ff(fileName);
1681     ff.open(QFile::WriteOnly);
1682     ff.write(fontData);
1683     ff.close();
1684 #endif
1685
1686     int fontDescriptor = requestObject();
1687     int fontstream = requestObject();
1688     int cidfont = requestObject();
1689     int toUnicode = requestObject();
1690
1691     QFontEngine::Properties properties = font->fontEngine->properties();
1692
1693     {
1694         qreal scale = 1000/properties.emSquare.toReal();
1695         addXrefEntry(fontDescriptor);
1696         QByteArray descriptor;
1697         QPdf::ByteStream s(&descriptor);
1698         s << "<< /Type /FontDescriptor\n"
1699             "/FontName /Q";
1700         int tag = fontDescriptor;
1701         for (int i = 0; i < 5; ++i) {
1702             s << (char)('A' + (tag % 26));
1703             tag /= 26;
1704         }
1705         s <<  '+' << properties.postscriptName << "\n"
1706             "/Flags " << 4 << "\n"
1707             "/FontBBox ["
1708           << properties.boundingBox.x()*scale
1709           << -(properties.boundingBox.y() + properties.boundingBox.height())*scale
1710           << (properties.boundingBox.x() + properties.boundingBox.width())*scale
1711           << -properties.boundingBox.y()*scale  << "]\n"
1712             "/ItalicAngle " << properties.italicAngle.toReal() << "\n"
1713             "/Ascent " << properties.ascent.toReal()*scale << "\n"
1714             "/Descent " << -properties.descent.toReal()*scale << "\n"
1715             "/CapHeight " << properties.capHeight.toReal()*scale << "\n"
1716             "/StemV " << properties.lineWidth.toReal()*scale << "\n"
1717             "/FontFile2 " << fontstream << "0 R\n"
1718             ">> endobj\n";
1719         write(descriptor);
1720     }
1721     {
1722         addXrefEntry(fontstream);
1723         QByteArray header;
1724         QPdf::ByteStream s(&header);
1725
1726         int length_object = requestObject();
1727         s << "<<\n"
1728             "/Length1 " << fontData.size() << "\n"
1729             "/Length " << length_object << "0 R\n";
1730         if (do_compress)
1731             s << "/Filter /FlateDecode\n";
1732         s << ">>\n"
1733             "stream\n";
1734         write(header);
1735         int len = writeCompressed(fontData);
1736         write("endstream\n"
1737               "endobj\n");
1738         addXrefEntry(length_object);
1739         xprintf("%d\n"
1740                 "endobj\n", len);
1741     }
1742     {
1743         addXrefEntry(cidfont);
1744         QByteArray cid;
1745         QPdf::ByteStream s(&cid);
1746         s << "<< /Type /Font\n"
1747             "/Subtype /CIDFontType2\n"
1748             "/BaseFont /" << properties.postscriptName << "\n"
1749             "/CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >>\n"
1750             "/FontDescriptor " << fontDescriptor << "0 R\n"
1751             "/CIDToGIDMap /Identity\n"
1752           << font->widthArray() <<
1753             ">>\n"
1754             "endobj\n";
1755         write(cid);
1756     }
1757     {
1758         addXrefEntry(toUnicode);
1759         QByteArray touc = font->createToUnicodeMap();
1760         xprintf("<< /Length %d >>\n"
1761                 "stream\n", touc.length());
1762         write(touc);
1763         write("endstream\n"
1764               "endobj\n");
1765     }
1766     {
1767         addXrefEntry(fontObject);
1768         QByteArray font;
1769         QPdf::ByteStream s(&font);
1770         s << "<< /Type /Font\n"
1771             "/Subtype /Type0\n"
1772             "/BaseFont /" << properties.postscriptName << "\n"
1773             "/Encoding /Identity-H\n"
1774             "/DescendantFonts [" << cidfont << "0 R]\n"
1775             "/ToUnicode " << toUnicode << "0 R"
1776             ">>\n"
1777             "endobj\n";
1778         write(font);
1779     }
1780 }
1781
1782
1783 void QPdfEnginePrivate::writeFonts()
1784 {
1785     for (QHash<QFontEngine::FaceId, QFontSubset *>::iterator it = fonts.begin(); it != fonts.end(); ++it) {
1786         embedFont(*it);
1787         delete *it;
1788     }
1789     fonts.clear();
1790 }
1791
1792 void QPdfEnginePrivate::writePage()
1793 {
1794     if (pages.empty())
1795         return;
1796
1797     *currentPage << "Q Q\n";
1798
1799     uint pageStream = requestObject();
1800     uint pageStreamLength = requestObject();
1801     uint resources = requestObject();
1802     uint annots = requestObject();
1803
1804     addXrefEntry(pages.last());
1805     xprintf("<<\n"
1806             "/Type /Page\n"
1807             "/Parent %d 0 R\n"
1808             "/Contents %d 0 R\n"
1809             "/Resources %d 0 R\n"
1810             "/Annots %d 0 R\n"
1811             "/MediaBox [0 0 %d %d]\n"
1812             ">>\n"
1813             "endobj\n",
1814             pageRoot, pageStream, resources, annots,
1815             // make sure we use the pagesize from when we started the page, since the user may have changed it
1816             currentPage->pageSize.width(), currentPage->pageSize.height());
1817
1818     addXrefEntry(resources);
1819     xprintf("<<\n"
1820             "/ColorSpace <<\n"
1821             "/PCSp %d 0 R\n"
1822             "/CSp /DeviceRGB\n"
1823             "/CSpg /DeviceGray\n"
1824             ">>\n"
1825             "/ExtGState <<\n"
1826             "/GSa %d 0 R\n",
1827             patternColorSpace, graphicsState);
1828
1829     for (int i = 0; i < currentPage->graphicStates.size(); ++i)
1830         xprintf("/GState%d %d 0 R\n", currentPage->graphicStates.at(i), currentPage->graphicStates.at(i));
1831     xprintf(">>\n");
1832
1833     xprintf("/Pattern <<\n");
1834     for (int i = 0; i < currentPage->patterns.size(); ++i)
1835         xprintf("/Pat%d %d 0 R\n", currentPage->patterns.at(i), currentPage->patterns.at(i));
1836     xprintf(">>\n");
1837
1838     xprintf("/Font <<\n");
1839     for (int i = 0; i < currentPage->fonts.size();++i)
1840         xprintf("/F%d %d 0 R\n", currentPage->fonts[i], currentPage->fonts[i]);
1841     xprintf(">>\n");
1842
1843     xprintf("/XObject <<\n");
1844     for (int i = 0; i<currentPage->images.size(); ++i) {
1845         xprintf("/Im%d %d 0 R\n", currentPage->images.at(i), currentPage->images.at(i));
1846     }
1847     xprintf(">>\n");
1848
1849     xprintf(">>\n"
1850             "endobj\n");
1851
1852     addXrefEntry(annots);
1853     xprintf("[ ");
1854     for (int i = 0; i<currentPage->annotations.size(); ++i) {
1855         xprintf("%d 0 R ", currentPage->annotations.at(i));
1856     }
1857     xprintf("]\nendobj\n");
1858
1859     addXrefEntry(pageStream);
1860     xprintf("<<\n"
1861             "/Length %d 0 R\n", pageStreamLength); // object number for stream length object
1862     if (do_compress)
1863         xprintf("/Filter /FlateDecode\n");
1864
1865     xprintf(">>\n");
1866     xprintf("stream\n");
1867     QIODevice *content = currentPage->stream();
1868     int len = writeCompressed(content);
1869     xprintf("endstream\n"
1870             "endobj\n");
1871
1872     addXrefEntry(pageStreamLength);
1873     xprintf("%d\nendobj\n",len);
1874 }
1875
1876 void QPdfEnginePrivate::writeTail()
1877 {
1878     writePage();
1879     writeFonts();
1880     writePageRoot();
1881     addXrefEntry(xrefPositions.size(),false);
1882     xprintf("xref\n"
1883             "0 %d\n"
1884             "%010d 65535 f \n", xrefPositions.size()-1, xrefPositions[0]);
1885
1886     for (int i = 1; i < xrefPositions.size()-1; ++i)
1887         xprintf("%010d 00000 n \n", xrefPositions[i]);
1888
1889     xprintf("trailer\n"
1890             "<<\n"
1891             "/Size %d\n"
1892             "/Info %d 0 R\n"
1893             "/Root %d 0 R\n"
1894             ">>\n"
1895             "startxref\n%d\n"
1896             "%%%%EOF\n",
1897             xrefPositions.size()-1, info, catalog, xrefPositions.last());
1898 }
1899
1900 int QPdfEnginePrivate::addXrefEntry(int object, bool printostr)
1901 {
1902     if (object < 0)
1903         object = requestObject();
1904
1905     if (object>=xrefPositions.size())
1906         xrefPositions.resize(object+1);
1907
1908     xrefPositions[object] = streampos;
1909     if (printostr)
1910         xprintf("%d 0 obj\n",object);
1911
1912     return object;
1913 }
1914
1915 void QPdfEnginePrivate::printString(const QString &string) {
1916     // The 'text string' type in PDF is encoded either as PDFDocEncoding, or
1917     // Unicode UTF-16 with a Unicode byte order mark as the first character
1918     // (0xfeff), with the high-order byte first.
1919     QByteArray array("(\xfe\xff");
1920     const ushort *utf16 = string.utf16();
1921
1922     for (int i=0; i < string.size(); ++i) {
1923         char part[2] = {char((*(utf16 + i)) >> 8), char((*(utf16 + i)) & 0xff)};
1924         for(int j=0; j < 2; ++j) {
1925             if (part[j] == '(' || part[j] == ')' || part[j] == '\\')
1926                 array.append('\\');
1927             array.append(part[j]);
1928         }
1929     }
1930     array.append(")");
1931     write(array);
1932 }
1933
1934
1935 // For strings up to 10000 bytes only !
1936 void QPdfEnginePrivate::xprintf(const char* fmt, ...)
1937 {
1938     if (!stream)
1939         return;
1940
1941     const int msize = 10000;
1942     char buf[msize];
1943
1944     va_list args;
1945     va_start(args, fmt);
1946     int bufsize = qvsnprintf(buf, msize, fmt, args);
1947
1948     Q_ASSERT(bufsize<msize);
1949
1950     va_end(args);
1951
1952     stream->writeRawData(buf, bufsize);
1953     streampos += bufsize;
1954 }
1955
1956 int QPdfEnginePrivate::writeCompressed(QIODevice *dev)
1957 {
1958 #ifndef QT_NO_COMPRESS
1959     if (do_compress) {
1960         int size = QPdfPage::chunkSize();
1961         int sum = 0;
1962         ::z_stream zStruct;
1963         zStruct.zalloc = Z_NULL;
1964         zStruct.zfree = Z_NULL;
1965         zStruct.opaque = Z_NULL;
1966         if (::deflateInit(&zStruct, Z_DEFAULT_COMPRESSION) != Z_OK) {
1967             qWarning("QPdfStream::writeCompressed: Error in deflateInit()");
1968             return sum;
1969         }
1970         zStruct.avail_in = 0;
1971         QByteArray in, out;
1972         out.resize(size);
1973         while (!dev->atEnd() || zStruct.avail_in != 0) {
1974             if (zStruct.avail_in == 0) {
1975                 in = dev->read(size);
1976                 zStruct.avail_in = in.size();
1977                 zStruct.next_in = reinterpret_cast<unsigned char*>(in.data());
1978                 if (in.size() <= 0) {
1979                     qWarning("QPdfStream::writeCompressed: Error in read()");
1980                     ::deflateEnd(&zStruct);
1981                     return sum;
1982                 }
1983             }
1984             zStruct.next_out = reinterpret_cast<unsigned char*>(out.data());
1985             zStruct.avail_out = out.size();
1986             if (::deflate(&zStruct, 0) != Z_OK) {
1987                 qWarning("QPdfStream::writeCompressed: Error in deflate()");
1988                 ::deflateEnd(&zStruct);
1989                 return sum;
1990             }
1991             int written = out.size() - zStruct.avail_out;
1992             stream->writeRawData(out.constData(), written);
1993             streampos += written;
1994             sum += written;
1995         }
1996         int ret;
1997         do {
1998             zStruct.next_out = reinterpret_cast<unsigned char*>(out.data());
1999             zStruct.avail_out = out.size();
2000             ret = ::deflate(&zStruct, Z_FINISH);
2001             if (ret != Z_OK && ret != Z_STREAM_END) {
2002                 qWarning("QPdfStream::writeCompressed: Error in deflate()");
2003                 ::deflateEnd(&zStruct);
2004                 return sum;
2005             }
2006             int written = out.size() - zStruct.avail_out;
2007             stream->writeRawData(out.constData(), written);
2008             streampos += written;
2009             sum += written;
2010         } while (ret == Z_OK);
2011
2012         ::deflateEnd(&zStruct);
2013
2014         return sum;
2015     } else
2016 #endif
2017     {
2018         QByteArray arr;
2019         int sum = 0;
2020         while (!dev->atEnd()) {
2021             arr = dev->read(QPdfPage::chunkSize());
2022             stream->writeRawData(arr.constData(), arr.size());
2023             streampos += arr.size();
2024             sum += arr.size();
2025         }
2026         return sum;
2027     }
2028 }
2029
2030 int QPdfEnginePrivate::writeCompressed(const char *src, int len)
2031 {
2032 #ifndef QT_NO_COMPRESS
2033     if(do_compress) {
2034         uLongf destLen = len + len/100 + 13; // zlib requirement
2035         Bytef* dest = new Bytef[destLen];
2036         if (Z_OK == ::compress(dest, &destLen, (const Bytef*) src, (uLongf)len)) {
2037             stream->writeRawData((const char*)dest, destLen);
2038         } else {
2039             qWarning("QPdfStream::writeCompressed: Error in compress()");
2040             destLen = 0;
2041         }
2042         delete [] dest;
2043         len = destLen;
2044     } else
2045 #endif
2046     {
2047         stream->writeRawData(src,len);
2048     }
2049     streampos += len;
2050     return len;
2051 }
2052
2053 int QPdfEnginePrivate::writeImage(const QByteArray &data, int width, int height, int depth,
2054                                   int maskObject, int softMaskObject, bool dct)
2055 {
2056     int image = addXrefEntry(-1);
2057     xprintf("<<\n"
2058             "/Type /XObject\n"
2059             "/Subtype /Image\n"
2060             "/Width %d\n"
2061             "/Height %d\n", width, height);
2062
2063     if (depth == 1) {
2064         xprintf("/ImageMask true\n"
2065                 "/Decode [1 0]\n");
2066     } else {
2067         xprintf("/BitsPerComponent 8\n"
2068                 "/ColorSpace %s\n", (depth == 32) ? "/DeviceRGB" : "/DeviceGray");
2069     }
2070     if (maskObject > 0)
2071         xprintf("/Mask %d 0 R\n", maskObject);
2072     if (softMaskObject > 0)
2073         xprintf("/SMask %d 0 R\n", softMaskObject);
2074
2075     int lenobj = requestObject();
2076     xprintf("/Length %d 0 R\n", lenobj);
2077     if (interpolateImages)
2078         xprintf("/Interpolate true\n");
2079     int len = 0;
2080     if (dct) {
2081         //qDebug() << "DCT";
2082         xprintf("/Filter /DCTDecode\n>>\nstream\n");
2083         write(data);
2084         len = data.length();
2085     } else {
2086         if (do_compress)
2087             xprintf("/Filter /FlateDecode\n>>\nstream\n");
2088         else
2089             xprintf(">>\nstream\n");
2090         len = writeCompressed(data);
2091     }
2092     xprintf("endstream\n"
2093             "endobj\n");
2094     addXrefEntry(lenobj);
2095     xprintf("%d\n"
2096             "endobj\n", len);
2097     return image;
2098 }
2099
2100 #ifdef USE_NATIVE_GRADIENTS
2101 int QPdfEnginePrivate::gradientBrush(const QBrush &b, const QMatrix &matrix, int *gStateObject)
2102 {
2103     const QGradient *gradient = b.gradient();
2104     if (!gradient)
2105         return 0;
2106
2107     QTransform inv = matrix.inverted();
2108     QPointF page_rect[4] = { inv.map(QPointF(0, 0)),
2109                              inv.map(QPointF(width_, 0)),
2110                              inv.map(QPointF(0, height_)),
2111                              inv.map(QPointF(width_, height_)) };
2112
2113     bool opaque = b.isOpaque();
2114
2115     QByteArray shader;
2116     QByteArray alphaShader;
2117     if (gradient->type() == QGradient::LinearGradient) {
2118         const QLinearGradient *lg = static_cast<const QLinearGradient *>(gradient);
2119         shader = QPdf::generateLinearGradientShader(lg, page_rect);
2120         if (!opaque)
2121             alphaShader = QPdf::generateLinearGradientShader(lg, page_rect, true);
2122     } else {
2123         // #############
2124         return 0;
2125     }
2126     int shaderObject = addXrefEntry(-1);
2127     write(shader);
2128
2129     QByteArray str;
2130     QPdf::ByteStream s(&str);
2131     s << "<<\n"
2132         "/Type /Pattern\n"
2133         "/PatternType 2\n"
2134         "/Shading " << shaderObject << "0 R\n"
2135         "/Matrix ["
2136       << matrix.m11()
2137       << matrix.m12()
2138       << matrix.m21()
2139       << matrix.m22()
2140       << matrix.dx()
2141       << matrix.dy() << "]\n";
2142     s << ">>\n"
2143         "endobj\n";
2144
2145     int patternObj = addXrefEntry(-1);
2146     write(str);
2147     currentPage->patterns.append(patternObj);
2148
2149     if (!opaque) {
2150         bool ca = true;
2151         QGradientStops stops = gradient->stops();
2152         int a = stops.at(0).second.alpha();
2153         for (int i = 1; i < stops.size(); ++i) {
2154             if (stops.at(i).second.alpha() != a) {
2155                 ca = false;
2156                 break;
2157             }
2158         }
2159         if (ca) {
2160             *gStateObject = addConstantAlphaObject(stops.at(0).second.alpha());
2161         } else {
2162             int alphaShaderObject = addXrefEntry(-1);
2163             write(alphaShader);
2164
2165             QByteArray content;
2166             QPdf::ByteStream c(&content);
2167             c << "/Shader" << alphaShaderObject << "sh\n";
2168
2169             QByteArray form;
2170             QPdf::ByteStream f(&form);
2171             f << "<<\n"
2172                 "/Type /XObject\n"
2173                 "/Subtype /Form\n"
2174                 "/BBox [0 0 " << width_ << height_ << "]\n"
2175                 "/Group <</S /Transparency >>\n"
2176                 "/Resources <<\n"
2177                 "/Shading << /Shader" << alphaShaderObject << alphaShaderObject << "0 R >>\n"
2178                 ">>\n";
2179
2180             f << "/Length " << content.length() << "\n"
2181                 ">>\n"
2182                 "stream\n"
2183               << content
2184               << "endstream\n"
2185                 "endobj\n";
2186
2187             int softMaskFormObject = addXrefEntry(-1);
2188             write(form);
2189             *gStateObject = addXrefEntry(-1);
2190             xprintf("<< /SMask << /S /Alpha /G %d 0 R >> >>\n"
2191                     "endobj\n", softMaskFormObject);
2192             currentPage->graphicStates.append(*gStateObject);
2193         }
2194     }
2195
2196     return patternObj;
2197 }
2198 #endif
2199
2200 int QPdfEnginePrivate::addConstantAlphaObject(int brushAlpha, int penAlpha)
2201 {
2202     if (brushAlpha == 255 && penAlpha == 255)
2203         return 0;
2204     int object = alphaCache.value(QPair<uint, uint>(brushAlpha, penAlpha), 0);
2205     if (!object) {
2206         object = addXrefEntry(-1);
2207         QByteArray alphaDef;
2208         QPdf::ByteStream s(&alphaDef);
2209         s << "<<\n/ca " << (brushAlpha/qreal(255.)) << '\n';
2210         s << "/CA " << (penAlpha/qreal(255.)) << "\n>>";
2211         xprintf("%s\nendobj\n", alphaDef.constData());
2212         alphaCache.insert(QPair<uint, uint>(brushAlpha, penAlpha), object);
2213     }
2214     if (currentPage->graphicStates.indexOf(object) < 0)
2215         currentPage->graphicStates.append(object);
2216
2217     return object;
2218 }
2219
2220 int QPdfEnginePrivate::addBrushPattern(const QTransform &m, bool *specifyColor, int *gStateObject)
2221 {
2222     int paintType = 2; // Uncolored tiling
2223     int w = 8;
2224     int h = 8;
2225
2226     *specifyColor = true;
2227     *gStateObject = 0;
2228
2229     QTransform matrix = m;
2230     matrix.translate(brushOrigin.x(), brushOrigin.y());
2231     matrix = matrix * pageMatrix();
2232     //qDebug() << brushOrigin << matrix;
2233
2234     Qt::BrushStyle style = brush.style();
2235     if (style == Qt::LinearGradientPattern) {// && style <= Qt::ConicalGradientPattern) {
2236 #ifdef USE_NATIVE_GRADIENTS
2237         *specifyColor = false;
2238         return gradientBrush(b, matrix, gStateObject);
2239 #else
2240         return 0;
2241 #endif
2242     }
2243
2244     if ((!brush.isOpaque() && brush.style() < Qt::LinearGradientPattern) || opacity != 1.0)
2245         *gStateObject = addConstantAlphaObject(qRound(brush.color().alpha() * opacity),
2246                                                qRound(pen.color().alpha() * opacity));
2247
2248     int imageObject = -1;
2249     QByteArray pattern = QPdf::patternForBrush(brush);
2250     if (pattern.isEmpty()) {
2251         if (brush.style() != Qt::TexturePattern)
2252             return 0;
2253         QImage image = brush.texture().toImage();
2254         bool bitmap = true;
2255         imageObject = addImage(image, &bitmap, brush.texture().cacheKey());
2256         if (imageObject != -1) {
2257             QImage::Format f = image.format();
2258             if (f != QImage::Format_MonoLSB && f != QImage::Format_Mono) {
2259                 paintType = 1; // Colored tiling
2260                 *specifyColor = false;
2261             }
2262             w = image.width();
2263             h = image.height();
2264             QTransform m(w, 0, 0, -h, 0, h);
2265             QPdf::ByteStream s(&pattern);
2266             s << QPdf::generateMatrix(m);
2267             s << "/Im" << imageObject << " Do\n";
2268         }
2269     }
2270
2271     QByteArray str;
2272     QPdf::ByteStream s(&str);
2273     s << "<<\n"
2274         "/Type /Pattern\n"
2275         "/PatternType 1\n"
2276         "/PaintType " << paintType << "\n"
2277         "/TilingType 1\n"
2278         "/BBox [0 0 " << w << h << "]\n"
2279         "/XStep " << w << "\n"
2280         "/YStep " << h << "\n"
2281         "/Matrix ["
2282       << matrix.m11()
2283       << matrix.m12()
2284       << matrix.m21()
2285       << matrix.m22()
2286       << matrix.dx()
2287       << matrix.dy() << "]\n"
2288         "/Resources \n<< "; // open resource tree
2289     if (imageObject > 0) {
2290         s << "/XObject << /Im" << imageObject << ' ' << imageObject << "0 R >> ";
2291     }
2292     s << ">>\n"
2293         "/Length " << pattern.length() << "\n"
2294         ">>\n"
2295         "stream\n"
2296       << pattern
2297       << "endstream\n"
2298         "endobj\n";
2299
2300     int patternObj = addXrefEntry(-1);
2301     write(str);
2302     currentPage->patterns.append(patternObj);
2303     return patternObj;
2304 }
2305
2306 /*!
2307  * Adds an image to the pdf and return the pdf-object id. Returns -1 if adding the image failed.
2308  */
2309 int QPdfEnginePrivate::addImage(const QImage &img, bool *bitmap, qint64 serial_no)
2310 {
2311     if (img.isNull())
2312         return -1;
2313
2314     int object = imageCache.value(serial_no);
2315     if(object)
2316         return object;
2317
2318     QImage image = img;
2319     QImage::Format format = image.format();
2320     if (image.depth() == 1 && *bitmap && img.colorTable().size() == 2
2321         && img.colorTable().at(0) == QColor(Qt::black).rgba()
2322         && img.colorTable().at(1) == QColor(Qt::white).rgba())
2323     {
2324         if (format == QImage::Format_MonoLSB)
2325             image = image.convertToFormat(QImage::Format_Mono);
2326         format = QImage::Format_Mono;
2327     } else {
2328         *bitmap = false;
2329         if (format != QImage::Format_RGB32 && format != QImage::Format_ARGB32) {
2330             image = image.convertToFormat(QImage::Format_ARGB32);
2331             format = QImage::Format_ARGB32;
2332         }
2333     }
2334
2335     int w = image.width();
2336     int h = image.height();
2337     int d = image.depth();
2338
2339     if (format == QImage::Format_Mono) {
2340         int bytesPerLine = (w + 7) >> 3;
2341         QByteArray data;
2342         data.resize(bytesPerLine * h);
2343         char *rawdata = data.data();
2344         for (int y = 0; y < h; ++y) {
2345             memcpy(rawdata, image.scanLine(y), bytesPerLine);
2346             rawdata += bytesPerLine;
2347         }
2348         object = writeImage(data, w, h, d, 0, 0);
2349     } else {
2350         QByteArray softMaskData;
2351         bool dct = false;
2352         QByteArray imageData;
2353         bool hasAlpha = false;
2354         bool hasMask = false;
2355
2356         if (QImageWriter::supportedImageFormats().contains("jpeg") && !grayscale) {
2357             QBuffer buffer(&imageData);
2358             QImageWriter writer(&buffer, "jpeg");
2359             writer.setQuality(94);
2360             writer.write(image);
2361             dct = true;
2362
2363             if (format != QImage::Format_RGB32) {
2364                 softMaskData.resize(w * h);
2365                 uchar *sdata = (uchar *)softMaskData.data();
2366                 for (int y = 0; y < h; ++y) {
2367                     const QRgb *rgb = (const QRgb *)image.scanLine(y);
2368                     for (int x = 0; x < w; ++x) {
2369                         uchar alpha = qAlpha(*rgb);
2370                         *sdata++ = alpha;
2371                         hasMask |= (alpha < 255);
2372                         hasAlpha |= (alpha != 0 && alpha != 255);
2373                         ++rgb;
2374                     }
2375                 }
2376             }
2377         } else {
2378             imageData.resize(grayscale ? w * h : 3 * w * h);
2379             uchar *data = (uchar *)imageData.data();
2380             softMaskData.resize(w * h);
2381             uchar *sdata = (uchar *)softMaskData.data();
2382             for (int y = 0; y < h; ++y) {
2383                 const QRgb *rgb = (const QRgb *)image.scanLine(y);
2384                 if (grayscale) {
2385                     for (int x = 0; x < w; ++x) {
2386                         *(data++) = qGray(*rgb);
2387                         uchar alpha = qAlpha(*rgb);
2388                         *sdata++ = alpha;
2389                         hasMask |= (alpha < 255);
2390                         hasAlpha |= (alpha != 0 && alpha != 255);
2391                         ++rgb;
2392                     }
2393                 } else {
2394                     for (int x = 0; x < w; ++x) {
2395                         *(data++) = qRed(*rgb);
2396                         *(data++) = qGreen(*rgb);
2397                         *(data++) = qBlue(*rgb);
2398                         uchar alpha = qAlpha(*rgb);
2399                         *sdata++ = alpha;
2400                         hasMask |= (alpha < 255);
2401                         hasAlpha |= (alpha != 0 && alpha != 255);
2402                         ++rgb;
2403                     }
2404                 }
2405             }
2406             if (format == QImage::Format_RGB32)
2407                 hasAlpha = hasMask = false;
2408         }
2409         int maskObject = 0;
2410         int softMaskObject = 0;
2411         if (hasAlpha) {
2412             softMaskObject = writeImage(softMaskData, w, h, 8, 0, 0);
2413         } else if (hasMask) {
2414             // dither the soft mask to 1bit and add it. This also helps PDF viewers
2415             // without transparency support
2416             int bytesPerLine = (w + 7) >> 3;
2417             QByteArray mask(bytesPerLine * h, 0);
2418             uchar *mdata = (uchar *)mask.data();
2419             const uchar *sdata = (const uchar *)softMaskData.constData();
2420             for (int y = 0; y < h; ++y) {
2421                 for (int x = 0; x < w; ++x) {
2422                     if (*sdata)
2423                         mdata[x>>3] |= (0x80 >> (x&7));
2424                     ++sdata;
2425                 }
2426                 mdata += bytesPerLine;
2427             }
2428             maskObject = writeImage(mask, w, h, 1, 0, 0);
2429         }
2430         object = writeImage(imageData, w, h, grayscale ? 8 : 32,
2431                             maskObject, softMaskObject, dct);
2432     }
2433     imageCache.insert(serial_no, object);
2434     return object;
2435 }
2436
2437 void QPdfEnginePrivate::drawTextItem(const QPointF &p, const QTextItemInt &ti)
2438 {
2439     Q_Q(QPdfEngine);
2440
2441     if (ti.charFormat.isAnchor()) {
2442         qreal size = ti.fontEngine->fontDef.pixelSize;
2443         int synthesized = ti.fontEngine->synthesized();
2444         qreal stretch = synthesized & QFontEngine::SynthesizedStretch ? ti.fontEngine->fontDef.stretch/100. : 1.;
2445
2446         QTransform trans;
2447         // Build text rendering matrix (Trm). We need it to map the text area to user
2448         // space units on the PDF page.
2449         trans = QTransform(size*stretch, 0, 0, size, 0, 0);
2450         // Apply text matrix (Tm).
2451         trans *= QTransform(1,0,0,-1,p.x(),p.y());
2452         // Apply page displacement (Identity for first page).
2453         trans *= stroker.matrix;
2454         // Apply Current Transformation Matrix (CTM)
2455         trans *= pageMatrix();
2456         qreal x1, y1, x2, y2;
2457         trans.map(0, 0, &x1, &y1);
2458         trans.map(ti.width.toReal()/size, (ti.ascent.toReal()-ti.descent.toReal())/size, &x2, &y2);
2459
2460         uint annot = addXrefEntry(-1);
2461 #ifdef Q_DEBUG_PDF_LINKS
2462         xprintf("<<\n/Type /Annot\n/Subtype /Link\n/Rect [%f %f %f %f]\n/Border [16 16 1]\n/A <<\n",
2463 #else
2464         xprintf("<<\n/Type /Annot\n/Subtype /Link\n/Rect [%f %f %f %f]\n/Border [0 0 0]\n/A <<\n",
2465 #endif
2466                 static_cast<double>(x1),
2467                 static_cast<double>(y1),
2468                 static_cast<double>(x2),
2469                 static_cast<double>(y2));
2470         xprintf("/Type /Action\n/S /URI\n/URI (%s)\n",
2471                 ti.charFormat.anchorHref().toLatin1().constData());
2472         xprintf(">>\n>>\n");
2473         xprintf("endobj\n");
2474
2475         if (!currentPage->annotations.contains(annot)) {
2476             currentPage->annotations.append(annot);
2477         }
2478     }
2479
2480     QFontEngine *fe = ti.fontEngine;
2481
2482     QFontEngine::FaceId face_id = fe->faceId();
2483     bool noEmbed = false;
2484     if (face_id.filename.isEmpty()
2485         || (!postscript && ((fe->fsType & 0x200) /* bitmap embedding only */
2486                             || (fe->fsType == 2) /* no embedding allowed */))) {
2487         *currentPage << "Q\n";
2488         q->QPaintEngine::drawTextItem(p, ti);
2489         *currentPage << "q\n";
2490         if (face_id.filename.isEmpty())
2491             return;
2492         noEmbed = true;
2493     }
2494
2495     QFontSubset *font = fonts.value(face_id, 0);
2496     if (!font) {
2497         font = new QFontSubset(fe, requestObject());
2498         font->noEmbed = noEmbed;
2499     }
2500     fonts.insert(face_id, font);
2501
2502     if (!currentPage->fonts.contains(font->object_id))
2503         currentPage->fonts.append(font->object_id);
2504
2505     qreal size = ti.fontEngine->fontDef.pixelSize;
2506
2507     QVarLengthArray<glyph_t> glyphs;
2508     QVarLengthArray<QFixedPoint> positions;
2509     QTransform m = QTransform::fromTranslate(p.x(), p.y());
2510     ti.fontEngine->getGlyphPositions(ti.glyphs, m, ti.flags,
2511                                      glyphs, positions);
2512     if (glyphs.size() == 0)
2513         return;
2514     int synthesized = ti.fontEngine->synthesized();
2515     qreal stretch = synthesized & QFontEngine::SynthesizedStretch ? ti.fontEngine->fontDef.stretch/100. : 1.;
2516
2517     *currentPage << "BT\n"
2518                  << "/F" << font->object_id << size << "Tf "
2519                  << stretch << (synthesized & QFontEngine::SynthesizedItalic
2520                                 ? "0 .3 -1 0 0 Tm\n"
2521                                 : "0 0 -1 0 0 Tm\n");
2522
2523
2524 #if 0
2525     // #### implement actual text for complex languages
2526     const unsigned short *logClusters = ti.logClusters;
2527     int pos = 0;
2528     do {
2529         int end = pos + 1;
2530         while (end < ti.num_chars && logClusters[end] == logClusters[pos])
2531             ++end;
2532         *currentPage << "/Span << /ActualText <FEFF";
2533         for (int i = pos; i < end; ++i) {
2534             s << toHex((ushort)ti.chars[i].unicode(), buf);
2535         }
2536         *currentPage << "> >>\n"
2537             "BDC\n"
2538             "<";
2539         int ge = end == ti.num_chars ? ti.num_glyphs : logClusters[end];
2540         for (int gs = logClusters[pos]; gs < ge; ++gs)
2541             *currentPage << toHex((ushort)ti.glyphs[gs].glyph, buf);
2542         *currentPage << "> Tj\n"
2543             "EMC\n";
2544         pos = end;
2545     } while (pos < ti.num_chars);
2546 #else
2547     qreal last_x = 0.;
2548     qreal last_y = 0.;
2549     for (int i = 0; i < glyphs.size(); ++i) {
2550         qreal x = positions[i].x.toReal();
2551         qreal y = positions[i].y.toReal();
2552         if (synthesized & QFontEngine::SynthesizedItalic)
2553             x += .3*y;
2554         x /= stretch;
2555         char buf[5];
2556         int g = font->addGlyph(glyphs[i]);
2557         *currentPage << x - last_x << last_y - y << "Td <"
2558                      << QPdf::toHex((ushort)g, buf) << "> Tj\n";
2559         last_x = x;
2560         last_y = y;
2561     }
2562     if (synthesized & QFontEngine::SynthesizedBold) {
2563         *currentPage << stretch << (synthesized & QFontEngine::SynthesizedItalic
2564                             ? "0 .3 -1 0 0 Tm\n"
2565                             : "0 0 -1 0 0 Tm\n");
2566         *currentPage << "/Span << /ActualText <> >> BDC\n";
2567         last_x = 0.5*fe->lineThickness().toReal();
2568         last_y = 0.;
2569         for (int i = 0; i < glyphs.size(); ++i) {
2570             qreal x = positions[i].x.toReal();
2571             qreal y = positions[i].y.toReal();
2572             if (synthesized & QFontEngine::SynthesizedItalic)
2573                 x += .3*y;
2574             x /= stretch;
2575             char buf[5];
2576             int g = font->addGlyph(glyphs[i]);
2577             *currentPage << x - last_x << last_y - y << "Td <"
2578                         << QPdf::toHex((ushort)g, buf) << "> Tj\n";
2579             last_x = x;
2580             last_y = y;
2581         }
2582         *currentPage << "EMC\n";
2583     }
2584 #endif
2585
2586     *currentPage << "ET\n";
2587 }
2588
2589 QTransform QPdfEnginePrivate::pageMatrix() const
2590 {
2591     qreal scale = 72./resolution;
2592     QTransform tmp(scale, 0.0, 0.0, -scale, 0.0, height());
2593     if (!fullPage) {
2594         QRect r = pageRect();
2595         tmp.translate(r.left(), r.top());
2596     }
2597     return tmp;
2598 }
2599
2600 void QPdfEnginePrivate::newPage()
2601 {
2602     if (currentPage && currentPage->pageSize.isEmpty())
2603         currentPage->pageSize = QSize(width(), height());
2604     writePage();
2605
2606     delete currentPage;
2607     currentPage = new QPdfPage;
2608     currentPage->pageSize = QSize(width(), height());
2609     stroker.stream = currentPage;
2610     pages.append(requestObject());
2611
2612     *currentPage << "/GSa gs /CSp cs /CSp CS\n"
2613                  << QPdf::generateMatrix(pageMatrix())
2614                  << "q q\n";
2615 }
2616
2617
2618 QT_END_NAMESPACE