mirror of https://github.com/JakubMelka/PDF4QT.git
Actions/outline basics
This commit is contained in:
parent
84d9b58476
commit
2a11fa18e0
|
@ -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 \
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue