Add Qt5 API that lazily builds an outline by wrapping the internal objects.
diff --git a/qt5/src/CMakeLists.txt b/qt5/src/CMakeLists.txt
index c94390c..9728212 100644
--- a/qt5/src/CMakeLists.txt
+++ b/qt5/src/CMakeLists.txt
@@ -34,6 +34,7 @@
poppler-textbox.cc
poppler-page-transition.cc
poppler-media.cc
+ poppler-outline.cc
ArthurOutputDev.cc
poppler-version.cpp
)
diff --git a/qt5/src/poppler-document.cc b/qt5/src/poppler-document.cc
index 216ef58..1b5eb1d 100644
--- a/qt5/src/poppler-document.cc
+++ b/qt5/src/poppler-document.cc
@@ -48,6 +48,7 @@
#include "poppler-private.h"
#include "poppler-page-private.h"
+#include "poppler-outline-private.h"
#if defined(USE_CMS)
#include <lcms2.h>
@@ -585,7 +586,7 @@
QDomDocument *Document::toc() const
{
- Outline * outline = m_doc->doc->getOutline();
+ ::Outline * outline = m_doc->doc->getOutline();
if ( !outline )
return nullptr;
@@ -600,6 +601,15 @@
return toc;
}
+ Outline *Document::outline() const
+ {
+ if (auto *outline = m_doc->doc->getOutline()) {
+ return new Outline{new OutlineData{outline, m_doc}};
+ }
+
+ return nullptr;
+ }
+
LinkDestination *Document::linkDestination( const QString &name )
{
GooString * namedDest = QStringToGooString( name );
diff --git a/qt5/src/poppler-outline-private.h b/qt5/src/poppler-outline-private.h
new file mode 100644
index 0000000..ccc0f8c
--- /dev/null
+++ b/qt5/src/poppler-outline-private.h
@@ -0,0 +1,52 @@
+/* poppler-outline-private.h: qt interface to poppler
+ *
+ * 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_OUTLINE_PRIVATE_H_
+#define _POPPLER_OUTLINE_PRIVATE_H_
+
+#include <QtCore/QSharedPointer>
+#include <QtCore/QString>
+
+class OutlineItem;
+
+namespace Poppler {
+
+class DocumentData;
+class LinkDestination;
+
+struct OutlineItemData
+{
+ OutlineItemData(::OutlineItem *data, DocumentData *documentData) : data{data}, documentData{documentData} {}
+ ::OutlineItem *data;
+ DocumentData *documentData;
+
+ mutable QString name;
+ mutable QSharedPointer<const LinkDestination> destination;
+ mutable QString externalFileName;
+ mutable QString uri;
+};
+
+struct OutlineData
+{
+ OutlineData(const ::Outline *data, DocumentData *documentData) : data{data}, documentData{documentData} {}
+ const ::Outline *data;
+ DocumentData *documentData;
+};
+
+}
+
+#endif
diff --git a/qt5/src/poppler-outline.cc b/qt5/src/poppler-outline.cc
new file mode 100644
index 0000000..a6e4b09
--- /dev/null
+++ b/qt5/src/poppler-outline.cc
@@ -0,0 +1,189 @@
+/* poppler-outline.cc: qt interface to poppler
+ *
+ * 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-qt5.h>
+#include <poppler-link.h>
+
+#include "poppler-private.h"
+#include "poppler-outline-private.h"
+
+#include "GooList.h"
+#include "Link.h"
+#include "Outline.h"
+
+namespace Poppler {
+
+OutlineItem::OutlineItem() : m_data{new OutlineItemData{nullptr, nullptr}} {}
+
+OutlineItem::OutlineItem(OutlineItemData *data) : m_data{data} {}
+
+OutlineItem::~OutlineItem()
+{
+ delete m_data;
+ m_data = nullptr;
+}
+
+OutlineItem::OutlineItem(const OutlineItem &other) : m_data{new OutlineItemData{*other.m_data}} {}
+
+OutlineItem &OutlineItem::operator=(const OutlineItem &other)
+{
+ auto *data = new OutlineItemData{*other.m_data};
+ qSwap(m_data, data);
+ delete data;
+
+ return *this;
+}
+
+OutlineItem::OutlineItem(OutlineItem &&other) : m_data{other.m_data}
+{
+ other.m_data = nullptr;
+}
+
+OutlineItem &OutlineItem::operator=(OutlineItem &&other)
+{
+ qSwap(m_data, other.m_data);
+
+ return *this;
+}
+
+bool OutlineItem::isNull() const
+{
+ return !m_data->data;
+}
+
+QString OutlineItem::name() const
+{
+ QString &name = m_data->name;
+
+ if (name.isEmpty()) {
+ if (const ::OutlineItem *data = m_data->data) {
+ name = unicodeToQString(data->getTitle(), data->getTitleLength());
+ }
+ }
+
+ return name;
+}
+
+bool OutlineItem::isOpen() const
+{
+ bool isOpen = false;
+
+ if (const ::OutlineItem *data = m_data->data) {
+ isOpen = data->isOpen();
+ }
+
+ return isOpen;
+}
+
+QSharedPointer<const LinkDestination> OutlineItem::destination() const
+{
+ QSharedPointer<const LinkDestination> &destination = m_data->destination;
+
+ if (!destination) {
+ if (const ::OutlineItem *data = m_data->data) {
+ if (const ::LinkAction *action = data->getAction()) {
+ if (action->getKind() == actionGoTo) {
+ const auto *linkGoTo = static_cast<const LinkGoTo *>(action);
+ destination.reset(new LinkDestination(LinkDestinationData(linkGoTo->getDest(), linkGoTo->getNamedDest(), m_data->documentData, false)));
+ } else if (action->getKind() == actionGoToR) {
+ const auto *linkGoToR = static_cast<const LinkGoToR *>(action);
+ const bool external = linkGoToR->getFileName() != nullptr;
+ destination.reset(new LinkDestination(LinkDestinationData(linkGoToR->getDest(), linkGoToR->getNamedDest(), m_data->documentData, external)));
+ }
+ }
+ }
+ }
+
+ return destination;
+}
+
+QString OutlineItem::externalFileName() const
+{
+ QString &externalFileName = m_data->externalFileName;
+
+ if (externalFileName.isEmpty()) {
+ if (const ::OutlineItem *data = m_data->data) {
+ if (const ::LinkAction *action = data->getAction()) {
+ if (action->getKind() == actionGoToR) {
+ if (const GooString *fileName = static_cast<const LinkGoToR *>(action)->getFileName()) {
+ externalFileName = UnicodeParsedString(fileName);
+ }
+ }
+ }
+ }
+ }
+
+ return externalFileName;
+}
+
+QString OutlineItem::uri() const
+{
+ QString &uri = m_data->uri;
+
+ if (uri.isEmpty()) {
+ if (const ::OutlineItem *data = m_data->data) {
+ if (const ::LinkAction *action = data->getAction()) {
+ if (action->getKind() == actionURI) {
+ uri = UnicodeParsedString(static_cast<const LinkURI *>(action)->getURI());
+ }
+ }
+ }
+ }
+
+ return uri;
+}
+
+QVector<OutlineItem> OutlineItem::children() const
+{
+ QVector<OutlineItem> result;
+
+ if (::OutlineItem *data = m_data->data) {
+ data->open();
+ if (const GooList *kids = data->getKids()) {
+ for (void *kid : *kids) {
+ result.push_back(OutlineItem{new OutlineItemData{static_cast<::OutlineItem *>(kid), m_data->documentData}});
+ }
+ }
+ }
+
+ return result;
+}
+
+Outline::Outline(OutlineData *data) : m_data{data} {}
+
+Outline::~Outline()
+{
+ delete m_data;
+ m_data = nullptr;
+}
+
+QVector<OutlineItem> Outline::items() const
+{
+ QVector<OutlineItem> result;
+
+ const ::Outline *data = m_data->data;
+
+ if (const GooList *items = data->getItems()) {
+ for (void *item : *items) {
+ result.push_back(OutlineItem{new OutlineItemData{static_cast<::OutlineItem *>(item), m_data->documentData}});
+ }
+ }
+
+ return result;
+}
+
+}
diff --git a/qt5/src/poppler-private.cc b/qt5/src/poppler-private.cc
index 333aafe..0c178a5 100644
--- a/qt5/src/poppler-private.cc
+++ b/qt5/src/poppler-private.cc
@@ -288,7 +288,7 @@
for ( int i = 0; i < numItems; ++i )
{
// iterate over every object in 'items'
- OutlineItem * outlineItem = (OutlineItem *)items->get( i );
+ ::OutlineItem * outlineItem = (::OutlineItem *)items->get( i );
// 1. create element using outlineItem's title as tagName
QString name;
diff --git a/qt5/src/poppler-qt5.h b/qt5/src/poppler-qt5.h
index 5373a3a..6bc96a2 100644
--- a/qt5/src/poppler-qt5.h
+++ b/qt5/src/poppler-qt5.h
@@ -70,6 +70,9 @@
class PDFConverter;
class PSConverter;
+ struct OutlineItemData;
+ struct OutlineData;
+
/**
Debug/error function.
@@ -978,6 +981,51 @@
PageData *m_page;
};
+ class POPPLER_QT5_EXPORT OutlineItem {
+ friend class Outline;
+ public:
+ OutlineItem();
+ ~OutlineItem();
+
+ OutlineItem(const OutlineItem &other);
+ OutlineItem &operator=(const OutlineItem &other);
+
+ OutlineItem(OutlineItem &&other);
+ OutlineItem &operator=(OutlineItem &&other);
+
+ bool isNull() const;
+
+ QString name() const;
+
+ bool isOpen() const;
+
+ QSharedPointer<const LinkDestination> destination() const;
+
+ QString externalFileName() const;
+
+ QString uri() const;
+
+ QVector<OutlineItem> children() const;
+
+ private:
+ OutlineItem(OutlineItemData *data);
+ OutlineItemData *m_data;
+ };
+
+ class POPPLER_QT5_EXPORT Outline {
+ friend class Document;
+ public:
+ ~Outline();
+
+ QVector<OutlineItem> items() const;
+
+ private:
+ Q_DISABLE_COPY(Outline)
+
+ Outline(OutlineData *data);
+ OutlineData *m_data;
+ };
+
/**
\brief PDF document.
@@ -1571,6 +1619,8 @@
\returns the TOC, or NULL if the Document does not have one
*/
QDomDocument *toc() const;
+
+ Outline *outline() const;
/**
Tries to resolve the named destination \p name.
diff --git a/qt5/tests/CMakeLists.txt b/qt5/tests/CMakeLists.txt
index 7fda268..5abcbc4 100644
--- a/qt5/tests/CMakeLists.txt
+++ b/qt5/tests/CMakeLists.txt
@@ -73,6 +73,7 @@
qt5_add_qtest(check_qt5_goostring check_goostring.cpp)
qt5_add_qtest(check_qt5_object check_object.cpp)
qt5_add_qtest(check_qt5_utf_conversion check_utf_conversion.cpp)
+qt5_add_qtest(check_qt5_outline check_outline.cpp)
if (NOT WIN32)
qt5_add_qtest(check_qt5_pagelabelinfo check_pagelabelinfo.cpp)
qt5_add_qtest(check_qt5_strings check_strings.cpp)
diff --git a/qt5/tests/check_outline.cpp b/qt5/tests/check_outline.cpp
new file mode 100644
index 0000000..c42f5e0
--- /dev/null
+++ b/qt5/tests/check_outline.cpp
@@ -0,0 +1,55 @@
+#include <QtTest/QtTest>
+
+#include <poppler-qt5.h>
+
+#include <memory>
+
+class TestOutline : public QObject
+{
+ Q_OBJECT
+public:
+ TestOutline(QObject *parent = nullptr) : QObject(parent) {}
+private slots:
+ void checkOutline_xr02();
+};
+
+void TestOutline::checkOutline_xr02()
+{
+ std::unique_ptr<Poppler::Document> document{
+ Poppler::Document::load(TESTDATADIR "/unittestcases/xr02.pdf")
+ };
+ QVERIFY(document.get());
+
+ std::unique_ptr<Poppler::Outline> outline{
+ document->outline()
+ };
+ QVERIFY(outline.get());
+
+ const auto items = outline->items();
+ QCOMPARE(items.size(), 2);
+
+ const auto &foo = items[0];
+ QVERIFY(!foo.isNull());
+ QCOMPARE(foo.name(), QStringLiteral("foo"));
+ QCOMPARE(foo.isOpen(), false);
+ const auto fooDest = foo.destination();
+ QVERIFY(!fooDest.isNull());
+ QCOMPARE(fooDest->pageNumber(), 1);
+ QVERIFY(foo.externalFileName().isEmpty());
+ QVERIFY(foo.uri().isEmpty());
+ QVERIFY(foo.children().isEmpty());
+
+ const auto &bar = items[1];
+ QVERIFY(!bar.isNull());
+ QCOMPARE(bar.name(), QStringLiteral("bar"));
+ QCOMPARE(bar.isOpen(), false);
+ const auto barDest = bar.destination();
+ QVERIFY(!barDest.isNull());
+ QCOMPARE(barDest->pageNumber(), 2);
+ QVERIFY(bar.externalFileName().isEmpty());
+ QVERIFY(bar.uri().isEmpty());
+ QVERIFY(bar.children().isEmpty());
+}
+
+QTEST_GUILESS_MAIN(TestOutline)
+#include "check_outline.moc"