src/gui/painting/qpdf.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 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 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 #include "qplatformdefs.h"
       
    42 #include <qdebug.h>
       
    43 #include "qpdf_p.h"
       
    44 #include <qfile.h>
       
    45 #include <qtemporaryfile.h>
       
    46 #include <private/qmath_p.h>
       
    47 #include "private/qcups_p.h"
       
    48 #include "qprinterinfo.h"
       
    49 #include <qnumeric.h>
       
    50 
       
    51 #ifdef Q_OS_UNIX
       
    52 #include "private/qcore_unix_p.h" // overrides QT_OPEN
       
    53 #endif
       
    54 
       
    55 QT_BEGIN_NAMESPACE
       
    56 
       
    57 extern int qt_defaultDpi();
       
    58 
       
    59 #ifndef QT_NO_PRINTER
       
    60 
       
    61 extern QSizeF qt_paperSizeToQSizeF(QPrinter::PaperSize size);
       
    62 
       
    63 /* also adds a space at the end of the number */
       
    64 const char *qt_real_to_string(qreal val, char *buf) {
       
    65     const char *ret = buf;
       
    66 
       
    67     if (qIsNaN(val)) {
       
    68         *(buf++) = '0';
       
    69         *(buf++) = ' ';
       
    70         *buf = 0;
       
    71         return ret;
       
    72     }
       
    73 
       
    74     if (val < 0) {
       
    75         *(buf++) = '-';
       
    76         val = -val;
       
    77     }
       
    78     unsigned int ival = (unsigned int) val;
       
    79     qreal frac = val - (qreal)ival;
       
    80 
       
    81     int ifrac = (int)(frac * 1000000);
       
    82     if (ifrac == 1000000) {
       
    83         ++ival;
       
    84         ifrac = 0;
       
    85     }
       
    86     char output[256];
       
    87     int i = 0;
       
    88     while (ival) {
       
    89         output[i] = '0' + (ival % 10);
       
    90         ++i;
       
    91         ival /= 10;
       
    92     }
       
    93     int fact = 100000;
       
    94     if (i == 0) {
       
    95         *(buf++) = '0';
       
    96     } else {
       
    97         while (i) {
       
    98             *(buf++) = output[--i];
       
    99             fact /= 10;
       
   100             ifrac /= 10;
       
   101         }
       
   102     }
       
   103 
       
   104     if (ifrac) {
       
   105         *(buf++) =  '.';
       
   106         while (fact) {
       
   107             *(buf++) = '0' + ((ifrac/fact) % 10);
       
   108             fact /= 10;
       
   109         }
       
   110     }
       
   111     *(buf++) = ' ';
       
   112     *buf = 0;
       
   113     return ret;
       
   114 }
       
   115 
       
   116 const char *qt_int_to_string(int val, char *buf) {
       
   117     const char *ret = buf;
       
   118     if (val < 0) {
       
   119         *(buf++) = '-';
       
   120         val = -val;
       
   121     }
       
   122     char output[256];
       
   123     int i = 0;
       
   124     while (val) {
       
   125         output[i] = '0' + (val % 10);
       
   126         ++i;
       
   127         val /= 10;
       
   128     }
       
   129     if (i == 0) {
       
   130         *(buf++) = '0';
       
   131     } else {
       
   132         while (i)
       
   133             *(buf++) = output[--i];
       
   134     }
       
   135     *(buf++) = ' ';
       
   136     *buf = 0;
       
   137     return ret;
       
   138 }
       
   139 
       
   140 
       
   141 namespace QPdf {
       
   142     ByteStream::ByteStream(QByteArray *byteArray, bool fileBacking)
       
   143             : dev(new QBuffer(byteArray)),
       
   144             fileBackingEnabled(fileBacking),
       
   145             fileBackingActive(false),
       
   146             handleDirty(false)
       
   147     {
       
   148         dev->open(QIODevice::ReadWrite);
       
   149     }
       
   150 
       
   151     ByteStream::ByteStream(bool fileBacking)
       
   152             : dev(new QBuffer(&ba)),
       
   153             fileBackingEnabled(fileBacking),
       
   154             fileBackingActive(false),
       
   155             handleDirty(false)
       
   156     {
       
   157         dev->open(QIODevice::ReadWrite);
       
   158     }
       
   159 
       
   160     ByteStream::~ByteStream()
       
   161     {
       
   162         delete dev;
       
   163     }
       
   164 
       
   165     ByteStream &ByteStream::operator <<(char chr)
       
   166     {
       
   167         if (handleDirty) prepareBuffer();
       
   168         dev->write(&chr, 1);
       
   169         return *this;
       
   170     }
       
   171 
       
   172     ByteStream &ByteStream::operator <<(const char *str)
       
   173     {
       
   174         if (handleDirty) prepareBuffer();
       
   175         dev->write(str, strlen(str));
       
   176         return *this;
       
   177     }
       
   178 
       
   179     ByteStream &ByteStream::operator <<(const QByteArray &str)
       
   180     {
       
   181         if (handleDirty) prepareBuffer();
       
   182         dev->write(str);
       
   183         return *this;
       
   184     }
       
   185 
       
   186     ByteStream &ByteStream::operator <<(const ByteStream &src)
       
   187     {
       
   188         Q_ASSERT(!src.dev->isSequential());
       
   189         if (handleDirty) prepareBuffer();
       
   190         // We do play nice here, even though it looks ugly.
       
   191         // We save the position and restore it afterwards.
       
   192         ByteStream &s = const_cast<ByteStream&>(src);
       
   193         qint64 pos = s.dev->pos();
       
   194         s.dev->reset();
       
   195         while (!s.dev->atEnd()) {
       
   196             QByteArray buf = s.dev->read(chunkSize());
       
   197             dev->write(buf);
       
   198         }
       
   199         s.dev->seek(pos);
       
   200         return *this;
       
   201     }
       
   202 
       
   203     ByteStream &ByteStream::operator <<(qreal val) {
       
   204         char buf[256];
       
   205         qt_real_to_string(val, buf);
       
   206         *this << buf;
       
   207         return *this;
       
   208     }
       
   209 
       
   210     ByteStream &ByteStream::operator <<(int val) {
       
   211         char buf[256];
       
   212         qt_int_to_string(val, buf);
       
   213         *this << buf;
       
   214         return *this;
       
   215     }
       
   216 
       
   217     ByteStream &ByteStream::operator <<(const QPointF &p) {
       
   218         char buf[256];
       
   219         qt_real_to_string(p.x(), buf);
       
   220         *this << buf;
       
   221         qt_real_to_string(p.y(), buf);
       
   222         *this << buf;
       
   223         return *this;
       
   224     }
       
   225 
       
   226     QIODevice *ByteStream::stream()
       
   227     {
       
   228         dev->reset();
       
   229         handleDirty = true;
       
   230         return dev;
       
   231     }
       
   232 
       
   233     void ByteStream::clear()
       
   234     {
       
   235         dev->open(QIODevice::ReadWrite | QIODevice::Truncate);
       
   236     }
       
   237 
       
   238     void ByteStream::constructor_helper(QByteArray *ba)
       
   239     {
       
   240         delete dev;
       
   241         dev = new QBuffer(ba);
       
   242         dev->open(QIODevice::ReadWrite);
       
   243     }
       
   244 
       
   245     void ByteStream::prepareBuffer()
       
   246     {
       
   247         Q_ASSERT(!dev->isSequential());
       
   248         qint64 size = dev->size();
       
   249         if (fileBackingEnabled && !fileBackingActive
       
   250                 && size > maxMemorySize()) {
       
   251             // Switch to file backing.
       
   252             QTemporaryFile *newFile = new QTemporaryFile;
       
   253             newFile->open();
       
   254             dev->reset();
       
   255             while (!dev->atEnd()) {
       
   256                 QByteArray buf = dev->read(chunkSize());
       
   257                 newFile->write(buf);
       
   258             }
       
   259             delete dev;
       
   260             dev = newFile;
       
   261             ba.clear();
       
   262             fileBackingActive = true;
       
   263         }
       
   264         if (dev->pos() != size) {
       
   265             dev->seek(size);
       
   266             handleDirty = false;
       
   267         }
       
   268     }
       
   269 }
       
   270 
       
   271 #define QT_PATH_ELEMENT(elm)
       
   272 
       
   273 QByteArray QPdf::generatePath(const QPainterPath &path, const QTransform &matrix, PathFlags flags)
       
   274 {
       
   275     QByteArray result;
       
   276     if (!path.elementCount())
       
   277         return result;
       
   278 
       
   279     ByteStream s(&result);
       
   280 
       
   281     int start = -1;
       
   282     for (int i = 0; i < path.elementCount(); ++i) {
       
   283         const QPainterPath::Element &elm = path.elementAt(i);
       
   284         switch (elm.type) {
       
   285         case QPainterPath::MoveToElement:
       
   286             if (start >= 0
       
   287                 && path.elementAt(start).x == path.elementAt(i-1).x
       
   288                 && path.elementAt(start).y == path.elementAt(i-1).y)
       
   289                 s << "h\n";
       
   290             s << matrix.map(QPointF(elm.x, elm.y)) << "m\n";
       
   291             start = i;
       
   292                 break;
       
   293         case QPainterPath::LineToElement:
       
   294             s << matrix.map(QPointF(elm.x, elm.y)) << "l\n";
       
   295             break;
       
   296         case QPainterPath::CurveToElement:
       
   297             Q_ASSERT(path.elementAt(i+1).type == QPainterPath::CurveToDataElement);
       
   298             Q_ASSERT(path.elementAt(i+2).type == QPainterPath::CurveToDataElement);
       
   299             s << matrix.map(QPointF(elm.x, elm.y))
       
   300               << matrix.map(QPointF(path.elementAt(i+1).x, path.elementAt(i+1).y))
       
   301               << matrix.map(QPointF(path.elementAt(i+2).x, path.elementAt(i+2).y))
       
   302               << "c\n";
       
   303             i += 2;
       
   304             break;
       
   305         default:
       
   306             qFatal("QPdf::generatePath(), unhandled type: %d", elm.type);
       
   307         }
       
   308     }
       
   309     if (start >= 0
       
   310         && path.elementAt(start).x == path.elementAt(path.elementCount()-1).x
       
   311         && path.elementAt(start).y == path.elementAt(path.elementCount()-1).y)
       
   312         s << "h\n";
       
   313 
       
   314     Qt::FillRule fillRule = path.fillRule();
       
   315 
       
   316     const char *op = "";
       
   317     switch (flags) {
       
   318     case ClipPath:
       
   319         op = (fillRule == Qt::WindingFill) ? "W n\n" : "W* n\n";
       
   320         break;
       
   321     case FillPath:
       
   322         op = (fillRule == Qt::WindingFill) ? "f\n" : "f*\n";
       
   323         break;
       
   324     case StrokePath:
       
   325         op = "S\n";
       
   326         break;
       
   327     case FillAndStrokePath:
       
   328         op = (fillRule == Qt::WindingFill) ? "B\n" : "B*\n";
       
   329         break;
       
   330     }
       
   331     s << op;
       
   332     return result;
       
   333 }
       
   334 
       
   335 QByteArray QPdf::generateMatrix(const QTransform &matrix)
       
   336 {
       
   337     QByteArray result;
       
   338     ByteStream s(&result);
       
   339     s << matrix.m11()
       
   340       << matrix.m12()
       
   341       << matrix.m21()
       
   342       << matrix.m22()
       
   343       << matrix.dx()
       
   344       << matrix.dy()
       
   345       << "cm\n";
       
   346     return result;
       
   347 }
       
   348 
       
   349 QByteArray QPdf::generateDashes(const QPen &pen)
       
   350 {
       
   351     QByteArray result;
       
   352     ByteStream s(&result);
       
   353     s << '[';
       
   354 
       
   355     QVector<qreal> dasharray = pen.dashPattern();
       
   356     qreal w = pen.widthF();
       
   357     if (w < 0.001)
       
   358         w = 1;
       
   359     for (int i = 0; i < dasharray.size(); ++i) {
       
   360         qreal dw = dasharray.at(i)*w;
       
   361         if (dw < 0.0001) dw = 0.0001;
       
   362         s << dw;
       
   363     }
       
   364     s << ']';
       
   365     //qDebug() << "dasharray: pen has" << dasharray;
       
   366     //qDebug() << "  => " << result;
       
   367     return result;
       
   368 }
       
   369 
       
   370 
       
   371 
       
   372 static const char* pattern_for_brush[] = {
       
   373     0, // NoBrush
       
   374     0, // SolidPattern
       
   375     "0 J\n"
       
   376     "6 w\n"
       
   377     "[] 0 d\n"
       
   378     "4 0 m\n"
       
   379     "4 8 l\n"
       
   380     "0 4 m\n"
       
   381     "8 4 l\n"
       
   382     "S\n", // Dense1Pattern
       
   383 
       
   384     "0 J\n"
       
   385     "2 w\n"
       
   386     "[6 2] 1 d\n"
       
   387     "0 0 m\n"
       
   388     "0 8 l\n"
       
   389     "8 0 m\n"
       
   390     "8 8 l\n"
       
   391     "S\n"
       
   392     "[] 0 d\n"
       
   393     "2 0 m\n"
       
   394     "2 8 l\n"
       
   395     "6 0 m\n"
       
   396     "6 8 l\n"
       
   397     "S\n"
       
   398     "[6 2] -3 d\n"
       
   399     "4 0 m\n"
       
   400     "4 8 l\n"
       
   401     "S\n", // Dense2Pattern
       
   402 
       
   403     "0 J\n"
       
   404     "2 w\n"
       
   405     "[6 2] 1 d\n"
       
   406     "0 0 m\n"
       
   407     "0 8 l\n"
       
   408     "8 0 m\n"
       
   409     "8 8 l\n"
       
   410     "S\n"
       
   411     "[2 2] -1 d\n"
       
   412     "2 0 m\n"
       
   413     "2 8 l\n"
       
   414     "6 0 m\n"
       
   415     "6 8 l\n"
       
   416     "S\n"
       
   417     "[6 2] -3 d\n"
       
   418     "4 0 m\n"
       
   419     "4 8 l\n"
       
   420     "S\n", // Dense3Pattern
       
   421 
       
   422     "0 J\n"
       
   423     "2 w\n"
       
   424     "[2 2] 1 d\n"
       
   425     "0 0 m\n"
       
   426     "0 8 l\n"
       
   427     "8 0 m\n"
       
   428     "8 8 l\n"
       
   429     "S\n"
       
   430     "[2 2] -1 d\n"
       
   431     "2 0 m\n"
       
   432     "2 8 l\n"
       
   433     "6 0 m\n"
       
   434     "6 8 l\n"
       
   435     "S\n"
       
   436     "[2 2] 1 d\n"
       
   437     "4 0 m\n"
       
   438     "4 8 l\n"
       
   439     "S\n", // Dense4Pattern
       
   440 
       
   441     "0 J\n"
       
   442     "2 w\n"
       
   443     "[2 6] -1 d\n"
       
   444     "0 0 m\n"
       
   445     "0 8 l\n"
       
   446     "8 0 m\n"
       
   447     "8 8 l\n"
       
   448     "S\n"
       
   449     "[2 2] 1 d\n"
       
   450     "2 0 m\n"
       
   451     "2 8 l\n"
       
   452     "6 0 m\n"
       
   453     "6 8 l\n"
       
   454     "S\n"
       
   455     "[2 6] 3 d\n"
       
   456     "4 0 m\n"
       
   457     "4 8 l\n"
       
   458     "S\n", // Dense5Pattern
       
   459 
       
   460     "0 J\n"
       
   461     "2 w\n"
       
   462     "[2 6] -1 d\n"
       
   463     "0 0 m\n"
       
   464     "0 8 l\n"
       
   465     "8 0 m\n"
       
   466     "8 8 l\n"
       
   467     "S\n"
       
   468     "[2 6] 3 d\n"
       
   469     "4 0 m\n"
       
   470     "4 8 l\n"
       
   471     "S\n", // Dense6Pattern
       
   472 
       
   473     "0 J\n"
       
   474     "2 w\n"
       
   475     "[2 6] -1 d\n"
       
   476     "0 0 m\n"
       
   477     "0 8 l\n"
       
   478     "8 0 m\n"
       
   479     "8 8 l\n"
       
   480     "S\n", // Dense7Pattern
       
   481 
       
   482     "1 w\n"
       
   483     "0 4 m\n"
       
   484     "8 4 l\n"
       
   485     "S\n", // HorPattern
       
   486 
       
   487     "1 w\n"
       
   488     "4 0 m\n"
       
   489     "4 8 l\n"
       
   490     "S\n", // VerPattern
       
   491 
       
   492     "1 w\n"
       
   493     "4 0 m\n"
       
   494     "4 8 l\n"
       
   495     "0 4 m\n"
       
   496     "8 4 l\n"
       
   497     "S\n", // CrossPattern
       
   498 
       
   499     "1 w\n"
       
   500     "-1 5 m\n"
       
   501     "5 -1 l\n"
       
   502     "3 9 m\n"
       
   503     "9 3 l\n"
       
   504     "S\n", // BDiagPattern
       
   505 
       
   506     "1 w\n"
       
   507     "-1 3 m\n"
       
   508     "5 9 l\n"
       
   509     "3 -1 m\n"
       
   510     "9 5 l\n"
       
   511     "S\n", // FDiagPattern
       
   512 
       
   513     "1 w\n"
       
   514     "-1 3 m\n"
       
   515     "5 9 l\n"
       
   516     "3 -1 m\n"
       
   517     "9 5 l\n"
       
   518     "-1 5 m\n"
       
   519     "5 -1 l\n"
       
   520     "3 9 m\n"
       
   521     "9 3 l\n"
       
   522     "S\n", // DiagCrossPattern
       
   523 };
       
   524 
       
   525 QByteArray QPdf::patternForBrush(const QBrush &b)
       
   526 {
       
   527     int style = b.style();
       
   528     if (style > Qt::DiagCrossPattern)
       
   529         return QByteArray();
       
   530     return pattern_for_brush[style];
       
   531 }
       
   532 
       
   533 #ifdef USE_NATIVE_GRADIENTS
       
   534 static void writeTriangleLine(uchar *&data, int xpos, int ypos, int xoff, int yoff, uint rgb, uchar flag, bool alpha)
       
   535 {
       
   536     data[0] =  flag;
       
   537     data[1] = (uchar)(xpos >> 16);
       
   538     data[2] = (uchar)(xpos >> 8);
       
   539     data[3] = (uchar)(xpos >> 0);
       
   540     data[4] = (uchar)(ypos >> 16);
       
   541     data[5] = (uchar)(ypos >> 8);
       
   542     data[6] = (uchar)(ypos >> 0);
       
   543     data += 7;
       
   544     if (alpha) {
       
   545         *data++ = (uchar)qAlpha(rgb);
       
   546     } else {
       
   547         *data++ = (uchar)qRed(rgb);
       
   548         *data++ = (uchar)qGreen(rgb);
       
   549         *data++ = (uchar)qBlue(rgb);
       
   550     }
       
   551     xpos += xoff;
       
   552     ypos += yoff;
       
   553     data[0] =  flag;
       
   554     data[1] = (uchar)(xpos >> 16);
       
   555     data[2] = (uchar)(xpos >> 8);
       
   556     data[3] = (uchar)(xpos >> 0);
       
   557     data[4] = (uchar)(ypos >> 16);
       
   558     data[5] = (uchar)(ypos >> 8);
       
   559     data[6] = (uchar)(ypos >> 0);
       
   560     data += 7;
       
   561     if (alpha) {
       
   562         *data++ = (uchar)qAlpha(rgb);
       
   563     } else {
       
   564         *data++ = (uchar)qRed(rgb);
       
   565         *data++ = (uchar)qGreen(rgb);
       
   566         *data++ = (uchar)qBlue(rgb);
       
   567     }
       
   568 }
       
   569 
       
   570 
       
   571 QByteArray QPdf::generateLinearGradientShader(const QLinearGradient *gradient, const QPointF *page_rect, bool alpha)
       
   572 {
       
   573     // generate list of triangles with colors
       
   574     QPointF start = gradient->start();
       
   575     QPointF stop = gradient->finalStop();
       
   576     QGradientStops stops = gradient->stops();
       
   577     QPointF offset = stop - start;
       
   578     QGradient::Spread spread = gradient->spread();
       
   579 
       
   580     if (gradient->spread() == QGradient::ReflectSpread) {
       
   581         offset *= 2;
       
   582         for (int i = stops.size() - 2; i >= 0; --i) {
       
   583             QGradientStop stop = stops.at(i);
       
   584             stop.first = 2. - stop.first;
       
   585             stops.append(stop);
       
   586         }
       
   587         for (int i = 0 ; i < stops.size(); ++i)
       
   588             stops[i].first /= 2.;
       
   589     }
       
   590 
       
   591     QPointF orthogonal(offset.y(), -offset.x());
       
   592     qreal length = offset.x()*offset.x() + offset.y()*offset.y();
       
   593 
       
   594     // find the max and min values in offset and orth direction that are needed to cover
       
   595     // the whole page
       
   596     int off_min = INT_MAX;
       
   597     int off_max = INT_MIN;
       
   598     qreal ort_min = INT_MAX;
       
   599     qreal ort_max = INT_MIN;
       
   600     for (int i = 0; i < 4; ++i) {
       
   601         qreal off = ((page_rect[i].x() - start.x()) * offset.x() + (page_rect[i].y() - start.y()) * offset.y())/length;
       
   602         qreal ort = ((page_rect[i].x() - start.x()) * orthogonal.x() + (page_rect[i].y() - start.y()) * orthogonal.y())/length;
       
   603         off_min = qMin(off_min, qFloor(off));
       
   604         off_max = qMax(off_max, qCeil(off));
       
   605         ort_min = qMin(ort_min, ort);
       
   606         ort_max = qMax(ort_max, ort);
       
   607     }
       
   608     ort_min -= 1;
       
   609     ort_max += 1;
       
   610 
       
   611     start += off_min * offset + ort_min * orthogonal;
       
   612     orthogonal *= (ort_max - ort_min);
       
   613     int num = off_max - off_min;
       
   614 
       
   615     QPointF gradient_rect[4] = { start,
       
   616                                  start + orthogonal,
       
   617                                  start + num*offset,
       
   618                                  start + num*offset + orthogonal };
       
   619     qreal xmin = gradient_rect[0].x();
       
   620     qreal xmax = gradient_rect[0].x();
       
   621     qreal ymin = gradient_rect[0].y();
       
   622     qreal ymax = gradient_rect[0].y();
       
   623     for (int i = 1; i < 4; ++i) {
       
   624         xmin = qMin(xmin, gradient_rect[i].x());
       
   625         xmax = qMax(xmax, gradient_rect[i].x());
       
   626         ymin = qMin(ymin, gradient_rect[i].y());
       
   627         ymax = qMax(ymax, gradient_rect[i].y());
       
   628     }
       
   629     xmin -= 1000;
       
   630     xmax += 1000;
       
   631     ymin -= 1000;
       
   632     ymax += 1000;
       
   633     start -= QPointF(xmin, ymin);
       
   634     qreal factor_x = qreal(1<<24)/(xmax - xmin);
       
   635     qreal factor_y = qreal(1<<24)/(ymax - ymin);
       
   636     int xoff = (int)(orthogonal.x()*factor_x);
       
   637     int yoff = (int)(orthogonal.y()*factor_y);
       
   638 
       
   639     QByteArray triangles;
       
   640     triangles.resize(spread == QGradient::PadSpread ? 20*(stops.size()+2) : 20*num*stops.size());
       
   641     uchar *data = (uchar *) triangles.data();
       
   642     if (spread == QGradient::PadSpread) {
       
   643         if (off_min > 0 || off_max < 1) {
       
   644             // linear gradient outside of page
       
   645             const QGradientStop &current_stop = off_min > 0 ? stops.at(stops.size()-1) : stops.at(0);
       
   646             uint rgb = current_stop.second.rgba();
       
   647             int xpos = (int)(start.x()*factor_x);
       
   648             int ypos = (int)(start.y()*factor_y);
       
   649             writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, 0, alpha);
       
   650             start += num*offset;
       
   651             xpos = (int)(start.x()*factor_x);
       
   652             ypos = (int)(start.y()*factor_y);
       
   653             writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, 1, alpha);
       
   654         } else {
       
   655             int flag = 0;
       
   656             if (off_min < 0) {
       
   657                 uint rgb = stops.at(0).second.rgba();
       
   658                 int xpos = (int)(start.x()*factor_x);
       
   659                 int ypos = (int)(start.y()*factor_y);
       
   660                 writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
       
   661                 start -= off_min*offset;
       
   662                 flag = 1;
       
   663             }
       
   664             for (int s = 0; s < stops.size(); ++s) {
       
   665                 const QGradientStop &current_stop = stops.at(s);
       
   666                 uint rgb = current_stop.second.rgba();
       
   667                 int xpos = (int)(start.x()*factor_x);
       
   668                 int ypos = (int)(start.y()*factor_y);
       
   669                 writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
       
   670                 if (s < stops.size()-1)
       
   671                     start += offset*(stops.at(s+1).first - stops.at(s).first);
       
   672                 flag = 1;
       
   673             }
       
   674             if (off_max > 1) {
       
   675                 start += (off_max - 1)*offset;
       
   676                 uint rgb = stops.at(stops.size()-1).second.rgba();
       
   677                 int xpos = (int)(start.x()*factor_x);
       
   678                 int ypos = (int)(start.y()*factor_y);
       
   679                 writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
       
   680             }
       
   681         }
       
   682     } else {
       
   683         for (int i = 0; i < num; ++i) {
       
   684             uchar flag = 0;
       
   685             for (int s = 0; s < stops.size(); ++s) {
       
   686                 uint rgb = stops.at(s).second.rgba();
       
   687                 int xpos = (int)(start.x()*factor_x);
       
   688                 int ypos = (int)(start.y()*factor_y);
       
   689                 writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
       
   690                 if (s < stops.size()-1)
       
   691                     start += offset*(stops.at(s+1).first - stops.at(s).first);
       
   692                 flag = 1;
       
   693             }
       
   694         }
       
   695     }
       
   696     triangles.resize((char *)data - triangles.constData());
       
   697 
       
   698     QByteArray shader;
       
   699     QPdf::ByteStream s(&shader);
       
   700     s << "<<\n"
       
   701         "/ShadingType 4\n"
       
   702         "/ColorSpace " << (alpha ? "/DeviceGray\n" : "/DeviceRGB\n") <<
       
   703         "/AntiAlias true\n"
       
   704         "/BitsPerCoordinate 24\n"
       
   705         "/BitsPerComponent 8\n"
       
   706         "/BitsPerFlag 8\n"
       
   707         "/Decode [" << xmin << xmax << ymin << ymax << (alpha ? "0 1]\n" : "0 1 0 1 0 1]\n") <<
       
   708         "/AntiAlias true\n"
       
   709         "/Length " << triangles.length() << "\n"
       
   710         ">>\n"
       
   711         "stream\n" << triangles << "endstream\n"
       
   712         "endobj\n";
       
   713     return shader;
       
   714 }
       
   715 #endif
       
   716 
       
   717 static void moveToHook(qfixed x, qfixed y, void *data)
       
   718 {
       
   719     QPdf::Stroker *t = (QPdf::Stroker *)data;
       
   720     if (!t->first)
       
   721         *t->stream << "h\n";
       
   722     if (!t->cosmeticPen)
       
   723         t->matrix.map(x, y, &x, &y);
       
   724     *t->stream << x << y << "m\n";
       
   725     t->first = false;
       
   726 }
       
   727 
       
   728 static void lineToHook(qfixed x, qfixed y, void *data)
       
   729 {
       
   730     QPdf::Stroker *t = (QPdf::Stroker *)data;
       
   731     if (!t->cosmeticPen)
       
   732         t->matrix.map(x, y, &x, &y);
       
   733     *t->stream << x << y << "l\n";
       
   734 }
       
   735 
       
   736 static void cubicToHook(qfixed c1x, qfixed c1y,
       
   737                         qfixed c2x, qfixed c2y,
       
   738                         qfixed ex, qfixed ey,
       
   739                         void *data)
       
   740 {
       
   741     QPdf::Stroker *t = (QPdf::Stroker *)data;
       
   742     if (!t->cosmeticPen) {
       
   743         t->matrix.map(c1x, c1y, &c1x, &c1y);
       
   744         t->matrix.map(c2x, c2y, &c2x, &c2y);
       
   745         t->matrix.map(ex, ey, &ex, &ey);
       
   746     }
       
   747     *t->stream << c1x << c1y
       
   748                << c2x << c2y
       
   749                << ex << ey
       
   750                << "c\n";
       
   751 }
       
   752 
       
   753 QPdf::Stroker::Stroker()
       
   754     : stream(0),
       
   755     first(true),
       
   756     dashStroker(&basicStroker)
       
   757 {
       
   758     stroker = &basicStroker;
       
   759     basicStroker.setMoveToHook(moveToHook);
       
   760     basicStroker.setLineToHook(lineToHook);
       
   761     basicStroker.setCubicToHook(cubicToHook);
       
   762     cosmeticPen = true;
       
   763     basicStroker.setStrokeWidth(.1);
       
   764 }
       
   765 
       
   766 void QPdf::Stroker::setPen(const QPen &pen)
       
   767 {
       
   768     if (pen.style() == Qt::NoPen) {
       
   769         stroker = 0;
       
   770         return;
       
   771     }
       
   772     qreal w = pen.widthF();
       
   773     bool zeroWidth = w < 0.0001;
       
   774     cosmeticPen = pen.isCosmetic();
       
   775     if (zeroWidth)
       
   776         w = .1;
       
   777 
       
   778     basicStroker.setStrokeWidth(w);
       
   779     basicStroker.setCapStyle(pen.capStyle());
       
   780     basicStroker.setJoinStyle(pen.joinStyle());
       
   781     basicStroker.setMiterLimit(pen.miterLimit());
       
   782 
       
   783     QVector<qreal> dashpattern = pen.dashPattern();
       
   784     if (zeroWidth) {
       
   785         for (int i = 0; i < dashpattern.size(); ++i)
       
   786             dashpattern[i] *= 10.;
       
   787     }
       
   788     if (!dashpattern.isEmpty()) {
       
   789         dashStroker.setDashPattern(dashpattern);
       
   790         dashStroker.setDashOffset(pen.dashOffset());
       
   791         stroker = &dashStroker;
       
   792     } else {
       
   793         stroker = &basicStroker;
       
   794     }
       
   795 }
       
   796 
       
   797 void QPdf::Stroker::strokePath(const QPainterPath &path)
       
   798 {
       
   799     if (!stroker)
       
   800         return;
       
   801     first = true;
       
   802 
       
   803     stroker->strokePath(path, this, cosmeticPen ? matrix : QTransform());
       
   804     *stream << "h f\n";
       
   805 }
       
   806 
       
   807 QByteArray QPdf::ascii85Encode(const QByteArray &input)
       
   808 {
       
   809     int isize = input.size()/4*4;
       
   810     QByteArray output;
       
   811     output.resize(input.size()*5/4+7);
       
   812     char *out = output.data();
       
   813     const uchar *in = (const uchar *)input.constData();
       
   814     for (int i = 0; i < isize; i += 4) {
       
   815         uint val = (((uint)in[i])<<24) + (((uint)in[i+1])<<16) + (((uint)in[i+2])<<8) + (uint)in[i+3];
       
   816         if (val == 0) {
       
   817             *out = 'z';
       
   818             ++out;
       
   819         } else {
       
   820             char base[5];
       
   821             base[4] = val % 85;
       
   822             val /= 85;
       
   823             base[3] = val % 85;
       
   824             val /= 85;
       
   825             base[2] = val % 85;
       
   826             val /= 85;
       
   827             base[1] = val % 85;
       
   828             val /= 85;
       
   829             base[0] = val % 85;
       
   830             *(out++) = base[0] + '!';
       
   831             *(out++) = base[1] + '!';
       
   832             *(out++) = base[2] + '!';
       
   833             *(out++) = base[3] + '!';
       
   834             *(out++) = base[4] + '!';
       
   835         }
       
   836     }
       
   837     //write the last few bytes
       
   838     int remaining = input.size() - isize;
       
   839     if (remaining) {
       
   840         uint val = 0;
       
   841         for (int i = isize; i < input.size(); ++i)
       
   842             val = (val << 8) + in[i];
       
   843         val <<= 8*(4-remaining);
       
   844         char base[5];
       
   845         base[4] = val % 85;
       
   846         val /= 85;
       
   847         base[3] = val % 85;
       
   848         val /= 85;
       
   849         base[2] = val % 85;
       
   850         val /= 85;
       
   851         base[1] = val % 85;
       
   852         val /= 85;
       
   853         base[0] = val % 85;
       
   854         for (int i = 0; i < remaining+1; ++i)
       
   855             *(out++) = base[i] + '!';
       
   856     }
       
   857     *(out++) = '~';
       
   858     *(out++) = '>';
       
   859     output.resize(out-output.data());
       
   860     return output;
       
   861 }
       
   862 
       
   863 const char *QPdf::toHex(ushort u, char *buffer)
       
   864 {
       
   865     int i = 3;
       
   866     while (i >= 0) {
       
   867         ushort hex = (u & 0x000f);
       
   868         if (hex < 0x0a)
       
   869             buffer[i] = '0'+hex;
       
   870         else
       
   871             buffer[i] = 'A'+(hex-0x0a);
       
   872         u = u >> 4;
       
   873         i--;
       
   874     }
       
   875     buffer[4] = '\0';
       
   876     return buffer;
       
   877 }
       
   878 
       
   879 const char *QPdf::toHex(uchar u, char *buffer)
       
   880 {
       
   881     int i = 1;
       
   882     while (i >= 0) {
       
   883         ushort hex = (u & 0x000f);
       
   884         if (hex < 0x0a)
       
   885             buffer[i] = '0'+hex;
       
   886         else
       
   887             buffer[i] = 'A'+(hex-0x0a);
       
   888         u = u >> 4;
       
   889         i--;
       
   890     }
       
   891     buffer[2] = '\0';
       
   892     return buffer;
       
   893 }
       
   894 
       
   895 #define Q_MM(n) int((n * 720 + 127) / 254)
       
   896 #define Q_IN(n) int(n * 72)
       
   897 
       
   898 static const char * const psToStr[QPrinter::NPaperSize+1] =
       
   899 {
       
   900     "A4", "B5", "Letter", "Legal", "Executive",
       
   901     "A0", "A1", "A2", "A3", "A5", "A6", "A7", "A8", "A9", "B0", "B1",
       
   902     "B10", "B2", "B3", "B4", "B6", "B7", "B8", "B9", "C5E", "Comm10E",
       
   903     "DLE", "Folio", "Ledger", "Tabloid", 0
       
   904 };
       
   905 
       
   906 QPdf::PaperSize QPdf::paperSize(QPrinter::PaperSize paperSize)
       
   907 {
       
   908     QSizeF s = qt_paperSizeToQSizeF(paperSize);
       
   909     PaperSize p = { Q_MM(s.width()), Q_MM(s.height()) };
       
   910     return p;
       
   911 }
       
   912 
       
   913 const char *QPdf::paperSizeToString(QPrinter::PaperSize paperSize)
       
   914 {
       
   915     return psToStr[paperSize];
       
   916 }
       
   917 
       
   918 
       
   919 QByteArray QPdf::stripSpecialCharacters(const QByteArray &string)
       
   920 {
       
   921     QByteArray s = string;
       
   922     s.replace(' ', "");
       
   923     s.replace('(', "");
       
   924     s.replace(')', "");
       
   925     s.replace('<', "");
       
   926     s.replace('>', "");
       
   927     s.replace('[', "");
       
   928     s.replace(']', "");
       
   929     s.replace('{', "");
       
   930     s.replace('}', "");
       
   931     s.replace('/', "");
       
   932     s.replace('%', "");
       
   933     return s;
       
   934 }
       
   935 
       
   936 
       
   937 // -------------------------- base engine, shared code between PS and PDF -----------------------
       
   938 
       
   939 QPdfBaseEngine::QPdfBaseEngine(QPdfBaseEnginePrivate &dd, PaintEngineFeatures f)
       
   940     : QAlphaPaintEngine(dd, f)
       
   941 {
       
   942     Q_D(QPdfBaseEngine);
       
   943 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
       
   944     if (QCUPSSupport::isAvailable()) {
       
   945         QCUPSSupport cups;
       
   946         const cups_dest_t* printers = cups.availablePrinters();
       
   947         int prnCount = cups.availablePrintersCount();
       
   948 
       
   949         for (int i = 0; i <  prnCount; ++i) {
       
   950             if (printers[i].is_default) {
       
   951                 d->printerName = QString::fromLocal8Bit(printers[i].name);
       
   952                 break;
       
   953             }
       
   954         }
       
   955 
       
   956     } else
       
   957 #endif
       
   958     {
       
   959         d->printerName = QString::fromLocal8Bit(qgetenv("PRINTER"));
       
   960         if (d->printerName.isEmpty())
       
   961             d->printerName = QString::fromLocal8Bit(qgetenv("LPDEST"));
       
   962         if (d->printerName.isEmpty())
       
   963             d->printerName = QString::fromLocal8Bit(qgetenv("NPRINTER"));
       
   964         if (d->printerName.isEmpty())
       
   965             d->printerName = QString::fromLocal8Bit(qgetenv("NGPRINTER"));
       
   966     }
       
   967 }
       
   968 
       
   969 void QPdfBaseEngine::drawPoints (const QPointF *points, int pointCount)
       
   970 {
       
   971     if (!points)
       
   972         return;
       
   973 
       
   974     QPainterPath p;
       
   975     for (int i=0; i!=pointCount;++i) {
       
   976         p.moveTo(points[i]);
       
   977         p.lineTo(points[i] + QPointF(0, 0.001));
       
   978     }
       
   979     drawPath(p);
       
   980 }
       
   981 
       
   982 void QPdfBaseEngine::drawLines (const QLineF *lines, int lineCount)
       
   983 {
       
   984     if (!lines)
       
   985         return;
       
   986 
       
   987     QPainterPath p;
       
   988     for (int i=0; i!=lineCount;++i) {
       
   989         p.moveTo(lines[i].p1());
       
   990         p.lineTo(lines[i].p2());
       
   991     }
       
   992     drawPath(p);
       
   993 }
       
   994 
       
   995 void QPdfBaseEngine::drawRects (const QRectF *rects, int rectCount)
       
   996 {
       
   997     if (!rects)
       
   998         return;
       
   999 
       
  1000     Q_D(QPdfBaseEngine);
       
  1001     if (d->useAlphaEngine) {
       
  1002         QAlphaPaintEngine::drawRects(rects, rectCount);
       
  1003         if (!continueCall())
       
  1004             return;
       
  1005     }
       
  1006 
       
  1007     if (d->clipEnabled && d->allClipped)
       
  1008         return;
       
  1009     if (!d->hasPen && !d->hasBrush)
       
  1010         return;
       
  1011 
       
  1012     QBrush penBrush = d->pen.brush();
       
  1013     if (d->simplePen || !d->hasPen) {
       
  1014         // draw strokes natively in this case for better output
       
  1015         if(!d->simplePen && !d->stroker.matrix.isIdentity())
       
  1016             *d->currentPage << "q\n" << QPdf::generateMatrix(d->stroker.matrix);
       
  1017         for (int i = 0; i < rectCount; ++i)
       
  1018             *d->currentPage << rects[i].x() << rects[i].y() << rects[i].width() << rects[i].height() << "re\n";
       
  1019         *d->currentPage << (d->hasPen ? (d->hasBrush ? "B\n" : "S\n") : "f\n");
       
  1020         if(!d->simplePen && !d->stroker.matrix.isIdentity())
       
  1021             *d->currentPage << "Q\n";
       
  1022     } else {
       
  1023         QPainterPath p;
       
  1024         for (int i=0; i!=rectCount; ++i)
       
  1025             p.addRect(rects[i]);
       
  1026         drawPath(p);
       
  1027     }
       
  1028 }
       
  1029 
       
  1030 void QPdfBaseEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
       
  1031 {
       
  1032     Q_D(QPdfBaseEngine);
       
  1033 
       
  1034     if (d->useAlphaEngine) {
       
  1035         QAlphaPaintEngine::drawPolygon(points, pointCount, mode);
       
  1036         if (!continueCall())
       
  1037             return;
       
  1038     }
       
  1039 
       
  1040     if (!points || !pointCount)
       
  1041         return;
       
  1042 
       
  1043     bool hb = d->hasBrush;
       
  1044     QPainterPath p;
       
  1045 
       
  1046     switch(mode) {
       
  1047         case OddEvenMode:
       
  1048             p.setFillRule(Qt::OddEvenFill);
       
  1049             break;
       
  1050         case ConvexMode:
       
  1051         case WindingMode:
       
  1052             p.setFillRule(Qt::WindingFill);
       
  1053             break;
       
  1054         case PolylineMode:
       
  1055             d->hasBrush = false;
       
  1056             break;
       
  1057         default:
       
  1058             break;
       
  1059     }
       
  1060 
       
  1061     p.moveTo(points[0]);
       
  1062     for (int i = 1; i < pointCount; ++i)
       
  1063         p.lineTo(points[i]);
       
  1064 
       
  1065     if (mode != PolylineMode)
       
  1066         p.closeSubpath();
       
  1067     drawPath(p);
       
  1068 
       
  1069     d->hasBrush = hb;
       
  1070 }
       
  1071 
       
  1072 void QPdfBaseEngine::drawPath (const QPainterPath &p)
       
  1073 {
       
  1074     Q_D(QPdfBaseEngine);
       
  1075 
       
  1076     if (d->useAlphaEngine) {
       
  1077         QAlphaPaintEngine::drawPath(p);
       
  1078         if (!continueCall())
       
  1079             return;
       
  1080     }
       
  1081 
       
  1082     if (d->clipEnabled && d->allClipped)
       
  1083         return;
       
  1084     if (!d->hasPen && !d->hasBrush)
       
  1085         return;
       
  1086 
       
  1087     if (d->simplePen) {
       
  1088         // draw strokes natively in this case for better output
       
  1089         *d->currentPage << QPdf::generatePath(p, QTransform(), d->hasBrush ? QPdf::FillAndStrokePath : QPdf::StrokePath);
       
  1090     } else {
       
  1091         if (d->hasBrush)
       
  1092             *d->currentPage << QPdf::generatePath(p, d->stroker.matrix, QPdf::FillPath);
       
  1093         if (d->hasPen) {
       
  1094             *d->currentPage << "q\n";
       
  1095             QBrush b = d->brush;
       
  1096             d->brush = d->pen.brush();
       
  1097             setBrush();
       
  1098             d->stroker.strokePath(p);
       
  1099             *d->currentPage << "Q\n";
       
  1100             d->brush = b;
       
  1101         }
       
  1102     }
       
  1103 }
       
  1104 
       
  1105 void QPdfBaseEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
       
  1106 {
       
  1107     Q_D(QPdfBaseEngine);
       
  1108 
       
  1109     if (d->useAlphaEngine) {
       
  1110         QAlphaPaintEngine::drawTextItem(p, textItem);
       
  1111         if (!continueCall())
       
  1112             return;
       
  1113     }
       
  1114 
       
  1115     if (!d->hasPen || (d->clipEnabled && d->allClipped))
       
  1116         return;
       
  1117 
       
  1118     if (d->stroker.matrix.type() >= QTransform::TxProject) {
       
  1119         QPaintEngine::drawTextItem(p, textItem);
       
  1120         return;
       
  1121     }
       
  1122 
       
  1123     *d->currentPage << "q\n";
       
  1124     if(!d->simplePen)
       
  1125         *d->currentPage << QPdf::generateMatrix(d->stroker.matrix);
       
  1126 
       
  1127     bool hp = d->hasPen;
       
  1128     d->hasPen = false;
       
  1129     QBrush b = d->brush;
       
  1130     d->brush = d->pen.brush();
       
  1131     setBrush();
       
  1132 
       
  1133     const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
       
  1134     Q_ASSERT(ti.fontEngine->type() != QFontEngine::Multi);
       
  1135     d->drawTextItem(p, ti);
       
  1136     d->hasPen = hp;
       
  1137     d->brush = b;
       
  1138     *d->currentPage << "Q\n";
       
  1139 }
       
  1140 
       
  1141 
       
  1142 void QPdfBaseEngine::updateState(const QPaintEngineState &state)
       
  1143 {
       
  1144     Q_D(QPdfBaseEngine);
       
  1145 
       
  1146     if (d->useAlphaEngine) {
       
  1147         QAlphaPaintEngine::updateState(state);
       
  1148         if (!continueCall())
       
  1149             return;
       
  1150     }
       
  1151 
       
  1152     QPaintEngine::DirtyFlags flags = state.state();
       
  1153 
       
  1154     if (flags & DirtyTransform)
       
  1155         d->stroker.matrix = state.transform();
       
  1156 
       
  1157     if (flags & DirtyPen) {
       
  1158         d->pen = state.pen();
       
  1159         d->hasPen = d->pen.style() != Qt::NoPen;
       
  1160         d->stroker.setPen(d->pen);
       
  1161         QBrush penBrush = d->pen.brush();
       
  1162         bool oldSimple = d->simplePen;
       
  1163         d->simplePen = (d->hasPen && (penBrush.style() == Qt::SolidPattern) && penBrush.isOpaque());
       
  1164         if (oldSimple != d->simplePen)
       
  1165             flags |= DirtyTransform;
       
  1166     }
       
  1167     if (flags & DirtyBrush) {
       
  1168         d->brush = state.brush();
       
  1169         d->hasBrush = d->brush.style() != Qt::NoBrush;
       
  1170     }
       
  1171     if (flags & DirtyBrushOrigin) {
       
  1172         d->brushOrigin = state.brushOrigin();
       
  1173         flags |= DirtyBrush;
       
  1174     }
       
  1175     if (flags & DirtyOpacity)
       
  1176         d->opacity = state.opacity();
       
  1177 
       
  1178     bool ce = d->clipEnabled;
       
  1179     if (flags & DirtyClipPath) {
       
  1180         d->clipEnabled = true;
       
  1181         updateClipPath(state.clipPath(), state.clipOperation());
       
  1182     } else if (flags & DirtyClipRegion) {
       
  1183         d->clipEnabled = true;
       
  1184         QPainterPath path;
       
  1185         QVector<QRect> rects = state.clipRegion().rects();
       
  1186         for (int i = 0; i < rects.size(); ++i)
       
  1187             path.addRect(rects.at(i));
       
  1188         updateClipPath(path, state.clipOperation());
       
  1189         flags |= DirtyClipPath;
       
  1190     } else if (flags & DirtyClipEnabled) {
       
  1191         d->clipEnabled = state.isClipEnabled();
       
  1192     }
       
  1193 
       
  1194     if (ce != d->clipEnabled)
       
  1195         flags |= DirtyClipPath;
       
  1196     else if (!d->clipEnabled)
       
  1197         flags &= ~DirtyClipPath;
       
  1198 
       
  1199     setupGraphicsState(flags);
       
  1200 }
       
  1201 
       
  1202 void QPdfBaseEngine::setupGraphicsState(QPaintEngine::DirtyFlags flags)
       
  1203 {
       
  1204     Q_D(QPdfBaseEngine);
       
  1205     if (flags & DirtyClipPath)
       
  1206         flags |= DirtyTransform|DirtyPen|DirtyBrush;
       
  1207 
       
  1208     if (flags & DirtyTransform) {
       
  1209         *d->currentPage << "Q\n";
       
  1210         flags |= DirtyPen|DirtyBrush;
       
  1211     }
       
  1212 
       
  1213     if (flags & DirtyClipPath) {
       
  1214         *d->currentPage << "Q q\n";
       
  1215 
       
  1216         d->allClipped = false;
       
  1217         if (d->clipEnabled && !d->clips.isEmpty()) {
       
  1218             for (int i = 0; i < d->clips.size(); ++i) {
       
  1219                 if (d->clips.at(i).isEmpty()) {
       
  1220                     d->allClipped = true;
       
  1221                     break;
       
  1222                 }
       
  1223             }
       
  1224             if (!d->allClipped) {
       
  1225                 for (int i = 0; i < d->clips.size(); ++i) {
       
  1226                     *d->currentPage << QPdf::generatePath(d->clips.at(i), QTransform(), QPdf::ClipPath);
       
  1227                 }
       
  1228             }
       
  1229         }
       
  1230     }
       
  1231 
       
  1232     if (flags & DirtyTransform) {
       
  1233         *d->currentPage << "q\n";
       
  1234         if (d->simplePen && !d->stroker.matrix.isIdentity())
       
  1235             *d->currentPage << QPdf::generateMatrix(d->stroker.matrix);
       
  1236     }
       
  1237     if (flags & DirtyBrush)
       
  1238         setBrush();
       
  1239     if (d->simplePen && (flags & DirtyPen))
       
  1240         setPen();
       
  1241 }
       
  1242 
       
  1243 extern QPainterPath qt_regionToPath(const QRegion &region);
       
  1244 
       
  1245 void QPdfBaseEngine::updateClipPath(const QPainterPath &p, Qt::ClipOperation op)
       
  1246 {
       
  1247     Q_D(QPdfBaseEngine);
       
  1248     QPainterPath path = d->stroker.matrix.map(p);
       
  1249     //qDebug() << "updateClipPath: " << d->stroker.matrix << p.boundingRect() << path.boundingRect() << op;
       
  1250 
       
  1251     if (op == Qt::NoClip) {
       
  1252         d->clipEnabled = false;
       
  1253         d->clips.clear();
       
  1254     } else if (op == Qt::ReplaceClip) {
       
  1255         d->clips.clear();
       
  1256         d->clips.append(path);
       
  1257     } else if (op == Qt::IntersectClip) {
       
  1258         d->clips.append(path);
       
  1259     } else { // UniteClip
       
  1260         // ask the painter for the current clipping path. that's the easiest solution
       
  1261         path = painter()->clipPath();
       
  1262         path = d->stroker.matrix.map(path);
       
  1263         d->clips.clear();
       
  1264         d->clips.append(path);
       
  1265     }
       
  1266 
       
  1267     if (d->useAlphaEngine) {
       
  1268         // if we have an alpha region, we have to subtract that from the
       
  1269         // any existing clip region since that region will be filled in
       
  1270         // later with images
       
  1271         QPainterPath alphaClip = qt_regionToPath(alphaClipping());
       
  1272         if (!alphaClip.isEmpty()) {
       
  1273             if (!d->clipEnabled) {
       
  1274                 QRect r = d->fullPage ? d->paperRect() : d->pageRect();
       
  1275                 QPainterPath dev;
       
  1276                 dev.addRect(QRect(0, 0, r.width(), r.height()));
       
  1277                 if (path.isEmpty())
       
  1278                     path = dev;
       
  1279                 else
       
  1280                     path = path.intersected(dev);
       
  1281                 d->clipEnabled = true;
       
  1282             } else {
       
  1283                 path = painter()->clipPath();
       
  1284                 path = d->stroker.matrix.map(path);
       
  1285             }
       
  1286             path = path.subtracted(alphaClip);
       
  1287             d->clips.clear();
       
  1288             d->clips.append(path);
       
  1289         }
       
  1290     }
       
  1291 }
       
  1292 
       
  1293 void QPdfBaseEngine::setPen()
       
  1294 {
       
  1295     Q_D(QPdfBaseEngine);
       
  1296     if (d->pen.style() == Qt::NoPen)
       
  1297         return;
       
  1298     QBrush b = d->pen.brush();
       
  1299     Q_ASSERT(b.style() == Qt::SolidPattern && b.isOpaque());
       
  1300 
       
  1301     QColor rgba = b.color();
       
  1302     if (d->colorMode == QPrinter::GrayScale) {
       
  1303         qreal gray = qGray(rgba.rgba())/255.;
       
  1304         *d->currentPage << gray << gray << gray;
       
  1305     } else {
       
  1306         *d->currentPage << rgba.redF()
       
  1307                         << rgba.greenF()
       
  1308                         << rgba.blueF();
       
  1309     }
       
  1310     *d->currentPage << "SCN\n";
       
  1311 
       
  1312     *d->currentPage << d->pen.widthF() << "w ";
       
  1313 
       
  1314     int pdfCapStyle = 0;
       
  1315     switch(d->pen.capStyle()) {
       
  1316     case Qt::FlatCap:
       
  1317         pdfCapStyle = 0;
       
  1318         break;
       
  1319     case Qt::SquareCap:
       
  1320         pdfCapStyle = 2;
       
  1321         break;
       
  1322     case Qt::RoundCap:
       
  1323         pdfCapStyle = 1;
       
  1324         break;
       
  1325     default:
       
  1326         break;
       
  1327     }
       
  1328     *d->currentPage << pdfCapStyle << "J ";
       
  1329 
       
  1330     int pdfJoinStyle = 0;
       
  1331     switch(d->pen.joinStyle()) {
       
  1332     case Qt::MiterJoin:
       
  1333         pdfJoinStyle = 0;
       
  1334         break;
       
  1335     case Qt::BevelJoin:
       
  1336         pdfJoinStyle = 2;
       
  1337         break;
       
  1338     case Qt::RoundJoin:
       
  1339         pdfJoinStyle = 1;
       
  1340         break;
       
  1341     default:
       
  1342         break;
       
  1343     }
       
  1344     *d->currentPage << pdfJoinStyle << "j ";
       
  1345 
       
  1346     *d->currentPage << QPdf::generateDashes(d->pen) << " 0 d\n";
       
  1347 }
       
  1348 
       
  1349 bool QPdfBaseEngine::newPage()
       
  1350 {
       
  1351     Q_D(QPdfBaseEngine);
       
  1352     setupGraphicsState(DirtyBrush|DirtyPen|DirtyClipPath);
       
  1353     QFile *outfile = qobject_cast<QFile*> (d->outDevice);
       
  1354     if (outfile && outfile->error() != QFile::NoError)
       
  1355         return false;
       
  1356     return true;
       
  1357 }
       
  1358 
       
  1359 
       
  1360 int QPdfBaseEngine::metric(QPaintDevice::PaintDeviceMetric metricType) const
       
  1361 {
       
  1362     Q_D(const QPdfBaseEngine);
       
  1363     int val;
       
  1364     QRect r = d->fullPage ? d->paperRect() : d->pageRect();
       
  1365     switch (metricType) {
       
  1366     case QPaintDevice::PdmWidth:
       
  1367         val = r.width();
       
  1368         break;
       
  1369     case QPaintDevice::PdmHeight:
       
  1370         val = r.height();
       
  1371         break;
       
  1372     case QPaintDevice::PdmDpiX:
       
  1373     case QPaintDevice::PdmDpiY:
       
  1374         val = d->resolution;
       
  1375         break;
       
  1376     case QPaintDevice::PdmPhysicalDpiX:
       
  1377     case QPaintDevice::PdmPhysicalDpiY:
       
  1378         val = 1200;
       
  1379         break;
       
  1380     case QPaintDevice::PdmWidthMM:
       
  1381         val = qRound(r.width()*25.4/d->resolution);
       
  1382         break;
       
  1383     case QPaintDevice::PdmHeightMM:
       
  1384         val = qRound(r.height()*25.4/d->resolution);
       
  1385         break;
       
  1386     case QPaintDevice::PdmNumColors:
       
  1387         val = INT_MAX;
       
  1388         break;
       
  1389     case QPaintDevice::PdmDepth:
       
  1390         val = 32;
       
  1391         break;
       
  1392     default:
       
  1393         qWarning("QPrinter::metric: Invalid metric command");
       
  1394         return 0;
       
  1395     }
       
  1396     return val;
       
  1397 }
       
  1398 
       
  1399 void QPdfBaseEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value)
       
  1400 {
       
  1401     Q_D(QPdfBaseEngine);
       
  1402     switch (key) {
       
  1403     case PPK_CollateCopies:
       
  1404         d->collate = value.toBool();
       
  1405         break;
       
  1406     case PPK_ColorMode:
       
  1407         d->colorMode = QPrinter::ColorMode(value.toInt());
       
  1408         break;
       
  1409     case PPK_Creator:
       
  1410         d->creator = value.toString();
       
  1411         break;
       
  1412     case PPK_DocumentName:
       
  1413         d->title = value.toString();
       
  1414         break;
       
  1415     case PPK_FullPage:
       
  1416         d->fullPage = value.toBool();
       
  1417         break;
       
  1418     case PPK_NumberOfCopies:
       
  1419         d->copies = value.toInt();
       
  1420         break;
       
  1421     case PPK_Orientation:
       
  1422         d->orientation = QPrinter::Orientation(value.toInt());
       
  1423         break;
       
  1424     case PPK_OutputFileName:
       
  1425         d->outputFileName = value.toString();
       
  1426         break;
       
  1427     case PPK_PageOrder:
       
  1428         d->pageOrder = QPrinter::PageOrder(value.toInt());
       
  1429         break;
       
  1430     case PPK_PaperSize:
       
  1431         d->paperSize = QPrinter::PaperSize(value.toInt());
       
  1432         break;
       
  1433     case PPK_PaperSource:
       
  1434         d->paperSource = QPrinter::PaperSource(value.toInt());
       
  1435         break;
       
  1436     case PPK_PrinterName:
       
  1437         d->printerName = value.toString();
       
  1438         break;
       
  1439     case PPK_PrinterProgram:
       
  1440         d->printProgram = value.toString();
       
  1441         break;
       
  1442     case PPK_Resolution:
       
  1443         d->resolution = value.toInt();
       
  1444         break;
       
  1445     case PPK_SelectionOption:
       
  1446         d->selectionOption = value.toString();
       
  1447         break;
       
  1448     case PPK_FontEmbedding:
       
  1449         d->embedFonts = value.toBool();
       
  1450         break;
       
  1451     case PPK_Duplex:
       
  1452         d->duplex = static_cast<QPrinter::DuplexMode> (value.toInt());
       
  1453         break;
       
  1454     case PPK_CupsPageRect:
       
  1455         d->cupsPageRect = value.toRect();
       
  1456         break;
       
  1457     case PPK_CupsPaperRect:
       
  1458         d->cupsPaperRect = value.toRect();
       
  1459         break;
       
  1460     case PPK_CupsOptions:
       
  1461         d->cupsOptions = value.toStringList();
       
  1462         break;
       
  1463     case PPK_CupsStringPageSize:
       
  1464         d->cupsStringPageSize = value.toString();
       
  1465         break;
       
  1466     case PPK_CustomPaperSize:
       
  1467         d->paperSize = QPrinter::Custom;
       
  1468         d->customPaperSize = value.toSizeF();
       
  1469         break;
       
  1470     case PPK_PageMargins:
       
  1471     {
       
  1472         QList<QVariant> margins(value.toList());
       
  1473         Q_ASSERT(margins.size() == 4);
       
  1474         d->leftMargin = margins.at(0).toReal();
       
  1475         d->topMargin = margins.at(1).toReal();
       
  1476         d->rightMargin = margins.at(2).toReal();
       
  1477         d->bottomMargin = margins.at(3).toReal();
       
  1478         d->hasCustomPageMargins = true;
       
  1479         break;
       
  1480     }
       
  1481     default:
       
  1482         break;
       
  1483     }
       
  1484 }
       
  1485 
       
  1486 QVariant QPdfBaseEngine::property(PrintEnginePropertyKey key) const
       
  1487 {
       
  1488     Q_D(const QPdfBaseEngine);
       
  1489 
       
  1490     QVariant ret;
       
  1491     switch (key) {
       
  1492     case PPK_CollateCopies:
       
  1493         ret = d->collate;
       
  1494         break;
       
  1495     case PPK_ColorMode:
       
  1496         ret = d->colorMode;
       
  1497         break;
       
  1498     case PPK_Creator:
       
  1499         ret = d->creator;
       
  1500         break;
       
  1501     case PPK_DocumentName:
       
  1502         ret = d->title;
       
  1503         break;
       
  1504     case PPK_FullPage:
       
  1505         ret = d->fullPage;
       
  1506         break;
       
  1507     case PPK_NumberOfCopies:
       
  1508 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
       
  1509         if (QCUPSSupport::isAvailable())
       
  1510             ret = 1;
       
  1511         else
       
  1512 #endif
       
  1513             ret = d->copies;
       
  1514         break;
       
  1515     case PPK_Orientation:
       
  1516         ret = d->orientation;
       
  1517         break;
       
  1518     case PPK_OutputFileName:
       
  1519         ret = d->outputFileName;
       
  1520         break;
       
  1521     case PPK_PageOrder:
       
  1522         ret = d->pageOrder;
       
  1523         break;
       
  1524     case PPK_PaperSize:
       
  1525         ret = d->paperSize;
       
  1526         break;
       
  1527     case PPK_PaperSource:
       
  1528         ret = d->paperSource;
       
  1529         break;
       
  1530     case PPK_PrinterName:
       
  1531         ret = d->printerName;
       
  1532         break;
       
  1533     case PPK_PrinterProgram:
       
  1534         ret = d->printProgram;
       
  1535         break;
       
  1536     case PPK_Resolution:
       
  1537         ret = d->resolution;
       
  1538         break;
       
  1539     case PPK_SupportedResolutions:
       
  1540         ret = QList<QVariant>() << 72;
       
  1541         break;
       
  1542     case PPK_PaperRect:
       
  1543         ret = d->paperRect();
       
  1544         break;
       
  1545     case PPK_PageRect:
       
  1546         ret = d->pageRect();
       
  1547         break;
       
  1548     case PPK_SelectionOption:
       
  1549         ret = d->selectionOption;
       
  1550         break;
       
  1551     case PPK_FontEmbedding:
       
  1552         ret = d->embedFonts;
       
  1553         break;
       
  1554     case PPK_Duplex:
       
  1555         ret = d->duplex;
       
  1556         break;
       
  1557     case PPK_CupsPageRect:
       
  1558         ret = d->cupsPageRect;
       
  1559         break;
       
  1560     case PPK_CupsPaperRect:
       
  1561         ret = d->cupsPaperRect;
       
  1562         break;
       
  1563     case PPK_CupsOptions:
       
  1564         ret = d->cupsOptions;
       
  1565         break;
       
  1566     case PPK_CupsStringPageSize:
       
  1567         ret = d->cupsStringPageSize;
       
  1568         break;
       
  1569     case PPK_CustomPaperSize:
       
  1570         ret = d->customPaperSize;
       
  1571         break;
       
  1572     case PPK_PageMargins:
       
  1573     {
       
  1574         QList<QVariant> margins;
       
  1575         if (d->hasCustomPageMargins) {
       
  1576             margins << d->leftMargin << d->topMargin
       
  1577                     << d->rightMargin << d->bottomMargin;
       
  1578         } else {
       
  1579             const qreal defaultMargin = 10; // ~3.5 mm
       
  1580             margins << defaultMargin << defaultMargin
       
  1581                     << defaultMargin << defaultMargin;
       
  1582         }
       
  1583         ret = margins;
       
  1584         break;
       
  1585     }
       
  1586     default:
       
  1587         break;
       
  1588     }
       
  1589     return ret;
       
  1590 }
       
  1591 
       
  1592 QPdfBaseEnginePrivate::QPdfBaseEnginePrivate(QPrinter::PrinterMode m)
       
  1593     : clipEnabled(false), allClipped(false), hasPen(true), hasBrush(false), simplePen(false),
       
  1594       useAlphaEngine(false),
       
  1595       outDevice(0), fd(-1),
       
  1596       duplex(QPrinter::DuplexNone), collate(false), fullPage(false), embedFonts(true), copies(1),
       
  1597       pageOrder(QPrinter::FirstPageFirst), orientation(QPrinter::Portrait),
       
  1598       paperSize(QPrinter::A4), colorMode(QPrinter::Color), paperSource(QPrinter::Auto),
       
  1599       hasCustomPageMargins(false),
       
  1600       leftMargin(0), topMargin(0), rightMargin(0), bottomMargin(0)
       
  1601 {
       
  1602     resolution = 72;
       
  1603     if (m == QPrinter::HighResolution)
       
  1604         resolution = 1200;
       
  1605     else if (m == QPrinter::ScreenResolution)
       
  1606         resolution = qt_defaultDpi();
       
  1607 
       
  1608     postscript = false;
       
  1609     currentObject = 1;
       
  1610     currentPage = 0;
       
  1611     stroker.stream = 0;
       
  1612 }
       
  1613 
       
  1614 bool QPdfBaseEngine::begin(QPaintDevice *pdev)
       
  1615 {
       
  1616     Q_D(QPdfBaseEngine);
       
  1617     d->pdev = pdev;
       
  1618 
       
  1619     d->postscript = false;
       
  1620     d->currentObject = 1;
       
  1621 
       
  1622     d->currentPage = new QPdfPage;
       
  1623     d->stroker.stream = d->currentPage;
       
  1624     d->opacity = 1.0;
       
  1625 
       
  1626     return d->openPrintDevice();
       
  1627 }
       
  1628 
       
  1629 bool QPdfBaseEngine::end()
       
  1630 {
       
  1631     Q_D(QPdfBaseEngine);
       
  1632     qDeleteAll(d->fonts);
       
  1633     d->fonts.clear();
       
  1634     delete d->currentPage;
       
  1635     d->currentPage = 0;
       
  1636 
       
  1637     d->closePrintDevice();
       
  1638     return true;
       
  1639 }
       
  1640 
       
  1641 #ifndef QT_NO_LPR
       
  1642 static void closeAllOpenFds()
       
  1643 {
       
  1644     // hack time... getting the maximum number of open
       
  1645     // files, if possible.  if not we assume it's the
       
  1646     // larger of 256 and the fd we got
       
  1647     int i;
       
  1648 #if defined(_SC_OPEN_MAX)
       
  1649     i = (int)sysconf(_SC_OPEN_MAX);
       
  1650 #elif defined(_POSIX_OPEN_MAX)
       
  1651     i = (int)_POSIX_OPEN_MAX;
       
  1652 #elif defined(OPEN_MAX)
       
  1653     i = (int)OPEN_MAX;
       
  1654 #else
       
  1655     i = 256;
       
  1656 #endif
       
  1657     // leave stdin/out/err untouched
       
  1658     while(--i > 2)
       
  1659         QT_CLOSE(i);
       
  1660 }
       
  1661 #endif
       
  1662 
       
  1663 bool QPdfBaseEnginePrivate::openPrintDevice()
       
  1664 {
       
  1665     if(outDevice)
       
  1666         return false;
       
  1667 
       
  1668     if (!outputFileName.isEmpty()) {
       
  1669         QFile *file = new QFile(outputFileName);
       
  1670         if (! file->open(QFile::WriteOnly|QFile::Truncate)) {
       
  1671             delete file;
       
  1672             return false;
       
  1673         }
       
  1674         outDevice = file;
       
  1675 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
       
  1676     } else if (QCUPSSupport::isAvailable()) {
       
  1677         QCUPSSupport cups;
       
  1678         QPair<int, QString> ret = cups.tempFd();
       
  1679         if (ret.first < 0) {
       
  1680             qWarning("QPdfPrinter: Could not open temporary file to print");
       
  1681             return false;
       
  1682         }
       
  1683         cupsTempFile = ret.second;
       
  1684         outDevice = new QFile();
       
  1685         static_cast<QFile *>(outDevice)->open(ret.first, QIODevice::WriteOnly);
       
  1686 #endif
       
  1687 #ifndef QT_NO_LPR
       
  1688     } else {
       
  1689         QString pr;
       
  1690         if (!printerName.isEmpty())
       
  1691             pr = printerName;
       
  1692         int fds[2];
       
  1693         if (qt_safe_pipe(fds) != 0) {
       
  1694             qWarning("QPdfPrinter: Could not open pipe to print");
       
  1695             return false;
       
  1696         }
       
  1697 
       
  1698         pid_t pid = fork();
       
  1699         if (pid == 0) {       // child process
       
  1700             // if possible, exit quickly, so the actual lp/lpr
       
  1701             // becomes a child of init, and ::waitpid() is
       
  1702             // guaranteed not to wait.
       
  1703             if (fork() > 0) {
       
  1704                 closeAllOpenFds();
       
  1705 
       
  1706                 // try to replace this process with "true" - this prevents
       
  1707                 // global destructors from being called (that could possibly
       
  1708                 // do wrong things to the parent process)
       
  1709                 (void)execlp("true", "true", (char *)0);
       
  1710                 (void)execl("/bin/true", "true", (char *)0);
       
  1711                 (void)execl("/usr/bin/true", "true", (char *)0);
       
  1712                 ::_exit(0);
       
  1713             }
       
  1714             qt_safe_dup2(fds[0], 0, 0);
       
  1715 
       
  1716             closeAllOpenFds();
       
  1717 
       
  1718             if (!printProgram.isEmpty()) {
       
  1719                 if (!selectionOption.isEmpty())
       
  1720                     pr.prepend(selectionOption);
       
  1721                 else
       
  1722                     pr.prepend(QLatin1String("-P"));
       
  1723                 (void)execlp(printProgram.toLocal8Bit().data(), printProgram.toLocal8Bit().data(),
       
  1724                              pr.toLocal8Bit().data(), (char *)0);
       
  1725             } else {
       
  1726                 // if no print program has been specified, be smart
       
  1727                 // about the option string too.
       
  1728                 QList<QByteArray> lprhack;
       
  1729                 QList<QByteArray> lphack;
       
  1730                 QByteArray media;
       
  1731                 if (!pr.isEmpty() || !selectionOption.isEmpty()) {
       
  1732                     if (!selectionOption.isEmpty()) {
       
  1733                         QStringList list = selectionOption.split(QLatin1Char(' '));
       
  1734                         for (int i = 0; i < list.size(); ++i)
       
  1735                             lprhack.append(list.at(i).toLocal8Bit());
       
  1736                         lphack = lprhack;
       
  1737                     } else {
       
  1738                         lprhack.append("-P");
       
  1739                         lphack.append("-d");
       
  1740                     }
       
  1741                     lprhack.append(pr.toLocal8Bit());
       
  1742                     lphack.append(pr.toLocal8Bit());
       
  1743                 }
       
  1744                 lphack.append("-s");
       
  1745 
       
  1746                 char ** lpargs = new char *[lphack.size()+6];
       
  1747                 char lp[] = "lp";
       
  1748                 lpargs[0] = lp;
       
  1749                 int i;
       
  1750                 for (i = 0; i < lphack.size(); ++i)
       
  1751                     lpargs[i+1] = (char *)lphack.at(i).constData();
       
  1752 #ifndef Q_OS_OSF
       
  1753                 if (QPdf::paperSizeToString(paperSize)) {
       
  1754                     char dash_o[] = "-o";
       
  1755                     lpargs[++i] = dash_o;
       
  1756                     lpargs[++i] = const_cast<char *>(QPdf::paperSizeToString(paperSize));
       
  1757                     lpargs[++i] = dash_o;
       
  1758                     media = "media=";
       
  1759                     media += QPdf::paperSizeToString(paperSize);
       
  1760                     lpargs[++i] = media.data();
       
  1761                 }
       
  1762 #endif
       
  1763                 lpargs[++i] = 0;
       
  1764                 char **lprargs = new char *[lprhack.size()+2];
       
  1765                 char lpr[] = "lpr";
       
  1766                 lprargs[0] = lpr;
       
  1767                 for (int i = 0; i < lprhack.size(); ++i)
       
  1768                     lprargs[i+1] = (char *)lprhack[i].constData();
       
  1769                 lprargs[lprhack.size() + 1] = 0;
       
  1770                 (void)execvp("lp", lpargs);
       
  1771                 (void)execvp("lpr", lprargs);
       
  1772                 (void)execv("/bin/lp", lpargs);
       
  1773                 (void)execv("/bin/lpr", lprargs);
       
  1774                 (void)execv("/usr/bin/lp", lpargs);
       
  1775                 (void)execv("/usr/bin/lpr", lprargs);
       
  1776 
       
  1777                 delete []lpargs;
       
  1778                 delete []lprargs;
       
  1779             }
       
  1780             // if we couldn't exec anything, close the fd,
       
  1781             // wait for a second so the parent process (the
       
  1782             // child of the GUI process) has exited.  then
       
  1783             // exit.
       
  1784             QT_CLOSE(0);
       
  1785             (void)::sleep(1);
       
  1786             ::_exit(0);
       
  1787         }
       
  1788         // parent process
       
  1789         QT_CLOSE(fds[0]);
       
  1790         fd = fds[1];
       
  1791         (void)qt_safe_waitpid(pid, 0, 0);
       
  1792 
       
  1793         if (fd < 0)
       
  1794             return false;
       
  1795 
       
  1796         outDevice = new QFile();
       
  1797         static_cast<QFile *>(outDevice)->open(fd, QIODevice::WriteOnly);
       
  1798 #endif
       
  1799     }
       
  1800 
       
  1801     return true;
       
  1802 }
       
  1803 
       
  1804 void QPdfBaseEnginePrivate::closePrintDevice()
       
  1805 {
       
  1806     if (!outDevice)
       
  1807         return;
       
  1808     outDevice->close();
       
  1809     if (fd >= 0)
       
  1810 #if defined(Q_OS_WIN) && defined(_MSC_VER) && _MSC_VER >= 1400
       
  1811         ::_close(fd);
       
  1812 #else
       
  1813         ::close(fd);
       
  1814 #endif
       
  1815     fd = -1;
       
  1816     delete outDevice;
       
  1817     outDevice = 0;
       
  1818 
       
  1819 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
       
  1820     if (!cupsTempFile.isEmpty()) {
       
  1821         QString tempFile = cupsTempFile;
       
  1822         cupsTempFile.clear();
       
  1823         QCUPSSupport cups;
       
  1824 
       
  1825         // Set up print options.
       
  1826         QByteArray prnName;
       
  1827         QList<QPair<QByteArray, QByteArray> > options;
       
  1828         QVector<cups_option_t> cupsOptStruct;
       
  1829 
       
  1830         if (!printerName.isEmpty()) {
       
  1831             prnName = printerName.toLocal8Bit();
       
  1832         } else {
       
  1833             QPrinterInfo def = QPrinterInfo::defaultPrinter();
       
  1834             if (def.isNull()) {
       
  1835                 qWarning("Could not determine printer to print to");
       
  1836                 QFile::remove(tempFile);
       
  1837                 return;
       
  1838             }
       
  1839             prnName = def.printerName().toLocal8Bit();
       
  1840         }
       
  1841 
       
  1842         if (!cupsStringPageSize.isEmpty()) {
       
  1843             options.append(QPair<QByteArray, QByteArray>("media", cupsStringPageSize.toLocal8Bit()));
       
  1844         }
       
  1845 
       
  1846         if (copies > 1) {
       
  1847             options.append(QPair<QByteArray, QByteArray>("copies", QString::number(copies).toLocal8Bit()));
       
  1848         }
       
  1849 
       
  1850         if (collate) {
       
  1851             options.append(QPair<QByteArray, QByteArray>("Collate", "True"));
       
  1852         }
       
  1853 
       
  1854         if (duplex != QPrinter::DuplexNone) {
       
  1855             switch(duplex) {
       
  1856             case QPrinter::DuplexNone: break;
       
  1857             case QPrinter::DuplexAuto:
       
  1858                 if (orientation == QPrinter::Portrait)
       
  1859                     options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-long-edge"));
       
  1860                 else
       
  1861                     options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-short-edge"));
       
  1862                 break;
       
  1863             case QPrinter::DuplexLongSide:
       
  1864                 options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-long-edge"));
       
  1865                 break;
       
  1866             case QPrinter::DuplexShortSide:
       
  1867                 options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-short-edge"));
       
  1868                 break;
       
  1869             }
       
  1870         }
       
  1871 
       
  1872         if (QCUPSSupport::cupsVersion() >= 10300 && orientation == QPrinter::Landscape) {
       
  1873             options.append(QPair<QByteArray, QByteArray>("landscape", ""));
       
  1874         }
       
  1875 
       
  1876         QStringList::const_iterator it = cupsOptions.constBegin();
       
  1877         while (it != cupsOptions.constEnd()) {
       
  1878             options.append(QPair<QByteArray, QByteArray>((*it).toLocal8Bit(), (*(it+1)).toLocal8Bit()));
       
  1879             it += 2;
       
  1880         }
       
  1881 
       
  1882         for (int c = 0; c < options.size(); ++c) {
       
  1883             cups_option_t opt;
       
  1884             opt.name = options[c].first.data();
       
  1885             opt.value = options[c].second.data();
       
  1886             cupsOptStruct.append(opt);
       
  1887         }
       
  1888 
       
  1889         // Print the file.
       
  1890         cups_option_t* optPtr = cupsOptStruct.size() ? &cupsOptStruct.first() : 0;
       
  1891         cups.printFile(prnName.constData(), tempFile.toLocal8Bit().constData(),
       
  1892                 title.toLocal8Bit().constData(), cupsOptStruct.size(), optPtr);
       
  1893 
       
  1894         QFile::remove(tempFile);
       
  1895     }
       
  1896 #endif
       
  1897 }
       
  1898 
       
  1899 QPdfBaseEnginePrivate::~QPdfBaseEnginePrivate()
       
  1900 {
       
  1901     qDeleteAll(fonts);
       
  1902     delete currentPage;
       
  1903 }
       
  1904 
       
  1905 void QPdfBaseEnginePrivate::drawTextItem(const QPointF &p, const QTextItemInt &ti)
       
  1906 {
       
  1907     Q_Q(QPdfBaseEngine);
       
  1908 
       
  1909     QFontEngine *fe = ti.fontEngine;
       
  1910 
       
  1911     QFontEngine::FaceId face_id = fe->faceId();
       
  1912     bool noEmbed = false;
       
  1913     if (face_id.filename.isEmpty()
       
  1914         || (!postscript && ((fe->fsType & 0x200) /* bitmap embedding only */
       
  1915                             || (fe->fsType == 2) /* no embedding allowed */))) {
       
  1916         *currentPage << "Q\n";
       
  1917         q->QPaintEngine::drawTextItem(p, ti);
       
  1918         *currentPage << "q\n";
       
  1919         if (face_id.filename.isEmpty())
       
  1920             return;
       
  1921         noEmbed = true;
       
  1922     }
       
  1923 
       
  1924     QFontSubset *font = fonts.value(face_id, 0);
       
  1925     if (!font) {
       
  1926         font = new QFontSubset(fe, requestObject());
       
  1927         font->noEmbed = noEmbed;
       
  1928     }
       
  1929     fonts.insert(face_id, font);
       
  1930 
       
  1931     if (!currentPage->fonts.contains(font->object_id))
       
  1932         currentPage->fonts.append(font->object_id);
       
  1933 
       
  1934     qreal size = ti.fontEngine->fontDef.pixelSize;
       
  1935 #ifdef Q_WS_WIN
       
  1936     if (ti.fontEngine->type() == QFontEngine::Win) {
       
  1937         QFontEngineWin *fe = static_cast<QFontEngineWin *>(ti.fontEngine);
       
  1938         size = fe->tm.tmHeight;
       
  1939     }
       
  1940 #endif
       
  1941 
       
  1942     QVarLengthArray<glyph_t> glyphs;
       
  1943     QVarLengthArray<QFixedPoint> positions;
       
  1944     QTransform m = QTransform::fromTranslate(p.x(), p.y());
       
  1945     ti.fontEngine->getGlyphPositions(ti.glyphs, m, ti.flags,
       
  1946                                      glyphs, positions);
       
  1947     if (glyphs.size() == 0)
       
  1948         return;
       
  1949     int synthesized = ti.fontEngine->synthesized();
       
  1950     qreal stretch = synthesized & QFontEngine::SynthesizedStretch ? ti.fontEngine->fontDef.stretch/100. : 1.;
       
  1951 
       
  1952     *currentPage << "BT\n"
       
  1953                  << "/F" << font->object_id << size << "Tf "
       
  1954                  << stretch << (synthesized & QFontEngine::SynthesizedItalic
       
  1955                                 ? "0 .3 -1 0 0 Tm\n"
       
  1956                                 : "0 0 -1 0 0 Tm\n");
       
  1957 
       
  1958 
       
  1959 #if 0
       
  1960     // #### implement actual text for complex languages
       
  1961     const unsigned short *logClusters = ti.logClusters;
       
  1962     int pos = 0;
       
  1963     do {
       
  1964         int end = pos + 1;
       
  1965         while (end < ti.num_chars && logClusters[end] == logClusters[pos])
       
  1966             ++end;
       
  1967         *currentPage << "/Span << /ActualText <FEFF";
       
  1968         for (int i = pos; i < end; ++i) {
       
  1969             s << toHex((ushort)ti.chars[i].unicode(), buf);
       
  1970         }
       
  1971         *currentPage << "> >>\n"
       
  1972             "BDC\n"
       
  1973             "<";
       
  1974         int ge = end == ti.num_chars ? ti.num_glyphs : logClusters[end];
       
  1975         for (int gs = logClusters[pos]; gs < ge; ++gs)
       
  1976             *currentPage << toHex((ushort)ti.glyphs[gs].glyph, buf);
       
  1977         *currentPage << "> Tj\n"
       
  1978             "EMC\n";
       
  1979         pos = end;
       
  1980     } while (pos < ti.num_chars);
       
  1981 #else
       
  1982     qreal last_x = 0.;
       
  1983     qreal last_y = 0.;
       
  1984     for (int i = 0; i < glyphs.size(); ++i) {
       
  1985         qreal x = positions[i].x.toReal();
       
  1986         qreal y = positions[i].y.toReal();
       
  1987         if (synthesized & QFontEngine::SynthesizedItalic)
       
  1988             x += .3*y;
       
  1989         x /= stretch;
       
  1990         char buf[5];
       
  1991         int g = font->addGlyph(glyphs[i]);
       
  1992         *currentPage << x - last_x << last_y - y << "Td <"
       
  1993                      << QPdf::toHex((ushort)g, buf) << "> Tj\n";
       
  1994         last_x = x;
       
  1995         last_y = y;
       
  1996     }
       
  1997     if (synthesized & QFontEngine::SynthesizedBold) {
       
  1998         *currentPage << stretch << (synthesized & QFontEngine::SynthesizedItalic
       
  1999                             ? "0 .3 -1 0 0 Tm\n"
       
  2000                             : "0 0 -1 0 0 Tm\n");
       
  2001         *currentPage << "/Span << /ActualText <> >> BDC\n";
       
  2002         last_x = 0.5*fe->lineThickness().toReal();
       
  2003         last_y = 0.;
       
  2004         for (int i = 0; i < glyphs.size(); ++i) {
       
  2005             qreal x = positions[i].x.toReal();
       
  2006             qreal y = positions[i].y.toReal();
       
  2007             if (synthesized & QFontEngine::SynthesizedItalic)
       
  2008                 x += .3*y;
       
  2009             x /= stretch;
       
  2010             char buf[5];
       
  2011             int g = font->addGlyph(glyphs[i]);
       
  2012             *currentPage << x - last_x << last_y - y << "Td <"
       
  2013                         << QPdf::toHex((ushort)g, buf) << "> Tj\n";
       
  2014             last_x = x;
       
  2015             last_y = y;
       
  2016         }
       
  2017         *currentPage << "EMC\n";
       
  2018     }
       
  2019 #endif
       
  2020 
       
  2021     *currentPage << "ET\n";
       
  2022 }
       
  2023 
       
  2024 QRect QPdfBaseEnginePrivate::paperRect() const
       
  2025 {
       
  2026     int w;
       
  2027     int h;
       
  2028     if (paperSize == QPrinter::Custom) {
       
  2029         w = qRound(customPaperSize.width()*resolution/72.);
       
  2030         h = qRound(customPaperSize.height()*resolution/72.);
       
  2031     } else {
       
  2032 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
       
  2033         if (QCUPSSupport::isAvailable() && !cupsPaperRect.isNull()) {
       
  2034             QRect r = cupsPaperRect;
       
  2035             w = r.width();
       
  2036             h = r.height();
       
  2037         } else
       
  2038 #endif
       
  2039         {
       
  2040             QPdf::PaperSize s = QPdf::paperSize(paperSize);
       
  2041             w = s.width;
       
  2042             h = s.height;
       
  2043         }
       
  2044         w = qRound(w*resolution/72.);
       
  2045         h = qRound(h*resolution/72.);
       
  2046     }
       
  2047     if (orientation == QPrinter::Portrait)
       
  2048         return QRect(0, 0, w, h);
       
  2049     else
       
  2050         return QRect(0, 0, h, w);
       
  2051 }
       
  2052 
       
  2053 QRect QPdfBaseEnginePrivate::pageRect() const
       
  2054 {
       
  2055     if(fullPage)
       
  2056         return paperRect();
       
  2057 
       
  2058     QRect r;
       
  2059 
       
  2060 #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
       
  2061     if (!hasCustomPageMargins && QCUPSSupport::isAvailable() && !cupsPageRect.isNull()) {
       
  2062         r = cupsPageRect;
       
  2063         if (r == cupsPaperRect) {
       
  2064             // if cups doesn't define any margins, give it at least approx 3.5 mm
       
  2065             r = QRect(10, 10, r.width() - 20, r.height() - 20);
       
  2066         }
       
  2067     } else
       
  2068 #endif
       
  2069     {
       
  2070         QPdf::PaperSize s;
       
  2071         if (paperSize == QPrinter::Custom) {
       
  2072             s.width = qRound(customPaperSize.width());
       
  2073             s.height = qRound(customPaperSize.height());
       
  2074         } else {
       
  2075             s = QPdf::paperSize(paperSize);
       
  2076         }
       
  2077         if (hasCustomPageMargins)
       
  2078             r = QRect(0, 0, s.width, s.height);
       
  2079         else
       
  2080             r = QRect(72/3, 72/3, s.width - 2*72/3, s.height - 2*72/3);
       
  2081     }
       
  2082 
       
  2083     int x = qRound(r.left()*resolution/72.);
       
  2084     int y = qRound(r.top()*resolution/72.);
       
  2085     int w = qRound(r.width()*resolution/72.);
       
  2086     int h = qRound(r.height()*resolution/72.);
       
  2087     if (orientation == QPrinter::Portrait)
       
  2088         r = QRect(x, y, w, h);
       
  2089     else
       
  2090         r = QRect(y, x, h, w);
       
  2091 
       
  2092     if (hasCustomPageMargins) {
       
  2093         r.adjust(qRound(leftMargin*(resolution/72.)),
       
  2094                  qRound(topMargin*(resolution/72.)),
       
  2095                  -qRound(rightMargin*(resolution/72.)),
       
  2096                  -qRound(bottomMargin*(resolution/72.)));
       
  2097     }
       
  2098     return r;
       
  2099 }
       
  2100 
       
  2101 #endif
       
  2102 
       
  2103 QT_END_NAMESPACE