diff --git a/examples/printheaderfooter.js b/examples/printheaderfooter.js new file mode 100644 index 00000000..90275ba8 --- /dev/null +++ b/examples/printheaderfooter.js @@ -0,0 +1,70 @@ +var page = require('webpage').create(), + system = require('system'); + +function someCallback(pageNum, numPages) { + return "

somCallback: " + pageNum + " / " + numPages + "

"; +} + +if (system.args.length < 3) { + console.log('Usage: printheaderfooter.js URL filename'); + phantom.exit(); +} else { + var address = system.args[1]; + var output = system.args[2]; + page.viewportSize = { width: 600, height: 600 }; + page.paperSize = { + format: 'A4', + margin: "1cm", + /* default header/footer for pages that don't have custom overwrites (see below) */ + header: { + height: "1cm", + contents: phantom.callback(function(pageNum, numPages) { + if (pageNum == 1) { + return ""; + } + return "

Header " + pageNum + " / " + numPages + "

"; + }) + }, + footer: { + height: "1cm", + contents: phantom.callback(function(pageNum, numPages) { + if (pageNum == numPages) { + return ""; + } + return "

Footer " + pageNum + " / " + numPages + "

"; + }) + } + }; + page.open(address, function (status) { + if (status !== 'success') { + console.log('Unable to load the address!'); + } else { + /* check whether the loaded page overwrites the header/footer setting, + i.e. whether a PhantomJSPriting object exists. Use that then instead + of our defaults above. */ + if (page.evaluate(function(){return typeof PhantomJSPrinting == "object";})) { + paperSize = page.paperSize; + paperSize.header.height = page.evaluate(function() { + console.log("woot?", PhantomJSPrinting.header.height); + return PhantomJSPrinting.header.height; + }); + paperSize.header.contents = phantom.callback(function(pageNum, numPages) { + return page.evaluate(function(pageNum, numPages){return PhantomJSPrinting.header.contents(pageNum, numPages);}, pageNum, numPages); + }); + paperSize.footer.height = page.evaluate(function() { + return PhantomJSPrinting.footer.height; + }); + paperSize.footer.contents = phantom.callback(function(pageNum, numPages) { + return page.evaluate(function(pageNum, numPages){return PhantomJSPrinting.footer.contents(pageNum, numPages);}, pageNum, numPages); + }); + page.paperSize = paperSize; + console.log(page.paperSize.header.height); + console.log(page.paperSize.footer.height); + } + window.setTimeout(function () { + page.render(output); + phantom.exit(); + }, 200); + } + }); +} diff --git a/src/bootstrap.js b/src/bootstrap.js index d4f23a68..5c70870a 100644 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -82,6 +82,15 @@ phantom.defaultErrorHandler = function(error, backtrace) { }) } +phantom.callback = function(callback) { + var ret = phantom.createCallback(); + ret.called.connect(function(args) { + var retVal = callback.apply(this, args); + ret.returnValue = retVal; + }); + return ret; +} + phantom.onError = phantom.defaultErrorHandler; // Legacy way to use WebPage diff --git a/src/callback.cpp b/src/callback.cpp new file mode 100644 index 00000000..f3032249 --- /dev/null +++ b/src/callback.cpp @@ -0,0 +1,52 @@ +/* + This file is part of the PhantomJS project from Ofi Labs. + + Copyright (C) 2012 Milian Wolff, KDAB + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "callback.h" + +Callback::Callback(QObject* parent) +: QObject(parent) +{ +} + +QVariant Callback::call(const QVariantList& arguments) +{ + emit called(arguments); + + return m_returnValue; +} + +QVariant Callback::returnValue() const +{ + return m_returnValue; +} + +void Callback::setReturnValue(const QVariant& returnValue) +{ + m_returnValue = returnValue; +} \ No newline at end of file diff --git a/src/callback.h b/src/callback.h new file mode 100644 index 00000000..96b3d8f8 --- /dev/null +++ b/src/callback.h @@ -0,0 +1,57 @@ +/* + This file is part of the PhantomJS project from Ofi Labs. + + Copyright (C) 2012 Milian Wolff, KDAB + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef CALLBACK_H +#define CALLBACK_H + +#include +#include + +class Callback : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QVariant returnValue READ returnValue WRITE setReturnValue) + +public: + Callback(QObject *parent); + + QVariant call(const QVariantList& arguments); + + QVariant returnValue() const; + void setReturnValue(const QVariant& returnValue); + +signals: + void called(const QVariantList& arguments); + +private: + QVariant m_returnValue; +}; + +#endif // CALLBACK_H diff --git a/src/phantom.cpp b/src/phantom.cpp index 2086ec92..5680e8c1 100644 --- a/src/phantom.cpp +++ b/src/phantom.cpp @@ -43,6 +43,7 @@ #include "webserver.h" #include "repl.h" #include "system.h" +#include "callback.h" // public: @@ -257,6 +258,11 @@ QObject *Phantom::createSystem() return m_system; } +QObject* Phantom::createCallback() +{ + return new Callback(this); +} + QString Phantom::loadModuleSource(const QString &name) { QString moduleSource; diff --git a/src/phantom.h b/src/phantom.h index 127b4967..981b8a8e 100644 --- a/src/phantom.h +++ b/src/phantom.h @@ -82,6 +82,7 @@ public slots: QObject *createWebServer(); QObject *createFilesystem(); QObject *createSystem(); + QObject *createCallback(); QString loadModuleSource(const QString &name); bool injectJs(const QString &jsFilePath); diff --git a/src/phantomjs.pro b/src/phantomjs.pro index ebceeb9b..f5887775 100644 --- a/src/phantomjs.pro +++ b/src/phantomjs.pro @@ -12,6 +12,7 @@ RESOURCES = phantomjs.qrc HEADERS += csconverter.h \ phantom.h \ + callback.h \ webpage.h \ webserver.h \ consts.h \ @@ -28,6 +29,7 @@ HEADERS += csconverter.h \ replcompletable.h SOURCES += phantom.cpp \ + callback.cpp \ webpage.cpp \ webserver.cpp \ main.cpp \ diff --git a/src/qt/src/3rdparty/webkit/Source/WebCore/page/Frame.cpp b/src/qt/src/3rdparty/webkit/Source/WebCore/page/Frame.cpp index 61282313..835be735 100644 --- a/src/qt/src/3rdparty/webkit/Source/WebCore/page/Frame.cpp +++ b/src/qt/src/3rdparty/webkit/Source/WebCore/page/Frame.cpp @@ -544,6 +544,8 @@ Color Frame::getDocumentBackgroundColor() const void Frame::setPrinting(bool printing, const FloatSize& pageSize, float maximumShrinkRatio, AdjustViewSizeOrNot shouldAdjustViewSize) { + m_pageResets.clear(); + m_doc->setPrinting(printing); view()->adjustMediaTypeForPrinting(printing); @@ -561,6 +563,38 @@ void Frame::setPrinting(bool printing, const FloatSize& pageSize, float maximumS child->setPrinting(printing, IntSize(), 0, shouldAdjustViewSize); } +void Frame::addResetPage(int page) +{ + m_pageResets.append(page); +} + +void Frame::getPagination(int page, int pages, int& logicalPage, int& logicalPages) const +{ + logicalPage = page; + logicalPages = pages; + int last_j = 0; + int j = 0; + for(size_t i = 0; i < m_pageResets.size(); ++i) { + j = m_pageResets.at(i); + if (j >= page) { + break; + } + last_j = j; + } + if (page > last_j) { + logicalPage = page - last_j; + } + if (last_j) { + if (j > last_j) { + logicalPages = j - last_j; + } else { + logicalPages = pages - last_j; + } + } else if (j >= page && j < pages) { + logicalPages = j; + } +} + void Frame::injectUserScripts(UserScriptInjectionTime injectionTime) { if (!m_page) diff --git a/src/qt/src/3rdparty/webkit/Source/WebCore/page/Frame.h b/src/qt/src/3rdparty/webkit/Source/WebCore/page/Frame.h index 2d76aeec..96ba9e63 100644 --- a/src/qt/src/3rdparty/webkit/Source/WebCore/page/Frame.h +++ b/src/qt/src/3rdparty/webkit/Source/WebCore/page/Frame.h @@ -144,6 +144,8 @@ namespace WebCore { enum AdjustViewSizeOrNot { DoNotAdjustViewSize, AdjustViewSize }; void setPrinting(bool printing, const FloatSize& pageSize, float maximumShrinkRatio, AdjustViewSizeOrNot); + void addResetPage(int page); + void getPagination(int page, int pages, int &logicalPage, int &logicalPages) const; bool inViewSourceMode() const; void setInViewSourceMode(bool = true); @@ -251,6 +253,8 @@ namespace WebCore { bool m_isDisconnected; bool m_excludeFromTextSearch; + Vector m_pageResets; + #if ENABLE(TILED_BACKING_STORE) // FIXME: The tiled backing store belongs in FrameView, not Frame. diff --git a/src/qt/src/3rdparty/webkit/Source/WebCore/page/PrintContext.cpp b/src/qt/src/3rdparty/webkit/Source/WebCore/page/PrintContext.cpp index 660ad11b..a1b8f84a 100644 --- a/src/qt/src/3rdparty/webkit/Source/WebCore/page/PrintContext.cpp +++ b/src/qt/src/3rdparty/webkit/Source/WebCore/page/PrintContext.cpp @@ -82,9 +82,16 @@ void PrintContext::computePageRects(const FloatRect& printRect, float headerHeig float pageWidth; float pageHeight; if (isHorizontal) { - float ratio = printRect.height() / printRect.width(); pageWidth = view->docWidth(); - pageHeight = floorf(pageWidth * ratio); + ///NOTE: if we do not reuse the previously set logical page height, + /// we can end up with off-by-one erros in the page height, + /// leading to rendering issues (e.g. rows overlap pagebreaks) + if (view->pageLogicalHeight() == 0) { + float ratio = printRect.height() / printRect.width(); + pageHeight = floorf(pageWidth * ratio); + } else { + pageHeight = view->pageLogicalHeight(); + } } else { float ratio = printRect.width() / printRect.height(); pageHeight = view->docHeight(); diff --git a/src/qt/src/3rdparty/webkit/Source/WebCore/rendering/RenderBlock.cpp b/src/qt/src/3rdparty/webkit/Source/WebCore/rendering/RenderBlock.cpp index 4ad1bfe8..01810eed 100644 --- a/src/qt/src/3rdparty/webkit/Source/WebCore/rendering/RenderBlock.cpp +++ b/src/qt/src/3rdparty/webkit/Source/WebCore/rendering/RenderBlock.cpp @@ -1348,6 +1348,15 @@ void RenderBlock::layoutBlock(bool relayoutChildren, int pageLogicalHeight) } } setNeedsLayout(false); + + if (document()->printing()) { + // PHANTOMJS CUSTOM: reset pagination counter for printing + StyledElement* elem = dynamic_cast(generatingNode()); + if (elem && elem->hasClass() && elem->classNames().contains("phantomjs_reset_pagination")) { + frame()->addResetPage(y() / view()->layoutState()->m_pageLogicalHeight); + } + } + } void RenderBlock::addOverflowFromChildren() diff --git a/src/qt/src/3rdparty/webkit/Source/WebKit/qt/Api/qwebframe.cpp b/src/qt/src/3rdparty/webkit/Source/WebKit/qt/Api/qwebframe.cpp index 744c270f..05a92cd8 100644 --- a/src/qt/src/3rdparty/webkit/Source/WebKit/qt/Api/qwebframe.cpp +++ b/src/qt/src/3rdparty/webkit/Source/WebKit/qt/Api/qwebframe.cpp @@ -110,6 +110,8 @@ #include #include +#include "qwebframe_printingaddons_p.h" + using namespace WebCore; // from text/qfont.cpp @@ -1431,12 +1433,19 @@ bool QWebFrame::event(QEvent *e) \sa render() */ -void QWebFrame::print(QPrinter *printer) const +void QWebFrame::print(QPrinter* printer) const +{ + print(printer, 0); +} + +void QWebFrame::print(QPrinter *printer, PrintCallback *callback) const { QPainter painter; if (!painter.begin(printer)) return; + HeaderFooter headerFooter(this, printer, callback); + const qreal zoomFactorX = (qreal)printer->logicalDpiX() / qt_defaultDpi(); const qreal zoomFactorY = (qreal)printer->logicalDpiY() / qt_defaultDpi(); @@ -1449,7 +1458,7 @@ void QWebFrame::print(QPrinter *printer) const int(qprinterRect.width() / zoomFactorX), int(qprinterRect.height() / zoomFactorY)); - printContext.begin(pageRect.width()); + printContext.begin(pageRect.width(), pageRect.height()); printContext.computePageRects(pageRect, /* headerHeight */ 0, /* footerHeight */ 0, /* userScaleFactor */ 1.0, pageHeight); @@ -1499,6 +1508,13 @@ void QWebFrame::print(QPrinter *printer) const printContext.end(); return; } + if (headerFooter.isValid()) { + // print header/footer + int logicalPage, logicalPages; + d->frame->getPagination(page, printContext.pageCount(), logicalPage, logicalPages); + headerFooter.paintHeader(ctx, pageRect, logicalPage, logicalPages); + headerFooter.paintFooter(ctx, pageRect, logicalPage, logicalPages); + } printContext.spoolPage(ctx, page - 1, pageRect.width()); if (j < pageCopies - 1) printer->newPage(); diff --git a/src/qt/src/3rdparty/webkit/Source/WebKit/qt/Api/qwebframe.h b/src/qt/src/3rdparty/webkit/Source/WebKit/qt/Api/qwebframe.h index ca1a0ad4..7a570439 100644 --- a/src/qt/src/3rdparty/webkit/Source/WebKit/qt/Api/qwebframe.h +++ b/src/qt/src/3rdparty/webkit/Source/WebKit/qt/Api/qwebframe.h @@ -199,10 +199,24 @@ public: QWebSecurityOrigin securityOrigin() const; +#ifndef QT_NO_PRINTER + struct PrintCallback { + /// height of header in points + virtual qreal headerHeight() const = 0; + /// height of footer in points + virtual qreal footerHeight() const = 0; + /// header contents (in HTML) on page @p page + virtual QString header(int page, int numPages) = 0; + /// footer contents (in HTML) on page @p page + virtual QString footer(int page, int numPages) = 0; + }; +#endif + public Q_SLOTS: QVariant evaluateJavaScript(const QString& scriptSource, const QString& file = QString()); #ifndef QT_NO_PRINTER void print(QPrinter *printer) const; + void print(QPrinter *printer, PrintCallback *callback) const; #endif Q_SIGNALS: diff --git a/src/qt/src/3rdparty/webkit/Source/WebKit/qt/Api/qwebframe_p.h b/src/qt/src/3rdparty/webkit/Source/WebKit/qt/Api/qwebframe_p.h index 4108972d..903db857 100644 --- a/src/qt/src/3rdparty/webkit/Source/WebKit/qt/Api/qwebframe_p.h +++ b/src/qt/src/3rdparty/webkit/Source/WebKit/qt/Api/qwebframe_p.h @@ -108,6 +108,8 @@ public: void emitUrlChanged(); void _q_orientationChanged(); + static WebCore::Frame* webcoreFrame(QWebFrame* frame) { return frame->d->frame; }; + QWebFrame *q; Qt::ScrollBarPolicy horizontalScrollBarPolicy; Qt::ScrollBarPolicy verticalScrollBarPolicy; diff --git a/src/qt/src/3rdparty/webkit/Source/WebKit/qt/Api/qwebframe_printingaddons_p.h b/src/qt/src/3rdparty/webkit/Source/WebKit/qt/Api/qwebframe_printingaddons_p.h new file mode 100644 index 00000000..c951ca08 --- /dev/null +++ b/src/qt/src/3rdparty/webkit/Source/WebKit/qt/Api/qwebframe_printingaddons_p.h @@ -0,0 +1,146 @@ +/* + Copyright (C) 2012 Milian Wolff, KDAB (milian.wolff@kdab.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef QWEBFRAME_PRINTINGADDONS_P_H +#define QWEBFRAME_PRINTINGADDONS_P_H + +#include "qwebframe.h" +#include "qwebframe_p.h" + +#include +#include + +#include "GraphicsContext.h" +#include "PrintContext.h" + +// for custom header or footers in printing +class HeaderFooter +{ +public: + HeaderFooter(const QWebFrame* frame, QPrinter* printer, QWebFrame::PrintCallback* callback); + ~HeaderFooter(); + + void setPageRect(const WebCore::IntRect& rect); + + void paintHeader(WebCore::GraphicsContext& ctx, const WebCore::IntRect& pageRect, int pageNum, int totalPages); + void paintFooter(WebCore::GraphicsContext& ctx, const WebCore::IntRect& pageRect, int pageNum, int totalPages); + + bool isValid() + { + return callback && (headerHeightPixel > 0 || footerHeightPixel > 0); + } + +private: + QWebPage page; + QWebFrame::PrintCallback* callback; + int headerHeightPixel; + int footerHeightPixel; + + WebCore::PrintContext* printCtx; + + void paint(WebCore::GraphicsContext& ctx, const WebCore::IntRect& pageRect, const QString& contents, int height); +}; + +HeaderFooter::HeaderFooter(const QWebFrame* frame, QPrinter* printer, QWebFrame::PrintCallback* callback_) +: printCtx(0) +, callback(callback_) +, headerHeightPixel(0) +, footerHeightPixel(0) +{ + if (callback) { + qreal headerHeight = qMax(qreal(0), callback->headerHeight()); + qreal footerHeight = qMax(qreal(0), callback->footerHeight()); + + if (headerHeight || footerHeight) { + // figure out the header/footer height in *DevicePixel* + // based on the height given in *Points* + qreal marginLeft, marginRight, marginTop, marginBottom; + // find existing margins + printer->getPageMargins(&marginLeft, &marginTop, &marginRight, &marginBottom, QPrinter::DevicePixel); + const qreal oldMarginTop = marginTop; + const qreal oldMarginBottom = marginTop; + + printer->getPageMargins(&marginLeft, &marginTop, &marginRight, &marginBottom, QPrinter::Point); + // increase margins to hold header+footer + marginTop += headerHeight; + marginBottom += footerHeight; + printer->setPageMargins(marginLeft, marginTop, marginRight, marginBottom, QPrinter::Point); + + // calculate actual heights + printer->getPageMargins(&marginLeft, &marginTop, &marginRight, &marginBottom, QPrinter::DevicePixel); + headerHeightPixel = marginTop - oldMarginTop; + footerHeightPixel = marginBottom - oldMarginBottom; + + printCtx = new WebCore::PrintContext(QWebFramePrivate::webcoreFrame(page.mainFrame())); + } + } +} + +HeaderFooter::~HeaderFooter() +{ + delete printCtx; + printCtx = 0; +} + +void HeaderFooter::paintHeader(WebCore::GraphicsContext& ctx, const WebCore::IntRect& pageRect, int pageNum, int totalPages) +{ + if (!headerHeightPixel) { + return; + } + const QString c = callback->header(pageNum, totalPages); + if (c.isEmpty()) { + return; + } + + ctx.translate(0, -headerHeightPixel); + paint(ctx, pageRect, c, headerHeightPixel); + ctx.translate(0, +headerHeightPixel); +} + +void HeaderFooter::paintFooter(WebCore::GraphicsContext& ctx, const WebCore::IntRect& pageRect, int pageNum, int totalPages) +{ + if (!footerHeightPixel) { + return; + } + const QString c = callback->footer(pageNum, totalPages); + if (c.isEmpty()) { + return; + } + + const int offset = pageRect.height(); + ctx.translate(0, +offset); + paint(ctx, pageRect, c, footerHeightPixel); + ctx.translate(0, -offset); +} + +void HeaderFooter::paint(WebCore::GraphicsContext& ctx, const WebCore::IntRect& pageRect, const QString& contents, int height) +{ + page.mainFrame()->setHtml(contents); + + printCtx->begin(pageRect.width(), height); + float tempHeight; + printCtx->computePageRects(pageRect, /* headerHeight */ 0, /* footerHeight */ 0, /* userScaleFactor */ 1.0, tempHeight); + + printCtx->spoolPage(ctx, 0, pageRect.width()); + + printCtx->end(); +} + + +#endif // QWEBFRAME_PRINTINGADDONS_P_H diff --git a/src/webpage.cpp b/src/webpage.cpp index 426a6173..6a8d3b08 100644 --- a/src/webpage.cpp +++ b/src/webpage.cpp @@ -53,6 +53,7 @@ #include #include "consts.h" +#include "callback.h" // Ensure we have at least head and body. #define BLANK_HTML "" @@ -613,10 +614,63 @@ bool WebPage::renderPdf(const QString &fileName) printer.setPageMargins(marginLeft, marginTop, marginRight, marginBottom, QPrinter::Point); - m_mainFrame->print(&printer); + m_mainFrame->print(&printer, this); return true; } +qreal getHeight(const QVariantMap &map, const QString &key) +{ + QVariant footer = map.value(key); + if (!footer.canConvert(QVariant::Map)) { + return 0; + } + QVariant height = footer.toMap().value("height"); + if (!height.canConvert(QVariant::String)) { + return 0; + } + return stringToPointSize(height.toString()); +} + +qreal WebPage::footerHeight() const +{ + return getHeight(m_paperSize, "footer"); +} + +qreal WebPage::headerHeight() const +{ + return getHeight(m_paperSize, "header"); +} + +QString getHeaderFooter(const QVariantMap &map, const QString &key, QWebFrame *frame, int page, int numPages) +{ + QVariant header = map.value(key); + if (!header.canConvert(QVariant::Map)) { + return QString(); + } + QVariant callback = header.toMap().value("contents"); + if (callback.canConvert()) { + Callback* caller = qobject_cast(callback.value()); + if (caller) { + QVariant ret = caller->call(QVariantList() << page << numPages); + if (ret.canConvert(QVariant::String)) { + return ret.toString(); + } + } + } + frame->evaluateJavaScript("console.error('Bad header callback given, use phantom.callback);", QString()); + return QString(); +} + +QString WebPage::header(int page, int numPages) +{ + return getHeaderFooter(m_paperSize, "header", m_mainFrame, page, numPages); +} + +QString WebPage::footer(int page, int numPages) +{ + return getHeaderFooter(m_paperSize, "footer", m_mainFrame, page, numPages); +} + void WebPage::uploadFile(const QString &selector, const QString &fileName) { QWebElement el = m_mainFrame->findFirstElement(selector); diff --git a/src/webpage.h b/src/webpage.h index f6b0a805..9cb51f83 100644 --- a/src/webpage.h +++ b/src/webpage.h @@ -34,6 +34,7 @@ #include #include #include +#include #include "replcompletable.h" @@ -43,7 +44,7 @@ class NetworkAccessManager; class QWebInspector; class Phantom; -class WebPage: public REPLCompletable +class WebPage: public REPLCompletable, public QWebFrame::PrintCallback { Q_OBJECT Q_PROPERTY(QString content READ content WRITE setContent) @@ -85,6 +86,11 @@ public: void showInspector(const int remotePort = -1); + QString footer(int page, int numPages); + qreal footerHeight() const; + QString header(int page, int numPages); + qreal headerHeight() const; + public slots: void openUrl(const QString &address, const QVariant &op, const QVariantMap &settings); void release();