From 09ad4d74cea40dd3d88c0a39b1dcbc8db5347c6e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 15 Dec 2014 17:40:41 +0100 Subject: Provide async loading through QNetworkReply --- src/pdf/pdf.pro | 3 +- src/pdf/qpdfdocument.cpp | 168 ++++++++++++++++++++++++++++++++++++++++++----- src/pdf/qpdfdocument.h | 17 ++++- src/pdf/qpdfdocument_p.h | 23 ++++++- 4 files changed, 189 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/pdf/pdf.pro b/src/pdf/pdf.pro index 4c3244c87..811a9468d 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 d6aed7451..3eeef297f 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(pThis); + return offset + size <= static_cast(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 b33f0fe29..df6e69fd9 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 d; }; diff --git a/src/pdf/qpdfdocument_p.h b/src/pdf/qpdfdocument_p.h index 7f9b10220..ec9e2e3ae 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 +#include +#include -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 device; QScopedPointer ownDevice; + QBuffer asyncBuffer; + QPointer 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 -- cgit v1.2.3