Replace 'i < len-1 && func(i+1)' by 'i+1 < len && func(i+1)'
[profile/ivi/qtbase.git] / src / gui / painting / qprintengine_ps.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 "qplatformdefs.h"
43
44 #include <private/qprintengine_ps_p.h>
45 #include <private/qpainter_p.h>
46 #include <private/qfontengine_p.h>
47 #include <private/qpaintengine_p.h>
48 #include <private/qpdf_p.h>
49
50 #ifndef QT_NO_PRINTER
51
52 #include "qprinter.h"
53 #include "qpainter.h"
54 #include "qapplication.h"
55 #include "qpixmap.h"
56 #include "qimage.h"
57 #include "qdatetime.h"
58 #include "qstring.h"
59 #include "qbytearray.h"
60 #include "qhash.h"
61 #include "qbuffer.h"
62 #include "qsettings.h"
63 #include "qmap.h"
64 #include "qbitmap.h"
65 #include "qregion.h"
66 #include "qimagewriter.h"
67 #include <private/qpainterpath_p.h>
68 #include <qdebug.h>
69 #include <private/qdrawhelper_p.h>
70 #include <private/qmutexpool_p.h>
71
72 #ifndef Q_OS_WIN
73 #include <unistd.h>
74 #endif
75 #include <stdlib.h>
76 #include <limits.h>
77
78 QT_BEGIN_NAMESPACE
79
80 static bool qt_gen_epsf = false;
81
82 void qt_generate_epsf(bool b)
83 {
84     qt_gen_epsf = b;
85 }
86
87 static const char *const ps_header =
88 "/BD{bind def}bind def/d2{dup dup}BD/ED{exch def}BD/D0{0 ED}BD/F{setfont}BD\n"
89 "/RL{rlineto}BD/CM{currentmatrix}BD/SM{setmatrix}BD/TR{translate}BD/SD\n"
90 "{setdash}BD/SC{aload pop setrgbcolor}BD/CR{currentfile read pop}BD/i{index}\n"
91 "BD/scs{setcolorspace}BD/DB{dict dup begin}BD/DE{end def}BD/ie{ifelse}BD/gs\n"
92 "{gsave}BD/gr{grestore}BD/w{setlinewidth}BD/d{setdash}BD/J{setlinecap}BD/j\n"
93 "{setlinejoin}BD/scn{3 array astore/BCol exch def}BD/SCN{3 array astore/PCol\n"
94 "exch def}BD/cm{6 array astore concat}BD/m{moveto}BD/l{lineto}BD/c{curveto}BD\n"
95 "/h{closepath}BD/W{clip}BD/W*{eoclip}BD/n{newpath}BD/q{gsave 10 dict begin}BD\n"
96 "/Q{end grestore}BD/re{4 2 roll m dup 0 exch RL exch 0 RL 0 exch neg RL h}BD\n"
97 "/S{gs PCol SC stroke gr n}BD/BT{gsave 10 dict begin/_m matrix CM def BCol\n"
98 "SC}BD/ET{end grestore}BD/Tf{/_fs ED findfont[_fs 0 0 _fs 0 0]makefont F}BD\n"
99 "/Tm{6 array astore concat}BD/Td{translate}BD/Tj{0 0 m show}BD/BDC{pop pop}BD\n"
100 "/EMC{}BD/BSt 0 def/WFi false def/BCol[1 1 1]def/PCol[0 0 0]def/BDArr[0.94\n"
101 "0.88 0.63 0.50 0.37 0.12 0.06]def/level3{/languagelevel where{pop\n"
102 "languagelevel 3 ge}{false}ie}BD/QCIgray D0/QCIcolor D0/QCIindex D0/QCI{\n"
103 "/colorimage where{pop false 3 colorimage}{exec/QCIcolor ED/QCIgray QCIcolor\n"
104 "length 3 idiv string def 0 1 QCIcolor length 3 idiv 1 sub{/QCIindex ED/_x\n"
105 "QCIindex 3 mul def QCIgray QCIindex QCIcolor _x get 0.30 mul QCIcolor _x 1\n"
106 "add get 0.59 mul QCIcolor _x 2 add get 0.11 mul add add cvi put}for QCIgray\n"
107 "image}ie}BD/di{gs TR 1 i 1 eq{pop pop false 3 1 roll BCol SC imagemask}{dup\n"
108 "false ne{level3}{false}ie{/_ma ED 8 eq{/_dc[0 1]def/DeviceGray}{/_dc[0 1 0 1\n"
109 "0 1]def/DeviceRGB}ie scs/_im ED/_mt ED/_h ED/_w ED <</ImageType 3/DataDict\n"
110 "<</ImageType 1/Width _w/Height _h/ImageMatrix _mt/DataSource _im\n"
111 "/BitsPerComponent 8/Decode _dc >>/MaskDict <</ImageType 1/Width _w/Height _h\n"
112 "/ImageMatrix _mt/DataSource _ma/BitsPerComponent 1/Decode[0 1]>>\n"
113 "/InterleaveType 3 >> image}{pop 8 4 1 roll 8 eq{image}{QCI}ie}ie}ie gr}BD/BF\n"
114 "{gs BSt 1 eq{BCol SC WFi{fill}{eofill}ie}if BSt 2 ge BSt 8 le and{BDArr BSt\n"
115 "2 sub get/_sc ED BCol{1. exch sub _sc mul 1. exch sub}forall 3 array astore\n"
116 "SC WFi{fill}{eofill}ie}if BSt 9 ge BSt 14 le and{WFi{W}{W*}ie pathbbox 3 i 3\n"
117 "i TR 4 2 roll 3 2 roll exch sub/_h ED sub/_w ED BCol SC 0.3 w n BSt 9 eq BSt\n"
118 "11 eq or{0 4 _h{dup 0 exch m _w exch l}for}if BSt 10 eq BSt 11 eq or{0 4 _w{\n"
119 "dup 0 m _h l}for}if BSt 12 eq BSt 14 eq or{_w _h gt{0 6 _w _h add{dup 0 m _h\n"
120 "sub _h l}for}{0 6 _w _h add{dup 0 exch m _w sub _w exch l}for}ie}if BSt 13\n"
121 "eq BSt 14 eq or{_w _h gt{0 6 _w _h add{dup _h m _h sub 0 l}for}{0 6 _w _h\n"
122 "add{dup _w exch m _w sub 0 exch l}for}ie}if stroke}if BSt 15 eq{}if BSt 24\n"
123 "eq{}if gr}BD/f{/WFi true def BF n}BD/f*{/WFi false def BF n}BD/B{/WFi true\n"
124 "def BF S n}BD/B*{/WFi false def BF S n}BD/QI{/C save def pageinit q n}BD/QP{\n"
125 "Q C restore showpage}BD/SPD{/setpagedevice where{<< 3 1 roll >>\n"
126 "setpagedevice}{pop pop}ie}BD/T1AddMapping{10 dict begin/glyphs ED/fnt ED\n"
127 "/current fnt/NumGlyphs get def/CMap fnt/CMap get def 0 1 glyphs length 1 sub\n"
128 "{glyphs exch get/gn ED current dup 256 mod/min ED 256 idiv/maj ED CMap dup\n"
129 "maj get dup null eq{pop 256 array 0 1 255{1 i exch/.notdef put}for}if dup\n"
130 "min gn put maj exch put/current current 1 add def}for fnt/CMap CMap put fnt\n"
131 "/NumGlyphs current put end}def/T1AddGlyphs{10 dict begin/glyphs ED/fnt ED\n"
132 "/current fnt/NumGlyphs get def/CMap fnt/CMap get def/CharStrings fnt\n"
133 "/CharStrings get def 0 1 glyphs length 2 idiv 1 sub{2 mul dup glyphs exch\n"
134 "get/gn ED 1 add glyphs exch get/cs ED current dup 256 mod/min ED 256 idiv\n"
135 "/maj ED CMap dup maj get dup null eq{pop 256 array 0 1 255{1 i exch/.notdef\n"
136 "put}for}if dup min gn put maj exch put CharStrings gn cs put/current current\n"
137 "1 add def}for fnt/CharStrings CharStrings put fnt/CMap CMap put fnt\n"
138 "/NumGlyphs current put end}def/StringAdd{1 i length 1 i length add string 3\n"
139 "1 roll 2 i 0 3 i putinterval 2 i 2 i length 2 i putinterval pop pop}def\n"
140 "/T1Setup{10 dict begin dup/FontName ED (-Base) StringAdd cvx cvn/Font ED\n"
141 "/MaxPage Font/NumGlyphs get 1 sub 256 idiv def/FDepVector MaxPage 1 add\n"
142 "array def/Encoding MaxPage 1 add array def 0 1 MaxPage{dup Encoding exch dup\n"
143 "put dup/Page ED FontName (-) StringAdd exch 20 string cvs StringAdd cvn Font\n"
144 "0 dict copy d2/CMap get Page get/Encoding exch put definefont FDepVector\n"
145 "exch Page exch put}for FontName cvn <</FontType 0/FMapType 2/FontMatrix[1 0\n"
146 "0 1 0 0]/Encoding Encoding/FDepVector FDepVector >> definefont pop end}def\n";
147
148
149
150 // ------------------------------End of static data ----------------------------------
151
152 // make sure DSC comments are not longer than 255 chars per line.
153 static QByteArray wrapDSC(const QByteArray &str)
154 {
155     QByteArray dsc = str.simplified();
156     const int wrapAt = 254;
157     QByteArray wrapped;
158     if (dsc.length() < wrapAt)
159         wrapped = dsc;
160     else {
161         wrapped = dsc.left(wrapAt);
162         QByteArray tmp = dsc.mid(wrapAt);
163         while (tmp.length() > wrapAt-3) {
164             wrapped += "\n%%+" + tmp.left(wrapAt-3);
165             tmp = tmp.mid(wrapAt-3);
166         }
167         wrapped += "\n%%+" + tmp;
168     }
169     return wrapped + '\n';
170 }
171
172 // ----------------------------- Internal class declarations -----------------------------
173
174 QPSPrintEnginePrivate::QPSPrintEnginePrivate(QPrinter::PrinterMode m)
175     : QPdfBaseEnginePrivate(m),
176       printerState(QPrinter::Idle), hugeDocument(false), headerDone(false)
177 {
178     useAlphaEngine = true;
179     postscript = true;
180
181     firstPage = true;
182
183 #ifndef QT_NO_SETTINGS
184     QSettings settings(QSettings::UserScope, QLatin1String("Trolltech"));
185     settings.beginGroup(QLatin1String("Qt"));
186     embedFonts = settings.value(QLatin1String("embedFonts"), true).toBool();
187 #else
188     embedFonts = true;
189 #endif
190 }
191
192 QPSPrintEnginePrivate::~QPSPrintEnginePrivate()
193 {
194 }
195
196 QT_BEGIN_INCLUDE_NAMESPACE
197 #include <qdebug.h>
198 QT_END_INCLUDE_NAMESPACE
199
200 static void ps_r7(QPdf::ByteStream& stream, const char * s, int l)
201 {
202     int i = 0;
203     uchar line[84];
204     int col = 0;
205
206     while(i < l) {
207         line[col++] = s[i++];
208         if (i < l - 1 && col >= 76) {
209             line[col++] = '\n';
210             line[col++] = '\0';
211             stream << (const char *)line;
212             col = 0;
213         }
214     }
215     if (col > 0) {
216         while((col&3) != 0)
217             line[col++] = '%'; // use a comment as padding
218         line[col++] = '\n';
219         line[col++] = '\0';
220         stream << (const char *)line;
221     }
222 }
223
224 static QByteArray runlengthEncode(const QByteArray &input)
225 {
226     if (!input.length())
227         return input;
228
229     const char *data = input.constData();
230
231     QByteArray out;
232     int start = 0;
233     char last = *data;
234
235     enum State {
236         Undef,
237         Equal,
238         Diff
239     };
240     State state = Undef;
241
242     int i = 1;
243     int written = 0;
244     while (1) {
245         bool flush = (i == input.size());
246         if (!flush) {
247             switch(state) {
248             case Undef:
249                 state = (last == data[i]) ? Equal : Diff;
250                 break;
251             case Equal:
252                 if (data[i] != last)
253                     flush = true;
254                 break;
255             case Diff:
256                 if (data[i] == last) {
257                     --i;
258                     flush = true;
259                 }
260             }
261         }
262         if (flush || i - start == 128) {
263             int size = i - start;
264             if (state == Equal) {
265                 out.append((char)(uchar)(257-size));
266                 out.append(last);
267                 written += size;
268             } else {
269                 out.append((char)(uchar)size-1);
270                 while (start < i)
271                     out.append(data[start++]);
272                 written += size;
273             }
274             state = Undef;
275             start = i;
276             if (i == input.size())
277                 break;
278         }
279         last = data[i];
280         ++i;
281     };
282     out.append((char)(uchar)128);
283     return out;
284 }
285
286 enum format {
287     Raw,
288     Runlength,
289     DCT
290 };
291 static const char *const filters[3] = {
292     " ",
293     "/RunLengthDecode filter ",
294     "/DCTDecode filter "
295 };
296
297 static QByteArray compressHelper(const QImage &image, bool gray, int *format)
298 {
299     // we can't use premultiplied here
300     QByteArray pixelData;
301     int depth = image.depth();
302
303     Q_ASSERT(image.format() != QImage::Format_ARGB32_Premultiplied);
304
305     if (depth != 1 && !gray && QImageWriter::supportedImageFormats().contains("jpeg")) {
306         QBuffer buffer(&pixelData);
307         QImageWriter writer(&buffer, "jpeg");
308         writer.setQuality(94);
309         writer.write(image);
310         *format = DCT;
311     } else {
312         int width = image.width();
313         int height = image.height();
314         int size = width*height;
315
316         if (depth == 1)
317             size = (width+7)/8*height;
318         else if (!gray)
319             size = size*3;
320
321         pixelData.resize(size);
322         uchar *pixel = (uchar *)pixelData.data();
323         int i = 0;
324         if (depth == 1) {
325             QImage::Format format = image.format();
326             memset(pixel, 0xff, size);
327             for(int y=0; y < height; y++) {
328                 const uchar * s = image.scanLine(y);
329                 for(int x=0; x < width; x++) {
330                     // need to copy bit for bit...
331                     bool b = (format == QImage::Format_MonoLSB) ?
332                              (*(s + (x >> 3)) >> (x & 7)) & 1 :
333                              (*(s + (x >> 3)) << (x & 7)) & 0x80 ;
334                     if (b)
335                         pixel[i >> 3] ^= (0x80 >> (i & 7));
336                     i++;
337                 }
338                 // we need to align to 8 bit here
339                 i = (i+7) & 0xffffff8;
340             }
341         } else if (depth == 8) {
342             for(int y=0; y < height; y++) {
343                 const uchar * s = image.scanLine(y);
344                 for(int x=0; x < width; x++) {
345                     QRgb rgb = image.color(s[x]);
346                     if (gray) {
347                         pixel[i] = (unsigned char) qGray(rgb);
348                         i++;
349                     } else {
350                         pixel[i] = (unsigned char) qRed(rgb);
351                         pixel[i+1] = (unsigned char) qGreen(rgb);
352                         pixel[i+2] = (unsigned char) qBlue(rgb);
353                         i += 3;
354                     }
355                 }
356             }
357         } else {
358             for(int y=0; y < height; y++) {
359                 QRgb * s = (QRgb*)(image.scanLine(y));
360                 for(int x=0; x < width; x++) {
361                     QRgb rgb = (*s++);
362                     if (gray) {
363                         pixel[i] = (unsigned char) qGray(rgb);
364                         i++;
365                     } else {
366                         pixel[i] = (unsigned char) qRed(rgb);
367                         pixel[i+1] = (unsigned char) qGreen(rgb);
368                         pixel[i+2] = (unsigned char) qBlue(rgb);
369                         i += 3;
370                     }
371                 }
372             }
373         }
374         *format = Raw;
375         if (depth == 1) {
376             pixelData = runlengthEncode(pixelData);
377             *format = Runlength;
378         }
379     }
380     QByteArray outarr = QPdf::ascii85Encode(pixelData);
381     return outarr;
382 }
383
384 void QPSPrintEnginePrivate::drawImageHelper(qreal x, qreal y, qreal w, qreal h, const QImage &img,
385                                             const QImage &mask, bool gray, qreal scaleX, qreal scaleY)
386 {
387     Q_UNUSED(h);
388     Q_UNUSED(w);
389     int width = img.width();
390     int height = img.height();
391
392     QByteArray out;
393     int size = 0;
394     const char *bits;
395
396     if (!mask.isNull()) {
397         int format;
398         out = compressHelper(mask, true, &format);
399         size = (width+7)/8*height;
400         *currentPage << "/mask currentfile/ASCII85Decode filter"
401                      << filters[format]
402                      << size << " string readstring\n";
403         ps_r7(*currentPage, out, out.size());
404         *currentPage << " pop def\n";
405     }
406     if (img.depth() == 1) {
407         size = (width+7)/8*height;
408         bits = "1 ";
409     } else if (gray) {
410         size = width*height;
411         bits = "8 ";
412     } else {
413         size = width*height*3;
414         bits = "24 ";
415     }
416
417     int format;
418     out = compressHelper(img, gray, &format);
419     *currentPage << "/sl currentfile/ASCII85Decode filter"
420                  << filters[format]
421                  << size << " string readstring\n";
422     ps_r7(*currentPage, out, out.size());
423     *currentPage << " pop def\n";
424     *currentPage << width << ' ' << height << '[' << scaleX << " 0 0 " << scaleY << " 0 0]sl "
425                  << bits << (!mask.isNull() ? "mask " : "false ")
426                  << x << ' ' << y << " di\n";
427 }
428
429
430 void QPSPrintEnginePrivate::drawImage(qreal x, qreal y, qreal w, qreal h,
431                                       const QImage &image, const QImage &msk)
432 {
433     if (!w || !h || image.isNull()) return;
434
435     QImage img(image);
436     QImage mask(msk);
437
438     if (image.format() == QImage::Format_ARGB32_Premultiplied)
439         img = image.convertToFormat(QImage::Format_ARGB32);
440
441     if (!msk.isNull() && msk.format() == QImage::Format_ARGB32_Premultiplied)
442         mask = msk.convertToFormat(QImage::Format_ARGB32);
443
444     int width  = img.width();
445     int height = img.height();
446     qreal scaleX = width/w;
447     qreal scaleY = height/h;
448
449     bool gray = (colorMode == QPrinter::GrayScale) || img.allGray();
450     int splitSize = 21830 * (gray ? 3 : 1);
451     if (width * height > splitSize) { // 65535/3, tolerance for broken printers
452         int images, subheight;
453         images = (width * height + splitSize - 1) / splitSize;
454         subheight = (height + images-1) / images;
455         while (subheight * width > splitSize) {
456             images++;
457             subheight = (height + images-1) / images;
458         }
459         int suby = 0;
460         const QImage constImg(img);
461         const QImage constMask(mask);
462         while(suby < height) {
463             qreal subImageHeight = qMin(subheight, height-suby);
464             const QImage subImage(constImg.scanLine(suby), width, subImageHeight,
465                                   constImg.bytesPerLine(), constImg.format());
466             const QImage subMask = mask.isNull() ? mask : QImage(constMask.scanLine(suby), width, subImageHeight,
467                                                                  constMask.bytesPerLine(), constMask.format());
468             drawImageHelper(x, y + suby/scaleY, w, subImageHeight/scaleY,
469                             subImage, subMask, gray, scaleX, scaleY);
470             suby += subheight;
471         }
472     } else {
473         drawImageHelper(x, y, width, height, img, mask, gray, scaleX, scaleY);
474     }
475 }
476
477 void QPSPrintEnginePrivate::emitHeader(bool finished)
478 {
479     QPSPrintEngine *q = static_cast<QPSPrintEngine *>(q_ptr);
480     QPrinter *printer = static_cast<QPrinter*>(pdev);
481
482     if (creator.isEmpty())
483         creator = QLatin1String("Qt " QT_VERSION_STR);
484
485     QByteArray header;
486     QPdf::ByteStream s(&header);
487
488     qreal scale = 72. / ((qreal) q->metric(QPaintDevice::PdmDpiY));
489     QRect pageRect = this->pageRect();
490     QRect paperRect = this->paperRect();
491     int mtop = pageRect.top() - paperRect.top();
492     int mleft = pageRect.left() - paperRect.left();
493     int mbottom = paperRect.bottom() - pageRect.bottom();
494     int mright = paperRect.right() - pageRect.right();
495     int width = pageRect.width();
496     int height = pageRect.height();
497     if (finished && pageCount == 1 && copies == 1 &&
498         ((fullPage && qt_gen_epsf) || (outputFileName.endsWith(QLatin1String(".eps")))))
499     {
500         // According to the EPSF 3.0 spec it is required that the PS
501         // version is PS-Adobe-3.0
502         s << "%!PS-Adobe-3.0";
503         if (!boundingBox.isValid())
504             boundingBox.setRect(0, 0, width, height);
505         if (orientation == QPrinter::Landscape) {
506             if (!fullPage)
507                 boundingBox.translate(-mleft, -mtop);
508             s << " EPSF-3.0\n%%BoundingBox: "
509               << int((printer->height() - boundingBox.bottom())*scale) // llx
510               << int((printer->width() - boundingBox.right())*scale - 1) // lly
511               << int((printer->height() - boundingBox.top())*scale + 1) // urx
512               << int((printer->width() - boundingBox.left())*scale); // ury
513         } else {
514             if (!fullPage)
515                 boundingBox.translate(mleft, -mtop);
516             s << " EPSF-3.0\n%%BoundingBox: "
517               << int((boundingBox.left())*scale)
518               << int((printer->height() - boundingBox.bottom())*scale - 1)
519               << int((boundingBox.right())*scale + 1)
520               << int((printer->height() - boundingBox.top())*scale);
521         }
522     } else {
523         s << "%!PS-Adobe-1.0";
524         int w = width + (fullPage ? 0 : mleft + mright);
525         int h = height + (fullPage ? 0 : mtop + mbottom);
526         w = (int)(w*scale);
527         h = (int)(h*scale);
528         // set a bounding box according to the DSC
529         if (orientation == QPrinter::Landscape)
530             s << "\n%%BoundingBox: 0 0 " << h << w;
531         else
532             s << "\n%%BoundingBox: 0 0 " << w << h;
533     }
534     s << '\n' << wrapDSC("%%Creator: " + creator.toUtf8());
535     if (!title.isEmpty())
536         s << wrapDSC("%%Title: " + title.toUtf8());
537 #ifndef QT_NO_DATESTRING
538     s << "%%CreationDate: " << QDateTime::currentDateTime().toString().toUtf8();
539 #endif
540     s << "\n%%Orientation: ";
541     if (orientation == QPrinter::Landscape)
542         s << "Landscape";
543     else
544         s << "Portrait";
545
546     s << "\n%%Pages: (atend)"
547         "\n%%DocumentFonts: (atend)"
548         "\n%%EndComments\n"
549
550         "%%BeginProlog\n"
551         "% Prolog copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).\n"
552         "% You may copy this prolog in any way that is directly related to this document.\n"
553         "% For other use of this prolog, see your licensing agreement for Qt.\n"
554       << ps_header << '\n';
555
556
557     s << "/pageinit {\n";
558     if (!fullPage) {
559         if (orientation == QPrinter::Portrait)
560             s << mleft*scale << mbottom*scale << "translate\n";
561         else
562             s << mtop*scale << mleft*scale << "translate\n";
563     }
564     if (orientation == QPrinter::Portrait) {
565         s << "% " << printer->widthMM() << '*' << printer->heightMM()
566           << "mm (portrait)\n0 " << height*scale
567           << "translate " << scale << '-' << scale << "scale } def\n";
568     } else {
569         s << "% " << printer->heightMM() << '*' << printer->widthMM()
570           << " mm (landscape)\n 90 rotate " << scale << '-' << scale << "scale } def\n";
571     }
572     s << "%%EndProlog\n";
573
574     outDevice->write(header);
575     headerDone = true;
576 }
577
578
579 void QPSPrintEnginePrivate::emitPages()
580 {
581     if (!hugeDocument) {
582         for (QHash<QFontEngine::FaceId, QFontSubset *>::const_iterator it = fonts.constBegin();
583              it != fonts.constEnd(); ++it)
584             outDevice->write((*it)->toType1());
585     }
586
587     QIODevice *content = buffer.stream();
588     // Write the page contents in chunks.
589     while (!content->atEnd()) {
590         QByteArray buf = content->read(currentPage->chunkSize());
591         if (!buf.isEmpty())
592             outDevice->write(buf);
593     }
594     content = currentPage->stream();
595     // Write the page contents in chunks.
596     while (!content->atEnd()) {
597         QByteArray buf = content->read(currentPage->chunkSize());
598         if (!buf.isEmpty())
599             outDevice->write(buf);
600     }
601     outDevice->write(trailer);
602
603     buffer.clear();
604     currentPage->clear();
605     trailer = QByteArray();
606     hugeDocument = true;
607 }
608
609
610 #ifdef Q_WS_QWS
611 static const int max_in_memory_size = 2000000;
612 #else
613 static const int max_in_memory_size = 32000000;
614 #endif
615
616 void QPSPrintEnginePrivate::flushPage(bool last)
617 {
618     if (!last && currentPage->stream()->size() == 0)
619         return;
620
621     QPdf::ByteStream e(&trailer);
622     buffer << "%%Page: "
623            << pageCount << pageCount << "\n"
624            "%%BeginPageSetup\n"
625            "QI\n";
626     if (hugeDocument) {
627         for (QHash<QFontEngine::FaceId, QFontSubset *>::const_iterator it = fonts.constBegin();
628              it != fonts.constEnd(); ++it) {
629             if (currentPage->fonts.contains((*it)->object_id)) {
630                 if ((*it)->downloaded_glyphs == 0) {
631                     buffer << (*it)->toType1();
632                     (*it)->downloaded_glyphs = 0;
633                 } else {
634                     buffer << (*it)->type1AddedGlyphs();
635                 }
636             }
637         }
638     }
639     for (int i = 0; i < currentPage->fonts.size(); ++i)
640         buffer << "(F" << QByteArray::number(currentPage->fonts.at(i)) << ") T1Setup\n";
641
642     buffer << "%%EndPageSetup\nq\n";
643     e << "\nQ QP\n";
644     if (last || hugeDocument
645         || buffer.stream()->size() + currentPage->stream()->size() > max_in_memory_size) {
646 //        qDebug("emiting header at page %d", pageCount);
647         if (!headerDone)
648             emitHeader(last);
649         emitPages();
650     } else {
651         buffer << *currentPage << e;
652         currentPage->clear();
653         trailer.clear();
654     }
655     pageCount++;
656 }
657
658 // ================ PSPrinter class ========================
659
660 QPSPrintEngine::QPSPrintEngine(QPrinter::PrinterMode m)
661     : QPdfBaseEngine(*(new QPSPrintEnginePrivate(m)),
662                      PrimitiveTransform
663                      | PatternTransform
664                      | PixmapTransform
665                      | PainterPaths
666                      | PatternBrush
667         )
668 {
669 }
670
671 static void ignoreSigPipe(bool b)
672 {
673 #ifndef QT_NO_LPR
674     static struct sigaction *users_sigpipe_handler = 0;
675     static int lockCount = 0;
676
677 #ifndef QT_NO_THREAD
678     QMutexLocker locker(QMutexPool::globalInstanceGet(&users_sigpipe_handler));
679 #endif
680
681     if (b) {
682         if (lockCount++ > 0)
683             return;
684
685         if (users_sigpipe_handler != 0)
686             return; // already ignoring sigpipe
687
688         users_sigpipe_handler = new struct sigaction;
689         struct sigaction tmp_sigpipe_handler;
690         tmp_sigpipe_handler.sa_handler = SIG_IGN;
691         sigemptyset(&tmp_sigpipe_handler.sa_mask);
692         tmp_sigpipe_handler.sa_flags = 0;
693
694         if (sigaction(SIGPIPE, &tmp_sigpipe_handler, users_sigpipe_handler) == -1) {
695             delete users_sigpipe_handler;
696             users_sigpipe_handler = 0;
697         }
698     }
699     else {
700         if (--lockCount > 0)
701             return;
702
703         if (users_sigpipe_handler == 0)
704             return; // not ignoring sigpipe
705
706         if (sigaction(SIGPIPE, users_sigpipe_handler, 0) == -1)
707             qWarning("QPSPrintEngine: Could not restore SIGPIPE handler");
708
709         delete users_sigpipe_handler;
710         users_sigpipe_handler = 0;
711     }
712 #else
713     Q_UNUSED(b);
714 #endif
715 }
716 QPSPrintEngine::~QPSPrintEngine()
717 {
718     Q_D(QPSPrintEngine);
719     if (d->fd >= 0)
720 #if defined(Q_OS_WIN) && defined(_MSC_VER) && _MSC_VER >= 1400
721         ::_close(d->fd);
722 #else
723         ::close(d->fd);
724 #endif
725 }
726
727 bool QPSPrintEngine::begin(QPaintDevice *pdev)
728 {
729     Q_D(QPSPrintEngine);
730
731     if (d->fd >= 0)
732         return true;
733
734     if (d->useAlphaEngine) {
735         QAlphaPaintEngine::begin(pdev);
736         if (!continueCall())
737             return true;
738     }
739
740     if(!QPdfBaseEngine::begin(pdev)) {
741         d->printerState = QPrinter::Error;
742         return false;
743     }
744
745     d->pageCount = 1;                // initialize state
746
747     d->pen = QPen(Qt::black);
748     d->brush = Qt::NoBrush;
749     d->hasPen = true;
750     d->hasBrush = false;
751     d->clipEnabled = false;
752     d->allClipped = false;
753     d->boundingBox = QRect();
754     d->fontsUsed = "";
755     d->hugeDocument = false;
756     d->simplePen = false;
757
758     setActive(true);
759     d->printerState = QPrinter::Active;
760
761     newPage();
762
763     return true;
764 }
765
766 bool QPSPrintEngine::end()
767 {
768     Q_D(QPSPrintEngine);
769
770     if (d->useAlphaEngine) {
771         QAlphaPaintEngine::end();
772         if (!continueCall())
773             return true;
774     }
775
776     // we're writing to lp/lpr through a pipe, we don't want to crash with SIGPIPE
777     // if lp/lpr dies
778     ignoreSigPipe(true);
779     d->flushPage(true);
780     QByteArray trailer;
781     QPdf::ByteStream s(&trailer);
782     s << "%%Trailer\n"
783          "%%Pages: " << d->pageCount - 1 << '\n' <<
784         wrapDSC("%%DocumentFonts: " + d->fontsUsed);
785     s << "%%EOF\n";
786     d->outDevice->write(trailer);
787
788     QPdfBaseEngine::end();
789     ignoreSigPipe(false);
790
791     d->firstPage = true;
792     d->headerDone = false;
793
794     setActive(false);
795     d->printerState = QPrinter::Idle;
796     d->pdev = 0;
797
798     return true;
799 }
800
801 void QPSPrintEngine::setBrush()
802 {
803     Q_D(QPSPrintEngine);
804 #if 0
805     bool specifyColor;
806     int gStateObject = 0;
807     int patternObject = d->addBrushPattern(brush, d->stroker.matrix, brushOrigin, &specifyColor, &gStateObject);
808
809     *d->currentPage << (patternObject ? "/PCSp cs " : "/CSp cs ");
810     if (specifyColor) {
811         QColor rgba = brush.color();
812         *d->currentPage << rgba.redF()
813                         << rgba.greenF()
814                         << rgba.blueF();
815     }
816     if (patternObject)
817         *d->currentPage << "/Pat" << patternObject;
818     *d->currentPage << "scn\n";
819 #endif
820     QColor rgba = d->brush.color();
821     if (d->colorMode == QPrinter::GrayScale) {
822         qreal gray = qGray(rgba.rgba())/255.;
823         *d->currentPage << gray << gray << gray;
824     } else {
825         *d->currentPage << rgba.redF()
826                         << rgba.greenF()
827                         << rgba.blueF();
828     }
829     *d->currentPage << "scn\n"
830                     << "/BSt " << d->brush.style() << "def\n";
831 }
832
833 void QPSPrintEngine::drawImageInternal(const QRectF &r, QImage image, bool bitmap)
834 {
835     Q_D(QPSPrintEngine);
836     if (d->clipEnabled && d->allClipped)
837         return;
838     if (bitmap && image.depth() != 1)
839         bitmap = false;
840     QImage mask;
841     // the below is not necessary since it's handled by the alpha
842     // engine
843     if (!d->useAlphaEngine && !bitmap) {
844         if (image.format() == QImage::Format_Mono || image.format() == QImage::Format_MonoLSB)
845             image = image.convertToFormat(QImage::Format_Indexed8);
846         if (image.hasAlphaChannel()) {
847             // get better alpha dithering
848             int xscale = image.width();
849             xscale *= xscale <= 800 ? 4 : (xscale <= 1600 ? 2 : 1);
850             int yscale = image.height();
851             yscale *= yscale <= 800 ? 4 : (yscale <= 1600 ? 2 : 1);
852             image = image.scaled(xscale, yscale);
853             mask = image.createAlphaMask(Qt::OrderedAlphaDither);
854         }
855     }
856     *d->currentPage << "q\n";
857     if(!d->simplePen)
858         *d->currentPage << QPdf::generateMatrix(d->stroker.matrix);
859     QBrush b = d->brush;
860     if (image.depth() == 1) {
861         // set current pen as brush
862         d->brush = d->pen.brush();
863         setBrush();
864     }
865     d->drawImage(r.x(), r.y(), r.width(), r.height(), image, mask);
866     *d->currentPage << "Q\n";
867     d->brush = b;
868 }
869
870
871 void QPSPrintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
872                                Qt::ImageConversionFlags)
873 {
874     Q_D(QPSPrintEngine);
875
876     if (d->useAlphaEngine) {
877         QAlphaPaintEngine::drawImage(r, img, sr);
878         if (!continueCall())
879             return;
880     }
881     QImage image = img.copy(sr.toRect());
882     drawImageInternal(r, image, false);
883 }
884
885 void QPSPrintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
886 {
887     Q_D(QPSPrintEngine);
888
889     if (d->useAlphaEngine) {
890         QAlphaPaintEngine::drawPixmap(r, pm, sr);
891         if (!continueCall())
892             return;
893     }
894
895     QImage img = pm.copy(sr.toRect()).toImage();
896     drawImageInternal(r, img, true);
897 }
898
899 void QPSPrintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &p)
900 {
901     Q_D(QPSPrintEngine);
902
903     if (d->useAlphaEngine) {
904         QAlphaPaintEngine::drawTiledPixmap(r, pixmap, p);
905         if (!continueCall())
906             return;
907     }
908
909     if (d->clipEnabled && d->allClipped)
910         return;
911     // ### Optimize implementation!
912     qreal yPos = r.y();
913     qreal yOff = p.y();
914     while(yPos < r.y() + r.height()) {
915         qreal drawH = pixmap.height() - yOff;    // Cropping first row
916         if (yPos + drawH > r.y() + r.height())        // Cropping last row
917             drawH = r.y() + r.height() - yPos;
918         qreal xPos = r.x();
919         qreal xOff = p.x();
920         while(xPos < r.x() + r.width()) {
921             qreal drawW = pixmap.width() - xOff; // Cropping first column
922             if (xPos + drawW > r.x() + r.width())    // Cropping last column
923                 drawW = r.x() + r.width() - xPos;
924             // ########
925             painter()->drawPixmap(QPointF(xPos, yPos).toPoint(), pixmap,
926                                   QRectF(xOff, yOff, drawW, drawH).toRect());
927             xPos += drawW;
928             xOff = 0;
929         }
930         yPos += drawH;
931         yOff = 0;
932     }
933
934 }
935
936 bool QPSPrintEngine::newPage()
937 {
938     Q_D(QPSPrintEngine);
939
940     if (!d->firstPage && d->useAlphaEngine)
941         flushAndInit();
942
943     // we're writing to lp/lpr through a pipe, we don't want to crash with SIGPIPE
944     // if lp/lpr dies
945     ignoreSigPipe(true);
946     if (!d->firstPage)
947         d->flushPage();
948     d->firstPage = false;
949     ignoreSigPipe(false);
950
951     delete d->currentPage;
952     d->currentPage = new QPdfPage;
953     d->stroker.stream = d->currentPage;
954
955     return QPdfBaseEngine::newPage();
956 }
957
958 bool QPSPrintEngine::abort()
959 {
960     // ### abort!?!
961     return false;
962 }
963
964 QPrinter::PrinterState QPSPrintEngine::printerState() const
965 {
966     Q_D(const QPSPrintEngine);
967     return d->printerState;
968 }
969
970 QT_END_NAMESPACE
971
972 #endif // QT_NO_PRINTER