Actions/outline basics

This commit is contained in:
Jakub Melka 2019-11-17 17:41:07 +01:00
parent 84d9b58476
commit 2a11fa18e0
10 changed files with 998 additions and 0 deletions

View File

@ -36,12 +36,15 @@ DEFINES += QT_DEPRECATED_WARNINGS
DESTDIR = $$OUT_PWD/..
SOURCES += \
sources/pdfaction.cpp \
sources/pdfblendfunction.cpp \
sources/pdfccittfaxdecoder.cpp \
sources/pdffile.cpp \
sources/pdfitemmodels.cpp \
sources/pdfjbig2decoder.cpp \
sources/pdfobject.cpp \
sources/pdfoptionalcontent.cpp \
sources/pdfoutline.cpp \
sources/pdfparser.cpp \
sources/pdfdocument.cpp \
sources/pdfdocumentreader.cpp \
@ -68,13 +71,16 @@ SOURCES += \
sources/pdfimage.cpp
HEADERS += \
sources/pdfaction.h \
sources/pdfblendfunction.h \
sources/pdfccittfaxdecoder.h \
sources/pdffile.h \
sources/pdfitemmodels.h \
sources/pdfjbig2decoder.h \
sources/pdfmeshqualitysettings.h \
sources/pdfobject.h \
sources/pdfoptionalcontent.h \
sources/pdfoutline.h \
sources/pdfparser.h \
sources/pdfglobal.h \
sources/pdfconstants.h \

View File

