Structure tree (first part)

This commit is contained in:
Jakub Melka 2020-07-16 19:43:51 +02:00
parent 6a7005675f
commit a21f185e89
8 changed files with 571 additions and 163 deletions

View File

@ -70,6 +70,7 @@ SOURCES += \
sources/pdfsecurityhandler.cpp \
sources/pdfsignaturehandler.cpp \
sources/pdfsnapper.cpp \
sources/pdfstructuretree.cpp \
sources/pdftextlayout.cpp \
sources/pdfutils.cpp \
sources/pdfwidgettool.cpp \
@ -128,6 +129,7 @@ HEADERS += \
sources/pdfsignaturehandler.h \
sources/pdfsignaturehandler_impl.h \
sources/pdfsnapper.h \
sources/pdfstructuretree.h \
sources/pdftextlayout.h \
sources/pdfwidgettool.h \
sources/pdfwidgetutils.h \

View File

@ -25,6 +25,20 @@
namespace pdf
{
// Entries for "Info" entry in trailer dictionary
static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_TITLE = "Title";
static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_AUTHOR = "Author";
static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_SUBJECT = "Subject";
static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_KEYWORDS = "Keywords";
static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_CREATOR = "Creator";
static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_PRODUCER = "Producer";
static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_CREATION_DATE = "CreationDate";
static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_MODIFIED_DATE = "ModDate";
static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_TRAPPED = "Trapped";
static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_TRAPPED_TRUE = "True";
static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_TRAPPED_FALSE = "False";
static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_TRAPPED_UNKNOWN = "Unknown";
static constexpr const char* PDF_VIEWER_PREFERENCES_DICTIONARY = "ViewerPreferences";
static constexpr const char* PDF_VIEWER_PREFERENCES_HIDE_TOOLBAR = "HideToolbar";
static constexpr const char* PDF_VIEWER_PREFERENCES_HIDE_MENUBAR = "HideMenubar";
@ -133,6 +147,15 @@ PDFCatalog PDFCatalog::parse(const PDFObject& catalog, const PDFDocument* docume
catalogObject.m_pageMode = loader.readEnumByName(catalogDictionary->get("PageMode"), pageModes.begin(), pageModes.end(), PageMode::UseNone);
}
if (const PDFDictionary* actionDictionary = document->getDictionaryFromObject(catalogDictionary->get("AA")))
{
catalogObject.m_documentActions[WillClose] = PDFAction::parse(&document->getStorage(), actionDictionary->get("WC"));
catalogObject.m_documentActions[WillSave] = PDFAction::parse(&document->getStorage(), actionDictionary->get("WS"));
catalogObject.m_documentActions[DidSave] = PDFAction::parse(&document->getStorage(), actionDictionary->get("DS"));
catalogObject.m_documentActions[WillPrint] = PDFAction::parse(&document->getStorage(), actionDictionary->get("WP"));
catalogObject.m_documentActions[DidPrint] = PDFAction::parse(&document->getStorage(), actionDictionary->get("DP"));
}
catalogObject.m_version = loader.readNameFromDictionary(catalogDictionary, "Version");
if (const PDFDictionary* namesDictionary = document->getDictionaryFromObject(catalogDictionary->get("Names")))
@ -172,6 +195,8 @@ PDFCatalog PDFCatalog::parse(const PDFObject& catalog, const PDFDocument* docume
catalogObject.m_formObject = catalogDictionary->get("AcroForm");
catalogObject.m_extensions = PDFDeveloperExtensions::parse(catalogDictionary->get("Extensions"), document);
catalogObject.m_documentSecurityStore = PDFDocumentSecurityStore::parse(catalogDictionary->get("DSS"), document);
catalogObject.m_threads = loader.readObjectList<PDFArticleThread>(catalogDictionary->get("Threads"));
catalogObject.m_metadata = catalogDictionary->get("Metadata");
return catalogObject;
}
@ -573,4 +598,163 @@ PDFDeveloperExtensions PDFDeveloperExtensions::parse(const PDFObject& object, co
return extensions;
}
PDFDocumentInfo PDFDocumentInfo::parse(const PDFObject& object, const PDFObjectStorage* storage)
{
PDFDocumentInfo info;
if (const PDFDictionary* infoDictionary = storage->getDictionaryFromObject(object))
{
auto readTextString = [storage, infoDictionary](const char* entry, QString& fillEntry)
{
if (infoDictionary->hasKey(entry))
{
const PDFObject& stringObject = storage->getObject(infoDictionary->get(entry));
if (stringObject.isString())
{
// We have succesfully read the string, convert it according to encoding
fillEntry = PDFEncoding::convertTextString(stringObject.getString());
}
else if (!stringObject.isNull())
{
throw PDFException(PDFTranslationContext::tr("Bad format of document info entry in trailer dictionary. String expected."));
}
}
};
readTextString(PDF_DOCUMENT_INFO_ENTRY_TITLE, info.title);
readTextString(PDF_DOCUMENT_INFO_ENTRY_AUTHOR, info.author);
readTextString(PDF_DOCUMENT_INFO_ENTRY_SUBJECT, info.subject);
readTextString(PDF_DOCUMENT_INFO_ENTRY_KEYWORDS, info.keywords);
readTextString(PDF_DOCUMENT_INFO_ENTRY_CREATOR, info.creator);
readTextString(PDF_DOCUMENT_INFO_ENTRY_PRODUCER, info.producer);
auto readDate= [storage, infoDictionary](const char* entry, QDateTime& fillEntry)
{
if (infoDictionary->hasKey(entry))
{
const PDFObject& stringObject = storage->getObject(infoDictionary->get(entry));
if (stringObject.isString())
{
// We have succesfully read the string, convert it to date time
fillEntry = PDFEncoding::convertToDateTime(stringObject.getString());
if (!fillEntry.isValid())
{
throw PDFException(PDFTranslationContext::tr("Bad format of document info entry in trailer dictionary. String with date time format expected."));
}
}
else if (!stringObject.isNull())
{
throw PDFException(PDFTranslationContext::tr("Bad format of document info entry in trailer dictionary. String with date time format expected."));
}
}
};
readDate(PDF_DOCUMENT_INFO_ENTRY_CREATION_DATE, info.creationDate);
readDate(PDF_DOCUMENT_INFO_ENTRY_MODIFIED_DATE, info.modifiedDate);
if (infoDictionary->hasKey(PDF_DOCUMENT_INFO_ENTRY_TRAPPED))
{
const PDFObject& nameObject = storage->getObject(infoDictionary->get(PDF_DOCUMENT_INFO_ENTRY_TRAPPED));
if (nameObject.isName())
{
const QByteArray& name = nameObject.getString();
if (name == PDF_DOCUMENT_INFO_ENTRY_TRAPPED_TRUE)
{
info.trapped = Trapped::True;
}
else if (name == PDF_DOCUMENT_INFO_ENTRY_TRAPPED_FALSE)
{
info.trapped = Trapped::False;
}
else if (name == PDF_DOCUMENT_INFO_ENTRY_TRAPPED_UNKNOWN)
{
info.trapped = Trapped::Unknown;
}
else
{
throw PDFException(PDFTranslationContext::tr("Bad format of document info entry in trailer dictionary. Trapping information expected"));
}
}
else if (nameObject.isBool())
{
info.trapped = nameObject.getBool() ? Trapped::True : Trapped::False;
}
else
{
throw PDFException(PDFTranslationContext::tr("Bad format of document info entry in trailer dictionary. Trapping information expected"));
}
}
// Scan for extra items
constexpr const char* PREDEFINED_ITEMS[] = { PDF_DOCUMENT_INFO_ENTRY_TITLE, PDF_DOCUMENT_INFO_ENTRY_AUTHOR, PDF_DOCUMENT_INFO_ENTRY_SUBJECT,
PDF_DOCUMENT_INFO_ENTRY_KEYWORDS, PDF_DOCUMENT_INFO_ENTRY_CREATOR, PDF_DOCUMENT_INFO_ENTRY_PRODUCER,
PDF_DOCUMENT_INFO_ENTRY_CREATION_DATE, PDF_DOCUMENT_INFO_ENTRY_MODIFIED_DATE, PDF_DOCUMENT_INFO_ENTRY_TRAPPED };
for (size_t i = 0; i < infoDictionary->getCount(); ++i)
{
QByteArray key = infoDictionary->getKey(i).getString();
if (std::none_of(std::begin(PREDEFINED_ITEMS), std::end(PREDEFINED_ITEMS), [&key](const char* item) { return item == key; }))
{
const PDFObject& value = storage->getObject(infoDictionary->getValue(i));
if (value.isString())
{
const QByteArray& stringValue = value.getString();
QDateTime dateTime = PDFEncoding::convertToDateTime(stringValue);
if (dateTime.isValid())
{
info.extra[key] = dateTime;
}
else
{
info.extra[key] = PDFEncoding::convertTextString(stringValue);
}
}
}
}
}
return info;
}
PDFArticleThread PDFArticleThread::parse(const PDFObjectStorage* storage, const PDFObject& object)
{
PDFArticleThread result;
if (const PDFDictionary* dictionary = storage->getDictionaryFromObject(object))
{
PDFDocumentDataLoaderDecorator loader(storage);
PDFObjectReference firstBeadReference = loader.readReferenceFromDictionary(dictionary, "F");
std::set<PDFObjectReference> visitedBeads;
PDFObjectReference currentBead = firstBeadReference;
while (!visitedBeads.count(currentBead))
{
visitedBeads.insert(currentBead);
// Read bead
if (const PDFDictionary* beadDictionary = storage->getDictionaryFromObject(storage->getObjectByReference(currentBead)))
{
Bead bead;
bead.self = currentBead;
bead.thread = loader.readReferenceFromDictionary(beadDictionary, "T");
bead.next = loader.readReferenceFromDictionary(beadDictionary, "N");
bead.previous = loader.readReferenceFromDictionary(beadDictionary, "V");
bead.page = loader.readReferenceFromDictionary(beadDictionary, "P");
bead.rect = loader.readRectangle(beadDictionary->get("R"), QRectF());
currentBead = bead.next;
result.m_beads.push_back(bead);
}
else
{
// current bead will be the same, the cycle will break
}
}
result.m_information = PDFDocumentInfo::parse(dictionary->get("I"), storage);
result.m_metadata = loader.readReferenceFromDictionary(dictionary, "Metadata");
}
return result;
}
} // namespace pdf

