diff options
-rw-r--r-- | src/gui/painting/qpdf.cpp | 1666 | ||||
-rw-r--r-- | src/gui/painting/qpdf_p.h | 85 | ||||
-rw-r--r-- | src/gui/painting/qprintengine_pdf.cpp | 1522 | ||||
-rw-r--r-- | src/gui/painting/qprintengine_pdf_p.h | 82 |
4 files changed, 1674 insertions, 1681 deletions
diff --git a/src/gui/painting/qpdf.cpp b/src/gui/painting/qpdf.cpp index 958e49907b..da87653ae7 100644 --- a/src/gui/painting/qpdf.cpp +++ b/src/gui/painting/qpdf.cpp @@ -44,18 +44,27 @@ #include <qfile.h> #include <qtemporaryfile.h> #include <private/qmath_p.h> -#include "private/qcups_p.h" -#include "qprinterinfo.h" #include <qnumeric.h> #include "private/qfont_p.h" +#include <qimagewriter.h> +#include "qbuffer.h" +#include "QtCore/qdatetime.h" -#ifdef Q_OS_UNIX -#include "private/qcore_unix_p.h" // overrides QT_OPEN +#ifndef QT_NO_COMPRESS +#include <zlib.h> #endif -QT_BEGIN_NAMESPACE +#ifdef QT_NO_COMPRESS +static const bool do_compress = false; +#else +static const bool do_compress = true; +#endif + +// 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; -#ifndef QT_NO_PRINTER +QT_BEGIN_NAMESPACE extern QSizeF qt_paperSizeToQSizeF(QPrinter::PaperSize size); @@ -914,36 +923,22 @@ const char *QPdf::paperSizeToString(QPrinter::PaperSize paperSize) return psToStr[paperSize]; } -// -------------------------- base engine, shared code between PS and PDF ----------------------- +QPdfPage::QPdfPage() + : QPdf::ByteStream(true) // Enable file backing +{ +} + +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); +} + QPdfBaseEngine::QPdfBaseEngine(QPdfBaseEnginePrivate &dd, PaintEngineFeatures f) : QAlphaPaintEngine(dd, f) { - Q_D(QPdfBaseEngine); -#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")); - } } void QPdfBaseEngine::drawPoints (const QPointF *points, int pointCount) @@ -1091,6 +1086,89 @@ void QPdfBaseEngine::drawPath (const QPainterPath &p) } } +void QPdfBaseEngine::drawPixmap (const QRectF &rectangle, const QPixmap &pixmap, const QRectF &sr) +{ + if (sr.isEmpty() || rectangle.isEmpty() || pixmap.isNull()) + return; + Q_D(QPdfBaseEngine); + + 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 QPdfBaseEngine::drawImage(const QRectF &rectangle, const QImage &image, const QRectF &sr, Qt::ImageConversionFlags) +{ + if (sr.isEmpty() || rectangle.isEmpty() || image.isNull()) + return; + Q_D(QPdfBaseEngine); + + 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 QPdfBaseEngine::drawTiledPixmap (const QRectF &rectangle, const QPixmap &pixmap, const QPointF &point) +{ + Q_D(QPdfBaseEngine); + + 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 QPdfBaseEngine::drawTextItem(const QPointF &p, const QTextItem &textItem) { Q_D(QPdfBaseEngine); @@ -1337,9 +1415,48 @@ void QPdfBaseEngine::setPen() *d->currentPage << QPdf::generateDashes(d->pen) << " 0 d\n"; } + +void QPdfBaseEngine::setBrush() +{ + Q_D(QPdfBaseEngine); + 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"; +} + + bool QPdfBaseEngine::newPage() { Q_D(QPdfBaseEngine); + if (!isActive()) + return false; + d->newPage(); + setupGraphicsState(DirtyBrush|DirtyPen|DirtyClipPath); QFile *outfile = qobject_cast<QFile*> (d->outDevice); if (outfile && outfile->error() != QFile::NoError) @@ -1347,6 +1464,12 @@ bool QPdfBaseEngine::newPage() return true; } +QPaintEngine::Type QPdfBaseEngine::type() const +{ + return QPaintEngine::Pdf; +} + + int QPdfBaseEngine::metric(QPaintDevice::PaintDeviceMetric metricType) const { @@ -1387,210 +1510,6 @@ int QPdfBaseEngine::metric(QPaintDevice::PaintDeviceMetric metricType) const return val; } -void QPdfBaseEngine::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; - } - default: - break; - } -} - -QVariant QPdfBaseEngine::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 { - const qreal defaultMargin = 10; // ~3.5 mm - margins << defaultMargin << defaultMargin - << defaultMargin << defaultMargin; - } - ret = margins; - break; - } - default: - break; - } - return ret; -} QPdfBaseEnginePrivate::QPdfBaseEnginePrivate(QPrinter::PrinterMode m) : clipEnabled(false), allClipped(false), hasPen(true), hasBrush(false), simplePen(false), @@ -1612,6 +1531,13 @@ QPdfBaseEnginePrivate::QPdfBaseEnginePrivate(QPrinter::PrinterMode m) currentObject = 1; currentPage = 0; stroker.stream = 0; + + streampos = 0; + + stream = new QDataStream; + pageOrder = QPrinter::FirstPageFirst; + orientation = QPrinter::Portrait; + fullPage = false; } bool QPdfBaseEngine::begin(QPaintDevice *pdev) @@ -1626,289 +1552,1021 @@ bool QPdfBaseEngine::begin(QPaintDevice *pdev) d->stroker.stream = d->currentPage; d->opacity = 1.0; - return d->openPrintDevice(); + 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); + d->writeHeader(); + newPage(); + + return true; } bool QPdfBaseEngine::end() { Q_D(QPdfBaseEngine); + d->writeTail(); + + d->stream->unsetDevice(); + qDeleteAll(d->fonts); d->fonts.clear(); delete d->currentPage; d->currentPage = 0; - d->closePrintDevice(); + setActive(false); return true; } -#ifndef QT_NO_LPR -static void closeAllOpenFds() +QPdfBaseEnginePrivate::~QPdfBaseEnginePrivate() { - // 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 - i = 256; -#endif - // leave stdin/out/err untouched - while(--i > 2) - QT_CLOSE(i); + qDeleteAll(fonts); + delete currentPage; + delete stream; } -#endif -bool QPdfBaseEnginePrivate::openPrintDevice() +QRect QPdfBaseEnginePrivate::paperRect() const { - if(outDevice) - return false; - - if (!outputFileName.isEmpty()) { - QFile *file = new QFile(outputFileName); - if (! file->open(QFile::WriteOnly|QFile::Truncate)) { - delete file; - return false; + int w; + int h; + if (paperSize == QPrinter::Custom) { + w = qRound(customPaperSize.width()*resolution/72.); + h = qRound(customPaperSize.height()*resolution/72.); + } else { + if (!cupsPaperRect.isNull()) { + QRect r = cupsPaperRect; + w = r.width(); + h = r.height(); + } else{ + QPdf::PaperSize s = QPdf::paperSize(paperSize); + w = s.width; + h = s.height; } - 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; + w = qRound(w*resolution/72.); + h = qRound(h*resolution/72.); + } + if (orientation == QPrinter::Portrait) + return QRect(0, 0, w, h); + else + return QRect(0, 0, h, w); +} + +QRect QPdfBaseEnginePrivate::pageRect() const +{ + if(fullPage) + return paperRect(); + + QRect r; + + if (!hasCustomPageMargins && !cupsPageRect.isNull()) { + r = cupsPageRect; + if (r == cupsPaperRect) { + // if cups doesn't define any margins, give it at least approx 3.5 mm + r = QRect(10, 10, r.width() - 20, r.height() - 20); } - cupsTempFile = ret.second; - outDevice = new QFile(); - static_cast<QFile *>(outDevice)->open(ret.first, QIODevice::WriteOnly); -#endif -#ifndef QT_NO_LPR } else { - 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; + QPdf::PaperSize s; + if (paperSize == QPrinter::Custom) { + s.width = qRound(customPaperSize.width()); + s.height = qRound(customPaperSize.height()); + } else { + s = QPdf::paperSize(paperSize); } + if (hasCustomPageMargins) + r = QRect(0, 0, s.width, s.height); + else + r = QRect(72/3, 72/3, s.width - 2*72/3, s.height - 2*72/3); + } - 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); - } - qt_safe_dup2(fds[0], 0, 0); + int x = qRound(r.left()*resolution/72.); + int y = qRound(r.top()*resolution/72.); + int w = qRound(r.width()*resolution/72.); + int h = qRound(r.height()*resolution/72.); + if (orientation == QPrinter::Portrait) + r = QRect(x, y, w, h); + else + r = QRect(y, x, h, w); + + if (hasCustomPageMargins) { + r.adjust(qRound(leftMargin*(resolution/72.)), + qRound(topMargin*(resolution/72.)), + -qRound(rightMargin*(resolution/72.)), + -qRound(bottomMargin*(resolution/72.))); + } + return r; +} - 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()); - } - 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(); - } +void QPdfBaseEnginePrivate::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 QPdfBaseEnginePrivate::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 QPdfBaseEnginePrivate::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 QPdfBaseEnginePrivate::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 - 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; - } - // 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); + + 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; } - // parent process - QT_CLOSE(fds[0]); - fd = fds[1]; - (void)qt_safe_waitpid(pid, 0, 0); + 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); + } +} - if (fd < 0) - return false; - outDevice = new QFile(); - static_cast<QFile *>(outDevice)->open(fd, QIODevice::WriteOnly); -#endif +void QPdfBaseEnginePrivate::writeFonts() +{ + for (QHash<QFontEngine::FaceId, QFontSubset *>::iterator it = fonts.begin(); it != fonts.end(); ++it) { + embedFont(*it); + delete *it; } + fonts.clear(); +} - return true; +void QPdfBaseEnginePrivate::writePage() +{ + 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 QPdfBaseEnginePrivate::writeTail() +{ + 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 QPdfBaseEnginePrivate::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 QPdfBaseEnginePrivate::closePrintDevice() +void QPdfBaseEnginePrivate::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); +} + + +// For strings up to 10000 bytes only ! +void QPdfBaseEnginePrivate::xprintf(const char* fmt, ...) { - if (!outDevice) + if (!stream) 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; + + 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); + + stream->writeRawData(buf, bufsize); + streampos += bufsize; +} + +int QPdfBaseEnginePrivate::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; + } + 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; + } } - prnName = def.printerName().toLocal8Bit(); + 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; + } + 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); - if (!cupsStringPageSize.isEmpty()) { - options.append(QPair<QByteArray, QByteArray>("media", cupsStringPageSize.toLocal8Bit())); - } + ::deflateEnd(&zStruct); - if (copies > 1) { - options.append(QPair<QByteArray, QByteArray>("copies", QString::number(copies).toLocal8Bit())); + 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(); } + return sum; + } +} - if (collate) { - options.append(QPair<QByteArray, QByteArray>("Collate", "True")); +int QPdfBaseEnginePrivate::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; } + delete [] dest; + len = destLen; + } else +#endif + { + stream->writeRawData(src,len); + } + streampos += len; + return len; +} - 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")); +int QPdfBaseEnginePrivate::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); + + 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); + + 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); + } + xprintf("endstream\n" + "endobj\n"); + addXrefEntry(lenobj); + xprintf("%d\n" + "endobj\n", len); + return image; +} + +#ifdef USE_NATIVE_GRADIENTS +int QPdfBaseEnginePrivate::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 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); + + 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 (QCUPSSupport::cupsVersion() >= 10300 && orientation == QPrinter::Landscape) { - options.append(QPair<QByteArray, QByteArray>("landscape", "")); + if (ca) { + *gStateObject = addConstantAlphaObject(stops.at(0).second.alpha()); + } 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); } + } - QStringList::const_iterator it = cupsOptions.constBegin(); - while (it != cupsOptions.constEnd()) { - options.append(QPair<QByteArray, QByteArray>((*it).toLocal8Bit(), (*(it+1)).toLocal8Bit())); - it += 2; - } + return patternObj; +} +#endif - 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); - } +int QPdfBaseEnginePrivate::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); + } + if (currentPage->graphicStates.indexOf(object) < 0) + currentPage->graphicStates.append(object); + + return object; +} - // 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 QPdfBaseEnginePrivate::addBrushPattern(const QTransform &m, bool *specifyColor, int *gStateObject) +{ + int paintType = 2; // Uncolored tiling + int w = 8; + int h = 8; - QFile::remove(tempFile); - } + *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); +#else + return 0; #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; } -QPdfBaseEnginePrivate::~QPdfBaseEnginePrivate() +/*! + * Adds an image to the pdf and return the pdf-object id. Returns -1 if adding the image failed. + */ +int QPdfBaseEnginePrivate::addImage(const QImage &img, bool *bitmap, qint64 serial_no) { - qDeleteAll(fonts); - delete currentPage; + if (img.isNull()) + return -1; + + int object = imageCache.value(serial_no); + if(object) + return object; + + 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; + } + } + + 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; + } + object = writeImage(data, w, h, d, 0, 0); + } 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; + + 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; + } + } + } + } 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; + } + } + } + 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; + } + mdata += bytesPerLine; + } + 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 QPdfBaseEnginePrivate::drawTextItem(const QPointF &p, const QTextItemInt &ti) { Q_Q(QPdfBaseEngine); + 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; + } +#endif + int synthesized = ti.fontEngine->synthesized(); + qreal stretch = synthesized & QFontEngine::SynthesizedStretch ? ti.fontEngine->fontDef.stretch/100. : 1.; + + 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); + + 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", +#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); + } + } + QFontEngine *fe = ti.fontEngine; QFontEngine::FaceId face_id = fe->faceId(); @@ -2024,83 +2682,33 @@ void QPdfBaseEnginePrivate::drawTextItem(const QPointF &p, const QTextItemInt &t *currentPage << "ET\n"; } -QRect QPdfBaseEnginePrivate::paperRect() const +QTransform QPdfBaseEnginePrivate::pageMatrix() const { - int w; - int h; - if (paperSize == QPrinter::Custom) { - w = qRound(customPaperSize.width()*resolution/72.); - h = qRound(customPaperSize.height()*resolution/72.); - } else { -#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) - if (QCUPSSupport::isAvailable() && !cupsPaperRect.isNull()) { - QRect r = cupsPaperRect; - w = r.width(); - h = r.height(); - } else -#endif - { - QPdf::PaperSize s = QPdf::paperSize(paperSize); - w = s.width; - h = s.height; - } - w = qRound(w*resolution/72.); - h = qRound(h*resolution/72.); + 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()); } - if (orientation == QPrinter::Portrait) - return QRect(0, 0, w, h); - else - return QRect(0, 0, h, w); + return tmp; } -QRect QPdfBaseEnginePrivate::pageRect() const +void QPdfBaseEnginePrivate::newPage() { - if(fullPage) - return paperRect(); - - QRect r; + if (currentPage && currentPage->pageSize.isEmpty()) + currentPage->pageSize = QSize(width(), height()); + writePage(); -#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) - if (!hasCustomPageMargins && QCUPSSupport::isAvailable() && !cupsPageRect.isNull()) { - r = cupsPageRect; - if (r == cupsPaperRect) { - // if cups doesn't define any margins, give it at least approx 3.5 mm - r = QRect(10, 10, r.width() - 20, r.height() - 20); - } - } else -#endif - { - QPdf::PaperSize s; - if (paperSize == QPrinter::Custom) { - s.width = qRound(customPaperSize.width()); - s.height = qRound(customPaperSize.height()); - } else { - s = QPdf::paperSize(paperSize); - } - if (hasCustomPageMargins) - r = QRect(0, 0, s.width, s.height); - else - r = QRect(72/3, 72/3, s.width - 2*72/3, s.height - 2*72/3); - } - - int x = qRound(r.left()*resolution/72.); - int y = qRound(r.top()*resolution/72.); - int w = qRound(r.width()*resolution/72.); - int h = qRound(r.height()*resolution/72.); - if (orientation == QPrinter::Portrait) - r = QRect(x, y, w, h); - else - r = QRect(y, x, h, w); - - if (hasCustomPageMargins) { - r.adjust(qRound(leftMargin*(resolution/72.)), - qRound(topMargin*(resolution/72.)), - -qRound(rightMargin*(resolution/72.)), - -qRound(bottomMargin*(resolution/72.))); - } - return r; + 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"; } -#endif QT_END_NAMESPACE diff --git a/src/gui/painting/qpdf_p.h b/src/gui/painting/qpdf_p.h index c9e14b6b7b..8efa51743d 100644 --- a/src/gui/painting/qpdf_p.h +++ b/src/gui/painting/qpdf_p.h @@ -56,14 +56,12 @@ #include "QtCore/qstring.h" #include "QtCore/qvector.h" #include "private/qstroker_p.h" +#include "private/qpaintengine_alpha_p.h" #include "private/qfontengine_p.h" -#include "QtGui/qprinter.h" #include "private/qfontsubset_p.h" -#include "private/qpaintengine_alpha_p.h" -#include "qprintengine.h" -#include "qbuffer.h" -#ifndef QT_NO_PRINTER +// ### remove! +#include <qprinter.h> QT_BEGIN_NAMESPACE @@ -179,7 +177,7 @@ private: class QPdfBaseEnginePrivate; -class QPdfBaseEngine : public QAlphaPaintEngine, public QPrintEngine +class QPdfBaseEngine : public QAlphaPaintEngine { Q_DECLARE_PRIVATE(QPdfBaseEngine) public: @@ -198,18 +196,22 @@ public: void drawTextItem(const QPointF &p, const QTextItem &textItem); + void drawPixmap (const QRectF & rectangle, const QPixmap & pixmap, const QRectF & sr); + void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, + Qt::ImageConversionFlags flags = Qt::AutoColor); + void drawTiledPixmap (const QRectF & rectangle, const QPixmap & pixmap, const QPointF & point); + void updateState(const QPaintEngineState &state); int metric(QPaintDevice::PaintDeviceMetric metricType) const; + Type type() const; // end reimplementations QPaintEngine // Printer stuff... bool newPage(); - void setProperty(PrintEnginePropertyKey key, const QVariant &value); - QVariant property(PrintEnginePropertyKey key) const; void setPen(); - virtual void setBrush() = 0; + void setBrush(); void setupGraphicsState(QPaintEngine::DirtyFlags flags); private: @@ -223,16 +225,33 @@ public: QPdfBaseEnginePrivate(QPrinter::PrinterMode m); ~QPdfBaseEnginePrivate(); - bool openPrintDevice(); - void closePrintDevice(); - - - virtual void drawTextItem(const QPointF &p, const QTextItemInt &ti); inline uint requestObject() { return currentObject++; } QRect paperRect() const; QRect pageRect() const; + int width() const { + QRect r = paperRect(); + return qRound(r.width()*72./resolution); + } + int height() const { + QRect r = paperRect(); + return qRound(r.height()*72./resolution); + } + + void writeHeader(); + void writeTail(); + + int addImage(const QImage &image, bool *bitmap, qint64 serial_no); + int addConstantAlphaObject(int brushAlpha, int penAlpha = 255); + int addBrushPattern(const QTransform &matrix, bool *specifyColor, int *gStateObject); + + void drawTextItem(const QPointF &p, const QTextItemInt &ti); + + QTransform pageMatrix() const; + + void newPage(); + bool postscript; int currentObject; @@ -289,11 +308,45 @@ public: #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) QString cupsTempFile; #endif + +private: +#ifdef USE_NATIVE_GRADIENTS + int gradientBrush(const QBrush &b, const QMatrix &matrix, int *gStateObject); +#endif + + void writeInfo(); + void writePageRoot(); + void writeFonts(); + void embedFont(QFontSubset *font); + + QVector<int> xrefPositions; + QDataStream* stream; + int streampos; + + int writeImage(const QByteArray &data, int width, int height, int depth, + int maskObject, int softMaskObject, bool dct = false); + void writePage(); + + int addXrefEntry(int object, bool printostr = true); + void printString(const QString &string); + void xprintf(const char* fmt, ...); + inline void write(const QByteArray &data) { + stream->writeRawData(data.constData(), data.size()); + streampos += data.size(); + } + + int writeCompressed(const char *src, int len); + inline int writeCompressed(const QByteArray &data) { return writeCompressed(data.constData(), data.length()); } + int writeCompressed(QIODevice *dev); + + // various PDF objects + int pageRoot, catalog, info, graphicsState, patternColorSpace; + QVector<uint> pages; + QHash<qint64, uint> imageCache; + QHash<QPair<uint, uint>, uint > alphaCache; }; QT_END_NAMESPACE -#endif // QT_NO_PRINTER - #endif // QPDF_P_H 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 diff --git a/src/gui/painting/qprintengine_pdf_p.h b/src/gui/painting/qprintengine_pdf_p.h index ee77e1599c..aa4a6c820a 100644 --- a/src/gui/painting/qprintengine_pdf_p.h +++ b/src/gui/painting/qprintengine_pdf_p.h @@ -67,6 +67,7 @@ #include "private/qfontengine_p.h" #include "private/qpdf_p.h" #include "private/qpaintengine_p.h" +#include "qprintengine.h" QT_BEGIN_NAMESPACE @@ -82,7 +83,7 @@ class QPdfEngine; class QPdfEnginePrivate; -class QPdfEngine : public QPdfBaseEngine +class QPdfEngine : public QPdfBaseEngine, public QPrintEngine { Q_DECLARE_PRIVATE(QPdfEngine) public: @@ -92,32 +93,22 @@ public: // reimplementations QPaintEngine bool begin(QPaintDevice *pdev); bool end(); - void drawPixmap (const QRectF & rectangle, const QPixmap & pixmap, const QRectF & sr); - void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, - Qt::ImageConversionFlags flags = Qt::AutoColor); - void drawTiledPixmap (const QRectF & rectangle, const QPixmap & pixmap, const QPointF & point); - - Type type() const; // end reimplementations QPaintEngine // reimplementations QPrintEngine bool abort() {return false;} - bool newPage(); QPrinter::PrinterState printerState() const {return state;} - // end reimplementations QPrintEngine - - void setBrush(); - // ### unused, should have something for this in QPrintEngine - void setAuthor(const QString &author); - QString author() const; + bool newPage(); + int metric(QPaintDevice::PaintDeviceMetric) const; + void setProperty(PrintEnginePropertyKey key, const QVariant &value); + QVariant property(PrintEnginePropertyKey key) const; + // end reimplementations QPrintEngine - void setDevice(QIODevice* dev); + QPrinter::PrinterState state; private: Q_DISABLE_COPY(QPdfEngine) - - QPrinter::PrinterState state; }; class QPdfEnginePrivate : public QPdfBaseEnginePrivate @@ -127,65 +118,12 @@ public: QPdfEnginePrivate(QPrinter::PrinterMode m); ~QPdfEnginePrivate(); - void newPage(); - - int width() const { - QRect r = paperRect(); - return qRound(r.width()*72./resolution); - } - int height() const { - QRect r = paperRect(); - return qRound(r.height()*72./resolution); - } - - void writeHeader(); - void writeTail(); - - int addImage(const QImage &image, bool *bitmap, qint64 serial_no); - int addConstantAlphaObject(int brushAlpha, int penAlpha = 255); - int addBrushPattern(const QTransform &matrix, bool *specifyColor, int *gStateObject); - - void drawTextItem(const QPointF &p, const QTextItemInt &ti); - - QTransform pageMatrix() const; + bool openPrintDevice(); + void closePrintDevice(); private: Q_DISABLE_COPY(QPdfEnginePrivate) -#ifdef USE_NATIVE_GRADIENTS - int gradientBrush(const QBrush &b, const QMatrix &matrix, int *gStateObject); -#endif - - void writeInfo(); - void writePageRoot(); - void writeFonts(); - void embedFont(QFontSubset *font); - - QVector<int> xrefPositions; - QDataStream* stream; - int streampos; - - int writeImage(const QByteArray &data, int width, int height, int depth, - int maskObject, int softMaskObject, bool dct = false); - void writePage(); - - int addXrefEntry(int object, bool printostr = true); - void printString(const QString &string); - void xprintf(const char* fmt, ...); - inline void write(const QByteArray &data) { - stream->writeRawData(data.constData(), data.size()); - streampos += data.size(); - } - - int writeCompressed(const char *src, int len); - inline int writeCompressed(const QByteArray &data) { return writeCompressed(data.constData(), data.length()); } - int writeCompressed(QIODevice *dev); - - // various PDF objects - int pageRoot, catalog, info, graphicsState, patternColorSpace; - QVector<uint> pages; - QHash<qint64, uint> imageCache; - QHash<QPair<uint, uint>, uint > alphaCache; }; QT_END_NAMESPACE |