Multimedia, rendition action

This commit is contained in:
Jakub Melka
2019-11-23 19:02:24 +01:00
parent 2a11fa18e0
commit 18ac9166c0
7 changed files with 1408 additions and 2 deletions

View File

@ -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 \

View File

@ -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();
}

View File

@ -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

View File

@ -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

View File

@ -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
{

View 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

View 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