@ -0,0 +1,243 @@
// Copyright (C) 2019 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 "pdfaction.h"
#include "pdfdocument.h"
#include "pdfexception.h"
#include "pdfencoding.h"
namespace pdf
{
PDFActionPtr PDFAction::parse(const PDFDocument* document, PDFObject object)
{
std::set<PDFObjectReference> usedReferences;
return parseImpl(document, qMove(object), usedReferences);
}
void PDFAction::apply(const std::function<void (const PDFAction*)>& callback)
{
callback(this);
for (const PDFActionPtr& nextAction : m_nextActions)
{
nextAction->apply(callback);
}
}
PDFActionPtr PDFAction::parseImpl(const PDFDocument* document, PDFObject object, std::set<PDFObjectReference>& usedReferences)
{
if (object.isReference())
{
PDFObjectReference reference = object.getReference();
if (usedReferences.count(reference))
{
throw PDFException(PDFTranslationContext::tr("Circular dependence in actions found."));
}
usedReferences.insert(reference);
object = document->getObjectByReference(reference);
}
if (object.isNull())
{
return PDFActionPtr();
}
if (!object.isDictionary())
{
throw PDFException(PDFTranslationContext::tr("Invalid action."));
}
PDFDocumentDataLoaderDecorator loader(document);
const PDFDictionary* dictionary = object.getDictionary();
QByteArray name = loader.readNameFromDictionary(dictionary, "S");
if (name == "GoTo") // Goto action
{
PDFDestination destination = PDFDestination::parse(document, dictionary->get("D"));
return PDFActionPtr(new PDFActionGoTo(qMove(destination)));
}
else if (name == "GoToR")
{
PDFDestination destination = PDFDestination::parse(document, dictionary->get("D"));
PDFFileSpecification fileSpecification = PDFFileSpecification::parse(document, dictionary->get("F"));
return PDFActionPtr(new PDFActionGoToR(qMove(destination), qMove(fileSpecification), loader.readBooleanFromDictionary(dictionary, "NewWindow", false)));
}
else if (name == "GoToE")
{
PDFDestination destination = PDFDestination::parse(document, dictionary->get("D"));
PDFFileSpecification fileSpecification = PDFFileSpecification::parse(document, dictionary->get("F"));
return PDFActionPtr(new PDFActionGoToE(qMove(destination), qMove(fileSpecification), loader.readBooleanFromDictionary(dictionary, "NewWindow", false), document->getObject(dictionary->get("T"))));
}
else if (name == "Launch")
{
PDFFileSpecification fileSpecification = PDFFileSpecification::parse(document, dictionary->get("F"));
const bool newWindow = loader.readBooleanFromDictionary(dictionary, "NewWindow", false);
PDFActionLaunch::Win win;
const PDFObject& winDictionaryObject = document->getObject(dictionary->get("Win"));
if (winDictionaryObject.isDictionary())
{
const PDFDictionary* winDictionary = winDictionaryObject.getDictionary();
win.file = loader.readStringFromDictionary(winDictionary, "F");
win.directory = loader.readStringFromDictionary(winDictionary, "D");
win.operation = loader.readStringFromDictionary(winDictionary, "O");
win.parameters = loader.readStringFromDictionary(winDictionary, "P");
}
return PDFActionPtr(new PDFActionLaunch(qMove(fileSpecification), newWindow, qMove(win)));
}
else if (name == "Thread")
{
PDFFileSpecification fileSpecification = PDFFileSpecification::parse(document, dictionary->get("F"));
PDFActionThread::Thread thread;
PDFActionThread::Bead bead;
const PDFObject& threadObject = dictionary->get("D");
if (threadObject.isReference())
{
thread = threadObject.getReference();
}
else if (threadObject.isInt())
{
thread = threadObject.getInteger();
}
else if (threadObject.isString())
{
thread = PDFEncoding::convertTextString(threadObject.getString());
}
const PDFObject& beadObject = dictionary->get("B");
if (beadObject.isReference())
{
bead = beadObject.getReference();
}
else if (beadObject.isInt())
{
bead = beadObject.getInteger();
}
return PDFActionPtr(new PDFActionThread(qMove(fileSpecification), qMove(thread), qMove(bead)));
}
else if (name == "URI")
{
return PDFActionPtr(new PDFActionURI(loader.readStringFromDictionary(dictionary, "URI"), loader.readBooleanFromDictionary(dictionary, "IsMap", false)));
}
return PDFActionPtr();
}
PDFDestination PDFDestination::parse(const PDFDocument* document, PDFObject object)
{
PDFDestination result;
object = document->getObject(object);
if (object.isName() || object.isString())
{
QByteArray name = object.getString();
result.m_destinationType = DestinationType::Named;
result.m_name = name;
}
else if (object.isArray())
{
const PDFArray* array = object.getArray();
if (array->getCount() < 2)
{
throw PDFException(PDFTranslationContext::tr("Invalid destination - array has invalid size."));
}
PDFDocumentDataLoaderDecorator loader(document);
// First parse page number/page index
PDFObject pageNumberObject = array->getItem(0);
if (pageNumberObject.isReference())
{
result.m_pageReference = pageNumberObject.getReference();
}
else if (pageNumberObject.isInt())
{
result.m_pageIndex = pageNumberObject.getInteger();
}
else
{
throw PDFException(PDFTranslationContext::tr("Invalid destination - invalid page reference."));
}
QByteArray name = loader.readName(array->getItem(1));
size_t currentIndex = 2;
auto readNumber = [&]()
{
if (currentIndex >= array->getCount())
{
throw PDFException(PDFTranslationContext::tr("Invalid destination - array has invalid size."));
}
return loader.readNumber(array->getItem(currentIndex++), 0.0);
};
if (name == "XYZ")
{
result.m_destinationType = DestinationType::XYZ;
result.m_left = readNumber();
result.m_top = readNumber();
result.m_zoom = readNumber();
}
else if (name == "Fit")
{
result.m_destinationType = DestinationType::Fit;
}
else if (name == "FitH")
{
result.m_destinationType = DestinationType::FitH;
result.m_top = readNumber();
}
else if (name == "FitV")
{
result.m_destinationType = DestinationType::FitV;
result.m_left = readNumber();
}
else if (name == "FitR")
{
result.m_destinationType = DestinationType::FitR;
result.m_left = readNumber();
result.m_bottom = readNumber();
result.m_right = readNumber();
result.m_top = readNumber();
}
else if (name == "FitB")
{
result.m_destinationType = DestinationType::FitB;
}
else if (name == "FitBH")
{
result.m_destinationType = DestinationType::FitBH;
result.m_top = readNumber();
}
else if (name == "FitBV")
{
result.m_destinationType = DestinationType::FitBV;
result.m_left = readNumber();
}
else
{
throw PDFException(PDFTranslationContext::tr("Invalid destination - unknown type."));
}
}
return result;
}
} // namespace pdf

