mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-02-19 05:00:49 +01:00
Visitor for performance measures
This commit is contained in:
parent
670a260265
commit
26a2a8deb5
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE QtCreatorProject>
|
<!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>
|
<qtcreator>
|
||||||
<data>
|
<data>
|
||||||
<variable>EnvironmentId</variable>
|
<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.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.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="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.ActiveDeployConfiguration">0</value>
|
||||||
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">1</value>
|
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">1</value>
|
||||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
|
<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.CommandLineArguments"></value>
|
||||||
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.ProFile">UnitTests/UnitTests.pro</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"></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="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
|
||||||
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
|
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
|
||||||
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</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.CommandLineArguments"></value>
|
||||||
<value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.ProFile">PdfForQtViewer/PdfForQtViewer.pro</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"></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="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
|
||||||
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
|
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
|
||||||
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||||
|
@ -40,7 +40,8 @@ SOURCES += \
|
|||||||
sources/pdfparser.cpp \
|
sources/pdfparser.cpp \
|
||||||
sources/pdfdocument.cpp \
|
sources/pdfdocument.cpp \
|
||||||
sources/pdfdocumentreader.cpp \
|
sources/pdfdocumentreader.cpp \
|
||||||
sources/pdfxreftable.cpp
|
sources/pdfxreftable.cpp \
|
||||||
|
sources/pdfvisitor.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
sources/pdfobject.h \
|
sources/pdfobject.h \
|
||||||
@ -50,7 +51,8 @@ HEADERS += \
|
|||||||
sources/pdfdocument.h \
|
sources/pdfdocument.h \
|
||||||
sources/pdfdocumentreader.h \
|
sources/pdfdocumentreader.h \
|
||||||
sources/pdfxreftable.h \
|
sources/pdfxreftable.h \
|
||||||
sources/pdfflatmap.h
|
sources/pdfflatmap.h \
|
||||||
|
sources/pdfvisitor.h
|
||||||
|
|
||||||
unix {
|
unix {
|
||||||
target.path = /usr/lib
|
target.path = /usr/lib
|
||||||
|
@ -38,7 +38,6 @@ public:
|
|||||||
constexpr inline PDFObjectStorage& operator=(const PDFObjectStorage&) = default;
|
constexpr inline PDFObjectStorage& operator=(const PDFObjectStorage&) = default;
|
||||||
constexpr inline PDFObjectStorage& operator=(PDFObjectStorage&&) = default;
|
constexpr inline PDFObjectStorage& operator=(PDFObjectStorage&&) = default;
|
||||||
|
|
||||||
|
|
||||||
struct Entry
|
struct Entry
|
||||||
{
|
{
|
||||||
constexpr inline explicit Entry() = default;
|
constexpr inline explicit Entry() = default;
|
||||||
@ -50,16 +49,41 @@ public:
|
|||||||
|
|
||||||
using PDFObjects = std::vector<Entry>;
|
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:
|
private:
|
||||||
PDFObjects m_pdfObjects;
|
PDFObjects m_objects;
|
||||||
|
PDFObject m_trailerDictionary;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// PDF document main class.
|
||||||
class PDFDocument
|
class PDFDocument
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit PDFDocument() = default;
|
explicit PDFDocument() = default;
|
||||||
|
|
||||||
|
const PDFObjectStorage& getStorage() const { return m_pdfObjectStorage; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend class PDFDocumentReader;
|
||||||
|
|
||||||
|
explicit PDFDocument(PDFObjectStorage&& storage) :
|
||||||
|
m_pdfObjectStorage(std::move(storage))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/// Storage of objects
|
/// Storage of objects
|
||||||
PDFObjectStorage m_pdfObjectStorage;
|
PDFObjectStorage m_pdfObjectStorage;
|
||||||
};
|
};
|
||||||
|
@ -260,6 +260,9 @@ PDFDocument PDFDocumentReader::readFromBuffer(const QByteArray& buffer)
|
|||||||
|
|
||||||
// Now, we are ready to scan all objects
|
// Now, we are ready to scan all objects
|
||||||
std::for_each(std::execution::parallel_policy(), occupiedEntries.cbegin(), occupiedEntries.cend(), processEntry);
|
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)
|
catch (PDFParserException parserException)
|
||||||
{
|
{
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "pdfobject.h"
|
#include "pdfobject.h"
|
||||||
|
#include "pdfvisitor.h"
|
||||||
|
|
||||||
namespace pdf
|
namespace pdf
|
||||||
{
|
{
|
||||||
@ -30,7 +31,7 @@ QByteArray PDFObject::getString() const
|
|||||||
return string->getString();
|
return string->getString();
|
||||||
}
|
}
|
||||||
|
|
||||||
const PDFDictionary*PDFObject::getDictionary() const
|
const PDFDictionary* PDFObject::getDictionary() const
|
||||||
{
|
{
|
||||||
const PDFObjectContentPointer& objectContent = std::get<PDFObjectContentPointer>(m_data);
|
const PDFObjectContentPointer& objectContent = std::get<PDFObjectContentPointer>(m_data);
|
||||||
|
|
||||||
@ -38,6 +39,30 @@ const PDFDictionary*PDFObject::getDictionary() const
|
|||||||
return static_cast<const PDFDictionary*>(objectContent.get());
|
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
|
bool PDFObject::operator==(const PDFObject &other) const
|
||||||
{
|
{
|
||||||
if (m_type == other.m_type)
|
if (m_type == other.m_type)
|
||||||
@ -61,6 +86,55 @@ bool PDFObject::operator==(const PDFObject &other) const
|
|||||||
return false;
|
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
|
bool PDFString::equals(const PDFObjectContent* other) const
|
||||||
{
|
{
|
||||||
Q_ASSERT(dynamic_cast<const PDFString*>(other));
|
Q_ASSERT(dynamic_cast<const PDFString*>(other));
|
||||||
@ -68,11 +142,6 @@ bool PDFString::equals(const PDFObjectContent* other) const
|
|||||||
return m_string == otherString->m_string;
|
return m_string == otherString->m_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray PDFString::getString() const
|
|
||||||
{
|
|
||||||
return m_string;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PDFString::setString(const QByteArray& string)
|
void PDFString::setString(const QByteArray& string)
|
||||||
{
|
{
|
||||||
m_string = string;
|
m_string = string;
|
||||||
|
@ -26,10 +26,15 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
#include <initializer_list>
|
||||||
|
|
||||||
namespace pdf
|
namespace pdf
|
||||||
{
|
{
|
||||||
|
class PDFArray;
|
||||||
|
class PDFString;
|
||||||
|
class PDFStream;
|
||||||
class PDFDictionary;
|
class PDFDictionary;
|
||||||
|
class PDFAbstractVisitor;
|
||||||
|
|
||||||
/// This class represents a content of the PDF object. It can be
|
/// This class represents a content of the PDF object. It can be
|
||||||
/// array of objects, dictionary, content stream data, or string data.
|
/// array of objects, dictionary, content stream data, or string data.
|
||||||
@ -64,6 +69,8 @@ public:
|
|||||||
Reference
|
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;
|
typedef std::shared_ptr<PDFObjectContent> PDFObjectContentPointer;
|
||||||
|
|
||||||
// Default constructor should be constexpr
|
// Default constructor should be constexpr
|
||||||
@ -97,14 +104,22 @@ public:
|
|||||||
inline bool isStream() const { return m_type == Type::Stream; }
|
inline bool isStream() const { return m_type == Type::Stream; }
|
||||||
inline bool isReference() const { return m_type == Type::Reference; }
|
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 PDFInteger getInteger() const { return std::get<PDFInteger>(m_data); }
|
||||||
|
inline PDFReal getReal() const { return std::get<PDFReal>(m_data); }
|
||||||
QByteArray getString() const;
|
QByteArray getString() const;
|
||||||
const PDFDictionary* getDictionary() const;
|
const PDFDictionary* getDictionary() const;
|
||||||
PDFObjectReference getReference() const { return std::get<PDFObjectReference>(m_data); }
|
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;
|
||||||
bool operator!=(const PDFObject& other) const { return !(*this == other); }
|
bool operator!=(const PDFObject& other) const { return !(*this == other); }
|
||||||
|
|
||||||
|
/// Accepts the visitor
|
||||||
|
void accept(PDFAbstractVisitor* visitor) const;
|
||||||
|
|
||||||
/// Creates a null object
|
/// Creates a null object
|
||||||
static inline PDFObject createNull() { return PDFObject(); }
|
static inline PDFObject createNull() { return PDFObject(); }
|
||||||
|
|
||||||
@ -165,7 +180,7 @@ public:
|
|||||||
|
|
||||||
virtual bool equals(const PDFObjectContent* other) const override;
|
virtual bool equals(const PDFObjectContent* other) const override;
|
||||||
|
|
||||||
QByteArray getString() const;
|
const QByteArray& getString() const { return m_string; }
|
||||||
void setString(const QByteArray &getString);
|
void setString(const QByteArray &getString);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -188,6 +203,9 @@ public:
|
|||||||
/// Returns size of the array (number of elements)
|
/// Returns size of the array (number of elements)
|
||||||
size_t getCount() const { return m_objects.size(); }
|
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
|
/// Appends object to the end of object list
|
||||||
void appendItem(PDFObject object);
|
void appendItem(PDFObject object);
|
||||||
|
|
||||||
@ -233,6 +251,20 @@ public:
|
|||||||
/// \param value Value
|
/// \param value Value
|
||||||
void addEntry(QByteArray&& key, PDFObject&& value) { m_dictionary.emplace_back(std::move(key), std::move(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:
|
private:
|
||||||
/// Finds an item in the dictionary array, if the item is not in the dictionary,
|
/// Finds an item in the dictionary array, if the item is not in the dictionary,
|
||||||
/// then end iterator is returned.
|
/// then end iterator is returned.
|
||||||
|
175
PdfForQtLib/sources/pdfvisitor.cpp
Normal file
175
PdfForQtLib/sources/pdfvisitor.cpp
Normal 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
|
203
PdfForQtLib/sources/pdfvisitor.h
Normal file
203
PdfForQtLib/sources/pdfvisitor.h
Normal 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
|
@ -77,6 +77,9 @@ public:
|
|||||||
/// then free entry is returned.
|
/// then free entry is returned.
|
||||||
const Entry& getEntry(PDFObjectReference reference) const;
|
const Entry& getEntry(PDFObjectReference reference) const;
|
||||||
|
|
||||||
|
/// Returns the trailer dictionary
|
||||||
|
const PDFObject& getTrailerDictionary() const { return m_trailerDictionary; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Reference table entries
|
/// Reference table entries
|
||||||
std::vector<Entry> m_entries;
|
std::vector<Entry> m_entries;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "ui_pdfviewermainwindow.h"
|
#include "ui_pdfviewermainwindow.h"
|
||||||
|
|
||||||
#include "pdfdocumentreader.h"
|
#include "pdfdocumentreader.h"
|
||||||
|
#include "pdfvisitor.h"
|
||||||
|
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
@ -30,6 +31,8 @@ void PDFViewerMainWindow::onActionOpenTriggered()
|
|||||||
{
|
{
|
||||||
pdf::PDFDocumentReader reader;
|
pdf::PDFDocumentReader reader;
|
||||||
pdf::PDFDocument document = reader.readFromFile(fileName);
|
pdf::PDFDocument document = reader.readFromFile(fileName);
|
||||||
|
pdf::PDFStatisticsCollector collector;
|
||||||
|
pdf::PDFApplyVisitor(document, &collector);
|
||||||
|
|
||||||
if (reader.isSuccessfull())
|
if (reader.isSuccessfull())
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user