mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-04-01 20:20:05 +02:00
Finishing of outline items
This commit is contained in:
parent
39059c645e
commit
5954b7f409
@ -81,6 +81,7 @@ HEADERS += \
|
|||||||
sources/pdfjbig2decoder.h \
|
sources/pdfjbig2decoder.h \
|
||||||
sources/pdfmeshqualitysettings.h \
|
sources/pdfmeshqualitysettings.h \
|
||||||
sources/pdfmultimedia.h \
|
sources/pdfmultimedia.h \
|
||||||
|
sources/pdfnametreeloader.h \
|
||||||
sources/pdfobject.h \
|
sources/pdfobject.h \
|
||||||
sources/pdfoptionalcontent.h \
|
sources/pdfoptionalcontent.h \
|
||||||
sources/pdfoutline.h \
|
sources/pdfoutline.h \
|
||||||
|
@ -39,6 +39,13 @@ void PDFAction::apply(const std::function<void (const PDFAction*)>& callback)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<const PDFAction*> PDFAction::getActionList() const
|
||||||
|
{
|
||||||
|
std::vector<const PDFAction*> result;
|
||||||
|
fillActionList(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
PDFActionPtr PDFAction::parseImpl(const PDFDocument* document, PDFObject object, std::set<PDFObjectReference>& usedReferences)
|
PDFActionPtr PDFAction::parseImpl(const PDFDocument* document, PDFObject object, std::set<PDFObjectReference>& usedReferences)
|
||||||
{
|
{
|
||||||
if (object.isReference())
|
if (object.isReference())
|
||||||
@ -273,10 +280,37 @@ PDFActionPtr PDFAction::parseImpl(const PDFDocument* document, PDFObject object,
|
|||||||
{
|
{
|
||||||
return PDFActionPtr(new PDFActionGoTo3DView(dictionary->get("TA"), dictionary->get("V")));
|
return PDFActionPtr(new PDFActionGoTo3DView(dictionary->get("TA"), dictionary->get("V")));
|
||||||
}
|
}
|
||||||
|
else if (name == "JavaScript")
|
||||||
|
{
|
||||||
|
QByteArray textJavaScript;
|
||||||
|
const PDFObject& javaScriptObject = document->getObject(dictionary->get("JS"));
|
||||||
|
if (javaScriptObject.isString())
|
||||||
|
{
|
||||||
|
textJavaScript = javaScriptObject.getString();
|
||||||
|
}
|
||||||
|
else if (javaScriptObject.isStream())
|
||||||
|
{
|
||||||
|
textJavaScript = document->getDecodedStream(javaScriptObject.getStream());
|
||||||
|
}
|
||||||
|
return PDFActionPtr(new PDFActionJavaScript(PDFEncoding::convertTextString(textJavaScript)));
|
||||||
|
}
|
||||||
|
|
||||||
return PDFActionPtr();
|
return PDFActionPtr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PDFAction::fillActionList(std::vector<const PDFAction*>& actionList) const
|
||||||
|
{
|
||||||
|
actionList.push_back(this);
|
||||||
|
|
||||||
|
for (const PDFActionPtr& actionPointer : m_nextActions)
|
||||||
|
{
|
||||||
|
if (actionPointer)
|
||||||
|
{
|
||||||
|
actionPointer->fillActionList(actionList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PDFDestination PDFDestination::parse(const PDFDocument* document, PDFObject object)
|
PDFDestination PDFDestination::parse(const PDFDocument* document, PDFObject object)
|
||||||
{
|
{
|
||||||
PDFDestination result;
|
PDFDestination result;
|
||||||
|
@ -50,7 +50,8 @@ enum class ActionType
|
|||||||
SetOCGState,
|
SetOCGState,
|
||||||
Rendition,
|
Rendition,
|
||||||
Transition,
|
Transition,
|
||||||
GoTo3DView
|
GoTo3DView,
|
||||||
|
JavaScript
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class DestinationType
|
enum class DestinationType
|
||||||
@ -105,7 +106,7 @@ private:
|
|||||||
using PDFActionPtr = QSharedPointer<PDFAction>;
|
using PDFActionPtr = QSharedPointer<PDFAction>;
|
||||||
|
|
||||||
/// Base class for action types.
|
/// Base class for action types.
|
||||||
class PDFAction
|
class PDFFORQTLIBSHARED_EXPORT PDFAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit PDFAction() = default;
|
explicit PDFAction() = default;
|
||||||
@ -127,9 +128,14 @@ public:
|
|||||||
/// the 'Next' entry, as described by PDF 1.7 specification.
|
/// the 'Next' entry, as described by PDF 1.7 specification.
|
||||||
void apply(const std::function<void(const PDFAction* action)>& callback);
|
void apply(const std::function<void(const PDFAction* action)>& callback);
|
||||||
|
|
||||||
|
/// Returns list of actions to be executed
|
||||||
|
std::vector<const PDFAction*> getActionList() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static PDFActionPtr parseImpl(const PDFDocument* document, PDFObject object, std::set<PDFObjectReference>& usedReferences);
|
static PDFActionPtr parseImpl(const PDFDocument* document, PDFObject object, std::set<PDFObjectReference>& usedReferences);
|
||||||
|
|
||||||
|
void fillActionList(std::vector<const PDFAction*>& actionList) const;
|
||||||
|
|
||||||
std::vector<PDFActionPtr> m_nextActions;
|
std::vector<PDFActionPtr> m_nextActions;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -490,6 +496,23 @@ private:
|
|||||||
PDFObject m_view;
|
PDFObject m_view;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PDFActionJavaScript : public PDFAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PDFActionJavaScript(const QString& javaScript) :
|
||||||
|
m_javaScript(javaScript)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ActionType getType() const override { return ActionType::JavaScript; }
|
||||||
|
|
||||||
|
const QString& getJavaScript() const { return m_javaScript; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_javaScript;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace pdf
|
} // namespace pdf
|
||||||
|
|
||||||
#endif // PDFACTION_H
|
#endif // PDFACTION_H
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "pdfdocument.h"
|
#include "pdfdocument.h"
|
||||||
#include "pdfexception.h"
|
#include "pdfexception.h"
|
||||||
#include "pdfnumbertreeloader.h"
|
#include "pdfnumbertreeloader.h"
|
||||||
|
#include "pdfnametreeloader.h"
|
||||||
|
|
||||||
namespace pdf
|
namespace pdf
|
||||||
{
|
{
|
||||||
@ -42,6 +43,28 @@ static constexpr const char* PDF_VIEWER_PREFERENCES_PICK_TRAY_BY_PDF_SIZE = "Pic
|
|||||||
static constexpr const char* PDF_VIEWER_PREFERENCES_NUMBER_OF_COPIES = "NumCopies";
|
static constexpr const char* PDF_VIEWER_PREFERENCES_NUMBER_OF_COPIES = "NumCopies";
|
||||||
static constexpr const char* PDF_VIEWER_PREFERENCES_PRINT_PAGE_RANGE = "PrintPageRange";
|
static constexpr const char* PDF_VIEWER_PREFERENCES_PRINT_PAGE_RANGE = "PrintPageRange";
|
||||||
|
|
||||||
|
size_t PDFCatalog::getPageIndexFromPageReference(PDFObjectReference reference) const
|
||||||
|
{
|
||||||
|
auto it = std::find_if(m_pages.cbegin(), m_pages.cend(), [reference](const PDFPage& page) { return page.getPageReference() == reference; });
|
||||||
|
if (it != m_pages.cend())
|
||||||
|
{
|
||||||
|
return std::distance(m_pages.cbegin(), it);
|
||||||
|
}
|
||||||
|
|
||||||
|
return INVALID_PAGE_INDEX;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PDFDestination* PDFCatalog::getDestination(const QByteArray& key) const
|
||||||
|
{
|
||||||
|
auto it = m_destinations.find(key);
|
||||||
|
if (it != m_destinations.cend())
|
||||||
|
{
|
||||||
|
return &it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
PDFCatalog PDFCatalog::parse(const PDFObject& catalog, const PDFDocument* document)
|
PDFCatalog PDFCatalog::parse(const PDFObject& catalog, const PDFDocument* document)
|
||||||
{
|
{
|
||||||
if (!catalog.isDictionary())
|
if (!catalog.isDictionary())
|
||||||
@ -109,6 +132,41 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
catalogObject.m_version = loader.readNameFromDictionary(catalogDictionary, "Version");
|
||||||
|
|
||||||
|
if (const PDFDictionary* namesDictionary = document->getDictionaryFromObject(catalogDictionary->get("Names")))
|
||||||
|
{
|
||||||
|
auto parseDestination = [](const PDFDocument* document, PDFObject object)
|
||||||
|
{
|
||||||
|
object = document->getObject(object);
|
||||||
|
if (object.isDictionary())
|
||||||
|
{
|
||||||
|
object = object.getDictionary()->get("D");
|
||||||
|
}
|
||||||
|
return PDFDestination::parse(document, qMove(object));
|
||||||
|
};
|
||||||
|
|
||||||
|
catalogObject.m_destinations = PDFNameTreeLoader<PDFDestination>::parse(document, namesDictionary->get("Dests"), parseDestination);
|
||||||
|
catalogObject.m_javaScriptActions = PDFNameTreeLoader<PDFActionPtr>::parse(document, namesDictionary->get("JavaScript"), &PDFAction::parse);
|
||||||
|
catalogObject.m_embeddedFiles = PDFNameTreeLoader<PDFFileSpecification>::parse(document, namesDictionary->get("EmbeddedFiles"), &PDFFileSpecification::parse);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Examine "Dests" dictionary
|
||||||
|
if (const PDFDictionary* destsDictionary = document->getDictionaryFromObject(catalogDictionary->get("Dests")))
|
||||||
|
{
|
||||||
|
const size_t count = destsDictionary->getCount();
|
||||||
|
for (size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
catalogObject.m_destinations[destsDictionary->getKey(i)] = PDFDestination::parse(document, destsDictionary->getValue(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Examine "URI" dictionary
|
||||||
|
if (const PDFDictionary* URIDictionary = document->getDictionaryFromObject(catalogDictionary->get("URI")))
|
||||||
|
{
|
||||||
|
catalogObject.m_baseURI = loader.readStringFromDictionary(URIDictionary, "Base");
|
||||||
|
}
|
||||||
|
|
||||||
return catalogObject;
|
return catalogObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,7 +194,7 @@ private:
|
|||||||
PDFInteger m_numberOfCopies = 1;
|
PDFInteger m_numberOfCopies = 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PDFCatalog
|
class PDFFORQTLIBSHARED_EXPORT PDFCatalog
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
constexpr inline PDFCatalog() = default;
|
constexpr inline PDFCatalog() = default;
|
||||||
@ -205,6 +205,8 @@ public:
|
|||||||
constexpr inline PDFCatalog& operator=(const PDFCatalog&) = default;
|
constexpr inline PDFCatalog& operator=(const PDFCatalog&) = default;
|
||||||
constexpr inline PDFCatalog& operator=(PDFCatalog&&) = default;
|
constexpr inline PDFCatalog& operator=(PDFCatalog&&) = default;
|
||||||
|
|
||||||
|
static constexpr const size_t INVALID_PAGE_INDEX = std::numeric_limits<size_t>::max();
|
||||||
|
|
||||||
/// 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; }
|
||||||
|
|
||||||
@ -214,6 +216,9 @@ public:
|
|||||||
/// Returns the page
|
/// Returns the page
|
||||||
const PDFPage* getPage(size_t index) const { return &m_pages.at(index); }
|
const PDFPage* getPage(size_t index) const { return &m_pages.at(index); }
|
||||||
|
|
||||||
|
/// Returns page index. If page is not found, then INVALID_PAGE_INDEX is returned.
|
||||||
|
size_t getPageIndexFromPageReference(PDFObjectReference reference) const;
|
||||||
|
|
||||||
/// Returns optional content properties
|
/// Returns optional content properties
|
||||||
const PDFOptionalContentProperties* getOptionalContentProperties() const { return &m_optionalContentProperties; }
|
const PDFOptionalContentProperties* getOptionalContentProperties() const { return &m_optionalContentProperties; }
|
||||||
|
|
||||||
@ -223,14 +228,25 @@ public:
|
|||||||
/// Returns action, which should be performed
|
/// Returns action, which should be performed
|
||||||
const PDFAction* getOpenAction() const { return m_openAction.data(); }
|
const PDFAction* getOpenAction() const { return m_openAction.data(); }
|
||||||
|
|
||||||
|
/// Returns version of the PDF specification, to which the document conforms.
|
||||||
|
const QByteArray& getVersion() const { return m_version; }
|
||||||
|
|
||||||
PageLayout getPageLayout() const { return m_pageLayout; }
|
PageLayout getPageLayout() const { return m_pageLayout; }
|
||||||
PageMode getPageMode() const { return m_pageMode; }
|
PageMode getPageMode() const { return m_pageMode; }
|
||||||
|
const QByteArray& getBaseURI() const { return m_baseURI; }
|
||||||
|
|
||||||
|
/// Returns destination using the key. If destination with the key is not found,
|
||||||
|
/// then nullptr is returned.
|
||||||
|
/// \param key Destination key
|
||||||
|
/// \returns Pointer to the destination, or nullptr
|
||||||
|
const PDFDestination* getDestination(const QByteArray& key) const;
|
||||||
|
|
||||||
/// Parses catalog from catalog dictionary. If object cannot be parsed, or error occurs,
|
/// Parses catalog from catalog dictionary. If object cannot be parsed, or error occurs,
|
||||||
/// then exception is thrown.
|
/// then exception is thrown.
|
||||||
static PDFCatalog parse(const PDFObject& catalog, const PDFDocument* document);
|
static PDFCatalog parse(const PDFObject& catalog, const PDFDocument* document);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QByteArray m_version;
|
||||||
PDFViewerPreferences m_viewerPreferences;
|
PDFViewerPreferences m_viewerPreferences;
|
||||||
std::vector<PDFPage> m_pages;
|
std::vector<PDFPage> m_pages;
|
||||||
std::vector<PDFPageLabel> m_pageLabels;
|
std::vector<PDFPageLabel> m_pageLabels;
|
||||||
@ -239,6 +255,12 @@ private:
|
|||||||
PDFActionPtr m_openAction;
|
PDFActionPtr m_openAction;
|
||||||
PageLayout m_pageLayout = PageLayout::SinglePage;
|
PageLayout m_pageLayout = PageLayout::SinglePage;
|
||||||
PageMode m_pageMode = PageMode::UseNone;
|
PageMode m_pageMode = PageMode::UseNone;
|
||||||
|
QByteArray m_baseURI;
|
||||||
|
|
||||||
|
// Maps from Names dictionary
|
||||||
|
std::map<QByteArray, PDFDestination> m_destinations;
|
||||||
|
std::map<QByteArray, PDFActionPtr> m_javaScriptActions;
|
||||||
|
std::map<QByteArray, PDFFileSpecification> m_embeddedFiles;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace pdf
|
} // namespace pdf
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
#ifndef PDFENCODING_H
|
#ifndef PDFENCODING_H
|
||||||
#define PDFENCODING_H
|
#define PDFENCODING_H
|
||||||
|
|
||||||
|
#include "pdfglobal.h"
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
|
||||||
@ -33,7 +35,7 @@ using EncodingTable = std::array<QChar, 256>;
|
|||||||
|
|
||||||
/// This class can convert byte stream to the QString in unicode encoding.
|
/// This class can convert byte stream to the QString in unicode encoding.
|
||||||
/// PDF has several encodings, see PDF Reference 1.7, Appendix D.
|
/// PDF has several encodings, see PDF Reference 1.7, Appendix D.
|
||||||
class PDFEncoding
|
class PDFFORQTLIBSHARED_EXPORT PDFEncoding
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit PDFEncoding() = delete;
|
explicit PDFEncoding() = delete;
|
||||||
|
@ -45,7 +45,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// File specification
|
/// File specification
|
||||||
class PDFFileSpecification
|
class PDFFORQTLIBSHARED_EXPORT PDFFileSpecification
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit PDFFileSpecification() = default;
|
explicit PDFFileSpecification() = default;
|
||||||
|
94
PdfForQtLib/sources/pdfnametreeloader.h
Normal file
94
PdfForQtLib/sources/pdfnametreeloader.h
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
// 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 PDFNAMETREELOADER_H
|
||||||
|
#define PDFNAMETREELOADER_H
|
||||||
|
|
||||||
|
#include "pdfdocument.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace pdf
|
||||||
|
{
|
||||||
|
|
||||||
|
/// This class can load a number tree into the array
|
||||||
|
template<typename Type>
|
||||||
|
class PDFNameTreeLoader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PDFNameTreeLoader() = delete;
|
||||||
|
|
||||||
|
using MappedObjects = std::map<QByteArray, Type>;
|
||||||
|
using LoadMethod = std::function<Type(const PDFDocument*, const PDFObject&)>;
|
||||||
|
|
||||||
|
/// Parses the name tree and loads its items into the map. Some errors are ignored,
|
||||||
|
/// e.g. when kid is null. Objects are retrieved by \p loadMethod.
|
||||||
|
/// \param document Document
|
||||||
|
/// \param root Root of the name tree
|
||||||
|
/// \param loadMethod Parsing method, which retrieves parsed object
|
||||||
|
static MappedObjects parse(const PDFDocument* document, const PDFObject& root, const LoadMethod& loadMethod)
|
||||||
|
{
|
||||||
|
MappedObjects result;
|
||||||
|
parseImpl(result, document, root, loadMethod);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void parseImpl(MappedObjects& objects, const PDFDocument* document, const PDFObject& root, const LoadMethod& loadMethod)
|
||||||
|
{
|
||||||
|
if (const PDFDictionary* dictionary = document->getDictionaryFromObject(root))
|
||||||
|
{
|
||||||
|
// Jakub Melka: First, load the objects into the map
|
||||||
|
const PDFObject& namedItems = document->getObject(dictionary->get("Names"));
|
||||||
|
if (namedItems.isArray())
|
||||||
|
{
|
||||||
|
const PDFArray* namedItemsArray = namedItems.getArray();
|
||||||
|
const size_t count = namedItemsArray->getCount() / 2;
|
||||||
|
for (size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
const size_t numberIndex = 2 * i;
|
||||||
|
const size_t valueIndex = 2 * i + 1;
|
||||||
|
|
||||||
|
const PDFObject& name = document->getObject(namedItemsArray->getItem(numberIndex));
|
||||||
|
if (!name.isString())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
objects[name.getString()] = loadMethod(document, namedItemsArray->getItem(valueIndex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then, follow the kids
|
||||||
|
const PDFObject& kids = document->getObject(dictionary->get("Kids"));
|
||||||
|
if (kids.isArray())
|
||||||
|
{
|
||||||
|
const PDFArray* kidsArray = kids.getArray();
|
||||||
|
const size_t count = kidsArray->getCount();
|
||||||
|
for (size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
parseImpl(objects, document, kidsArray->getItem(i), loadMethod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace pdf
|
||||||
|
|
||||||
|
#endif // PDFNAMETREELOADER_H
|
@ -53,11 +53,8 @@ public:
|
|||||||
private:
|
private:
|
||||||
static void parseImpl(Objects& objects, const PDFDocument* document, const PDFObject& root)
|
static void parseImpl(Objects& objects, const PDFDocument* document, const PDFObject& root)
|
||||||
{
|
{
|
||||||
const PDFObject& dereferencedRoot = document->getObject(root);
|
if (const PDFDictionary* dictionary = document->getDictionaryFromObject(root))
|
||||||
if (dereferencedRoot.isDictionary())
|
|
||||||
{
|
{
|
||||||
const PDFDictionary* dictionary = dereferencedRoot.getDictionary();
|
|
||||||
|
|
||||||
// First, load the objects into the array
|
// First, load the objects into the array
|
||||||
const PDFObject& numberedItems = document->getObject(dictionary->get("Nums"));
|
const PDFObject& numberedItems = document->getObject(dictionary->get("Nums"));
|
||||||
if (numberedItems.isArray())
|
if (numberedItems.isArray())
|
||||||
|
@ -343,13 +343,13 @@ OCState PDFOptionalContentActivity::getState(PDFObjectReference ocg) const
|
|||||||
return OCState::Unknown;
|
return OCState::Unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PDFOptionalContentActivity::setState(PDFObjectReference ocg, OCState state)
|
void PDFOptionalContentActivity::setState(PDFObjectReference ocg, OCState state, bool preserveRadioButtons)
|
||||||
{
|
{
|
||||||
auto it = m_states.find(ocg);
|
auto it = m_states.find(ocg);
|
||||||
if (it != m_states.cend() && it->second != state)
|
if (it != m_states.cend() && it->second != state)
|
||||||
{
|
{
|
||||||
// We are changing the state. If new state is ON, then we must check radio button groups.
|
// We are changing the state. If new state is ON, then we must check radio button groups.
|
||||||
if (state == OCState::ON)
|
if (state == OCState::ON && preserveRadioButtons)
|
||||||
{
|
{
|
||||||
for (const std::vector<PDFObjectReference>& radioButtonGroup : m_properties->getDefaultConfiguration().getRadioButtonGroups())
|
for (const std::vector<PDFObjectReference>& radioButtonGroup : m_properties->getDefaultConfiguration().getRadioButtonGroups())
|
||||||
{
|
{
|
||||||
|
@ -164,18 +164,20 @@ public:
|
|||||||
|
|
||||||
/// Gets the optional content groups state. If optional content group doesn't exist,
|
/// Gets the optional content groups state. If optional content group doesn't exist,
|
||||||
/// then it returns Unknown state.
|
/// then it returns Unknown state.
|
||||||
/// \param ocg Optional conteng group
|
/// \param ocg Optional content group
|
||||||
OCState getState(PDFObjectReference ocg) const;
|
OCState getState(PDFObjectReference ocg) const;
|
||||||
|
|
||||||
/// Sets the state of optional content group. If optional content group doesn't exist,
|
/// Sets the state of optional content group. If optional content group doesn't exist,
|
||||||
/// then nothing happens. If optional content group is contained in radio button group, then
|
/// then nothing happens. If optional content group is contained in radio button group, then
|
||||||
/// all other optional content groups in the group are switched off, if we are
|
/// all other optional content groups in the group are switched off, if we are
|
||||||
/// switching this one to ON state. If we are switching it off, then nothing happens (as all
|
/// switching this one to ON state. If we are switching it off, then nothing happens (as all
|
||||||
/// optional content groups in radio button group can be switched off).
|
/// optional content groups in radio button group can be switched off). This behaviour can be
|
||||||
|
/// controlled via parameter \p preserveRadioButtons.
|
||||||
/// \param ocg Optional content group
|
/// \param ocg Optional content group
|
||||||
/// \param state New state of the optional content group
|
/// \param state New state of the optional content group
|
||||||
|
/// \param preserveRadioButtons Switch off other radio buttons in group?
|
||||||
/// \note If something changed, then signal \p optionalContentGroupStateChanged is emitted.
|
/// \note If something changed, then signal \p optionalContentGroupStateChanged is emitted.
|
||||||
void setState(PDFObjectReference ocg, OCState state);
|
void setState(PDFObjectReference ocg, OCState state, bool preserveRadioButtons = true);
|
||||||
|
|
||||||
/// Applies configuration to the current state of optional content groups
|
/// Applies configuration to the current state of optional content groups
|
||||||
void applyConfiguration(const PDFOptionalContentConfiguration& configuration);
|
void applyConfiguration(const PDFOptionalContentConfiguration& configuration);
|
||||||
|
@ -150,6 +150,7 @@ void PDFPage::parseImpl(std::vector<PDFPage>& pages,
|
|||||||
const PDFDocument* document)
|
const PDFDocument* document)
|
||||||
{
|
{
|
||||||
// Are we in internal node, or leaf (page object)?
|
// Are we in internal node, or leaf (page object)?
|
||||||
|
PDFObjectReference objectReference = root.isReference() ? root.getReference() : PDFObjectReference();
|
||||||
const PDFObject& dereferenced = document->getObject(root);
|
const PDFObject& dereferenced = document->getObject(root);
|
||||||
|
|
||||||
if (dereferenced.isDictionary())
|
if (dereferenced.isDictionary())
|
||||||
@ -198,6 +199,7 @@ void PDFPage::parseImpl(std::vector<PDFPage>& pages,
|
|||||||
{
|
{
|
||||||
PDFPage page;
|
PDFPage page;
|
||||||
|
|
||||||
|
page.m_pageReference = objectReference;
|
||||||
page.m_mediaBox = currentInheritableAttributes.getMediaBox();
|
page.m_mediaBox = currentInheritableAttributes.getMediaBox();
|
||||||
page.m_cropBox = currentInheritableAttributes.getCropBox();
|
page.m_cropBox = currentInheritableAttributes.getCropBox();
|
||||||
page.m_resources = document->getObject(currentInheritableAttributes.getResources());
|
page.m_resources = document->getObject(currentInheritableAttributes.getResources());
|
||||||
|
@ -94,6 +94,8 @@ public:
|
|||||||
inline QRectF getTrimBoxMM() const { return getRectMM(m_trimBox); }
|
inline QRectF getTrimBoxMM() const { return getRectMM(m_trimBox); }
|
||||||
inline QRectF getArtBoxMM() const { return getRectMM(m_artBox); }
|
inline QRectF getArtBoxMM() const { return getRectMM(m_artBox); }
|
||||||
|
|
||||||
|
inline PDFObjectReference getPageReference() const { return m_pageReference; }
|
||||||
|
|
||||||
QRectF getRotatedMediaBox() const;
|
QRectF getRotatedMediaBox() const;
|
||||||
QRectF getRotatedCropBox() const;
|
QRectF getRotatedCropBox() const;
|
||||||
|
|
||||||
@ -120,6 +122,7 @@ private:
|
|||||||
PageRotation m_pageRotation = PageRotation::None;
|
PageRotation m_pageRotation = PageRotation::None;
|
||||||
PDFObject m_resources;
|
PDFObject m_resources;
|
||||||
PDFObject m_contents;
|
PDFObject m_contents;
|
||||||
|
PDFObjectReference m_pageReference;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace pdf
|
} // namespace pdf
|
||||||
|
@ -17,5 +17,6 @@
|
|||||||
<file>resources/zoom-in.svg</file>
|
<file>resources/zoom-in.svg</file>
|
||||||
<file>resources/zoom-out.svg</file>
|
<file>resources/zoom-out.svg</file>
|
||||||
<file>resources/bookmark.svg</file>
|
<file>resources/bookmark.svg</file>
|
||||||
|
<file>resources/security.svg</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
@ -47,6 +47,11 @@
|
|||||||
#include <QSpinBox>
|
#include <QSpinBox>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QDoubleSpinBox>
|
#include <QDoubleSpinBox>
|
||||||
|
#include <QDesktopServices>
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include "Windows.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace pdfviewer
|
namespace pdfviewer
|
||||||
{
|
{
|
||||||
@ -251,7 +256,216 @@ void PDFViewerMainWindow::onPageZoomSpinboxEditingFinished()
|
|||||||
|
|
||||||
void PDFViewerMainWindow::onActionTriggered(const pdf::PDFAction* action)
|
void PDFViewerMainWindow::onActionTriggered(const pdf::PDFAction* action)
|
||||||
{
|
{
|
||||||
|
Q_ASSERT(action);
|
||||||
|
|
||||||
|
for (const pdf::PDFAction* currentAction : action->getActionList())
|
||||||
|
{
|
||||||
|
switch (action->getType())
|
||||||
|
{
|
||||||
|
case pdf::ActionType::GoTo:
|
||||||
|
{
|
||||||
|
const pdf::PDFActionGoTo* typedAction = dynamic_cast<const pdf::PDFActionGoTo*>(currentAction);
|
||||||
|
pdf::PDFDestination destination = typedAction->getDestination();
|
||||||
|
if (destination.getDestinationType() == pdf::DestinationType::Named)
|
||||||
|
{
|
||||||
|
if (const pdf::PDFDestination* targetDestination = m_pdfDocument->getCatalog()->getDestination(destination.getName()))
|
||||||
|
{
|
||||||
|
destination = *targetDestination;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
destination = pdf::PDFDestination();
|
||||||
|
QMessageBox::critical(this, tr("Go to action"), tr("Failed to go to destination '%1'. Destination wasn't found.").arg(pdf::PDFEncoding::convertTextString(destination.getName())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (destination.getDestinationType() != pdf::DestinationType::Invalid &&
|
||||||
|
destination.getPageReference() != pdf::PDFObjectReference())
|
||||||
|
{
|
||||||
|
const size_t pageIndex = m_pdfDocument->getCatalog()->getPageIndexFromPageReference(destination.getPageReference());
|
||||||
|
if (pageIndex != pdf::PDFCatalog::INVALID_PAGE_INDEX)
|
||||||
|
{
|
||||||
|
m_pdfWidget->getDrawWidgetProxy()->goToPage(pageIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case pdf::ActionType::Launch:
|
||||||
|
{
|
||||||
|
if (!m_settings->getSettings().m_allowLaunchApplications)
|
||||||
|
{
|
||||||
|
// Launching of applications is disabled -> continue to next action
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pdf::PDFActionLaunch* typedAction = dynamic_cast<const pdf::PDFActionLaunch*>(currentAction);
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
const pdf::PDFActionLaunch::Win& winSpecification = typedAction->getWinSpecification();
|
||||||
|
if (!winSpecification.file.isEmpty())
|
||||||
|
{
|
||||||
|
QString message = tr("Would you like to launch application '%1' in working directory '%2' with parameters '%3'?").arg(QString::fromLatin1(winSpecification.file), QString::fromLatin1(winSpecification.directory), QString::fromLatin1(winSpecification.parameters));
|
||||||
|
if (QMessageBox::question(this, tr("Launch application"), message) == QMessageBox::Yes)
|
||||||
|
{
|
||||||
|
auto getStringOrNULL = [](const QByteArray& array) -> LPCSTR
|
||||||
|
{
|
||||||
|
if (!array.isEmpty())
|
||||||
|
{
|
||||||
|
return array.data();
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
const HINSTANCE result = ::ShellExecuteA(NULL, getStringOrNULL(winSpecification.operation), getStringOrNULL(winSpecification.file), getStringOrNULL(winSpecification.parameters), getStringOrNULL(winSpecification.directory), SW_SHOWNORMAL);
|
||||||
|
if (result <= HINSTANCE(32))
|
||||||
|
{
|
||||||
|
// Error occured
|
||||||
|
QMessageBox::warning(this, tr("Launch application"), tr("Executing application failed. Error code is %1.").arg(reinterpret_cast<intptr_t>(result)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue next action
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pdf::PDFFileSpecification& fileSpecification = typedAction->getFileSpecification();
|
||||||
|
QString plaftormFileName = fileSpecification.getPlatformFileName();
|
||||||
|
if (!plaftormFileName.isEmpty())
|
||||||
|
{
|
||||||
|
QString message = tr("Would you like to launch application '%1'?").arg(plaftormFileName);
|
||||||
|
if (QMessageBox::question(this, tr("Launch application"), message) == QMessageBox::Yes)
|
||||||
|
{
|
||||||
|
const HINSTANCE result = ::ShellExecuteW(NULL, NULL, plaftormFileName.toStdWString().c_str(), NULL, NULL, SW_SHOWNORMAL);
|
||||||
|
if (result <= HINSTANCE(32))
|
||||||
|
{
|
||||||
|
// Error occured
|
||||||
|
QMessageBox::warning(this, tr("Launch application"), tr("Executing application failed. Error code is %1.").arg(reinterpret_cast<intptr_t>(result)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue next action
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case pdf::ActionType::URI:
|
||||||
|
{
|
||||||
|
if (!m_settings->getSettings().m_allowLaunchURI)
|
||||||
|
{
|
||||||
|
// Launching of URI is disabled -> continue to next action
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pdf::PDFActionURI* typedAction = dynamic_cast<const pdf::PDFActionURI*>(currentAction);
|
||||||
|
QByteArray URI = m_pdfDocument->getCatalog()->getBaseURI() + typedAction->getURI();
|
||||||
|
QString urlString = QString::fromLatin1(URI);
|
||||||
|
QString message = tr("Would you like to open URL '%1'?").arg(urlString);
|
||||||
|
if (QMessageBox::question(this, tr("Open URL"), message) == QMessageBox::Yes)
|
||||||
|
{
|
||||||
|
if (!QDesktopServices::openUrl(QUrl(urlString)))
|
||||||
|
{
|
||||||
|
// Error occured
|
||||||
|
QMessageBox::warning(this, tr("Open URL"), tr("Opening url '%1' failed.").arg(urlString));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case pdf::ActionType::Named:
|
||||||
|
{
|
||||||
|
const pdf::PDFActionNamed* typedAction = dynamic_cast<const pdf::PDFActionNamed*>(currentAction);
|
||||||
|
switch (typedAction->getNamedActionType())
|
||||||
|
{
|
||||||
|
case pdf::PDFActionNamed::NamedActionType::NextPage:
|
||||||
|
m_pdfWidget->getDrawWidgetProxy()->performOperation(pdf::PDFDrawWidgetProxy::NavigateNextPage);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case pdf::PDFActionNamed::NamedActionType::PrevPage:
|
||||||
|
m_pdfWidget->getDrawWidgetProxy()->performOperation(pdf::PDFDrawWidgetProxy::NavigatePreviousPage);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case pdf::PDFActionNamed::NamedActionType::FirstPage:
|
||||||
|
m_pdfWidget->getDrawWidgetProxy()->performOperation(pdf::PDFDrawWidgetProxy::NavigateDocumentStart);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case pdf::PDFActionNamed::NamedActionType::LastPage:
|
||||||
|
m_pdfWidget->getDrawWidgetProxy()->performOperation(pdf::PDFDrawWidgetProxy::NavigateDocumentEnd);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case pdf::ActionType::SetOCGState:
|
||||||
|
{
|
||||||
|
const pdf::PDFActionSetOCGState* typedAction = dynamic_cast<const pdf::PDFActionSetOCGState*>(currentAction);
|
||||||
|
const pdf::PDFActionSetOCGState::StateChangeItems& stateChanges = typedAction->getStateChangeItems();
|
||||||
|
const bool isRadioButtonsPreserved = typedAction->isRadioButtonsPreserved();
|
||||||
|
|
||||||
|
if (m_optionalContentActivity)
|
||||||
|
{
|
||||||
|
for (const pdf::PDFActionSetOCGState::StateChangeItem& stateChange : stateChanges)
|
||||||
|
{
|
||||||
|
pdf::OCState newState = pdf::OCState::Unknown;
|
||||||
|
switch (stateChange.first)
|
||||||
|
{
|
||||||
|
case pdf::PDFActionSetOCGState::SwitchType::ON:
|
||||||
|
newState = pdf::OCState::ON;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case pdf::PDFActionSetOCGState::SwitchType::OFF:
|
||||||
|
newState = pdf::OCState::OFF;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case pdf::PDFActionSetOCGState::SwitchType::Toggle:
|
||||||
|
{
|
||||||
|
pdf::OCState oldState = m_optionalContentActivity->getState(stateChange.second);
|
||||||
|
switch (oldState)
|
||||||
|
{
|
||||||
|
case pdf::OCState::ON:
|
||||||
|
newState = pdf::OCState::OFF;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case pdf::OCState::OFF:
|
||||||
|
newState = pdf::OCState::ON;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case pdf::OCState::Unknown:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Q_ASSERT(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
Q_ASSERT(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newState != pdf::OCState::Unknown)
|
||||||
|
{
|
||||||
|
m_optionalContentActivity->setState(stateChange.second, newState, isRadioButtonsPreserved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PDFViewerMainWindow::onProgressStarted()
|
void PDFViewerMainWindow::onProgressStarted()
|
||||||
|
@ -22,6 +22,8 @@ void PDFViewerSettings::readSettings(QSettings& settings)
|
|||||||
m_settings.m_preferredMeshResolutionRatio = settings.value("preferredMeshResolutionRatio", defaultSettings.m_preferredMeshResolutionRatio).toDouble();
|
m_settings.m_preferredMeshResolutionRatio = settings.value("preferredMeshResolutionRatio", defaultSettings.m_preferredMeshResolutionRatio).toDouble();
|
||||||
m_settings.m_minimalMeshResolutionRatio = settings.value("minimalMeshResolutionRatio", defaultSettings.m_minimalMeshResolutionRatio).toDouble();
|
m_settings.m_minimalMeshResolutionRatio = settings.value("minimalMeshResolutionRatio", defaultSettings.m_minimalMeshResolutionRatio).toDouble();
|
||||||
m_settings.m_colorTolerance = settings.value("colorTolerance", defaultSettings.m_colorTolerance).toDouble();
|
m_settings.m_colorTolerance = settings.value("colorTolerance", defaultSettings.m_colorTolerance).toDouble();
|
||||||
|
m_settings.m_allowLaunchApplications = settings.value("allowLaunchApplications", defaultSettings.m_allowLaunchApplications).toBool();
|
||||||
|
m_settings.m_allowLaunchURI = settings.value("allowLaunchURI", defaultSettings.m_allowLaunchURI).toBool();
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
|
|
||||||
emit settingsChanged();
|
emit settingsChanged();
|
||||||
@ -38,6 +40,8 @@ void PDFViewerSettings::writeSettings(QSettings& settings)
|
|||||||
settings.setValue("preferredMeshResolutionRatio", m_settings.m_preferredMeshResolutionRatio);
|
settings.setValue("preferredMeshResolutionRatio", m_settings.m_preferredMeshResolutionRatio);
|
||||||
settings.setValue("minimalMeshResolutionRatio", m_settings.m_minimalMeshResolutionRatio);
|
settings.setValue("minimalMeshResolutionRatio", m_settings.m_minimalMeshResolutionRatio);
|
||||||
settings.setValue("colorTolerance", m_settings.m_colorTolerance);
|
settings.setValue("colorTolerance", m_settings.m_colorTolerance);
|
||||||
|
settings.setValue("allowLaunchApplications", m_settings.m_allowLaunchApplications);
|
||||||
|
settings.setValue("allowLaunchURI", m_settings.m_allowLaunchURI);
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,9 @@ public:
|
|||||||
m_rendererSamples(16),
|
m_rendererSamples(16),
|
||||||
m_preferredMeshResolutionRatio(0.02),
|
m_preferredMeshResolutionRatio(0.02),
|
||||||
m_minimalMeshResolutionRatio(0.005),
|
m_minimalMeshResolutionRatio(0.005),
|
||||||
m_colorTolerance(0.01)
|
m_colorTolerance(0.01),
|
||||||
|
m_allowLaunchApplications(true),
|
||||||
|
m_allowLaunchURI(true)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -41,6 +43,8 @@ public:
|
|||||||
pdf::PDFReal m_preferredMeshResolutionRatio;
|
pdf::PDFReal m_preferredMeshResolutionRatio;
|
||||||
pdf::PDFReal m_minimalMeshResolutionRatio;
|
pdf::PDFReal m_minimalMeshResolutionRatio;
|
||||||
pdf::PDFReal m_colorTolerance;
|
pdf::PDFReal m_colorTolerance;
|
||||||
|
bool m_allowLaunchApplications;
|
||||||
|
bool m_allowLaunchURI;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Settings& getSettings() const { return m_settings; }
|
const Settings& getSettings() const { return m_settings; }
|
||||||
|
@ -21,6 +21,7 @@ PDFViewerSettingsDialog::PDFViewerSettingsDialog(const PDFViewerSettings::Settin
|
|||||||
new QListWidgetItem(QIcon(":/resources/engine.svg"), tr("Engine"), ui->optionsPagesWidget, EngineSettings);
|
new QListWidgetItem(QIcon(":/resources/engine.svg"), tr("Engine"), ui->optionsPagesWidget, EngineSettings);
|
||||||
new QListWidgetItem(QIcon(":/resources/rendering.svg"), tr("Rendering"), ui->optionsPagesWidget, RenderingSettings);
|
new QListWidgetItem(QIcon(":/resources/rendering.svg"), tr("Rendering"), ui->optionsPagesWidget, RenderingSettings);
|
||||||
new QListWidgetItem(QIcon(":/resources/shading.svg"), tr("Shading"), ui->optionsPagesWidget, ShadingSettings);
|
new QListWidgetItem(QIcon(":/resources/shading.svg"), tr("Shading"), ui->optionsPagesWidget, ShadingSettings);
|
||||||
|
new QListWidgetItem(QIcon(":/resources/security.svg"), tr("Security"), ui->optionsPagesWidget, SecuritySettings);
|
||||||
|
|
||||||
ui->renderingEngineComboBox->addItem(tr("Software"), static_cast<int>(pdf::RendererEngine::Software));
|
ui->renderingEngineComboBox->addItem(tr("Software"), static_cast<int>(pdf::RendererEngine::Software));
|
||||||
ui->renderingEngineComboBox->addItem(tr("Hardware accelerated (OpenGL)"), static_cast<int>(pdf::RendererEngine::OpenGL));
|
ui->renderingEngineComboBox->addItem(tr("Hardware accelerated (OpenGL)"), static_cast<int>(pdf::RendererEngine::OpenGL));
|
||||||
@ -30,20 +31,20 @@ PDFViewerSettingsDialog::PDFViewerSettingsDialog(const PDFViewerSettings::Settin
|
|||||||
ui->multisampleAntialiasingSamplesCountComboBox->addItem(QString::number(i), i);
|
ui->multisampleAntialiasingSamplesCountComboBox->addItem(QString::number(i), i);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (QWidget* widget : { ui->engineInfoLabel, ui->renderingInfoLabel })
|
for (QWidget* widget : { ui->engineInfoLabel, ui->renderingInfoLabel, ui->securityInfoLabel })
|
||||||
{
|
{
|
||||||
widget->setMinimumWidth(widget->sizeHint().width());
|
widget->setMinimumWidth(widget->sizeHint().width());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (QCheckBox* checkBox : { ui->multisampleAntialiasingCheckBox, ui->antialiasingCheckBox, ui->textAntialiasingCheckBox, ui->smoothPicturesCheckBox, ui->ignoreOptionalContentCheckBox, ui->clipToCropBoxCheckBox })
|
for (QCheckBox* checkBox : findChildren<QCheckBox*>())
|
||||||
{
|
{
|
||||||
connect(checkBox, &QCheckBox::clicked, this, &PDFViewerSettingsDialog::saveData);
|
connect(checkBox, &QCheckBox::clicked, this, &PDFViewerSettingsDialog::saveData);
|
||||||
}
|
}
|
||||||
for (QComboBox* comboBox : { ui->renderingEngineComboBox, ui->multisampleAntialiasingSamplesCountComboBox })
|
for (QComboBox* comboBox : findChildren<QComboBox*>())
|
||||||
{
|
{
|
||||||
connect(comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &PDFViewerSettingsDialog::saveData);
|
connect(comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &PDFViewerSettingsDialog::saveData);
|
||||||
}
|
}
|
||||||
for (QDoubleSpinBox* spinBox : { ui->preferredMeshResolutionEdit, ui->minimalMeshResolutionEdit, ui->colorToleranceEdit })
|
for (QDoubleSpinBox* spinBox : findChildren<QDoubleSpinBox*>())
|
||||||
{
|
{
|
||||||
connect(spinBox, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &PDFViewerSettingsDialog::saveData);
|
connect(spinBox, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &PDFViewerSettingsDialog::saveData);
|
||||||
}
|
}
|
||||||
@ -76,6 +77,10 @@ void PDFViewerSettingsDialog::on_optionsPagesWidget_currentItemChanged(QListWidg
|
|||||||
ui->stackedWidget->setCurrentWidget(ui->shadingPage);
|
ui->stackedWidget->setCurrentWidget(ui->shadingPage);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SecuritySettings:
|
||||||
|
ui->stackedWidget->setCurrentWidget(ui->securityPage);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Q_ASSERT(false);
|
Q_ASSERT(false);
|
||||||
break;
|
break;
|
||||||
@ -123,6 +128,10 @@ void PDFViewerSettingsDialog::loadData()
|
|||||||
ui->preferredMeshResolutionEdit->setValue(m_settings.m_preferredMeshResolutionRatio);
|
ui->preferredMeshResolutionEdit->setValue(m_settings.m_preferredMeshResolutionRatio);
|
||||||
ui->minimalMeshResolutionEdit->setValue(m_settings.m_minimalMeshResolutionRatio);
|
ui->minimalMeshResolutionEdit->setValue(m_settings.m_minimalMeshResolutionRatio);
|
||||||
ui->colorToleranceEdit->setValue(m_settings.m_colorTolerance);
|
ui->colorToleranceEdit->setValue(m_settings.m_colorTolerance);
|
||||||
|
|
||||||
|
// Security
|
||||||
|
ui->allowLaunchCheckBox->setChecked(m_settings.m_allowLaunchApplications);
|
||||||
|
ui->allowRunURICheckBox->setChecked(m_settings.m_allowLaunchURI);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PDFViewerSettingsDialog::saveData()
|
void PDFViewerSettingsDialog::saveData()
|
||||||
@ -178,6 +187,14 @@ void PDFViewerSettingsDialog::saveData()
|
|||||||
{
|
{
|
||||||
m_settings.m_colorTolerance = ui->colorToleranceEdit->value();
|
m_settings.m_colorTolerance = ui->colorToleranceEdit->value();
|
||||||
}
|
}
|
||||||
|
else if (sender == ui->allowLaunchCheckBox)
|
||||||
|
{
|
||||||
|
m_settings.m_allowLaunchApplications = ui->allowLaunchCheckBox->isChecked();
|
||||||
|
}
|
||||||
|
else if (sender == ui->allowRunURICheckBox)
|
||||||
|
{
|
||||||
|
m_settings.m_allowLaunchURI = ui->allowRunURICheckBox->isChecked();
|
||||||
|
}
|
||||||
|
|
||||||
loadData();
|
loadData();
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,8 @@ public:
|
|||||||
{
|
{
|
||||||
EngineSettings,
|
EngineSettings,
|
||||||
RenderingSettings,
|
RenderingSettings,
|
||||||
ShadingSettings
|
ShadingSettings,
|
||||||
|
SecuritySettings
|
||||||
};
|
};
|
||||||
|
|
||||||
const PDFViewerSettings::Settings& getSettings() const { return m_settings; }
|
const PDFViewerSettings::Settings& getSettings() const { return m_settings; }
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QStackedWidget" name="stackedWidget">
|
<widget class="QStackedWidget" name="stackedWidget">
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>0</number>
|
<number>3</number>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="enginePage">
|
<widget class="QWidget" name="enginePage">
|
||||||
<layout class="QVBoxLayout" name="enginePageLayout">
|
<layout class="QVBoxLayout" name="enginePageLayout">
|
||||||
@ -366,6 +366,86 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QWidget" name="securityPage">
|
||||||
|
<layout class="QVBoxLayout" name="securityPageLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="securitySettingsGroupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Security Settings</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="securitySettingsGroupBoxLayout">
|
||||||
|
<item>
|
||||||
|
<layout class="QGridLayout" name="securitySettingsGridLayout">
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QCheckBox" name="allowLaunchCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Enable</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="allowRunURILabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Allow URI link launch</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="allowRunExeLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Allow application launch</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QCheckBox" name="allowRunURICheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Enable</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="securityInfoLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string><html><head/><body><p>If launch of application is allowed, then, after <span style=" font-weight:600;">Launch</span> action is triggered, message box appears, which asks the user, if he wants to execute external application. If user confirms the dialog, then external application is executed (for example, by windows function <span style=" font-weight:600;">ShellExecute</span>). When this option is turned off, then no dialog appears and external application is never executed.</p><p>If <span style=" font-weight:600;">URI link </span>launch is allowed, then, again, message box appears and asks the user, if he wants to open the URI link. If user confirms the dialog, then URI link is opened by standard web browser (or standard application, if URI links to the file system).</p><p>User should be really carefull with these settings, and only confirm the execution, if the document is safe, i.e. it comes from reliable source. Launching external applications and following URI links can be harmfull to the user's computer.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="securityPageSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>74</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
|
122
PdfForQtViewer/resources/security.svg
Normal file
122
PdfForQtViewer/resources/security.svg
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="30mm"
|
||||||
|
height="30mm"
|
||||||
|
viewBox="0 0 30 30"
|
||||||
|
version="1.1"
|
||||||
|
id="svg5291"
|
||||||
|
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||||
|
sodipodi:docname="security.svg">
|
||||||
|
<defs
|
||||||
|
id="defs5285">
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="bspline"
|
||||||
|
id="path-effect831"
|
||||||
|
is_visible="true"
|
||||||
|
weight="33.333333"
|
||||||
|
steps="2"
|
||||||
|
helper_size="0"
|
||||||
|
apply_no_weight="true"
|
||||||
|
apply_with_weight="true"
|
||||||
|
only_selected="false" />
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="0.35"
|
||||||
|
inkscape:cx="-887.45193"
|
||||||
|
inkscape:cy="248.19877"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:window-width="3840"
|
||||||
|
inkscape:window-height="2035"
|
||||||
|
inkscape:window-x="-13"
|
||||||
|
inkscape:window-y="-13"
|
||||||
|
inkscape:window-maximized="1" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5288">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
<dc:creator>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>Jakub Melka</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:creator>
|
||||||
|
<cc:license
|
||||||
|
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
||||||
|
</cc:Work>
|
||||||
|
<cc:License
|
||||||
|
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||||
|
<cc:permits
|
||||||
|
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||||
|
<cc:requires
|
||||||
|
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||||
|
</cc:License>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Vrstva 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-267)">
|
||||||
|
<rect
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect827"
|
||||||
|
width="22.395088"
|
||||||
|
height="12.425969"
|
||||||
|
x="4.2049851"
|
||||||
|
y="280.18005"
|
||||||
|
ry="1.3063761"
|
||||||
|
rx="0.80319941" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
|
||||||
|
d="m 8.8351933,280.08557 c -0.015751,-1.9059 -0.0315,-3.81153 0.2362696,-5.37063 0.2677698,-1.55909 0.8190596,-2.77193 2.8738961,-3.37025 2.054836,-0.59831 5.615048,-0.58257 7.670115,0.0158 2.055068,0.59836 2.606322,1.77962 2.889831,3.29942 0.283509,1.51979 0.299258,3.37799 0.31501,5.23664"
|
||||||
|
id="path829"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
inkscape:path-effect="#path-effect831"
|
||||||
|
inkscape:original-d="m 8.8351933,280.08557 c -0.015483,-1.9059 -0.031233,-3.81153 -0.047247,-5.71689 0.5514038,-1.21278 1.1026949,-2.42562 1.6536457,-3.63803 3.558604,0.0155 7.118816,0.0312 10.677827,0.0472 0.55144,1.18083 1.102695,2.36209 1.653646,3.54353 0.01602,1.8583 0.03176,3.7165 0.04725,5.57515" />
|
||||||
|
<ellipse
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="path839"
|
||||||
|
cx="15.56845"
|
||||||
|
cy="285.39047"
|
||||||
|
rx="2.5390604"
|
||||||
|
ry="2.4555387" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect845"
|
||||||
|
width="2.5513394"
|
||||||
|
height="4.9136906"
|
||||||
|
x="14.315848"
|
||||||
|
y="286.88913"
|
||||||
|
rx="0.80319828"
|
||||||
|
ry="1.1575521" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 4.5 KiB |
Loading…
x
Reference in New Issue
Block a user