View File

@ -104,6 +104,39 @@ private:
PDFInteger m_startNumber;
};
/// Info about the document. Title, Author, Keywords... It also stores "extra"
/// values, which are in info dictionary. They can be either strings, or date
/// time (QString or QDateTime).
struct PDFDocumentInfo
{
/// Indicates, that document was modified that it includes trapping information.
/// See PDF Reference 1.7, Section 10.10.5 "Trapping Support".
enum class Trapped
{
True, ///< Fully trapped
False, ///< Not yet trapped
Unknown ///< Either unknown, or it has been trapped partly, not fully
};
/// Parses info from catalog dictionary. If object cannot be parsed, or error occurs,
/// then exception is thrown. This function may throw exceptions, if error occured.
/// \param object Object containing info dictionary
/// \param storage Storage of objects
static PDFDocumentInfo parse(const PDFObject& object, const PDFObjectStorage* storage);
QString title;
QString author;
QString subject;
QString keywords;
QString creator;
QString producer;
QDateTime creationDate;
QDateTime modifiedDate;
Trapped trapped = Trapped::Unknown;
PDFVersion version;
std::map<QByteArray, QVariant> extra;
};
class PDFViewerPreferences
{
public:
@ -225,6 +258,40 @@ private:
std::map<QByteArray, SecurityStoreItem> m_VRI;
};
/// Article thread. Each thread contains beads, which can be across multiple pages.
class PDFArticleThread
{
public:
explicit inline PDFArticleThread() = default;
struct Bead
{
PDFObjectReference self;
PDFObjectReference thread;
PDFObjectReference next;
PDFObjectReference previous;
PDFObjectReference page;
QRectF rect;
};
using Beads = std::vector<Bead>;
using Information = PDFDocumentInfo;
const Beads& getBeads() const { return m_beads; }
const Information& getInformation() const { return m_information; }
const PDFObjectReference& getMetadata() const { return m_metadata; }
/// Parses article thread from object. If object cannot be parsed, or error occurs,
/// then empty object is returned.
/// \param storage Storage
/// \param object Object
static PDFArticleThread parse(const PDFObjectStorage* storage, const PDFObject& object);
private:
Beads m_beads;
Information m_information;
PDFObjectReference m_metadata;
};
/// Document extensions. Contains information about developer's extensions
/// used in document.
class PDFFORQTLIBSHARED_EXPORT PDFDeveloperExtensions
@ -268,6 +335,16 @@ public:
static constexpr const size_t INVALID_PAGE_INDEX = std::numeric_limits<size_t>::max();
enum DocumentAction
{
WillClose,
WillSave,
DidSave,
WillPrint,
DidPrint,
LastDocumentAction
};
/// Returns viewer preferences of the application
const PDFViewerPreferences* getViewerPreferences() const { return &m_viewerPreferences; }
@ -299,6 +376,9 @@ public:
const PDFObject& getFormObject() const { return m_formObject; }
const PDFDeveloperExtensions& getExtensions() const { return m_extensions; }
const PDFDocumentSecurityStore& getDocumentSecurityStore() const { return m_documentSecurityStore; }
const std::vector<PDFArticleThread>& getArticleThreads() const { return m_threads; }
const PDFAction* getDocumentAction(DocumentAction action) const { return m_documentActions.at(action).get(); }
const PDFObject& getMetadata() const { return m_metadata; }
/// Returns destination using the key. If destination with the key is not found,
/// then nullptr is returned.
@ -318,12 +398,15 @@ private:
PDFOptionalContentProperties m_optionalContentProperties;
QSharedPointer<PDFOutlineItem> m_outlineRoot;
PDFActionPtr m_openAction;
std::array<PDFActionPtr, LastDocumentAction> m_documentActions;
PageLayout m_pageLayout = PageLayout::SinglePage;
PageMode m_pageMode = PageMode::UseNone;
QByteArray m_baseURI;
PDFObject m_formObject;
PDFDeveloperExtensions m_extensions;
PDFDocumentSecurityStore m_documentSecurityStore;
std::vector<PDFArticleThread> m_threads;
PDFObject m_metadata;
// Maps from Names dictionary
std::map<QByteArray, PDFDestination> m_destinations;

