src/gui/painting/qprintengine_ps.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 **
00003 ** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.
00004 **
00005 ** This file is part of the QtGui module of the Qt Toolkit.
00006 **
00007 ** This file may be used under the terms of the GNU General Public
00008 ** License version 2.0 as published by the Free Software Foundation
00009 ** and appearing in the file LICENSE.GPL included in the packaging of
00010 ** this file.  Please review the following information to ensure GNU
00011 ** General Public Licensing requirements will be met:
00012 ** http://www.trolltech.com/products/qt/opensource.html
00013 **
00014 ** If you are unsure which license is appropriate for your use, please
00015 ** review the following information:
00016 ** http://www.trolltech.com/products/qt/licensing.html or contact the
00017 ** sales department at sales@trolltech.com.
00018 **
00019 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00020 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00021 **
00022 ****************************************************************************/
00023 
00024 #include "qplatformdefs.h"
00025 
00026 #include <private/qprintengine_ps_p.h>
00027 #include <private/qpainter_p.h>
00028 #include <private/qfontengine_p.h>
00029 #include <private/qpaintengine_p.h>
00030 #include <private/qpdf_p.h>
00031 
00032 #ifndef QT_NO_PRINTER
00033 
00034 #include "qprinter.h"
00035 #include "qpainter.h"
00036 #include "qapplication.h"
00037 #include "qpixmap.h"
00038 #include "qimage.h"
00039 #include "qdatetime.h"
00040 #include "qstring.h"
00041 #include "qbytearray.h"
00042 #include "qhash.h"
00043 #include "qbuffer.h"
00044 #include "qsettings.h"
00045 #include "qmap.h"
00046 #include "qbitmap.h"
00047 #include "qregion.h"
00048 #include "qimagewriter.h"
00049 #include <private/qunicodetables_p.h>
00050 #include <private/qpainterpath_p.h>
00051 #include <qdebug.h>
00052 #include <private/qdrawhelper_p.h>
00053 
00054 #ifndef Q_OS_WIN
00055 #include <unistd.h>
00056 #endif
00057 #include <stdlib.h>
00058 #include <limits.h>
00059 
00060 static bool qt_gen_epsf = false;
00061 
00062 void qt_generate_epsf(bool b)
00063 {
00064     qt_gen_epsf = b;
00065 }
00066 
00067 static const char *const ps_header =
00068 "/BD{bind def}bind def/d2{dup dup}BD/ED{exch def}BD/D0{0 ED}BD/F{setfont}BD\n"
00069 "/RL{rlineto}BD/CM{currentmatrix}BD/SM{setmatrix}BD/TR{translate}BD/SD\n"
00070 "{setdash}BD/SC{aload pop setrgbcolor}BD/CR{currentfile read pop}BD/i{index}\n"
00071 "BD/scs{setcolorspace}BD/DB{dict dup begin}BD/DE{end def}BD/ie{ifelse}BD/gs\n"
00072 "{gsave}BD/gr{grestore}BD/w{setlinewidth}BD/d{setdash}BD/J{setlinecap}BD/j\n"
00073 "{setlinejoin}BD/scn{3 array astore/BCol exch def}BD/SCN{3 array astore/PCol\n"
00074 "exch def}BD/cm{6 array astore concat}BD/m{moveto}BD/l{lineto}BD/c{curveto}BD\n"
00075 "/h{closepath}BD/W{clip}BD/W*{eoclip}BD/n{newpath}BD/q{gsave 10 dict begin}BD\n"
00076 "/Q{end grestore}BD/re{4 2 roll m dup 0 exch RL exch 0 RL 0 exch neg RL h}BD\n"
00077 "/S{gs PCol SC stroke gr n}BD/BT{gsave 10 dict begin/_m matrix CM def BCol\n"
00078 "SC}BD/ET{end grestore}BD/Tf{/_fs ED findfont[_fs 0 0 _fs 0 0]makefont F}BD\n"
00079 "/Tm{6 array astore concat}BD/Td{translate}BD/Tj{0 0 m show}BD/BDC{pop pop}BD\n"
00080 "/EMC{}BD/BSt 0 def/WFi false def/BCol[1 1 1]def/PCol[0 0 0]def/BDArr[0.94\n"
00081 "0.88 0.63 0.50 0.37 0.12 0.06]def/level3{/languagelevel where{pop\n"
00082 "languagelevel 3 ge}{false}ie}BD/QCIgray D0/QCIcolor D0/QCIindex D0/QCI{\n"
00083 "/colorimage where{pop false 3 colorimage}{exec/QCIcolor ED/QCIgray QCIcolor\n"
00084 "length 3 idiv string def 0 1 QCIcolor length 3 idiv 1 sub{/QCIindex ED/_x\n"
00085 "QCIindex 3 mul def QCIgray QCIindex QCIcolor _x get 0.30 mul QCIcolor _x 1\n"
00086 "add get 0.59 mul QCIcolor _x 2 add get 0.11 mul add add cvi put}for QCIgray\n"
00087 "image}ie}BD/di{gs TR 1 i 1 eq{pop pop false 3 1 roll BCol SC imagemask}{dup\n"
00088 "false ne{level3}{false}ie{/_ma ED 8 eq{/_dc[0 1]def/DeviceGray}{/_dc[0 1 0 1\n"
00089 "0 1]def/DeviceRGB}ie scs/_im ED/_mt ED/_h ED/_w ED <</ImageType 3/DataDict\n"
00090 "<</ImageType 1/Width _w/Height _h/ImageMatrix _mt/DataSource _im\n"
00091 "/BitsPerComponent 8/Decode _dc >>/MaskDict <</ImageType 1/Width _w/Height _h\n"
00092 "/ImageMatrix _mt/DataSource _ma/BitsPerComponent 1/Decode[0 1]>>\n"
00093 "/InterleaveType 3 >> image}{pop 8 4 1 roll 8 eq{image}{QCI}ie}ie}ie gr}BD/BF\n"
00094 "{gs BSt 1 eq{BCol SC WFi{fill}{eofill}ie}if BSt 2 ge BSt 8 le and{BDArr BSt\n"
00095 "2 sub get/_sc ED BCol{1. exch sub _sc mul 1. exch sub}forall 3 array astore\n"
00096 "SC WFi{fill}{eofill}ie}if BSt 9 ge BSt 14 le and{WFi{W}{W*}ie pathbbox 3 i 3\n"
00097 "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"
00098 "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"
00099 "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"
00100 "sub _h l}for}{0 6 _w _h add{dup 0 exch m _w sub _w exch l}for}ie}if BSt 13\n"
00101 "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"
00102 "add{dup _w exch m _w sub 0 exch l}for}ie}if S}if BSt 15 eq{}if BSt 24 eq{}if\n"
00103 "gr}BD/f{/WFi true def BF n}BD/f*{/WFi false def BF n}BD/B{/WFi true def BF S\n"
00104 "n}BD/B*{/WFi false def BF S n}BD/QI{/C save def pageinit q n}BD/QP{Q C\n"
00105 "restore showpage}BD/SPD{/setpagedevice where{<< 3 1 roll >> setpagedevice}{\n"
00106 "pop pop}ie}BD/T1AddMapping{10 dict begin/glyphs ED/fnt ED/current fnt\n"
00107 "/NumGlyphs get def/CMap fnt/CMap get def 0 1 glyphs length 1 sub{glyphs exch\n"
00108 "get/gn ED current dup 256 mod/min ED 256 idiv/maj ED CMap dup maj get dup\n"
00109 "null eq{pop 256 array 0 1 255{1 i exch/.notdef put}for}if dup min gn put maj\n"
00110 "exch put/current current 1 add def}for fnt/CMap CMap put fnt/NumGlyphs\n"
00111 "current put end}def/T1AddGlyphs{10 dict begin/glyphs ED/fnt ED/current fnt\n"
00112 "/NumGlyphs get def/CMap fnt/CMap get def/CharStrings fnt/CharStrings get def\n"
00113 "0 1 glyphs length 2 idiv 1 sub{2 mul dup glyphs exch get/gn ED 1 add glyphs\n"
00114 "exch get/cs ED current dup 256 mod/min ED 256 idiv/maj ED CMap dup maj get\n"
00115 "dup null eq{pop 256 array 0 1 255{1 i exch/.notdef put}for}if dup min gn put\n"
00116 "maj exch put CharStrings gn cs put/current current 1 add def}for fnt\n"
00117 "/CharStrings CharStrings put fnt/CMap CMap put fnt/NumGlyphs current put end\n"
00118 "}def/StringAdd{1 i length 1 i length add string 3 1 roll 2 i 0 3 i\n"
00119 "putinterval 2 i 2 i length 2 i putinterval pop pop}def/T1Setup{10 dict begin\n"
00120 "dup/FontName ED (-Base) StringAdd cvx cvn/Font ED/MaxPage Font/NumGlyphs get\n"
00121 "1 sub 256 idiv def/FDepVector MaxPage 1 add array def/Encoding MaxPage 1 add\n"
00122 "array def 0 1 MaxPage{dup Encoding exch dup put dup/Page ED FontName (-)\n"
00123 "StringAdd exch 20 string cvs StringAdd cvn Font 0 dict copy d2/CMap get Page\n"
00124 "get/Encoding exch put definefont FDepVector exch Page exch put}for FontName\n"
00125 "cvn <</FontType 0/FMapType 2/FontMatrix[1 0 0 1 0 0]/Encoding Encoding\n"
00126 "/FDepVector FDepVector >> definefont pop end}def\n";
00127 
00128 
00129 
00130 // ------------------------------End of static data ----------------------------------
00131 
00132 // make sure DSC comments are not longer than 255 chars per line.
00133 static QByteArray wrapDSC(const QByteArray &str)
00134 {
00135     QByteArray dsc = str.simplified();
00136     const int wrapAt = 254;
00137     QByteArray wrapped;
00138     if (dsc.length() < wrapAt)
00139         wrapped = dsc;
00140     else {
00141         wrapped = dsc.left(wrapAt);
00142         QByteArray tmp = dsc.mid(wrapAt);
00143         while (tmp.length() > wrapAt-3) {
00144             wrapped += "\n%%+" + tmp.left(wrapAt-3);
00145             tmp = tmp.mid(wrapAt-3);
00146         }
00147         wrapped += "\n%%+" + tmp;
00148     }
00149     return wrapped + "\n";
00150 }
00151 
00152 // ----------------------------- Internal class declarations -----------------------------
00153 
00154 QPSPrintEnginePrivate::QPSPrintEnginePrivate(QPrinter::PrinterMode m)
00155     : QPdfBaseEnginePrivate(m),
00156       printerState(QPrinter::Idle), hugeDocument(false), headerDone(false)
00157 {
00158     postscript = true;
00159 
00160     firstPage = true;
00161 
00162 #ifndef QT_NO_SETTINGS
00163     QSettings settings(QSettings::UserScope, QLatin1String("Trolltech"));
00164     settings.beginGroup(QLatin1String("Qt"));
00165     embedFonts = settings.value(QLatin1String("embedFonts"), true).toBool();
00166 #else
00167     embedFonts = true;
00168 #endif
00169 }
00170 
00171 QPSPrintEnginePrivate::~QPSPrintEnginePrivate()
00172 {
00173 }
00174 
00175 #include <QDebug>
00176 
00177 static void ps_r7(QPdf::ByteStream& stream, const char * s, int l)
00178 {
00179     int i = 0;
00180     uchar line[84];
00181     int col = 0;
00182 
00183     while(i < l) {
00184         line[col++] = s[i++];
00185         if (i < l - 1 && col >= 76) {
00186             line[col++] = '\n';
00187             line[col++] = '\0';
00188             stream << (const char *)line;
00189             col = 0;
00190         }
00191     }
00192     if (col > 0) {
00193         while((col&3) != 0)
00194             line[col++] = '%'; // use a comment as padding
00195         line[col++] = '\n';
00196         line[col++] = '\0';
00197         stream << (const char *)line;
00198     }
00199 }
00200 
00201 static QByteArray runlengthEncode(const QByteArray &input)
00202 {
00203     if (!input.length())
00204         return input;
00205 
00206     const char *data = input.constData();
00207 
00208     QByteArray out;
00209     int start = 0;
00210     char last = *data;
00211 
00212     enum State {
00213         Undef,
00214         Equal,
00215         Diff
00216     };
00217     State state = Undef;
00218 
00219     int i = 1;
00220     int written = 0;
00221     while (1) {
00222         bool flush = (i == input.size());
00223         if (!flush) {
00224             switch(state) {
00225             case Undef:
00226                 state = (last == data[i]) ? Equal : Diff;
00227                 break;
00228             case Equal:
00229                 if (data[i] != last)
00230                     flush = true;
00231                 break;
00232             case Diff:
00233                 if (data[i] == last) {
00234                     --i;
00235                     flush = true;
00236                 }
00237             }
00238         }
00239         if (flush || i - start == 128) {
00240             int size = i - start;
00241             if (state == Equal) {
00242                 out.append((char)(uchar)(257-size));
00243                 out.append(last);
00244                 written += size;
00245             } else {
00246                 out.append((char)(uchar)size-1);
00247                 while (start < i)
00248                     out.append(data[start++]);
00249                 written += size;
00250             }
00251             state = Undef;
00252             start = i;
00253             if (i == input.size())
00254                 break;
00255         }
00256         last = data[i];
00257         ++i;
00258     };
00259     out.append((char)(uchar)128);
00260     return out;
00261 }
00262 
00263 enum format {
00264     Raw,
00265     Runlength,
00266     DCT
00267 };
00268 static const char *const filters[3] = {
00269     " ",
00270     "/RunLengthDecode filter ",
00271     "/DCTDecode filter "
00272 };
00273 
00274 static QByteArray compress(const QImage &img, bool gray, int *format)
00275 {
00276     // we can't use premultiplied here
00277     QImage image = img;
00278 
00279     if (image.format() == QImage::Format_ARGB32_Premultiplied)
00280         image = image.convertToFormat(QImage::Format_ARGB32);
00281 
00282     QByteArray pixelData;
00283     int depth = image.depth();
00284 
00285     if (depth != 1 && !gray && QImageWriter::supportedImageFormats().contains("jpeg")) {
00286         QBuffer buffer(&pixelData);
00287         QImageWriter writer(&buffer, "jpeg");
00288         writer.setQuality(94);
00289         writer.write(img);
00290         *format = DCT;
00291     } else {
00292         int width = image.width();
00293         int height = image.height();
00294         int size = width*height;
00295 
00296         if (depth == 1)
00297             size = (width+7)/8*height;
00298         else if (!gray)
00299             size = size*3;
00300 
00301         pixelData.resize(size);
00302         uchar *pixel = (uchar *)pixelData.data();
00303         int i = 0;
00304         if (depth == 1) {
00305             QImage::Format format = image.format();
00306             memset(pixel, 0xff, size);
00307             for(int y=0; y < height; y++) {
00308                 const uchar * s = image.scanLine(y);
00309                 for(int x=0; x < width; x++) {
00310                     // need to copy bit for bit...
00311                     bool b = (format == QImage::Format_MonoLSB) ?
00312                              (*(s + (x >> 3)) >> (x & 7)) & 1 :
00313                              (*(s + (x >> 3)) << (x & 7)) & 0x80 ;
00314                     if (b)
00315                         pixel[i >> 3] ^= (0x80 >> (i & 7));
00316                     i++;
00317                 }
00318                 // we need to align to 8 bit here
00319                 i = (i+7) & 0xffffff8;
00320             }
00321         } else if (depth == 8) {
00322             for(int y=0; y < height; y++) {
00323                 const uchar * s = image.scanLine(y);
00324                 for(int x=0; x < width; x++) {
00325                     QRgb rgb = image.color(s[x]);
00326                     if (gray) {
00327                         pixel[i] = (unsigned char) qGray(rgb);
00328                         i++;
00329                     } else {
00330                         pixel[i] = (unsigned char) qRed(rgb);
00331                         pixel[i+1] = (unsigned char) qGreen(rgb);
00332                         pixel[i+2] = (unsigned char) qBlue(rgb);
00333                         i += 3;
00334                     }
00335                 }
00336             }
00337         } else {
00338             for(int y=0; y < height; y++) {
00339                 QRgb * s = (QRgb*)(image.scanLine(y));
00340                 for(int x=0; x < width; x++) {
00341                     QRgb rgb = (*s++);
00342                     if (gray) {
00343                         pixel[i] = (unsigned char) qGray(rgb);
00344                         i++;
00345                     } else {
00346                         pixel[i] = (unsigned char) qRed(rgb);
00347                         pixel[i+1] = (unsigned char) qGreen(rgb);
00348                         pixel[i+2] = (unsigned char) qBlue(rgb);
00349                         i += 3;
00350                     }
00351                 }
00352             }
00353         }
00354         *format = Raw;
00355         if (depth == 1) {
00356             pixelData = runlengthEncode(pixelData);
00357             *format = Runlength;
00358         }
00359     }
00360     QByteArray outarr = QPdf::ascii85Encode(pixelData);
00361     return outarr;
00362 }
00363 
00364 
00365 void QPSPrintEnginePrivate::drawImage(qreal x, qreal y, qreal w, qreal h,
00366                                       const QImage &img, const QImage &mask)
00367 {
00368     if (!w || !h || img.isNull()) return;
00369 
00370     int width  = img.width();
00371     int height = img.height();
00372     qreal scaleX = width/w;
00373     qreal scaleY = height/h;
00374 
00375     bool gray = (colorMode == QPrinter::GrayScale) ||
00376                 img.allGray();
00377     int splitSize = 21830 * (gray ? 3 : 1);
00378     if (width * height > splitSize) { // 65535/3, tolerance for broken printers
00379         int images, subheight;
00380         images = (width * height + splitSize - 1) / splitSize;
00381         subheight = (height + images-1) / images;
00382         while (subheight * width > splitSize) {
00383             images++;
00384             subheight = (height + images-1) / images;
00385         }
00386         int suby = 0;
00387         while(suby < height) {
00388             drawImage(x, y + suby/scaleY, w, qMin(subheight, height-suby)/scaleY,
00389                       img.copy(0, suby, width, qMin(subheight, height-suby)),
00390                       mask.isNull() ? mask : mask.copy(0, suby, width, qMin(subheight, height-suby)));
00391             suby += subheight;
00392         }
00393     } else {
00394         QByteArray out;
00395         int size = 0;
00396         const char *bits;
00397 
00398         if (!mask.isNull()) {
00399             int format;
00400             out = ::compress(mask, true, &format);
00401             size = (width+7)/8*height;
00402             *currentPage << "/mask currentfile/ASCII85Decode filter"
00403                          << filters[format]
00404                          << size << " string readstring\n";
00405             ps_r7(*currentPage, out, out.size());
00406             *currentPage << " pop def\n";
00407         }
00408         if (img.depth() == 1) {
00409             size = (width+7)/8*height;
00410             bits = "1 ";
00411         } else if (gray) {
00412             size = width*height;
00413             bits = "8 ";
00414         } else {
00415             size = width*height*3;
00416             bits = "24 ";
00417         }
00418 
00419         int format;
00420         out = ::compress(img, gray, &format);
00421         *currentPage << "/sl currentfile/ASCII85Decode filter"
00422                      << filters[format]
00423                      << size << " string readstring\n";
00424         ps_r7(*currentPage, out, out.size());
00425         *currentPage << " pop def\n";
00426         *currentPage << width << ' ' << height << "[" << scaleX << " 0 0 " << scaleY << " 0 0]sl "
00427                     << bits << (!mask.isNull() ? "mask " : "false ")
00428                     << x << ' ' << y << " di\n";
00429     }
00430 }
00431 
00432 void QPSPrintEnginePrivate::emitHeader(bool finished)
00433 {
00434     QPSPrintEngine *q = static_cast<QPSPrintEngine *>(q_ptr);
00435     QPrinter *printer = static_cast<QPrinter*>(pdev);
00436 
00437     if (creator.isEmpty())
00438         creator = QLatin1String("Qt " QT_VERSION_STR);
00439 
00440     QByteArray header;
00441     QPdf::ByteStream s(&header);
00442     s << "%!PS-Adobe-1.0";
00443 
00444     qreal scale = 72. / ((qreal) q->metric(QPaintDevice::PdmDpiY));
00445     QRect pageRect = this->pageRect();
00446     QRect paperRect = this->paperRect();
00447     int mtop = pageRect.top() - paperRect.top();
00448     int mleft = pageRect.left() - paperRect.left();
00449     int mbottom = paperRect.bottom() - pageRect.bottom();
00450     int mright = paperRect.right() - pageRect.right();
00451     int width = pageRect.width();
00452     int height = pageRect.height();
00453     if (finished && pageCount == 1 && copies == 1 &&
00454         ((fullPage && qt_gen_epsf) || (outputFileName.endsWith(QLatin1String(".eps"))))
00455        ) {
00456         if (!boundingBox.isValid())
00457             boundingBox.setRect(0, 0, width, height);
00458         if (orientation == QPrinter::Landscape) {
00459             if (!fullPage)
00460                 boundingBox.translate(-mleft, -mtop);
00461             s << " EPSF-3.0\n%%BoundingBox: "
00462               << (int)(printer->height() - boundingBox.bottom())*scale // llx
00463               << (int)(printer->width() - boundingBox.right())*scale - 1 // lly
00464               << (int)(printer->height() - boundingBox.top())*scale + 1 // urx
00465               << (int)(printer->width() - boundingBox.left())*scale; // ury
00466         } else {
00467             if (!fullPage)
00468                 boundingBox.translate(mleft, -mtop);
00469             s << " EPSF-3.0\n%%BoundingBox: "
00470               << (int)(boundingBox.left())*scale
00471               << (int)(printer->height() - boundingBox.bottom())*scale - 1
00472               << (int)(boundingBox.right())*scale + 1
00473               << (int)(printer->height() - boundingBox.top())*scale;
00474         }
00475     } else {
00476         int w = width + (fullPage ? 0 : mleft + mright);
00477         int h = height + (fullPage ? 0 : mtop + mbottom);
00478         w = (int)(w*scale);
00479         h = (int)(h*scale);
00480         // set a bounding box according to the DSC
00481         if (orientation == QPrinter::Landscape)
00482             s << "\n%%BoundingBox: 0 0 " << h << w;
00483         else
00484             s << "\n%%BoundingBox: 0 0 " << w << h;
00485     }
00486     s << "\n" << wrapDSC("%%Creator: " + creator.toUtf8());
00487     if (!title.isEmpty())
00488         s << wrapDSC("%%Title: " + title.toUtf8());
00489 #ifndef QT_NO_DATESTRING
00490     s << "%%CreationDate: " << QDateTime::currentDateTime().toString().toUtf8();
00491 #endif
00492     s << "\n%%Orientation: ";
00493     if (orientation == QPrinter::Landscape)
00494         s << "Landscape";
00495     else
00496         s << "Portrait";
00497 
00498     s << "\n%%Pages: (atend)"
00499         "\n%%DocumentFonts: (atend)"
00500         "\n%%EndComments\n"
00501 
00502         "%%BeginProlog\n"
00503         "% Prolog copyright 1994-2006 Trolltech. You may copy this prolog in any way\n"
00504         "% that is directly related to this document. For other use of this prolog,\n"
00505         "% see your licensing agreement for Qt.\n"
00506       << ps_header << "\n";
00507 
00508 
00509     s << "/pageinit {\n";
00510     if (!fullPage) {
00511         if (orientation == QPrinter::Portrait)
00512             s << mleft*scale << mbottom*scale << "translate\n";
00513         else
00514             s << mtop*scale << mleft*scale << "translate\n";
00515     }
00516     if (orientation == QPrinter::Portrait) {
00517         s << "% " << printer->widthMM() << "*" << printer->heightMM()
00518           << "mm (portrait)\n0 " << height*scale
00519           << "translate " << scale << "-" << scale << "scale } def\n";
00520     } else {
00521         s << "% " << printer->heightMM() << "*" << printer->widthMM()
00522           << " mm (landscape)\n 90 rotate " << scale << "-" << scale << "scale } def\n";
00523     }
00524     s << "%%EndProlog\n";
00525 
00526 
00527     s << "%%BeginSetup\n";
00528     if (copies > 1) {
00529         s << "/#copies " << copies << " def\n";
00530         s << "/NumCopies " << copies << " SPD\n";
00531         s << "/Collate " << (collate ? "true" : "false") << " SPD\n";
00532     }
00533     s << "%%EndSetup\n";
00534 
00535     outDevice->write(header);
00536     headerDone = true;
00537 }
00538 
00539 
00540 void QPSPrintEnginePrivate::emitPages()
00541 {
00542     if (!hugeDocument) {
00543         for (QHash<QFontEngine::FaceId, QFontSubset *>::const_iterator it = fonts.constBegin();
00544              it != fonts.constEnd(); ++it)
00545             outDevice->write((*it)->toType1());
00546     }
00547 
00548     outDevice->write(buffer);
00549 
00550     buffer = QByteArray();
00551     hugeDocument = true;
00552 }
00553 
00554 
00555 #ifdef Q_WS_QWS
00556 static const int max_in_memory_size = 2000000;
00557 #else
00558 static const int max_in_memory_size = 32000000;
00559 #endif
00560 
00561 void QPSPrintEnginePrivate::flushPage(bool last)
00562 {
00563     if (!last && currentPage->content().isEmpty())
00564         return;
00565 
00566     QPdf::ByteStream s(&buffer);
00567     s << "%%Page: "
00568       << pageCount << pageCount << "\n"
00569       << "%%BeginPageSetup\n"
00570       << "QI\n";
00571     if (hugeDocument) {
00572         for (QHash<QFontEngine::FaceId, QFontSubset *>::const_iterator it = fonts.constBegin();
00573              it != fonts.constEnd(); ++it) {
00574             if (currentPage->fonts.contains((*it)->object_id)) {
00575                 if ((*it)->downloaded_glyphs == 0) {
00576                     s << (*it)->toType1();
00577                     (*it)->downloaded_glyphs = 0;
00578                 } else {
00579                     s << (*it)->type1AddedGlyphs();
00580                 }
00581             }
00582         }
00583     }
00584     for (int i = 0; i < currentPage->fonts.size(); ++i)
00585         s << "(F" << QByteArray::number(currentPage->fonts.at(i)) << ") T1Setup\n";
00586 
00587     s << "%%EndPageSetup\nq\n"
00588       << currentPage->content()
00589       << "\nQ QP\n";
00590     if (last || hugeDocument || buffer.size() > max_in_memory_size) {
00591 //        qDebug("emiting header at page %d", pageCount);
00592         if (!headerDone)
00593             emitHeader(last);
00594         emitPages();
00595     }
00596     pageCount++;
00597 }
00598 
00599 // ================ PSPrinter class ========================
00600 
00601 QPSPrintEngine::QPSPrintEngine(QPrinter::PrinterMode m)
00602     : QPdfBaseEngine(*(new QPSPrintEnginePrivate(m)),
00603                      PrimitiveTransform
00604                      | PatternTransform
00605                      | PixmapTransform
00606                      | PainterPaths
00607                      | PatternBrush
00608         )
00609 {
00610 }
00611 
00612 static void ignoreSigPipe(bool b)
00613 {
00614 #ifndef QT_NO_LPR
00615     static struct sigaction *users_sigpipe_handler = 0;
00616 
00617     if (b) {
00618         if (users_sigpipe_handler != 0)
00619             return; // already ignoring sigpipe
00620 
00621         users_sigpipe_handler = new struct sigaction;
00622         struct sigaction tmp_sigpipe_handler;
00623         tmp_sigpipe_handler.sa_handler = SIG_IGN;
00624         sigemptyset(&tmp_sigpipe_handler.sa_mask);
00625         tmp_sigpipe_handler.sa_flags = 0;
00626 
00627         if (sigaction(SIGPIPE, &tmp_sigpipe_handler, users_sigpipe_handler) == -1) {
00628             delete users_sigpipe_handler;
00629             users_sigpipe_handler = 0;
00630         }
00631     }
00632     else {
00633         if (users_sigpipe_handler == 0)
00634             return; // not ignoring sigpipe
00635 
00636         if (sigaction(SIGPIPE, users_sigpipe_handler, 0) == -1)
00637             qWarning("QPSPrintEngine: Could not restore SIGPIPE handler");
00638 
00639         delete users_sigpipe_handler;
00640         users_sigpipe_handler = 0;
00641     }
00642 #else
00643     Q_UNUSED(b);
00644 #endif
00645 }
00646 QPSPrintEngine::~QPSPrintEngine()
00647 {
00648     Q_D(QPSPrintEngine);
00649     if (d->fd >= 0)
00650 #if defined(Q_OS_WIN) && defined(_MSC_VER) && _MSC_VER >= 1400
00651         ::_close(d->fd);
00652 #else
00653         ::close(d->fd);
00654 #endif
00655 }
00656 
00657 bool QPSPrintEngine::begin(QPaintDevice *pdev)
00658 {
00659     Q_D(QPSPrintEngine);
00660 
00661     if (d->fd >= 0)
00662         return true;
00663 
00664     if(!QPdfBaseEngine::begin(pdev))
00665         return false;
00666 
00667     d->pageCount = 1;                // initialize state
00668 
00669     d->pen = QPen(Qt::black);
00670     d->brush = Qt::NoBrush;
00671     d->hasPen = true;
00672     d->hasBrush = false;
00673     d->clipEnabled = false;
00674     d->allClipped = false;
00675     d->boundingBox = QRect();
00676     d->fontsUsed = "";
00677     d->hugeDocument = false;
00678 
00679     setActive(true);
00680     d->printerState = QPrinter::Active;
00681 
00682     newPage();
00683 
00684     return true;
00685 }
00686 
00687 bool QPSPrintEngine::end()
00688 {
00689     Q_D(QPSPrintEngine);
00690 
00691     // we're writing to lp/lpr through a pipe, we don't want to crash with SIGPIPE
00692     // if lp/lpr dies
00693     ignoreSigPipe(true);
00694     d->flushPage(true);
00695     QByteArray trailer;
00696     QPdf::ByteStream s(&trailer);
00697     s << "%%Trailer\n";
00698     s << "%%Pages: " << d->pageCount - 1 << "\n" <<
00699         wrapDSC("%%DocumentFonts: " + d->fontsUsed);
00700     s << "%%EOF\n";
00701     d->outDevice->write(trailer);
00702     ignoreSigPipe(false);
00703 
00704     QPdfBaseEngine::end();
00705 
00706     d->firstPage = true;
00707     d->headerDone = false;
00708 
00709     setActive(false);
00710     d->printerState = QPrinter::Idle;
00711     d->pdev = 0;
00712 
00713     return true;
00714 }
00715 
00716 void QPSPrintEngine::setBrush()
00717 {
00718     Q_D(QPSPrintEngine);
00719 #if 0
00720     bool specifyColor;
00721     int gStateObject = 0;
00722     int patternObject = d->addBrushPattern(brush, d->stroker.matrix, brushOrigin, &specifyColor, &gStateObject);
00723 
00724     *d->currentPage << (patternObject ? "/PCSp cs " : "/CSp cs ");
00725     if (specifyColor) {
00726         QColor rgba = brush.color();
00727         *d->currentPage << rgba.redF()
00728                         << rgba.greenF()
00729                         << rgba.blueF();
00730     }
00731     if (patternObject)
00732         *d->currentPage << "/Pat" << patternObject;
00733     *d->currentPage << "scn\n";
00734 #endif
00735     QColor rgba = d->brush.color();
00736     *d->currentPage << rgba.redF()
00737                     << rgba.greenF()
00738                     << rgba.blueF()
00739                     << "scn\n";
00740     *d->currentPage << "/BSt " << d->brush.style() << "def\n";
00741 }
00742 
00743 void QPSPrintEngine::drawImageInternal(const QRectF &r, QImage image, bool bitmap)
00744 {
00745     Q_D(QPSPrintEngine);
00746     if (d->clipEnabled && d->allClipped)
00747         return;
00748     if (bitmap && image.depth() != 1)
00749         bitmap = false;
00750     QImage mask;
00751     if (!bitmap) {
00752         if (image.format() == QImage::Format_Mono || image.format() == QImage::Format_MonoLSB)
00753             image = image.convertToFormat(QImage::Format_Indexed8);
00754         if (image.hasAlphaChannel()) {
00755             // get better alpha dithering
00756             int xscale = image.width();
00757             xscale *= xscale <= 800 ? 4 : (xscale <= 1600 ? 2 : 1);
00758             int yscale = image.height();
00759             yscale *= yscale <= 800 ? 4 : (yscale <= 1600 ? 2 : 1);
00760             image = image.scaled(xscale, yscale);
00761             mask = image.createAlphaMask(Qt::OrderedAlphaDither);
00762         }
00763     }
00764     *d->currentPage << "q\n";
00765     if(!d->simplePen)
00766         *d->currentPage << QPdf::generateMatrix(d->stroker.matrix);
00767     QBrush b = d->brush;
00768     if (image.depth() == 1) {
00769         // set current pen as brush
00770         d->brush = d->pen.brush();
00771         setBrush();
00772     }
00773     d->drawImage(r.x(), r.y(), r.width(), r.height(), image, mask);
00774     *d->currentPage << "Q\n";
00775     d->brush = b;
00776 }
00777 
00778 
00779 void QPSPrintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
00780                                Qt::ImageConversionFlags)
00781 {
00782     QImage image = img.copy(sr.toRect());
00783     drawImageInternal(r, image, false);
00784 }
00785 
00786 void QPSPrintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
00787 {
00788     QImage img = pm.copy(sr.toRect()).toImage();
00789     drawImageInternal(r, img, true);
00790 }
00791 
00792 void QPSPrintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &p)
00793 {
00794     Q_D(QPSPrintEngine);
00795     if (d->clipEnabled && d->allClipped)
00796         return;
00797     // ### Optimise implementation!
00798     qreal yPos = r.y();
00799     qreal yOff = p.y();
00800     while(yPos < r.y() + r.height()) {
00801         qreal drawH = pixmap.height() - yOff;    // Cropping first row
00802         if (yPos + drawH > r.y() + r.height())        // Cropping last row
00803             drawH = r.y() + r.height() - yPos;
00804         qreal xPos = r.x();
00805         qreal xOff = p.x();
00806         while(xPos < r.x() + r.width()) {
00807             qreal drawW = pixmap.width() - xOff; // Cropping first column
00808             if (xPos + drawW > r.x() + r.width())    // Cropping last column
00809                 drawW = r.x() + r.width() - xPos;
00810             // ########
00811             painter()->drawPixmap(QPointF(xPos, yPos).toPoint(), pixmap,
00812                                    QRectF(xOff, yOff, drawW, drawH).toRect());
00813             xPos += drawW;
00814             xOff = 0;
00815         }
00816         yPos += drawH;
00817         yOff = 0;
00818     }
00819 
00820 }
00821 
00822 bool QPSPrintEngine::newPage()
00823 {
00824     Q_D(QPSPrintEngine);
00825     // we're writing to lp/lpr through a pipe, we don't want to crash with SIGPIPE
00826     // if lp/lpr dies
00827     ignoreSigPipe(true);
00828     if (!d->firstPage)
00829         d->flushPage();
00830     d->firstPage = false;
00831     ignoreSigPipe(false);
00832 
00833     delete d->currentPage;
00834     d->currentPage = new QPdfPage;
00835     d->stroker.stream = d->currentPage;
00836 
00837     return QPdfBaseEngine::newPage();
00838 }
00839 
00840 bool QPSPrintEngine::abort()
00841 {
00842     // ### abort!?!
00843     return false;
00844 }
00845 
00846 QPrinter::PrinterState QPSPrintEngine::printerState() const
00847 {
00848     Q_D(const QPSPrintEngine);
00849     return d->printerState;
00850 }
00851 
00852 #endif // QT_NO_PRINTER
00853 
00854 

Generated on Thu Mar 15 11:55:34 2007 for Qt 4.2 User's Guide by  doxygen 1.5.1