mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-01-30 09:04:48 +01:00
Structure tree (first part)
This commit is contained in:
parent
6a7005675f
commit
a21f185e89
@ -70,6 +70,7 @@ SOURCES += \
|
|||||||
sources/pdfsecurityhandler.cpp \
|
sources/pdfsecurityhandler.cpp \
|
||||||
sources/pdfsignaturehandler.cpp \
|
sources/pdfsignaturehandler.cpp \
|
||||||
sources/pdfsnapper.cpp \
|
sources/pdfsnapper.cpp \
|
||||||
|
sources/pdfstructuretree.cpp \
|
||||||
sources/pdftextlayout.cpp \
|
sources/pdftextlayout.cpp \
|
||||||
sources/pdfutils.cpp \
|
sources/pdfutils.cpp \
|
||||||
sources/pdfwidgettool.cpp \
|
sources/pdfwidgettool.cpp \
|
||||||
@ -128,6 +129,7 @@ HEADERS += \
|
|||||||
sources/pdfsignaturehandler.h \
|
sources/pdfsignaturehandler.h \
|
||||||
sources/pdfsignaturehandler_impl.h \
|
sources/pdfsignaturehandler_impl.h \
|
||||||
sources/pdfsnapper.h \
|
sources/pdfsnapper.h \
|
||||||
|
sources/pdfstructuretree.h \
|
||||||
sources/pdftextlayout.h \
|
sources/pdftextlayout.h \
|
||||||
sources/pdfwidgettool.h \
|
sources/pdfwidgettool.h \
|
||||||
sources/pdfwidgetutils.h \
|
sources/pdfwidgetutils.h \
|
||||||
|
@ -25,6 +25,20 @@
|
|||||||
namespace pdf
|
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_DICTIONARY = "ViewerPreferences";
|
||||||
static constexpr const char* PDF_VIEWER_PREFERENCES_HIDE_TOOLBAR = "HideToolbar";
|
static constexpr const char* PDF_VIEWER_PREFERENCES_HIDE_TOOLBAR = "HideToolbar";
|
||||||
static constexpr const char* PDF_VIEWER_PREFERENCES_HIDE_MENUBAR = "HideMenubar";
|
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);
|
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");
|
catalogObject.m_version = loader.readNameFromDictionary(catalogDictionary, "Version");
|
||||||
|
|
||||||
if (const PDFDictionary* namesDictionary = document->getDictionaryFromObject(catalogDictionary->get("Names")))
|
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_formObject = catalogDictionary->get("AcroForm");
|
||||||
catalogObject.m_extensions = PDFDeveloperExtensions::parse(catalogDictionary->get("Extensions"), document);
|
catalogObject.m_extensions = PDFDeveloperExtensions::parse(catalogDictionary->get("Extensions"), document);
|
||||||
catalogObject.m_documentSecurityStore = PDFDocumentSecurityStore::parse(catalogDictionary->get("DSS"), 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;
|
return catalogObject;
|
||||||
}
|
}
|
||||||
@ -573,4 +598,163 @@ PDFDeveloperExtensions PDFDeveloperExtensions::parse(const PDFObject& object, co
|
|||||||
return extensions;
|
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
|
} // namespace pdf
|
||||||
|
@ -104,6 +104,39 @@ private:
|
|||||||
PDFInteger m_startNumber;
|
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
|
class PDFViewerPreferences
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -225,6 +258,40 @@ private:
|
|||||||
std::map<QByteArray, SecurityStoreItem> m_VRI;
|
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
|
/// Document extensions. Contains information about developer's extensions
|
||||||
/// used in document.
|
/// used in document.
|
||||||
class PDFFORQTLIBSHARED_EXPORT PDFDeveloperExtensions
|
class PDFFORQTLIBSHARED_EXPORT PDFDeveloperExtensions
|
||||||
@ -268,6 +335,16 @@ public:
|
|||||||
|
|
||||||
static constexpr const size_t INVALID_PAGE_INDEX = std::numeric_limits<size_t>::max();
|
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
|
/// Returns viewer preferences of the application
|
||||||
const PDFViewerPreferences* getViewerPreferences() const { return &m_viewerPreferences; }
|
const PDFViewerPreferences* getViewerPreferences() const { return &m_viewerPreferences; }
|
||||||
|
|
||||||
@ -299,6 +376,9 @@ public:
|
|||||||
const PDFObject& getFormObject() const { return m_formObject; }
|
const PDFObject& getFormObject() const { return m_formObject; }
|
||||||
const PDFDeveloperExtensions& getExtensions() const { return m_extensions; }
|
const PDFDeveloperExtensions& getExtensions() const { return m_extensions; }
|
||||||
const PDFDocumentSecurityStore& getDocumentSecurityStore() const { return m_documentSecurityStore; }
|
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,
|
/// Returns destination using the key. If destination with the key is not found,
|
||||||
/// then nullptr is returned.
|
/// then nullptr is returned.
|
||||||
@ -318,12 +398,15 @@ private:
|
|||||||
PDFOptionalContentProperties m_optionalContentProperties;
|
PDFOptionalContentProperties m_optionalContentProperties;
|
||||||
QSharedPointer<PDFOutlineItem> m_outlineRoot;
|
QSharedPointer<PDFOutlineItem> m_outlineRoot;
|
||||||
PDFActionPtr m_openAction;
|
PDFActionPtr m_openAction;
|
||||||
|
std::array<PDFActionPtr, LastDocumentAction> m_documentActions;
|
||||||
PageLayout m_pageLayout = PageLayout::SinglePage;
|
PageLayout m_pageLayout = PageLayout::SinglePage;
|
||||||
PageMode m_pageMode = PageMode::UseNone;
|
PageMode m_pageMode = PageMode::UseNone;
|
||||||
QByteArray m_baseURI;
|
QByteArray m_baseURI;
|
||||||
PDFObject m_formObject;
|
PDFObject m_formObject;
|
||||||
PDFDeveloperExtensions m_extensions;
|
PDFDeveloperExtensions m_extensions;
|
||||||
PDFDocumentSecurityStore m_documentSecurityStore;
|
PDFDocumentSecurityStore m_documentSecurityStore;
|
||||||
|
std::vector<PDFArticleThread> m_threads;
|
||||||
|
PDFObject m_metadata;
|
||||||
|
|
||||||
// Maps from Names dictionary
|
// Maps from Names dictionary
|
||||||
std::map<QByteArray, PDFDestination> m_destinations;
|
std::map<QByteArray, PDFDestination> m_destinations;
|
||||||
|
@ -25,20 +25,7 @@
|
|||||||
namespace pdf
|
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 = "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
|
QByteArray PDFObjectStorage::getDecodedStream(const PDFStream* stream) const
|
||||||
{
|
{
|
||||||
@ -110,123 +97,7 @@ void PDFDocument::initInfo()
|
|||||||
|
|
||||||
if (dictionary->hasKey(PDF_DOCUMENT_INFO_ENTRY))
|
if (dictionary->hasKey(PDF_DOCUMENT_INFO_ENTRY))
|
||||||
{
|
{
|
||||||
const PDFObject& info = getObject(dictionary->get(PDF_DOCUMENT_INFO_ENTRY));
|
m_info = PDFDocumentInfo::parse(dictionary->get(PDF_DOCUMENT_INFO_ENTRY), &m_pdfObjectStorage);
|
||||||
|
|
||||||
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."));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -408,35 +408,8 @@ public:
|
|||||||
|
|
||||||
const PDFObjectStorage& getStorage() const { return m_pdfObjectStorage; }
|
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.)
|
/// 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
|
/// If object is reference, the dereference attempt is performed
|
||||||
/// and object is returned. If it is not a reference, then self
|
/// and object is returned. If it is not a reference, then self
|
||||||
@ -494,7 +467,7 @@ private:
|
|||||||
PDFObjectStorage m_pdfObjectStorage;
|
PDFObjectStorage m_pdfObjectStorage;
|
||||||
|
|
||||||
/// Info about the PDF document
|
/// Info about the PDF document
|
||||||
Info m_info;
|
PDFDocumentInfo m_info;
|
||||||
|
|
||||||
/// Catalog object
|
/// Catalog object
|
||||||
PDFCatalog m_catalog;
|
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
|
// Initialize document properties
|
||||||
QTreeWidgetItem* propertiesRoot = new QTreeWidgetItem({ tr("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();
|
const pdf::PDFCatalog* catalog = document->getCatalog();
|
||||||
new QTreeWidgetItem(propertiesRoot, { tr("PDF version"), QString::fromLatin1(document->getVersion()) });
|
new QTreeWidgetItem(propertiesRoot, { tr("PDF version"), QString::fromLatin1(document->getVersion()) });
|
||||||
new QTreeWidgetItem(propertiesRoot, { tr("Title"), info->title });
|
new QTreeWidgetItem(propertiesRoot, { tr("Title"), info->title });
|
||||||
@ -85,15 +85,15 @@ void PDFDocumentPropertiesDialog::initializeProperties(const pdf::PDFDocument* d
|
|||||||
QString trapped;
|
QString trapped;
|
||||||
switch (info->trapped)
|
switch (info->trapped)
|
||||||
{
|
{
|
||||||
case pdf::PDFDocument::Info::Trapped::True:
|
case pdf::PDFDocumentInfo::Trapped::True:
|
||||||
trapped = tr("True");
|
trapped = tr("True");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case pdf::PDFDocument::Info::Trapped::False:
|
case pdf::PDFDocumentInfo::Trapped::False:
|
||||||
trapped = tr("False");
|
trapped = tr("False");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case pdf::PDFDocument::Info::Trapped::Unknown:
|
case pdf::PDFDocumentInfo::Trapped::Unknown:
|
||||||
trapped = tr("Unknown");
|
trapped = tr("Unknown");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user