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();