View File

@ -0,0 +1,269 @@
// Copyright (C) 2019 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 PDFACTION_H
#define PDFACTION_H
#include "pdfglobal.h"
#include "pdfobject.h"
#include "pdffile.h"
#include <QSharedPointer>
#include <set>
#include <variant>
#include <functional>
namespace pdf
{
class PDFAction;
class PDFDocument;
enum class ActionType
{
GoTo,
GoToR,
GoToE,
Launch,
Thread,
URI
};
enum class DestinationType
{
Invalid,
Named,
XYZ,
Fit,
FitH,
FitV,
FitR,
FitB,
FitBH,
FitBV
};
/// Destination to the specific location of the document. Destination can also be 'Named' type,
/// in this case, destination in name tree is found and used.
class PDFDestination
{
public:
explicit inline PDFDestination() = default;
DestinationType getDestinationType() const { return m_destinationType; }
PDFReal getLeft() const { return m_left; }
PDFReal getTop() const { return m_top; }
PDFReal getRight() const { return m_right; }
PDFReal getBottom() const { return m_bottom; }
PDFReal getZoom() const { return m_zoom; }
const QByteArray& getName() const { return m_name; }
PDFObjectReference getPageReference() const { return m_pageReference; }
PDFInteger getPageIndex() const { return m_pageIndex; }
/// Parses the destination from the object. If object contains invalid destination,
/// then exception is thrown. If object is empty, empty destination is returned.
/// \param document Document
/// \param object Destination object
static PDFDestination parse(const PDFDocument* document, PDFObject object);
private:
DestinationType m_destinationType = DestinationType::Invalid;
PDFReal m_left = 0.0;
PDFReal m_top = 0.0;
PDFReal m_right = 0.0;
PDFReal m_bottom = 0.0;
PDFReal m_zoom = 0.0;
QByteArray m_name;
PDFObjectReference m_pageReference;
PDFInteger m_pageIndex = 0;
};
using PDFActionPtr = QSharedPointer<PDFAction>;
/// Base class for action types.
class PDFAction
{
public:
explicit PDFAction() = default;
virtual ~PDFAction() = default;
/// Returns type of the action.
virtual ActionType getType() const = 0;
/// Returns container with next actions
const std::vector<PDFActionPtr>& getNextActions() const { return m_nextActions; }
/// Tries to parse the action. If serious error occurs, then exception is thrown.
/// If \p object is null object, then nullptr is returned.
/// \param document Document
/// \param object Object containing the action
static PDFActionPtr parse(const PDFDocument* document, PDFObject object);
/// Calls the lambda function with action as parameter, then following
/// the 'Next' entry, as described by PDF 1.7 specification.
void apply(const std::function<void(const PDFAction* action)>& callback);
private:
static PDFActionPtr parseImpl(const PDFDocument* document, PDFObject object, std::set<PDFObjectReference>& usedReferences);
std::vector<PDFActionPtr> m_nextActions;
};
class PDFActionGoTo : public PDFAction
{
public:
explicit inline PDFActionGoTo(PDFDestination destination) : m_destination(qMove(destination)) { }
virtual ActionType getType() const override { return ActionType::GoTo; }
const PDFDestination& getDestination() const { return m_destination; }
private:
PDFDestination m_destination;
};
class PDFActionGoToR : public PDFAction
{
public:
explicit inline PDFActionGoToR(PDFDestination destination, PDFFileSpecification fileSpecification, bool newWindow) :
m_destination(qMove(destination)),
m_fileSpecification(qMove(fileSpecification)),
m_newWindow(newWindow)
{
}
virtual ActionType getType() const override { return ActionType::GoToR; }
const PDFDestination& getDestination() const { return m_destination; }
const PDFFileSpecification& getFileSpecification() const { return m_fileSpecification; }
bool isNewWindow() const { return m_newWindow; }
private:
PDFDestination m_destination;
PDFFileSpecification m_fileSpecification;
bool m_newWindow = false;
};
class PDFActionGoToE : public PDFAction
{
public:
explicit inline PDFActionGoToE(PDFDestination destination, PDFFileSpecification fileSpecification, bool newWindow, const PDFObject& target) :
m_destination(qMove(destination)),
m_fileSpecification(qMove(fileSpecification)),
m_newWindow(newWindow),
m_target(target)
{
}
virtual ActionType getType() const override { return ActionType::GoToE; }
const PDFDestination& getDestination() const { return m_destination; }
const PDFFileSpecification& getFileSpecification() const { return m_fileSpecification; }
bool isNewWindow() const { return m_newWindow; }
const PDFObject& getTarget() const { return m_target; }
private:
PDFDestination m_destination;
PDFFileSpecification m_fileSpecification;
bool m_newWindow = false;
PDFObject m_target;
};
class PDFActionLaunch : public PDFAction
{
public:
/// Specification of launched application (if not file specification is attached)
struct Win
{
QByteArray file;
QByteArray directory;
QByteArray operation;
QByteArray parameters;
};
explicit inline PDFActionLaunch(PDFFileSpecification fileSpecification, bool newWindow, Win win) :
m_fileSpecification(qMove(fileSpecification)),
m_newWindow(newWindow),
m_win(qMove(win))
{
}
virtual ActionType getType() const override { return ActionType::Launch; }
const PDFFileSpecification& getFileSpecification() const { return m_fileSpecification; }
const Win& getWinSpecification() const { return m_win; }
bool isNewWindow() const { return m_newWindow; }
private:
PDFFileSpecification m_fileSpecification;
bool m_newWindow = false;
Win m_win;
};
class PDFActionThread : public PDFAction
{
public:
using Thread = std::variant<typename std::monostate, PDFObjectReference, PDFInteger, QString>;
using Bead = std::variant<typename std::monostate, PDFObjectReference, PDFInteger>;
explicit inline PDFActionThread(PDFFileSpecification fileSpecification, Thread thread, Bead bead) :
m_fileSpecification(qMove(fileSpecification)),
m_thread(qMove(thread)),
m_bead(qMove(bead))
{
}
virtual ActionType getType() const override { return ActionType::Thread; }
const PDFFileSpecification& getFileSpecification() const { return m_fileSpecification; }
const Thread& getThread() const { return m_thread; }
const Bead& getBead() const { return m_bead; }
private:
PDFFileSpecification m_fileSpecification;
Thread m_thread;
Bead m_bead;
};
class PDFActionURI : public PDFAction
{
public:
explicit inline PDFActionURI(QByteArray URI, bool isMap) :
m_URI(qMove(URI)),
m_isMap(isMap)
{
}
virtual ActionType getType() const override { return ActionType::URI; }
const QByteArray& getURI() const { return m_URI; }
bool isMap() const { return m_isMap; }
private:
QByteArray m_URI;
bool m_isMap;
};
} // namespace pdf
#endif // PDFACTION_H