View File

@ -25,20 +25,7 @@
namespace pdf
{
// Entries for "Info" entry in trailer dictionary
static constexpr const char* PDF_DOCUMENT_INFO_ENTRY = "Info";
static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_TITLE = "Title";
static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_AUTHOR = "Author";
static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_SUBJECT = "Subject";
static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_KEYWORDS = "Keywords";
static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_CREATOR = "Creator";
static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_PRODUCER = "Producer";
static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_CREATION_DATE = "CreationDate";
static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_MODIFIED_DATE = "ModDate";
static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_TRAPPED = "Trapped";
static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_TRAPPED_TRUE = "True";
static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_TRAPPED_FALSE = "False";
static constexpr const char* PDF_DOCUMENT_INFO_ENTRY_TRAPPED_UNKNOWN = "Unknown";
QByteArray PDFObjectStorage::getDecodedStream(const PDFStream* stream) const
{
@ -110,123 +97,7 @@ void PDFDocument::initInfo()
if (dictionary->hasKey(PDF_DOCUMENT_INFO_ENTRY))
{
const PDFObject& info = getObject(dictionary->get(PDF_DOCUMENT_INFO_ENTRY));
if (info.isDictionary())
{
const PDFDictionary* infoDictionary = info.getDictionary();
Q_ASSERT(infoDictionary);
auto readTextString = [this, infoDictionary](const char* entry, QString& fillEntry)
{
if (infoDictionary->hasKey(entry))
{
const PDFObject& stringObject = getObject(infoDictionary->get(entry));
if (stringObject.isString())
{
// We have succesfully read the string, convert it according to encoding
fillEntry = PDFEncoding::convertTextString(stringObject.getString());
}
else if (!stringObject.isNull())
{
throw PDFException(tr("Bad format of document info entry in trailer dictionary. String expected."));
}
}
};
readTextString(PDF_DOCUMENT_INFO_ENTRY_TITLE, m_info.title);
readTextString(PDF_DOCUMENT_INFO_ENTRY_AUTHOR, m_info.author);
readTextString(PDF_DOCUMENT_INFO_ENTRY_SUBJECT, m_info.subject);
readTextString(PDF_DOCUMENT_INFO_ENTRY_KEYWORDS, m_info.keywords);
readTextString(PDF_DOCUMENT_INFO_ENTRY_CREATOR, m_info.creator);
readTextString(PDF_DOCUMENT_INFO_ENTRY_PRODUCER, m_info.producer);
auto readDate= [this, infoDictionary](const char* entry, QDateTime& fillEntry)
{
if (infoDictionary->hasKey(entry))
{
const PDFObject& stringObject = getObject(infoDictionary->get(entry));
if (stringObject.isString())
{
// We have succesfully read the string, convert it to date time
fillEntry = PDFEncoding::convertToDateTime(stringObject.getString());
if (!fillEntry.isValid())
{
throw PDFException(tr("Bad format of document info entry in trailer dictionary. String with date time format expected."));
}
}
else if (!stringObject.isNull())
{
throw PDFException(tr("Bad format of document info entry in trailer dictionary. String with date time format expected."));
}
}
};
readDate(PDF_DOCUMENT_INFO_ENTRY_CREATION_DATE, m_info.creationDate);
readDate(PDF_DOCUMENT_INFO_ENTRY_MODIFIED_DATE, m_info.modifiedDate);
if (infoDictionary->hasKey(PDF_DOCUMENT_INFO_ENTRY_TRAPPED))
{
const PDFObject& nameObject = getObject(infoDictionary->get(PDF_DOCUMENT_INFO_ENTRY_TRAPPED));
if (nameObject.isName())
{
const QByteArray& name = nameObject.getString();
if (name == PDF_DOCUMENT_INFO_ENTRY_TRAPPED_TRUE)
{
m_info.trapped = Info::Trapped::True;
}
else if (name == PDF_DOCUMENT_INFO_ENTRY_TRAPPED_FALSE)
{
m_info.trapped = Info::Trapped::False;
}
else if (name == PDF_DOCUMENT_INFO_ENTRY_TRAPPED_UNKNOWN)
{
m_info.trapped = Info::Trapped::Unknown;
}
else
{
throw PDFException(tr("Bad format of document info entry in trailer dictionary. Trapping information expected"));
}
}
else if (nameObject.isBool())
{
m_info.trapped = nameObject.getBool() ? Info::Trapped::True : Info::Trapped::False;
}
else
{
throw PDFException(tr("Bad format of document info entry in trailer dictionary. Trapping information expected"));
}
}
// Scan for extra items
constexpr const char* PREDEFINED_ITEMS[] = { PDF_DOCUMENT_INFO_ENTRY_TITLE, PDF_DOCUMENT_INFO_ENTRY_AUTHOR, PDF_DOCUMENT_INFO_ENTRY_SUBJECT,
PDF_DOCUMENT_INFO_ENTRY_KEYWORDS, PDF_DOCUMENT_INFO_ENTRY_CREATOR, PDF_DOCUMENT_INFO_ENTRY_PRODUCER,
PDF_DOCUMENT_INFO_ENTRY_CREATION_DATE, PDF_DOCUMENT_INFO_ENTRY_MODIFIED_DATE, PDF_DOCUMENT_INFO_ENTRY_TRAPPED };
for (size_t i = 0; i < infoDictionary->getCount(); ++i)
{
QByteArray key = infoDictionary->getKey(i).getString();
if (std::none_of(std::begin(PREDEFINED_ITEMS), std::end(PREDEFINED_ITEMS), [&key](const char* item) { return item == key; }))
{
const PDFObject& value = getObject(infoDictionary->getValue(i));
if (value.isString())
{
const QByteArray& stringValue = value.getString();
QDateTime dateTime = PDFEncoding::convertToDateTime(stringValue);
if (dateTime.isValid())
{
m_info.extra[key] = dateTime;
}
else
{
m_info.extra[key] = PDFEncoding::convertTextString(stringValue);
}
}
}
}
}
else if (!info.isNull()) // Info may be invalid...
{
throw PDFException(tr("Bad format of document info entry in trailer dictionary."));
}
m_info = PDFDocumentInfo::parse(dictionary->get(PDF_DOCUMENT_INFO_ENTRY), &m_pdfObjectStorage);
}
}

View File

@ -408,35 +408,8 @@ public:
const PDFObjectStorage& getStorage() const { return m_pdfObjectStorage; }
/// Info about the document. Title, Author, Keywords... It also stores "extra"
/// values, which are in info dictionary. They can be either strings, or date
/// time (QString or QDateTime).
struct Info
{
/// Indicates, that document was modified that it includes trapping information.
/// See PDF Reference 1.7, Section 10.10.5 "Trapping Support".
enum class Trapped
{
True, ///< Fully trapped
False, ///< Not yet trapped
Unknown ///< Either unknown, or it has been trapped partly, not fully
};
QString title;
QString author;
QString subject;
QString keywords;
QString creator;
QString producer;
QDateTime creationDate;
QDateTime modifiedDate;
Trapped trapped = Trapped::Unknown;
PDFVersion version;
std::map<QByteArray, QVariant> extra;
};
/// Returns info about the document (title, author, etc.)
const Info* getInfo() const { return &m_info; }
const PDFDocumentInfo* getInfo() const { return &m_info; }
/// If object is reference, the dereference attempt is performed
/// and object is returned. If it is not a reference, then self
@ -494,7 +467,7 @@ private:
PDFObjectStorage m_pdfObjectStorage;
/// Info about the PDF document
Info m_info;
PDFDocumentInfo m_info;
/// Catalog object
PDFCatalog m_catalog;

View File

@ -0,0 +1,163 @@
// Copyright (C) 2019-2020 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 "pdfstructuretree.h"
#include <array>
namespace pdf
{
/// Attribute definition structure
struct PDFStructureTreeAttributeDefinition
{
constexpr inline PDFStructureTreeAttributeDefinition() = default;
constexpr inline PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute type,
const char* name,
bool inheritable) :
type(type),
name(name),
inheritable(inheritable)
{
}
/// Returns attribute definition for given attribute name. This function
/// always returns valid pointer. For uknown attribute, it returns
/// user attribute definition.
/// \param name Attribute name
const PDFStructureTreeAttributeDefinition* getDefinition(const QByteArray& name);
PDFStructureTreeAttribute::Owner getOwnerFromString(const QByteArray& string);
PDFStructureTreeAttribute::Attribute type = PDFStructureTreeAttribute::Attribute::User;
const char* name = nullptr;
bool inheritable = false;
};
static constexpr std::array<std::pair<const char*, const PDFStructureTreeAttribute::Owner>, 16> s_ownerDefinitions =
{
std::pair<const char*, const PDFStructureTreeAttribute::Owner>("Layout", PDFStructureTreeAttribute::Owner::Layout),
std::pair<const char*, const PDFStructureTreeAttribute::Owner>("List", PDFStructureTreeAttribute::Owner::List),
std::pair<const char*, const PDFStructureTreeAttribute::Owner>("PrintField", PDFStructureTreeAttribute::Owner::PrintField),
std::pair<const char*, const PDFStructureTreeAttribute::Owner>("Table", PDFStructureTreeAttribute::Owner::Table),
std::pair<const char*, const PDFStructureTreeAttribute::Owner>("Artifact", PDFStructureTreeAttribute::Owner::Artifact),
std::pair<const char*, const PDFStructureTreeAttribute::Owner>("XML-1.00", PDFStructureTreeAttribute::Owner::XML_1_00),
std::pair<const char*, const PDFStructureTreeAttribute::Owner>("HTML-3.20", PDFStructureTreeAttribute::Owner::HTML_3_20),
std::pair<const char*, const PDFStructureTreeAttribute::Owner>("HTML-4.01", PDFStructureTreeAttribute::Owner::HTML_4_01),
std::pair<const char*, const PDFStructureTreeAttribute::Owner>("HTML-5.00", PDFStructureTreeAttribute::Owner::HTML_5_00),
std::pair<const char*, const PDFStructureTreeAttribute::Owner>("OEB-1.00", PDFStructureTreeAttribute::Owner::OEB_1_00),
std::pair<const char*, const PDFStructureTreeAttribute::Owner>("RTF-1.05", PDFStructureTreeAttribute::Owner::RTF_1_05),
std::pair<const char*, const PDFStructureTreeAttribute::Owner>("CSS-1.00", PDFStructureTreeAttribute::Owner::CSS_1_00),
std::pair<const char*, const PDFStructureTreeAttribute::Owner>("CSS-2.00", PDFStructureTreeAttribute::Owner::CSS_2_00),
std::pair<const char*, const PDFStructureTreeAttribute::Owner>("CSS-3.00", PDFStructureTreeAttribute::Owner::CSS_3_00),
std::pair<const char*, const PDFStructureTreeAttribute::Owner>("RDFa-1.10", PDFStructureTreeAttribute::Owner::RDFa_1_10),
std::pair<const char*, const PDFStructureTreeAttribute::Owner>("ARIA-1.1", PDFStructureTreeAttribute::Owner::ARIA_1_1)
};
static constexpr std::array<const PDFStructureTreeAttributeDefinition, PDFStructureTreeAttribute::Attribute::LastAttribute + 1> s_attributeDefinitions =
{
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::User, "", false), // User
// Standard layout attributes
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::Placement, "Placement", false),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::WritingMode, "WritingMode", true),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::BackgroundColor, "BackgroundColor", false),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::BorderColor, "BorderColor", true),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::BorderStyle, "BorderStyle", false),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::BorderThickness, "BorderThickness", true),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::Color, "Color", true),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::Padding, "Padding", false),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::SpaceBefore, "SpaceBefore", false),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::SpaceAfter, "SpaceAfter", false),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::StartIndent, "StartIndent", true),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::EndIndent, "EndIndent", true),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::TextIndent, "TextIndent", true),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::TextAlign, "TextAlign", true),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::BBox, "BBox", false),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::Width, "Width", false),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::Height, "Height", false),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::BlockAlign, "BlockAlign", true),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::InlineAlign, "InlineAlign", true),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::TBorderStyle, "TBorderStyle", true),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::TPadding, "TPadding", true),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::LineHeight, "LineHeight", true),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::BaselineShift, "BaselineShift", false),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::TextDecorationType, "TextDecorationType", true),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::TextDecorationColor, "TextDecorationColor", true),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::TextDecorationThickness, "TextDecorationThickness", true),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::ColumnCount, "ColumnCount", false),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::ColumnWidths, "ColumnWidths", false),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::ColumnGap, "ColumnGap", false),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::GlyphOrientationVertical, "GlyphOrientationVertical", true),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::RubyAlign, "RubyAlign", true),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::RubyPosition, "RubyPosition", true),
// List attributes
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::ListNumbering, "ListNumbering", true),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::ContinuedList, "ContinuedList", false),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::ContinuedFrom, "ContinuedFrom", false),
// PrintField attributes
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::Role, "Role", false),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::Checked, "Checked", false),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::Checked, "checked", false),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::Desc, "Desc", false),
// Table attributes
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::RowSpan, "RowSpan", false),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::ColSpan, "ColSpan", false),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::Headers, "Headers", false),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::Scope, "Scope", false),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::Short, "Short", false),
// Artifact attributes
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::Type, "Type", false),
PDFStructureTreeAttributeDefinition(PDFStructureTreeAttribute::Attribute::Subtype, "Subtype", false)
};
const PDFStructureTreeAttributeDefinition* PDFStructureTreeAttributeDefinition::getDefinition(const QByteArray& name)
{
for (const PDFStructureTreeAttributeDefinition& definition : s_attributeDefinitions)
{
if (name == definition.name)
{
return &definition;
}
}
Q_ASSERT(s_attributeDefinitions.front().type == PDFStructureTreeAttribute::Attribute::User);
return &s_attributeDefinitions.front();
}
PDFStructureTreeAttribute::Owner PDFStructureTreeAttributeDefinition::getOwnerFromString(const QByteArray& string)
{
for (const auto& item : s_ownerDefinitions)
{
if (string == item.first)
{
return item.second;
}
}
return PDFStructureTreeAttribute::Owner::User;
}
} // namespace pdf

