mirror of https://github.com/vitalif/phantomjs
Make it possible to print pages with user-defined headers/footers.
Usage: page.paperSize = { margin: "1cm", header: { height: "1cm", contents: phantom.callback(function(pageNum, numPages) { return "<h1>" + pageNum + " / " + numPages + "</h1>"; }) }, footer: { height: "0.5cm", contents: phantom.callback(function(pageNum, numPages) { return "<h2>" + pageNum + " / " + numPages + "</h1>"; }) } }; Note: The contents can return arbitrary HTML but since we cannot re-layout the whole website for every page, the header/footers must have the static height defined in the height property. Note: The new example printheaderfooter.js shows the usage. It also shows how one could delegate the above to a JavaScript function on the loaded website, which allows one to print pages and let the actually printed page decide how the header/footer should look like. Note: The page-counter can be reset by adding the class "phantomjs_reset_pagination" to HTML block-elements that should reset the counter. ISSUE: 410 (http://code.google.com/p/phantomjs/issues/detail?id=410)1.6
parent
67d2e8c2f7
commit
24a9665c4d
|
@ -0,0 +1,70 @@
|
|||
var page = require('webpage').create(),
|
||||
system = require('system');
|
||||
|
||||
function someCallback(pageNum, numPages) {
|
||||
return "<h1> somCallback: " + pageNum + " / " + numPages + "</h1>";
|
||||
}
|
||||
|
||||
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 "<h1>Header <span style='float:right'>" + pageNum + " / " + numPages + "</span></h1>";
|
||||
})
|
||||
},
|
||||
footer: {
|
||||
height: "1cm",
|
||||
contents: phantom.callback(function(pageNum, numPages) {
|
||||
if (pageNum == numPages) {
|
||||
return "";
|
||||
}
|
||||
return "<h1>Footer <span style='float:right'>" + pageNum + " / " + numPages + "</span></h1>";
|
||||
})
|
||||
}
|
||||
};
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
This file is part of the PhantomJS project from Ofi Labs.
|
||||
|
||||
Copyright (C) 2012 Milian Wolff, KDAB <milian.wolff@kdab.com>
|
||||
|
||||
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 <organization> 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 <COPYRIGHT HOLDER> 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;
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
This file is part of the PhantomJS project from Ofi Labs.
|
||||
|
||||
Copyright (C) 2012 Milian Wolff, KDAB <milian.wolff@kdab.com>
|
||||
|
||||
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 <organization> 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 <COPYRIGHT HOLDER> 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 <QObject>
|
||||
#include <QVariant>
|
||||
|
||||
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
|
|
@ -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;
|
||||
|
|
|
@ -82,6 +82,7 @@ public slots:
|
|||
QObject *createWebServer();
|
||||
QObject *createFilesystem();
|
||||
QObject *createSystem();
|
||||
QObject *createCallback();
|
||||
QString loadModuleSource(const QString &name);
|
||||
bool injectJs(const QString &jsFilePath);
|
||||
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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<int> m_pageResets;
|
||||
|
||||
#if ENABLE(TILED_BACKING_STORE)
|
||||
// FIXME: The tiled backing store belongs in FrameView, not Frame.
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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<StyledElement*>(generatingNode());
|
||||
if (elem && elem->hasClass() && elem->classNames().contains("phantomjs_reset_pagination")) {
|
||||
frame()->addResetPage(y() / view()->layoutState()->m_pageLogicalHeight);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void RenderBlock::addOverflowFromChildren()
|
||||
|
|
|
@ -110,6 +110,8 @@
|
|||
#include <qregion.h>
|
||||
#include <qnetworkrequest.h>
|
||||
|
||||
#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();
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
146
src/qt/src/3rdparty/webkit/Source/WebKit/qt/Api/qwebframe_printingaddons_p.h
vendored
Normal file
146
src/qt/src/3rdparty/webkit/Source/WebKit/qt/Api/qwebframe_printingaddons_p.h
vendored
Normal file
|
@ -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 <qprinter.h>
|
||||
#include <qstring.h>
|
||||
|
||||
#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
|
|
@ -53,6 +53,7 @@
|
|||
#include <gifwriter.h>
|
||||
|
||||
#include "consts.h"
|
||||
#include "callback.h"
|
||||
|
||||
// Ensure we have at least head and body.
|
||||
#define BLANK_HTML "<html><head></head><body></body></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<QObject*>()) {
|
||||
Callback* caller = qobject_cast<Callback*>(callback.value<QObject*>());
|
||||
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);
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <QMap>
|
||||
#include <QVariantMap>
|
||||
#include <QWebPage>
|
||||
#include <QWebFrame>
|
||||
|
||||
#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();
|
||||
|
|
Loading…
Reference in New Issue