Visitor for performance measures

This commit is contained in:
Jakub Melka 2018-12-01 11:36:07 +01:00
parent 670a260265
commit 26a2a8deb5
10 changed files with 529 additions and 15 deletions

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.7.2, 2018-11-24T18:51:27. -->
<!-- Written by QtCreator 4.7.2, 2018-11-30T19:34:52. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
@ -67,7 +67,7 @@
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop Qt 5.11.2 MSVC2017 64bit</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop Qt 5.11.2 MSVC2017 64bit</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">qt.qt5.5112.win64_msvc2017_64_kit</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">1</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
@ -296,7 +296,7 @@
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.CommandLineArguments"></value>
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.ProFile">UnitTests/UnitTests.pro</value>
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory"></value>
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory.default">K:/Programming/PDF/PDF_For_Qt/bin_release/UnitTests/..</value>
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory.default">K:/Programming/PDF/PDF_For_Qt/bin_debug/UnitTests/..</value>
<value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
@ -353,7 +353,7 @@
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.CommandLineArguments"></value>
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.ProFile">PdfForQtViewer/PdfForQtViewer.pro</value>
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory"></value>
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory.default">K:/Programming/PDF/PDF_For_Qt/bin_release/PdfForQtViewer/..</value>
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory.default">K:/Programming/PDF/PDF_For_Qt/bin_debug/PdfForQtViewer/..</value>
<value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>

View File

