diff --git a/src/gui/painting/qpaintengine.h b/src/gui/painting/qpaintengine.h index e8b6b900fe2..0cdbd65f624 100644 --- a/src/gui/painting/qpaintengine.h +++ b/src/gui/painting/qpaintengine.h @@ -45,6 +45,7 @@ #include #include #include +#include #include QT_BEGIN_HEADER @@ -162,6 +163,10 @@ class Q_GUI_EXPORT QPaintEngine virtual void drawRects(const QRect *rects, int rectCount); virtual void drawRects(const QRectF *rects, int rectCount); + virtual void addHyperlink(const QRectF &r, const QUrl &url) {Q_UNUSED(r); Q_UNUSED(url);} + virtual void addAnchor(const QRectF &r, const QString &name) {Q_UNUSED(r); Q_UNUSED(name);} + virtual void addLink(const QRectF &r, const QString &anchor) {Q_UNUSED(r); Q_UNUSED(anchor);} + virtual void drawLines(const QLine *lines, int lineCount); virtual void drawLines(const QLineF *lines, int lineCount); diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index f7066ff333f..01bb0c76db7 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -7254,6 +7254,172 @@ void QPainter::fillRect(const QRectF &r, const QColor &color) \since 4.5 */ + +/*! + \fn void QPainter::addAnchor(int x, int y, int w, int h, const QString &name); + + \overload + + Add an anchor to the current page at the rect specified by \a x, \a y, \a w and \a h + named \a name. + + Note that for output formats not supporting links, currently all other then PDF, + this call has no effect. + + \sa addLink() + + \since 4.7 +*/ + +/*! + \fn void QPainter::addAnchor(const QRect &r, const QString &name); + + \overload + + Add an anchor to the current page at the rect specified by \a r named \a name. + + Note that for output formats not supporting links, currently all other then PDF, + this call has no effect. + + \sa addLink() + + \since 4.7 +*/ + +/*! + \fn void addAnchor(const QRectF &r, const QString &name); + + \overload + + Add an anchor to the current page at the rect specified by \a r named \a name. + + Note that for output formats not supporting links, currently all other then PDF, + this call has no effect. + + \sa addLink() + + \since 4.7 +*/ +void QPainter::addAnchor(const QRectF &r, const QString &name) +{ + Q_D(QPainter); + if (!d->engine) { + qWarning("QPainter::addAnchor: Painter not active"); + return; + } + d->engine->addAnchor(worldTransform().mapRect(r), name); +} + +/*! + \fn void QPainter::addLink(int x, int y, int w, int h, const QString &anchor); + + \overload + + Add a link to the current page at the rect specified by \a x, \a y, \a w and \a h + linking to the anchor named \a anchor. + + Note that for output formats not supporting links, currently all other then PDF, + this call has no effect. + + \sa addAnchor() + + \since 4.7 +*/ + +/*! + \fn void QPainter::addLink(const QRect &r, const QString &anchor); + + \overload + + Add a link to the current page at the rect specified by \a r + linking to the anchor named \a anchor. + + Note that for output formats not supporting links, currently all other then PDF, + this call has no effect. + + \sa addAnchor() + + \since 4.7 +*/ + +/*! + \fn void QPainter::addLink(const QRectF &r, const QString &anchor); + + \overload + + Add a link to the current page at the rect specified by \a r + linking to the anchor named \a anchor. + + Note that for output formats not supporting links, currently all other then PDF, + this call has no effect. + + \sa addAnchor() + + \since 4.7 +*/ +void QPainter::addLink(const QRectF &r, const QString &anchor) +{ + Q_D(QPainter); + if (!d->engine) { + qWarning("QPainter::addLink: Painter not active"); + return; + } + + d->engine->addLink(worldTransform().mapRect(r), anchor); +} + + +/*! + \fn void QPainter::addHyperlink(int x, int y, int w, int h, const QUrl &url); + + \overload + + Add a link to the current page at the rect specified by \a x, \a y, \a w and \a h + linking to \a url. + + Note that for output formats not supporting links, currently all other then PDF, + this call has no effect. + + \since 4.7 +*/ + +/*! + \fn void QPainter::addHyperlink(const QRect &r, const QUrl &url); + + \overload + + Add a link to the current page at the rect specified by \a r + linking to \a url. + + Note that for output formats not supporting links, currently all other then PDF, + this call has no effect. + + \since 4.7 +*/ + +/*! + \fn void QPainter::addHyperlink(const QRectF &r, const QUrl &url); + + \overload + + Add a link to the current page at the rect specified by \a r + linking to \a url. + + Note that for output formats not supporting links, currently all other then PDF, + this call has no effect. + + \since 4.7 +*/ +void QPainter::addHyperlink(const QRectF &r, const QUrl &url) +{ + Q_D(QPainter); + if (!d->engine) { + qWarning("QPainter::addHyperlink: Painter not active"); + return; + } + d->engine->addHyperlink(worldTransform().mapRect(r), url); +} + /*! Sets the given render \a hint on the painter if \a on is true; otherwise clears the render hint. diff --git a/src/gui/painting/qpainter.h b/src/gui/painting/qpainter.h index 76f07c2c123..7e038ce1b56 100644 --- a/src/gui/painting/qpainter.h +++ b/src/gui/painting/qpainter.h @@ -447,6 +447,18 @@ class Q_GUI_EXPORT QPainter inline void fillRect(const QRect &r, Qt::BrushStyle style); inline void fillRect(const QRectF &r, Qt::BrushStyle style); + inline void addAnchor(int x, int y, int w, int h, const QString &name); + inline void addAnchor(const QRect &r, const QString &name); + void addAnchor(const QRectF &r, const QString &name); + + inline void addLink(int x, int y, int w, int h, const QString &anchor); + inline void addLink(const QRect &r, const QString &anchor); + void addLink(const QRectF &r, const QString &anchor); + + inline void addHyperlink(int x, int y, int w, int h, const QUrl &url); + inline void addHyperlink(const QRect &r, const QUrl &url); + void addHyperlink(const QRectF &r, const QUrl &url); + void eraseRect(const QRectF &); inline void eraseRect(int x, int y, int w, int h); inline void eraseRect(const QRect &); @@ -821,6 +833,35 @@ inline void QPainter::fillRect(const QRectF &r, Qt::BrushStyle style) fillRect(r, QBrush(style)); } +inline void QPainter::addAnchor(int x, int y, int w, int h, const QString &name) +{ + addAnchor(QRectF(x, y, w, h), name); +} + +inline void QPainter::addAnchor(const QRect &r, const QString &name) +{ + addAnchor(QRectF(r), name); +} + +inline void QPainter::addLink(int x, int y, int w, int h, const QString &anchor) +{ + addLink(QRectF(x, y, w, h), anchor); +} + +inline void QPainter::addLink(const QRect &r, const QString &anchor) +{ + addLink(QRectF(r), anchor); +} + +inline void QPainter::addHyperlink(int x, int y, int w, int h, const QUrl &url) +{ + addHyperlink(QRectF(x, y, w, h), url); +} + +inline void QPainter::addHyperlink(const QRect &r, const QUrl &url) +{ + addHyperlink(QRectF(r), url); +} inline void QPainter::setBrushOrigin(int x, int y) { diff --git a/src/gui/painting/qprintengine.h b/src/gui/painting/qprintengine.h index ad00816de80..7dad0b3342d 100644 --- a/src/gui/painting/qprintengine.h +++ b/src/gui/painting/qprintengine.h @@ -97,6 +97,8 @@ class Q_GUI_EXPORT QPrintEngine virtual QVariant property(PrintEnginePropertyKey key) const = 0; virtual bool newPage() = 0; + virtual void beginSectionOutline(const QString &text, const QString &anchor) {Q_UNUSED(text); Q_UNUSED(anchor);} + virtual void endSectionOutline() {} virtual bool abort() = 0; virtual int metric(QPaintDevice::PaintDeviceMetric) const = 0; diff --git a/src/gui/painting/qprintengine_pdf.cpp b/src/gui/painting/qprintengine_pdf.cpp index e21de2f4c2f..d6903f3635d 100644 --- a/src/gui/painting/qprintengine_pdf.cpp +++ b/src/gui/painting/qprintengine_pdf.cpp @@ -51,6 +51,7 @@ #include #include #include +#include #ifndef QT_NO_PRINTER #include @@ -156,6 +157,41 @@ bool QPdfEngine::begin(QPaintDevice *pdev) bool QPdfEngine::end() { Q_D(QPdfEngine); + + uint dests; + if (d->anchors.size()) { + dests = d->addXrefEntry(-1); + d->xprintf("<<\n"); + for (QHash::iterator i=d->anchors.begin(); + i != d->anchors.end(); ++i) { + d->printAnchor(i.key()); + d->xprintf(" %d 0 R\n", i.value()); + } + d->xprintf(">>\nendobj\n"); + } + + if (d->outlineRoot) { + d->outlineRoot->obj = d->requestObject(); + d->writeOutlineChildren(d->outlineRoot); + d->addXrefEntry(d->outlineRoot->obj); + d->xprintf("<>\nendobj\n", + d->outlineRoot->firstChild->obj, d->outlineRoot->lastChild->obj); + } + + d->catalog = d->addXrefEntry(-1); + d->xprintf("<<\n" + "/Type /Catalog\n" + "/Pages %d 0 R\n", d->pageRoot); + if (d->outlineRoot) + d->xprintf("/Outlines %d 0 R\n" + "/PageMode /UseOutlines\n", d->outlineRoot->obj); + + if (d->anchors.size()) + d->xprintf("/Dests %d 0 R\n", dests); + + d->xprintf(">>\n" + "endobj\n"); + d->writeTail(); d->stream->unsetDevice(); @@ -305,14 +341,62 @@ QPdfEnginePrivate::QPdfEnginePrivate(QPrinter::PrinterMode m) stream = new QDataStream; pageOrder = QPrinter::FirstPageFirst; orientation = QPrinter::Portrait; + outlineRoot = NULL; + outlineCurrent = NULL; fullPage = false; } QPdfEnginePrivate::~QPdfEnginePrivate() { + if (outlineRoot) + delete outlineRoot; delete stream; } +void QPdfEnginePrivate::printAnchor(const QString &name) { + QByteArray a = name.toUtf8(); + if (a.size() >= 127) + a = QCryptographicHash::hash(a,QCryptographicHash::Sha1); + xprintf("/"); + for (int i=0; i < a.size(); ++i) { + unsigned char c = a[i]; + if (('a' <= c && c <= 'z') || + ('A' <= c && c <= 'Z') || + ('0' <= c && c <= '9') || + c == '.' || c == '_') + xprintf("%c", c); + else if(c == 0) + xprintf("!"); + else + xprintf("#%02x", c); + } +} + +void QPdfEnginePrivate::writeOutlineChildren(OutlineItem * node) { + for (OutlineItem * i = node->firstChild; i != NULL; i = i->next) + i->obj = requestObject(); + for (OutlineItem * i = node->firstChild; i != NULL; i = i->next) { + QPdfEnginePrivate::writeOutlineChildren(i); + addXrefEntry(i->obj); + xprintf("<text); + xprintf("\n" + " /Parent %d 0 R\n" + " /Dest ", i->parent->obj); + printAnchor(i->anchor); + xprintf("\n /Count 0\n"); + if (i->next) + xprintf(" /Next %d 0 R\n", i->next->obj); + if (i->prev) + xprintf(" /Prev %d 0 R\n", i->prev->obj); + if (i->firstChild) + xprintf(" /First %d 0 R\n", i->firstChild->obj); + if (i->lastChild) + xprintf(" /Last %d 0 R\n", i->lastChild->obj); + xprintf(">>\n" + "endobj\n"); + } +} #ifdef USE_NATIVE_GRADIENTS int QPdfEnginePrivate::gradientBrush(const QBrush &b, const QMatrix &matrix, int *gStateObject) @@ -398,7 +482,7 @@ int QPdfEnginePrivate::gradientBrush(const QBrush &b, const QMatrix &matrix, int ">>\n" "stream\n" << content - << "endstream\n" + << "\nendstream\n" "endobj\n"; int softMaskFormObject = addXrefEntry(-1); @@ -891,7 +975,7 @@ int QPdfEnginePrivate::writeImage(const QByteArray &data, int width, int height, xprintf(">>\nstream\n"); len = writeCompressed(data); } - xprintf("endstream\n" + xprintf("\nendstream\n" "endobj\n"); addXrefEntry(lenobj); xprintf("%d\n" @@ -908,13 +992,7 @@ void QPdfEnginePrivate::writeHeader() 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); @@ -1027,7 +1105,7 @@ void QPdfEnginePrivate::embedFont(QFontSubset *font) "/CapHeight " << properties.capHeight.toReal()*scale << "\n" "/StemV " << properties.lineWidth.toReal()*scale << "\n" "/FontFile2 " << fontstream << "0 R\n" - ">> endobj\n"; + ">>\nendobj\n"; write(descriptor); } { @@ -1045,7 +1123,7 @@ void QPdfEnginePrivate::embedFont(QFontSubset *font) "stream\n"; write(header); int len = writeCompressed(fontData); - write("endstream\n" + write("\nendstream\n" "endobj\n"); addXrefEntry(length_object); xprintf("%d\n" @@ -1072,7 +1150,7 @@ void QPdfEnginePrivate::embedFont(QFontSubset *font) xprintf("<< /Length %d >>\n" "stream\n", touc.length()); write(touc); - write("endstream\n" + write("\nendstream\n" "endobj\n"); } { @@ -1101,6 +1179,101 @@ void QPdfEnginePrivate::writeFonts() fonts.clear(); } + +void QPdfEngine::addHyperlink(const QRectF &r, const QUrl &url) +{ + Q_D(QPdfEngine); + char buf[256]; + QRectF rr = d->pageMatrix().mapRect(r); + uint annot = d->addXrefEntry(-1); + QByteArray urlascii = url.toString().toLatin1(); + int len = urlascii.size(); + char *url_esc = new char[len * 2 + 1]; + const char * urldata = urlascii.constData(); + int k = 0; + for (int j = 0; j < len; j++, k++){ + if (urldata[j] == '(' || + urldata[j] == ')' || + urldata[j] == '\\'){ + url_esc[k] = '\\'; + k++; + } + url_esc[k] = urldata[j]; + } + url_esc[k] = 0; + d->xprintf("<<\n/Type /Annot\n/Subtype /Link\n/Rect ["); + d->xprintf("%s ", qt_real_to_string(rr.left(),buf)); + d->xprintf("%s ", qt_real_to_string(rr.top(),buf)); + d->xprintf("%s ", qt_real_to_string(rr.right(),buf)); + d->xprintf("%s", qt_real_to_string(rr.bottom(),buf)); + d->xprintf("]\n/Border [0 0 0]\n/A <<\n"); + d->xprintf("/Type /Action\n/S /URI\n/URI (%s)\n", url_esc); + d->xprintf(">>\n>>\n"); + d->xprintf("endobj\n"); + d->currentPage->annotations.append(annot); + delete[] url_esc; +} + +void QPdfEngine::addLink(const QRectF &r, const QString &anchor) +{ + Q_D(QPdfEngine); + char buf[256]; + QRectF rr = d->pageMatrix().mapRect(r); + uint annot = d->addXrefEntry(-1); + d->xprintf("<<\n/Type /Annot\n/Subtype /Link\n/Rect ["); + d->xprintf("%s ", qt_real_to_string(rr.left(),buf)); + d->xprintf("%s ", qt_real_to_string(rr.top(),buf)); + d->xprintf("%s ", qt_real_to_string(rr.right(),buf)); + d->xprintf("%s", qt_real_to_string(rr.bottom(),buf)); + d->xprintf("]\n/Border [0 0 0]\n/Dest "); + d->printAnchor(anchor); + d->xprintf("\n>>\n"); + d->xprintf("endobj\n"); + d->currentPage->annotations.append(annot); +} + +void QPdfEngine::addAnchor(const QRectF &r, const QString &name) +{ + Q_D(QPdfEngine); + char buf[256]; + QRectF rr = d->pageMatrix().mapRect(r); + uint anchor = d->addXrefEntry(-1); + d->xprintf("[%d /XYZ %s \n", + d->pages.size() - 1, + qt_real_to_string(rr.left(), buf)); + d->xprintf("%s 0]\n", + qt_real_to_string(rr.bottom(), buf)); + d->xprintf("endobj\n"); + d->anchors[name] = anchor; +} + +void QPdfEngine::beginSectionOutline(const QString &text, const QString &anchor) +{ + Q_D(QPdfEngine); + if (d->outlineCurrent == NULL) { + if (d->outlineRoot) + delete d->outlineRoot; + d->outlineCurrent = d->outlineRoot = new QPdfEnginePrivate::OutlineItem(QString(), QString()); + } + + QPdfEnginePrivate::OutlineItem *i = new QPdfEnginePrivate::OutlineItem(text, anchor); + i->parent = d->outlineCurrent; + i->prev = d->outlineCurrent->lastChild; + if (d->outlineCurrent->firstChild) + d->outlineCurrent->lastChild->next = i; + else + d->outlineCurrent->firstChild = i; + d->outlineCurrent->lastChild = i; + d->outlineCurrent = i; +} + +void QPdfEngine::endSectionOutline() +{ + Q_D(QPdfEngine); + if (d->outlineCurrent) + d->outlineCurrent = d->outlineCurrent->parent; +} + void QPdfEnginePrivate::writePage() { if (pages.empty()) @@ -1178,7 +1351,7 @@ void QPdfEnginePrivate::writePage() xprintf("stream\n"); QIODevice *content = currentPage->stream(); int len = writeCompressed(content); - xprintf("endstream\n" + xprintf("\nendstream\n" "endobj\n"); addXrefEntry(pageStreamLength); diff --git a/src/gui/painting/qprintengine_pdf_p.h b/src/gui/painting/qprintengine_pdf_p.h index 4cbfac3c398..102c1fe58cf 100644 --- a/src/gui/painting/qprintengine_pdf_p.h +++ b/src/gui/painting/qprintengine_pdf_p.h @@ -108,12 +108,18 @@ class QPdfEngine : public QPdfBaseEngine void setBrush(); + virtual void addHyperlink(const QRectF &r, const QUrl &url); + virtual void addAnchor(const QRectF &r, const QString &name); + virtual void addLink(const QRectF &r, const QString &anchor); + // ### unused, should have something for this in QPrintEngine void setAuthor(const QString &author); QString author() const; void setDevice(QIODevice* dev); + void beginSectionOutline(const QString &text, const QString &anchor); + void endSectionOutline(); private: Q_DISABLE_COPY(QPdfEngine) @@ -124,6 +130,35 @@ class QPdfEnginePrivate : public QPdfBaseEnginePrivate { Q_DECLARE_PUBLIC(QPdfEngine) public: + + class OutlineItem { + public: + OutlineItem *parent; + OutlineItem *next; + OutlineItem *prev; + OutlineItem *firstChild; + OutlineItem *lastChild; + uint obj; + QString text; + QString anchor; + + OutlineItem(const QString &t, const QString &a): + parent(NULL), next(NULL), prev(NULL), firstChild(NULL), lastChild(NULL), + obj(0), text(t), anchor(a) {} + ~OutlineItem() { + OutlineItem *i = firstChild; + while(i != NULL) { + OutlineItem *n = i->next; + delete i; + i=n; + } + } + }; + + OutlineItem *outlineRoot; + OutlineItem *outlineCurrent; + void writeOutlineChildren(OutlineItem *node); + QPdfEnginePrivate(QPrinter::PrinterMode m); ~QPdfEnginePrivate(); @@ -170,7 +205,10 @@ class QPdfEnginePrivate : public QPdfBaseEnginePrivate void writePage(); int addXrefEntry(int object, bool printostr = true); + void printString(const QString &string); + void printAnchor(const QString &name); + void xprintf(const char* fmt, ...); inline void write(const QByteArray &data) { stream->writeRawData(data.constData(), data.size()); @@ -183,6 +221,8 @@ class QPdfEnginePrivate : public QPdfBaseEnginePrivate // various PDF objects int pageRoot, catalog, info, graphicsState, patternColorSpace; + QVector dests; + QHash anchors; QVector pages; QHash imageCache; QHash, uint > alphaCache; diff --git a/src/gui/painting/qprinter.cpp b/src/gui/painting/qprinter.cpp index 139bf3bc272..94ebf4c1f32 100644 --- a/src/gui/painting/qprinter.cpp +++ b/src/gui/painting/qprinter.cpp @@ -933,6 +933,39 @@ void QPrinter::setOutputFileName(const QString &fileName) d->addToManualSetList(QPrintEngine::PPK_OutputFileName); } +/*! + Add a section to the document outline. All following sections will be added + to as subsections to this section, until endSectionOutline() has been called. + + \a name is the name of the added section. \a anchor is the name of an anchor + indicating the beginning of the section. This anchor must be added by calling + QPainter::addAnchor(). + + Note that for output formats not supporting outlines, currently all other then PDF, + this call has no effect. + + \sa endSectionOutline() QPainter::addAnchor() + + \since 4.7 +*/ +void QPrinter::beginSectionOutline(const QString &name, const QString &anchor) +{ + Q_D(QPrinter); + d->printEngine->beginSectionOutline(name, anchor); +} + +/*! + End the current section. + + \sa beginSectionOutline() + + \since 4.7 +*/ +void QPrinter::endSectionOutline() +{ + Q_D(QPrinter); + d->printEngine->endSectionOutline(); +} /*! Returns the name of the program that sends the print output to the diff --git a/src/gui/painting/qprinter.h b/src/gui/painting/qprinter.h index c2d871989f7..53075e10188 100644 --- a/src/gui/painting/qprinter.h +++ b/src/gui/painting/qprinter.h @@ -147,6 +147,9 @@ class Q_GUI_EXPORT QPrinter : public QPaintDevice enum PrinterOption { PrintToFile, PrintSelection, PrintPageRange }; #endif // QT3_SUPPORT + void beginSectionOutline(const QString &text, const QString &anchor); + void endSectionOutline(); + void setOutputFormat(OutputFormat format); OutputFormat outputFormat() const;