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