Replace 'i < len-1 && func(i+1)' by 'i+1 < len && func(i+1)'
[profile/ivi/qtbase.git] / src / gui / painting / qprintengine_pdf.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtGui module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <QtGui/qprintengine.h>
43
44 #include <qiodevice.h>
45 #include <qpainter.h>
46 #include <qbitmap.h>
47 #include <qpainterpath.h>
48 #include <qpaintdevice.h>
49 #include <qfile.h>
50 #include <qdebug.h>
51 #include <qimagewriter.h>
52 #include <qbuffer.h>
53 #include <qdatetime.h>
54
55 #ifndef QT_NO_PRINTER
56 #include <limits.h>
57 #include <math.h>
58 #ifndef QT_NO_COMPRESS
59 #include <zlib.h>
60 #endif
61
62 #if defined(Q_OS_WINCE)
63 #include "qwinfunctions_wince.h"
64 #endif
65
66 #include "qprintengine_pdf_p.h"
67 #include "private/qdrawhelper_p.h"
68
69 QT_BEGIN_NAMESPACE
70
71 extern qint64 qt_pixmap_id(const QPixmap &pixmap);
72 extern qint64 qt_image_id(const QImage &image);
73
74 //#define FONT_DUMP
75
76 // might be helpful for smooth transforms of images
77 // Can't use it though, as gs generates completely wrong images if this is true.
78 static const bool interpolateImages = false;
79
80 #ifdef QT_NO_COMPRESS
81 static const bool do_compress = false;
82 #else
83 static const bool do_compress = true;
84 #endif
85
86 QPdfPage::QPdfPage()
87     : QPdf::ByteStream(true) // Enable file backing
88 {
89 }
90
91 void QPdfPage::streamImage(int w, int h, int object)
92 {
93     *this << w << "0 0 " << -h << "0 " << h << "cm /Im" << object << " Do\n";
94     if (!images.contains(object))
95         images.append(object);
96 }
97
98
99 inline QPaintEngine::PaintEngineFeatures qt_pdf_decide_features()
100 {
101     QPaintEngine::PaintEngineFeatures f = QPaintEngine::AllFeatures;
102     f &= ~(QPaintEngine::PorterDuff | QPaintEngine::PerspectiveTransform
103            | QPaintEngine::ObjectBoundingModeGradients
104 #ifndef USE_NATIVE_GRADIENTS
105            | QPaintEngine::LinearGradientFill
106 #endif
107            | QPaintEngine::RadialGradientFill
108            | QPaintEngine::ConicalGradientFill);
109     return f;
110 }
111
112 QPdfEngine::QPdfEngine(QPrinter::PrinterMode m)
113     : QPdfBaseEngine(*new QPdfEnginePrivate(m), qt_pdf_decide_features())
114 {
115     state = QPrinter::Idle;
116 }
117
118 QPdfEngine::~QPdfEngine()
119 {
120 }
121
122 bool QPdfEngine::begin(QPaintDevice *pdev)
123 {
124     Q_D(QPdfEngine);
125
126     if(!QPdfBaseEngine::begin(pdev)) {
127         state = QPrinter::Error;
128         return false;
129     }
130     d->stream->setDevice(d->outDevice);
131
132     d->streampos = 0;
133     d->hasPen = true;
134     d->hasBrush = false;
135     d->clipEnabled = false;
136     d->allClipped = false;
137
138     d->xrefPositions.clear();
139     d->pageRoot = 0;
140     d->catalog = 0;
141     d->info = 0;
142     d->graphicsState = 0;
143     d->patternColorSpace = 0;
144
145     d->pages.clear();
146     d->imageCache.clear();
147
148     setActive(true);
149     state = QPrinter::Active;
150     d->writeHeader();
151     newPage();
152
153     return true;
154 }
155
156 bool QPdfEngine::end()
157 {
158     Q_D(QPdfEngine);
159     d->writeTail();
160
161     d->stream->unsetDevice();
162     QPdfBaseEngine::end();
163     setActive(false);
164     state = QPrinter::Idle;
165     return true;
166 }
167
168
169 void QPdfEngine::drawPixmap (const QRectF &rectangle, const QPixmap &pixmap, const QRectF &sr)
170 {
171     if (sr.isEmpty() || rectangle.isEmpty() || pixmap.isNull())
172         return;
173     Q_D(QPdfEngine);
174
175     QBrush b = d->brush;
176
177     QRect sourceRect = sr.toRect();
178     QPixmap pm = sourceRect != pixmap.rect() ? pixmap.copy(sourceRect) : pixmap;
179     QImage image = pm.toImage();
180     bool bitmap = true;
181     const int object = d->addImage(image, &bitmap, pm.cacheKey());
182     if (object < 0)
183         return;
184
185     *d->currentPage << "q\n/GSa gs\n";
186     *d->currentPage
187         << QPdf::generateMatrix(QTransform(rectangle.width() / sr.width(), 0, 0, rectangle.height() / sr.height(),
188                                            rectangle.x(), rectangle.y()) * (d->simplePen ? QTransform() : d->stroker.matrix));
189     if (bitmap) {
190         // set current pen as d->brush
191         d->brush = d->pen.brush();
192     }
193     setBrush();
194     d->currentPage->streamImage(image.width(), image.height(), object);
195     *d->currentPage << "Q\n";
196
197     d->brush = b;
198 }
199
200 void QPdfEngine::drawImage(const QRectF &rectangle, const QImage &image, const QRectF &sr, Qt::ImageConversionFlags)
201 {
202     if (sr.isEmpty() || rectangle.isEmpty() || image.isNull())
203         return;
204     Q_D(QPdfEngine);
205
206     QRect sourceRect = sr.toRect();
207     QImage im = sourceRect != image.rect() ? image.copy(sourceRect) : image;
208     bool bitmap = true;
209     const int object = d->addImage(im, &bitmap, im.cacheKey());
210     if (object < 0)
211         return;
212
213     *d->currentPage << "q\n/GSa gs\n";
214     *d->currentPage
215         << QPdf::generateMatrix(QTransform(rectangle.width() / sr.width(), 0, 0, rectangle.height() / sr.height(),
216                                            rectangle.x(), rectangle.y()) * (d->simplePen ? QTransform() : d->stroker.matrix));
217     setBrush();
218     d->currentPage->streamImage(im.width(), im.height(), object);
219     *d->currentPage << "Q\n";
220 }
221
222 void QPdfEngine::drawTiledPixmap (const QRectF &rectangle, const QPixmap &pixmap, const QPointF &point)
223 {
224     Q_D(QPdfEngine);
225
226     bool bitmap = (pixmap.depth() == 1);
227     QBrush b = d->brush;
228     QPointF bo = d->brushOrigin;
229     bool hp = d->hasPen;
230     d->hasPen = false;
231     bool hb = d->hasBrush;
232     d->hasBrush = true;
233
234     d->brush = QBrush(pixmap);
235     if (bitmap)
236         // #### fix bitmap case where we have a brush pen
237         d->brush.setColor(d->pen.color());
238
239     d->brushOrigin = -point;
240     *d->currentPage << "q\n";
241     setBrush();
242
243     drawRects(&rectangle, 1);
244     *d->currentPage << "Q\n";
245
246     d->hasPen = hp;
247     d->hasBrush = hb;
248     d->brush = b;
249     d->brushOrigin = bo;
250 }
251
252
253 void QPdfEngine::setBrush()
254 {
255     Q_D(QPdfEngine);
256     Qt::BrushStyle style = d->brush.style();
257     if (style == Qt::NoBrush)
258         return;
259
260     bool specifyColor;
261     int gStateObject = 0;
262     int patternObject = d->addBrushPattern(d->stroker.matrix, &specifyColor, &gStateObject);
263
264     *d->currentPage << (patternObject ? "/PCSp cs " : "/CSp cs ");
265     if (specifyColor) {
266         QColor rgba = d->brush.color();
267         if (d->colorMode == QPrinter::GrayScale) {
268             qreal gray = qGray(rgba.rgba())/255.;
269             *d->currentPage << gray << gray << gray;
270         } else {
271             *d->currentPage << rgba.redF()
272                             << rgba.greenF()
273                             << rgba.blueF();
274         }
275     }
276     if (patternObject)
277         *d->currentPage << "/Pat" << patternObject;
278     *d->currentPage << "scn\n";
279
280     if (gStateObject)
281         *d->currentPage << "/GState" << gStateObject << "gs\n";
282     else
283         *d->currentPage << "/GSa gs\n";
284 }
285
286 QPaintEngine::Type QPdfEngine::type() const
287 {
288     return QPaintEngine::Pdf;
289 }
290
291 bool QPdfEngine::newPage()
292 {
293     Q_D(QPdfEngine);
294     if (!isActive())
295         return false;
296     d->newPage();
297     return QPdfBaseEngine::newPage();
298 }
299
300 QPdfEnginePrivate::QPdfEnginePrivate(QPrinter::PrinterMode m)
301     : QPdfBaseEnginePrivate(m)
302 {
303     streampos = 0;
304
305     stream = new QDataStream;
306     pageOrder = QPrinter::FirstPageFirst;
307     orientation = QPrinter::Portrait;
308     fullPage = false;
309 }
310
311 QPdfEnginePrivate::~QPdfEnginePrivate()
312 {
313     delete stream;
314 }
315
316
317 #ifdef USE_NATIVE_GRADIENTS
318 int QPdfEnginePrivate::gradientBrush(const QBrush &b, const QMatrix &matrix, int *gStateObject)
319 {
320     const QGradient *gradient = b.gradient();
321     if (!gradient)
322         return 0;
323
324     QTransform inv = matrix.inverted();
325     QPointF page_rect[4] = { inv.map(QPointF(0, 0)),
326                              inv.map(QPointF(width_, 0)),
327                              inv.map(QPointF(0, height_)),
328                              inv.map(QPointF(width_, height_)) };
329
330     bool opaque = b.isOpaque();
331
332     QByteArray shader;
333     QByteArray alphaShader;
334     if (gradient->type() == QGradient::LinearGradient) {
335         const QLinearGradient *lg = static_cast<const QLinearGradient *>(gradient);
336         shader = QPdf::generateLinearGradientShader(lg, page_rect);
337         if (!opaque)
338             alphaShader = QPdf::generateLinearGradientShader(lg, page_rect, true);
339     } else {
340         // #############
341         return 0;
342     }
343     int shaderObject = addXrefEntry(-1);
344     write(shader);
345
346     QByteArray str;
347     QPdf::ByteStream s(&str);
348     s << "<<\n"
349         "/Type /Pattern\n"
350         "/PatternType 2\n"
351         "/Shading " << shaderObject << "0 R\n"
352         "/Matrix ["
353       << matrix.m11()
354       << matrix.m12()
355       << matrix.m21()
356       << matrix.m22()
357       << matrix.dx()
358       << matrix.dy() << "]\n";
359     s << ">>\n"
360         "endobj\n";
361
362     int patternObj = addXrefEntry(-1);
363     write(str);
364     currentPage->patterns.append(patternObj);
365
366     if (!opaque) {
367         bool ca = true;
368         QGradientStops stops = gradient->stops();
369         int a = stops.at(0).second.alpha();
370         for (int i = 1; i < stops.size(); ++i) {
371             if (stops.at(i).second.alpha() != a) {
372                 ca = false;
373                 break;
374             }
375         }
376         if (ca) {
377             *gStateObject = addConstantAlphaObject(stops.at(0).second.alpha());
378         } else {
379             int alphaShaderObject = addXrefEntry(-1);
380             write(alphaShader);
381
382             QByteArray content;
383             QPdf::ByteStream c(&content);
384             c << "/Shader" << alphaShaderObject << "sh\n";
385
386             QByteArray form;
387             QPdf::ByteStream f(&form);
388             f << "<<\n"
389                 "/Type /XObject\n"
390                 "/Subtype /Form\n"
391                 "/BBox [0 0 " << width_ << height_ << "]\n"
392                 "/Group <</S /Transparency >>\n"
393                 "/Resources <<\n"
394                 "/Shading << /Shader" << alphaShaderObject << alphaShaderObject << "0 R >>\n"
395                 ">>\n";
396
397             f << "/Length " << content.length() << "\n"
398                 ">>\n"
399                 "stream\n"
400               << content
401               << "endstream\n"
402                 "endobj\n";
403
404             int softMaskFormObject = addXrefEntry(-1);
405             write(form);
406             *gStateObject = addXrefEntry(-1);
407             xprintf("<< /SMask << /S /Alpha /G %d 0 R >> >>\n"
408                     "endobj\n", softMaskFormObject);
409             currentPage->graphicStates.append(*gStateObject);
410         }
411     }
412
413     return patternObj;
414 }
415 #endif
416
417 int QPdfEnginePrivate::addConstantAlphaObject(int brushAlpha, int penAlpha)
418 {
419     if (brushAlpha == 255 && penAlpha == 255)
420         return 0;
421     int object = alphaCache.value(QPair<uint, uint>(brushAlpha, penAlpha), 0);
422     if (!object) {
423         object = addXrefEntry(-1);
424         QByteArray alphaDef;
425         QPdf::ByteStream s(&alphaDef);
426         s << "<<\n/ca " << (brushAlpha/qreal(255.)) << '\n';
427         s << "/CA " << (penAlpha/qreal(255.)) << "\n>>";
428         xprintf("%s\nendobj\n", alphaDef.constData());
429         alphaCache.insert(QPair<uint, uint>(brushAlpha, penAlpha), object);
430     }
431     if (currentPage->graphicStates.indexOf(object) < 0)
432         currentPage->graphicStates.append(object);
433
434     return object;
435 }
436
437 int QPdfEnginePrivate::addBrushPattern(const QTransform &m, bool *specifyColor, int *gStateObject)
438 {
439     int paintType = 2; // Uncolored tiling
440     int w = 8;
441     int h = 8;
442
443     *specifyColor = true;
444     *gStateObject = 0;
445
446     QTransform matrix = m;
447     matrix.translate(brushOrigin.x(), brushOrigin.y());
448     matrix = matrix * pageMatrix();
449     //qDebug() << brushOrigin << matrix;
450
451     Qt::BrushStyle style = brush.style();
452     if (style == Qt::LinearGradientPattern) {// && style <= Qt::ConicalGradientPattern) {
453 #ifdef USE_NATIVE_GRADIENTS
454         *specifyColor = false;
455         return gradientBrush(b, matrix, gStateObject);
456 #else
457         return 0;
458 #endif
459     }
460
461     if ((!brush.isOpaque() && brush.style() < Qt::LinearGradientPattern) || opacity != 1.0)
462         *gStateObject = addConstantAlphaObject(qRound(brush.color().alpha() * opacity),
463                                                qRound(pen.color().alpha() * opacity));
464
465     int imageObject = -1;
466     QByteArray pattern = QPdf::patternForBrush(brush);
467     if (pattern.isEmpty()) {
468         if (brush.style() != Qt::TexturePattern)
469             return 0;
470         QImage image = brush.texture().toImage();
471         bool bitmap = true;
472         imageObject = addImage(image, &bitmap, qt_pixmap_id(brush.texture()));
473         if (imageObject != -1) {
474             QImage::Format f = image.format();
475             if (f != QImage::Format_MonoLSB && f != QImage::Format_Mono) {
476                 paintType = 1; // Colored tiling
477                 *specifyColor = false;
478             }
479             w = image.width();
480             h = image.height();
481             QTransform m(w, 0, 0, -h, 0, h);
482             QPdf::ByteStream s(&pattern);
483             s << QPdf::generateMatrix(m);
484             s << "/Im" << imageObject << " Do\n";
485         }
486     }
487
488     QByteArray str;
489     QPdf::ByteStream s(&str);
490     s << "<<\n"
491         "/Type /Pattern\n"
492         "/PatternType 1\n"
493         "/PaintType " << paintType << "\n"
494         "/TilingType 1\n"
495         "/BBox [0 0 " << w << h << "]\n"
496         "/XStep " << w << "\n"
497         "/YStep " << h << "\n"
498         "/Matrix ["
499       << matrix.m11()
500       << matrix.m12()
501       << matrix.m21()
502       << matrix.m22()
503       << matrix.dx()
504       << matrix.dy() << "]\n"
505         "/Resources \n<< "; // open resource tree
506     if (imageObject > 0) {
507         s << "/XObject << /Im" << imageObject << ' ' << imageObject << "0 R >> ";
508     }
509     s << ">>\n"
510         "/Length " << pattern.length() << "\n"
511         ">>\n"
512         "stream\n"
513       << pattern
514       << "endstream\n"
515         "endobj\n";
516
517     int patternObj = addXrefEntry(-1);
518     write(str);
519     currentPage->patterns.append(patternObj);
520     return patternObj;
521 }
522
523 /*!
524  * Adds an image to the pdf and return the pdf-object id. Returns -1 if adding the image failed.
525  */
526 int QPdfEnginePrivate::addImage(const QImage &img, bool *bitmap, qint64 serial_no)
527 {
528     if (img.isNull())
529         return -1;
530
531     int object = imageCache.value(serial_no);
532     if(object)
533         return object;
534
535     QImage image = img;
536     QImage::Format format = image.format();
537     if (image.depth() == 1 && *bitmap && img.colorTable().size() == 2
538         && img.colorTable().at(0) == QColor(Qt::black).rgba()
539         && img.colorTable().at(1) == QColor(Qt::white).rgba())
540     {
541         if (format == QImage::Format_MonoLSB)
542             image = image.convertToFormat(QImage::Format_Mono);
543         format = QImage::Format_Mono;
544     } else {
545         *bitmap = false;
546         if (format != QImage::Format_RGB32 && format != QImage::Format_ARGB32) {
547             image = image.convertToFormat(QImage::Format_ARGB32);
548             format = QImage::Format_ARGB32;
549         }
550     }
551
552     int w = image.width();
553     int h = image.height();
554     int d = image.depth();
555
556     if (format == QImage::Format_Mono) {
557         int bytesPerLine = (w + 7) >> 3;
558         QByteArray data;
559         data.resize(bytesPerLine * h);
560         char *rawdata = data.data();
561         for (int y = 0; y < h; ++y) {
562             memcpy(rawdata, image.scanLine(y), bytesPerLine);
563             rawdata += bytesPerLine;
564         }
565         object = writeImage(data, w, h, d, 0, 0);
566     } else {
567         QByteArray softMaskData;
568         bool dct = false;
569         QByteArray imageData;
570         bool hasAlpha = false;
571         bool hasMask = false;
572
573         if (QImageWriter::supportedImageFormats().contains("jpeg") && colorMode != QPrinter::GrayScale) {
574             QBuffer buffer(&imageData);
575             QImageWriter writer(&buffer, "jpeg");
576             writer.setQuality(94);
577             writer.write(image);
578             dct = true;
579
580             if (format != QImage::Format_RGB32) {
581                 softMaskData.resize(w * h);
582                 uchar *sdata = (uchar *)softMaskData.data();
583                 for (int y = 0; y < h; ++y) {
584                     const QRgb *rgb = (const QRgb *)image.scanLine(y);
585                     for (int x = 0; x < w; ++x) {
586                         uchar alpha = qAlpha(*rgb);
587                         *sdata++ = alpha;
588                         hasMask |= (alpha < 255);
589                         hasAlpha |= (alpha != 0 && alpha != 255);
590                         ++rgb;
591                     }
592                 }
593             }
594         } else {
595             imageData.resize(colorMode == QPrinter::GrayScale ? w * h : 3 * w * h);
596             uchar *data = (uchar *)imageData.data();
597             softMaskData.resize(w * h);
598             uchar *sdata = (uchar *)softMaskData.data();
599             for (int y = 0; y < h; ++y) {
600                 const QRgb *rgb = (const QRgb *)image.scanLine(y);
601                 if (colorMode == QPrinter::GrayScale) {
602                     for (int x = 0; x < w; ++x) {
603                         *(data++) = qGray(*rgb);
604                         uchar alpha = qAlpha(*rgb);
605                         *sdata++ = alpha;
606                         hasMask |= (alpha < 255);
607                         hasAlpha |= (alpha != 0 && alpha != 255);
608                         ++rgb;
609                     }
610                 } else {
611                     for (int x = 0; x < w; ++x) {
612                         *(data++) = qRed(*rgb);
613                         *(data++) = qGreen(*rgb);
614                         *(data++) = qBlue(*rgb);
615                         uchar alpha = qAlpha(*rgb);
616                         *sdata++ = alpha;
617                         hasMask |= (alpha < 255);
618                         hasAlpha |= (alpha != 0 && alpha != 255);
619                         ++rgb;
620                     }
621                 }
622             }
623             if (format == QImage::Format_RGB32)
624                 hasAlpha = hasMask = false;
625         }
626         int maskObject = 0;
627         int softMaskObject = 0;
628         if (hasAlpha) {
629             softMaskObject = writeImage(softMaskData, w, h, 8, 0, 0);
630         } else if (hasMask) {
631             // dither the soft mask to 1bit and add it. This also helps PDF viewers
632             // without transparency support
633             int bytesPerLine = (w + 7) >> 3;
634             QByteArray mask(bytesPerLine * h, 0);
635             uchar *mdata = (uchar *)mask.data();
636             const uchar *sdata = (const uchar *)softMaskData.constData();
637             for (int y = 0; y < h; ++y) {
638                 for (int x = 0; x < w; ++x) {
639                     if (*sdata)
640                         mdata[x>>3] |= (0x80 >> (x&7));
641                     ++sdata;
642                 }
643                 mdata += bytesPerLine;
644             }
645             maskObject = writeImage(mask, w, h, 1, 0, 0);
646         }
647         object = writeImage(imageData, w, h, colorMode == QPrinter::GrayScale ? 8 : 32,
648                             maskObject, softMaskObject, dct);
649     }
650     imageCache.insert(serial_no, object);
651     return object;
652 }
653
654 void QPdfEnginePrivate::drawTextItem(const QPointF &p, const QTextItemInt &ti)
655 {
656     if (ti.charFormat.isAnchor()) {
657         qreal size = ti.fontEngine->fontDef.pixelSize;
658 #ifdef Q_WS_WIN
659         if (ti.fontEngine->type() == QFontEngine::Win) {
660             QFontEngineWin *fe = static_cast<QFontEngineWin *>(ti.fontEngine);
661             size = fe->tm.tmHeight;
662         }
663 #endif
664         int synthesized = ti.fontEngine->synthesized();
665         qreal stretch = synthesized & QFontEngine::SynthesizedStretch ? ti.fontEngine->fontDef.stretch/100. : 1.;
666
667         QTransform trans;
668         // Build text rendering matrix (Trm). We need it to map the text area to user
669         // space units on the PDF page.
670         trans = QTransform(size*stretch, 0, 0, size, 0, 0);
671         // Apply text matrix (Tm).
672         trans *= QTransform(1,0,0,-1,p.x(),p.y());
673         // Apply page displacement (Identity for first page).
674         trans *= stroker.matrix;
675         // Apply Current Transformation Matrix (CTM)
676         trans *= pageMatrix();
677         qreal x1, y1, x2, y2;
678         trans.map(0, 0, &x1, &y1);
679         trans.map(ti.width.toReal()/size, (ti.ascent.toReal()-ti.descent.toReal())/size, &x2, &y2);
680
681         uint annot = addXrefEntry(-1);
682 #ifdef Q_DEBUG_PDF_LINKS
683         xprintf("<<\n/Type /Annot\n/Subtype /Link\n/Rect [%f %f %f %f]\n/Border [16 16 1]\n/A <<\n",
684 #else
685         xprintf("<<\n/Type /Annot\n/Subtype /Link\n/Rect [%f %f %f %f]\n/Border [0 0 0]\n/A <<\n",
686 #endif
687                 static_cast<double>(x1),
688                 static_cast<double>(y1),
689                 static_cast<double>(x2),
690                 static_cast<double>(y2));
691         xprintf("/Type /Action\n/S /URI\n/URI (%s)\n",
692                 ti.charFormat.anchorHref().toLatin1().constData());
693         xprintf(">>\n>>\n");
694         xprintf("endobj\n");
695
696         if (!currentPage->annotations.contains(annot)) {
697             currentPage->annotations.append(annot);
698         }
699     }
700
701     QPdfBaseEnginePrivate::drawTextItem(p, ti);
702 }
703
704 QTransform QPdfEnginePrivate::pageMatrix() const
705 {
706     qreal scale = 72./resolution;
707     QTransform tmp(scale, 0.0, 0.0, -scale, 0.0, height());
708     if (!fullPage) {
709         QRect r = pageRect();
710         tmp.translate(r.left(), r.top());
711     }
712     return tmp;
713 }
714
715 void QPdfEnginePrivate::newPage()
716 {
717     if (currentPage && currentPage->pageSize.isEmpty())
718         currentPage->pageSize = QSize(width(), height());
719     writePage();
720
721     delete currentPage;
722     currentPage = new QPdfPage;
723     currentPage->pageSize = QSize(width(), height());
724     stroker.stream = currentPage;
725     pages.append(requestObject());
726
727     *currentPage << "/GSa gs /CSp cs /CSp CS\n"
728                  << QPdf::generateMatrix(pageMatrix())
729                  << "q q\n";
730 }
731
732
733 // For strings up to 10000 bytes only !
734 void QPdfEnginePrivate::xprintf(const char* fmt, ...)
735 {
736     if (!stream)
737         return;
738
739     const int msize = 10000;
740     char buf[msize];
741
742     va_list args;
743     va_start(args, fmt);
744     int bufsize = qvsnprintf(buf, msize, fmt, args);
745
746     Q_ASSERT(bufsize<msize);
747
748     va_end(args);
749
750     stream->writeRawData(buf, bufsize);
751     streampos += bufsize;
752 }
753
754 int QPdfEnginePrivate::writeCompressed(QIODevice *dev)
755 {
756 #ifndef QT_NO_COMPRESS
757     if (do_compress) {
758         int size = QPdfPage::chunkSize();
759         int sum = 0;
760         ::z_stream zStruct;
761         zStruct.zalloc = Z_NULL;
762         zStruct.zfree = Z_NULL;
763         zStruct.opaque = Z_NULL;
764         if (::deflateInit(&zStruct, Z_DEFAULT_COMPRESSION) != Z_OK) {
765             qWarning("QPdfStream::writeCompressed: Error in deflateInit()");
766             return sum;
767         }
768         zStruct.avail_in = 0;
769         QByteArray in, out;
770         out.resize(size);
771         while (!dev->atEnd() || zStruct.avail_in != 0) {
772             if (zStruct.avail_in == 0) {
773                 in = dev->read(size);
774                 zStruct.avail_in = in.size();
775                 zStruct.next_in = reinterpret_cast<unsigned char*>(in.data());
776                 if (in.size() <= 0) {
777                     qWarning("QPdfStream::writeCompressed: Error in read()");
778                     ::deflateEnd(&zStruct);
779                     return sum;
780                 }
781             }
782             zStruct.next_out = reinterpret_cast<unsigned char*>(out.data());
783             zStruct.avail_out = out.size();
784             if (::deflate(&zStruct, 0) != Z_OK) {
785                 qWarning("QPdfStream::writeCompressed: Error in deflate()");
786                 ::deflateEnd(&zStruct);
787                 return sum;
788             }
789             int written = out.size() - zStruct.avail_out;
790             stream->writeRawData(out.constData(), written);
791             streampos += written;
792             sum += written;
793         }
794         int ret;
795         do {
796             zStruct.next_out = reinterpret_cast<unsigned char*>(out.data());
797             zStruct.avail_out = out.size();
798             ret = ::deflate(&zStruct, Z_FINISH);
799             if (ret != Z_OK && ret != Z_STREAM_END) {
800                 qWarning("QPdfStream::writeCompressed: Error in deflate()");
801                 ::deflateEnd(&zStruct);
802                 return sum;
803             }
804             int written = out.size() - zStruct.avail_out;
805             stream->writeRawData(out.constData(), written);
806             streampos += written;
807             sum += written;
808         } while (ret == Z_OK);
809
810         ::deflateEnd(&zStruct);
811
812         return sum;
813     } else
814 #endif
815     {
816         QByteArray arr;
817         int sum = 0;
818         while (!dev->atEnd()) {
819             arr = dev->read(QPdfPage::chunkSize());
820             stream->writeRawData(arr.constData(), arr.size());
821             streampos += arr.size();
822             sum += arr.size();
823         }
824         return sum;
825     }
826 }
827
828 int QPdfEnginePrivate::writeCompressed(const char *src, int len)
829 {
830 #ifndef QT_NO_COMPRESS
831     if(do_compress) {
832         uLongf destLen = len + len/100 + 13; // zlib requirement
833         Bytef* dest = new Bytef[destLen];
834         if (Z_OK == ::compress(dest, &destLen, (const Bytef*) src, (uLongf)len)) {
835             stream->writeRawData((const char*)dest, destLen);
836         } else {
837             qWarning("QPdfStream::writeCompressed: Error in compress()");
838             destLen = 0;
839         }
840         delete [] dest;
841         len = destLen;
842     } else
843 #endif
844     {
845         stream->writeRawData(src,len);
846     }
847     streampos += len;
848     return len;
849 }
850
851 int QPdfEnginePrivate::writeImage(const QByteArray &data, int width, int height, int depth,
852                                   int maskObject, int softMaskObject, bool dct)
853 {
854     int image = addXrefEntry(-1);
855     xprintf("<<\n"
856             "/Type /XObject\n"
857             "/Subtype /Image\n"
858             "/Width %d\n"
859             "/Height %d\n", width, height);
860
861     if (depth == 1) {
862         xprintf("/ImageMask true\n"
863                 "/Decode [1 0]\n");
864     } else {
865         xprintf("/BitsPerComponent 8\n"
866                 "/ColorSpace %s\n", (depth == 32) ? "/DeviceRGB" : "/DeviceGray");
867     }
868     if (maskObject > 0)
869         xprintf("/Mask %d 0 R\n", maskObject);
870     if (softMaskObject > 0)
871         xprintf("/SMask %d 0 R\n", softMaskObject);
872
873     int lenobj = requestObject();
874     xprintf("/Length %d 0 R\n", lenobj);
875     if (interpolateImages)
876         xprintf("/Interpolate true\n");
877     int len = 0;
878     if (dct) {
879         //qDebug() << "DCT";
880         xprintf("/Filter /DCTDecode\n>>\nstream\n");
881         write(data);
882         len = data.length();
883     } else {
884         if (do_compress)
885             xprintf("/Filter /FlateDecode\n>>\nstream\n");
886         else
887             xprintf(">>\nstream\n");
888         len = writeCompressed(data);
889     }
890     xprintf("endstream\n"
891             "endobj\n");
892     addXrefEntry(lenobj);
893     xprintf("%d\n"
894             "endobj\n", len);
895     return image;
896 }
897
898
899 void QPdfEnginePrivate::writeHeader()
900 {
901     addXrefEntry(0,false);
902
903     xprintf("%%PDF-1.4\n");
904
905     writeInfo();
906
907     catalog = addXrefEntry(-1);
908     pageRoot = requestObject();
909     xprintf("<<\n"
910             "/Type /Catalog\n"
911             "/Pages %d 0 R\n"
912             ">>\n"
913             "endobj\n", pageRoot);
914
915     // graphics state
916     graphicsState = addXrefEntry(-1);
917     xprintf("<<\n"
918             "/Type /ExtGState\n"
919             "/SA true\n"
920             "/SM 0.02\n"
921             "/ca 1.0\n"
922             "/CA 1.0\n"
923             "/AIS false\n"
924             "/SMask /None"
925             ">>\n"
926             "endobj\n");
927
928     // color space for pattern
929     patternColorSpace = addXrefEntry(-1);
930     xprintf("[/Pattern /DeviceRGB]\n"
931             "endobj\n");
932 }
933
934 void QPdfEnginePrivate::writeInfo()
935 {
936     info = addXrefEntry(-1);
937     xprintf("<<\n/Title ");
938     printString(title);
939     xprintf("\n/Creator ");
940     printString(creator);
941     xprintf("\n/Producer ");
942     printString(QString::fromLatin1("Qt " QT_VERSION_STR " (C) 2011 Nokia Corporation and/or its subsidiary(-ies)"));
943     QDateTime now = QDateTime::currentDateTime().toUTC();
944     QTime t = now.time();
945     QDate d = now.date();
946     xprintf("\n/CreationDate (D:%d%02d%02d%02d%02d%02d)\n",
947             d.year(),
948             d.month(),
949             d.day(),
950             t.hour(),
951             t.minute(),
952             t.second());
953     xprintf(">>\n"
954             "endobj\n");
955 }
956
957 void QPdfEnginePrivate::writePageRoot()
958 {
959     addXrefEntry(pageRoot);
960
961     xprintf("<<\n"
962             "/Type /Pages\n"
963             "/Kids \n"
964             "[\n");
965     int size = pages.size();
966     for (int i = 0; i < size; ++i)
967         xprintf("%d 0 R\n", pages[i]);
968     xprintf("]\n");
969
970     //xprintf("/Group <</S /Transparency /I true /K false>>\n");
971     xprintf("/Count %d\n", pages.size());
972
973     xprintf("/ProcSet [/PDF /Text /ImageB /ImageC]\n"
974             ">>\n"
975             "endobj\n");
976 }
977
978
979 void QPdfEnginePrivate::embedFont(QFontSubset *font)
980 {
981     //qDebug() << "embedFont" << font->object_id;
982     int fontObject = font->object_id;
983     QByteArray fontData = font->toTruetype();
984 #ifdef FONT_DUMP
985     static int i = 0;
986     QString fileName("font%1.ttf");
987     fileName = fileName.arg(i++);
988     QFile ff(fileName);
989     ff.open(QFile::WriteOnly);
990     ff.write(fontData);
991     ff.close();
992 #endif
993
994     int fontDescriptor = requestObject();
995     int fontstream = requestObject();
996     int cidfont = requestObject();
997     int toUnicode = requestObject();
998
999     QFontEngine::Properties properties = font->fontEngine->properties();
1000
1001     {
1002         qreal scale = 1000/properties.emSquare.toReal();
1003         addXrefEntry(fontDescriptor);
1004         QByteArray descriptor;
1005         QPdf::ByteStream s(&descriptor);
1006         s << "<< /Type /FontDescriptor\n"
1007             "/FontName /Q";
1008         int tag = fontDescriptor;
1009         for (int i = 0; i < 5; ++i) {
1010             s << (char)('A' + (tag % 26));
1011             tag /= 26;
1012         }
1013         s <<  '+' << properties.postscriptName << "\n"
1014             "/Flags " << 4 << "\n"
1015             "/FontBBox ["
1016           << properties.boundingBox.x()*scale
1017           << -(properties.boundingBox.y() + properties.boundingBox.height())*scale
1018           << (properties.boundingBox.x() + properties.boundingBox.width())*scale
1019           << -properties.boundingBox.y()*scale  << "]\n"
1020             "/ItalicAngle " << properties.italicAngle.toReal() << "\n"
1021             "/Ascent " << properties.ascent.toReal()*scale << "\n"
1022             "/Descent " << -properties.descent.toReal()*scale << "\n"
1023             "/CapHeight " << properties.capHeight.toReal()*scale << "\n"
1024             "/StemV " << properties.lineWidth.toReal()*scale << "\n"
1025             "/FontFile2 " << fontstream << "0 R\n"
1026             ">> endobj\n";
1027         write(descriptor);
1028     }
1029     {
1030         addXrefEntry(fontstream);
1031         QByteArray header;
1032         QPdf::ByteStream s(&header);
1033
1034         int length_object = requestObject();
1035         s << "<<\n"
1036             "/Length1 " << fontData.size() << "\n"
1037             "/Length " << length_object << "0 R\n";
1038         if (do_compress)
1039             s << "/Filter /FlateDecode\n";
1040         s << ">>\n"
1041             "stream\n";
1042         write(header);
1043         int len = writeCompressed(fontData);
1044         write("endstream\n"
1045               "endobj\n");
1046         addXrefEntry(length_object);
1047         xprintf("%d\n"
1048                 "endobj\n", len);
1049     }
1050     {
1051         addXrefEntry(cidfont);
1052         QByteArray cid;
1053         QPdf::ByteStream s(&cid);
1054         s << "<< /Type /Font\n"
1055             "/Subtype /CIDFontType2\n"
1056             "/BaseFont /" << properties.postscriptName << "\n"
1057             "/CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >>\n"
1058             "/FontDescriptor " << fontDescriptor << "0 R\n"
1059             "/CIDToGIDMap /Identity\n"
1060           << font->widthArray() <<
1061             ">>\n"
1062             "endobj\n";
1063         write(cid);
1064     }
1065     {
1066         addXrefEntry(toUnicode);
1067         QByteArray touc = font->createToUnicodeMap();
1068         xprintf("<< /Length %d >>\n"
1069                 "stream\n", touc.length());
1070         write(touc);
1071         write("endstream\n"
1072               "endobj\n");
1073     }
1074     {
1075         addXrefEntry(fontObject);
1076         QByteArray font;
1077         QPdf::ByteStream s(&font);
1078         s << "<< /Type /Font\n"
1079             "/Subtype /Type0\n"
1080             "/BaseFont /" << properties.postscriptName << "\n"
1081             "/Encoding /Identity-H\n"
1082             "/DescendantFonts [" << cidfont << "0 R]\n"
1083             "/ToUnicode " << toUnicode << "0 R"
1084             ">>\n"
1085             "endobj\n";
1086         write(font);
1087     }
1088 }
1089
1090
1091 void QPdfEnginePrivate::writeFonts()
1092 {
1093     for (QHash<QFontEngine::FaceId, QFontSubset *>::iterator it = fonts.begin(); it != fonts.end(); ++it) {
1094         embedFont(*it);
1095         delete *it;
1096     }
1097     fonts.clear();
1098 }
1099
1100 void QPdfEnginePrivate::writePage()
1101 {
1102     if (pages.empty())
1103         return;
1104
1105     *currentPage << "Q Q\n";
1106
1107     uint pageStream = requestObject();
1108     uint pageStreamLength = requestObject();
1109     uint resources = requestObject();
1110     uint annots = requestObject();
1111
1112     addXrefEntry(pages.last());
1113     xprintf("<<\n"
1114             "/Type /Page\n"
1115             "/Parent %d 0 R\n"
1116             "/Contents %d 0 R\n"
1117             "/Resources %d 0 R\n"
1118             "/Annots %d 0 R\n"
1119             "/MediaBox [0 0 %d %d]\n"
1120             ">>\n"
1121             "endobj\n",
1122             pageRoot, pageStream, resources, annots,
1123             // make sure we use the pagesize from when we started the page, since the user may have changed it
1124             currentPage->pageSize.width(), currentPage->pageSize.height());
1125
1126     addXrefEntry(resources);
1127     xprintf("<<\n"
1128             "/ColorSpace <<\n"
1129             "/PCSp %d 0 R\n"
1130             "/CSp /DeviceRGB\n"
1131             "/CSpg /DeviceGray\n"
1132             ">>\n"
1133             "/ExtGState <<\n"
1134             "/GSa %d 0 R\n",
1135             patternColorSpace, graphicsState);
1136
1137     for (int i = 0; i < currentPage->graphicStates.size(); ++i)
1138         xprintf("/GState%d %d 0 R\n", currentPage->graphicStates.at(i), currentPage->graphicStates.at(i));
1139     xprintf(">>\n");
1140
1141     xprintf("/Pattern <<\n");
1142     for (int i = 0; i < currentPage->patterns.size(); ++i)
1143         xprintf("/Pat%d %d 0 R\n", currentPage->patterns.at(i), currentPage->patterns.at(i));
1144     xprintf(">>\n");
1145
1146     xprintf("/Font <<\n");
1147     for (int i = 0; i < currentPage->fonts.size();++i)
1148         xprintf("/F%d %d 0 R\n", currentPage->fonts[i], currentPage->fonts[i]);
1149     xprintf(">>\n");
1150
1151     xprintf("/XObject <<\n");
1152     for (int i = 0; i<currentPage->images.size(); ++i) {
1153         xprintf("/Im%d %d 0 R\n", currentPage->images.at(i), currentPage->images.at(i));
1154     }
1155     xprintf(">>\n");
1156
1157     xprintf(">>\n"
1158             "endobj\n");
1159
1160     addXrefEntry(annots);
1161     xprintf("[ ");
1162     for (int i = 0; i<currentPage->annotations.size(); ++i) {
1163         xprintf("%d 0 R ", currentPage->annotations.at(i));
1164     }
1165     xprintf("]\nendobj\n");
1166
1167     addXrefEntry(pageStream);
1168     xprintf("<<\n"
1169             "/Length %d 0 R\n", pageStreamLength); // object number for stream length object
1170     if (do_compress)
1171         xprintf("/Filter /FlateDecode\n");
1172
1173     xprintf(">>\n");
1174     xprintf("stream\n");
1175     QIODevice *content = currentPage->stream();
1176     int len = writeCompressed(content);
1177     xprintf("endstream\n"
1178             "endobj\n");
1179
1180     addXrefEntry(pageStreamLength);
1181     xprintf("%d\nendobj\n",len);
1182 }
1183
1184 void QPdfEnginePrivate::writeTail()
1185 {
1186     writePage();
1187     writeFonts();
1188     writePageRoot();
1189     addXrefEntry(xrefPositions.size(),false);
1190     xprintf("xref\n"
1191             "0 %d\n"
1192             "%010d 65535 f \n", xrefPositions.size()-1, xrefPositions[0]);
1193
1194     for (int i = 1; i < xrefPositions.size()-1; ++i)
1195         xprintf("%010d 00000 n \n", xrefPositions[i]);
1196
1197     xprintf("trailer\n"
1198             "<<\n"
1199             "/Size %d\n"
1200             "/Info %d 0 R\n"
1201             "/Root %d 0 R\n"
1202             ">>\n"
1203             "startxref\n%d\n"
1204             "%%%%EOF\n",
1205             xrefPositions.size()-1, info, catalog, xrefPositions.last());
1206 }
1207
1208 int QPdfEnginePrivate::addXrefEntry(int object, bool printostr)
1209 {
1210     if (object < 0)
1211         object = requestObject();
1212
1213     if (object>=xrefPositions.size())
1214         xrefPositions.resize(object+1);
1215
1216     xrefPositions[object] = streampos;
1217     if (printostr)
1218         xprintf("%d 0 obj\n",object);
1219
1220     return object;
1221 }
1222
1223 void QPdfEnginePrivate::printString(const QString &string) {
1224     // The 'text string' type in PDF is encoded either as PDFDocEncoding, or
1225     // Unicode UTF-16 with a Unicode byte order mark as the first character
1226     // (0xfeff), with the high-order byte first.
1227     QByteArray array("(\xfe\xff");
1228     const ushort *utf16 = string.utf16();
1229     
1230     for (int i=0; i < string.size(); ++i) {
1231         char part[2] = {char((*(utf16 + i)) >> 8), char((*(utf16 + i)) & 0xff)};
1232         for(int j=0; j < 2; ++j) {
1233             if (part[j] == '(' || part[j] == ')' || part[j] == '\\')
1234                 array.append('\\');
1235             array.append(part[j]);
1236         }
1237     }
1238     array.append(")");
1239     write(array);
1240 }
1241
1242 QT_END_NAMESPACE
1243
1244 #endif // QT_NO_PRINTER