View File

@ -62,6 +62,11 @@ PDFCatalog PDFCatalog::parse(const PDFObject& catalog, const PDFDocument* docume
catalogObject.m_optionalContentProperties = PDFOptionalContentProperties::create(document, catalogDictionary->get("OCProperties"));
}
if (catalogDictionary->hasKey("Outlines"))
{
catalogObject.m_outlineRoot = PDFOutlineItem::parse(document, catalogDictionary->get("Outlines"));
}
return catalogObject;
}

View File

@ -21,6 +21,7 @@
#include "pdfobject.h"
#include "pdfpage.h"
#include "pdfoptionalcontent.h"
#include "pdfoutline.h"
#include <QtCore>
@ -224,6 +225,7 @@ private:
std::vector<PDFPage> m_pages;
std::vector<PDFPageLabel> m_pageLabels;
PDFOptionalContentProperties m_optionalContentProperties;
QSharedPointer<PDFOutlineItem> m_outlineRoot;
};
} // namespace pdf

View File

@ -326,6 +326,10 @@ public:
/// is returned (no exception is thrown).
const PDFObject& getObject(const PDFObject& object) const;
/// Returns object by reference. If dereference attempt fails, then null object
/// is returned (no exception is thrown).
const PDFObject& getObjectByReference(PDFObjectReference reference) const;
/// Returns the document catalog
const PDFCatalog* getCatalog() const { return &m_catalog; }
@ -379,6 +383,12 @@ const PDFObject& PDFDocument::getObject(const PDFObject& object) const
return object;
}
inline
const PDFObject& PDFDocument::getObjectByReference(PDFObjectReference reference) const
{
return m_pdfObjectStorage.getObject(reference);
}
} // namespace pdf
#endif // PDFDOCUMENT_H

