mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-01-16 10:32:29 +01:00
Structure tree (first part)
This commit is contained in:
parent
6a7005675f
commit
a21f185e89
@ -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 \
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
163
PdfForQtLib/sources/pdfstructuretree.cpp
Normal file
163
PdfForQtLib/sources/pdfstructuretree.cpp
Normal 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
|
132
PdfForQtLib/sources/pdfstructuretree.h
Normal file
132
PdfForQtLib/sources/pdfstructuretree.h
Normal 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
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user