mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
3D annotation - finishing
This commit is contained in:
@ -539,6 +539,44 @@ PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObject
|
|||||||
annotation->m_relativeVerticalOffset = loader.readNumberFromDictionary(fixedPrintDictionary, "V", 0.0);
|
annotation->m_relativeVerticalOffset = loader.readNumberFromDictionary(fixedPrintDictionary, "V", 0.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (subtype == "3D")
|
||||||
|
{
|
||||||
|
PDF3DAnnotation* annotation = new PDF3DAnnotation();
|
||||||
|
result.reset(annotation);
|
||||||
|
|
||||||
|
annotation->m_stream = PDF3DStream::parse(storage, dictionary->get("3DD"));
|
||||||
|
|
||||||
|
const std::vector<PDF3DView>& views = annotation->getStream().getViews();
|
||||||
|
PDFObject defaultViewObject = storage->getObject(dictionary->get("DV"));
|
||||||
|
if (defaultViewObject.isDictionary())
|
||||||
|
{
|
||||||
|
annotation->m_defaultView = PDF3DView::parse(storage, defaultViewObject);
|
||||||
|
}
|
||||||
|
else if (defaultViewObject.isInt())
|
||||||
|
{
|
||||||
|
PDFInteger index = defaultViewObject.getInteger();
|
||||||
|
if (index >= 0 && index < PDFInteger(views.size()))
|
||||||
|
{
|
||||||
|
annotation->m_defaultView = views[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (defaultViewObject.isName() && !views.empty())
|
||||||
|
{
|
||||||
|
QByteArray name = defaultViewObject.getString();
|
||||||
|
if (name == "F")
|
||||||
|
{
|
||||||
|
annotation->m_defaultView = views.front();
|
||||||
|
}
|
||||||
|
else if (name == "L")
|
||||||
|
{
|
||||||
|
annotation->m_defaultView = views.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
annotation->m_activation = PDF3DActivation::parse(storage, dictionary->get("3DA"));
|
||||||
|
annotation->m_interactive = loader.readBooleanFromDictionary(dictionary, "3DI", true);
|
||||||
|
annotation->m_viewBox = loader.readRectangle(dictionary->get("3DB"), QRectF());
|
||||||
|
}
|
||||||
else if (subtype == "RichMedia")
|
else if (subtype == "RichMedia")
|
||||||
{
|
{
|
||||||
PDFRichMediaAnnotation* annotation = new PDFRichMediaAnnotation();
|
PDFRichMediaAnnotation* annotation = new PDFRichMediaAnnotation();
|
||||||
|
@ -1183,6 +1183,30 @@ private:
|
|||||||
PDFReal m_relativeVerticalOffset = 0.0;
|
PDFReal m_relativeVerticalOffset = 0.0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// 3D annotations represents 3D scene, which can be viewed in the application.
|
||||||
|
class PDF3DAnnotation : public PDFAnnotation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline explicit PDF3DAnnotation() = default;
|
||||||
|
|
||||||
|
virtual AnnotationType getType() const override { return AnnotationType::_3D; }
|
||||||
|
|
||||||
|
const PDF3DStream& getStream() const { return m_stream; }
|
||||||
|
const std::optional<PDF3DView>& getDefaultView() const { return m_defaultView; }
|
||||||
|
const PDF3DActivation& getActivation() const { return m_activation; }
|
||||||
|
bool isInteractive() const { return m_interactive; }
|
||||||
|
QRectF getViewBox() const { return m_viewBox; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObject object);
|
||||||
|
|
||||||
|
PDF3DStream m_stream;
|
||||||
|
std::optional<PDF3DView> m_defaultView;
|
||||||
|
PDF3DActivation m_activation;
|
||||||
|
bool m_interactive = true;
|
||||||
|
QRectF m_viewBox;
|
||||||
|
};
|
||||||
|
|
||||||
/// Rich media annotations can be video, audio, or other multimedia presentations.
|
/// Rich media annotations can be video, audio, or other multimedia presentations.
|
||||||
/// The application should provide additional functionality to control rich media,
|
/// The application should provide additional functionality to control rich media,
|
||||||
/// such as buttons to play/pause/stop video etc. This annotation consists
|
/// such as buttons to play/pause/stop video etc. This annotation consists
|
||||||
|
@ -635,6 +635,26 @@ std::vector<QByteArray> PDFDocumentDataLoaderDecorator::readStringArrayFromDicti
|
|||||||
return std::vector<QByteArray>();
|
return std::vector<QByteArray>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QStringList PDFDocumentDataLoaderDecorator::readTextStringList(const PDFObject& object)
|
||||||
|
{
|
||||||
|
QStringList result;
|
||||||
|
|
||||||
|
const PDFObject& dereferencedObject = m_storage->getObject(object);
|
||||||
|
if (dereferencedObject.isArray())
|
||||||
|
{
|
||||||
|
const PDFArray* array = dereferencedObject.getArray();
|
||||||
|
const size_t count = array->getCount();
|
||||||
|
result.reserve(int(count));
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
result << readTextString(array->getItem(i), QString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<QByteArray> PDFDocumentDataLoaderDecorator::readStringArray(const PDFObject& object) const
|
std::vector<QByteArray> PDFDocumentDataLoaderDecorator::readStringArray(const PDFObject& object) const
|
||||||
{
|
{
|
||||||
const PDFObject& dereferencedObject = m_storage->getObject(object);
|
const PDFObject& dereferencedObject = m_storage->getObject(object);
|
||||||
|
@ -337,6 +337,30 @@ public:
|
|||||||
/// \param key Entry key
|
/// \param key Entry key
|
||||||
std::vector<QByteArray> readStringArrayFromDictionary(const PDFDictionary* dictionary, const char* key) const;
|
std::vector<QByteArray> readStringArrayFromDictionary(const PDFDictionary* dictionary, const char* key) const;
|
||||||
|
|
||||||
|
/// Reads string list. If error occurs, empty list is returned.
|
||||||
|
QStringList readTextStringList(const PDFObject& object);
|
||||||
|
|
||||||
|
/// Reads list of object, using parse function defined in object
|
||||||
|
template<typename Object>
|
||||||
|
std::vector<Object> readObjectList(PDFObject object)
|
||||||
|
{
|
||||||
|
std::vector<Object> result;
|
||||||
|
object = m_storage->getObject(object);
|
||||||
|
if (object.isArray())
|
||||||
|
{
|
||||||
|
const PDFArray* array = object.getArray();
|
||||||
|
const size_t count = array->getCount();
|
||||||
|
result.reserve(count);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
result.emplace_back(Object::parse(m_storage, array->getItem(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const PDFObjectStorage* m_storage;
|
const PDFObjectStorage* m_storage;
|
||||||
};
|
};
|
||||||
|
@ -1174,7 +1174,7 @@ PDF3DProjection PDF3DProjection::parse(const PDFObjectStorage* storage, PDFObjec
|
|||||||
};
|
};
|
||||||
|
|
||||||
PDFDocumentDataLoaderDecorator loader(storage);
|
PDFDocumentDataLoaderDecorator loader(storage);
|
||||||
result.m_projection = loader.readEnumByName(dictionary->get("Subtype"), projections.cbegin(), projections.cend(), Projection::Orthographic);
|
result.m_projection = loader.readEnumByName(dictionary->get("Subtype"), projections.cbegin(), projections.cend(), Projection::Perspective);
|
||||||
result.m_clippingStyle = loader.readEnumByName(dictionary->get("CS"), clippingStyles.cbegin(), clippingStyles.cend(), ClippingStyle::Automatic);
|
result.m_clippingStyle = loader.readEnumByName(dictionary->get("CS"), clippingStyles.cbegin(), clippingStyles.cend(), ClippingStyle::Automatic);
|
||||||
result.m_near = loader.readNumberFromDictionary(dictionary, "N", 0.0);
|
result.m_near = loader.readNumberFromDictionary(dictionary, "N", 0.0);
|
||||||
result.m_far = loader.readNumberFromDictionary(dictionary, "F", qInf());
|
result.m_far = loader.readNumberFromDictionary(dictionary, "F", qInf());
|
||||||
@ -1191,4 +1191,107 @@ PDF3DProjection PDF3DProjection::parse(const PDFObjectStorage* storage, PDFObjec
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PDF3DView PDF3DView::parse(const PDFObjectStorage* storage, PDFObject object)
|
||||||
|
{
|
||||||
|
PDF3DView result;
|
||||||
|
|
||||||
|
if (const PDFDictionary* dictionary = storage->getDictionaryFromObject(object))
|
||||||
|
{
|
||||||
|
constexpr const std::array<std::pair<const char*, MatrixSelection>, 2> matrixSelection = {
|
||||||
|
std::pair<const char*, MatrixSelection>{ "M", MatrixSelection::M },
|
||||||
|
std::pair<const char*, MatrixSelection>{ "U3D", MatrixSelection::U3D }
|
||||||
|
};
|
||||||
|
|
||||||
|
PDFDocumentDataLoaderDecorator loader(storage);
|
||||||
|
result.m_externalName = loader.readTextStringFromDictionary(dictionary, "XN", QString());
|
||||||
|
result.m_internalName = loader.readTextStringFromDictionary(dictionary, "IN", QString());
|
||||||
|
result.m_matrixSelection = loader.readEnumByName(dictionary->get("MS"), matrixSelection.cbegin(), matrixSelection.cend(), MatrixSelection::M);
|
||||||
|
result.m_cameraToWorld = PDF3DAuxiliaryParser::parseMatrix4x4(storage, dictionary->get("C2W"));
|
||||||
|
result.m_U3Dpath = loader.readTextStringList(dictionary->get("U3DPath"));
|
||||||
|
result.m_cameraDistance = loader.readNumberFromDictionary(dictionary, "CO", 0.0);
|
||||||
|
result.m_projection = PDF3DProjection::parse(storage, dictionary->get("P"));
|
||||||
|
result.m_overlay = dictionary->get("O");
|
||||||
|
result.m_background = PDF3DBackground::parse(storage, dictionary->get("BG"));
|
||||||
|
result.m_renderMode = PDF3DRenderMode::parse(storage, dictionary->get("RM"));
|
||||||
|
result.m_lightingScheme = PDF3DLightingScheme::parse(storage, dictionary->get("LS"));
|
||||||
|
result.m_crossSections = loader.readObjectList<PDF3DCrossSection>(dictionary->get("SA"));
|
||||||
|
result.m_nodes = loader.readObjectList<PDF3DNode>(dictionary->get("NA"));
|
||||||
|
result.m_nodesRestore = loader.readBooleanFromDictionary(dictionary, "NR", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
PDF3DAnimation PDF3DAnimation::parse(const PDFObjectStorage* storage, PDFObject object)
|
||||||
|
{
|
||||||
|
PDF3DAnimation result;
|
||||||
|
|
||||||
|
if (const PDFDictionary* dictionary = storage->getDictionaryFromObject(object))
|
||||||
|
{
|
||||||
|
constexpr const std::array<std::pair<const char*, Animation>, 3> animations = {
|
||||||
|
std::pair<const char*, Animation>{ "None", Animation::None },
|
||||||
|
std::pair<const char*, Animation>{ "Linear", Animation::Linear },
|
||||||
|
std::pair<const char*, Animation>{ "Oscillating", Animation::Oscillating }
|
||||||
|
};
|
||||||
|
|
||||||
|
PDFDocumentDataLoaderDecorator loader(storage);
|
||||||
|
result.m_animation = loader.readEnumByName(dictionary->get("Subtype"), animations.cbegin(), animations.cend(), Animation::None);
|
||||||
|
result.m_playCount = loader.readIntegerFromDictionary(dictionary, "PC", -1);
|
||||||
|
result.m_speed = loader.readNumberFromDictionary(dictionary, "TM", 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
PDF3DStream PDF3DStream::parse(const PDFObjectStorage* storage, PDFObject object)
|
||||||
|
{
|
||||||
|
PDF3DStream result;
|
||||||
|
|
||||||
|
if (const PDFDictionary* dictionary = storage->getDictionaryFromObject(object))
|
||||||
|
{
|
||||||
|
constexpr const std::array<std::pair<const char*, Type>, 2> types = {
|
||||||
|
std::pair<const char*, Type>{ "U3D", Type::U3D },
|
||||||
|
std::pair<const char*, Type>{ "PRC", Type::PRC }
|
||||||
|
};
|
||||||
|
|
||||||
|
PDFDocumentDataLoaderDecorator loader(storage);
|
||||||
|
result.m_stream = object;
|
||||||
|
result.m_type = loader.readEnumByName(dictionary->get("Subtype"), types.cbegin(), types.cend(), Type::Invalid);
|
||||||
|
result.m_views = loader.readObjectList<PDF3DView>(dictionary->get("VA"));
|
||||||
|
|
||||||
|
PDFObject defaultViewObject = storage->getObject(dictionary->get("DV"));
|
||||||
|
if (defaultViewObject.isDictionary())
|
||||||
|
{
|
||||||
|
result.m_defaultView = PDF3DView::parse(storage, defaultViewObject);
|
||||||
|
}
|
||||||
|
else if (defaultViewObject.isInt())
|
||||||
|
{
|
||||||
|
PDFInteger index = defaultViewObject.getInteger();
|
||||||
|
if (index >= 0 && index < PDFInteger(result.m_views.size()))
|
||||||
|
{
|
||||||
|
result.m_defaultView = result.m_views[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (defaultViewObject.isName() && !result.m_views.empty())
|
||||||
|
{
|
||||||
|
QByteArray name = defaultViewObject.getString();
|
||||||
|
if (name == "F")
|
||||||
|
{
|
||||||
|
result.m_defaultView = result.m_views.front();
|
||||||
|
}
|
||||||
|
else if (name == "L")
|
||||||
|
{
|
||||||
|
result.m_defaultView = result.m_views.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.m_resources = dictionary->get("Resources");
|
||||||
|
result.m_onInstantiateJavascript = dictionary->get("OnInstantiate");
|
||||||
|
result.m_animation = PDF3DAnimation::parse(storage, dictionary->get("AN"));
|
||||||
|
result.m_colorSpace = dictionary->get("ColorSpace");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace pdf
|
} // namespace pdf
|
||||||
|
@ -1139,7 +1139,7 @@ public:
|
|||||||
static PDF3DProjection parse(const PDFObjectStorage* storage, PDFObject object);
|
static PDF3DProjection parse(const PDFObjectStorage* storage, PDFObject object);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Projection m_projection = Projection::Orthographic;
|
Projection m_projection = Projection::Perspective;
|
||||||
ClippingStyle m_clippingStyle = ClippingStyle::Automatic;
|
ClippingStyle m_clippingStyle = ClippingStyle::Automatic;
|
||||||
PDFReal m_near = 0.0;
|
PDFReal m_near = 0.0;
|
||||||
PDFReal m_far = qInf();
|
PDFReal m_far = qInf();
|
||||||
@ -1150,6 +1150,118 @@ private:
|
|||||||
ScaleMode m_scaleMode = ScaleMode::Absolute;
|
ScaleMode m_scaleMode = ScaleMode::Absolute;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// 3D PDF view
|
||||||
|
class PDF3DView
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit inline PDF3DView() = default;
|
||||||
|
|
||||||
|
enum class MatrixSelection
|
||||||
|
{
|
||||||
|
M,
|
||||||
|
U3D
|
||||||
|
};
|
||||||
|
|
||||||
|
const QString& getExternalName() const { return m_externalName; }
|
||||||
|
const QString& getInternalName() const { return m_internalName; }
|
||||||
|
MatrixSelection getMatrixSelection() const{ return m_matrixSelection; }
|
||||||
|
const QMatrix4x4& getCameraToWorld() const { return m_cameraToWorld; }
|
||||||
|
const QStringList& getU3DPath() const { return m_U3Dpath; }
|
||||||
|
PDFReal getCameraDistance() const { return m_cameraDistance; }
|
||||||
|
const PDF3DProjection& getProjection() const { return m_projection; }
|
||||||
|
const PDFObject& getOverlay() const { return m_overlay; }
|
||||||
|
const PDF3DBackground& getBackground() const { return m_background; }
|
||||||
|
const PDF3DRenderMode& getRenderMode() const { return m_renderMode; }
|
||||||
|
const PDF3DLightingScheme& getLightingScheme() const { return m_lightingScheme; }
|
||||||
|
const std::vector<PDF3DCrossSection>& getCrossSections() const { return m_crossSections; }
|
||||||
|
const std::vector<PDF3DNode>& getNodes() const { return m_nodes; }
|
||||||
|
bool getNodesRestore() const { return m_nodesRestore; }
|
||||||
|
|
||||||
|
/// Creates a new 3D view from the object. If data are invalid, then invalid object
|
||||||
|
/// is returned, no exception is thrown.
|
||||||
|
static PDF3DView parse(const PDFObjectStorage* storage, PDFObject object);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_externalName;
|
||||||
|
QString m_internalName;
|
||||||
|
MatrixSelection m_matrixSelection = MatrixSelection::M;
|
||||||
|
QMatrix4x4 m_cameraToWorld;
|
||||||
|
QStringList m_U3Dpath;
|
||||||
|
PDFReal m_cameraDistance = 0.0;
|
||||||
|
PDF3DProjection m_projection;
|
||||||
|
PDFObject m_overlay;
|
||||||
|
PDF3DBackground m_background;
|
||||||
|
PDF3DRenderMode m_renderMode;
|
||||||
|
PDF3DLightingScheme m_lightingScheme;
|
||||||
|
std::vector<PDF3DCrossSection> m_crossSections;
|
||||||
|
std::vector<PDF3DNode> m_nodes;
|
||||||
|
bool m_nodesRestore = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// 3D PDF animation
|
||||||
|
class PDF3DAnimation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit inline PDF3DAnimation() = default;
|
||||||
|
|
||||||
|
enum class Animation
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Linear,
|
||||||
|
Oscillating
|
||||||
|
};
|
||||||
|
|
||||||
|
Animation getAnimation() const { return m_animation; }
|
||||||
|
PDFInteger getPlayCount() const { return m_playCount; }
|
||||||
|
PDFReal getSpeed() const { return m_speed; }
|
||||||
|
|
||||||
|
/// Creates a new 3D animation from the object. If data are invalid, then invalid object
|
||||||
|
/// is returned, no exception is thrown.
|
||||||
|
static PDF3DAnimation parse(const PDFObjectStorage* storage, PDFObject object);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Animation m_animation = Animation::None;
|
||||||
|
PDFInteger m_playCount = -1;
|
||||||
|
PDFReal m_speed = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// 3D PDF stream
|
||||||
|
class PDF3DStream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit inline PDF3DStream() = default;
|
||||||
|
|
||||||
|
enum class Type
|
||||||
|
{
|
||||||
|
Invalid,
|
||||||
|
U3D,
|
||||||
|
PRC
|
||||||
|
};
|
||||||
|
|
||||||
|
PDFObject getStream() const { return m_stream; }
|
||||||
|
Type getType() const { return m_type; }
|
||||||
|
const std::vector<PDF3DView>& getViews() const { return m_views; }
|
||||||
|
const std::optional<PDF3DView>& getDefaultView() const { return m_defaultView; }
|
||||||
|
PDFObject getResources() const { return m_resources; }
|
||||||
|
PDFObject getOnInstantiateJavascript() const { return m_onInstantiateJavascript; }
|
||||||
|
PDF3DAnimation getAnimation() const { return m_animation; }
|
||||||
|
PDFObject getColorSpace() const { return m_colorSpace; }
|
||||||
|
|
||||||
|
/// Creates a new 3D stream from the object. If data are invalid, then invalid object
|
||||||
|
/// is returned, no exception is thrown.
|
||||||
|
static PDF3DStream parse(const PDFObjectStorage* storage, PDFObject object);
|
||||||
|
|
||||||
|
private:
|
||||||
|
PDFObject m_stream;
|
||||||
|
Type m_type = Type::Invalid;
|
||||||
|
std::vector<PDF3DView> m_views;
|
||||||
|
std::optional<PDF3DView> m_defaultView;
|
||||||
|
PDFObject m_resources;
|
||||||
|
PDFObject m_onInstantiateJavascript;
|
||||||
|
PDF3DAnimation m_animation;
|
||||||
|
PDFObject m_colorSpace;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace pdf
|
} // namespace pdf
|
||||||
|
|
||||||
#endif // PDFMULTIMEDIA_H
|
#endif // PDFMULTIMEDIA_H
|
||||||
|
Reference in New Issue
Block a user