View File

@ -0,0 +1,183 @@
// Copyright (C) 2019 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 "pdffile.h"
#include "pdfdocument.h"
#include "pdfencoding.h"
namespace pdf
{
QString PDFFileSpecification::getPlatformFileName() const
{
// UF has maximal precedence, because it is unicode string
if (!m_UF.isEmpty())
{
return m_UF;
}
if (!m_F.isEmpty())
{
return QString::fromLatin1(m_F);
}
#ifdef Q_OS_WIN
for (const QByteArray& platformName : { m_DOS, m_Mac, m_Unix })
{
if (!platformName.isEmpty())
{
return QString::fromLatin1(platformName);
}
}
#endif
#ifdef Q_OS_UNIX
for (const QByteArray& platformName : { m_Unix, m_Mac, m_DOS })
{
if (!platformName.isEmpty())
{
return QString::fromLatin1(platformName);
}
}
#endif
#ifdef Q_OS_MAC
for (const QByteArray& platformName : { m_Mac, m_Unix, m_DOS })
{
if (!platformName.isEmpty())
{
return QString::fromLatin1(platformName);
}
}
#endif
return QString();
}
const PDFEmbeddedFile* PDFFileSpecification::getPlatformFile() const
{
if (m_embeddedFiles.count("UF"))
{
return &m_embeddedFiles.at("UF");
}
if (m_embeddedFiles.count("F"))
{
return &m_embeddedFiles.at("F");
}
#ifdef Q_OS_WIN
if (m_embeddedFiles.count("DOS"))
{
return &m_embeddedFiles.at("DOS");
}
#endif
#ifdef Q_OS_UNIX
if (m_embeddedFiles.count("Unix"))
{
return &m_embeddedFiles.at("Unix");
}
#endif
#ifdef Q_OS_MAC
if (m_embeddedFiles.count("Mac"))
{
return &m_embeddedFiles.at("Mac");
}
#endif
return nullptr;
}
PDFFileSpecification PDFFileSpecification::parse(const PDFDocument* document, PDFObject object)
{
PDFFileSpecification result;
object = document->getObject(object);
if (object.isString())
{
result.m_UF = PDFEncoding::convertTextString(object.getString());
}
else if (object.isDictionary())
{
PDFDocumentDataLoaderDecorator loader(document);
const PDFDictionary* dictionary = object.getDictionary();
PDFObject collectionObject = dictionary->get("Cl");
result.m_fileSystem = loader.readNameFromDictionary(dictionary, "FS");
result.m_F = loader.readStringFromDictionary(dictionary, "F");
result.m_UF = loader.readTextStringFromDictionary(dictionary, "UF", QString());
result.m_DOS = loader.readStringFromDictionary(dictionary, "DOS");
result.m_Mac = loader.readStringFromDictionary(dictionary, "Mac");
result.m_Unix = loader.readStringFromDictionary(dictionary, "Unix");
result.m_volatile = loader.readBooleanFromDictionary(dictionary, "V", false);
result.m_description = loader.readTextStringFromDictionary(dictionary, "Desc", QString());
result.m_collection = collectionObject.isReference() ? collectionObject.getReference() : PDFObjectReference();
PDFObject embeddedFiles = document->getObject(dictionary->get("EF"));
if (embeddedFiles.isDictionary())
{
const PDFDictionary* embeddedFilesDictionary = embeddedFiles.getDictionary();
for (size_t i = 0; i < embeddedFilesDictionary->getCount(); ++i)
{
result.m_embeddedFiles[embeddedFilesDictionary->getKey(i)] = PDFEmbeddedFile::parse(document, embeddedFilesDictionary->getValue(i));
}
}
}
return result;
}
PDFEmbeddedFile PDFEmbeddedFile::parse(const PDFDocument* document, PDFObject object)
{
PDFEmbeddedFile result;
object = document->getObject(object);
if (object.isStream())
{
const PDFStream* stream = object.getStream();
const PDFDictionary* dictionary = stream->getDictionary();
PDFDocumentDataLoaderDecorator loader(document);
result.m_stream = object;
result.m_subtype = loader.readNameFromDictionary(dictionary, "Subtype");
const PDFObject& paramsObject = document->getObject(dictionary->get("Params"));
if (paramsObject.isDictionary())
{
const PDFDictionary* paramsDictionary = paramsObject.getDictionary();
auto getDateTime = [&loader, paramsDictionary](const char* name)
{
QByteArray ba = loader.readStringFromDictionary(paramsDictionary, name);
if (!ba.isEmpty())
{
return PDFEncoding::convertToDateTime(ba);
}
return QDateTime();
};
result.m_size = loader.readIntegerFromDictionary(paramsDictionary, "Size", -1);
result.m_creationDate = getDateTime("CreationDate");
result.m_modifiedDate = getDateTime("ModDate");
result.m_checksum = loader.readStringFromDictionary(paramsDictionary, "CheckSum");
}
}
return result;
}
} // namespace pdf