View File

@ -0,0 +1,132 @@
// Copyright (C) 2019-2020 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 PDFSTRUCTURETREE_H
#define PDFSTRUCTURETREE_H
#include "pdfobject.h"
namespace pdf
{
struct PDFStructureTreeAttributeDefinition;
class PDFStructureTreeAttribute
{
public:
enum class Owner
{
/// Defined for user owner
User,
Layout,
List,
PrintField,
Table,
Artifact,
XML_1_00,
HTML_3_20,
HTML_4_01,
HTML_5_00,
OEB_1_00,
RTF_1_05,
CSS_1_00,
CSS_2_00,
CSS_3_00,
RDFa_1_10,
ARIA_1_1,
};
enum Attribute
{
User,
// Standard layout attributes
Placement,
WritingMode,
BackgroundColor,
BorderColor,
BorderStyle,
BorderThickness,
Color,
Padding,
// Block element attributes
SpaceBefore,
SpaceAfter,
StartIndent,
EndIndent,
TextIndent,
TextAlign,
BBox,
Width,
Height,
BlockAlign,
InlineAlign,
TBorderStyle,
TPadding,
LineHeight,
BaselineShift,
TextDecorationType,
TextDecorationColor,
TextDecorationThickness,
ColumnCount,
ColumnWidths,
ColumnGap,
GlyphOrientationVertical,
RubyAlign,
RubyPosition,
// List attributes
ListNumbering,
ContinuedList,
ContinuedFrom,
// PrintField attributes
Role,
Checked,
Desc,
// Table attributes
RowSpan,
ColSpan,
Headers,
Scope,
Short,
// Artifact attributes
Type,
Subtype,
LastAttribute
};
private:
};
class PDFStructureTree
{
public:
PDFStructureTree();
};
} // namespace pdf
#endif // PDFSTRUCTURETREE_H

