glib/poppler-input-stream.cc | 98 +------------------------ glib/poppler-input-stream.h | 33 +------- poppler/Stream.cc | 107 ++++++++++++++++++++++++++++ poppler/Stream.h | 58 +++++++++++++++ qt5/src/CMakeLists.txt | 1 qt5/src/poppler-document.cc | 17 ++++ qt5/src/poppler-private.h | 16 ++++ qt5/src/poppler-qiodeviceinstream-private.h | 49 ++++++++++++ qt5/src/poppler-qiodeviceinstream.cc | 63 ++++++++++++++++ qt5/src/poppler-qt5.h | 29 +++++++ qt5/tests/check_actualtext.cpp | 30 ++++++- 11 files changed, 376 insertions(+), 125 deletions(-)
New commits: commit a047dea62d28402e819d8ecacdaca95a18f2e3f1 Author: Alexander Volkov <[email protected]> Date: Thu Sep 12 00:04:20 2019 +0300 qt5: Allow to load document from QIODevice Extract BaseSeekInputStream class from PopplerInputStream class and inherit an introduced Poppler::QIODeviceInStream from it. This allows to implement Poppler::Document::load(QIODevice *, ...) overload. diff --git a/glib/poppler-input-stream.cc b/glib/poppler-input-stream.cc index 2365be87..2c75cc0c 100644 --- a/glib/poppler-input-stream.cc +++ b/glib/poppler-input-stream.cc @@ -22,17 +22,10 @@ PopplerInputStream::PopplerInputStream(GInputStream *inputStreamA, GCancellable *cancellableA, Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA) - : BaseStream(std::move(dictA), lengthA) + : BaseSeekInputStream(startA, limitedA, lengthA, std::move(dictA)) { inputStream = (GInputStream *)g_object_ref(inputStreamA); cancellable = cancellableA ? (GCancellable *)g_object_ref(cancellableA) : nullptr; - start = startA; - limited = limitedA; - length = lengthA; - bufPtr = bufEnd = buf; - bufPos = start; - savePos = 0; - saved = false; } PopplerInputStream::~PopplerInputStream() @@ -53,96 +46,19 @@ Stream *PopplerInputStream::makeSubStream(Goffset startA, bool limitedA, return new PopplerInputStream(inputStream, cancellable, startA, limitedA, lengthA, std::move(dictA)); } -void PopplerInputStream::reset() +Goffset PopplerInputStream::currentPos() const { GSeekable *seekable = G_SEEKABLE(inputStream); - - savePos = (unsigned int)g_seekable_tell(seekable); - g_seekable_seek(seekable, start, G_SEEK_SET, cancellable, nullptr); - saved = true; - bufPtr = bufEnd = buf; - bufPos = start; + return g_seekable_tell(seekable); } -void PopplerInputStream::close() +void PopplerInputStream::setCurrentPos(Goffset offset) { - if (!saved) - return; - g_seekable_seek(G_SEEKABLE(inputStream), savePos, G_SEEK_SET, cancellable, nullptr); - saved = false; -} - -void PopplerInputStream::setPos(Goffset pos, int dir) -{ - unsigned int size; GSeekable *seekable = G_SEEKABLE(inputStream); - - if (dir >= 0) { - g_seekable_seek(seekable, pos, G_SEEK_SET, cancellable, nullptr); - bufPos = pos; - } else { - g_seekable_seek(seekable, 0, G_SEEK_END, cancellable, nullptr); - size = (unsigned int)g_seekable_tell(seekable); - - if (pos > size) - pos = size; - - g_seekable_seek(seekable, -(goffset)pos, G_SEEK_END, cancellable, nullptr); - bufPos = (unsigned int)g_seekable_tell(seekable); - } - bufPtr = bufEnd = buf; -} - -void PopplerInputStream::moveStart(Goffset delta) -{ - start += delta; - bufPtr = bufEnd = buf; - bufPos = start; + g_seekable_seek(seekable, offset, G_SEEK_SET, cancellable, nullptr); } -bool PopplerInputStream::fillBuf() +Goffset PopplerInputStream::read(char *buffer, Goffset count) { - int n; - - bufPos += bufEnd - buf; - bufPtr = bufEnd = buf; - if (limited && bufPos >= start + length) { - return false; - } - - if (limited && bufPos + inputStreamBufSize > start + length) { - n = start + length - bufPos; - } else { - n = inputStreamBufSize - (bufPos % inputStreamBufSize); - } - - n = g_input_stream_read(inputStream, buf, n, cancellable, nullptr); - bufEnd = buf + n; - if (bufPtr >= bufEnd) { - return false; - } - - return true; -} - -int PopplerInputStream::getChars(int nChars, unsigned char *buffer) -{ - int n, m; - - n = 0; - while (n < nChars) { - if (bufPtr >= bufEnd) { - if (!fillBuf()) { - break; - } - } - m = (int)(bufEnd - bufPtr); - if (m > nChars - n) { - m = nChars - n; - } - memcpy(buffer + n, bufPtr, m); - bufPtr += m; - n += m; - } - return n; + return g_input_stream_read(inputStream, buffer, count, cancellable, nullptr); } diff --git a/glib/poppler-input-stream.h b/glib/poppler-input-stream.h index 799d752e..1c6011f8 100644 --- a/glib/poppler-input-stream.h +++ b/glib/poppler-input-stream.h @@ -26,9 +26,7 @@ #include <Object.h> #include <Stream.h> -#define inputStreamBufSize 1024 - -class PopplerInputStream: public BaseStream { +class PopplerInputStream: public BaseSeekInputStream { public: PopplerInputStream(GInputStream *inputStream, GCancellable *cancellableA, @@ -37,38 +35,15 @@ public: BaseStream *copy() override; Stream *makeSubStream(Goffset start, bool limited, Goffset lengthA, Object &&dictA) override; - StreamKind getKind() const override { return strWeird; } - void reset() override; - void close() override; - int getChar() override - { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } - int lookChar() override - { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } - Goffset getPos() override { return bufPos + (bufPtr - buf); } - void setPos(Goffset pos, int dir = 0) override; - Goffset getStart() override { return start; } - void moveStart(Goffset delta) override; - - int getUnfilteredChar() override { return getChar(); } - void unfilteredReset() override { reset(); } private: - bool fillBuf(); - - bool hasGetChars() override { return true; } - int getChars(int nChars, unsigned char *buffer) override; + Goffset currentPos() const override; + void setCurrentPos(Goffset offset) override; + Goffset read(char *buffer, Goffset count) override; GInputStream *inputStream; GCancellable *cancellable; - Goffset start; - bool limited; - char buf[inputStreamBufSize]; - char *bufPtr; - char *bufEnd; - Goffset bufPos; - int savePos; - bool saved; }; #endif /* __GI_SCANNER__ */ diff --git a/poppler/Stream.cc b/poppler/Stream.cc index 76a542af..fa89fc5c 100644 --- a/poppler/Stream.cc +++ b/poppler/Stream.cc @@ -37,6 +37,7 @@ // Copyright (C) 2019 Christian Persch <[email protected]> // Copyright (C) 2019 LE GARREC Vincent <[email protected]> // Copyright (C) 2019 Volker Krause <[email protected]> +// Copyright (C) 2019 Alexander Volkov <[email protected]> // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -408,6 +409,112 @@ BaseStream::BaseStream(Object &&dictA, Goffset lengthA) { BaseStream::~BaseStream() { } +//------------------------------------------------------------------------ +// BaseStream +//------------------------------------------------------------------------ + +BaseSeekInputStream::BaseSeekInputStream(Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA) + : BaseStream(std::move(dictA), lengthA) + , start(startA) + , limited(limitedA) + , bufPtr(buf) + , bufEnd(buf) + , bufPos(start) + , savePos(0) + , saved(false) +{ +} + +BaseSeekInputStream::~BaseSeekInputStream() +{ +} + +void BaseSeekInputStream::reset() +{ + savePos = currentPos(); + setCurrentPos(start); + saved = true; + bufPtr = bufEnd = buf; + bufPos = start; +} + +void BaseSeekInputStream::close() +{ + if (!saved) + return; + setCurrentPos(savePos); + saved = false; +} + +void BaseSeekInputStream::setPos(Goffset pos, int dir) +{ + if (dir >= 0) { + setCurrentPos(pos); + bufPos = pos; + } else { + if (pos > length) + pos = length; + + bufPos = length - pos; + setCurrentPos(bufPos); + } + bufPtr = bufEnd = buf; +} + +void BaseSeekInputStream::moveStart(Goffset delta) +{ + start += delta; + bufPtr = bufEnd = buf; + bufPos = start; +} + +bool BaseSeekInputStream::fillBuf() +{ + Goffset n; + + bufPos += bufEnd - buf; + bufPtr = bufEnd = buf; + if (limited && bufPos >= start + length) { + return false; + } + + if (limited && bufPos + seekInputStreamBufSize > start + length) { + n = start + length - bufPos; + } else { + n = seekInputStreamBufSize - (bufPos % seekInputStreamBufSize); + } + + n = read(buf, n); + bufEnd = buf + n; + if (bufPtr >= bufEnd) { + return false; + } + + return true; +} + +int BaseSeekInputStream::getChars(int nChars, unsigned char *buffer) +{ + int n, m; + + n = 0; + while (n < nChars) { + if (bufPtr >= bufEnd) { + if (!fillBuf()) { + break; + } + } + m = (int)(bufEnd - bufPtr); + if (m > nChars - n) { + m = nChars - n; + } + memcpy(buffer + n, bufPtr, m); + bufPtr += m; + n += m; + } + return n; +} + //------------------------------------------------------------------------ // FilterStream //------------------------------------------------------------------------ diff --git a/poppler/Stream.h b/poppler/Stream.h index 46f2b375..b675bcc6 100644 --- a/poppler/Stream.h +++ b/poppler/Stream.h @@ -27,6 +27,7 @@ // Copyright (C) 2013, 2018 Adam Reichold <[email protected]> // Copyright (C) 2013 Pino Toscano <[email protected]> // Copyright (C) 2019 Volker Krause <[email protected]> +// Copyright (C) 2019 Alexander Volkov <[email protected]> // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -326,6 +327,63 @@ protected: Object dict; }; +//------------------------------------------------------------------------ +// BaseInputStream +//------------------------------------------------------------------------ + +class BaseSeekInputStream: public BaseStream { +public: + + // This enum is used to tell the seek() method how it must reposition + // the stream offset. + enum SeekType { + SeekSet, // the offset is set to offset bytes + SeekCur, // the offset is set to its current location plus offset bytes + SeekEnd // the offset is set to the size of the stream plus offset bytes + }; + + BaseSeekInputStream(Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA); + ~BaseSeekInputStream() override; + StreamKind getKind() const override { return strWeird; } + void reset() override; + void close() override; + int getChar() override + { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } + int lookChar() override + { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } + Goffset getPos() override { return bufPos + (bufPtr - buf); } + void setPos(Goffset pos, int dir = 0) override; + Goffset getStart() override { return start; } + void moveStart(Goffset delta) override; + + int getUnfilteredChar() override { return getChar(); } + void unfilteredReset() override { reset(); } + +protected: + + Goffset start; + bool limited; + +private: + + bool fillBuf(); + + bool hasGetChars() override { return true; } + int getChars(int nChars, unsigned char *buffer) override; + + virtual Goffset currentPos() const = 0; + virtual void setCurrentPos(Goffset offset) = 0; + virtual Goffset read(char *buf, Goffset size) = 0; + + static constexpr int seekInputStreamBufSize = 1024; + char buf[seekInputStreamBufSize]; + char *bufPtr; + char *bufEnd; + Goffset bufPos; + Goffset savePos; + bool saved; +}; + //------------------------------------------------------------------------ // FilterStream // diff --git a/qt5/src/CMakeLists.txt b/qt5/src/CMakeLists.txt index 643f56a1..b22bd40d 100644 --- a/qt5/src/CMakeLists.txt +++ b/qt5/src/CMakeLists.txt @@ -29,6 +29,7 @@ set(poppler_qt5_SRCS poppler-pdf-converter.cc poppler-private.cc poppler-ps-converter.cc + poppler-qiodeviceinstream.cc poppler-qiodeviceoutstream.cc poppler-sound.cc poppler-textbox.cc diff --git a/qt5/src/poppler-document.cc b/qt5/src/poppler-document.cc index fd525e62..bff01205 100644 --- a/qt5/src/poppler-document.cc +++ b/qt5/src/poppler-document.cc @@ -14,6 +14,7 @@ * Copyright (C) 2017 Suzuki Toshiya <[email protected]> * Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, <[email protected]>. Work sponsored by the LiMux project of the city of Munich * Copyright (C) 2019 Oliver Sander <[email protected]> + * Copyright (C) 2019 Alexander Volkov <[email protected]> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -66,6 +67,15 @@ namespace Poppler { return DocumentData::checkDocument(doc); } + Document *Document::load(QIODevice *device, const QByteArray &ownerPassword, + const QByteArray &userPassword) + { + DocumentData *doc = new DocumentData(device, + new GooString(ownerPassword.data()), + new GooString(userPassword.data())); + return DocumentData::checkDocument(doc); + } + Document *Document::loadFromData(const QByteArray &fileContents, const QByteArray &ownerPassword, const QByteArray &userPassword) @@ -136,7 +146,12 @@ namespace Poppler { new GooString(ownerPassword.data()), new GooString(userPassword.data())); } - else + else if (m_doc->m_device) + { + doc2 = new DocumentData(m_doc->m_device, + new GooString(ownerPassword.data()), + new GooString(userPassword.data())); + } { doc2 = new DocumentData(m_doc->m_filePath, new GooString(ownerPassword.data()), diff --git a/qt5/src/poppler-private.h b/qt5/src/poppler-private.h index 0b09d30e..9131dba6 100644 --- a/qt5/src/poppler-private.h +++ b/qt5/src/poppler-private.h @@ -16,6 +16,7 @@ * Copyright (C) 2019 Oliver Sander <[email protected]> * Copyright (C) 2019 João Netto <[email protected]> * Copyright (C) 2019 Jan Grulich <[email protected]> + * Copyright (C) 2019 Alexander Volkov <[email protected]> * Inspired on code by * Copyright (C) 2004 by Albert Astals Cid <[email protected]> * Copyright (C) 2004 by Enrico Ros <[email protected]> @@ -57,6 +58,7 @@ #include "poppler-qt5.h" #include "poppler-embeddedfile-private.h" +#include "poppler-qiodeviceinstream-private.h" class LinkDest; class FormWidget; @@ -99,6 +101,7 @@ namespace Poppler { GlobalParamsIniter(qt5ErrorFunction) { init(); + m_device = nullptr; m_filePath = filePath; #ifdef _WIN32 @@ -112,9 +115,21 @@ namespace Poppler { delete userPassword; } + DocumentData(QIODevice *device, GooString *ownerPassword, GooString *userPassword) : + GlobalParamsIniter(qt5ErrorFunction) + { + m_device = device; + QIODeviceInStream *str = new QIODeviceInStream(device, 0, false, device->size(), Object(objNull)); + init(); + doc = new PDFDoc(str, ownerPassword, userPassword); + delete ownerPassword; + delete userPassword; + } + DocumentData(const QByteArray &data, GooString *ownerPassword, GooString *userPassword) : GlobalParamsIniter(qt5ErrorFunction) { + m_device = nullptr; fileContents = data; MemStream *str = new MemStream((char*)fileContents.data(), 0, fileContents.length(), Object(objNull)); init(); @@ -153,6 +168,7 @@ namespace Poppler { PDFDoc *doc; QString m_filePath; + QIODevice *m_device; QByteArray fileContents; bool locked; Document::RenderBackend m_backend; diff --git a/qt5/src/poppler-qiodeviceinstream-private.h b/qt5/src/poppler-qiodeviceinstream-private.h new file mode 100644 index 00000000..13aa5128 --- /dev/null +++ b/qt5/src/poppler-qiodeviceinstream-private.h @@ -0,0 +1,49 @@ +/* poppler-qiodeviceinstream-private.h: Qt5 interface to poppler + * Copyright (C) 2019 Alexander Volkov <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef POPPLER_QIODEVICEINSTREAM_PRIVATE_H +#define POPPLER_QIODEVICEINSTREAM_PRIVATE_H + +#include "Object.h" +#include "Stream.h" + +class QIODevice; + +namespace Poppler { + +class QIODeviceInStream : public BaseSeekInputStream +{ + public: + QIODeviceInStream(QIODevice* device, Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA); + ~QIODeviceInStream() override; + + BaseStream *copy() override; + Stream *makeSubStream(Goffset startA, bool limitedA, + Goffset lengthA, Object &&dictA) override; + + private: + Goffset currentPos() const override; + void setCurrentPos(Goffset offset) override; + Goffset read(char *buffer, Goffset count) override; + + QIODevice *m_device; +}; + +} + +#endif diff --git a/qt5/src/poppler-qiodeviceinstream.cc b/qt5/src/poppler-qiodeviceinstream.cc new file mode 100644 index 00000000..d1cd433c --- /dev/null +++ b/qt5/src/poppler-qiodeviceinstream.cc @@ -0,0 +1,63 @@ +/* poppler-qiodeviceinstream.cc: Qt5 interface to poppler + * Copyright (C) 2019 Alexander Volkov <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "poppler-qiodeviceinstream-private.h" + +#include <QtCore/QIODevice> + +#include <cstdio> + +namespace Poppler { + +QIODeviceInStream::QIODeviceInStream(QIODevice *device, Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA) + : BaseSeekInputStream(startA, limitedA, lengthA, std::move(dictA)) + , m_device(device) +{ +} + +QIODeviceInStream::~QIODeviceInStream() +{ + close(); +} + +BaseStream *QIODeviceInStream::copy() +{ + return new QIODeviceInStream(m_device, start, limited, length, dict.copy()); +} + +Stream *QIODeviceInStream::makeSubStream(Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA) +{ + return new QIODeviceInStream(m_device, startA, limitedA, lengthA, std::move(dictA)); +} + +Goffset QIODeviceInStream::currentPos() const +{ + return m_device->pos(); +} + +void QIODeviceInStream::setCurrentPos(Goffset offset) +{ + m_device->seek(offset); +} + +Goffset QIODeviceInStream::read(char *buffer, Goffset count) +{ + return m_device->read(buffer, count); +} + +} diff --git a/qt5/src/poppler-qt5.h b/qt5/src/poppler-qt5.h index c501f67f..d32ffa3b 100644 --- a/qt5/src/poppler-qt5.h +++ b/qt5/src/poppler-qt5.h @@ -20,6 +20,7 @@ * Copyright (C) 2017, 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, <[email protected]>. Work sponsored by the LiMux project of the city of Munich * Copyright (C) 2018 Nelson Benítez León <[email protected]> * Copyright (C) 2019 Jan Grulich <[email protected]> + * Copyright (C) 2019 Alexander Volkov <[email protected]> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -1241,6 +1242,34 @@ delete it; const QByteArray &ownerPassword=QByteArray(), const QByteArray &userPassword=QByteArray()); + /** + Load the document from a device + + \param device the device of the data to load + \param ownerPassword the Latin1-encoded owner password to use in + loading the file + \param userPassword the Latin1-encoded user ("open") password + to use in loading the file + + \return the loaded document, or NULL on error + + \note The caller owns the pointer to Document, and this should + be deleted when no longer required. + + \note The ownership of the device stays with the caller. + + \note if the file is on disk it is recommended to use the other load overload + since it is less resource intensive + + \warning The returning document may be locked if a password is required + to open the file, and one is not provided (as the userPassword). + + \since 0.85 + */ + static Document *load(QIODevice *device, + const QByteArray &ownerPassword=QByteArray(), + const QByteArray &userPassword=QByteArray()); + /** Load the document from memory diff --git a/qt5/tests/check_actualtext.cpp b/qt5/tests/check_actualtext.cpp index 29865665..09bf3156 100644 --- a/qt5/tests/check_actualtext.cpp +++ b/qt5/tests/check_actualtext.cpp @@ -11,20 +11,42 @@ public: TestActualText(QObject *parent = nullptr) : QObject(parent) { } private slots: void checkActualText1(); + void checkActualText2(); +private: + void checkActualText(Poppler::Document *doc); }; +void TestActualText::checkActualText(Poppler::Document *doc) +{ + Poppler::Page *page = doc->page(0); + QVERIFY( page ); + + QCOMPARE( page->text(QRectF()), QLatin1String("The slow brown fox jumps over the black dog.") ); + + delete page; +} + void TestActualText::checkActualText1() { Poppler::Document *doc; doc = Poppler::Document::load(TESTDATADIR "/unittestcases/WithActualText.pdf"); QVERIFY( doc ); - Poppler::Page *page = doc->page(0); - QVERIFY( page ); + checkActualText(doc); - QCOMPARE( page->text(QRectF()), QLatin1String("The slow brown fox jumps over the black dog.") ); + delete doc; +} - delete page; +void TestActualText::checkActualText2() +{ + QFile file(TESTDATADIR "/unittestcases/WithActualText.pdf"); + QVERIFY(file.open(QIODevice::ReadOnly)); + + Poppler::Document *doc; + doc = Poppler::Document::load(&file); + QVERIFY( doc ); + + checkActualText(doc); delete doc; } _______________________________________________ poppler mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/poppler
