diff options
Diffstat (limited to 'src/gui/painting/qprintengine_pdf.cpp')
-rw-r--r-- | src/gui/painting/qprintengine_pdf.cpp | 1522 |
1 files changed, 458 insertions, 1064 deletions
diff --git a/src/gui/painting/qprintengine_pdf.cpp b/src/gui/painting/qprintengine_pdf.cpp index 4b5d41cf1c..b796cd59e7 100644 --- a/src/gui/painting/qprintengine_pdf.cpp +++ b/src/gui/painting/qprintengine_pdf.cpp @@ -42,22 +42,15 @@ #include <QtGui/qprintengine.h> #include <qiodevice.h> -#include <qpainter.h> -#include <qbitmap.h> -#include <qpainterpath.h> -#include <qpaintdevice.h> #include <qfile.h> #include <qdebug.h> -#include <qimagewriter.h> #include <qbuffer.h> -#include <qdatetime.h> +#include "private/qcups_p.h" +#include "qprinterinfo.h" #ifndef QT_NO_PRINTER #include <limits.h> #include <math.h> -#ifndef QT_NO_COMPRESS -#include <zlib.h> -#endif #if defined(Q_OS_WINCE) #include "qwinfunctions_wince.h" @@ -66,31 +59,13 @@ #include "qprintengine_pdf_p.h" #include "private/qdrawhelper_p.h" -QT_BEGIN_NAMESPACE - -//#define FONT_DUMP - -// might be helpful for smooth transforms of images -// Can't use it though, as gs generates completely wrong images if this is true. -static const bool interpolateImages = false; - -#ifdef QT_NO_COMPRESS -static const bool do_compress = false; -#else -static const bool do_compress = true; +#ifdef Q_OS_UNIX +#include "private/qcore_unix_p.h" // overrides QT_OPEN #endif -QPdfPage::QPdfPage() - : QPdf::ByteStream(true) // Enable file backing -{ -} +QT_BEGIN_NAMESPACE -void QPdfPage::streamImage(int w, int h, int object) -{ - *this << w << "0 0 " << -h << "0 " << h << "cm /Im" << object << " Do\n"; - if (!images.contains(object)) - images.append(object); -} +//#define FONT_DUMP inline QPaintEngine::PaintEngineFeatures qt_pdf_decide_features() @@ -109,6 +84,32 @@ inline QPaintEngine::PaintEngineFeatures qt_pdf_decide_features() QPdfEngine::QPdfEngine(QPrinter::PrinterMode m) : QPdfBaseEngine(*new QPdfEnginePrivate(m), qt_pdf_decide_features()) { + Q_D(QPdfEngine); +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + if (QCUPSSupport::isAvailable()) { + QCUPSSupport cups; + const cups_dest_t* printers = cups.availablePrinters(); + int prnCount = cups.availablePrintersCount(); + + for (int i = 0; i < prnCount; ++i) { + if (printers[i].is_default) { + d->printerName = QString::fromLocal8Bit(printers[i].name); + break; + } + } + + } else +#endif + { + d->printerName = QString::fromLocal8Bit(qgetenv("PRINTER")); + if (d->printerName.isEmpty()) + d->printerName = QString::fromLocal8Bit(qgetenv("LPDEST")); + if (d->printerName.isEmpty()) + d->printerName = QString::fromLocal8Bit(qgetenv("NPRINTER")); + if (d->printerName.isEmpty()) + d->printerName = QString::fromLocal8Bit(qgetenv("NGPRINTER")); + } + state = QPrinter::Idle; } @@ -120,1121 +121,514 @@ bool QPdfEngine::begin(QPaintDevice *pdev) { Q_D(QPdfEngine); - if(!QPdfBaseEngine::begin(pdev)) { + if (!d->openPrintDevice()) { state = QPrinter::Error; return false; } - d->stream->setDevice(d->outDevice); - - d->streampos = 0; - d->hasPen = true; - d->hasBrush = false; - d->clipEnabled = false; - d->allClipped = false; - - d->xrefPositions.clear(); - d->pageRoot = 0; - d->catalog = 0; - d->info = 0; - d->graphicsState = 0; - d->patternColorSpace = 0; - - d->pages.clear(); - d->imageCache.clear(); - - setActive(true); state = QPrinter::Active; - d->writeHeader(); - newPage(); - return true; + return QPdfBaseEngine::begin(pdev); } bool QPdfEngine::end() { Q_D(QPdfEngine); - d->writeTail(); - d->stream->unsetDevice(); QPdfBaseEngine::end(); - setActive(false); - state = QPrinter::Idle; - return true; -} - - -void QPdfEngine::drawPixmap (const QRectF &rectangle, const QPixmap &pixmap, const QRectF &sr) -{ - if (sr.isEmpty() || rectangle.isEmpty() || pixmap.isNull()) - return; - Q_D(QPdfEngine); - - QBrush b = d->brush; - - QRect sourceRect = sr.toRect(); - QPixmap pm = sourceRect != pixmap.rect() ? pixmap.copy(sourceRect) : pixmap; - QImage image = pm.toImage(); - bool bitmap = true; - const int object = d->addImage(image, &bitmap, pm.cacheKey()); - if (object < 0) - return; - - *d->currentPage << "q\n/GSa gs\n"; - *d->currentPage - << QPdf::generateMatrix(QTransform(rectangle.width() / sr.width(), 0, 0, rectangle.height() / sr.height(), - rectangle.x(), rectangle.y()) * (d->simplePen ? QTransform() : d->stroker.matrix)); - if (bitmap) { - // set current pen as d->brush - d->brush = d->pen.brush(); - } - setBrush(); - d->currentPage->streamImage(image.width(), image.height(), object); - *d->currentPage << "Q\n"; - - d->brush = b; -} - -void QPdfEngine::drawImage(const QRectF &rectangle, const QImage &image, const QRectF &sr, Qt::ImageConversionFlags) -{ - if (sr.isEmpty() || rectangle.isEmpty() || image.isNull()) - return; - Q_D(QPdfEngine); - - QRect sourceRect = sr.toRect(); - QImage im = sourceRect != image.rect() ? image.copy(sourceRect) : image; - bool bitmap = true; - const int object = d->addImage(im, &bitmap, im.cacheKey()); - if (object < 0) - return; - - *d->currentPage << "q\n/GSa gs\n"; - *d->currentPage - << QPdf::generateMatrix(QTransform(rectangle.width() / sr.width(), 0, 0, rectangle.height() / sr.height(), - rectangle.x(), rectangle.y()) * (d->simplePen ? QTransform() : d->stroker.matrix)); - setBrush(); - d->currentPage->streamImage(im.width(), im.height(), object); - *d->currentPage << "Q\n"; -} - -void QPdfEngine::drawTiledPixmap (const QRectF &rectangle, const QPixmap &pixmap, const QPointF &point) -{ - Q_D(QPdfEngine); - - bool bitmap = (pixmap.depth() == 1); - QBrush b = d->brush; - QPointF bo = d->brushOrigin; - bool hp = d->hasPen; - d->hasPen = false; - bool hb = d->hasBrush; - d->hasBrush = true; - - d->brush = QBrush(pixmap); - if (bitmap) - // #### fix bitmap case where we have a brush pen - d->brush.setColor(d->pen.color()); - - d->brushOrigin = -point; - *d->currentPage << "q\n"; - setBrush(); - - drawRects(&rectangle, 1); - *d->currentPage << "Q\n"; - - d->hasPen = hp; - d->hasBrush = hb; - d->brush = b; - d->brushOrigin = bo; -} - - -void QPdfEngine::setBrush() -{ - Q_D(QPdfEngine); - Qt::BrushStyle style = d->brush.style(); - if (style == Qt::NoBrush) - return; - - bool specifyColor; - int gStateObject = 0; - int patternObject = d->addBrushPattern(d->stroker.matrix, &specifyColor, &gStateObject); - - *d->currentPage << (patternObject ? "/PCSp cs " : "/CSp cs "); - if (specifyColor) { - QColor rgba = d->brush.color(); - if (d->colorMode == QPrinter::GrayScale) { - qreal gray = qGray(rgba.rgba())/255.; - *d->currentPage << gray << gray << gray; - } else { - *d->currentPage << rgba.redF() - << rgba.greenF() - << rgba.blueF(); - } - } - if (patternObject) - *d->currentPage << "/Pat" << patternObject; - *d->currentPage << "scn\n"; - if (gStateObject) - *d->currentPage << "/GState" << gStateObject << "gs\n"; - else - *d->currentPage << "/GSa gs\n"; -} + d->closePrintDevice(); + state = QPrinter::Idle; -QPaintEngine::Type QPdfEngine::type() const -{ - return QPaintEngine::Pdf; + return true; } bool QPdfEngine::newPage() { - Q_D(QPdfEngine); - if (!isActive()) - return false; - d->newPage(); return QPdfBaseEngine::newPage(); } -QPdfEnginePrivate::QPdfEnginePrivate(QPrinter::PrinterMode m) - : QPdfBaseEnginePrivate(m) -{ - streampos = 0; - - stream = new QDataStream; - pageOrder = QPrinter::FirstPageFirst; - orientation = QPrinter::Portrait; - fullPage = false; -} - -QPdfEnginePrivate::~QPdfEnginePrivate() -{ - delete stream; -} - - -#ifdef USE_NATIVE_GRADIENTS -int QPdfEnginePrivate::gradientBrush(const QBrush &b, const QMatrix &matrix, int *gStateObject) -{ - const QGradient *gradient = b.gradient(); - if (!gradient) - return 0; - - QTransform inv = matrix.inverted(); - QPointF page_rect[4] = { inv.map(QPointF(0, 0)), - inv.map(QPointF(width_, 0)), - inv.map(QPointF(0, height_)), - inv.map(QPointF(width_, height_)) }; - - bool opaque = b.isOpaque(); - - QByteArray shader; - QByteArray alphaShader; - if (gradient->type() == QGradient::LinearGradient) { - const QLinearGradient *lg = static_cast<const QLinearGradient *>(gradient); - shader = QPdf::generateLinearGradientShader(lg, page_rect); - if (!opaque) - alphaShader = QPdf::generateLinearGradientShader(lg, page_rect, true); - } else { - // ############# - return 0; +int QPdfEngine::metric(QPaintDevice::PaintDeviceMetric m) const +{ + return QPdfBaseEngine::metric(m); +} + +void QPdfEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value) +{ + Q_D(QPdfBaseEngine); + switch (int(key)) { + case PPK_CollateCopies: + d->collate = value.toBool(); + break; + case PPK_ColorMode: + d->colorMode = QPrinter::ColorMode(value.toInt()); + break; + case PPK_Creator: + d->creator = value.toString(); + break; + case PPK_DocumentName: + d->title = value.toString(); + break; + case PPK_FullPage: + d->fullPage = value.toBool(); + break; + case PPK_CopyCount: // fallthrough + case PPK_NumberOfCopies: + d->copies = value.toInt(); + break; + case PPK_Orientation: + d->orientation = QPrinter::Orientation(value.toInt()); + break; + case PPK_OutputFileName: + d->outputFileName = value.toString(); + break; + case PPK_PageOrder: + d->pageOrder = QPrinter::PageOrder(value.toInt()); + break; + case PPK_PaperSize: + d->paperSize = QPrinter::PaperSize(value.toInt()); + break; + case PPK_PaperSource: + d->paperSource = QPrinter::PaperSource(value.toInt()); + break; + case PPK_PrinterName: + d->printerName = value.toString(); + break; + case PPK_PrinterProgram: + d->printProgram = value.toString(); + break; + case PPK_Resolution: + d->resolution = value.toInt(); + break; + case PPK_SelectionOption: + d->selectionOption = value.toString(); + break; + case PPK_FontEmbedding: + d->embedFonts = value.toBool(); + break; + case PPK_Duplex: + d->duplex = static_cast<QPrinter::DuplexMode> (value.toInt()); + break; + case PPK_CupsPageRect: + d->cupsPageRect = value.toRect(); + break; + case PPK_CupsPaperRect: + d->cupsPaperRect = value.toRect(); + break; + case PPK_CupsOptions: + d->cupsOptions = value.toStringList(); + break; + case PPK_CupsStringPageSize: + d->cupsStringPageSize = value.toString(); + break; + case PPK_CustomPaperSize: + d->paperSize = QPrinter::Custom; + d->customPaperSize = value.toSizeF(); + break; + case PPK_PageMargins: + { + QList<QVariant> margins(value.toList()); + Q_ASSERT(margins.size() == 4); + d->leftMargin = margins.at(0).toReal(); + d->topMargin = margins.at(1).toReal(); + d->rightMargin = margins.at(2).toReal(); + d->bottomMargin = margins.at(3).toReal(); + d->hasCustomPageMargins = true; + break; } - int shaderObject = addXrefEntry(-1); - write(shader); - - QByteArray str; - QPdf::ByteStream s(&str); - s << "<<\n" - "/Type /Pattern\n" - "/PatternType 2\n" - "/Shading " << shaderObject << "0 R\n" - "/Matrix [" - << matrix.m11() - << matrix.m12() - << matrix.m21() - << matrix.m22() - << matrix.dx() - << matrix.dy() << "]\n"; - s << ">>\n" - "endobj\n"; - - int patternObj = addXrefEntry(-1); - write(str); - currentPage->patterns.append(patternObj); + default: + break; + } +} - if (!opaque) { - bool ca = true; - QGradientStops stops = gradient->stops(); - int a = stops.at(0).second.alpha(); - for (int i = 1; i < stops.size(); ++i) { - if (stops.at(i).second.alpha() != a) { - ca = false; - break; - } - } - if (ca) { - *gStateObject = addConstantAlphaObject(stops.at(0).second.alpha()); +QVariant QPdfEngine::property(PrintEnginePropertyKey key) const +{ + Q_D(const QPdfBaseEngine); + + QVariant ret; + switch (int(key)) { + case PPK_CollateCopies: + ret = d->collate; + break; + case PPK_ColorMode: + ret = d->colorMode; + break; + case PPK_Creator: + ret = d->creator; + break; + case PPK_DocumentName: + ret = d->title; + break; + case PPK_FullPage: + ret = d->fullPage; + break; + case PPK_CopyCount: + ret = d->copies; + break; + case PPK_SupportsMultipleCopies: +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + if (QCUPSSupport::isAvailable()) + ret = true; + else +#endif + ret = false; + break; + case PPK_NumberOfCopies: +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + if (QCUPSSupport::isAvailable()) + ret = 1; + else +#endif + ret = d->copies; + break; + case PPK_Orientation: + ret = d->orientation; + break; + case PPK_OutputFileName: + ret = d->outputFileName; + break; + case PPK_PageOrder: + ret = d->pageOrder; + break; + case PPK_PaperSize: + ret = d->paperSize; + break; + case PPK_PaperSource: + ret = d->paperSource; + break; + case PPK_PrinterName: + ret = d->printerName; + break; + case PPK_PrinterProgram: + ret = d->printProgram; + break; + case PPK_Resolution: + ret = d->resolution; + break; + case PPK_SupportedResolutions: + ret = QList<QVariant>() << 72; + break; + case PPK_PaperRect: + ret = d->paperRect(); + break; + case PPK_PageRect: + ret = d->pageRect(); + break; + case PPK_SelectionOption: + ret = d->selectionOption; + break; + case PPK_FontEmbedding: + ret = d->embedFonts; + break; + case PPK_Duplex: + ret = d->duplex; + break; + case PPK_CupsPageRect: + ret = d->cupsPageRect; + break; + case PPK_CupsPaperRect: + ret = d->cupsPaperRect; + break; + case PPK_CupsOptions: + ret = d->cupsOptions; + break; + case PPK_CupsStringPageSize: + ret = d->cupsStringPageSize; + break; + case PPK_CustomPaperSize: + ret = d->customPaperSize; + break; + case PPK_PageMargins: + { + QList<QVariant> margins; + if (d->hasCustomPageMargins) { + margins << d->leftMargin << d->topMargin + << d->rightMargin << d->bottomMargin; } else { - int alphaShaderObject = addXrefEntry(-1); - write(alphaShader); - - QByteArray content; - QPdf::ByteStream c(&content); - c << "/Shader" << alphaShaderObject << "sh\n"; - - QByteArray form; - QPdf::ByteStream f(&form); - f << "<<\n" - "/Type /XObject\n" - "/Subtype /Form\n" - "/BBox [0 0 " << width_ << height_ << "]\n" - "/Group <</S /Transparency >>\n" - "/Resources <<\n" - "/Shading << /Shader" << alphaShaderObject << alphaShaderObject << "0 R >>\n" - ">>\n"; - - f << "/Length " << content.length() << "\n" - ">>\n" - "stream\n" - << content - << "endstream\n" - "endobj\n"; - - int softMaskFormObject = addXrefEntry(-1); - write(form); - *gStateObject = addXrefEntry(-1); - xprintf("<< /SMask << /S /Alpha /G %d 0 R >> >>\n" - "endobj\n", softMaskFormObject); - currentPage->graphicStates.append(*gStateObject); + const qreal defaultMargin = 10; // ~3.5 mm + margins << defaultMargin << defaultMargin + << defaultMargin << defaultMargin; } + ret = margins; + break; } - - return patternObj; -} -#endif - -int QPdfEnginePrivate::addConstantAlphaObject(int brushAlpha, int penAlpha) -{ - if (brushAlpha == 255 && penAlpha == 255) - return 0; - int object = alphaCache.value(QPair<uint, uint>(brushAlpha, penAlpha), 0); - if (!object) { - object = addXrefEntry(-1); - QByteArray alphaDef; - QPdf::ByteStream s(&alphaDef); - s << "<<\n/ca " << (brushAlpha/qreal(255.)) << '\n'; - s << "/CA " << (penAlpha/qreal(255.)) << "\n>>"; - xprintf("%s\nendobj\n", alphaDef.constData()); - alphaCache.insert(QPair<uint, uint>(brushAlpha, penAlpha), object); + default: + break; } - if (currentPage->graphicStates.indexOf(object) < 0) - currentPage->graphicStates.append(object); - - return object; + return ret; } -int QPdfEnginePrivate::addBrushPattern(const QTransform &m, bool *specifyColor, int *gStateObject) -{ - int paintType = 2; // Uncolored tiling - int w = 8; - int h = 8; - - *specifyColor = true; - *gStateObject = 0; - QTransform matrix = m; - matrix.translate(brushOrigin.x(), brushOrigin.y()); - matrix = matrix * pageMatrix(); - //qDebug() << brushOrigin << matrix; - - Qt::BrushStyle style = brush.style(); - if (style == Qt::LinearGradientPattern) {// && style <= Qt::ConicalGradientPattern) { -#ifdef USE_NATIVE_GRADIENTS - *specifyColor = false; - return gradientBrush(b, matrix, gStateObject); +#ifndef QT_NO_LPR +static void closeAllOpenFds() +{ + // hack time... getting the maximum number of open + // files, if possible. if not we assume it's the + // larger of 256 and the fd we got + int i; +#if defined(_SC_OPEN_MAX) + i = (int)sysconf(_SC_OPEN_MAX); +#elif defined(_POSIX_OPEN_MAX) + i = (int)_POSIX_OPEN_MAX; +#elif defined(OPEN_MAX) + i = (int)OPEN_MAX; #else - return 0; + i = 256; #endif - } - - if ((!brush.isOpaque() && brush.style() < Qt::LinearGradientPattern) || opacity != 1.0) - *gStateObject = addConstantAlphaObject(qRound(brush.color().alpha() * opacity), - qRound(pen.color().alpha() * opacity)); - - int imageObject = -1; - QByteArray pattern = QPdf::patternForBrush(brush); - if (pattern.isEmpty()) { - if (brush.style() != Qt::TexturePattern) - return 0; - QImage image = brush.texture().toImage(); - bool bitmap = true; - imageObject = addImage(image, &bitmap, brush.texture().cacheKey()); - if (imageObject != -1) { - QImage::Format f = image.format(); - if (f != QImage::Format_MonoLSB && f != QImage::Format_Mono) { - paintType = 1; // Colored tiling - *specifyColor = false; - } - w = image.width(); - h = image.height(); - QTransform m(w, 0, 0, -h, 0, h); - QPdf::ByteStream s(&pattern); - s << QPdf::generateMatrix(m); - s << "/Im" << imageObject << " Do\n"; - } - } - - QByteArray str; - QPdf::ByteStream s(&str); - s << "<<\n" - "/Type /Pattern\n" - "/PatternType 1\n" - "/PaintType " << paintType << "\n" - "/TilingType 1\n" - "/BBox [0 0 " << w << h << "]\n" - "/XStep " << w << "\n" - "/YStep " << h << "\n" - "/Matrix [" - << matrix.m11() - << matrix.m12() - << matrix.m21() - << matrix.m22() - << matrix.dx() - << matrix.dy() << "]\n" - "/Resources \n<< "; // open resource tree - if (imageObject > 0) { - s << "/XObject << /Im" << imageObject << ' ' << imageObject << "0 R >> "; - } - s << ">>\n" - "/Length " << pattern.length() << "\n" - ">>\n" - "stream\n" - << pattern - << "endstream\n" - "endobj\n"; - - int patternObj = addXrefEntry(-1); - write(str); - currentPage->patterns.append(patternObj); - return patternObj; + // leave stdin/out/err untouched + while(--i > 2) + QT_CLOSE(i); } +#endif -/*! - * Adds an image to the pdf and return the pdf-object id. Returns -1 if adding the image failed. - */ -int QPdfEnginePrivate::addImage(const QImage &img, bool *bitmap, qint64 serial_no) +bool QPdfEnginePrivate::openPrintDevice() { - if (img.isNull()) - return -1; - - int object = imageCache.value(serial_no); - if(object) - return object; + if(outDevice) + return false; - QImage image = img; - QImage::Format format = image.format(); - if (image.depth() == 1 && *bitmap && img.colorTable().size() == 2 - && img.colorTable().at(0) == QColor(Qt::black).rgba() - && img.colorTable().at(1) == QColor(Qt::white).rgba()) - { - if (format == QImage::Format_MonoLSB) - image = image.convertToFormat(QImage::Format_Mono); - format = QImage::Format_Mono; - } else { - *bitmap = false; - if (format != QImage::Format_RGB32 && format != QImage::Format_ARGB32) { - image = image.convertToFormat(QImage::Format_ARGB32); - format = QImage::Format_ARGB32; + if (!outputFileName.isEmpty()) { + QFile *file = new QFile(outputFileName); + if (! file->open(QFile::WriteOnly|QFile::Truncate)) { + delete file; + return false; } - } - - int w = image.width(); - int h = image.height(); - int d = image.depth(); - - if (format == QImage::Format_Mono) { - int bytesPerLine = (w + 7) >> 3; - QByteArray data; - data.resize(bytesPerLine * h); - char *rawdata = data.data(); - for (int y = 0; y < h; ++y) { - memcpy(rawdata, image.scanLine(y), bytesPerLine); - rawdata += bytesPerLine; + outDevice = file; +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + } else if (QCUPSSupport::isAvailable()) { + QCUPSSupport cups; + QPair<int, QString> ret = cups.tempFd(); + if (ret.first < 0) { + qWarning("QPdfPrinter: Could not open temporary file to print"); + return false; } - object = writeImage(data, w, h, d, 0, 0); + cupsTempFile = ret.second; + outDevice = new QFile(); + static_cast<QFile *>(outDevice)->open(ret.first, QIODevice::WriteOnly); +#endif +#ifndef QT_NO_LPR } else { - QByteArray softMaskData; - bool dct = false; - QByteArray imageData; - bool hasAlpha = false; - bool hasMask = false; - - if (QImageWriter::supportedImageFormats().contains("jpeg") && colorMode != QPrinter::GrayScale) { - QBuffer buffer(&imageData); - QImageWriter writer(&buffer, "jpeg"); - writer.setQuality(94); - writer.write(image); - dct = true; + QString pr; + if (!printerName.isEmpty()) + pr = printerName; + int fds[2]; + if (qt_safe_pipe(fds) != 0) { + qWarning("QPdfPrinter: Could not open pipe to print"); + return false; + } - if (format != QImage::Format_RGB32) { - softMaskData.resize(w * h); - uchar *sdata = (uchar *)softMaskData.data(); - for (int y = 0; y < h; ++y) { - const QRgb *rgb = (const QRgb *)image.scanLine(y); - for (int x = 0; x < w; ++x) { - uchar alpha = qAlpha(*rgb); - *sdata++ = alpha; - hasMask |= (alpha < 255); - hasAlpha |= (alpha != 0 && alpha != 255); - ++rgb; - } - } + pid_t pid = fork(); + if (pid == 0) { // child process + // if possible, exit quickly, so the actual lp/lpr + // becomes a child of init, and ::waitpid() is + // guaranteed not to wait. + if (fork() > 0) { + closeAllOpenFds(); + + // try to replace this process with "true" - this prevents + // global destructors from being called (that could possibly + // do wrong things to the parent process) + (void)execlp("true", "true", (char *)0); + (void)execl("/bin/true", "true", (char *)0); + (void)execl("/usr/bin/true", "true", (char *)0); + ::_exit(0); } - } else { - imageData.resize(colorMode == QPrinter::GrayScale ? w * h : 3 * w * h); - uchar *data = (uchar *)imageData.data(); - softMaskData.resize(w * h); - uchar *sdata = (uchar *)softMaskData.data(); - for (int y = 0; y < h; ++y) { - const QRgb *rgb = (const QRgb *)image.scanLine(y); - if (colorMode == QPrinter::GrayScale) { - for (int x = 0; x < w; ++x) { - *(data++) = qGray(*rgb); - uchar alpha = qAlpha(*rgb); - *sdata++ = alpha; - hasMask |= (alpha < 255); - hasAlpha |= (alpha != 0 && alpha != 255); - ++rgb; - } - } else { - for (int x = 0; x < w; ++x) { - *(data++) = qRed(*rgb); - *(data++) = qGreen(*rgb); - *(data++) = qBlue(*rgb); - uchar alpha = qAlpha(*rgb); - *sdata++ = alpha; - hasMask |= (alpha < 255); - hasAlpha |= (alpha != 0 && alpha != 255); - ++rgb; + qt_safe_dup2(fds[0], 0, 0); + + closeAllOpenFds(); + + if (!printProgram.isEmpty()) { + if (!selectionOption.isEmpty()) + pr.prepend(selectionOption); + else + pr.prepend(QLatin1String("-P")); + (void)execlp(printProgram.toLocal8Bit().data(), printProgram.toLocal8Bit().data(), + pr.toLocal8Bit().data(), (char *)0); + } else { + // if no print program has been specified, be smart + // about the option string too. + QList<QByteArray> lprhack; + QList<QByteArray> lphack; + QByteArray media; + if (!pr.isEmpty() || !selectionOption.isEmpty()) { + if (!selectionOption.isEmpty()) { + QStringList list = selectionOption.split(QLatin1Char(' ')); + for (int i = 0; i < list.size(); ++i) + lprhack.append(list.at(i).toLocal8Bit()); + lphack = lprhack; + } else { + lprhack.append("-P"); + lphack.append("-d"); } + lprhack.append(pr.toLocal8Bit()); + lphack.append(pr.toLocal8Bit()); } - } - if (format == QImage::Format_RGB32) - hasAlpha = hasMask = false; - } - int maskObject = 0; - int softMaskObject = 0; - if (hasAlpha) { - softMaskObject = writeImage(softMaskData, w, h, 8, 0, 0); - } else if (hasMask) { - // dither the soft mask to 1bit and add it. This also helps PDF viewers - // without transparency support - int bytesPerLine = (w + 7) >> 3; - QByteArray mask(bytesPerLine * h, 0); - uchar *mdata = (uchar *)mask.data(); - const uchar *sdata = (const uchar *)softMaskData.constData(); - for (int y = 0; y < h; ++y) { - for (int x = 0; x < w; ++x) { - if (*sdata) - mdata[x>>3] |= (0x80 >> (x&7)); - ++sdata; + lphack.append("-s"); + + char ** lpargs = new char *[lphack.size()+6]; + char lp[] = "lp"; + lpargs[0] = lp; + int i; + for (i = 0; i < lphack.size(); ++i) + lpargs[i+1] = (char *)lphack.at(i).constData(); +#ifndef Q_OS_OSF + if (QPdf::paperSizeToString(paperSize)) { + char dash_o[] = "-o"; + lpargs[++i] = dash_o; + lpargs[++i] = const_cast<char *>(QPdf::paperSizeToString(paperSize)); + lpargs[++i] = dash_o; + media = "media="; + media += QPdf::paperSizeToString(paperSize); + lpargs[++i] = media.data(); } - mdata += bytesPerLine; +#endif + lpargs[++i] = 0; + char **lprargs = new char *[lprhack.size()+2]; + char lpr[] = "lpr"; + lprargs[0] = lpr; + for (int i = 0; i < lprhack.size(); ++i) + lprargs[i+1] = (char *)lprhack[i].constData(); + lprargs[lprhack.size() + 1] = 0; + (void)execvp("lp", lpargs); + (void)execvp("lpr", lprargs); + (void)execv("/bin/lp", lpargs); + (void)execv("/bin/lpr", lprargs); + (void)execv("/usr/bin/lp", lpargs); + (void)execv("/usr/bin/lpr", lprargs); + + delete []lpargs; + delete []lprargs; } - maskObject = writeImage(mask, w, h, 1, 0, 0); - } - object = writeImage(imageData, w, h, colorMode == QPrinter::GrayScale ? 8 : 32, - maskObject, softMaskObject, dct); - } - imageCache.insert(serial_no, object); - return object; -} - -void QPdfEnginePrivate::drawTextItem(const QPointF &p, const QTextItemInt &ti) -{ - if (ti.charFormat.isAnchor()) { - qreal size = ti.fontEngine->fontDef.pixelSize; -#ifdef Q_WS_WIN - if (ti.fontEngine->type() == QFontEngine::Win) { - QFontEngineWin *fe = static_cast<QFontEngineWin *>(ti.fontEngine); - size = fe->tm.tmHeight; + // if we couldn't exec anything, close the fd, + // wait for a second so the parent process (the + // child of the GUI process) has exited. then + // exit. + QT_CLOSE(0); + (void)::sleep(1); + ::_exit(0); } -#endif - int synthesized = ti.fontEngine->synthesized(); - qreal stretch = synthesized & QFontEngine::SynthesizedStretch ? ti.fontEngine->fontDef.stretch/100. : 1.; + // parent process + QT_CLOSE(fds[0]); + fd = fds[1]; + (void)qt_safe_waitpid(pid, 0, 0); - QTransform trans; - // Build text rendering matrix (Trm). We need it to map the text area to user - // space units on the PDF page. - trans = QTransform(size*stretch, 0, 0, size, 0, 0); - // Apply text matrix (Tm). - trans *= QTransform(1,0,0,-1,p.x(),p.y()); - // Apply page displacement (Identity for first page). - trans *= stroker.matrix; - // Apply Current Transformation Matrix (CTM) - trans *= pageMatrix(); - qreal x1, y1, x2, y2; - trans.map(0, 0, &x1, &y1); - trans.map(ti.width.toReal()/size, (ti.ascent.toReal()-ti.descent.toReal())/size, &x2, &y2); + if (fd < 0) + return false; - uint annot = addXrefEntry(-1); -#ifdef Q_DEBUG_PDF_LINKS - xprintf("<<\n/Type /Annot\n/Subtype /Link\n/Rect [%f %f %f %f]\n/Border [16 16 1]\n/A <<\n", -#else - xprintf("<<\n/Type /Annot\n/Subtype /Link\n/Rect [%f %f %f %f]\n/Border [0 0 0]\n/A <<\n", + outDevice = new QFile(); + static_cast<QFile *>(outDevice)->open(fd, QIODevice::WriteOnly); #endif - static_cast<double>(x1), - static_cast<double>(y1), - static_cast<double>(x2), - static_cast<double>(y2)); - xprintf("/Type /Action\n/S /URI\n/URI (%s)\n", - ti.charFormat.anchorHref().toLatin1().constData()); - xprintf(">>\n>>\n"); - xprintf("endobj\n"); - - if (!currentPage->annotations.contains(annot)) { - currentPage->annotations.append(annot); - } } - QPdfBaseEnginePrivate::drawTextItem(p, ti); -} - -QTransform QPdfEnginePrivate::pageMatrix() const -{ - qreal scale = 72./resolution; - QTransform tmp(scale, 0.0, 0.0, -scale, 0.0, height()); - if (!fullPage) { - QRect r = pageRect(); - tmp.translate(r.left(), r.top()); - } - return tmp; -} - -void QPdfEnginePrivate::newPage() -{ - if (currentPage && currentPage->pageSize.isEmpty()) - currentPage->pageSize = QSize(width(), height()); - writePage(); - - delete currentPage; - currentPage = new QPdfPage; - currentPage->pageSize = QSize(width(), height()); - stroker.stream = currentPage; - pages.append(requestObject()); - - *currentPage << "/GSa gs /CSp cs /CSp CS\n" - << QPdf::generateMatrix(pageMatrix()) - << "q q\n"; + return true; } - -// For strings up to 10000 bytes only ! -void QPdfEnginePrivate::xprintf(const char* fmt, ...) +void QPdfEnginePrivate::closePrintDevice() { - if (!stream) + if (!outDevice) return; + outDevice->close(); + if (fd >= 0) +#if defined(Q_OS_WIN) && defined(_MSC_VER) && _MSC_VER >= 1400 + ::_close(fd); +#else + ::close(fd); +#endif + fd = -1; + delete outDevice; + outDevice = 0; + +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + if (!cupsTempFile.isEmpty()) { + QString tempFile = cupsTempFile; + cupsTempFile.clear(); + QCUPSSupport cups; + + // Set up print options. + QByteArray prnName; + QList<QPair<QByteArray, QByteArray> > options; + QVector<cups_option_t> cupsOptStruct; + + if (!printerName.isEmpty()) { + prnName = printerName.toLocal8Bit(); + } else { + QPrinterInfo def = QPrinterInfo::defaultPrinter(); + if (def.isNull()) { + qWarning("Could not determine printer to print to"); + QFile::remove(tempFile); + return; + } + prnName = def.printerName().toLocal8Bit(); + } - const int msize = 10000; - char buf[msize]; - - va_list args; - va_start(args, fmt); - int bufsize = qvsnprintf(buf, msize, fmt, args); - - Q_ASSERT(bufsize<msize); - - va_end(args); + if (!cupsStringPageSize.isEmpty()) { + options.append(QPair<QByteArray, QByteArray>("media", cupsStringPageSize.toLocal8Bit())); + } - stream->writeRawData(buf, bufsize); - streampos += bufsize; -} + if (copies > 1) { + options.append(QPair<QByteArray, QByteArray>("copies", QString::number(copies).toLocal8Bit())); + } -int QPdfEnginePrivate::writeCompressed(QIODevice *dev) -{ -#ifndef QT_NO_COMPRESS - if (do_compress) { - int size = QPdfPage::chunkSize(); - int sum = 0; - ::z_stream zStruct; - zStruct.zalloc = Z_NULL; - zStruct.zfree = Z_NULL; - zStruct.opaque = Z_NULL; - if (::deflateInit(&zStruct, Z_DEFAULT_COMPRESSION) != Z_OK) { - qWarning("QPdfStream::writeCompressed: Error in deflateInit()"); - return sum; + if (collate) { + options.append(QPair<QByteArray, QByteArray>("Collate", "True")); } - zStruct.avail_in = 0; - QByteArray in, out; - out.resize(size); - while (!dev->atEnd() || zStruct.avail_in != 0) { - if (zStruct.avail_in == 0) { - in = dev->read(size); - zStruct.avail_in = in.size(); - zStruct.next_in = reinterpret_cast<unsigned char*>(in.data()); - if (in.size() <= 0) { - qWarning("QPdfStream::writeCompressed: Error in read()"); - ::deflateEnd(&zStruct); - return sum; - } - } - zStruct.next_out = reinterpret_cast<unsigned char*>(out.data()); - zStruct.avail_out = out.size(); - if (::deflate(&zStruct, 0) != Z_OK) { - qWarning("QPdfStream::writeCompressed: Error in deflate()"); - ::deflateEnd(&zStruct); - return sum; + + if (duplex != QPrinter::DuplexNone) { + switch(duplex) { + case QPrinter::DuplexNone: break; + case QPrinter::DuplexAuto: + if (orientation == QPrinter::Portrait) + options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-long-edge")); + else + options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-short-edge")); + break; + case QPrinter::DuplexLongSide: + options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-long-edge")); + break; + case QPrinter::DuplexShortSide: + options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-short-edge")); + break; } - int written = out.size() - zStruct.avail_out; - stream->writeRawData(out.constData(), written); - streampos += written; - sum += written; } - int ret; - do { - zStruct.next_out = reinterpret_cast<unsigned char*>(out.data()); - zStruct.avail_out = out.size(); - ret = ::deflate(&zStruct, Z_FINISH); - if (ret != Z_OK && ret != Z_STREAM_END) { - qWarning("QPdfStream::writeCompressed: Error in deflate()"); - ::deflateEnd(&zStruct); - return sum; - } - int written = out.size() - zStruct.avail_out; - stream->writeRawData(out.constData(), written); - streampos += written; - sum += written; - } while (ret == Z_OK); - ::deflateEnd(&zStruct); - - return sum; - } else -#endif - { - QByteArray arr; - int sum = 0; - while (!dev->atEnd()) { - arr = dev->read(QPdfPage::chunkSize()); - stream->writeRawData(arr.constData(), arr.size()); - streampos += arr.size(); - sum += arr.size(); + if (QCUPSSupport::cupsVersion() >= 10300 && orientation == QPrinter::Landscape) { + options.append(QPair<QByteArray, QByteArray>("landscape", "")); } - return sum; - } -} -int QPdfEnginePrivate::writeCompressed(const char *src, int len) -{ -#ifndef QT_NO_COMPRESS - if(do_compress) { - uLongf destLen = len + len/100 + 13; // zlib requirement - Bytef* dest = new Bytef[destLen]; - if (Z_OK == ::compress(dest, &destLen, (const Bytef*) src, (uLongf)len)) { - stream->writeRawData((const char*)dest, destLen); - } else { - qWarning("QPdfStream::writeCompressed: Error in compress()"); - destLen = 0; + QStringList::const_iterator it = cupsOptions.constBegin(); + while (it != cupsOptions.constEnd()) { + options.append(QPair<QByteArray, QByteArray>((*it).toLocal8Bit(), (*(it+1)).toLocal8Bit())); + it += 2; } - delete [] dest; - len = destLen; - } else -#endif - { - stream->writeRawData(src,len); - } - streampos += len; - return len; -} -int QPdfEnginePrivate::writeImage(const QByteArray &data, int width, int height, int depth, - int maskObject, int softMaskObject, bool dct) -{ - int image = addXrefEntry(-1); - xprintf("<<\n" - "/Type /XObject\n" - "/Subtype /Image\n" - "/Width %d\n" - "/Height %d\n", width, height); + for (int c = 0; c < options.size(); ++c) { + cups_option_t opt; + opt.name = options[c].first.data(); + opt.value = options[c].second.data(); + cupsOptStruct.append(opt); + } - if (depth == 1) { - xprintf("/ImageMask true\n" - "/Decode [1 0]\n"); - } else { - xprintf("/BitsPerComponent 8\n" - "/ColorSpace %s\n", (depth == 32) ? "/DeviceRGB" : "/DeviceGray"); - } - if (maskObject > 0) - xprintf("/Mask %d 0 R\n", maskObject); - if (softMaskObject > 0) - xprintf("/SMask %d 0 R\n", softMaskObject); + // Print the file. + cups_option_t* optPtr = cupsOptStruct.size() ? &cupsOptStruct.first() : 0; + cups.printFile(prnName.constData(), tempFile.toLocal8Bit().constData(), + title.toLocal8Bit().constData(), cupsOptStruct.size(), optPtr); - int lenobj = requestObject(); - xprintf("/Length %d 0 R\n", lenobj); - if (interpolateImages) - xprintf("/Interpolate true\n"); - int len = 0; - if (dct) { - //qDebug() << "DCT"; - xprintf("/Filter /DCTDecode\n>>\nstream\n"); - write(data); - len = data.length(); - } else { - if (do_compress) - xprintf("/Filter /FlateDecode\n>>\nstream\n"); - else - xprintf(">>\nstream\n"); - len = writeCompressed(data); + QFile::remove(tempFile); } - xprintf("endstream\n" - "endobj\n"); - addXrefEntry(lenobj); - xprintf("%d\n" - "endobj\n", len); - return image; -} - - -void QPdfEnginePrivate::writeHeader() -{ - addXrefEntry(0,false); - - xprintf("%%PDF-1.4\n"); - - writeInfo(); - - catalog = addXrefEntry(-1); - pageRoot = requestObject(); - xprintf("<<\n" - "/Type /Catalog\n" - "/Pages %d 0 R\n" - ">>\n" - "endobj\n", pageRoot); - - // graphics state - graphicsState = addXrefEntry(-1); - xprintf("<<\n" - "/Type /ExtGState\n" - "/SA true\n" - "/SM 0.02\n" - "/ca 1.0\n" - "/CA 1.0\n" - "/AIS false\n" - "/SMask /None" - ">>\n" - "endobj\n"); - - // color space for pattern - patternColorSpace = addXrefEntry(-1); - xprintf("[/Pattern /DeviceRGB]\n" - "endobj\n"); -} - -void QPdfEnginePrivate::writeInfo() -{ - info = addXrefEntry(-1); - xprintf("<<\n/Title "); - printString(title); - xprintf("\n/Creator "); - printString(creator); - xprintf("\n/Producer "); - printString(QString::fromLatin1("Qt " QT_VERSION_STR " (C) 2011 Nokia Corporation and/or its subsidiary(-ies)")); - QDateTime now = QDateTime::currentDateTime().toUTC(); - QTime t = now.time(); - QDate d = now.date(); - xprintf("\n/CreationDate (D:%d%02d%02d%02d%02d%02d)\n", - d.year(), - d.month(), - d.day(), - t.hour(), - t.minute(), - t.second()); - xprintf(">>\n" - "endobj\n"); -} - -void QPdfEnginePrivate::writePageRoot() -{ - addXrefEntry(pageRoot); - - xprintf("<<\n" - "/Type /Pages\n" - "/Kids \n" - "[\n"); - int size = pages.size(); - for (int i = 0; i < size; ++i) - xprintf("%d 0 R\n", pages[i]); - xprintf("]\n"); - - //xprintf("/Group <</S /Transparency /I true /K false>>\n"); - xprintf("/Count %d\n", pages.size()); - - xprintf("/ProcSet [/PDF /Text /ImageB /ImageC]\n" - ">>\n" - "endobj\n"); -} - - -void QPdfEnginePrivate::embedFont(QFontSubset *font) -{ - //qDebug() << "embedFont" << font->object_id; - int fontObject = font->object_id; - QByteArray fontData = font->toTruetype(); -#ifdef FONT_DUMP - static int i = 0; - QString fileName("font%1.ttf"); - fileName = fileName.arg(i++); - QFile ff(fileName); - ff.open(QFile::WriteOnly); - ff.write(fontData); - ff.close(); #endif - - int fontDescriptor = requestObject(); - int fontstream = requestObject(); - int cidfont = requestObject(); - int toUnicode = requestObject(); - - QFontEngine::Properties properties = font->fontEngine->properties(); - - { - qreal scale = 1000/properties.emSquare.toReal(); - addXrefEntry(fontDescriptor); - QByteArray descriptor; - QPdf::ByteStream s(&descriptor); - s << "<< /Type /FontDescriptor\n" - "/FontName /Q"; - int tag = fontDescriptor; - for (int i = 0; i < 5; ++i) { - s << (char)('A' + (tag % 26)); - tag /= 26; - } - s << '+' << properties.postscriptName << "\n" - "/Flags " << 4 << "\n" - "/FontBBox [" - << properties.boundingBox.x()*scale - << -(properties.boundingBox.y() + properties.boundingBox.height())*scale - << (properties.boundingBox.x() + properties.boundingBox.width())*scale - << -properties.boundingBox.y()*scale << "]\n" - "/ItalicAngle " << properties.italicAngle.toReal() << "\n" - "/Ascent " << properties.ascent.toReal()*scale << "\n" - "/Descent " << -properties.descent.toReal()*scale << "\n" - "/CapHeight " << properties.capHeight.toReal()*scale << "\n" - "/StemV " << properties.lineWidth.toReal()*scale << "\n" - "/FontFile2 " << fontstream << "0 R\n" - ">> endobj\n"; - write(descriptor); - } - { - addXrefEntry(fontstream); - QByteArray header; - QPdf::ByteStream s(&header); - - int length_object = requestObject(); - s << "<<\n" - "/Length1 " << fontData.size() << "\n" - "/Length " << length_object << "0 R\n"; - if (do_compress) - s << "/Filter /FlateDecode\n"; - s << ">>\n" - "stream\n"; - write(header); - int len = writeCompressed(fontData); - write("endstream\n" - "endobj\n"); - addXrefEntry(length_object); - xprintf("%d\n" - "endobj\n", len); - } - { - addXrefEntry(cidfont); - QByteArray cid; - QPdf::ByteStream s(&cid); - s << "<< /Type /Font\n" - "/Subtype /CIDFontType2\n" - "/BaseFont /" << properties.postscriptName << "\n" - "/CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >>\n" - "/FontDescriptor " << fontDescriptor << "0 R\n" - "/CIDToGIDMap /Identity\n" - << font->widthArray() << - ">>\n" - "endobj\n"; - write(cid); - } - { - addXrefEntry(toUnicode); - QByteArray touc = font->createToUnicodeMap(); - xprintf("<< /Length %d >>\n" - "stream\n", touc.length()); - write(touc); - write("endstream\n" - "endobj\n"); - } - { - addXrefEntry(fontObject); - QByteArray font; - QPdf::ByteStream s(&font); - s << "<< /Type /Font\n" - "/Subtype /Type0\n" - "/BaseFont /" << properties.postscriptName << "\n" - "/Encoding /Identity-H\n" - "/DescendantFonts [" << cidfont << "0 R]\n" - "/ToUnicode " << toUnicode << "0 R" - ">>\n" - "endobj\n"; - write(font); - } } -void QPdfEnginePrivate::writeFonts() -{ - for (QHash<QFontEngine::FaceId, QFontSubset *>::iterator it = fonts.begin(); it != fonts.end(); ++it) { - embedFont(*it); - delete *it; - } - fonts.clear(); -} -void QPdfEnginePrivate::writePage() +QPdfEnginePrivate::QPdfEnginePrivate(QPrinter::PrinterMode m) + : QPdfBaseEnginePrivate(m) { - if (pages.empty()) - return; - - *currentPage << "Q Q\n"; - - uint pageStream = requestObject(); - uint pageStreamLength = requestObject(); - uint resources = requestObject(); - uint annots = requestObject(); - - addXrefEntry(pages.last()); - xprintf("<<\n" - "/Type /Page\n" - "/Parent %d 0 R\n" - "/Contents %d 0 R\n" - "/Resources %d 0 R\n" - "/Annots %d 0 R\n" - "/MediaBox [0 0 %d %d]\n" - ">>\n" - "endobj\n", - pageRoot, pageStream, resources, annots, - // make sure we use the pagesize from when we started the page, since the user may have changed it - currentPage->pageSize.width(), currentPage->pageSize.height()); - - addXrefEntry(resources); - xprintf("<<\n" - "/ColorSpace <<\n" - "/PCSp %d 0 R\n" - "/CSp /DeviceRGB\n" - "/CSpg /DeviceGray\n" - ">>\n" - "/ExtGState <<\n" - "/GSa %d 0 R\n", - patternColorSpace, graphicsState); - - for (int i = 0; i < currentPage->graphicStates.size(); ++i) - xprintf("/GState%d %d 0 R\n", currentPage->graphicStates.at(i), currentPage->graphicStates.at(i)); - xprintf(">>\n"); - - xprintf("/Pattern <<\n"); - for (int i = 0; i < currentPage->patterns.size(); ++i) - xprintf("/Pat%d %d 0 R\n", currentPage->patterns.at(i), currentPage->patterns.at(i)); - xprintf(">>\n"); - - xprintf("/Font <<\n"); - for (int i = 0; i < currentPage->fonts.size();++i) - xprintf("/F%d %d 0 R\n", currentPage->fonts[i], currentPage->fonts[i]); - xprintf(">>\n"); - - xprintf("/XObject <<\n"); - for (int i = 0; i<currentPage->images.size(); ++i) { - xprintf("/Im%d %d 0 R\n", currentPage->images.at(i), currentPage->images.at(i)); - } - xprintf(">>\n"); - - xprintf(">>\n" - "endobj\n"); - - addXrefEntry(annots); - xprintf("[ "); - for (int i = 0; i<currentPage->annotations.size(); ++i) { - xprintf("%d 0 R ", currentPage->annotations.at(i)); - } - xprintf("]\nendobj\n"); - - addXrefEntry(pageStream); - xprintf("<<\n" - "/Length %d 0 R\n", pageStreamLength); // object number for stream length object - if (do_compress) - xprintf("/Filter /FlateDecode\n"); - - xprintf(">>\n"); - xprintf("stream\n"); - QIODevice *content = currentPage->stream(); - int len = writeCompressed(content); - xprintf("endstream\n" - "endobj\n"); - - addXrefEntry(pageStreamLength); - xprintf("%d\nendobj\n",len); } -void QPdfEnginePrivate::writeTail() +QPdfEnginePrivate::~QPdfEnginePrivate() { - writePage(); - writeFonts(); - writePageRoot(); - addXrefEntry(xrefPositions.size(),false); - xprintf("xref\n" - "0 %d\n" - "%010d 65535 f \n", xrefPositions.size()-1, xrefPositions[0]); - - for (int i = 1; i < xrefPositions.size()-1; ++i) - xprintf("%010d 00000 n \n", xrefPositions[i]); - - xprintf("trailer\n" - "<<\n" - "/Size %d\n" - "/Info %d 0 R\n" - "/Root %d 0 R\n" - ">>\n" - "startxref\n%d\n" - "%%%%EOF\n", - xrefPositions.size()-1, info, catalog, xrefPositions.last()); } -int QPdfEnginePrivate::addXrefEntry(int object, bool printostr) -{ - if (object < 0) - object = requestObject(); - - if (object>=xrefPositions.size()) - xrefPositions.resize(object+1); - - xrefPositions[object] = streampos; - if (printostr) - xprintf("%d 0 obj\n",object); - return object; -} -void QPdfEnginePrivate::printString(const QString &string) { - // The 'text string' type in PDF is encoded either as PDFDocEncoding, or - // Unicode UTF-16 with a Unicode byte order mark as the first character - // (0xfeff), with the high-order byte first. - QByteArray array("(\xfe\xff"); - const ushort *utf16 = string.utf16(); - - for (int i=0; i < string.size(); ++i) { - char part[2] = {char((*(utf16 + i)) >> 8), char((*(utf16 + i)) & 0xff)}; - for(int j=0; j < 2; ++j) { - if (part[j] == '(' || part[j] == ')' || part[j] == '\\') - array.append('\\'); - array.append(part[j]); - } - } - array.append(")"); - write(array); -} QT_END_NAMESPACE |