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/pdfitemmodels.cpp \
|
||||
sources/pdfjbig2decoder.cpp \
|
||||
sources/pdfmultimedia.cpp \
|
||||
sources/pdfobject.cpp \
|
||||
sources/pdfoptionalcontent.cpp \
|
||||
sources/pdfoutline.cpp \
|
||||
@ -78,6 +79,7 @@ HEADERS += \
|
||||
sources/pdfitemmodels.h \
|
||||
sources/pdfjbig2decoder.h \
|
||||
sources/pdfmeshqualitysettings.h \
|
||||
sources/pdfmultimedia.h \
|
||||
sources/pdfobject.h \
|
||||
sources/pdfoptionalcontent.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)));
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "pdfglobal.h"
|
||||
#include "pdfobject.h"
|
||||
#include "pdffile.h"
|
||||
#include "pdfmultimedia.h"
|
||||
|
||||
#include <QSharedPointer>
|
||||
|
||||
@ -40,7 +41,12 @@ enum class ActionType
|
||||
GoToE,
|
||||
Launch,
|
||||
Thread,
|
||||
URI
|
||||
URI,
|
||||
Sound,
|
||||
Movie,
|
||||
Hide,
|
||||
Named,
|
||||
SetOCGState
|
||||
};
|
||||
|
||||
enum class DestinationType
|
||||
@ -264,6 +270,149 @@ private:
|
||||
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
|
||||
|
||||
#endif // PDFACTION_H
|
||||
|
@ -547,4 +547,46 @@ QByteArray PDFDocumentDataLoaderDecorator::readStringFromDictionary(const PDFDic
|
||||
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
|
||||
|
@ -139,7 +139,7 @@ public:
|
||||
Enum readEnumByName(const PDFObject& object, Iterator begin, Iterator end, Enum defaultValue) const
|
||||
{
|
||||
const PDFObject& dereferencedObject = m_document->getObject(object);
|
||||
if (dereferencedObject.isName())
|
||||
if (dereferencedObject.isName() || dereferencedObject.isString())
|
||||
{
|
||||
QByteArray name = dereferencedObject.getString();
|
||||
|
||||
@ -258,6 +258,11 @@ public:
|
||||
/// \param object Object containing array of references
|
||||
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,
|
||||
/// then empty array is returned.
|
||||
/// \param dictionary Dictionary containing desired data
|
||||
@ -280,6 +285,12 @@ public:
|
||||
/// \param key Entry 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:
|
||||
const PDFDocument* m_document;
|
||||
};
|
||||
@ -326,6 +337,10 @@ public:
|
||||
/// is returned (no exception is thrown).
|
||||
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
|
||||
/// is returned (no exception is thrown).
|
||||
const PDFObject& getObjectByReference(PDFObjectReference reference) const;
|
||||
@ -383,6 +398,22 @@ const PDFObject& PDFDocument::getObject(const PDFObject& object) const
|
||||
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
|
||||
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