Headless and PDF printing fixes.

This is just applying headless_and_pdf_fixes.patch.
See also https://github.com/ariya/phantomjs/pull/173..
1.5
Ariya Hidayat 2012-03-06 19:28:56 -08:00
parent 6299e14641
commit 08fc50d149
15 changed files with 835 additions and 87 deletions

View File

@ -102,7 +102,6 @@ CONFIG -= warn_on
# Treat warnings as errors on x86/Linux/GCC
linux-g++* {
isEqual(QT_ARCH,x86_64)|isEqual(QT_ARCH,i386): QMAKE_CXXFLAGS += -Werror
greaterThan(QT_GCC_MAJOR_VERSION, 3):greaterThan(QT_GCC_MINOR_VERSION, 5) {
if (!contains(QMAKE_CXXFLAGS, -std=c++0x) && !contains(QMAKE_CXXFLAGS, -std=gnu++0x)) {

View File

@ -46,6 +46,7 @@
#include <QtCore/qobjectdefs.h>
#include <QtCore/qscopedpointer.h>
#include <QtGui/qpainter.h>
#include <QtCore/qurl.h>
QT_BEGIN_HEADER
@ -162,6 +163,19 @@ public:
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 addTextField(const QRectF &r, const QString &text, const QString &name, bool multiLine, bool password, bool readOnly, int maxLength) {
Q_UNUSED(r); Q_UNUSED(text); Q_UNUSED(name); Q_UNUSED(multiLine); Q_UNUSED(password); Q_UNUSED(readOnly); Q_UNUSED(maxLength);
}
virtual void addCheckBox(const QRectF &r, bool checked, const QString &name, bool readOnly) {
Q_UNUSED(r); Q_UNUSED(checked); Q_UNUSED(name); Q_UNUSED(readOnly);
}
virtual void addRadioButton(const QRectF &r, const QString & group="", bool checked=false, const QString &name="", bool readOnly=false) {
Q_UNUSED(r); Q_UNUSED(checked); Q_UNUSED(name); Q_UNUSED(readOnly); Q_UNUSED(group);
}
virtual void drawLines(const QLine *lines, int lineCount);
virtual void drawLines(const QLineF *lines, int lineCount);
@ -177,6 +191,11 @@ public:
virtual void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode);
virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) = 0;
virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr, const QByteArray * data) {
Q_UNUSED(data);
drawPixmap(r,pm,sr);
}
virtual void drawTextItem(const QPointF &p, const QTextItem &textItem);
virtual void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s);
virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr,

View File