@ -40,7 +40,8 @@ SOURCES += \
sources/pdfparser.cpp \
sources/pdfdocument.cpp \
sources/pdfdocumentreader.cpp \
sources/pdfxreftable.cpp
sources/pdfxreftable.cpp \
sources/pdfvisitor.cpp
HEADERS += \
sources/pdfobject.h \
@ -50,7 +51,8 @@ HEADERS += \
sources/pdfdocument.h \
sources/pdfdocumentreader.h \
sources/pdfxreftable.h \
sources/pdfflatmap.h
sources/pdfflatmap.h \
sources/pdfvisitor.h
unix {
target.path = /usr/lib

View File

@ -38,7 +38,6 @@ public:
constexpr inline PDFObjectStorage& operator=(const PDFObjectStorage&) = default;
constexpr inline PDFObjectStorage& operator=(PDFObjectStorage&&) = default;
struct Entry
{
constexpr inline explicit Entry() = default;
@ -50,16 +49,41 @@ public:
using PDFObjects = std::vector<Entry>;
explicit PDFObjectStorage(PDFObjects&& objects, PDFObject&& trailerDictionary) :
m_objects(std::move(objects)),
m_trailerDictionary(std::move(trailerDictionary))
{
}
/// Returns array of objects stored in this storage
const PDFObjects& getObjects() const { return m_objects; }
/// Returns trailer dictionary
const PDFObject& getTrailerDictionary() const { return m_trailerDictionary; }
private:
PDFObjects m_pdfObjects;
PDFObjects m_objects;
PDFObject m_trailerDictionary;
};
/// PDF document main class.
class PDFDocument
{
public:
explicit PDFDocument() = default;
const PDFObjectStorage& getStorage() const { return m_pdfObjectStorage; }
private:
friend class PDFDocumentReader;
explicit PDFDocument(PDFObjectStorage&& storage) :
m_pdfObjectStorage(std::move(storage))
{
}
/// Storage of objects
PDFObjectStorage m_pdfObjectStorage;
};

View File

@ -260,6 +260,9 @@ PDFDocument PDFDocumentReader::readFromBuffer(const QByteArray& buffer)
// Now, we are ready to scan all objects
std::for_each(std::execution::parallel_policy(), occupiedEntries.cbegin(), occupiedEntries.cend(), processEntry);
PDFObjectStorage storage(std::move(objects), PDFObject(xrefTable.getTrailerDictionary()));
return PDFDocument(std::move(storage));
}
catch (PDFParserException parserException)
{

View File

@ -17,6 +17,7 @@
#include "pdfobject.h"
#include "pdfvisitor.h"
namespace pdf
{
@ -30,7 +31,7 @@ QByteArray PDFObject::getString() const
return string->getString();
}
const PDFDictionary*PDFObject::getDictionary() const
const PDFDictionary* PDFObject::getDictionary() const
{
const PDFObjectContentPointer& objectContent = std::get<PDFObjectContentPointer>(m_data);
@ -38,6 +39,30 @@ const PDFDictionary*PDFObject::getDictionary() const
return static_cast<const PDFDictionary*>(objectContent.get());
}
const PDFString* PDFObject::getStringObject() const
{
const PDFObjectContentPointer& objectContent = std::get<PDFObjectContentPointer>(m_data);
Q_ASSERT(dynamic_cast<const PDFString*>(objectContent.get()));
return static_cast<const PDFString*>(objectContent.get());
}
const PDFStream* PDFObject::getStream() const
{
const PDFObjectContentPointer& objectContent = std::get<PDFObjectContentPointer>(m_data);
Q_ASSERT(dynamic_cast<const PDFStream*>(objectContent.get()));
return static_cast<const PDFStream*>(objectContent.get());
}
const PDFArray* PDFObject::getArray() const
{
const PDFObjectContentPointer& objectContent = std::get<PDFObjectContentPointer>(m_data);
Q_ASSERT(dynamic_cast<const PDFArray*>(objectContent.get()));
return static_cast<const PDFArray*>(objectContent.get());
}
bool PDFObject::operator==(const PDFObject &other) const
{
if (m_type == other.m_type)
@ -61,6 +86,55 @@ bool PDFObject::operator==(const PDFObject &other) const
return false;
}
void PDFObject::accept(PDFAbstractVisitor* visitor) const
{
switch (m_type)
{
case Type::Null:
visitor->visitNull();
break;
case Type::Bool:
visitor->visitBool(getBool());
break;
case Type::Int:
visitor->visitInt(getInteger());
break;
case Type::Real:
visitor->visitReal(getReal());
break;
case Type::String:
visitor->visitString(getStringObject());
break;
case Type::Name:
visitor->visitName(getStringObject());
break;
case Type::Array:
visitor->visitArray(getArray());
break;
case Type::Dictionary:
visitor->visitDictionary(getDictionary());
break;
case Type::Stream:
visitor->visitStream(getStream());
break;
case Type::Reference:
visitor->visitReference(getReference());
break;
default:
Q_ASSERT(false);
}
}
bool PDFString::equals(const PDFObjectContent* other) const
{
Q_ASSERT(dynamic_cast<const PDFString*>(other));
@ -68,11 +142,6 @@ bool PDFString::equals(const PDFObjectContent* other) const
return m_string == otherString->m_string;
}
QByteArray PDFString::getString() const
{
return m_string;
}
void PDFString::setString(const QByteArray& string)
{
m_string = string;

View File

@ -26,10 +26,15 @@
#include <memory>
#include <vector>
#include <variant>
#include <initializer_list>
namespace pdf
{
class PDFArray;
class PDFString;
class PDFStream;
class PDFDictionary;
class PDFAbstractVisitor;
/// This class represents a content of the PDF object. It can be
/// array of objects, dictionary, content stream data, or string data.
@ -64,6 +69,8 @@ public:
Reference
};
static std::vector<Type> getTypes() { return { Type::Null, Type::Bool, Type::Int, Type::Real, Type::String, Type::Name, Type::Array, Type::Dictionary, Type::Stream, Type::Reference }; }
typedef std::shared_ptr<PDFObjectContent> PDFObjectContentPointer;
// Default constructor should be constexpr
@ -97,14 +104,22 @@ public:
inline bool isStream() const { return m_type == Type::Stream; }
inline bool isReference() const { return m_type == Type::Reference; }
inline bool getBool() const { return std::get<bool>(m_data); }
inline PDFInteger getInteger() const { return std::get<PDFInteger>(m_data); }
inline PDFReal getReal() const { return std::get<PDFReal>(m_data); }
QByteArray getString() const;
const PDFDictionary* getDictionary() const;
PDFObjectReference getReference() const { return std::get<PDFObjectReference>(m_data); }
const PDFString* getStringObject() const;
const PDFStream* getStream() const;
const PDFArray* getArray() const;
bool operator==(const PDFObject& other) const;
bool operator!=(const PDFObject& other) const { return !(*this == other); }
/// Accepts the visitor
void accept(PDFAbstractVisitor* visitor) const;
/// Creates a null object
static inline PDFObject createNull() { return PDFObject(); }
@ -165,7 +180,7 @@ public:
virtual bool equals(const PDFObjectContent* other) const override;
QByteArray getString() const;
const QByteArray& getString() const { return m_string; }
void setString(const QByteArray &getString);
private:
@ -188,6 +203,9 @@ public:
/// Returns size of the array (number of elements)
size_t getCount() const { return m_objects.size(); }
/// Returns capacity of the array (theoretical number of elements before reallocation)
size_t getCapacity() const { return m_objects.capacity(); }
/// Appends object to the end of object list
void appendItem(PDFObject object);
@ -233,6 +251,20 @@ public:
/// \param value Value
void addEntry(QByteArray&& key, PDFObject&& value) { m_dictionary.emplace_back(std::move(key), std::move(value)); }
/// Returns count of items in the dictionary
size_t getCount() const { return m_dictionary.size(); }
/// Returns capacity of items in the dictionary
size_t getCapacity() const { return m_dictionary.capacity(); }
/// Returns n-th key of the dictionary
/// \param index Zero-based index of key in the dictionary
const QByteArray& getKey(size_t index) const { return m_dictionary[index].first; }
/// Returns n-th value of the dictionary
/// \param index Zero-based index of value in the dictionary
const PDFObject& getValue(size_t index) const { return m_dictionary[index].second; }
private:
/// Finds an item in the dictionary array, if the item is not in the dictionary,
/// then end iterator is returned.

View File

@ -0,0 +1,175 @@
// Copyright (C) 2018 Jakub Melka
//
// This file is part of PdfForQt.
//
// PdfForQt is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// PdfForQt 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDFForQt. If not, see <https://www.gnu.org/licenses/>.
#include "pdfvisitor.h"
namespace pdf
{
void PDFAbstractVisitor::acceptArray(const PDFArray* array)
{
Q_ASSERT(array);
for (size_t i = 0, count = array->getCount(); i < count; ++i)
{
array->getItem(i).accept(this);
}
}
void PDFAbstractVisitor::acceptDictionary(const PDFDictionary* dictionary)
{
Q_ASSERT(dictionary);
for (size_t i = 0, count = dictionary->getCount(); i < count; ++i)
{
dictionary->getValue(i).accept(this);
}
}
void PDFAbstractVisitor::acceptStream(const PDFStream* stream)
{
Q_ASSERT(stream);
acceptDictionary(stream->getDictionary());
}
PDFStatisticsCollector::PDFStatisticsCollector()
{
// We must avoid to allocate map item
//std::vector<PDFObject::Type> types = PDFObject::getTypes();
for (PDFObject::Type type : PDFObject::getTypes())
{
m_statistics.emplace(std::make_pair(type, Statistics()));
}
}
void PDFStatisticsCollector::visitNull()
{
collectStatisticsOfSimpleObject(PDFObject::Type::Null);
}
void PDFStatisticsCollector::visitBool(bool value)
{
Q_UNUSED(value);
collectStatisticsOfSimpleObject(PDFObject::Type::Bool);
}
void PDFStatisticsCollector::visitInt(PDFInteger value)
{
Q_UNUSED(value);
collectStatisticsOfSimpleObject(PDFObject::Type::Int);
}
void PDFStatisticsCollector::visitReal(PDFReal value)
{
Q_UNUSED(value);
collectStatisticsOfSimpleObject(PDFObject::Type::Real);
}
void PDFStatisticsCollector::visitString(const PDFString* string)
{
Statistics& statistics = m_statistics[PDFObject::Type::String];
collectStatisticsOfString(string, statistics);
}
void PDFStatisticsCollector::visitName(const PDFString* name)
{
Statistics& statistics = m_statistics[PDFObject::Type::Name];
collectStatisticsOfString(name, statistics);
}
void PDFStatisticsCollector::visitArray(const PDFArray* array)
{
Statistics& statistics = m_statistics[PDFObject::Type::Array];
statistics.count += 1;
statistics.memoryConsumptionEstimate += sizeof(PDFObject) + sizeof(PDFArray);
// We process elements of the array, together with memory consumption,
// in the call of acceptArray function. No need to calculate memory consumption here.
// Just calculate the overhead.
statistics.memoryOverheadEstimate += (array->getCapacity() - array->getCount()) * sizeof(PDFObject);
acceptArray(array);
}
void PDFStatisticsCollector::visitDictionary(const PDFDictionary* dictionary)
{
Statistics& statistics = m_statistics[PDFObject::Type::Dictionary];
collectStatisticsOfDictionary(statistics, dictionary);
acceptDictionary(dictionary);
}
void PDFStatisticsCollector::visitStream(const PDFStream* stream)
{
Statistics& statistics = m_statistics[PDFObject::Type::Stream];
collectStatisticsOfDictionary(statistics, stream->getDictionary());
acceptStream(stream);
}
void PDFStatisticsCollector::visitReference(const PDFObjectReference reference)
{
Q_UNUSED(reference);
collectStatisticsOfSimpleObject(PDFObject::Type::Reference);
}
void PDFStatisticsCollector::collectStatisticsOfDictionary(Statistics& statistics, const PDFDictionary* dictionary)
{
statistics.count += 1;
statistics.memoryConsumptionEstimate += sizeof(PDFObject) + sizeof(PDFDictionary);
constexpr uint64_t sizeOfItem = sizeof(std::pair<QByteArray, PDFObject>);
constexpr uint64_t sizeOfItemWithoutObject = sizeOfItem - sizeof(PDFObject);
uint64_t consumptionEstimate = sizeOfItemWithoutObject * dictionary->getCount();
uint64_t overheadEstimate = sizeOfItem * (dictionary->getCapacity() - dictionary->getCount());
for (size_t i = 0, count = dictionary->getCount(); i < count; ++i)
{
const QByteArray& key = dictionary->getKey(i);
consumptionEstimate += key.size() * sizeof(char);
overheadEstimate += (key.capacity() - key.size()) * sizeof(char);
}
statistics.memoryConsumptionEstimate += consumptionEstimate;
statistics.memoryOverheadEstimate += overheadEstimate;
}
void PDFStatisticsCollector::collectStatisticsOfString(const PDFString* string, Statistics& statistics)
{
statistics.count += 1;
statistics.memoryConsumptionEstimate += sizeof(PDFObject) + sizeof(PDFString);
const QByteArray& byteArray = string->getString();
const uint64_t memoryConsumption = byteArray.size() * sizeof(char);
const uint64_t memoryOverhead = (byteArray.capacity() - byteArray.size()) * sizeof(char);
statistics.memoryConsumptionEstimate += memoryConsumption;
statistics.memoryOverheadEstimate += memoryOverhead;
}
void PDFStatisticsCollector::collectStatisticsOfSimpleObject(PDFObject::Type type)
{
Statistics& statistics = m_statistics[type];
statistics.count += 1;
statistics.memoryConsumptionEstimate += sizeof(PDFObject);
}
} // namespace pdf

View File

@ -0,0 +1,203 @@
// Copyright (C) 2018 Jakub Melka
//
// This file is part of PdfForQt.
//
// PdfForQt is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// PdfForQt 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDFForQt. If not, see <https://www.gnu.org/licenses/>.
#ifndef PDFVISITOR_H
#define PDFVISITOR_H
#include "pdfglobal.h"
#include "pdfobject.h"
#include "pdfdocument.h"
#include <QMutex>
#include <map>
#include <atomic>
#include <execution>
namespace pdf
{
/// Abstract visitor, can iterate trough object tree
class PDFAbstractVisitor
{
public:
/// This enum identifies how the tree of objects should be processed.
/// Some visitors allow parallel (concurrent) run, some visitors are not
/// thread safe, so they run in single thread. Please select strategy
/// of your visitor carefully to avoid race conditions.
enum class Strategy
{
/// Visitor with this strategy allows fully parallel run across multiple threads.
/// So single instance of visitor is used and it is assumed, that all functions
/// are thread safe. This means full parallelization.
Parallel,
/// Merging paraller strategy. For each thread, instance of visitor is created, and
/// run on object tree. Then they are merged by the \p merge function. Strategy assumes,
/// that \p merge function is not reentrant, and it is not run by multiple threads
/// on same object. Locking is not needed in this function.
Merging,
/// Do not allow parallel run, use single instance and single thread run. This can
/// be slower than previous two strategies, but allows visitor not to be thread
/// safe. Use this strategy on visitors, which can't be thread safe from principle.
Sequential
};
explicit constexpr PDFAbstractVisitor() = default;
virtual ~PDFAbstractVisitor() = default;
virtual void visitNull() { }
virtual void visitBool(bool value) { Q_UNUSED(value); }
virtual void visitInt(PDFInteger value) { Q_UNUSED(value); }
virtual void visitReal(PDFReal value) { Q_UNUSED(value); }
virtual void visitString(const PDFString* string) { Q_UNUSED(string); }
virtual void visitName(const PDFString* name) { Q_UNUSED(name); }
virtual void visitArray(const PDFArray* array) { Q_UNUSED(array); }
virtual void visitDictionary(const PDFDictionary* dictionary) { Q_UNUSED(dictionary); }
virtual void visitStream(const PDFStream* stream) { Q_UNUSED(stream); }
virtual void visitReference(const PDFObjectReference reference) { Q_UNUSED(reference); }
protected:
void acceptArray(const PDFArray* array);
void acceptDictionary(const PDFDictionary* dictionary);
void acceptStream(const PDFStream* stream);
};
/// Statistics visitor, collects statistics about PDF object, can be
/// invoked from multiple threads.
class PDFFORQTLIBSHARED_EXPORT PDFStatisticsCollector : public PDFAbstractVisitor
{
public:
explicit PDFStatisticsCollector();
virtual ~PDFStatisticsCollector() override = default;
/// We implement this visitor as fully parallel
static constexpr const Strategy VisitorStrategy = Strategy::Parallel;
struct Statistics
{
explicit constexpr Statistics() = default;
/// This constructor must not be used while \p other is write-accessed from another thread.
/// We use relaxed memory order, because we assume this constructor is used only while
/// copying data, not when statistics is collected.
/// \param other Object, from which data should be copied
inline Statistics(const Statistics& other) :
count(other.count.load(std::memory_order_relaxed)),
memoryConsumptionEstimate(other.memoryConsumptionEstimate.load(std::memory_order_relaxed)),
memoryOverheadEstimate(other.memoryOverheadEstimate.load(std::memory_order_relaxed))
{
}
std::atomic_uint64_t count = 0;
std::atomic_uint64_t memoryConsumptionEstimate = 0;
std::atomic_uint64_t memoryOverheadEstimate = 0;
};
virtual void visitNull() override;
virtual void visitBool(bool value) override;
virtual void visitInt(PDFInteger value) override;
virtual void visitReal(PDFReal value) override;
virtual void visitString(const PDFString* string) override;
virtual void visitName(const PDFString* name) override;
virtual void visitArray(const PDFArray* array) override;
virtual void visitDictionary(const PDFDictionary* dictionary) override;
virtual void visitStream(const PDFStream* stream) override;
virtual void visitReference(const PDFObjectReference reference) override;
private:
void collectStatisticsOfSimpleObject(PDFObject::Type type);
void collectStatisticsOfString(const PDFString* string, Statistics& statistics);
void collectStatisticsOfDictionary(Statistics& statistics, const PDFDictionary* dictionary);
std::map<PDFObject::Type, Statistics> m_statistics;
};
template<typename Visitor, PDFAbstractVisitor::Strategy strategy>
struct PDFApplyVisitorImpl;
template<typename Visitor>
void PDFApplyVisitor(const PDFDocument& document, Visitor* visitor)
{
PDFApplyVisitorImpl<Visitor, Visitor::VisitorStrategy>::apply(document, visitor);
}
template<typename Visitor, PDFAbstractVisitor::Strategy strategy>
struct PDFApplyVisitorImpl
{
// Do nothing, no strategy defined
};
template<typename Visitor>
struct PDFApplyVisitorImpl<Visitor, PDFAbstractVisitor::Strategy::Parallel>
{
static void apply(const PDFDocument& document, Visitor* visitor)
{
const PDFObjectStorage& storage = document.getStorage();
const PDFObjectStorage::PDFObjects& objects = storage.getObjects();
const PDFObject& trailerDictionary = storage.getTrailerDictionary();
std::for_each(std::execution::parallel_policy(), objects.cbegin(), objects.cend(), [visitor](const PDFObjectStorage::Entry& entry) { entry.object.accept(visitor); });
trailerDictionary.accept(visitor);
}
};
template<typename Visitor>
struct PDFApplyVisitorImpl<Visitor, PDFAbstractVisitor::Strategy::Merging>
{
static void apply(const PDFDocument& document, Visitor* visitor)
{
const PDFObjectStorage& storage = document.getStorage();
const PDFObjectStorage::PDFObjects& objects = storage.getObjects();
const PDFObject& trailerDictionary = storage.getTrailerDictionary();
QMutex mutex;
auto process = [&mutex, visitor](const PDFObjectStorage::Entry& entry)
{
Visitor localVisitor;
entry.object.accept(&localVisitor);
QMutexLocker lock(&mutex);
visitor->merge(&localVisitor);
};
std::for_each(std::execution::parallel_policy(), objects.cbegin(), objects.cend(), process);
trailerDictionary.accept(visitor);
}
};
template<typename Visitor>
struct PDFApplyVisitorImpl<Visitor, PDFAbstractVisitor::Strategy::Sequential>
{
static void apply(const PDFDocument& document, Visitor* visitor)
{
const PDFObjectStorage& storage = document.getStorage();
const PDFObjectStorage::PDFObjects& objects = storage.getObjects();
const PDFObject& trailerDictionary = storage.getTrailerDictionary();
std::for_each(std::execution::sequenced_policy(), objects.cbegin(), objects.cend(), [](const PDFObjectStorage::Entry& entry) { entry.object.accept(visitor); });
trailerDictionary.accept(visitor);
}
};
} // namespace pdf
#endif // PDFVISITOR_H

View File

@ -77,6 +77,9 @@ public:
/// then free entry is returned.
const Entry& getEntry(PDFObjectReference reference) const;
/// Returns the trailer dictionary
const PDFObject& getTrailerDictionary() const { return m_trailerDictionary; }
private:
/// Reference table entries
std::vector<Entry> m_entries;

View File

@ -2,6 +2,7 @@
#include "ui_pdfviewermainwindow.h"
#include "pdfdocumentreader.h"
#include "pdfvisitor.h"
#include <QFileDialog>
#include <QMessageBox>
@ -30,6 +31,8 @@ void PDFViewerMainWindow::onActionOpenTriggered()
{
pdf::PDFDocumentReader reader;
pdf::PDFDocument document = reader.readFromFile(fileName);
pdf::PDFStatisticsCollector collector;
pdf::PDFApplyVisitor(document, &collector);
if (reader.isSuccessfull())
{