View File

@ -70,7 +70,7 @@ void PDFDocumentPropertiesDialog::initializeProperties(const pdf::PDFDocument* d
// Initialize document properties
QTreeWidgetItem* propertiesRoot = new QTreeWidgetItem({ tr("Properties") });
const pdf::PDFDocument::Info* info = document->getInfo();
const pdf::PDFDocumentInfo* info = document->getInfo();
const pdf::PDFCatalog* catalog = document->getCatalog();
new QTreeWidgetItem(propertiesRoot, { tr("PDF version"), QString::fromLatin1(document->getVersion()) });
new QTreeWidgetItem(propertiesRoot, { tr("Title"), info->title });
@ -85,15 +85,15 @@ void PDFDocumentPropertiesDialog::initializeProperties(const pdf::PDFDocument* d
QString trapped;
switch (info->trapped)
{
case pdf::PDFDocument::Info::Trapped::True:
case pdf::PDFDocumentInfo::Trapped::True:
trapped = tr("True");
break;
case pdf::PDFDocument::Info::Trapped::False:
case pdf::PDFDocumentInfo::Trapped::False:
trapped = tr("False");
break;
case pdf::PDFDocument::Info::Trapped::Unknown:
case pdf::PDFDocumentInfo::Trapped::Unknown:
trapped = tr("Unknown");
break;