diff options
Diffstat (limited to 'src/gui/painting/qprintengine_pdf.cpp')
-rw-r--r-- | src/gui/painting/qprintengine_pdf.cpp | 1244 |
1 files changed, 0 insertions, 1244 deletions
diff --git a/src/gui/painting/qprintengine_pdf.cpp b/src/gui/painting/qprintengine_pdf.cpp deleted file mode 100644 index 2b0bca8024..0000000000 --- a/src/gui/painting/qprintengine_pdf.cpp +++ /dev/null @@ -1,1244 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** This file may be used under the terms of the GNU Lesser General Public -** License version 2.1 as published by the Free Software Foundation and -** appearing in the file LICENSE.LGPL included in the packaging of this -** file. Please review the following information to ensure the GNU Lesser -** General Public License version 2.1 requirements will be met: -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#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> - -#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" -#endif - -#include "qprintengine_pdf_p.h" -#include "private/qdrawhelper_p.h" - -QT_BEGIN_NAMESPACE - -extern qint64 qt_pixmap_id(const QPixmap &pixmap); -extern qint64 qt_image_id(const QImage &image); - -//#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; -#endif - -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); -} - - -inline QPaintEngine::PaintEngineFeatures qt_pdf_decide_features() -{ - QPaintEngine::PaintEngineFeatures f = QPaintEngine::AllFeatures; - f &= ~(QPaintEngine::PorterDuff | QPaintEngine::PerspectiveTransform - | QPaintEngine::ObjectBoundingModeGradients -#ifndef USE_NATIVE_GRADIENTS - | QPaintEngine::LinearGradientFill -#endif - | QPaintEngine::RadialGradientFill - | QPaintEngine::ConicalGradientFill); - return f; -} - -QPdfEngine::QPdfEngine(QPrinter::PrinterMode m) - : QPdfBaseEngine(*new QPdfEnginePrivate(m), qt_pdf_decide_features()) -{ - state = QPrinter::Idle; -} - -QPdfEngine::~QPdfEngine() -{ -} - -bool QPdfEngine::begin(QPaintDevice *pdev) -{ - Q_D(QPdfEngine); - - if(!QPdfBaseEngine::begin(pdev)) { - 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; -} - -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"; -} - -QPaintEngine::Type QPdfEngine::type() const -{ - return QPaintEngine::Pdf; -} - -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 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 (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); - } - } - - 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); - } - if (currentPage->graphicStates.indexOf(object) < 0) - currentPage->graphicStates.append(object); - - return object; -} - -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); -#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, qt_pixmap_id(brush.texture())); - 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; -} - -/*! - * 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) -{ - 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 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; - } -#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); - } - } - - 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"; -} - - -// For strings up to 10000 bytes only ! -void QPdfEnginePrivate::xprintf(const char* fmt, ...) -{ - if (!stream) - 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 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; - } - 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; - } - 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(); - } - 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; - } - 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); - - 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; -} - - -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() -{ - 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() -{ - 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 - -#endif // QT_NO_PRINTER |