View File

@ -0,0 +1,106 @@
// Copyright (C) 2019 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 PDFFILE_H
#define PDFFILE_H
#include "pdfobject.h"
#include <QDateTime>
namespace pdf
{
class PDFDocument;
class PDFEmbeddedFile
{
public:
explicit PDFEmbeddedFile() = default;
bool isValid() const { return m_stream.isStream(); }
static PDFEmbeddedFile parse(const PDFDocument* document, PDFObject object);
private:
PDFObject m_stream;
QByteArray m_subtype;
PDFInteger m_size = -1;
QDateTime m_creationDate;
QDateTime m_modifiedDate;
QByteArray m_checksum;
};
/// File specification
class PDFFileSpecification
{
public:
explicit PDFFileSpecification() = default;
/// Returns platform file name as string. It looks into the UF, F,
/// and platform names and selects the appropriate one. If error
/// occurs. then empty string is returned.
QString getPlatformFileName() const;
/// Returns platform file.
const PDFEmbeddedFile* getPlatformFile() const;
const QByteArray& getFileSystem() const { return m_fileSystem; }
const QByteArray& getF() const { return m_F; }
const QString& getUF() const { return m_UF; }
const QByteArray& getDOS() const { return m_DOS; }
const QByteArray& getMac() const { return m_Mac; }
const QByteArray& getUnix() const { return m_Unix; }
bool isVolatile() const { return m_volatile; }
const QString& getDescription() const { return m_description; }
PDFObjectReference getCollection() const { return m_collection; }
const std::map<QByteArray, PDFEmbeddedFile>& getEmbeddedFiles() const { return m_embeddedFiles; }
static PDFFileSpecification parse(const PDFDocument* document, PDFObject object);
private:
/// Name of the file system used to interpret this file specification,
/// usually, it is URL (this is only file system defined in PDF specification 1.7).
QByteArray m_fileSystem;
/// File specification string (for backward compatibility). If file system is URL,
/// it contains unified resource locator.
QByteArray m_F;
/// File specification string as unicode.
QString m_UF;
QByteArray m_DOS;
QByteArray m_Mac;
QByteArray m_Unix;
/// Is file volatile? I.e it is, for example, link to a video file from online camera?
/// If this boolean is true, then file should never be cached.
bool m_volatile = false;
/// Description of the file (for example, if file is embedded file stream)
QString m_description;
/// Collection item dictionary reference
PDFObjectReference m_collection;
/// Embedded files
std::map<QByteArray, PDFEmbeddedFile> m_embeddedFiles;
};
} // namespace pdf
#endif // PDFFILE_H

View File

