Skip to content

Commit

Permalink
Provide async loading through QNetworkReply
Browse files Browse the repository at this point in the history
  • Loading branch information
Simon Hausmann committed Dec 15, 2014
1 parent 06a055a commit aa54c8d
Show file tree
Hide file tree
Showing 6 changed files with 225 additions and 32 deletions.
3 changes: 2 additions & 1 deletion src/pdf/pdf.pro
Original file line number Diff line number Diff line change
@@ -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
Expand Down
168 changes: 151 additions & 17 deletions src/pdf/qpdfdocument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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);
Expand All @@ -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)
Expand All @@ -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()
Expand All @@ -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)
Expand Down Expand Up @@ -135,3 +267,5 @@ QImage QPdfDocument::render(int page, const QSizeF &pageSize)
FPDFBitmap_Destroy(bitmap);
return result;
}

#include "moc_qpdfdocument.cpp"
17 changes: 16 additions & 1 deletion src/pdf/qpdfdocument.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -28,13 +30,26 @@ class Q_PDF_EXPORT QPdfDocument : public QObject
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;
};

Expand Down
23 changes: 20 additions & 3 deletions src/pdf/qpdfdocument_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion tests/auto/qpdfdocument/qpdfdocument.pro
Original file line number Diff line number Diff line change
@@ -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

Loading

0 comments on commit aa54c8d

Please sign in to comment.