Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add patches

  • Loading branch information...
commit d5b2d110fbd819219daffc1a64537cc4ee53694b 1 parent f22877d
@lfranchi lfranchi authored
View
35 deploy/qt48_enable_debugger.patch
@@ -0,0 +1,35 @@
+diff --git a/src/3rdparty/webkit/Source/WebCore/inspector/front-end/Settings.js b/src/3rdparty/webkit/Source/WebCore/inspector/front-end/Settings.js
+index 62bf84a..e830f85 100644
+--- a/src/3rdparty/webkit/Source/WebCore/inspector/front-end/Settings.js
++++ b/src/3rdparty/webkit/Source/WebCore/inspector/front-end/Settings.js
+@@ -40,7 +40,7 @@ var Preferences = {
+ showMissingLocalizedStrings: false,
+ samplingCPUProfiler: false,
+ showColorNicknames: true,
+- debuggerAlwaysEnabled: false,
++ debuggerAlwaysEnabled: true,
+ profilerAlwaysEnabled: false,
+ onlineDetectionEnabled: true,
+ nativeInstrumentationEnabled: false,
+@@ -58,7 +58,7 @@ WebInspector.Settings = function()
+ {
+ this.installApplicationSetting("colorFormat", "hex");
+ this.installApplicationSetting("consoleHistory", []);
+- this.installApplicationSetting("debuggerEnabled", false);
++ this.installApplicationSetting("debuggerEnabled", true);
+ this.installApplicationSetting("domWordWrap", true);
+ this.installApplicationSetting("profilerEnabled", false);
+ this.installApplicationSetting("eventListenersFilter", "all");
+diff --git a/src/3rdparty/webkit/Source/WebKit/qt/Api/qwebinspector.cpp b/src/3rdparty/webkit/Source/WebKit/qt/Api/qwebinspector.cpp
+index 6706f2a..bec3d1c 100644
+--- a/src/3rdparty/webkit/Source/WebKit/qt/Api/qwebinspector.cpp
++++ b/src/3rdparty/webkit/Source/WebKit/qt/Api/qwebinspector.cpp
+@@ -161,7 +161,7 @@ void QWebInspector::showEvent(QShowEvent* event)
+ #if ENABLE(INSPECTOR)
+ // Allows QWebInspector::show() to init the inspector.
+ if (d->page)
+- d->page->d->inspectorController()->show();
++ d->page->d->inspectorController()->showAndEnableDebugger();
+ #endif
+ }
+
View
268 deploy/qt48_fix_inspector.patch
@@ -0,0 +1,268 @@
+diff --git a/src/3rdparty/webkit/Source/WebKit/qt/WebCoreSupport/InspectorServerQt.cpp b/src/3rdparty/webkit/Source/WebKit/qt/WebCoreSupport/InspectorServerQt.cpp
+index 92b7d5c..c5e5bd5 100644
+--- a/src/3rdparty/webkit/Source/WebKit/qt/WebCoreSupport/InspectorServerQt.cpp
++++ b/src/3rdparty/webkit/Source/WebKit/qt/WebCoreSupport/InspectorServerQt.cpp
+@@ -22,7 +22,8 @@
+
+ #include "InspectorClientQt.h"
+ #include "InspectorController.h"
+-#include "MD5.h"
++#include "Base64.h"
++#include "SHA1.h"
+ #include "Page.h"
+ #include "qwebpage.h"
+ #include "qwebpage_p.h"
+@@ -44,43 +45,17 @@ namespace WebCore {
+ /*!
+ Computes the WebSocket handshake response given the two challenge numbers and key3.
+ */
+-static void generateWebSocketChallengeResponse(uint32_t number1, uint32_t number2, const unsigned char key3[8], unsigned char response[16])
++static QByteArray generateWebSocketChallengeResponse(const QByteArray& key)
+ {
+- uint8_t challenge[16];
+- qToBigEndian<qint32>(number1, &challenge[0]);
+- qToBigEndian<qint32>(number2, &challenge[4]);
+- memcpy(&challenge[8], key3, 8);
+- MD5 md5;
+- md5.addBytes(challenge, sizeof(challenge));
+- Vector<uint8_t, 16> digest;
+- md5.checksum(digest);
+- memcpy(response, digest.data(), 16);
+-}
+-
+-/*!
+- Parses and returns a WebSocket challenge number according to the
+- method specified in the WebSocket protocol.
+-
+- The field contains numeric digits interspersed with spaces and
+- non-numeric digits. The protocol ignores the characters that are
+- neither digits nor spaces. The digits are concatenated and
+- interpreted as a long int. The result is this number divided by
+- the number of spaces.
+- */
+-static quint32 parseWebSocketChallengeNumber(QString field)
+-{
+- QString nString;
+- int numSpaces = 0;
+- for (int i = 0; i < field.size(); i++) {
+- QChar c = field[i];
+- if (c == QLatin1Char(' '))
+- numSpaces++;
+- else if ((c >= QLatin1Char('0')) && (c <= QLatin1Char('9')))
+- nString.append(c);
+- }
+- quint32 num = nString.toLong();
+- quint32 result = (numSpaces ? (num / numSpaces) : num);
+- return result;
++ SHA1 sha1;
++ Vector<uint8_t, 20> digest;
++ Vector<char> encoded;
++ QByteArray toHash("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
++ toHash.prepend(key);
++ sha1.addBytes((uint8_t*)toHash.data(), toHash.size());
++ sha1.computeHash(digest);
++ base64Encode((char*)digest.data(), digest.size(), encoded);
++ return QByteArray(encoded.data(), encoded.size());
+ }
+
+ static InspectorServerQt* s_inspectorServer;
+@@ -194,7 +169,7 @@ void InspectorServerRequestHandlerQt::tcpReadyRead()
+ m_path = header.path();
+ m_contentType = header.contentType().toLatin1();
+ m_contentLength = header.contentLength();
+- if (header.hasKey(QLatin1String("Upgrade")) && (header.value(QLatin1String("Upgrade")) == QLatin1String("WebSocket")))
++ if (header.hasKey(QLatin1String("Upgrade")) && (header.value(QLatin1String("Upgrade")) == QLatin1String("websocket")))
+ isWebSocket = true;
+
+ m_data.clear();
+@@ -211,25 +186,19 @@ void InspectorServerRequestHandlerQt::tcpReadyRead()
+ // switch to websocket-style WebSocketService messaging
+ if (m_tcpConnection) {
+ m_tcpConnection->disconnect(SIGNAL(readyRead()));
+- connect(m_tcpConnection, SIGNAL(readyRead()), SLOT(webSocketReadyRead()));
+-
+- QByteArray key3 = m_tcpConnection->read(8);
+-
+- quint32 number1 = parseWebSocketChallengeNumber(header.value(QLatin1String("Sec-WebSocket-Key1")));
+- quint32 number2 = parseWebSocketChallengeNumber(header.value(QLatin1String("Sec-WebSocket-Key2")));
+-
+- char responseData[16];
+- generateWebSocketChallengeResponse(number1, number2, (unsigned char*)key3.data(), (unsigned char*)responseData);
+- QByteArray response(responseData, sizeof(responseData));
++ connect(m_tcpConnection, SIGNAL(readyRead()), SLOT(webSocketReadyRead()), Qt::QueuedConnection);
++
++ QByteArray key = header.value(QLatin1String("Sec-WebSocket-Key")).toLatin1();
++ QString accept = QString::fromLatin1(generateWebSocketChallengeResponse(key));
+
+ QHttpResponseHeader responseHeader(101, QLatin1String("WebSocket Protocol Handshake"), 1, 1);
+ responseHeader.setValue(QLatin1String("Upgrade"), header.value(QLatin1String("Upgrade")));
+ responseHeader.setValue(QLatin1String("Connection"), header.value(QLatin1String("Connection")));
+- responseHeader.setValue(QLatin1String("Sec-WebSocket-Origin"), header.value(QLatin1String("Origin")));
+- responseHeader.setValue(QLatin1String("Sec-WebSocket-Location"), (QLatin1String("ws://") + header.value(QLatin1String("Host")) + m_path));
+- responseHeader.setContentLength(response.size());
++ responseHeader.setValue(QLatin1String("Sec-WebSocket-Accept"), accept);
++ // responseHeader.setValue(QLatin1String("Sec-WebSocket-Origin"), header.value(QLatin1String("Sec-WebSocket-Origin")));
++ // responseHeader.setValue(QLatin1String("Sec-WebSocket-Location"), (QLatin1String("ws://") + header.value(QLatin1String("Host")) + m_path));
+ m_tcpConnection->write(responseHeader.toString().toLatin1());
+- m_tcpConnection->write(response);
++ //m_tcpConnection->write(response);
+ m_tcpConnection->flush();
+
+ if ((words.size() == 4)
+@@ -308,26 +277,54 @@ void InspectorServerRequestHandlerQt::tcpConnectionDisconnected()
+ m_tcpConnection = 0;
+ }
+
+-int InspectorServerRequestHandlerQt::webSocketSend(QByteArray payload)
++int InspectorServerRequestHandlerQt::webSocketSend(const QString& message)
+ {
+- Q_ASSERT(m_tcpConnection);
+- m_tcpConnection->putChar(0x00);
+- int nBytes = m_tcpConnection->write(payload);
+- m_tcpConnection->putChar(0xFF);
+- m_tcpConnection->flush();
+- return nBytes;
++ QByteArray payload = message.toUtf8();
++ return webSocketSend(payload.data(), payload.size());
+ }
+
+ int InspectorServerRequestHandlerQt::webSocketSend(const char* data, size_t length)
+ {
+ Q_ASSERT(m_tcpConnection);
+- m_tcpConnection->putChar(0x00);
++ m_tcpConnection->putChar(0x81);
++ if (length <= 125)
++ m_tcpConnection->putChar(static_cast<uint8_t>(length));
++ else if (length <= pow(2,16)) {
++ m_tcpConnection->putChar(126);
++ quint16 length16 = qToBigEndian<quint16>(static_cast<quint16>(length));
++ m_tcpConnection->write(reinterpret_cast<char*>(&length16), 2);
++ } else {
++ m_tcpConnection->putChar(127);
++ quint64 length64 = qToBigEndian<quint64>(static_cast<quint64>(length));
++ m_tcpConnection->write(reinterpret_cast<char*>(&length64), 8);
++ }
+ int nBytes = m_tcpConnection->write(data, length);
+- m_tcpConnection->putChar(0xFF);
+ m_tcpConnection->flush();
+ return nBytes;
+ }
+
++static QByteArray applyMask(const QByteArray& payload, const QByteArray& maskingKey)
++{
++ Q_ASSERT(maskingKey.size() == 4);
++ QByteArray unmaskedPayload;
++ for (int i = 0; i < payload.size(); ++i) {
++ char unmaskedByte = payload[i] ^ maskingKey[i % 4];
++ unmaskedPayload.append(unmaskedByte);
++ }
++ return unmaskedPayload;
++}
++
++#define BYTETOBINARYPATTERN "%d%d%d%d%d%d%d%d"
++#define BYTETOBINARY(byte) \
++ (byte & 0x80 ? 1 : 0), \
++ (byte & 0x40 ? 1 : 0), \
++ (byte & 0x20 ? 1 : 0), \
++ (byte & 0x10 ? 1 : 0), \
++ (byte & 0x08 ? 1 : 0), \
++ (byte & 0x04 ? 1 : 0), \
++ (byte & 0x02 ? 1 : 0), \
++ (byte & 0x01 ? 1 : 0)
++
+ void InspectorServerRequestHandlerQt::webSocketReadyRead()
+ {
+ Q_ASSERT(m_tcpConnection);
+@@ -336,38 +333,49 @@ void InspectorServerRequestHandlerQt::webSocketReadyRead()
+ QByteArray content = m_tcpConnection->read(m_tcpConnection->bytesAvailable());
+ m_data.append(content);
+ while (m_data.size() > 0) {
+- // first byte in websocket frame should be 0
+- Q_ASSERT(!m_data[0]);
++ bool isMasked = m_data[1] & 0x80;
++ quint64 payloadLen = m_data[1] & 0x7F;
++ int pos = 2;
+
+- // Start of WebSocket frame is indicated by 0
+- if (m_data[0]) {
+- qCritical() << "webSocketReadyRead: unknown frame type" << m_data[0];
+- m_data.clear();
+- m_tcpConnection->close();
+- return;
++ if (payloadLen == 126) {
++ payloadLen = qFromBigEndian<quint16>(*reinterpret_cast<quint16*>(m_data.mid(pos, 2).data()));
++ pos = 4;
++ } else if (payloadLen == 127) {
++ payloadLen = qFromBigEndian<quint64>(*reinterpret_cast<quint64*>(m_data.mid(pos, 8).data()));
++ pos = 8;
+ }
+
+- // End of WebSocket frame indicated by 0xff.
+- int pos = m_data.indexOf(0xff, 1);
+- if (pos < 1)
+- return;
+-
+- // After above checks, length will be >= 0.
+- size_t length = pos - 1;
+- if (length <= 0)
+- return;
+-
+- QByteArray payload = m_data.mid(1, length);
++ QByteArray payload;
++ if (isMasked) {
++ QByteArray maskingKey = m_data.mid(pos, 4);
++ pos += 4;
++ payload = applyMask(m_data.mid(pos, payloadLen), maskingKey);
++ } else {
++ payload = m_data.mid(pos, payloadLen);
++ }
+
++ // Handle fragmentation
++ if ((m_data[0] & 0x80) == 0) { // Non-last fragmented payload
++ m_fragmentedPayload.append(payload);
++
++ m_data = m_data.mid(pos + payloadLen);
++ continue;
++ } else {
++ if ((m_data[0] & 0x0F) == 0) { // Last fragment
++ m_fragmentedPayload.append(payload);
++ payload = m_fragmentedPayload;
++ m_fragmentedPayload.clear();
++ }
++ }
++
++ // Remove this WebSocket message from m_data (payload, start-of-frame byte, end-of-frame byte).
++ m_data = m_data.mid(pos + payloadLen);
+ #if ENABLE(INSPECTOR)
+ if (m_inspectorClient) {
+ InspectorController* inspectorController = m_inspectorClient->m_inspectedWebPage->d->page->inspectorController();
+ inspectorController->dispatchMessageFromFrontend(QString::fromUtf8(payload));
+ }
+ #endif
+-
+- // Remove this WebSocket message from m_data (payload, start-of-frame byte, end-of-frame byte).
+- m_data = m_data.mid(length + 2);
+ }
+ }
+
+diff --git a/src/3rdparty/webkit/Source/WebKit/qt/WebCoreSupport/InspectorServerQt.h b/src/3rdparty/webkit/Source/WebKit/qt/WebCoreSupport/InspectorServerQt.h
+index 922b63e..e1265b9 100644
+--- a/src/3rdparty/webkit/Source/WebKit/qt/WebCoreSupport/InspectorServerQt.h
++++ b/src/3rdparty/webkit/Source/WebKit/qt/WebCoreSupport/InspectorServerQt.h
+@@ -83,7 +83,7 @@ public:
+
+ InspectorServerRequestHandlerQt(QTcpSocket *tcpConnection, InspectorServerQt *server);
+ virtual ~InspectorServerRequestHandlerQt();
+- virtual int webSocketSend(QByteArray payload);
++ virtual int webSocketSend(const QString& message);
+ virtual int webSocketSend(const char *payload, size_t length);
+
+ private slots:
+@@ -100,6 +100,7 @@ private:
+ int m_contentLength;
+ bool m_endOfHeaders;
+ QByteArray m_data;
++ QByteArray m_fragmentedPayload;
+ InspectorClientQt* m_inspectorClient;
+
+ void handleInspectorRequest(QStringList words);
View
2,452 deploy/qt48_headless_and_pdf_fixes.patch
@@ -0,0 +1,2452 @@
+diff --git a/src/3rdparty/webkit/Source/WebKit.pri b/src/3rdparty/webkit/Source/WebKit.pri
+index 5bd9577..ceeff95 100644
+--- a/src/3rdparty/webkit/Source/WebKit.pri
++++ b/src/3rdparty/webkit/Source/WebKit.pri
+@@ -93,7 +93,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)) {
+diff --git a/src/gui/painting/qpaintengine.h b/src/gui/painting/qpaintengine.h
+index 6befdd8..c175b73 100644
+--- a/src/gui/painting/qpaintengine.h
++++ b/src/gui/painting/qpaintengine.h
+@@ -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,
+diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp
+index bcc5f9d..c50640b 100644
+--- a/src/gui/painting/qpaintengine_raster.cpp
++++ b/src/gui/painting/qpaintengine_raster.cpp
+@@ -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());
+diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp
+index efb016e..4952b4b 100644
+--- a/src/gui/painting/qpainter.cpp
++++ b/src/gui/painting/qpainter.cpp
+@@ -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.
+diff --git a/src/gui/painting/qpainter.h b/src/gui/painting/qpainter.h
+index 12a14a2..9a8845d 100644
+--- a/src/gui/painting/qpainter.h
++++ b/src/gui/painting/qpainter.h
+@@ -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)
+ {
+diff --git a/src/gui/painting/qprintengine.h b/src/gui/painting/qprintengine.h
+index da4fe2a..53015ec 100644
+--- a/src/gui/painting/qprintengine.h
++++ b/src/gui/painting/qprintengine.h
+@@ -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;
+diff --git a/src/gui/painting/qprintengine_pdf.cpp b/src/gui/painting/qprintengine_pdf.cpp
+index 2b0bca8..c44dd1b 100644
+--- a/src/gui/painting/qprintengine_pdf.cpp
++++ b/src/gui/painting/qprintengine_pdf.cpp
+@@ -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;
+- bool hasAlpha = false;
+- bool hasMask = false;
+-
++ uLongf target=1024*1024*1024;
++ bool uns=false;
++ bool dct = false;
+ if (QImageWriter::supportedImageFormats().contains("jpeg") && colorMode != QPrinter::GrayScale) {
+- QBuffer buffer(&imageData);
++ QByteArray imageData2;
++
++ QBuffer buffer(&imageData2);
+ QImageWriter writer(&buffer, "jpeg");
+- writer.setQuality(94);
++ writer.setQuality(imageQuality);
+ writer.write(image);
+- dct = true;
++
++ if ((uLongf)imageData2.size() < target) {
++ imageData=imageData2;
++ target = imageData2.size();
++ dct = true;
++ uns=false;
++ }
++ }
+
+- 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;
+- }
+- }
++ 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;
+ }
+- } else {
+- imageData.resize(colorMode == QPrinter::GrayScale ? w * h : 3 * w * h);
+- uchar *data = (uchar *)imageData.data();
++ 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 (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");
+diff --git a/src/gui/painting/qprintengine_pdf_p.h b/src/gui/painting/qprintengine_pdf_p.h
+index ee77e15..d0d87fd 100644
+--- a/src/gui/painting/qprintengine_pdf_p.h
++++ b/src/gui/painting/qprintengine_pdf_p.h
+@@ -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;
+diff --git a/src/gui/painting/qprinter.cpp b/src/gui/painting/qprinter.cpp
+index 74a8f6a..e8b40fa 100644
+--- a/src/gui/painting/qprinter.cpp
++++ b/src/gui/painting/qprinter.cpp
+@@ -933,6 +933,39 @@ void QPrinter::setOutputFileName(const QString &fileName)
+ d->addToManualSetList(QPrintEngine::PPK_OutputFileName);
+ }
+
++/*!
++ Add a section to the document outline. All following sections will be added
++ to as subsections to this section, until endSectionOutline() has been called.
++
++ \a name is the name of the added section. \a anchor is the name of an anchor
++ indicating the beginning of the section. This anchor must be added by calling
++ QPainter::addAnchor().
++
++ Note that for output formats not supporting outlines, currently all other then PDF,
++ this call has no effect.
++
++ \sa endSectionOutline() QPainter::addAnchor()
++
++ \since 4.7
++*/
++void QPrinter::beginSectionOutline(const QString &name, const QString &anchor)
++{
++ Q_D(QPrinter);
++ d->printEngine->beginSectionOutline(name, anchor);
++}
++
++/*!
++ End the current section.
++
++ \sa beginSectionOutline()
++
++ \since 4.7
++*/
++void QPrinter::endSectionOutline()
++{
++ Q_D(QPrinter);
++ d->printEngine->endSectionOutline();
++}
+
+ /*!
+ Returns the name of the program that sends the print output to the
+diff --git a/src/gui/painting/qprinter.h b/src/gui/painting/qprinter.h
+index 58db612..457bd13 100644
+--- a/src/gui/painting/qprinter.h
++++ b/src/gui/painting/qprinter.h
+@@ -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;
+
+diff --git a/src/gui/styles/qstyle.cpp b/src/gui/styles/qstyle.cpp
+index c5eb330..cf3a2f7 100644
+--- a/src/gui/styles/qstyle.cpp
++++ b/src/gui/styles/qstyle.cpp
+@@ -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);
+diff --git a/src/plugins/platforms/minimal/minimal.pro b/src/plugins/platforms/minimal/minimal.pro
+index 438a88e..2886996 100644
+--- a/src/plugins/platforms/minimal/minimal.pro
++++ b/src/plugins/platforms/minimal/minimal.pro
+@@ -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
+\ No newline at end of file
+diff --git a/src/plugins/platforms/minimal/qfontconfigdatabase.cpp b/src/plugins/platforms/minimal/qfontconfigdatabase.cpp
+new file mode 100644
+index 0000000..377d655
+--- /dev/null
++++ b/src/plugins/platforms/minimal/qfontconfigdatabase.cpp
+@@ -0,0 +1,601 @@
++/****************************************************************************
++**
++** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
++** All rights reserved.
++** Contact: Nokia Corporation (qt-info@nokia.com)
++**
++** This file is part of the plugins of the Qt Toolkit.
++**
++** $QT_BEGIN_LICENSE:LGPL$
++** GNU Lesser General Public License Usage
++** This file may be used under the terms of the GNU Lesser General Public
++** License version 2.1 as published by the Free Software Foundation and
++** appearing in the file LICENSE.LGPL included in the packaging of this
++** file. Please review the following information to ensure the GNU Lesser
++** General Public License version 2.1 requirements will be met:
++** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
++**
++** In addition, as a special exception, Nokia gives you certain additional
++** rights. These rights are described in the Nokia Qt LGPL Exception
++** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
++**
++** GNU General Public License Usage
++** Alternatively, this file may be used under the terms of the GNU General
++** Public License version 3.0 as published by the Free Software Foundation
++** and appearing in the file LICENSE.GPL included in the packaging of this
++** file. Please review the following information to ensure the GNU General
++** Public License version 3.0 requirements will be met:
++** http://www.gnu.org/copyleft/gpl.html.
++**
++** Other Usage
++** Alternatively, this file may be used in accordance with the terms and
++** conditions contained in a signed written agreement between you and Nokia.
++**
++**
++**
++**
++**
++** $QT_END_LICENSE$
++**
++****************************************************************************/
++
++#include "qfontconfigdatabase.h"
++
++#include <QtCore/QList>
++#include <QtGui/private/qfont_p.h>
++
++#include <QtCore/QElapsedTimer>
++
++#include <QtGui/private/qapplication_p.h>
++#include <QtGui/QPlatformScreen>
++
++#include <QtGui/private/qfontengine_ft_p.h>
++#include <QtGui/private/qfontengine_p.h>
++
++
++
++#include <ft2build.h>
++#include FT_TRUETYPE_TABLES_H
++
++#include <fontconfig/fontconfig.h>
++
++#define SimplifiedChineseCsbBit 18
++#define TraditionalChineseCsbBit 20
++#define JapaneseCsbBit 17
++#define KoreanCsbBit 21
++
++static inline bool requiresOpenType(int writingSystem)
++{
++ return ((writingSystem >= QFontDatabase::Syriac && writingSystem <= QFontDatabase::Sinhala)
++ || writingSystem == QFontDatabase::Khmer || writingSystem == QFontDatabase::Nko);
++}
++static inline bool scriptRequiresOpenType(int script)
++{
++ return ((script >= QUnicodeTables::Syriac && script <= QUnicodeTables::Sinhala)
++ || script == QUnicodeTables::Khmer || script == QUnicodeTables::Nko);
++}
++
++static int getFCWeight(int fc_weight)
++{
++ int qtweight = QFont::Black;
++ if (fc_weight <= (FC_WEIGHT_LIGHT + FC_WEIGHT_MEDIUM) / 2)
++ qtweight = QFont::Light;
++ else if (fc_weight <= (FC_WEIGHT_MEDIUM + FC_WEIGHT_DEMIBOLD) / 2)
++ qtweight = QFont::Normal;
++ else if (fc_weight <= (FC_WEIGHT_DEMIBOLD + FC_WEIGHT_BOLD) / 2)
++ qtweight = QFont::DemiBold;
++ else if (fc_weight <= (FC_WEIGHT_BOLD + FC_WEIGHT_BLACK) / 2)
++ qtweight = QFont::Bold;
++
++ return qtweight;
++}
++
++static const char *specialLanguages[] = {
++ "en", // Common
++ "el", // Greek
++ "ru", // Cyrillic
++ "hy", // Armenian
++ "he", // Hebrew
++ "ar", // Arabic
++ "syr", // Syriac
++ "div", // Thaana
++ "hi", // Devanagari
++ "bn", // Bengali
++ "pa", // Gurmukhi
++ "gu", // Gujarati
++ "or", // Oriya
++ "ta", // Tamil
++ "te", // Telugu
++ "kn", // Kannada
++ "ml", // Malayalam
++ "si", // Sinhala
++ "th", // Thai
++ "lo", // Lao
++ "bo", // Tibetan
++ "my", // Myanmar
++ "ka", // Georgian
++ "ko", // Hangul
++ "", // Ogham
++ "", // Runic
++ "km", // Khmer
++ "" // N'Ko
++};
++enum { SpecialLanguageCount = sizeof(specialLanguages) / sizeof(const char *) };
++
++static const ushort specialChars[] = {
++ 0, // English
++ 0, // Greek
++ 0, // Cyrillic
++ 0, // Armenian
++ 0, // Hebrew
++ 0, // Arabic
++ 0, // Syriac
++ 0, // Thaana
++ 0, // Devanagari
++ 0, // Bengali
++ 0, // Gurmukhi
++ 0, // Gujarati
++ 0, // Oriya
++ 0, // Tamil
++ 0xc15, // Telugu
++ 0xc95, // Kannada
++ 0xd15, // Malayalam
++ 0xd9a, // Sinhala
++ 0, // Thai
++ 0, // Lao
++ 0, // Tibetan
++ 0x1000, // Myanmar
++ 0, // Georgian
++ 0, // Hangul
++ 0x1681, // Ogham
++ 0x16a0, // Runic
++ 0, // Khmer
++ 0x7ca // N'Ko
++};
++enum { SpecialCharCount = sizeof(specialChars) / sizeof(ushort) };
++
++// this could become a list of all languages used for each writing
++// system, instead of using the single most common language.
++static const char *languageForWritingSystem[] = {
++ 0, // Any
++ "en", // Latin
++ "el", // Greek
++ "ru", // Cyrillic
++ "hy", // Armenian
++ "he", // Hebrew
++ "ar", // Arabic
++ "syr", // Syriac
++ "div", // Thaana
++ "hi", // Devanagari
++ "bn", // Bengali
++ "pa", // Gurmukhi
++ "gu", // Gujarati
++ "or", // Oriya
++ "ta", // Tamil
++ "te", // Telugu
++ "kn", // Kannada
++ "ml", // Malayalam
++ "si", // Sinhala
++ "th", // Thai
++ "lo", // Lao
++ "bo", // Tibetan
++ "my", // Myanmar
++ "ka", // Georgian
++ "km", // Khmer
++ "zh-cn", // SimplifiedChinese
++ "zh-tw", // TraditionalChinese
++ "ja", // Japanese
++ "ko", // Korean
++ "vi", // Vietnamese
++ 0, // Symbol
++ 0, // Ogham
++ 0, // Runic
++ 0 // N'Ko
++};
++enum { LanguageCount = sizeof(languageForWritingSystem) / sizeof(const char *) };
++
++// Unfortunately FontConfig doesn't know about some languages. We have to test these through the
++// charset. The lists below contain the systems where we need to do this.
++static const ushort sampleCharForWritingSystem[] = {
++ 0, // Any
++ 0, // Latin
++ 0, // Greek
++ 0, // Cyrillic
++ 0, // Armenian
++ 0, // Hebrew
++ 0, // Arabic
++ 0, // Syriac
++ 0, // Thaana
++ 0, // Devanagari
++ 0, // Bengali
++ 0, // Gurmukhi
++ 0, // Gujarati
++ 0, // Oriya
++ 0, // Tamil
++ 0xc15, // Telugu
++ 0xc95, // Kannada
++ 0xd15, // Malayalam
++ 0xd9a, // Sinhala
++ 0, // Thai
++ 0, // Lao
++ 0, // Tibetan
++ 0x1000, // Myanmar
++ 0, // Georgian
++ 0, // Khmer
++ 0, // SimplifiedChinese
++ 0, // TraditionalChinese
++ 0, // Japanese
++ 0, // Korean
++ 0, // Vietnamese
++ 0, // Symbol
++ 0x1681, // Ogham
++ 0x16a0, // Runic
++ 0x7ca // N'Ko
++};
++enum { SampleCharCount = sizeof(sampleCharForWritingSystem) / sizeof(ushort) };
++
++// Newer FontConfig let's us sort out fonts that contain certain glyphs, but no
++// open type tables for is directly. Do this so we don't pick some strange
++// pseudo unicode font
++static const char *openType[] = {
++ 0, // Any
++ 0, // Latin
++ 0, // Greek
++ 0, // Cyrillic
++ 0, // Armenian
++ 0, // Hebrew
++ 0, // Arabic
++ "syrc", // Syriac
++ "thaa", // Thaana
++ "deva", // Devanagari
++ "beng", // Bengali
++ "guru", // Gurmukhi
++ "gurj", // Gujarati
++ "orya", // Oriya
++ "taml", // Tamil
++ "telu", // Telugu
++ "knda", // Kannada
++ "mlym", // Malayalam
++ "sinh", // Sinhala
++ 0, // Thai
++ 0, // Lao
++ "tibt", // Tibetan
++ "mymr", // Myanmar
++ 0, // Georgian
++ "khmr", // Khmer
++ 0, // SimplifiedChinese
++ 0, // TraditionalChinese
++ 0, // Japanese
++ 0, // Korean
++ 0, // Vietnamese
++ 0, // Symbol
++ 0, // Ogham
++ 0, // Runic
++ "nko " // N'Ko
++};
++
++static const char *getFcFamilyForStyleHint(const QFont::StyleHint style)
++{
++ const char *stylehint = 0;
++ switch (style) {
++ case QFont::SansSerif:
++ stylehint = "sans-serif";
++ break;
++ case QFont::Serif:
++ stylehint = "serif";
++ break;
++ case QFont::TypeWriter:
++ stylehint = "monospace";
++ break;
++ default:
++ break;
++ }
++ return stylehint;
++}
++
++void QFontconfigDatabase::populateFontDatabase()
++{
++ FcFontSet *fonts;
++
++ QString familyName;
++ FcChar8 *value = 0;
++ int weight_value;
++ int slant_value;
++ int spacing_value;
++ FcChar8 *file_value;
++ int indexValue;
++ FcChar8 *foundry_value;
++ FcBool scalable;
++ FcBool antialias;
++
++ {
++ FcObjectSet *os = FcObjectSetCreate();
++ FcPattern *pattern = FcPatternCreate();
++ const char *properties [] = {
++ FC_FAMILY, FC_WEIGHT, FC_SLANT,
++ FC_SPACING, FC_FILE, FC_INDEX,
++ FC_LANG, FC_CHARSET, FC_FOUNDRY, FC_SCALABLE, FC_PIXEL_SIZE, FC_WEIGHT,
++ FC_WIDTH,
++#if FC_VERSION >= 20297
++ FC_CAPABILITY,
++#endif
++ (const char *)0
++ };
++ const char **p = properties;
++ while (*p) {
++ FcObjectSetAdd(os, *p);
++ ++p;
++ }
++ fonts = FcFontList(0, pattern, os);
++ FcObjectSetDestroy(os);
++ FcPatternDestroy(pattern);
++ }
++
++ for (int i = 0; i < fonts->nfont; i++) {
++ if (FcPatternGetString(fonts->fonts[i], FC_FAMILY, 0, &value) != FcResultMatch)
++ continue;
++ // capitalize(value);
++ familyName = QString::fromUtf8((const char *)value);
++ slant_value = FC_SLANT_ROMAN;
++ weight_value = FC_WEIGHT_MEDIUM;
++ spacing_value = FC_PROPORTIONAL;
++ file_value = 0;
++ indexValue = 0;
++ scalable = FcTrue;
++
++
++ if (FcPatternGetInteger (fonts->fonts[i], FC_SLANT, 0, &slant_value) != FcResultMatch)
++ slant_value = FC_SLANT_ROMAN;
++ if (FcPatternGetInteger (fonts->fonts[i], FC_WEIGHT, 0, &weight_value) != FcResultMatch)
++ weight_value = FC_WEIGHT_MEDIUM;
++ if (FcPatternGetInteger (fonts->fonts[i], FC_SPACING, 0, &spacing_value) != FcResultMatch)
++ spacing_value = FC_PROPORTIONAL;
++ if (FcPatternGetString (fonts->fonts[i], FC_FILE, 0, &file_value) != FcResultMatch)
++ file_value = 0;
++ if (FcPatternGetInteger (fonts->fonts[i], FC_INDEX, 0, &indexValue) != FcResultMatch)
++ indexValue = 0;
++ if (FcPatternGetBool(fonts->fonts[i], FC_SCALABLE, 0, &scalable) != FcResultMatch)
++ scalable = FcTrue;
++ if (FcPatternGetString(fonts->fonts[i], FC_FOUNDRY, 0, &foundry_value) != FcResultMatch)
++ foundry_value = 0;
++ if(FcPatternGetBool(fonts->fonts[i],FC_ANTIALIAS,0,&antialias) != FcResultMatch)
++ antialias = true;
++
++ QSupportedWritingSystems writingSystems;
++ FcLangSet *langset = 0;
++ FcResult res = FcPatternGetLangSet(fonts->fonts[i], FC_LANG, 0, &langset);
++ if (res == FcResultMatch) {
++ for (int i = 1; i < LanguageCount; ++i) {
++ const FcChar8 *lang = (const FcChar8*) languageForWritingSystem[i];
++ if (lang) {
++ FcLangResult langRes = FcLangSetHasLang(langset, lang);
++ if (langRes != FcLangDifferentLang)
++ writingSystems.setSupported(QFontDatabase::WritingSystem(i));
++ }
++ }
++ } else {
++ // we set Other to supported for symbol fonts. It makes no
++ // sense to merge these with other ones, as they are
++ // special in a way.
++ writingSystems.setSupported(QFontDatabase::Other);
++ }
++
++ FcCharSet *cs = 0;
++ res = FcPatternGetCharSet(fonts->fonts[i], FC_CHARSET, 0, &cs);
++ if (res == FcResultMatch) {
++ // some languages are not supported by FontConfig, we rather check the
++ // charset to detect these
++ for (int i = 1; i < SampleCharCount; ++i) {
++ if (!sampleCharForWritingSystem[i])
++ continue;
++ if (FcCharSetHasChar(cs, sampleCharForWritingSystem[i]))
++ writingSystems.setSupported(QFontDatabase::WritingSystem(i));
++ }
++ }
++
++#if FC_VERSION >= 20297
++ for (int j = 1; j < LanguageCount; ++j) {
++ if (writingSystems.supported(QFontDatabase::WritingSystem(j))
++ && requiresOpenType(j) && openType[j]) {
++ FcChar8 *cap;
++ res = FcPatternGetString (fonts->fonts[i], FC_CAPABILITY, 0, &cap);
++ if (res != FcResultMatch || !strstr((const char *)cap, openType[j]))
++ writingSystems.setSupported(QFontDatabase::WritingSystem(j),false);
++ }
++ }
++#endif
++
++ FontFile *fontFile = new FontFile;
++ fontFile->fileName = QLatin1String((const char *)file_value);
++ fontFile->indexValue = indexValue;
++
++ QFont::Style style = (slant_value == FC_SLANT_ITALIC)
++ ? QFont::StyleItalic
++ : ((slant_value == FC_SLANT_OBLIQUE)
++ ? QFont::StyleOblique
++ : QFont::StyleNormal);
++ QFont::Weight weight = QFont::Weight(getFCWeight(weight_value));
++
++ double pixel_size = 0;
++ if (!scalable) {
++ int width = 100;
++ FcPatternGetInteger (fonts->fonts[i], FC_WIDTH, 0, &width);
++ FcPatternGetDouble (fonts->fonts[i], FC_PIXEL_SIZE, 0, &pixel_size);
++ }
++
++ QFont::Stretch stretch = QFont::Unstretched;
++ QPlatformFontDatabase::registerFont(familyName,QLatin1String((const char *)foundry_value),weight,style,stretch,antialias,scalable,pixel_size,writingSystems,fontFile);
++// qDebug() << familyName << (const char *)foundry_value << weight << style << &writingSystems << scalable << true << pixel_size;
++ }
++
++ FcFontSetDestroy (fonts);
++
++ struct FcDefaultFont {
++ const char *qtname;
++ const char *rawname;
++ bool fixed;
++ };
++ const FcDefaultFont defaults[] = {
++ { "Serif", "serif", false },
++ { "Sans Serif", "sans-serif", false },
++ { "Monospace", "monospace", true },
++ { 0, 0, false }
++ };
++ const FcDefaultFont *f = defaults;
++ // aliases only make sense for 'common', not for any of the specials
++ QSupportedWritingSystems ws;
++ ws.setSupported(QFontDatabase::Latin);
++
++
++ while (f->qtname) {
++ registerFont(f->qtname,QLatin1String(""),QFont::Normal,QFont::StyleNormal,QFont::Unstretched,true,true,0,ws,0);
++ registerFont(f->qtname,QLatin1String(""),QFont::Normal,QFont::StyleItalic,QFont::Unstretched,true,true,0,ws,0);
++ registerFont(f->qtname,QLatin1String(""),QFont::Normal,QFont::StyleOblique,QFont::Unstretched,true,true,0,ws,0);
++ ++f;
++ }
++
++ //Lighthouse has very lazy population of the font db. We want it to be initialized when
++ //QApplication is constructed, so that the population procedure can do something like this to
++ //set the default font
++// const FcDefaultFont *s = defaults;
++// QFont font("Sans Serif");
++// font.setPointSize(9);
++// QApplication::setFont(font);
++}
++
++QFontEngine *QFontconfigDatabase::fontEngine(const QFontDef &f, QUnicodeTables::Script script, void *usrPtr)
++{
++ if (!usrPtr)
++ return 0;
++ QFontDef fontDef = f;
++
++ QFontEngineFT *engine;
++ FontFile *fontfile = static_cast<FontFile *> (usrPtr);
++ QFontEngine::FaceId fid;
++ fid.filename = fontfile->fileName.toLocal8Bit();
++ fid.index = fontfile->indexValue;
++
++ //try and get the pattern
++ FcPattern *pattern = FcPatternCreate();
++
++ bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias);
++ QFontEngineFT::GlyphFormat format = antialias? QFontEngineFT::Format_A8 : QFontEngineFT::Format_Mono;
++
++ engine = new QFontEngineFT(fontDef);
++
++ FcValue value;
++ value.type = FcTypeString;
++ QByteArray cs = fontDef.family.toUtf8();
++ value.u.s = (const FcChar8 *)cs.data();
++ FcPatternAdd(pattern,FC_FAMILY,value,true);
++
++
++ value.u.s = (const FcChar8 *)fid.filename.data();
++ FcPatternAdd(pattern,FC_FILE,value,true);
++
++ value.type = FcTypeInteger;
++ value.u.i = fid.index;
++ FcPatternAdd(pattern,FC_INDEX,value,true);
++
++ QFontEngineFT::HintStyle default_hint_style;
++
++ if (FcConfigSubstitute(0,pattern,FcMatchPattern)) {
++
++ //hinting
++ int hint_style = 0;
++ if (FcPatternGetInteger (pattern, FC_HINT_STYLE, 0, &hint_style) == FcResultNoMatch)
++ hint_style = QFontEngineFT::HintFull;
++ switch (hint_style) {
++ case FC_HINT_NONE:
++ default_hint_style = QFontEngineFT::HintNone;
++ break;
++ case FC_HINT_SLIGHT:
++ default_hint_style = QFontEngineFT::HintLight;
++ break;
++ case FC_HINT_MEDIUM:
++ default_hint_style = QFontEngineFT::HintMedium;
++ break;
++ default:
++ default_hint_style = QFontEngineFT::HintFull;
++ break;
++ }
++ }
++
++ engine->setDefaultHintStyle(default_hint_style);
++ if (!engine->init(fid,antialias,format)) {
++ delete engine;
++ engine = 0;
++ return engine;
++ }
++ if (engine->invalid()) {
++ delete engine;
++ engine = 0;
++ } else if (scriptRequiresOpenType(script)) {
++ HB_Face hbFace = engine->harfbuzzFace();
++ if (!hbFace || !hbFace->supported_scripts[script]) {
++ delete engine;
++ engine = 0;
++ }
++ }
++
++ return engine;
++}
++
++QStringList QFontconfigDatabase::fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const
++{
++ QStringList fallbackFamilies;
++ FcPattern *pattern = FcPatternCreate();
++ if (!pattern)
++ return fallbackFamilies;
++
++ FcValue value;
++ value.type = FcTypeString;
++ QByteArray cs = family.toUtf8();
++ value.u.s = (const FcChar8 *)cs.data();
++ FcPatternAdd(pattern,FC_FAMILY,value,true);
++
++ int slant_value = FC_SLANT_ROMAN;
++ if (style == QFont::StyleItalic)
++ slant_value = FC_SLANT_ITALIC;
++ else if (style == QFont::StyleOblique)
++ slant_value = FC_SLANT_OBLIQUE;
++ FcPatternAddInteger(pattern, FC_SLANT, slant_value);
++
++ if (script != QUnicodeTables::Common && *specialLanguages[script] != '\0') {
++ Q_ASSERT(script < QUnicodeTables::ScriptCount);
++ FcLangSet *ls = FcLangSetCreate();
++ FcLangSetAdd(ls, (const FcChar8*)specialLanguages[script]);
++ FcPatternAddLangSet(pattern, FC_LANG, ls);
++ FcLangSetDestroy(ls);
++ }
++
++ const char *stylehint = getFcFamilyForStyleHint(styleHint);
++ if (stylehint) {
++ value.u.s = (const FcChar8 *)stylehint;
++ FcPatternAddWeak(pattern, FC_FAMILY, value, FcTrue);
++ }
++
++ FcConfigSubstitute(0, pattern, FcMatchPattern);
++ FcConfigSubstitute(0, pattern, FcMatchFont);
++
++ FcResult result = FcResultMatch;
++ FcFontSet *fontSet = FcFontSort(0,pattern,FcFalse,0,&result);
++
++ if (fontSet && result == FcResultMatch)
++ {
++ for (int i = 0; i < fontSet->nfont; i++) {
++ FcChar8 *value = 0;
++ if (FcPatternGetString(fontSet->fonts[i], FC_FAMILY, 0, &value) != FcResultMatch)
++ continue;
++ // capitalize(value);
++ QString familyName = QString::fromUtf8((const char *)value);
++ if (!fallbackFamilies.contains(familyName,Qt::CaseInsensitive)) {
++ fallbackFamilies << familyName;
++ }
++
++ }
++ }
++// qDebug() << "fallbackFamilies for:" << family << fallbackFamilies;
++
++ return fallbackFamilies;
++}
+diff --git a/src/plugins/platforms/minimal/qfontconfigdatabase.h b/src/plugins/platforms/minimal/qfontconfigdatabase.h
+new file mode 100644
+index 0000000..61700e3
+--- /dev/null
++++ b/src/plugins/platforms/minimal/qfontconfigdatabase.h
+@@ -0,0 +1,56 @@
++/****************************************************************************
++**
++** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
++** All rights reserved.
++** Contact: Nokia Corporation (qt-info@nokia.com)
++**
++** This file is part of the plugins of the Qt Toolkit.
++**
++** $QT_BEGIN_LICENSE:LGPL$
++** GNU Lesser General Public License Usage
++** This file may be used under the terms of the GNU Lesser General Public
++** License version 2.1 as published by the Free Software Foundation and
++** appearing in the file LICENSE.LGPL included in the packaging of this
++** file. Please review the following information to ensure the GNU Lesser
++** General Public License version 2.1 requirements will be met:
++** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
++**
++** In addition, as a special exception, Nokia gives you certain additional
++** rights. These rights are described in the Nokia Qt LGPL Exception
++** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
++**
++** GNU General Public License Usage
++** Alternatively, this file may be used under the terms of the GNU General
++** Public License version 3.0 as published by the Free Software Foundation
++** and appearing in the file LICENSE.GPL included in the packaging of this
++** file. Please review the following information to ensure the GNU General
++** Public License version 3.0 requirements will be met:
++** http://www.gnu.org/copyleft/gpl.html.
++**
++** Other Usage
++** Alternatively, this file may be used in accordance with the terms and
++** conditions contained in a signed written agreement between you and Nokia.
++**
++**
++**
++**
++**
++** $QT_END_LICENSE$
++**
++****************************************************************************/
++
++#ifndef QFONTCONFIGDATABASE_H
++#define QFONTCONFIGDATABASE_H
++
++#include <QPlatformFontDatabase>
++#include "qbasicunixfontdatabase.h"
++
++class QFontconfigDatabase : public QBasicUnixFontDatabase
++{
++public:
++ void populateFontDatabase();
++ QFontEngine *fontEngine(const QFontDef &fontDef, QUnicodeTables::Script script, void *handle);
++ QStringList fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const;
++};
++
++#endif // QFONTCONFIGDATABASE_H
+diff --git a/src/plugins/platforms/minimal/qminimalintegration.cpp b/src/plugins/platforms/minimal/qminimalintegration.cpp
+index b9ab528..b6028ff 100644
+--- a/src/plugins/platforms/minimal/qminimalintegration.cpp
++++ b/src/plugins/platforms/minimal/qminimalintegration.cpp
+@@ -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) {
+diff --git a/src/plugins/platforms/minimal/qminimalintegration.h b/src/plugins/platforms/minimal/qminimalintegration.h
+index d1fcc42..13dd8a4 100644
+--- a/src/plugins/platforms/minimal/qminimalintegration.h
++++ b/src/plugins/platforms/minimal/qminimalintegration.h
+@@ -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
+diff --git a/src/plugins/platforms/minimal/qminimalwindowsurface.cpp b/src/plugins/platforms/minimal/qminimalwindowsurface.cpp
+index 91c68d1..c24fbaf 100644
+--- a/src/plugins/platforms/minimal/qminimalwindowsurface.cpp
++++ b/src/plugins/platforms/minimal/qminimalwindowsurface.cpp
+@@ -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)
+diff --git a/src/svg/qsvggenerator.cpp b/src/svg/qsvggenerator.cpp
+index 746e2b3..ba23360 100644
+--- a/src/svg/qsvggenerator.cpp
++++ b/src/svg/qsvggenerator.cpp
+@@ -103,6 +103,7 @@ public:
+
+ afterFirstUpdate = false;
+ numGradients = 0;
++ clip = false;
+ }
+
+ QSize size;
+@@ -129,6 +130,9 @@ public:
+
+ QString currentGradientName;
+ int numGradients;
++ QString stateString;
++ QString oldStateString;
++ bool clip;
+
+ struct _attributes {
+ QString document_title;
+@@ -141,6 +145,18 @@ public:
+ QString dashPattern, dashOffset;
+ QString fill, fillOpacity;
+ } attributes;
++
++ void emitState() {
++ if (stateString == oldStateString) return;
++
++ // close old state and start a new one...
++ if (afterFirstUpdate)
++ *stream << "</g>\n\n";
++
++ *stream << stateString;
++ afterFirstUpdate = true;
++ oldStateString = stateString;
++ }
+ };
+
+ static inline QPaintEngine::PaintEngineFeatures svgEngineFeatures()
+@@ -322,7 +338,7 @@ public:
+ }
+
+
+- void qpenToSvg(const QPen &spen)
++ void qpenToSvg(const QPen &spen, QTextStream & s)
+ {
+ QString width;
+
+@@ -330,7 +346,7 @@ public:
+
+ switch (spen.style()) {
+ case Qt::NoPen:
+- stream() << QLatin1String("stroke=\"none\" ");
++ s << QLatin1String("stroke=\"none\" ");
+
+ d_func()->attributes.stroke = QLatin1String("none");
+ d_func()->attributes.strokeOpacity = QString();
+@@ -344,8 +360,8 @@ public:
+ d_func()->attributes.stroke = color;
+ d_func()->attributes.strokeOpacity = colorOpacity;
+
+- stream() << QLatin1String("stroke=\"")<<color<< QLatin1String("\" ");
+- stream() << QLatin1String("stroke-opacity=\"")<<colorOpacity<< QLatin1String("\" ");
++ s << QLatin1String("stroke=\"")<<color<< QLatin1String("\" ");
++ s << QLatin1String("stroke-opacity=\"")<<colorOpacity<< QLatin1String("\" ");
+ }
+ break;
+ case Qt::DashLine:
+@@ -368,10 +384,10 @@ public:
+ d_func()->attributes.dashPattern = dashPattern;
+ d_func()->attributes.dashOffset = dashOffset;
+
+- stream() << QLatin1String("stroke=\"")<<color<< QLatin1String("\" ");
+- stream() << QLatin1String("stroke-opacity=\"")<<colorOpacity<< QLatin1String("\" ");
+- stream() << QLatin1String("stroke-dasharray=\"")<<dashPattern<< QLatin1String("\" ");
+- stream() << QLatin1String("stroke-dashoffset=\"")<<dashOffset<< QLatin1String("\" ");
++ s << QLatin1String("stroke=\"")<<color<< QLatin1String("\" ");
++ s << QLatin1String("stroke-opacity=\"")<<colorOpacity<< QLatin1String("\" ");
++ s << QLatin1String("stroke-dasharray=\"")<<dashPattern<< QLatin1String("\" ");
++ s << QLatin1String("stroke-dashoffset=\"")<<dashOffset<< QLatin1String("\" ");
+ break;
+ }
+ default:
+@@ -380,50 +396,50 @@ public:
+ }
+
+ if (spen.widthF() == 0)
+- stream() <<"stroke-width=\"1\" ";
++ s <<"stroke-width=\"1\" ";
+ else
+- stream() <<"stroke-width=\"" << spen.widthF() << "\" ";
++ s <<"stroke-width=\"" << spen.widthF() << "\" ";
+
+ switch (spen.capStyle()) {
+ case Qt::FlatCap:
+- stream() << "stroke-linecap=\"butt\" ";
++ s << "stroke-linecap=\"butt\" ";
+ break;
+ case Qt::SquareCap:
+- stream() << "stroke-linecap=\"square\" ";
++ s << "stroke-linecap=\"square\" ";
+ break;
+ case Qt::RoundCap:
+- stream() << "stroke-linecap=\"round\" ";
++ s << "stroke-linecap=\"round\" ";
+ break;
+ default:
+ qWarning("Unhandled cap style");
+ }
+ switch (spen.joinStyle()) {
+ case Qt::MiterJoin:
+- stream() << "stroke-linejoin=\"miter\" "
++ s << "stroke-linejoin=\"miter\" "
+ "stroke-miterlimit=\""<<spen.miterLimit()<<"\" ";
+ break;
+ case Qt::BevelJoin:
+- stream() << "stroke-linejoin=\"bevel\" ";
++ s << "stroke-linejoin=\"bevel\" ";
+ break;
+ case Qt::RoundJoin:
+- stream() << "stroke-linejoin=\"round\" ";
++ s << "stroke-linejoin=\"round\" ";
+ break;
+ case Qt::SvgMiterJoin:
+- stream() << "stroke-linejoin=\"miter\" "
++ s << "stroke-linejoin=\"miter\" "
+ "stroke-miterlimit=\""<<spen.miterLimit()<<"\" ";
+ break;
+ default:
+ qWarning("Unhandled join style");
+ }
+ }
+- void qbrushToSvg(const QBrush &sbrush)
++ void qbrushToSvg(const QBrush &sbrush, QTextStream & s)
+ {
+ d_func()->brush = sbrush;
+ switch (sbrush.style()) {
+ case Qt::SolidPattern: {
+ QString color, colorOpacity;
+ translate_color(sbrush.color(), &color, &colorOpacity);
+- stream() << "fill=\"" << color << "\" "
++ s << "fill=\"" << color << "\" "
+ "fill-opacity=\""
+ << colorOpacity << "\" ";
+ d_func()->attributes.fill = color;
+@@ -434,22 +450,22 @@ public:
+ saveLinearGradientBrush(sbrush.gradient());
+ d_func()->attributes.fill = QString::fromLatin1("url(#%1)").arg(d_func()->currentGradientName);
+ d_func()->attributes.fillOpacity = QString();
+- stream() << QLatin1String("fill=\"url(#") << d_func()->currentGradientName << QLatin1String(")\" ");
++ s << QLatin1String("fill=\"url(#") << d_func()->currentGradientName << QLatin1String(")\" ");
+ break;
+ case Qt::RadialGradientPattern:
+ saveRadialGradientBrush(sbrush.gradient());
+ d_func()->attributes.fill = QString::fromLatin1("url(#%1)").arg(d_func()->currentGradientName);
+ d_func()->attributes.fillOpacity = QString();
+- stream() << QLatin1String("fill=\"url(#") << d_func()->currentGradientName << QLatin1String(")\" ");
++ s << QLatin1String("fill=\"url(#") << d_func()->currentGradientName << QLatin1String(")\" ");
+ break;
+ case Qt::ConicalGradientPattern:
+ saveConicalGradientBrush(sbrush.gradient());
+ d_func()->attributes.fill = QString::fromLatin1("url(#%1)").arg(d_func()->currentGradientName);
+ d_func()->attributes.fillOpacity = QString();
+- stream() << QLatin1String("fill=\"url(#") << d_func()->currentGradientName << QLatin1String(")\" ");
++ s << QLatin1String("fill=\"url(#") << d_func()->currentGradientName << QLatin1String(")\" ");
+ break;
+ case Qt::NoBrush:
+- stream() << QLatin1String("fill=\"none\" ");
++ s << QLatin1String("fill=\"none\" ");
+ d_func()->attributes.fill = QLatin1String("none");
+ d_func()->attributes.fillOpacity = QString();
+ return;
+@@ -458,7 +474,7 @@ public:
+ break;
+ }
+ }
+- void qfontToSvg(const QFont &sfont)
++ void qfontToSvg(const QFont &sfont, QTextStream & s)
+ {
+ Q_D(QSvgPaintEngine);
+
+@@ -488,12 +504,23 @@ public:
+ d->attributes.font_family = d->font.family();
+ d->attributes.font_style = d->font.italic() ? QLatin1String("italic") : QLatin1String("normal");
+
+- *d->stream << "font-family=\"" << d->attributes.font_family << "\" "
+- "font-size=\"" << d->attributes.font_size << "\" "
+- "font-weight=\"" << d->attributes.font_weight << "\" "
+- "font-style=\"" << d->attributes.font_style << "\" "
+- << endl;
++ s << "font-family=\"" << d->attributes.font_family << "\" "
++ "font-size=\"" << d->attributes.font_size << "\" "
++ "font-weight=\"" << d->attributes.font_weight << "\" "
++ "font-style=\"" << d->attributes.font_style << "\" "
++ << endl;
++ }
++
++ void setViewBoxClip(bool clip) {
++ Q_D(QSvgPaintEngine);
++ d->clip = clip;
+ }
++
++ bool viewBoxClip() const {
++ Q_D(const QSvgPaintEngine);
++ return d->clip;
++ }
++
+ };
+
+ class QSvgGeneratorPrivate
+@@ -808,6 +835,27 @@ int QSvgGenerator::metric(QPaintDevice::PaintDeviceMetric metric) const
+ return 0;
+ }
+
++/*!
++ \property QSvgGenerator::resolution
++ \brief do not draw objects outside the viewBox
++ \since 4.7
++
++ When specified objects drawn compleatly outsite the viewBox
++ are not include in the output SVG.
++
++ \sa viewBox
++*/
++
++bool QSvgGenerator::viewBoxClip() const {
++ Q_D(const QSvgGenerator);
++ return d->engine->viewBoxClip();
++}
++
++void QSvgGenerator::setViewBoxClip(bool clip) {
++ Q_D(QSvgGenerator);
++ d->engine->setViewBoxClip(clip);
++}
++
+ /*****************************************************************************
+ * class QSvgPaintEngine
+ */
+@@ -908,10 +956,13 @@ void QSvgPaintEngine::drawImage(const QRectF &r, const QImage &image,
+ const QRectF &sr,
+ Qt::ImageConversionFlag flags)
+ {
+- //Q_D(QSvgPaintEngine);
++ Q_D(QSvgPaintEngine);
+
+ Q_UNUSED(sr);