@ -0,0 +1,110 @@
// Copyright (C) 2019 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 "pdfoutline.h"
#include "pdfdocument.h"
#include "pdfexception.h"
#include "pdfencoding.h"
namespace pdf
{
QSharedPointer<PDFOutlineItem> PDFOutlineItem::parse(const PDFDocument* document, const PDFObject& root)
{
const PDFObject& rootDereferenced = document->getObject(root);
if (rootDereferenced.isDictionary())
{
const PDFDictionary* dictionary = rootDereferenced.getDictionary();
const PDFObject& first = dictionary->get("First");
if (first.isReference())
{
QSharedPointer<PDFOutlineItem> result(new PDFOutlineItem());
std::set<PDFObjectReference> visitedOutlineItems;
parseImpl(document, result.get(), first.getReference(), visitedOutlineItems);
return result;
}
}
return QSharedPointer<PDFOutlineItem>();
}
void PDFOutlineItem::parseImpl(const PDFDocument* document,
PDFOutlineItem* parent,
PDFObjectReference currentItem,
std::set<PDFObjectReference>& visitedOutlineItems)
{
auto checkCyclicDependence = [&visitedOutlineItems](PDFObjectReference reference)
{
if (visitedOutlineItems.count(reference))
{
throw PDFException(PDFTranslationContext::tr("Outline items have cyclic dependence."));
}
visitedOutlineItems.insert(reference);
};
checkCyclicDependence(currentItem);
PDFObject dereferencedItem = document->getObjectByReference(currentItem);
while (dereferencedItem.isDictionary())
{
const PDFDictionary* dictionary = dereferencedItem.getDictionary();
QSharedPointer<PDFOutlineItem> currentOutlineItem(new PDFOutlineItem());
const PDFObject& titleObject = document->getObject(dictionary->get("Title"));
if (titleObject.isString())
{
currentOutlineItem->setTitle(PDFEncoding::convertTextString(titleObject.getString()));
}
currentOutlineItem->setAction(PDFAction::parse(document, dictionary->get("A")));
// Parse children of this item
const PDFObject& firstItem = dictionary->get("First");
if (firstItem.isReference())
{
parseImpl(document, currentOutlineItem.get(), firstItem.getReference(), visitedOutlineItems);
}
// Add new child to the parent
parent->addChild(qMove(currentOutlineItem));
// Parse next item
const PDFObject& nextItem = dictionary->get("Next");
if (nextItem.isReference())
{
checkCyclicDependence(nextItem.getReference());
dereferencedItem = document->getObject(nextItem);
}
else
{
// We are finished
break;
}
}
}
const PDFAction* PDFOutlineItem::getAction() const
{
return m_action.get();
}
void PDFOutlineItem::setAction(const PDFActionPtr& action)
{
m_action = action;
}
} // namespace pdf

View File

@ -0,0 +1,64 @@
// Copyright (C) 2019 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 PDFOUTLINE_H
#define PDFOUTLINE_H
#include "pdfglobal.h"
#include "pdfobject.h"
#include "pdfaction.h"
#include <QSharedPointer>
#include <set>
namespace pdf
{
class PDFDocument;
/// Outline item
class PDFOutlineItem
{
public:
explicit PDFOutlineItem() = default;
const QString& getTitle() const { return m_title; }
void setTitle(const QString& title) { m_title = title; }
size_t getChildCount() const { return m_children.size(); }
const PDFOutlineItem* getChild(size_t index) const { return m_children[index].get(); }
void addChild(QSharedPointer<PDFOutlineItem> child) { m_children.emplace_back(qMove(child)); }
static QSharedPointer<PDFOutlineItem> parse(const PDFDocument* document, const PDFObject& root);
const PDFAction* getAction() const;
void setAction(const PDFActionPtr& action);
private:
static void parseImpl(const PDFDocument* document,
PDFOutlineItem* parent,
PDFObjectReference currentItem,
std::set<PDFObjectReference>& visitedOutlineItems);
QString m_title;
std::vector<QSharedPointer<PDFOutlineItem>> m_children;
PDFActionPtr m_action;
};
} // namespace pdf
#endif // PDFOUTLINE_H