summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@theqtcompany.com>2014-12-15 17:40:41 +0100
committerSimon Hausmann <simon.hausmann@theqtcompany.com>2014-12-15 20:32:26 +0100
commitaa54c8d2286c8732a049af7cffbc1ec6e1f7f6e7 (patch)
treeb3616d388ae170467f837536339ff7ffb27ac9e0
parent06a055a3eaa7cc60cc453615d6dc03a106198551 (diff)
Provide async loading through QNetworkReply
-rw-r--r--src/pdf/pdf.pro3
-rw-r--r--src/pdf/qpdfdocument.cpp168
-rw-r--r--src/pdf/qpdfdocument.h17
-rw-r--r--src/pdf/qpdfdocument_p.h23
-rw-r--r--tests/auto/qpdfdocument/qpdfdocument.pro2
-rw-r--r--tests/auto/qpdfdocument/tst_qpdfdocument.cpp44
6 files changed, 225 insertions, 32 deletions
diff --git a/src/pdf/pdf.pro b/src/pdf/pdf.pro
index 4c3244c..811a946 100644
--- a/src/pdf/pdf.pro
+++ b/src/pdf/pdf.pro
@@ -1,5 +1,6 @@
TARGET = QtQPdf
-QT = gui core network
+QT += gui core
+QT_PRIVATE += network
TEMPLATE = lib
CONFIG += c++11
INCLUDEPATH += ../3rdparty/pdfium/fpdfsdk/include
diff --git a/src/pdf/qpdfdocument.cpp b/src/pdf/qpdfdocument.cpp
index d6aed74..3eeef29 100644
--- a/src/pdf/qpdfdocument.cpp
+++ b/src/pdf/qpdfdocument.cpp
@@ -11,7 +11,9 @@ Q_GLOBAL_STATIC_WITH_ARGS(QMutex, pdfMutex, (QMutex::Recursive));
static int libraryRefCount;
QPdfDocumentPrivate::QPdfDocumentPrivate()
- : doc(0)
+ : avail(0)
+ , doc(0)
+ , lastError(QPdfDocument::NoError)
{
QMutexLocker lock(pdfMutex());
if (libraryRefCount == 0)
@@ -21,25 +23,58 @@ QPdfDocumentPrivate::QPdfDocumentPrivate()
// FPDF_FILEACCESS setup
m_Param = this;
m_GetBlock = fpdf_GetBlock;
+
+ // FX_FILEAVAIL setup
+ FX_FILEAVAIL::version = 1;
+ IsDataAvail = fpdf_IsDataAvail;
+
+ // FX_DOWNLOADHINTS setup
+ FX_DOWNLOADHINTS::version = 1;
+ AddSegment = fpdf_AddSegment;
}
QPdfDocumentPrivate::~QPdfDocumentPrivate()
{
QMutexLocker lock(pdfMutex());
- if (doc)
- FPDF_CloseDocument(doc);
- doc = 0;
+
+ clear();
if (!--libraryRefCount)
FPDF_DestroyLibrary();
}
-QPdfDocument::Error QPdfDocumentPrivate::load(QIODevice *newDevice, bool transferDeviceOwnership, const QString &documentPassword)
+void QPdfDocumentPrivate::clear()
{
- QMutexLocker lock(pdfMutex());
-
if (doc)
FPDF_CloseDocument(doc);
+ doc = 0;
+
+ if (avail)
+ FPDFAvail_Destroy(avail);
+ avail = 0;
+
+ asyncBuffer.close();
+ asyncBuffer.setData(QByteArray());
+ asyncBuffer.open(QIODevice::ReadWrite);
+}
+
+void QPdfDocumentPrivate::setErrorCode()
+{
+ switch (FPDF_GetLastError()) {
+ case FPDF_ERR_SUCCESS: lastError = QPdfDocument::NoError; break;
+ case FPDF_ERR_UNKNOWN: lastError = QPdfDocument::UnknownError; break;
+ case FPDF_ERR_FILE: lastError = QPdfDocument::FileNotFoundError; break;
+ case FPDF_ERR_FORMAT: lastError = QPdfDocument::InvalidFileFormatError; break;
+ case FPDF_ERR_PASSWORD: lastError = QPdfDocument::IncorrectPasswordError; break;
+ case FPDF_ERR_SECURITY: lastError = QPdfDocument::UnsupportedSecuritySchemeError; break;
+ default:
+ Q_UNREACHABLE();
+ }
+}
+
+QPdfDocument::Error QPdfDocumentPrivate::load(QIODevice *newDevice, bool transferDeviceOwnership, const QString &documentPassword)
+{
+ clear();
if (transferDeviceOwnership)
ownDevice.reset(newDevice);
@@ -56,17 +91,66 @@ QPdfDocument::Error QPdfDocumentPrivate::load(QIODevice *newDevice, bool transfe
password = documentPassword.toUtf8();
doc = FPDF_LoadCustomDocument(this, password.constData());
- switch (FPDF_GetLastError()) {
- case FPDF_ERR_SUCCESS: return QPdfDocument::NoError;
- case FPDF_ERR_UNKNOWN: return QPdfDocument::UnknownError;
- case FPDF_ERR_FILE: return QPdfDocument::FileNotFoundError;
- case FPDF_ERR_FORMAT: return QPdfDocument::InvalidFileFormatError;
- case FPDF_ERR_PASSWORD: return QPdfDocument::IncorrectPasswordError;
- case FPDF_ERR_SECURITY: return QPdfDocument::UnsupportedSecuritySchemeError;
- default:
- Q_UNREACHABLE();
+ setErrorCode();
+ return lastError;
+}
+
+void QPdfDocumentPrivate::_q_initiateAsyncLoad()
+{
+ QMutexLocker lock(pdfMutex());
+ if (avail)
+ return;
+
+ QVariant contentLength = remoteDevice->header(QNetworkRequest::ContentLengthHeader);
+ if (!contentLength.isValid())
+ return;
+
+ // FPDF_FILEACCESS setup
+ m_FileLen = contentLength.toULongLong();
+
+ QObject::connect(remoteDevice, SIGNAL(readyRead()), q, SLOT(_q_readFromDevice()));
+
+ avail = FPDFAvail_Create(this, this);
+
+ if (remoteDevice->bytesAvailable())
+ _q_readFromDevice();
+}
+
+void QPdfDocumentPrivate::_q_readFromDevice()
+{
+ QMutexLocker lock(pdfMutex());
+ QByteArray data = remoteDevice->read(remoteDevice->bytesAvailable());
+ if (data.isEmpty())
+ return;
+ asyncBuffer.seek(asyncBuffer.size());
+ asyncBuffer.write(data);
+
+ if (!doc) {
+ tryLoadDocument();
+ }
+}
+
+void QPdfDocumentPrivate::tryLoadDocument()
+{
+ if (!FPDFAvail_IsDocAvail(avail, this))
+ return;
+
+ Q_ASSERT(!doc);
+
+ doc = FPDFAvail_GetDocument(avail, password);
+ if (!doc) {
+ setErrorCode();
+ if (lastError == QPdfDocument::IncorrectPasswordError)
+ emit q->passwordRequired();
}
- return QPdfDocument::UnknownError;
+ if (doc)
+ emit q->documentReady();
+}
+
+bool QPdfDocumentPrivate::fpdf_IsDataAvail(_FX_FILEAVAIL *pThis, size_t offset, size_t size)
+{
+ QPdfDocumentPrivate *d = static_cast<QPdfDocumentPrivate*>(pThis);
+ return offset + size <= static_cast<quint64>(d->asyncBuffer.size());
}
int QPdfDocumentPrivate::fpdf_GetBlock(void *param, unsigned long position, unsigned char *pBuf, unsigned long size)
@@ -77,10 +161,18 @@ int QPdfDocumentPrivate::fpdf_GetBlock(void *param, unsigned long position, unsi
}
+void QPdfDocumentPrivate::fpdf_AddSegment(_FX_DOWNLOADHINTS *pThis, size_t offset, size_t size)
+{
+ Q_UNUSED(pThis);
+ Q_UNUSED(offset);
+ Q_UNUSED(size);
+}
+
QPdfDocument::QPdfDocument(QObject *parent)
: QObject(parent)
, d(new QPdfDocumentPrivate)
{
+ d->q = this;
}
QPdfDocument::~QPdfDocument()
@@ -89,14 +181,54 @@ QPdfDocument::~QPdfDocument()
QPdfDocument::Error QPdfDocument::load(const QString &fileName, const QString &password)
{
+ QMutexLocker lock(pdfMutex());
return d->load(new QFile(fileName), /*transfer ownership*/true, password);
}
QPdfDocument::Error QPdfDocument::load(QIODevice *device, const QString &password)
{
+ QMutexLocker lock(pdfMutex());
return d->load(device, /*transfer ownership*/false, password);
}
+void QPdfDocument::loadAsynchronously(QNetworkReply *device)
+{
+ QMutexLocker lock(pdfMutex());
+ d->clear();
+
+ d->ownDevice.reset();
+ d->device = &d->asyncBuffer;
+
+ if (d->remoteDevice)
+ d->remoteDevice->disconnect(this);
+
+ d->remoteDevice = device;
+
+ if (d->remoteDevice->header(QNetworkRequest::ContentLengthHeader).isValid())
+ d->_q_initiateAsyncLoad();
+ else
+ connect(d->remoteDevice, SIGNAL(metaDataChanged()), this, SLOT(_q_initiateAsyncLoad()));
+}
+
+void QPdfDocument::setPassword(const QString &password)
+{
+ QMutexLocker lock(pdfMutex());
+ d->password = password.toUtf8();
+
+ if (!d->doc && d->avail)
+ d->tryLoadDocument();
+}
+
+QString QPdfDocument::password() const
+{
+ return QString::fromUtf8(d->password);
+}
+
+QPdfDocument::Error QPdfDocument::error() const
+{
+ return d->lastError;
+}
+
int QPdfDocument::pageCount() const
{
if (!d->doc)
@@ -135,3 +267,5 @@ QImage QPdfDocument::render(int page, const QSizeF &pageSize)
FPDFBitmap_Destroy(bitmap);
return result;
}
+
+#include "moc_qpdfdocument.cpp"
diff --git a/src/pdf/qpdfdocument.h b/src/pdf/qpdfdocument.h
index b33f0fe..df6e69f 100644
--- a/src/pdf/qpdfdocument.h
+++ b/src/pdf/qpdfdocument.h
@@ -6,11 +6,13 @@
#include "qtpdfglobal.h"
class QPdfDocumentPrivate;
+class QNetworkReply;
class Q_PDF_EXPORT QPdfDocument : public QObject
{
Q_OBJECT
- Q_PROPERTY(int pageCount READ pageCount FINAL)
+ Q_PROPERTY(int pageCount READ pageCount NOTIFY pageCountChanged FINAL)
+ Q_PROPERTY(QString password READ password WRITE setPassword FINAL)
public:
enum Error {
@@ -28,13 +30,26 @@ public:
Error load(const QString &fileName, const QString &password = QString());
Error load(QIODevice *device, const QString &password = QString());
+ void loadAsynchronously(QNetworkReply *device);
+ void setPassword(const QString &password);
+ QString password() const;
+
+ Error error() const;
+
int pageCount() const;
QSizeF pageSize(int page) const;
QImage render(int page, const QSizeF &pageSize);
+Q_SIGNALS:
+ void passwordRequired();
+ void documentReady();
+ void pageCountChanged();
+
private:
+ Q_PRIVATE_SLOT(d, void _q_initiateAsyncLoad())
+ Q_PRIVATE_SLOT(d, void _q_readFromDevice())
QScopedPointer<QPdfDocumentPrivate> d;
};
diff --git a/src/pdf/qpdfdocument_p.h b/src/pdf/qpdfdocument_p.h
index 7f9b102..ec9e2e3 100644
--- a/src/pdf/qpdfdocument_p.h
+++ b/src/pdf/qpdfdocument_p.h
@@ -5,24 +5,41 @@
#include "fpdf_dataavail.h"
#include "qpdfdocument.h"
-#include <qiodevice.h>
+#include <qbuffer.h>
+#include <qnetworkreply.h>
-class QPdfDocumentPrivate: public FPDF_FILEACCESS
+class QPdfDocumentPrivate: public FPDF_FILEACCESS, public FX_FILEAVAIL, public FX_DOWNLOADHINTS
{
public:
QPdfDocumentPrivate();
~QPdfDocumentPrivate();
+ QPdfDocument *q;
+
+ FPDF_AVAIL avail;
FPDF_DOCUMENT doc;
- QIODevice *device;
+ QPointer<QIODevice> device;
QScopedPointer<QIODevice> ownDevice;
+ QBuffer asyncBuffer;
+ QPointer<QNetworkReply> remoteDevice;
QByteArray password;
+ QPdfDocument::Error lastError;
+
+ void clear();
+
QPdfDocument::Error load(QIODevice *device, bool ownDevice, const QString &documentPassword);
+ void loadAsync(QIODevice *device);
+
+ void _q_initiateAsyncLoad();
+ void _q_readFromDevice();
+ void tryLoadDocument();
static bool fpdf_IsDataAvail(struct _FX_FILEAVAIL* pThis, size_t offset, size_t size);
static int fpdf_GetBlock(void* param, unsigned long position, unsigned char* pBuf, unsigned long size);
+ static void fpdf_AddSegment(struct _FX_DOWNLOADHINTS* pThis, size_t offset, size_t size);
+ void setErrorCode();
};
#endif // QPDFDOCUMENT_P_H
diff --git a/tests/auto/qpdfdocument/qpdfdocument.pro b/tests/auto/qpdfdocument/qpdfdocument.pro
index 28e56e0..8382a25 100644
--- a/tests/auto/qpdfdocument/qpdfdocument.pro
+++ b/tests/auto/qpdfdocument/qpdfdocument.pro
@@ -1,6 +1,6 @@
CONFIG += testcase
TARGET = tst_qpdfdocument
-QT += pdf printsupport testlib
+QT += pdf printsupport testlib network
macx:CONFIG -= app_bundle
SOURCES += tst_qpdfdocument.cpp
diff --git a/tests/auto/qpdfdocument/tst_qpdfdocument.cpp b/tests/auto/qpdfdocument/tst_qpdfdocument.cpp
index a51c172..c0fe93c 100644
--- a/tests/auto/qpdfdocument/tst_qpdfdocument.cpp
+++ b/tests/auto/qpdfdocument/tst_qpdfdocument.cpp
@@ -5,6 +5,9 @@
#include <QPdfDocument>
#include <QPrinter>
#include <QTemporaryFile>
+#include <QNetworkAccessManager>
+#include <QNetworkRequest>
+#include <QNetworkReply>
class tst_QPdfDocument: public QObject
{
@@ -14,6 +17,7 @@ public:
private slots:
void pageCount();
void loadFromIODevice();
+ void loadAsync();
};
struct TemporaryPdf: public QTemporaryFile
@@ -28,17 +32,21 @@ TemporaryPdf::TemporaryPdf()
open();
pageLayout = QPageLayout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF());
- QPrinter printer;
- printer.setOutputFormat(QPrinter::PdfFormat);
- printer.setOutputFileName(fileName());
- printer.setPageLayout(pageLayout);
-
{
- QPainter painter(&printer);
- painter.drawText(100, 100, QStringLiteral("Hello Page 1"));
- printer.newPage();
- painter.drawText(100, 100, QStringLiteral("Hello Page 2"));
+ QPrinter printer;
+ printer.setOutputFormat(QPrinter::PdfFormat);
+ printer.setOutputFileName(fileName());
+ printer.setPageLayout(pageLayout);
+
+ {
+ QPainter painter(&printer);
+ painter.drawText(100, 100, QStringLiteral("Hello Page 1"));
+ printer.newPage();
+ painter.drawText(100, 100, QStringLiteral("Hello Page 2"));
+ }
}
+
+ seek(0);
}
void tst_QPdfDocument::pageCount()
@@ -61,6 +69,24 @@ void tst_QPdfDocument::loadFromIODevice()
QCOMPARE(doc.pageCount(), 2);
}
+void tst_QPdfDocument::loadAsync()
+{
+ TemporaryPdf tempPdf;
+
+ QNetworkAccessManager nam;
+
+ QUrl url = QUrl::fromLocalFile(tempPdf.fileName());
+ QScopedPointer<QNetworkReply> reply(nam.get(QNetworkRequest(url)));
+
+ QPdfDocument doc;
+ QSignalSpy readySpy(&doc, SIGNAL(documentReady()));
+
+ doc.loadAsynchronously(reply.data());
+
+ QCOMPARE(readySpy.count(), 1);
+ QCOMPARE(doc.pageCount(), 2);
+}
+
QTEST_MAIN(tst_QPdfDocument)
#include "tst_qpdfdocument.moc"