mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
Multimedia, rendition action
This commit is contained in:
@ -42,6 +42,7 @@ SOURCES += \
|
|||||||
sources/pdffile.cpp \
|
sources/pdffile.cpp \
|
||||||
sources/pdfitemmodels.cpp \
|
sources/pdfitemmodels.cpp \
|
||||||
sources/pdfjbig2decoder.cpp \
|
sources/pdfjbig2decoder.cpp \
|
||||||
|
sources/pdfmultimedia.cpp \
|
||||||
sources/pdfobject.cpp \
|
sources/pdfobject.cpp \
|
||||||
sources/pdfoptionalcontent.cpp \
|
sources/pdfoptionalcontent.cpp \
|
||||||
sources/pdfoutline.cpp \
|
sources/pdfoutline.cpp \
|
||||||
@ -78,6 +79,7 @@ HEADERS += \
|
|||||||
sources/pdfitemmodels.h \
|
sources/pdfitemmodels.h \
|
||||||
sources/pdfjbig2decoder.h \
|
sources/pdfjbig2decoder.h \
|
||||||
sources/pdfmeshqualitysettings.h \
|
sources/pdfmeshqualitysettings.h \
|
||||||
|
sources/pdfmultimedia.h \
|
||||||
sources/pdfobject.h \
|
sources/pdfobject.h \
|
||||||
sources/pdfoptionalcontent.h \
|
sources/pdfoptionalcontent.h \
|
||||||
sources/pdfoutline.h \
|
sources/pdfoutline.h \
|
||||||
|
@ -136,6 +136,111 @@ PDFActionPtr PDFAction::parseImpl(const PDFDocument* document, PDFObject object,
|
|||||||
{
|
{
|
||||||
return PDFActionPtr(new PDFActionURI(loader.readStringFromDictionary(dictionary, "URI"), loader.readBooleanFromDictionary(dictionary, "IsMap", false)));
|
return PDFActionPtr(new PDFActionURI(loader.readStringFromDictionary(dictionary, "URI"), loader.readBooleanFromDictionary(dictionary, "IsMap", false)));
|
||||||
}
|
}
|
||||||
|
else if (name == "Sound")
|
||||||
|
{
|
||||||
|
const PDFReal volume = loader.readNumberFromDictionary(dictionary, "Volume", 1.0);
|
||||||
|
const bool isSynchronous = loader.readBooleanFromDictionary(dictionary, "Synchronous", false);
|
||||||
|
const bool isRepeat = loader.readBooleanFromDictionary(dictionary, "Repeat", false);
|
||||||
|
const bool isMix = loader.readBooleanFromDictionary(dictionary, "Mix", false);
|
||||||
|
return PDFActionPtr(new PDFActionSound(PDFSound::parse(document, dictionary->get("Sound")), volume, isSynchronous, isRepeat, isMix));
|
||||||
|
}
|
||||||
|
else if (name == "Movie")
|
||||||
|
{
|
||||||
|
constexpr const std::array<std::pair<const char*, PDFActionMovie::Operation>, 4> operations = {
|
||||||
|
std::pair<const char*, PDFActionMovie::Operation>{ "Play", PDFActionMovie::Operation::Play },
|
||||||
|
std::pair<const char*, PDFActionMovie::Operation>{ "Stop", PDFActionMovie::Operation::Stop },
|
||||||
|
std::pair<const char*, PDFActionMovie::Operation>{ "Pause", PDFActionMovie::Operation::Pause },
|
||||||
|
std::pair<const char*, PDFActionMovie::Operation>{ "Resume", PDFActionMovie::Operation::Resume }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Jakub Melka: parse the movie action
|
||||||
|
PDFObject annotationObject = dictionary->get("Annotation");
|
||||||
|
PDFObjectReference annotation = annotationObject.isReference() ? annotationObject.getReference() : PDFObjectReference();
|
||||||
|
QString title = loader.readTextStringFromDictionary(dictionary, "T", QString());
|
||||||
|
PDFActionMovie::Operation operation = loader.readEnumByName(dictionary->get("Operation"), operations.cbegin(), operations.cend(), PDFActionMovie::Operation::Play);
|
||||||
|
|
||||||
|
return PDFActionPtr(new PDFActionMovie(annotation, qMove(title), operation));
|
||||||
|
}
|
||||||
|
else if (name == "Hide")
|
||||||
|
{
|
||||||
|
std::vector<PDFObjectReference> annotations;
|
||||||
|
std::vector<QString> fieldNames;
|
||||||
|
|
||||||
|
const PDFObject& object = dictionary->get("T");
|
||||||
|
if (object.isReference())
|
||||||
|
{
|
||||||
|
annotations = { object.getReference() };
|
||||||
|
}
|
||||||
|
else if (object.isString())
|
||||||
|
{
|
||||||
|
fieldNames = { loader.readTextString(object, QString()) };
|
||||||
|
}
|
||||||
|
else if (object.isArray())
|
||||||
|
{
|
||||||
|
const PDFArray* items = object.getArray();
|
||||||
|
for (size_t i = 0; i < items->getCount(); ++i)
|
||||||
|
{
|
||||||
|
const PDFObject& itemObject = items->getItem(i);
|
||||||
|
if (itemObject.isReference())
|
||||||
|
{
|
||||||
|
annotations.push_back(itemObject.getReference());
|
||||||
|
}
|
||||||
|
else if (itemObject.isString())
|
||||||
|
{
|
||||||
|
fieldNames.push_back(loader.readTextString(itemObject, QString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool hide = loader.readBooleanFromDictionary(dictionary, "H", true);
|
||||||
|
return PDFActionPtr(new PDFActionHide(qMove(annotations), qMove(fieldNames), hide));
|
||||||
|
}
|
||||||
|
else if (name == "Named")
|
||||||
|
{
|
||||||
|
constexpr const std::array<std::pair<const char*, PDFActionNamed::NamedActionType>, 4> types = {
|
||||||
|
std::pair<const char*, PDFActionNamed::NamedActionType>{ "NextPage", PDFActionNamed::NamedActionType::NextPage },
|
||||||
|
std::pair<const char*, PDFActionNamed::NamedActionType>{ "PrevPage", PDFActionNamed::NamedActionType::PrevPage },
|
||||||
|
std::pair<const char*, PDFActionNamed::NamedActionType>{ "FirstPage", PDFActionNamed::NamedActionType::FirstPage },
|
||||||
|
std::pair<const char*, PDFActionNamed::NamedActionType>{ "LastPage", PDFActionNamed::NamedActionType::LastPage }
|
||||||
|
};
|
||||||
|
|
||||||
|
QByteArray name = loader.readNameFromDictionary(dictionary, "N");
|
||||||
|
PDFActionNamed::NamedActionType actionType = loader.readEnumByName(dictionary->get("N"), types.cbegin(), types.cend(), PDFActionNamed::NamedActionType::Custom);
|
||||||
|
return PDFActionPtr(new PDFActionNamed(actionType, qMove(name)));
|
||||||
|
}
|
||||||
|
else if (name == "SetOCGState")
|
||||||
|
{
|
||||||
|
const bool isRadioButtonsPreserved = loader.readBooleanFromDictionary(dictionary, "PreserveRB", true);
|
||||||
|
PDFActionSetOCGState::StateChangeItems items;
|
||||||
|
|
||||||
|
PDFObject stateArrayObject = document->getObject(dictionary->get("State"));
|
||||||
|
if (stateArrayObject.isArray())
|
||||||
|
{
|
||||||
|
constexpr const std::array<std::pair<const char*, PDFActionSetOCGState::SwitchType>, 3> types = {
|
||||||
|
std::pair<const char*, PDFActionSetOCGState::SwitchType>{ "ON", PDFActionSetOCGState::SwitchType::ON },
|
||||||
|
std::pair<const char*, PDFActionSetOCGState::SwitchType>{ "OFF", PDFActionSetOCGState::SwitchType::OFF },
|
||||||
|
std::pair<const char*, PDFActionSetOCGState::SwitchType>{ "Toggle", PDFActionSetOCGState::SwitchType::Toggle }
|
||||||
|
};
|
||||||
|
|
||||||
|
PDFActionSetOCGState::SwitchType switchType = PDFActionSetOCGState::SwitchType::ON;
|
||||||
|
const PDFArray* stateArray = stateArrayObject.getArray();
|
||||||
|
items.reserve(stateArray->getCount());
|
||||||
|
for (size_t i = 0; i < stateArray->getCount(); ++i)
|
||||||
|
{
|
||||||
|
const PDFObject& item = stateArray->getItem(i);
|
||||||
|
if (item.isName())
|
||||||
|
{
|
||||||
|
switchType = loader.readEnumByName(item, types.cbegin(), types.cend(), PDFActionSetOCGState::SwitchType::ON);
|
||||||
|
}
|
||||||
|
else if (item.isReference())
|
||||||
|
{
|
||||||
|
items.emplace_back(switchType, item.getReference());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return PDFActionPtr(new PDFActionSetOCGState(qMove(items), isRadioButtonsPreserved));
|
||||||
|
}
|
||||||
|
|
||||||
return PDFActionPtr();
|
return PDFActionPtr();
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "pdfglobal.h"
|
#include "pdfglobal.h"
|
||||||
#include "pdfobject.h"
|
#include "pdfobject.h"
|
||||||
#include "pdffile.h"
|
#include "pdffile.h"
|
||||||
|
#include "pdfmultimedia.h"
|
||||||
|
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
|
|
||||||
@ -40,7 +41,12 @@ enum class ActionType
|
|||||||
GoToE,
|
GoToE,
|
||||||
Launch,
|
Launch,
|
||||||
Thread,
|
Thread,
|
||||||
URI
|
URI,
|
||||||
|
Sound,
|
||||||
|
Movie,
|
||||||
|
Hide,
|
||||||
|
Named,
|
||||||
|
SetOCGState
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class DestinationType
|
enum class DestinationType
|
||||||
@ -264,6 +270,149 @@ private:
|
|||||||
bool m_isMap;
|
bool m_isMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PDFActionSound : public PDFAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit inline PDFActionSound(PDFSound sound, PDFReal volume, bool isSynchronous, bool isRepeat, bool isMix) :
|
||||||
|
m_sound(qMove(sound)),
|
||||||
|
m_volume(qMove(volume)),
|
||||||
|
m_isSynchronous(isSynchronous),
|
||||||
|
m_isRepeat(isRepeat),
|
||||||
|
m_isMix(isMix)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ActionType getType() const override { return ActionType::Sound; }
|
||||||
|
|
||||||
|
const PDFSound* getSound() const { return &m_sound; }
|
||||||
|
PDFReal getVolume() const { return m_volume; }
|
||||||
|
bool isSynchronous() const { return m_isSynchronous; }
|
||||||
|
bool isRepeat() const { return m_isRepeat; }
|
||||||
|
bool isMix() const { return m_isMix; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
PDFSound m_sound;
|
||||||
|
PDFReal m_volume;
|
||||||
|
bool m_isSynchronous;
|
||||||
|
bool m_isRepeat;
|
||||||
|
bool m_isMix;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PDFActionMovie : public PDFAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class Operation
|
||||||
|
{
|
||||||
|
Play,
|
||||||
|
Stop,
|
||||||
|
Pause,
|
||||||
|
Resume
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit inline PDFActionMovie(PDFObjectReference annotation, QString title, Operation operation) :
|
||||||
|
m_annotation(qMove(annotation)),
|
||||||
|
m_title(qMove(title)),
|
||||||
|
m_operation(operation)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ActionType getType() const override { return ActionType::Movie; }
|
||||||
|
|
||||||
|
PDFObjectReference getAnnotation() const { return m_annotation; }
|
||||||
|
const QString& getTitle() const { return m_title; }
|
||||||
|
Operation getOperation() const { return m_operation; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
PDFObjectReference m_annotation;
|
||||||
|
QString m_title;
|
||||||
|
Operation m_operation;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PDFActionHide : public PDFAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit inline PDFActionHide(std::vector<PDFObjectReference>&& annotations, std::vector<QString>&& fieldNames, bool hide) :
|
||||||
|
m_annotations(qMove(annotations)),
|
||||||
|
m_fieldNames(qMove(fieldNames)),
|
||||||
|
m_hide(hide)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ActionType getType() const override { return ActionType::Hide; }
|
||||||
|
|
||||||
|
const std::vector<PDFObjectReference>& getAnnotations() const { return m_annotations; }
|
||||||
|
const std::vector<QString>& getFieldNames() const { return m_fieldNames; }
|
||||||
|
bool isHide() const { return m_hide; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<PDFObjectReference> m_annotations;
|
||||||
|
std::vector<QString> m_fieldNames;
|
||||||
|
bool m_hide;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PDFActionNamed : public PDFAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class NamedActionType
|
||||||
|
{
|
||||||
|
Custom,
|
||||||
|
NextPage,
|
||||||
|
PrevPage,
|
||||||
|
FirstPage,
|
||||||
|
LastPage
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit inline PDFActionNamed(NamedActionType namedActionType, QByteArray&& customNamedAction) :
|
||||||
|
m_namedActionType(namedActionType),
|
||||||
|
m_customNamedAction(qMove(customNamedAction))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ActionType getType() const override { return ActionType::Named; }
|
||||||
|
|
||||||
|
NamedActionType getNamedActionType() const { return m_namedActionType; }
|
||||||
|
const QByteArray& getCustomNamedAction() const { return m_customNamedAction; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
NamedActionType m_namedActionType;
|
||||||
|
QByteArray m_customNamedAction;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PDFActionSetOCGState : public PDFAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum class SwitchType
|
||||||
|
{
|
||||||
|
ON,
|
||||||
|
OFF,
|
||||||
|
Toggle
|
||||||
|
};
|
||||||
|
|
||||||
|
using StateChangeItem = std::pair<SwitchType, PDFObjectReference>;
|
||||||
|
using StateChangeItems = std::vector<StateChangeItem>;
|
||||||
|
|
||||||
|
explicit inline PDFActionSetOCGState(StateChangeItems&& stateChangeItems, bool isRadioButtonsPreserved) :
|
||||||
|
m_items(qMove(stateChangeItems)),
|
||||||
|
m_isRadioButtonsPreserved(isRadioButtonsPreserved)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ActionType getType() const override { return ActionType::SetOCGState; }
|
||||||
|
|
||||||
|
const StateChangeItems& getStateChangeItems() const { return m_items; }
|
||||||
|
bool isRadioButtonsPreserved() const { return m_isRadioButtonsPreserved; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
StateChangeItems m_items;
|
||||||
|
bool m_isRadioButtonsPreserved;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace pdf
|
} // namespace pdf
|
||||||
|
|
||||||
#endif // PDFACTION_H
|
#endif // PDFACTION_H
|
||||||
|
@ -547,4 +547,46 @@ QByteArray PDFDocumentDataLoaderDecorator::readStringFromDictionary(const PDFDic
|
|||||||
return QByteArray();
|
return QByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<QByteArray> PDFDocumentDataLoaderDecorator::readStringArrayFromDictionary(const PDFDictionary* dictionary, const char* key) const
|
||||||
|
{
|
||||||
|
if (dictionary->hasKey(key))
|
||||||
|
{
|
||||||
|
return readStringArray(dictionary->get(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::vector<QByteArray>();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<QByteArray> PDFDocumentDataLoaderDecorator::readStringArray(const PDFObject& object) const
|
||||||
|
{
|
||||||
|
const PDFObject& dereferencedObject = m_document->getObject(object);
|
||||||
|
if (dereferencedObject.isArray())
|
||||||
|
{
|
||||||
|
const PDFArray* array = dereferencedObject.getArray();
|
||||||
|
std::vector<QByteArray> result;
|
||||||
|
const size_t count = array->getCount();
|
||||||
|
result.reserve(count);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
const PDFObject& stringObject = array->getItem(i);
|
||||||
|
if (stringObject.isString())
|
||||||
|
{
|
||||||
|
result.push_back(stringObject.getString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We assume, that RVO (return value optimization) will not work for this function
|
||||||
|
// (multiple return points).
|
||||||
|
return std::move(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::vector<QByteArray>();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace pdf
|
} // namespace pdf
|
||||||
|
@ -139,7 +139,7 @@ public:
|
|||||||
Enum readEnumByName(const PDFObject& object, Iterator begin, Iterator end, Enum defaultValue) const
|
Enum readEnumByName(const PDFObject& object, Iterator begin, Iterator end, Enum defaultValue) const
|
||||||
{
|
{
|
||||||
const PDFObject& dereferencedObject = m_document->getObject(object);
|
const PDFObject& dereferencedObject = m_document->getObject(object);
|
||||||
if (dereferencedObject.isName())
|
if (dereferencedObject.isName() || dereferencedObject.isString())
|
||||||
{
|
{
|
||||||
QByteArray name = dereferencedObject.getString();
|
QByteArray name = dereferencedObject.getString();
|
||||||
|
|
||||||
@ -258,6 +258,11 @@ public:
|
|||||||
/// \param object Object containing array of references
|
/// \param object Object containing array of references
|
||||||
std::vector<QByteArray> readNameArray(const PDFObject& object) const;
|
std::vector<QByteArray> readNameArray(const PDFObject& object) const;
|
||||||
|
|
||||||
|
/// Reads string array. Reads all values. If error occurs,
|
||||||
|
/// then empty array is returned.
|
||||||
|
/// \param object Object containing array of references
|
||||||
|
std::vector<QByteArray> readStringArray(const PDFObject& object) const;
|
||||||
|
|
||||||
/// Reads name array from dictionary. Reads all values. If error occurs,
|
/// Reads name array from dictionary. Reads all values. If error occurs,
|
||||||
/// then empty array is returned.
|
/// then empty array is returned.
|
||||||
/// \param dictionary Dictionary containing desired data
|
/// \param dictionary Dictionary containing desired data
|
||||||
@ -280,6 +285,12 @@ public:
|
|||||||
/// \param key Entry key
|
/// \param key Entry key
|
||||||
QByteArray readStringFromDictionary(const PDFDictionary* dictionary, const char* key);
|
QByteArray readStringFromDictionary(const PDFDictionary* dictionary, const char* key);
|
||||||
|
|
||||||
|
/// Reads string array from dictionary. Reads all values. If error occurs,
|
||||||
|
/// then empty array is returned.
|
||||||
|
/// \param dictionary Dictionary containing desired data
|
||||||
|
/// \param key Entry key
|
||||||
|
std::vector<QByteArray> readStringArrayFromDictionary(const PDFDictionary* dictionary, const char* key) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const PDFDocument* m_document;
|
const PDFDocument* m_document;
|
||||||
};
|
};
|
||||||
@ -326,6 +337,10 @@ public:
|
|||||||
/// is returned (no exception is thrown).
|
/// is returned (no exception is thrown).
|
||||||
const PDFObject& getObject(const PDFObject& object) const;
|
const PDFObject& getObject(const PDFObject& object) const;
|
||||||
|
|
||||||
|
/// Returns dictionary from an object. If object is not a dictionary,
|
||||||
|
/// then nullptr is returned (no exception is thrown).
|
||||||
|
const PDFDictionary* getDictionaryFromObject(const PDFObject& object) const;
|
||||||
|
|
||||||
/// Returns object by reference. If dereference attempt fails, then null object
|
/// Returns object by reference. If dereference attempt fails, then null object
|
||||||
/// is returned (no exception is thrown).
|
/// is returned (no exception is thrown).
|
||||||
const PDFObject& getObjectByReference(PDFObjectReference reference) const;
|
const PDFObject& getObjectByReference(PDFObjectReference reference) const;
|
||||||
@ -383,6 +398,22 @@ const PDFObject& PDFDocument::getObject(const PDFObject& object) const
|
|||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
const PDFDictionary* PDFDocument::getDictionaryFromObject(const PDFObject& object) const
|
||||||
|
{
|
||||||
|
const PDFObject& dereferencedObject = getObject(object);
|
||||||
|
if (dereferencedObject.isDictionary())
|
||||||
|
{
|
||||||
|
return dereferencedObject.getDictionary();
|
||||||
|
}
|
||||||
|
else if (dereferencedObject.isStream())
|
||||||
|
{
|
||||||
|
return dereferencedObject.getStream()->getDictionary();
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
const PDFObject& PDFDocument::getObjectByReference(PDFObjectReference reference) const
|
const PDFObject& PDFDocument::getObjectByReference(PDFObjectReference reference) const
|
||||||
{
|
{
|
||||||
|
540
PdfForQtLib/sources/pdfmultimedia.cpp
Normal file
540
PdfForQtLib/sources/pdfmultimedia.cpp
Normal file
@ -0,0 +1,540 @@
|
|||||||
|
// 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 "pdfmultimedia.h"
|
||||||
|
#include "pdfdocument.h"
|
||||||
|
|
||||||
|
namespace pdf
|
||||||
|
{
|
||||||
|
|
||||||
|
PDFSound PDFSound::parse(const PDFDocument* document, PDFObject object)
|
||||||
|
{
|
||||||
|
PDFSound result;
|
||||||
|
|
||||||
|
object = document->getObject(object);
|
||||||
|
if (object.isStream())
|
||||||
|
{
|
||||||
|
const PDFStream* stream = object.getStream();
|
||||||
|
const PDFDictionary* dictionary = stream->getDictionary();
|
||||||
|
|
||||||
|
constexpr const std::array<std::pair<const char*, Format>, 4> formats = {
|
||||||
|
std::pair<const char*, Format>{ "Raw", Format::Raw },
|
||||||
|
std::pair<const char*, Format>{ "Signed", Format::Signed },
|
||||||
|
std::pair<const char*, Format>{ "muLaw", Format::muLaw },
|
||||||
|
std::pair<const char*, Format>{ "ALaw", Format::ALaw }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Jakub Melka: parse the sound without exceptions
|
||||||
|
PDFDocumentDataLoaderDecorator loader(document);
|
||||||
|
result.m_fileSpecification = PDFFileSpecification::parse(document, dictionary->get("F"));
|
||||||
|
result.m_samplingRate = loader.readNumberFromDictionary(dictionary, "R", 0.0);
|
||||||
|
result.m_channels = loader.readIntegerFromDictionary(dictionary, "C", 1);
|
||||||
|
result.m_bitsPerSample = loader.readIntegerFromDictionary(dictionary, "B", 8);
|
||||||
|
result.m_format = loader.readEnumByName(dictionary->get("E"), formats.cbegin(), formats.cend(), Format::Raw);
|
||||||
|
result.m_soundCompression = loader.readNameFromDictionary(dictionary, "CO");
|
||||||
|
result.m_soundCompressionParameters = document->getObject(dictionary->get("CP"));
|
||||||
|
result.m_streamObject = object;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFRendition PDFRendition::parse(const PDFDocument* document, PDFObject object)
|
||||||
|
{
|
||||||
|
PDFRendition result;
|
||||||
|
object = document->getObject(object);
|
||||||
|
const PDFDictionary* renditionDictionary = nullptr;
|
||||||
|
if (object.isDictionary())
|
||||||
|
{
|
||||||
|
renditionDictionary = object.getDictionary();
|
||||||
|
}
|
||||||
|
else if (object.isStream())
|
||||||
|
{
|
||||||
|
renditionDictionary = object.getStream()->getDictionary();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (renditionDictionary)
|
||||||
|
{
|
||||||
|
constexpr const std::array<std::pair<const char*, Type>, 2> types = {
|
||||||
|
std::pair<const char*, Type>{ "MR", Type::Media },
|
||||||
|
std::pair<const char*, Type>{ "SR", Type::Selector }
|
||||||
|
};
|
||||||
|
|
||||||
|
PDFDocumentDataLoaderDecorator loader(document);
|
||||||
|
result.m_type = loader.readEnumByName(renditionDictionary->get("S"), types.cbegin(), types.cend(), Type::Invalid);
|
||||||
|
result.m_name = loader.readTextStringFromDictionary(renditionDictionary, "N", QString());
|
||||||
|
|
||||||
|
auto readMediaCriteria = [renditionDictionary, document](const char* entry)
|
||||||
|
{
|
||||||
|
PDFObject dictionaryObject = document->getObject(renditionDictionary->get(entry));
|
||||||
|
if (dictionaryObject.isDictionary())
|
||||||
|
{
|
||||||
|
const PDFDictionary* dictionary = dictionaryObject.getDictionary();
|
||||||
|
return PDFMediaCriteria::parse(document, dictionary->get("C"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return PDFMediaCriteria();
|
||||||
|
};
|
||||||
|
|
||||||
|
result.m_mustHonored = readMediaCriteria("MH");
|
||||||
|
result.m_bestEffort = readMediaCriteria("BE");
|
||||||
|
|
||||||
|
switch (result.m_type)
|
||||||
|
{
|
||||||
|
case Type::Media:
|
||||||
|
{
|
||||||
|
MediaRenditionData data;
|
||||||
|
data.clip = PDFMediaClip::parse(document, renditionDictionary->get("C"));
|
||||||
|
data.playParameters = PDFMediaPlayParameters::parse(document, renditionDictionary->get("P"));
|
||||||
|
data.screenParameters = PDFMediaScreenParameters::parse(document, renditionDictionary->get("SP"));
|
||||||
|
|
||||||
|
result.m_data = qMove(data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Type::Selector:
|
||||||
|
{
|
||||||
|
result.m_data = SelectorRenditionData{ renditionDictionary->get("R") };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFMediaMinimumBitDepth PDFMediaMinimumBitDepth::parse(const PDFDocument* document, PDFObject object)
|
||||||
|
{
|
||||||
|
if (const PDFDictionary* dictionary = document->getDictionaryFromObject(object))
|
||||||
|
{
|
||||||
|
PDFDocumentDataLoaderDecorator loader(document);
|
||||||
|
return PDFMediaMinimumBitDepth(loader.readIntegerFromDictionary(dictionary, "V", -1), loader.readIntegerFromDictionary(dictionary, "M", 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return PDFMediaMinimumBitDepth(-1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFMediaMinimumScreenSize PDFMediaMinimumScreenSize::parse(const PDFDocument* document, PDFObject object)
|
||||||
|
{
|
||||||
|
if (const PDFDictionary* dictionary = document->getDictionaryFromObject(object))
|
||||||
|
{
|
||||||
|
PDFDocumentDataLoaderDecorator loader(document);
|
||||||
|
std::vector<PDFInteger> values = loader.readIntegerArrayFromDictionary(dictionary, "V");
|
||||||
|
if (values.size() == 2)
|
||||||
|
{
|
||||||
|
return PDFMediaMinimumScreenSize(values[0], values[1], loader.readIntegerFromDictionary(dictionary, "M", 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return PDFMediaMinimumScreenSize(-1, -1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFMediaSoftwareIdentifier PDFMediaSoftwareIdentifier::parse(const PDFDocument* document, PDFObject object)
|
||||||
|
{
|
||||||
|
if (const PDFDictionary* dictionary = document->getDictionaryFromObject(object))
|
||||||
|
{
|
||||||
|
PDFDocumentDataLoaderDecorator loader(document);
|
||||||
|
return PDFMediaSoftwareIdentifier(loader.readTextStringFromDictionary(dictionary, "U", QString()).toLatin1(),
|
||||||
|
loader.readIntegerArrayFromDictionary(dictionary, "L"),
|
||||||
|
loader.readIntegerArrayFromDictionary(dictionary, "H"),
|
||||||
|
loader.readBooleanFromDictionary(dictionary, "LI", true),
|
||||||
|
loader.readBooleanFromDictionary(dictionary, "HI", true),
|
||||||
|
loader.readStringArrayFromDictionary(dictionary, "OS"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return PDFMediaSoftwareIdentifier(QByteArray(), { }, { }, true, true, { });
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFMediaCriteria PDFMediaCriteria::parse(const PDFDocument* document, PDFObject object)
|
||||||
|
{
|
||||||
|
PDFMediaCriteria criteria;
|
||||||
|
|
||||||
|
if (const PDFDictionary* dictionary = document->getDictionaryFromObject(object))
|
||||||
|
{
|
||||||
|
PDFDocumentDataLoaderDecorator loader(document);
|
||||||
|
auto readBoolean = [&loader, dictionary](const char* name, std::optional<bool>& value)
|
||||||
|
{
|
||||||
|
if (dictionary->hasKey(name))
|
||||||
|
{
|
||||||
|
value = loader.readBooleanFromDictionary(dictionary, name, false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
readBoolean("A", criteria.m_audioDescriptions);
|
||||||
|
readBoolean("C", criteria.m_textCaptions);
|
||||||
|
readBoolean("O", criteria.m_audioOverdubs);
|
||||||
|
readBoolean("S", criteria.m_subtitles);
|
||||||
|
if (dictionary->hasKey("R"))
|
||||||
|
{
|
||||||
|
criteria.m_bitrate = loader.readIntegerFromDictionary(dictionary, "R", 0);
|
||||||
|
}
|
||||||
|
if (dictionary->hasKey("D"))
|
||||||
|
{
|
||||||
|
criteria.m_minimumBitDepth = PDFMediaMinimumBitDepth::parse(document, dictionary->get("D"));
|
||||||
|
}
|
||||||
|
if (dictionary->hasKey("Z"))
|
||||||
|
{
|
||||||
|
criteria.m_minimumScreenSize = PDFMediaMinimumScreenSize::parse(document, dictionary->get("Z"));
|
||||||
|
}
|
||||||
|
if (dictionary->hasKey("V"))
|
||||||
|
{
|
||||||
|
const PDFObject& viewerObject = document->getObject(dictionary->get("V"));
|
||||||
|
if (viewerObject.isArray())
|
||||||
|
{
|
||||||
|
std::vector<PDFMediaSoftwareIdentifier> viewers;
|
||||||
|
const PDFArray* viewersArray = viewerObject.getArray();
|
||||||
|
viewers.reserve(viewersArray->getCount());
|
||||||
|
for (size_t i = 0; i < viewersArray->getCount(); ++i)
|
||||||
|
{
|
||||||
|
viewers.emplace_back(PDFMediaSoftwareIdentifier::parse(document, viewersArray->getItem(i)));
|
||||||
|
}
|
||||||
|
criteria.m_viewers = qMove(viewers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::vector<QByteArray> pdfVersions = loader.readNameArrayFromDictionary(dictionary, "P");
|
||||||
|
if (pdfVersions.size() > 0)
|
||||||
|
{
|
||||||
|
criteria.m_minimumPdfVersion = qMove(pdfVersions[0]);
|
||||||
|
if (pdfVersions.size() > 1)
|
||||||
|
{
|
||||||
|
criteria.m_maximumPdfVersion = qMove(pdfVersions[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dictionary->hasKey("L"))
|
||||||
|
{
|
||||||
|
criteria.m_languages = loader.readStringArrayFromDictionary(dictionary, "L");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return criteria;
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFMediaPermissions PDFMediaPermissions::parse(const PDFDocument* document, PDFObject object)
|
||||||
|
{
|
||||||
|
if (const PDFDictionary* dictionary = document->getDictionaryFromObject(object))
|
||||||
|
{
|
||||||
|
PDFDocumentDataLoaderDecorator loader(document);
|
||||||
|
constexpr const std::array<std::pair<const char*, Permission>, 4> types = {
|
||||||
|
std::pair<const char*, Permission>{ "TEMPNEVER", Permission::Never },
|
||||||
|
std::pair<const char*, Permission>{ "TEMPEXTRACT", Permission::Extract },
|
||||||
|
std::pair<const char*, Permission>{ "TEMPACCESS", Permission::Access },
|
||||||
|
std::pair<const char*, Permission>{ "TEMPALWAYS", Permission::Always }
|
||||||
|
};
|
||||||
|
|
||||||
|
return PDFMediaPermissions(loader.readEnumByName(dictionary->get("TF"), types.cbegin(), types.cend(), Permission::Never));
|
||||||
|
}
|
||||||
|
|
||||||
|
return PDFMediaPermissions(Permission::Never);
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFMediaPlayers PDFMediaPlayers::parse(const PDFDocument* document, PDFObject object)
|
||||||
|
{
|
||||||
|
if (const PDFDictionary* dictionary = document->getDictionaryFromObject(object))
|
||||||
|
{
|
||||||
|
auto readPlayers = [document, dictionary](const char* key)
|
||||||
|
{
|
||||||
|
std::vector<PDFMediaPlayer> result;
|
||||||
|
|
||||||
|
const PDFObject& playersArrayObject = document->getObject(dictionary->get(key));
|
||||||
|
if (playersArrayObject.isArray())
|
||||||
|
{
|
||||||
|
const PDFArray* playersArray = playersArrayObject.getArray();
|
||||||
|
result.reserve(playersArray->getCount());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < playersArray->getCount(); ++i)
|
||||||
|
{
|
||||||
|
result.emplace_back(PDFMediaPlayer::parse(document, playersArray->getItem(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
return PDFMediaPlayers(readPlayers("MU"), readPlayers("A"), readPlayers("NU"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return PDFMediaPlayers({ }, { }, { });
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFMediaPlayer PDFMediaPlayer::parse(const PDFDocument* document, PDFObject object)
|
||||||
|
{
|
||||||
|
if (const PDFDictionary* dictionary = document->getDictionaryFromObject(object))
|
||||||
|
{
|
||||||
|
return PDFMediaPlayer(PDFMediaSoftwareIdentifier::parse(document, dictionary->get("PID")));
|
||||||
|
}
|
||||||
|
return PDFMediaPlayer(PDFMediaSoftwareIdentifier(QByteArray(), { }, { }, true, true, { }));
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFMediaOffset PDFMediaOffset::parse(const PDFDocument* document, PDFObject object)
|
||||||
|
{
|
||||||
|
if (const PDFDictionary* dictionary = document->getDictionaryFromObject(object))
|
||||||
|
{
|
||||||
|
PDFDocumentDataLoaderDecorator loader(document);
|
||||||
|
QByteArray S = loader.readNameFromDictionary(dictionary, "S");
|
||||||
|
if (S == "T")
|
||||||
|
{
|
||||||
|
if (const PDFDictionary* timespanDictionary = document->getDictionaryFromObject(dictionary->get("T")))
|
||||||
|
{
|
||||||
|
return PDFMediaOffset(Type::Time, TimeData{ loader.readIntegerFromDictionary(timespanDictionary, "V", 0) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (S == "F")
|
||||||
|
{
|
||||||
|
return PDFMediaOffset(Type::Frame, FrameData{ loader.readIntegerFromDictionary(dictionary, "F", 0) });
|
||||||
|
}
|
||||||
|
else if (S == "M")
|
||||||
|
{
|
||||||
|
return PDFMediaOffset(Type::Marker, MarkerData{ loader.readTextStringFromDictionary(dictionary, "M", QString()) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return PDFMediaOffset(Type::Invalid, std::monostate());
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFMediaClip PDFMediaClip::parse(const PDFDocument* document, PDFObject object)
|
||||||
|
{
|
||||||
|
MediaClipData clipData;
|
||||||
|
std::vector<MediaSectionData> sections;
|
||||||
|
|
||||||
|
if (const PDFDictionary* dictionary = document->getDictionaryFromObject(object))
|
||||||
|
{
|
||||||
|
PDFDocumentDataLoaderDecorator loader(document);
|
||||||
|
std::set<PDFObjectReference> usedReferences;
|
||||||
|
while (dictionary)
|
||||||
|
{
|
||||||
|
QByteArray type = loader.readNameFromDictionary(dictionary, "S");
|
||||||
|
if (type == "MCD")
|
||||||
|
{
|
||||||
|
clipData.name = loader.readTextStringFromDictionary(dictionary, "N", QString());
|
||||||
|
|
||||||
|
PDFObject fileSpecificationOrStreamObject = document->getObject(dictionary->get("D"));
|
||||||
|
if (fileSpecificationOrStreamObject.isStream())
|
||||||
|
{
|
||||||
|
clipData.dataStream = fileSpecificationOrStreamObject;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clipData.fileSpecification = PDFFileSpecification::parse(document, fileSpecificationOrStreamObject);
|
||||||
|
}
|
||||||
|
clipData.contentType = loader.readStringFromDictionary(dictionary, "CT");
|
||||||
|
clipData.permissions = PDFMediaPermissions::parse(document, dictionary->get("P"));
|
||||||
|
clipData.alternateTextDescriptions = PDFMediaMultiLanguageTexts::parse(document, dictionary->get("Alt"));
|
||||||
|
clipData.players = PDFMediaPlayers::parse(document, dictionary->get("PL"));
|
||||||
|
|
||||||
|
auto getBaseUrl = [&loader, document, dictionary](const char* name)
|
||||||
|
{
|
||||||
|
if (const PDFDictionary* subdictionary = document->getDictionaryFromObject(dictionary->get(name)))
|
||||||
|
{
|
||||||
|
return loader.readStringFromDictionary(subdictionary, "BU");
|
||||||
|
}
|
||||||
|
|
||||||
|
return QByteArray();
|
||||||
|
};
|
||||||
|
clipData.m_baseUrlMustHonored = getBaseUrl("MH");
|
||||||
|
clipData.m_baseUrlBestEffort = getBaseUrl("BE");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (type == "MCS")
|
||||||
|
{
|
||||||
|
MediaSectionData sectionData;
|
||||||
|
sectionData.name = loader.readTextStringFromDictionary(dictionary, "N", QString());
|
||||||
|
sectionData.alternateTextDescriptions = PDFMediaMultiLanguageTexts::parse(document, dictionary->get("Alt"));
|
||||||
|
|
||||||
|
auto readMediaSectionBeginEnd = [document, dictionary](const char* name)
|
||||||
|
{
|
||||||
|
MediaSectionBeginEnd result;
|
||||||
|
|
||||||
|
if (const PDFDictionary* subdictionary = document->getDictionaryFromObject(dictionary->get(name)))
|
||||||
|
{
|
||||||
|
result.offsetBegin = PDFMediaOffset::parse(document, subdictionary->get("B"));
|
||||||
|
result.offsetEnd = PDFMediaOffset::parse(document, subdictionary->get("E"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
sectionData.m_mustHonored = readMediaSectionBeginEnd("MH");
|
||||||
|
sectionData.m_bestEffort = readMediaSectionBeginEnd("BE");
|
||||||
|
|
||||||
|
// Jakub Melka: parse next item in linked list
|
||||||
|
PDFObject next = dictionary->get("D");
|
||||||
|
if (next.isReference())
|
||||||
|
{
|
||||||
|
if (usedReferences.count(next.getReference()))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
usedReferences.insert(next.getReference());
|
||||||
|
}
|
||||||
|
dictionary = document->getDictionaryFromObject(next);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
dictionary = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return PDFMediaClip(qMove(clipData), qMove(sections));
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFMediaMultiLanguageTexts PDFMediaMultiLanguageTexts::parse(const PDFDocument* document, PDFObject object)
|
||||||
|
{
|
||||||
|
PDFMediaMultiLanguageTexts texts;
|
||||||
|
|
||||||
|
object = document->getObject(object);
|
||||||
|
if (object.isArray())
|
||||||
|
{
|
||||||
|
const PDFArray* array = object.getArray();
|
||||||
|
if (array->getCount() % 2 == 0)
|
||||||
|
{
|
||||||
|
PDFDocumentDataLoaderDecorator loader(document);
|
||||||
|
const size_t pairs = array->getCount() / 2;
|
||||||
|
for (size_t i = 0; i < pairs; ++i)
|
||||||
|
{
|
||||||
|
const PDFObject& languageName = document->getObject(array->getItem(2 * i));
|
||||||
|
const PDFObject& text = array->getItem(2 * i + 1);
|
||||||
|
|
||||||
|
if (languageName.isString())
|
||||||
|
{
|
||||||
|
texts.texts[languageName.getString()] = loader.readTextString(text, QString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return texts;
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFMediaPlayParameters PDFMediaPlayParameters::parse(const PDFDocument* document, PDFObject object)
|
||||||
|
{
|
||||||
|
PDFMediaPlayParameters result;
|
||||||
|
|
||||||
|
if (const PDFDictionary* dictionary = document->getDictionaryFromObject(object))
|
||||||
|
{
|
||||||
|
result.m_players = PDFMediaPlayers::parse(document, dictionary->get("PL"));
|
||||||
|
|
||||||
|
auto getPlayParameters = [document, dictionary](const char* name)
|
||||||
|
{
|
||||||
|
PlayParameters parameters;
|
||||||
|
|
||||||
|
if (const PDFDictionary* subdictionary = document->getDictionaryFromObject(dictionary->get(name)))
|
||||||
|
{
|
||||||
|
PDFDocumentDataLoaderDecorator loader(document);
|
||||||
|
parameters.volume = loader.readIntegerFromDictionary(subdictionary, "V", 100);
|
||||||
|
parameters.controllerUserInterface = loader.readBooleanFromDictionary(subdictionary, "C", false);
|
||||||
|
parameters.fitMode = static_cast<FitMode>(loader.readIntegerFromDictionary(subdictionary, "F", 5));
|
||||||
|
parameters.playAutomatically = loader.readBooleanFromDictionary(subdictionary, "A", true);
|
||||||
|
parameters.repeat = loader.readNumberFromDictionary(dictionary, "RC", 1.0);
|
||||||
|
|
||||||
|
if (const PDFDictionary* durationDictionary = document->getDictionaryFromObject(subdictionary->get("D")))
|
||||||
|
{
|
||||||
|
constexpr const std::array<std::pair<const char*, Duration>, 3> durations = {
|
||||||
|
std::pair<const char*, Duration>{ "I", Duration::Intrinsic },
|
||||||
|
std::pair<const char*, Duration>{ "F", Duration::Infinity },
|
||||||
|
std::pair<const char*, Duration>{ "T", Duration::Seconds }
|
||||||
|
};
|
||||||
|
parameters.duration = loader.readEnumByName(durationDictionary->get("S"), durations.cbegin(), durations.cend(), Duration::Intrinsic);
|
||||||
|
|
||||||
|
if (const PDFDictionary* timeDictionary = document->getDictionaryFromObject(durationDictionary->get("T")))
|
||||||
|
{
|
||||||
|
parameters.durationSeconds = loader.readNumberFromDictionary(timeDictionary, "V", 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return parameters;
|
||||||
|
};
|
||||||
|
result.m_mustHonored = getPlayParameters("MH");
|
||||||
|
result.m_bestEffort = getPlayParameters("BE");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFMediaScreenParameters PDFMediaScreenParameters::parse(const PDFDocument* document, PDFObject object)
|
||||||
|
{
|
||||||
|
if (const PDFDictionary* dictionary = document->getDictionaryFromObject(object))
|
||||||
|
{
|
||||||
|
auto getScreenParameters = [document, dictionary](const char* name)
|
||||||
|
{
|
||||||
|
ScreenParameters result;
|
||||||
|
if (const PDFDictionary* screenDictionary = document->getDictionaryFromObject(dictionary->get(name)))
|
||||||
|
{
|
||||||
|
PDFDocumentDataLoaderDecorator loader(document);
|
||||||
|
result.windowType = static_cast<WindowType>(loader.readIntegerFromDictionary(screenDictionary, "W", 3));
|
||||||
|
result.opacity = loader.readNumberFromDictionary(screenDictionary, "O", 1.0);
|
||||||
|
result.monitorSpecification = loader.readIntegerFromDictionary(screenDictionary, "M", 0);
|
||||||
|
std::vector<PDFReal> rgb = loader.readNumberArrayFromDictionary(screenDictionary, "B", { 1.0, 1.0, 1.0 });
|
||||||
|
rgb.resize(3, 1.0);
|
||||||
|
result.backgroundColor.setRgbF(rgb[0], rgb[1], rgb[2]);
|
||||||
|
|
||||||
|
if (const PDFDictionary* floatWindowDictionary = document->getDictionaryFromObject(screenDictionary->get("F")))
|
||||||
|
{
|
||||||
|
std::vector<PDFInteger> sizeArray = loader.readIntegerArrayFromDictionary(floatWindowDictionary, "D");
|
||||||
|
sizeArray.resize(2, 0);
|
||||||
|
result.floatingWindowSize = QSize(sizeArray[0], sizeArray[1]);
|
||||||
|
result.floatingWindowReference = static_cast<WindowRelativeTo>(loader.readIntegerFromDictionary(floatWindowDictionary, "RT", 0));
|
||||||
|
result.floatingWindowOffscreenMode = static_cast<OffscreenMode>(loader.readIntegerFromDictionary(floatWindowDictionary, "O", 1));
|
||||||
|
result.floatingWindowHasTitleBar = loader.readBooleanFromDictionary(floatWindowDictionary, "T", true);
|
||||||
|
result.floatingWindowCloseable = loader.readBooleanFromDictionary(floatWindowDictionary, "UC", true);
|
||||||
|
result.floatingWindowResizeMode = static_cast<ResizeMode>(loader.readIntegerFromDictionary(floatWindowDictionary, "R", 0));
|
||||||
|
result.floatingWindowTitle = PDFMediaMultiLanguageTexts::parse(document, floatWindowDictionary->get("TT"));
|
||||||
|
switch (loader.readIntegerFromDictionary(floatWindowDictionary, "P", 4))
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
result.floatingWindowAlignment = Qt::AlignTop | Qt::AlignLeft;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
result.floatingWindowAlignment = Qt::AlignTop | Qt::AlignHCenter;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
result.floatingWindowAlignment = Qt::AlignTop | Qt::AlignRight;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
result.floatingWindowAlignment = Qt::AlignVCenter | Qt::AlignLeft;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
default:
|
||||||
|
result.floatingWindowAlignment = Qt::AlignCenter;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
result.floatingWindowAlignment = Qt::AlignVCenter | Qt::AlignRight;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
result.floatingWindowAlignment = Qt::AlignBottom | Qt::AlignLeft;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
result.floatingWindowAlignment = Qt::AlignBottom | Qt::AlignHCenter;
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
result.floatingWindowAlignment = Qt::AlignBottom | Qt::AlignRight;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
return PDFMediaScreenParameters(getScreenParameters("MH"), getScreenParameters("BE"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return PDFMediaScreenParameters();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace pdf
|
537
PdfForQtLib/sources/pdfmultimedia.h
Normal file
537
PdfForQtLib/sources/pdfmultimedia.h
Normal file
@ -0,0 +1,537 @@
|
|||||||
|
// 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 PDFMULTIMEDIA_H
|
||||||
|
#define PDFMULTIMEDIA_H
|
||||||
|
|
||||||
|
#include "pdfobject.h"
|
||||||
|
#include "pdffile.h"
|
||||||
|
|
||||||
|
#include <QColor>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace pdf
|
||||||
|
{
|
||||||
|
class PDFDocument;
|
||||||
|
|
||||||
|
struct PDFMediaMultiLanguageTexts
|
||||||
|
{
|
||||||
|
static PDFMediaMultiLanguageTexts parse(const PDFDocument* document, PDFObject object);
|
||||||
|
|
||||||
|
std::map<QByteArray, QString> texts;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PDFMediaOffset
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum class Type
|
||||||
|
{
|
||||||
|
Invalid,
|
||||||
|
Time,
|
||||||
|
Frame,
|
||||||
|
Marker
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TimeData
|
||||||
|
{
|
||||||
|
PDFInteger seconds = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FrameData
|
||||||
|
{
|
||||||
|
PDFInteger frame = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MarkerData
|
||||||
|
{
|
||||||
|
QString namedOffset;
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit inline PDFMediaOffset() :
|
||||||
|
m_type(Type::Invalid)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Data>
|
||||||
|
explicit inline PDFMediaOffset(Type type, Data&& data) :
|
||||||
|
m_type(type),
|
||||||
|
m_data(qMove(data))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static PDFMediaOffset parse(const PDFDocument* document, PDFObject object);
|
||||||
|
|
||||||
|
const TimeData* getTimeData() const { return std::holds_alternative<TimeData>(m_data) ? &std::get<TimeData>(m_data) : nullptr; }
|
||||||
|
const FrameData* getFrameData() const { return std::holds_alternative<FrameData>(m_data) ? &std::get<FrameData>(m_data) : nullptr; }
|
||||||
|
const MarkerData* getMarkerData() const { return std::holds_alternative<MarkerData>(m_data) ? &std::get<MarkerData>(m_data) : nullptr; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Type m_type;
|
||||||
|
std::variant<std::monostate, TimeData, FrameData, MarkerData> m_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PDFMediaSoftwareIdentifier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit inline PDFMediaSoftwareIdentifier(QByteArray&& software, std::vector<PDFInteger>&& lowVersion, std::vector<PDFInteger>&& highVersion,
|
||||||
|
bool lowVersionInclusive, bool highVersionInclusive, std::vector<QByteArray>&& languages) :
|
||||||
|
m_software(qMove(software)),
|
||||||
|
m_lowVersion(qMove(lowVersion)),
|
||||||
|
m_highVersion(qMove(highVersion)),
|
||||||
|
m_lowVersionInclusive(lowVersionInclusive),
|
||||||
|
m_highVersionInclusive(highVersionInclusive),
|
||||||
|
m_languages(qMove(languages))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static PDFMediaSoftwareIdentifier parse(const PDFDocument* document, PDFObject object);
|
||||||
|
|
||||||
|
const QByteArray& getSoftware() const { return m_software; }
|
||||||
|
const std::vector<PDFInteger>& getLowVersion() const { return m_lowVersion; }
|
||||||
|
const std::vector<PDFInteger>& getHighVersion() const { return m_highVersion; }
|
||||||
|
bool isLowVersionInclusive() const { return m_lowVersionInclusive; }
|
||||||
|
bool isHighVersionInclusive() const { return m_highVersionInclusive; }
|
||||||
|
const std::vector<QByteArray>& getLanguages() const { return m_languages; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QByteArray m_software;
|
||||||
|
std::vector<PDFInteger> m_lowVersion;
|
||||||
|
std::vector<PDFInteger> m_highVersion;
|
||||||
|
bool m_lowVersionInclusive;
|
||||||
|
bool m_highVersionInclusive;
|
||||||
|
std::vector<QByteArray> m_languages;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PDFMediaPlayer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit inline PDFMediaPlayer(PDFMediaSoftwareIdentifier&& softwareIdentifier) :
|
||||||
|
m_softwareIdentifier(qMove(softwareIdentifier))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static PDFMediaPlayer parse(const PDFDocument* document, PDFObject object);
|
||||||
|
|
||||||
|
const PDFMediaSoftwareIdentifier* getSoftwareIdentifier() const { return &m_softwareIdentifier; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
PDFMediaSoftwareIdentifier m_softwareIdentifier;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PDFMediaPlayers
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit inline PDFMediaPlayers() = default;
|
||||||
|
|
||||||
|
explicit inline PDFMediaPlayers(std::vector<PDFMediaPlayer>&& playersMustUsed,
|
||||||
|
std::vector<PDFMediaPlayer>&& playersAlternate,
|
||||||
|
std::vector<PDFMediaPlayer>&& playersNeverUsed) :
|
||||||
|
m_playersMustUsed(qMove(playersMustUsed)),
|
||||||
|
m_playersNeverUsed(qMove(playersNeverUsed)),
|
||||||
|
m_playersAlternate(qMove(playersAlternate))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static PDFMediaPlayers parse(const PDFDocument* document, PDFObject object);
|
||||||
|
|
||||||
|
const std::vector<PDFMediaPlayer>& getPlayersMustUsed() const { return m_playersMustUsed; }
|
||||||
|
const std::vector<PDFMediaPlayer>& getPlayersAlternate() const { return m_playersAlternate; }
|
||||||
|
const std::vector<PDFMediaPlayer>& getPlayersNeverUsed() const { return m_playersNeverUsed; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<PDFMediaPlayer> m_playersMustUsed;
|
||||||
|
std::vector<PDFMediaPlayer> m_playersAlternate;
|
||||||
|
std::vector<PDFMediaPlayer> m_playersNeverUsed;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PDFMediaPermissions
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// Are we allowed to save temporary file to play rendition?
|
||||||
|
enum class Permission
|
||||||
|
{
|
||||||
|
Never,
|
||||||
|
Extract,
|
||||||
|
Access,
|
||||||
|
Always
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit inline PDFMediaPermissions() :
|
||||||
|
m_permission(Permission::Never)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit inline PDFMediaPermissions(Permission permission) :
|
||||||
|
m_permission(permission)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static PDFMediaPermissions parse(const PDFDocument* document, PDFObject object);
|
||||||
|
|
||||||
|
Permission getPermission() const { return m_permission; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Permission m_permission;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PDFMediaPlayParameters
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit inline PDFMediaPlayParameters() = default;
|
||||||
|
|
||||||
|
enum class FitMode
|
||||||
|
{
|
||||||
|
Meet,
|
||||||
|
Slice,
|
||||||
|
Fill,
|
||||||
|
Scroll,
|
||||||
|
Hidden,
|
||||||
|
Default
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Duration
|
||||||
|
{
|
||||||
|
Intrinsic,
|
||||||
|
Infinity,
|
||||||
|
Seconds
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PlayParameters
|
||||||
|
{
|
||||||
|
PDFInteger volume = 100;
|
||||||
|
bool controllerUserInterface = false;
|
||||||
|
FitMode fitMode = FitMode::Default;
|
||||||
|
bool playAutomatically = true;
|
||||||
|
PDFReal repeat = 1.0;
|
||||||
|
Duration duration = Duration::Intrinsic;
|
||||||
|
PDFReal durationSeconds = 0.0;
|
||||||
|
};
|
||||||
|
|
||||||
|
static PDFMediaPlayParameters parse(const PDFDocument* document, PDFObject object);
|
||||||
|
|
||||||
|
const PDFMediaPlayers* getPlayers() const { return &m_players; }
|
||||||
|
const PlayParameters* getPlayParametersMustHonored() const { return &m_mustHonored; }
|
||||||
|
const PlayParameters* getPlayParametersBestEffort() const { return &m_bestEffort; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
PDFMediaPlayers m_players;
|
||||||
|
PlayParameters m_mustHonored;
|
||||||
|
PlayParameters m_bestEffort;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PDFMediaScreenParameters
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum class WindowType
|
||||||
|
{
|
||||||
|
Floating,
|
||||||
|
FullScreen,
|
||||||
|
Hidden,
|
||||||
|
ScreenAnnotation
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class WindowRelativeTo
|
||||||
|
{
|
||||||
|
DocumentWindow,
|
||||||
|
ApplicationWindow,
|
||||||
|
VirtualDesktop,
|
||||||
|
Monitor
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class OffscreenMode
|
||||||
|
{
|
||||||
|
NoAction,
|
||||||
|
MoveOnScreen,
|
||||||
|
NonViable
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ResizeMode
|
||||||
|
{
|
||||||
|
Fixed,
|
||||||
|
ResizableKeepAspectRatio,
|
||||||
|
Resizeble
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ScreenParameters
|
||||||
|
{
|
||||||
|
WindowType windowType = WindowType::ScreenAnnotation;
|
||||||
|
QColor backgroundColor = QColor(Qt::white);
|
||||||
|
PDFReal opacity = 1.0;
|
||||||
|
PDFInteger monitorSpecification = 0;
|
||||||
|
|
||||||
|
QSize floatingWindowSize;
|
||||||
|
WindowRelativeTo floatingWindowReference = WindowRelativeTo::DocumentWindow;
|
||||||
|
Qt::Alignment floatingWindowAlignment = Qt::AlignCenter;
|
||||||
|
OffscreenMode floatingWindowOffscreenMode = OffscreenMode::MoveOnScreen;
|
||||||
|
bool floatingWindowHasTitleBar = true;
|
||||||
|
bool floatingWindowCloseable = true;
|
||||||
|
ResizeMode floatingWindowResizeMode = ResizeMode::Fixed;
|
||||||
|
PDFMediaMultiLanguageTexts floatingWindowTitle;
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit inline PDFMediaScreenParameters() = default;
|
||||||
|
explicit inline PDFMediaScreenParameters(ScreenParameters&& mustHonored, ScreenParameters&& bestEffort) :
|
||||||
|
m_mustHonored(qMove(mustHonored)),
|
||||||
|
m_bestEffort(qMove(bestEffort))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static PDFMediaScreenParameters parse(const PDFDocument* document, PDFObject object);
|
||||||
|
|
||||||
|
const ScreenParameters* getScreenParametersMustHonored() const { return &m_mustHonored; }
|
||||||
|
const ScreenParameters* getScreenParametersBestEffort() const { return &m_bestEffort; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ScreenParameters m_mustHonored;
|
||||||
|
ScreenParameters m_bestEffort;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PDFMediaClip
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct MediaClipData
|
||||||
|
{
|
||||||
|
QString name;
|
||||||
|
PDFFileSpecification fileSpecification;
|
||||||
|
PDFObject dataStream;
|
||||||
|
QByteArray contentType;
|
||||||
|
PDFMediaPermissions permissions;
|
||||||
|
PDFMediaMultiLanguageTexts alternateTextDescriptions;
|
||||||
|
PDFMediaPlayers players;
|
||||||
|
QByteArray m_baseUrlMustHonored;
|
||||||
|
QByteArray m_baseUrlBestEffort;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MediaSectionBeginEnd
|
||||||
|
{
|
||||||
|
PDFMediaOffset offsetBegin;
|
||||||
|
PDFMediaOffset offsetEnd;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MediaSectionData
|
||||||
|
{
|
||||||
|
QString name;
|
||||||
|
PDFMediaMultiLanguageTexts alternateTextDescriptions;
|
||||||
|
MediaSectionBeginEnd m_mustHonored;
|
||||||
|
MediaSectionBeginEnd m_bestEffort;
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit inline PDFMediaClip() = default;
|
||||||
|
|
||||||
|
explicit inline PDFMediaClip(MediaClipData&& mediaClipData, std::vector<MediaSectionData>&& sections) :
|
||||||
|
m_mediaClipData(qMove(mediaClipData)),
|
||||||
|
m_sections(qMove(sections))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static PDFMediaClip parse(const PDFDocument* document, PDFObject object);
|
||||||
|
|
||||||
|
const MediaClipData& getMediaClipData() const { return m_mediaClipData; }
|
||||||
|
const std::vector<MediaSectionData>& getClipSections() const { return m_sections; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
MediaClipData m_mediaClipData;
|
||||||
|
std::vector<MediaSectionData> m_sections;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PDFMediaMinimumBitDepth
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit inline PDFMediaMinimumBitDepth(PDFInteger screenMinimumBitDepth, PDFInteger monitorSpecifier) :
|
||||||
|
m_screenMinimumBitDepth(screenMinimumBitDepth),
|
||||||
|
m_monitorSpecifier(monitorSpecifier)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static PDFMediaMinimumBitDepth parse(const PDFDocument* document, PDFObject object);
|
||||||
|
|
||||||
|
PDFInteger getScreenMinimumBitDepth() const { return m_screenMinimumBitDepth; }
|
||||||
|
PDFInteger getMonitorSpecifier() const { return m_monitorSpecifier; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
PDFInteger m_screenMinimumBitDepth;
|
||||||
|
PDFInteger m_monitorSpecifier;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PDFMediaMinimumScreenSize
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit inline PDFMediaMinimumScreenSize(PDFInteger minimumWidth, PDFInteger minimumHeight, PDFInteger monitorSpecifier) :
|
||||||
|
m_minimumWidth(minimumWidth),
|
||||||
|
m_minimumHeight(minimumHeight),
|
||||||
|
m_monitorSpecifier(monitorSpecifier)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static PDFMediaMinimumScreenSize parse(const PDFDocument* document, PDFObject object);
|
||||||
|
|
||||||
|
private:
|
||||||
|
PDFInteger m_minimumWidth;
|
||||||
|
PDFInteger m_minimumHeight;
|
||||||
|
PDFInteger m_monitorSpecifier;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Media critera object (see PDF 1.7 reference, chapter 9.1.2). Some values are optional,
|
||||||
|
/// so they are implemented using std::optional. Always call "has" functions before
|
||||||
|
/// accessing the getters.
|
||||||
|
class PDFMediaCriteria
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit inline PDFMediaCriteria() = default;
|
||||||
|
|
||||||
|
static PDFMediaCriteria parse(const PDFDocument* document, PDFObject object);
|
||||||
|
|
||||||
|
bool hasAudioDescriptions() const { return m_audioDescriptions.has_value(); }
|
||||||
|
bool hasTextCaptions() const { return m_textCaptions.has_value(); }
|
||||||
|
bool hasAudioOverdubs() const { return m_audioOverdubs.has_value(); }
|
||||||
|
bool hasSubtitles() const { return m_subtitles.has_value(); }
|
||||||
|
bool hasBitrate() const { return m_bitrate.has_value(); }
|
||||||
|
bool hasMinimumBitDepth() const { return m_minimumBitDepth.has_value(); }
|
||||||
|
bool hasMinimumScreenSize() const { return m_minimumScreenSize.has_value(); }
|
||||||
|
bool hasViewers() const { return m_viewers.has_value(); }
|
||||||
|
bool hasMinimumPdfVersion() const { return m_minimumPdfVersion.has_value(); }
|
||||||
|
bool hasMaximumPdfVersion() const { return m_maximumPdfVersion.has_value(); }
|
||||||
|
bool hasLanguages() const { return m_languages.has_value(); }
|
||||||
|
|
||||||
|
bool getAudioDescriptions() const { return m_audioDescriptions.value(); }
|
||||||
|
bool getTextCaptions() const { return m_textCaptions.value(); }
|
||||||
|
bool getAudioOverdubs() const { return m_audioOverdubs.value(); }
|
||||||
|
bool getSubtitles() const { return m_subtitles.value(); }
|
||||||
|
PDFInteger getBitrate() const { return m_bitrate.value(); }
|
||||||
|
const PDFMediaMinimumBitDepth& getMinimumBitDepth() const { return m_minimumBitDepth.value(); }
|
||||||
|
const PDFMediaMinimumScreenSize& getMinimumScreenSize() const { return m_minimumScreenSize.value(); }
|
||||||
|
const std::vector<PDFMediaSoftwareIdentifier>& getViewers() const { return m_viewers.value(); }
|
||||||
|
const QByteArray& getMinimumPdfVersion() const { return m_minimumPdfVersion.value(); }
|
||||||
|
const QByteArray& getMaximumPdfVersion() const { return m_maximumPdfVersion.value(); }
|
||||||
|
const std::vector<QByteArray>& getLanguages() const { return m_languages.value(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::optional<bool> m_audioDescriptions;
|
||||||
|
std::optional<bool> m_textCaptions;
|
||||||
|
std::optional<bool> m_audioOverdubs;
|
||||||
|
std::optional<bool> m_subtitles;
|
||||||
|
std::optional<PDFInteger> m_bitrate;
|
||||||
|
std::optional<PDFMediaMinimumBitDepth> m_minimumBitDepth;
|
||||||
|
std::optional<PDFMediaMinimumScreenSize> m_minimumScreenSize;
|
||||||
|
std::optional<std::vector<PDFMediaSoftwareIdentifier>> m_viewers;
|
||||||
|
std::optional<QByteArray> m_minimumPdfVersion;
|
||||||
|
std::optional<QByteArray> m_maximumPdfVersion;
|
||||||
|
std::optional<std::vector<QByteArray>> m_languages;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Rendition object
|
||||||
|
class PDFRendition
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum class Type
|
||||||
|
{
|
||||||
|
Invalid,
|
||||||
|
Media,
|
||||||
|
Selector
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MediaRenditionData
|
||||||
|
{
|
||||||
|
PDFMediaClip clip;
|
||||||
|
PDFMediaPlayParameters playParameters;
|
||||||
|
PDFMediaScreenParameters screenParameters;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SelectorRenditionData
|
||||||
|
{
|
||||||
|
PDFObject renditions;
|
||||||
|
};
|
||||||
|
|
||||||
|
static PDFRendition parse(const PDFDocument* document, PDFObject object);
|
||||||
|
|
||||||
|
Type getType() const { return m_type; }
|
||||||
|
const QString& getName() const { return m_name; }
|
||||||
|
const PDFMediaCriteria* getMediaCriteriaMustHonored() const { return &m_mustHonored; }
|
||||||
|
const PDFMediaCriteria* getMediaCriteriaBestEffort() const { return &m_bestEffort; }
|
||||||
|
const MediaRenditionData* getMediaRenditionData() const { return std::holds_alternative<MediaRenditionData>(m_data) ? &std::get<MediaRenditionData>(m_data) : nullptr; }
|
||||||
|
const SelectorRenditionData* getSelectorRenditionData() const { return std::holds_alternative<SelectorRenditionData>(m_data) ? &std::get<SelectorRenditionData>(m_data) : nullptr; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Type m_type = Type::Invalid;
|
||||||
|
QString m_name;
|
||||||
|
|
||||||
|
PDFMediaCriteria m_mustHonored;
|
||||||
|
PDFMediaCriteria m_bestEffort;
|
||||||
|
std::variant<std::monostate, MediaRenditionData, SelectorRenditionData> m_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Sound object, see chapter 9.2 in PDF 1.7 reference
|
||||||
|
class PDFSound
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit inline PDFSound() = default;
|
||||||
|
|
||||||
|
enum class Format
|
||||||
|
{
|
||||||
|
Raw,
|
||||||
|
Signed,
|
||||||
|
muLaw,
|
||||||
|
ALaw
|
||||||
|
};
|
||||||
|
|
||||||
|
const PDFFileSpecification* getFileSpecification() const { return &m_fileSpecification; }
|
||||||
|
PDFReal getSamplingRate() const { return m_samplingRate; }
|
||||||
|
PDFInteger getChannels() const { return m_channels; }
|
||||||
|
PDFInteger getBitsPerSample() const { return m_bitsPerSample; }
|
||||||
|
Format getFormat() const { return m_format; }
|
||||||
|
const QByteArray& getSoundCompression() { return m_soundCompression; }
|
||||||
|
const PDFObject& getSoundCompressionParameters() const { return m_soundCompressionParameters; }
|
||||||
|
const PDFStream* getStream() const { return m_streamObject.isStream() ? m_streamObject.getStream() : nullptr; }
|
||||||
|
|
||||||
|
/// If this function returns true, sound is valid
|
||||||
|
bool isValid() const { return getStream(); }
|
||||||
|
|
||||||
|
/// Creates a new sound from the object. If data are invalid, then invalid object
|
||||||
|
/// is returned, no exception is thrown.
|
||||||
|
static PDFSound parse(const PDFDocument* document, PDFObject object);
|
||||||
|
|
||||||
|
private:
|
||||||
|
PDFFileSpecification m_fileSpecification;
|
||||||
|
PDFReal m_samplingRate = 0.0;
|
||||||
|
PDFInteger m_channels = 0;
|
||||||
|
PDFInteger m_bitsPerSample = 0;
|
||||||
|
Format m_format = Format::Raw;
|
||||||
|
QByteArray m_soundCompression;
|
||||||
|
PDFObject m_soundCompressionParameters;
|
||||||
|
PDFObject m_streamObject;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace pdf
|
||||||
|
|
||||||
|
#endif // PDFMULTIMEDIA_H
|
Reference in New Issue
Block a user