@ -2248,11 +2248,11 @@ namespace {
/*!
\reimp
*/
void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &_img, const QRectF &_sr,
Qt::ImageConversionFlags)
{
#ifdef QT_DEBUG_DRAW
qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << _sr << " image=" << _img.size() << "depth=" << img.depth();
#endif
if (r.isEmpty())
@ -2260,6 +2260,17 @@ void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRe
Q_D(QRasterPaintEngine);
QRasterPaintEngineState *s = state();
QImage img;
QRectF sr=_sr;
if (s->matrix.isAffine()) {
img = _img.copy(sr.toRect()).scaled(
s->matrix.mapRect(r).size().toSize(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
sr = img.rect();
} else {
img=_img;
}
int sr_l = qFloor(sr.left());
int sr_r = qCeil(sr.right()) - 1;
int sr_t = qFloor(sr.top());

View File

@ -5393,7 +5393,7 @@ void QPainter::drawPixmap(const QPointF &p, const QPixmap &pm)
}
}
void QPainter::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
void QPainter::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr, const QByteArray * data)
{
#if defined QT_DEBUG_DRAW
if (qt_show_painter_debug_output)
@ -5518,7 +5518,7 @@ void QPainter::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
x += d->state->matrix.dx();
y += d->state->matrix.dy();
}
d->engine->drawPixmap(QRectF(x, y, w, h), pm, QRectF(sx, sy, sw, sh));
d->engine->drawPixmap(QRectF(x, y, w, h), pm, QRectF(sx, sy, sw, sh), data);
}
}
@ -7254,6 +7254,200 @@ 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);
}
void QPainter::addTextField(const QRectF &r, const QString &text, const QString &name, bool multiLine, bool password, bool readOnly, int maxLength) {
Q_D(QPainter);
if (!d->engine) {
qWarning("QPainter::addTextField: Painter not active");
return;
}
d->engine->addTextField(worldTransform().mapRect(r), text, name, multiLine, password, readOnly, maxLength);
}
void QPainter::addCheckBox(const QRectF &r, bool checked, const QString &name, bool readOnly) {
Q_D(QPainter);
if (!d->engine) {
qWarning("QPainter::addCheckBox: Painter not active");
return;
}
d->engine->addCheckBox(worldTransform().mapRect(r), checked, name, readOnly);
}
void QPainter::addRadioButton(const QRectF &r, const QString & group, bool checked, const QString &name, bool readOnly) {
Q_D(QPainter);
if (!d->engine) {
qWarning("QPainter::addRadioButton: Painter not active");
return;
}
d->engine->addRadioButton(worldTransform().mapRect(r), group, checked, name, readOnly);
}
/*!
Sets the given render \a hint on the painter if \a on is true;
otherwise clears the render hint.

View File

@ -364,7 +364,7 @@ public:
inline void drawPicture(const QPoint &p, const QPicture &picture);
#endif
void drawPixmap(const QRectF &targetRect, const QPixmap &pixmap, const QRectF &sourceRect);
void drawPixmap(const QRectF &targetRect, const QPixmap &pixmap, const QRectF &sourceRect, const QByteArray * data=0);
inline void drawPixmap(const QRect &targetRect, const QPixmap &pixmap, const QRect &sourceRect);
inline void drawPixmap(int x, int y, int w, int h, const QPixmap &pm,
int sx, int sy, int sw, int sh);
@ -447,6 +447,22 @@ public:
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);
void addTextField(const QRectF &r, const QString &text=QString(), const QString &name=QString(), bool multiLine=false, bool password=false, bool readOnly=false, int maxLength=-1);
void addCheckBox(const QRectF &r, bool checked=false, const QString &name=QString(), bool readOnly=false);
void addRadioButton(const QRectF &r, const QString & group=QString(), bool checked=false, const QString &name=QString(), bool readOnly=false);;
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 +837,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)
{

View File

@ -88,8 +88,11 @@ public:
PPK_PageMargins,
PPK_CopyCount,
PPK_SupportsMultipleCopies,
PPK_PaperSize = PPK_PageSize,
PPK_UseCompression,
PPK_ImageQuality,
PPK_ImageDPI,
PPK_PaperSize = PPK_PageSize,
PPK_CustomBase = 0xff00
};
@ -97,6 +100,8 @@ public:
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;

View File

@ -51,6 +51,7 @@
#include <qimagewriter.h>
#include <qbuffer.h>
#include <qdatetime.h>
#include <QCryptographicHash>
#ifndef QT_NO_PRINTER
#include <limits.h>
@ -77,12 +78,6 @@ extern qint64 qt_image_id(const QImage &image);
// Can't use it though, as gs generates completely wrong images if this is true.
static const bool interpolateImages = false;
#ifdef QT_NO_COMPRESS
static const bool do_compress = false;
#else
static const bool do_compress = true;
#endif
QPdfPage::QPdfPage()
: QPdf::ByteStream(true) // Enable file backing
{
@ -109,6 +104,30 @@ inline QPaintEngine::PaintEngineFeatures qt_pdf_decide_features()
return f;
}
void QPdfEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value) {
Q_D(QPdfEngine);
if (key==PPK_UseCompression)
d->doCompress = value.toBool();
else if (key==PPK_ImageQuality)
d->imageQuality = value.toInt();
else if (key==PPK_ImageDPI)
d->imageDPI = value.toInt();
else
QPdfBaseEngine::setProperty(key, value);
}
QVariant QPdfEngine::property(PrintEnginePropertyKey key) const {
Q_D(const QPdfEngine);
if (key==PPK_UseCompression)
return d->doCompress;
else if (key==PPK_ImageQuality)
return d->imageQuality;
else if (key==PPK_ImageDPI)
return d->imageDPI;
else
return QPdfBaseEngine::property(key);
}
QPdfEngine::QPdfEngine(QPrinter::PrinterMode m)
: QPdfBaseEngine(*new QPdfEnginePrivate(m), qt_pdf_decide_features())
{
@ -156,6 +175,51 @@ bool QPdfEngine::begin(QPaintDevice *pdev)
bool QPdfEngine::end()
{
Q_D(QPdfEngine);
if (d->outlineRoot) {
d->outlineRoot->obj = d->requestObject();
d->writeOutlineChildren(d->outlineRoot);
d->addXrefEntry(d->outlineRoot->obj);
d->xprintf("<</Type /Outlines /First %d 0 R\n/Last %d 0 R>>\nendobj\n",
d->outlineRoot->firstChild->obj, d->outlineRoot->lastChild->obj);
}
if (d->formFields.size()) {
uint font = d->addXrefEntry(-1);
d->xprintf("<</Type/Font/Name/Helv/BaseFont/Helvetica/Subtype/Type1>>\n"
"endobj\n");
d->addXrefEntry(d->formFieldList);
d->xprintf("<</Fields[");
foreach(const uint & i, d->formFields)
d->xprintf("%d 0 R ",i);
d->xprintf("]\n"
"/DR<</Font<</Helv %d 0 R>>>>\n"
"/DA(/Helv 0 Tf 0 g)\n"
">>\n"
"endobj\n", font);
}
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->formFields.size())
d->xprintf("/AcroForm %d 0 R\n", d->formFieldList);
if (d->anchors.size()) {
d->xprintf("/Dests <<\n");
for (QHash<QString, uint>::iterator i=d->anchors.begin();
i != d->anchors.end(); ++i) {
d->printAnchor(i.key());
d->xprintf(" %d 0 R\n", i.value());
}
d->xprintf(">>\n");
}
d->xprintf(">>\n"
"endobj\n");
d->writeTail();
d->stream->unsetDevice();
@ -165,8 +229,83 @@ bool QPdfEngine::end()
return true;
}
void QPdfEngine::addCheckBox(const QRectF &r, bool checked, const QString &name, bool readOnly) {
Q_D(QPdfEngine);
uint obj = d->addXrefEntry(-1);
char buf[256];
QRectF rr = d->pageMatrix().mapRect(r);
//Note that the pdf spec sayes that we should add some sort of default appearence atleast for yes, which we dont ghost script provides one, however acroread does not
if (d->formFieldList == -1) d->formFieldList = d->requestObject();
d->xprintf("<<\n"
"/Type /Annot\n"
"/Parrent %d 0 R\n"
"/Rect[", d->formFieldList);
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"
"/FT/Btn\n"
"/Subtype/Widget\n"
"/P %d 0 R\n", d->pages.back());
if (checked)
d->xprintf("/AS /Yes\n");
if (!name.isEmpty()) {
d->xprintf("/T");
d->printString(name);
d->xprintf("\n");
}
d->xprintf("/Ff %d\n"
">>\n"
"endobj\n",
(readOnly?1:0)<<0);
d->currentPage->annotations.push_back(obj);
d->formFields.push_back(obj);
}
void QPdfEngine::drawPixmap (const QRectF &rectangle, const QPixmap &pixmap, const QRectF &sr)
void QPdfEngine::addTextField(const QRectF &r, const QString &text, const QString &name, bool multiLine, bool password, bool readOnly, int maxLength)
{
Q_D(QPdfEngine);
uint obj = d->addXrefEntry(-1);
char buf[256];
QRectF rr = d->pageMatrix().mapRect(r);
if (d->formFieldList == -1) d->formFieldList = d->requestObject();
d->xprintf("<<\n"
"/Type /Annot\n"
"/Parrent %d 0 R\n"
"/Rect[", d->formFieldList);
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"
"/BS<</S/I>>\n"
"/FT/Tx\n"
"/Subtype/Widget\n"
"/P %d 0 R\n", d->pages.back());
if (!text.isEmpty()) {
d->xprintf("/V");
d->printString(text);
d->xprintf("\n");
}
if (!name.isEmpty()) {
d->xprintf("/T");
d->printString(name);
d->xprintf("\n");
}
if (maxLength >= 0)
d->xprintf("/MaxLen %d\n",maxLength);
d->xprintf("/DA(/Helv 12 Tf 0 g)\n"
"/Ff %d\n"
">>\n"
"endobj\n",
(readOnly?1:0)<<0 | (password?1:0)<<13 | (multiLine?1:0)<<12
);
d->currentPage->annotations.push_back(obj);
d->formFields.push_back(obj);
}
void QPdfEngine::drawPixmap (const QRectF &rectangle, const QPixmap &pixmap, const QRectF &sr, const QByteArray * data)
{
if (sr.isEmpty() || rectangle.isEmpty() || pixmap.isNull())
return;
@ -176,22 +315,35 @@ void QPdfEngine::drawPixmap (const QRectF &rectangle, const QPixmap &pixmap, con
QRect sourceRect = sr.toRect();
QPixmap pm = sourceRect != pixmap.rect() ? pixmap.copy(sourceRect) : pixmap;
QImage image = pm.toImage();
QImage unscaled = pm.toImage();
QImage image = unscaled;
QRectF a = d->stroker.matrix.mapRect(rectangle);
QRectF c = d->paperRect();
int maxWidth = int(a.width() / c.width() * d->width() / 72.0 * d->imageDPI);
int maxHeight = int(a.height() / c.height() * d->height() / 72.0 * d->imageDPI);
if (image.width() > maxWidth || image.height() > maxHeight)
image = unscaled.scaled( image.size().boundedTo( QSize(maxWidth, maxHeight) ), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
bool useScaled=true;
bool bitmap = true;
const int object = d->addImage(image, &bitmap, pm.cacheKey());
const int object = d->addImage(image, &bitmap, pm.cacheKey(), &unscaled, (sr == pixmap.rect()?data:0), &useScaled );
int width = useScaled?image.width():unscaled.width();
int height = useScaled?image.height():unscaled.height();
if (object < 0)
return;
*d->currentPage << "q\n/GSa gs\n";
*d->currentPage
<< QPdf::generateMatrix(QTransform(rectangle.width() / sr.width(), 0, 0, rectangle.height() / sr.height(),
<< QPdf::generateMatrix(QTransform(rectangle.width() / width, 0, 0, rectangle.height() / height,
rectangle.x(), rectangle.y()) * (d->simplePen ? QTransform() : d->stroker.matrix));
if (bitmap) {
// set current pen as d->brush
d->brush = d->pen.brush();
}
setBrush();
d->currentPage->streamImage(image.width(), image.height(), object);
d->currentPage->streamImage(width, height, object);
*d->currentPage << "Q\n";
d->brush = b;
@ -301,18 +453,67 @@ QPdfEnginePrivate::QPdfEnginePrivate(QPrinter::PrinterMode m)
: QPdfBaseEnginePrivate(m)
{
streampos = 0;
doCompress = true;
imageDPI = 1400;
imageQuality = 94;
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
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("<</Title ");
printString(i->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)
@ -520,10 +721,34 @@ int QPdfEnginePrivate::addBrushPattern(const QTransform &m, bool *specifyColor,
return patternObj;
}
void QPdfEnginePrivate::convertImage(const QImage & image, QByteArray & imageData) {
int w = image.width();
int h = image.height();
imageData.resize(colorMode == QPrinter::GrayScale ? w * h : 3 * w * h);
uchar *data = (uchar *)imageData.data();
for (int y = 0; y < h; ++y) {
const QRgb *rgb = (const QRgb *)image.scanLine(y);
if (colorMode == QPrinter::GrayScale) {
for (int x = 0; x < w; ++x) {
*(data++) = qGray(*rgb);
++rgb;
}
} else {
for (int x = 0; x < w; ++x) {
*(data++) = qRed(*rgb);
*(data++) = qGreen(*rgb);
*(data++) = qBlue(*rgb);
++rgb;
}
}
}
}
/*!
* Adds an image to the pdf and return the pdf-object id. Returns -1 if adding the image failed.
*/
int QPdfEnginePrivate::addImage(const QImage &img, bool *bitmap, qint64 serial_no)
int QPdfEnginePrivate::addImage(const QImage &img, bool *bitmap, qint64 serial_no, const QImage * noneScaled, const QByteArray * data, bool * useScaled)
{
if (img.isNull())
return -1;
@ -564,65 +789,91 @@ int QPdfEnginePrivate::addImage(const QImage &img, bool *bitmap, qint64 serial_n
}
object = writeImage(data, w, h, d, 0, 0);
} else {
QByteArray softMaskData;
bool dct = false;
QByteArray imageData;
uLongf target=1024*1024*1024;
bool uns=false;
bool dct = false;
if (QImageWriter::supportedImageFormats().contains("jpeg") && colorMode != QPrinter::GrayScale) {
QByteArray imageData2;
QBuffer buffer(&imageData2);
QImageWriter writer(&buffer, "jpeg");
writer.setQuality(imageQuality);
writer.write(image);
if ((uLongf)imageData2.size() < target) {
imageData=imageData2;
target = imageData2.size();
dct = true;
uns=false;
}
}
if (noneScaled && noneScaled->rect() != image.rect()) {
QByteArray imageData2;
convertImage(*noneScaled, imageData2);
uLongf len = imageData2.size();
uLongf destLen = len + len/100 + 13; // zlib requirement
Bytef* dest = new Bytef[destLen];
if (Z_OK == ::compress(dest, &destLen, (const Bytef*) imageData2.data(), (uLongf)len) &&
(uLongf)destLen < target) {
imageData=imageData2;
target=destLen;
dct=false;
uns=true;
}
delete[] dest;
}
{
QByteArray imageData2;
convertImage(image, imageData2);
uLongf len = imageData2.size();
uLongf destLen = len + len/100 + 13; // zlib requirement
Bytef* dest = new Bytef[destLen];
if (Z_OK == ::compress(dest, &destLen, (const Bytef*) imageData2.data(), (uLongf)len) &&
(uLongf)destLen < target) {
imageData=imageData2;
target=destLen;
dct=false;
uns=false;
}
delete[] dest;
}
if (colorMode != QPrinter::GrayScale && noneScaled != 0 && data != 0 &&
data->size() > 2 && (unsigned char)data->data()[0] == 0xff && (unsigned char)data->data()[1] == 0xd8 &&
(uLongf)data->size()*10 < target*13) {
imageData = *data;
target=data->size();
dct=true;
uns=true;
}
if (uns) {
w = noneScaled->width();
h = noneScaled->height();
}
if (useScaled) *useScaled = (uns?false:true);
QByteArray softMaskData;
bool hasAlpha = false;
bool hasMask = false;
if (QImageWriter::supportedImageFormats().contains("jpeg") && colorMode != QPrinter::GrayScale) {
QBuffer buffer(&imageData);
QImageWriter writer(&buffer, "jpeg");
writer.setQuality(94);
writer.write(image);
dct = true;
if (format != QImage::Format_RGB32) {
softMaskData.resize(w * h);
uchar *sdata = (uchar *)softMaskData.data();
for (int y = 0; y < h; ++y) {
const QRgb *rgb = (const QRgb *)image.scanLine(y);
for (int x = 0; x < w; ++x) {
uchar alpha = qAlpha(*rgb);
*sdata++ = alpha;
hasMask |= (alpha < 255);
hasAlpha |= (alpha != 0 && alpha != 255);
++rgb;
}
}
}
} else {
imageData.resize(colorMode == QPrinter::GrayScale ? w * h : 3 * w * h);
uchar *data = (uchar *)imageData.data();
if (format != QImage::Format_RGB32) {
softMaskData.resize(w * h);
uchar *sdata = (uchar *)softMaskData.data();
for (int y = 0; y < h; ++y) {
const QRgb *rgb = (const QRgb *)image.scanLine(y);
if (colorMode == QPrinter::GrayScale) {
for (int x = 0; x < w; ++x) {
*(data++) = qGray(*rgb);
uchar alpha = qAlpha(*rgb);
*sdata++ = alpha;
hasMask |= (alpha < 255);
hasAlpha |= (alpha != 0 && alpha != 255);
++rgb;
}
} else {
for (int x = 0; x < w; ++x) {
*(data++) = qRed(*rgb);
*(data++) = qGreen(*rgb);
*(data++) = qBlue(*rgb);
uchar alpha = qAlpha(*rgb);
*sdata++ = alpha;
hasMask |= (alpha < 255);
hasAlpha |= (alpha != 0 && alpha != 255);
++rgb;
}
const QRgb *rgb = (const QRgb *)(uns?noneScaled->scanLine(y):image.scanLine(y));
for (int x = 0; x < w; ++x) {
uchar alpha = qAlpha(*rgb);
*sdata++ = alpha;
hasMask |= (alpha < 255);
hasAlpha |= (alpha != 0 && alpha != 255);
++rgb;
}
}
if (format == QImage::Format_RGB32)
hasAlpha = hasMask = false;
}
int maskObject = 0;
int softMaskObject = 0;
if (hasAlpha) {
@ -754,7 +1005,7 @@ void QPdfEnginePrivate::xprintf(const char* fmt, ...)
int QPdfEnginePrivate::writeCompressed(QIODevice *dev)
{
#ifndef QT_NO_COMPRESS
if (do_compress) {
if (doCompress) {
int size = QPdfPage::chunkSize();
int sum = 0;
::z_stream zStruct;
@ -828,7 +1079,7 @@ int QPdfEnginePrivate::writeCompressed(QIODevice *dev)
int QPdfEnginePrivate::writeCompressed(const char *src, int len)
{
#ifndef QT_NO_COMPRESS
if(do_compress) {
if(doCompress) {
uLongf destLen = len + len/100 + 13; // zlib requirement
Bytef* dest = new Bytef[destLen];
if (Z_OK == ::compress(dest, &destLen, (const Bytef*) src, (uLongf)len)) {
@ -881,7 +1132,7 @@ int QPdfEnginePrivate::writeImage(const QByteArray &data, int width, int height,
write(data);
len = data.length();
} else {
if (do_compress)
if (doCompress)
xprintf("/Filter /FlateDecode\n>>\nstream\n");
else
xprintf(">>\nstream\n");
@ -904,14 +1155,9 @@ void QPdfEnginePrivate::writeHeader()
writeInfo();
catalog = addXrefEntry(-1);
pageRoot = requestObject();
xprintf("<<\n"
"/Type /Catalog\n"
"/Pages %d 0 R\n"
">>\n"
"endobj\n", pageRoot);
formFieldList = -1;
// graphics state
graphicsState = addXrefEntry(-1);
xprintf("<<\n"
@ -943,13 +1189,22 @@ void QPdfEnginePrivate::writeInfo()
QDateTime now = QDateTime::currentDateTime().toUTC();
QTime t = now.time();
QDate d = now.date();
xprintf("\n/CreationDate (D:%d%02d%02d%02d%02d%02d)\n",
xprintf("\n/CreationDate (D:%d%02d%02d%02d%02d%02d)",
d.year(),
d.month(),
d.day(),
t.hour(),
t.minute(),
t.second());
QDateTime fake=now;
fake.setTimeSpec(Qt::UTC);
int offset = now.secsTo(fake);
if (offset == 0)
xprintf("Z)\n");
else if (offset < 0)
xprintf("-%02d'%02d')\n", (-offset)/60/60 , ((-offset)/60) % 60);
else if (offset > 0)
xprintf("+%02d'%02d')\n", offset/60/60 , (offset/60) % 60);
xprintf(">>\n"
"endobj\n");
}
@ -1035,7 +1290,7 @@ void QPdfEnginePrivate::embedFont(QFontSubset *font)
s << "<<\n"
"/Length1 " << fontData.size() << "\n"
"/Length " << length_object << "0 R\n";
if (do_compress)
if (doCompress)
s << "/Filter /FlateDecode\n";
s << ">>\n"
"stream\n";
@ -1097,6 +1352,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())
@ -1167,7 +1517,7 @@ void QPdfEnginePrivate::writePage()
addXrefEntry(pageStream);
xprintf("<<\n"
"/Length %d 0 R\n", pageStreamLength); // object number for stream length object
if (do_compress)
if (doCompress)
xprintf("/Filter /FlateDecode\n");
xprintf(">>\n");

View File

@ -1,3 +1,4 @@
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
@ -92,7 +93,12 @@ public:
// reimplementations QPaintEngine
bool begin(QPaintDevice *pdev);
bool end();
void drawPixmap (const QRectF & rectangle, const QPixmap & pixmap, const QRectF & sr);
void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr, const QByteArray * data=0);
void drawPixmap(const QRectF & rectangle, const QPixmap & pixmap, const QRectF & sr) {
drawPixmap(rectangle, pixmap, sr, 0);
}
void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr,
Qt::ImageConversionFlags flags = Qt::AutoColor);
void drawTiledPixmap (const QRectF & rectangle, const QPixmap & pixmap, const QPointF & point);
@ -108,12 +114,23 @@ public:
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);
virtual void addTextField(const QRectF &r, const QString &text, const QString &name, bool multiLine, bool password, bool readOnly, int maxLength);
virtual void addCheckBox(const QRectF &r, bool checked, const QString &name, bool readOnly);
// ### 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();
void setProperty(PrintEnginePropertyKey key, const QVariant &value);
QVariant property(PrintEnginePropertyKey key) const;
private:
Q_DISABLE_COPY(QPdfEngine)
@ -124,6 +141,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();
@ -141,7 +187,9 @@ public:
void writeHeader();
void writeTail();
int addImage(const QImage &image, bool *bitmap, qint64 serial_no);
void convertImage(const QImage & image, QByteArray & imageData);
int addImage(const QImage &image, bool *bitmap, qint64 serial_no, const QImage * noneScaled=0, const QByteArray * data=0, bool * useScaled=0);
int addConstantAlphaObject(int brushAlpha, int penAlpha = 255);
int addBrushPattern(const QTransform &matrix, bool *specifyColor, int *gStateObject);
@ -161,16 +209,24 @@ private:
void writeFonts();
void embedFont(QFontSubset *font);
int formFieldList;
QVector<uint> formFields;
QVector<int> xrefPositions;
QDataStream* stream;
int streampos;
bool doCompress;
int imageDPI;
int imageQuality;
int writeImage(const QByteArray &data, int width, int height, int depth,
int maskObject, int softMaskObject, bool dct = false);
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 +239,8 @@ private:
// various PDF objects
int pageRoot, catalog, info, graphicsState, patternColorSpace;
QVector<uint> dests;
QHash<QString, uint> anchors;
QVector<uint> pages;
QHash<qint64, uint> imageCache;
QHash<QPair<uint, uint>, uint > alphaCache;

View File

@ -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

View File

@ -147,6 +147,9 @@ public:
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;

View File

@ -47,6 +47,7 @@
#include "qpixmapcache.h"
#include "qstyleoption.h"
#include "private/qstyle_p.h"
#include "private/qapplication_p.h"
#ifndef QT_NO_DEBUG
#include "qdebug.h"
#endif
@ -2229,7 +2230,7 @@ QPalette QStyle::standardPalette() const
{
#ifdef Q_WS_X11
QColor background;
if (QX11Info::appDepth() > 8)
if (!qt_is_gui_used || QX11Info::appDepth() > 8)
background = QColor(0xd4, 0xd0, 0xc8); // win 2000 grey
else
background = QColor(192, 192, 192);

View File

@ -5,9 +5,15 @@ QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/platforms
SOURCES = main.cpp \
qminimalintegration.cpp \
qminimalwindowsurface.cpp
qminimalwindowsurface.cpp \
qfontconfigdatabase.cpp
HEADERS = qminimalintegration.h \
qminimalwindowsurface.h
qminimalwindowsurface.h \
qfontconfigdatabase.h
include(../fontdatabases/basicunix/basicunix.pri)
target.path += $$[QT_INSTALL_PLUGINS]/platforms
INSTALLS += target
LIBS += -lfontconfig

View File

@ -41,11 +41,22 @@
#include "qminimalintegration.h"
#include "qminimalwindowsurface.h"
#include "qfontconfigdatabase.h"
#include <QtGui/private/qpixmap_raster_p.h>
#include <QtGui/QPlatformWindow>
QSize QMinimalScreen::physicalSize() const
{
static const int dpi = 85;
int width = geometry().width() / dpi * qreal(25.4) ;
int height = geometry().height() / dpi * qreal(25.4) ;
return QSize(width,height);
}
QMinimalIntegration::QMinimalIntegration()
: mFontDb(new QFontconfigDatabase())
{
QMinimalScreen *mPrimaryScreen = new QMinimalScreen();
@ -56,6 +67,11 @@ QMinimalIntegration::QMinimalIntegration()
mScreens.append(mPrimaryScreen);
}
QPlatformFontDatabase *QMinimalIntegration::fontDatabase() const
{
return mFontDb;
}
bool QMinimalIntegration::hasCapability(QPlatformIntegration::Capability cap) const
{
switch (cap) {

View File

@ -47,6 +47,8 @@
QT_BEGIN_NAMESPACE
class QPlatformFontDatabase;
class QMinimalScreen : public QPlatformScreen
{
public:
@ -56,6 +58,7 @@ public:
QRect geometry() const { return mGeometry; }
int depth() const { return mDepth; }
QImage::Format format() const { return mFormat; }
QSize physicalSize() const;
public:
QRect mGeometry;
@ -74,11 +77,14 @@ public:
QPixmapData *createPixmapData(QPixmapData::PixelType type) const;
QPlatformWindow *createPlatformWindow(QWidget *widget, WId winId) const;
QWindowSurface *createWindowSurface(QWidget *widget, WId winId) const;
QPlatformFontDatabase *fontDatabase() const;
QList<QPlatformScreen *> screens() const { return mScreens; }
private:
QList<QPlatformScreen *> mScreens;
QPlatformFontDatabase *mFontDb;
};
QT_END_NAMESPACE

View File

@ -68,10 +68,12 @@ void QMinimalWindowSurface::flush(QWidget *widget, const QRegion &region, const
Q_UNUSED(region);
Q_UNUSED(offset);
/* Don't save to a temporary file
static int c = 0;
QString filename = QString("output%1.png").arg(c++, 4, 10, QLatin1Char('0'));
qDebug() << "QMinimalWindowSurface::flush() saving contents to" << filename.toLocal8Bit().constData();
mImage.save(filename);
*/
}
void QMinimalWindowSurface::resize(const QSize &size)