3D PDF annotation beginning

This commit is contained in:
Jakub Melka 2020-04-10 15:23:38 +02:00
parent 6d9744886a
commit a87670df1d
2 changed files with 511 additions and 3 deletions

View File

@ -31,6 +31,74 @@ constexpr const std::array<std::pair<const char*, RichMediaType>, 4> richMediaTy
std::pair<const char*, RichMediaType>{ "Video", RichMediaType::Video }
};
class PDF3DAuxiliaryParser
{
public:
PDF3DAuxiliaryParser() = delete;
/// Parses 4x4 transformation matrix
/// \param storage Storage
/// \param object Object
static QMatrix4x4 parseMatrix4x4(const PDFObjectStorage* storage, PDFObject object);
/// Parses 3D annotation color
static QColor parseColor(const PDFObjectStorage* storage, PDFObject object, QColor defaultColor);
};
QMatrix4x4 PDF3DAuxiliaryParser::parseMatrix4x4(const PDFObjectStorage* storage, PDFObject object)
{
QMatrix4x4 matrix;
PDFDocumentDataLoaderDecorator loader(storage);
std::vector<PDFReal> elements = loader.readNumberArray(object);
if (elements.size() == 12)
{
const PDFReal a = elements[ 0];
const PDFReal b = elements[ 1];
const PDFReal c = elements[ 2];
const PDFReal d = elements[ 3];
const PDFReal e = elements[ 4];
const PDFReal f = elements[ 5];
const PDFReal g = elements[ 6];
const PDFReal h = elements[ 7];
const PDFReal i = elements[ 8];
const PDFReal tx = elements[ 9];
const PDFReal ty = elements[10];
const PDFReal tz = elements[11];
matrix = QMatrix4x4(a, b, c, 0,
d, e, f, 0,
g, h, i, 0,
tx, ty, tz, 1.0);
}
return matrix;
}
QColor PDF3DAuxiliaryParser::parseColor(const PDFObjectStorage* storage, PDFObject object, QColor defaultColor)
{
object = storage->getObject(object);
// If color is invalid according to the specification, then return default value
if (object.isArray())
{
const PDFArray* array = object.getArray();
if (array->getCount() == 4)
{
PDFDocumentDataLoaderDecorator loader(storage);
if (loader.readName(array->getItem(0)) == "DeviceRGB")
{
const PDFReal r = loader.readNumber(array->getItem(1), 0.0);
const PDFReal g = loader.readNumber(array->getItem(2), 0.0);
const PDFReal b = loader.readNumber(array->getItem(3), 0.0);
return QColor::fromRgbF(r, g, b, 1.0);
}
}
}
return defaultColor;
}
PDFSound PDFSound::parse(const PDFObjectStorage* storage, PDFObject object)
{
PDFSound result;
@ -876,7 +944,7 @@ PDF3DActivation PDF3DActivation::parse(const PDFObjectStorage* storage, PDFObjec
std::pair<const char*, TriggerMode>{ "PV", TriggerMode::PageVisibility }
};
constexpr const std::array<std::pair<const char*, ActivationMode>, 3> deactivationModes = {
constexpr const std::array<std::pair<const char*, TriggerMode>, 3> deactivationModes = {
std::pair<const char*, TriggerMode>{ "XD", TriggerMode::ExplicitlyByUserAction },
std::pair<const char*, TriggerMode>{ "PC", TriggerMode::PageFocus },
std::pair<const char*, TriggerMode>{ "PI", TriggerMode::PageVisibility }
@ -908,4 +976,219 @@ PDF3DActivation PDF3DActivation::parse(const PDFObjectStorage* storage, PDFObjec
return result;
}
PDF3DRenderMode PDF3DRenderMode::parse(const PDFObjectStorage* storage, PDFObject object)
{
PDF3DRenderMode result;
if (const PDFDictionary* dictionary = storage->getDictionaryFromObject(object))
{
constexpr const std::array<std::pair<const char*, RenderMode>, 15> renderModes = {
std::pair<const char*, RenderMode>{ "Solid", RenderMode::Solid },
std::pair<const char*, RenderMode>{ "SolidWireframe", RenderMode::SolidWireframe },
std::pair<const char*, RenderMode>{ "Transparent", RenderMode::Transparent },
std::pair<const char*, RenderMode>{ "TransparentWireframe", RenderMode::TransparentWireframe },
std::pair<const char*, RenderMode>{ "BoundingBox", RenderMode::BoundingBox },
std::pair<const char*, RenderMode>{ "TransparentBoundingBox", RenderMode::TransparentBoundingBox },
std::pair<const char*, RenderMode>{ "TransparentBoundingBoxOutline", RenderMode::TransparentBoundingBoxOutline },
std::pair<const char*, RenderMode>{ "Wireframe", RenderMode::Wireframe },
std::pair<const char*, RenderMode>{ "ShadedWireframe", RenderMode::ShadedWireframe },
std::pair<const char*, RenderMode>{ "HiddenWireframe", RenderMode::HiddenWireframe },
std::pair<const char*, RenderMode>{ "Vertices", RenderMode::Vertices },
std::pair<const char*, RenderMode>{ "ShadedVertices", RenderMode::ShadedVertices },
std::pair<const char*, RenderMode>{ "Illustration", RenderMode::Illustration },
std::pair<const char*, RenderMode>{ "SolidOutline", RenderMode::SolidOutline },
std::pair<const char*, RenderMode>{ "ShadedIllustration", RenderMode::ShadedIllustration }
};
PDFDocumentDataLoaderDecorator loader(storage);
result.m_renderMode = loader.readEnumByName(dictionary->get("Subtype"), renderModes.cbegin(), renderModes.cend(), RenderMode::Solid);
result.m_auxiliaryColor = PDF3DAuxiliaryParser::parseColor(storage, dictionary->get("AC"), Qt::black);
result.m_faceColor = PDF3DAuxiliaryParser::parseColor(storage, dictionary->get("FC"), Qt::black);
result.m_faceColorMode = (loader.readName(dictionary->get("FC")) != "BG") ? FaceColorMode::Color : FaceColorMode::BG;
result.m_opacity = loader.readNumberFromDictionary(dictionary, "O", 0.5);
result.m_creaseAngle = loader.readNumberFromDictionary(dictionary, "CV", 45.0);
}
return result;
}
PDF3DNode PDF3DNode::parse(const PDFObjectStorage* storage, PDFObject object)
{
PDF3DNode result;
if (const PDFDictionary* dictionary = storage->getDictionaryFromObject(object))
{
PDFDocumentDataLoaderDecorator loader(storage);
result.m_name = loader.readTextStringFromDictionary(dictionary, "N", QString());
if (dictionary->hasKey("O"))
{
result.m_opacity = loader.readNumberFromDictionary(dictionary, "O", 1.0);
}
if (dictionary->hasKey("V"))
{
result.m_visibility = loader.readBooleanFromDictionary(dictionary, "V", true);
}
if (dictionary->hasKey("M"))
{
result.m_matrix = PDF3DAuxiliaryParser::parseMatrix4x4(storage, dictionary->get("M"));
}
result.m_instance = loader.readReferenceFromDictionary(dictionary, "Instance");
result.m_data = loader.readTextStringFromDictionary(dictionary, "Data", QString());
if (dictionary->hasKey("RM"))
{
result.m_renderMode = PDF3DRenderMode::parse(storage, dictionary->get("RM"));
}
}
return result;
}
PDF3DCrossSection PDF3DCrossSection::parse(const PDFObjectStorage* storage, PDFObject object)
{
PDF3DCrossSection result;
if (const PDFDictionary* dictionary = storage->getDictionaryFromObject(object))
{
PDFDocumentDataLoaderDecorator loader(storage);
loader.readNumberArray(dictionary->get("C"), result.m_centerOfRotation.begin(), result.m_centerOfRotation.end());
PDFObject axesRotation = storage->getObject(dictionary->get("O"));
if (const PDFArray* rotations = axesRotation.getArray())
{
if (rotations->getCount() == 3)
{
PDFObject xObject = storage->getObject(rotations->getItem(0));
PDFObject yObject = storage->getObject(rotations->getItem(1));
PDFObject zObject = storage->getObject(rotations->getItem(2));
auto readValue = [&result, &loader](const PDFObject& object, int index, Direction direction)
{
if (object.isNull())
{
result.m_perpendicularDirection = direction;
result.m_rotationAngles[index] = 0.0;
}
else
{
result.m_rotationAngles[index] = loader.readNumber(object, 0.0);
}
};
readValue(xObject, 0, Direction::X);
readValue(yObject, 1, Direction::Y);
readValue(zObject, 2, Direction::Z);
}
}
result.m_cutPlaneOpacity = loader.readNumberFromDictionary(dictionary, "PO", 0.5);
result.m_cutPlaneColor = PDF3DAuxiliaryParser::parseColor(storage, dictionary->get("PC"), Qt::white);
result.m_intersectionVisibility = loader.readBooleanFromDictionary(dictionary, "IV", false);
result.m_intersectionColor = PDF3DAuxiliaryParser::parseColor(storage, dictionary->get("IC"), Qt::green);
result.m_showTransparent = loader.readBooleanFromDictionary(dictionary, "ST", false);
result.m_sectionCapping = loader.readBooleanFromDictionary(dictionary, "SC", false);
}
return result;
}
PDF3DLightingScheme PDF3DLightingScheme::parse(const PDFObjectStorage* storage, PDFObject object)
{
PDF3DLightingScheme result;
if (const PDFDictionary* dictionary = storage->getDictionaryFromObject(object))
{
constexpr const std::array<std::pair<const char*, LightingScheme>, 12> lightingSchemes = {
std::pair<const char*, LightingScheme>{ "Artwork", LightingScheme::Artwork },
std::pair<const char*, LightingScheme>{ "None", LightingScheme::None },
std::pair<const char*, LightingScheme>{ "White", LightingScheme::White },
std::pair<const char*, LightingScheme>{ "Day", LightingScheme::Day },
std::pair<const char*, LightingScheme>{ "Night", LightingScheme::Night },
std::pair<const char*, LightingScheme>{ "Hard", LightingScheme::Hard },
std::pair<const char*, LightingScheme>{ "Primary", LightingScheme::Primary },
std::pair<const char*, LightingScheme>{ "Blue", LightingScheme::Blue },
std::pair<const char*, LightingScheme>{ "Red", LightingScheme::Red },
std::pair<const char*, LightingScheme>{ "Cube", LightingScheme::Cube },
std::pair<const char*, LightingScheme>{ "CAD", LightingScheme::CAD },
std::pair<const char*, LightingScheme>{ "Headlamp", LightingScheme::Headlamp }
};
PDFDocumentDataLoaderDecorator loader(storage);
result.m_scheme = loader.readEnumByName(dictionary->get("Subtype"), lightingSchemes.cbegin(), lightingSchemes.cend(), LightingScheme::Artwork);
}
return result;
}
PDF3DBackground PDF3DBackground::parse(const PDFObjectStorage* storage, PDFObject object)
{
PDF3DBackground result;
if (const PDFDictionary* dictionary = storage->getDictionaryFromObject(object))
{
PDFDocumentDataLoaderDecorator loader(storage);
// This is a hack to parse color in same way as in another objects
PDFObject colorSpace = dictionary->get("CS");
if (colorSpace.isNull())
{
colorSpace = PDFObject::createName(std::make_shared<PDFString>("DeviceRGB"));
}
std::vector<PDFReal> color = loader.readNumberArrayFromDictionary(dictionary, "C", { 1.0, 1.0, 1.0});
PDFArray array;
array.appendItem(colorSpace);
for (PDFReal colorComponent : color)
{
array.appendItem(PDFObject::createReal(colorComponent));
}
PDFObject colorObject = PDFObject::createArray(std::make_shared<PDFArray>(qMove(array)));
result.m_color = PDF3DAuxiliaryParser::parseColor(storage, colorObject, Qt::white);
result.m_entireAnnotation = loader.readBooleanFromDictionary(dictionary, "EA", false);
}
return result;
}
PDF3DProjection PDF3DProjection::parse(const PDFObjectStorage* storage, PDFObject object)
{
PDF3DProjection result;
if (const PDFDictionary* dictionary = storage->getDictionaryFromObject(object))
{
constexpr const std::array<std::pair<const char*, Projection>, 2> projections = {
std::pair<const char*, Projection>{ "O", Projection::Orthographic },
std::pair<const char*, Projection>{ "P", Projection::Perspective }
};
constexpr const std::array<std::pair<const char*, ClippingStyle>, 2> clippingStyles = {
std::pair<const char*, ClippingStyle>{ "XNF", ClippingStyle::Explicit },
std::pair<const char*, ClippingStyle>{ "ANF", ClippingStyle::Automatic }
};
constexpr const std::array<std::pair<const char*, ScaleMode>, 5> scaleModes = {
std::pair<const char*, ScaleMode>{ "W", ScaleMode::W },
std::pair<const char*, ScaleMode>{ "H", ScaleMode::H },
std::pair<const char*, ScaleMode>{ "Min", ScaleMode::Min },
std::pair<const char*, ScaleMode>{ "Max", ScaleMode::Max },
std::pair<const char*, ScaleMode>{ "Absolute", ScaleMode::Absolute }
};
PDFDocumentDataLoaderDecorator loader(storage);
result.m_projection = loader.readEnumByName(dictionary->get("Subtype"), projections.cbegin(), projections.cend(), Projection::Orthographic);
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_far = loader.readNumberFromDictionary(dictionary, "F", qInf());
result.m_fieldOfViewAngle = loader.readNumberFromDictionary(dictionary, "FOV", 90.0);
if (dictionary->hasKey("PS"))
{
result.m_projectionScalingDiameter = loader.readNumberFromDictionary(dictionary, "PS", 0.0);
result.m_projectionScaleMode = loader.readEnumByName(dictionary->get("PS"), scaleModes.cbegin(), scaleModes.cend(), ScaleMode::Value);
}
result.m_scaleFactor = loader.readNumberFromDictionary(dictionary, "OS", 1.0);
result.m_scaleMode = result.m_projectionScaleMode = loader.readEnumByName(dictionary->get("OB"), scaleModes.cbegin(), scaleModes.cend(), ScaleMode::Absolute);
}
return result;
}
} // namespace pdf

View File

@ -22,6 +22,7 @@
#include "pdffile.h"
#include <QColor>
#include <QMatrix4x4>
#include <map>
#include <array>
@ -31,6 +32,8 @@ namespace pdf
{
class PDFObjectStorage;
using PDFPoint3D = std::array<PDFReal, 3>;
struct PDFMediaMultiLanguageTexts
{
static PDFMediaMultiLanguageTexts parse(const PDFObjectStorage* storage, PDFObject object);
@ -899,8 +902,8 @@ public:
Windowed
};
TriggerMode getActivationTriggerMode() const { return m_activationMode; }
TriggerMode getDeactivationTriggerMode() const { return m_deactivationMode; }
TriggerMode getActivationTriggerMode() const { return m_activationTriggerMode; }
TriggerMode getDeactivationTriggerMode() const { return m_deactivationTriggerMode; }
ActivationMode getActivationMode() const { return m_activationMode; }
ActivationMode getDeactivationMode() const { return m_deactivationMode; }
bool hasToolbar() const { return m_toolbar; }
@ -925,6 +928,228 @@ private:
bool m_transparent = false;
};
/// 3D PDF render mode
class PDF3DRenderMode
{
public:
explicit inline PDF3DRenderMode() = default;
enum class RenderMode
{
Solid,
SolidWireframe,
Transparent,
TransparentWireframe,
BoundingBox,
TransparentBoundingBox,
TransparentBoundingBoxOutline,
Wireframe,
ShadedWireframe,
HiddenWireframe,
Vertices,
ShadedVertices,
Illustration,
SolidOutline,
ShadedIllustration
};
enum class FaceColorMode
{
BG, ///< Current background color
Color ///< Color specified
};
RenderMode getRenderMode() const { return m_renderMode; }
QColor getAuxiliaryColor() const { return m_auxiliaryColor; }
QColor getFaceColor() const { return m_faceColor; }
FaceColorMode getFaceColorMode() const { return m_faceColorMode; }
PDFReal getOpacity() const { return m_opacity; }
PDFReal getCreaseAngle() const { return m_creaseAngle; }
/// Creates a new 3D render mode from the object. If data are invalid, then invalid object
/// is returned, no exception is thrown.
static PDF3DRenderMode parse(const PDFObjectStorage* storage, PDFObject object);
private:
RenderMode m_renderMode = RenderMode::Solid;
QColor m_auxiliaryColor = Qt::black;
QColor m_faceColor = Qt::black;
FaceColorMode m_faceColorMode = FaceColorMode::BG;
PDFReal m_opacity = 0.5;
PDFReal m_creaseAngle = 45.0;
};
/// 3D PDF node (part of 3D structure)
class PDF3DNode
{
public:
explicit inline PDF3DNode() = default;
const QString& getName() const { return m_name; }
const std::optional<PDFReal>& getOpacity() const { return m_opacity; }
const std::optional<bool>& getVisibility() const { return m_visibility; }
const std::optional<QMatrix4x4>& getMatrix() const { return m_matrix; }
PDFObjectReference getInstance() const { return m_instance; }
const QString& getData() const { return m_data; }
const std::optional<PDF3DRenderMode>& getRenderMode() const { return m_renderMode; }
/// Creates a new 3D node from the object. If data are invalid, then invalid object
/// is returned, no exception is thrown.
static PDF3DNode parse(const PDFObjectStorage* storage, PDFObject object);
private:
QString m_name;
std::optional<PDFReal> m_opacity;
std::optional<bool> m_visibility;
std::optional<QMatrix4x4> m_matrix;
PDFObjectReference m_instance;
QString m_data;
std::optional<PDF3DRenderMode> m_renderMode;
};
/// 3D PDF Cross section
class PDF3DCrossSection
{
public:
explicit inline PDF3DCrossSection() = default;
enum class Direction
{
X,
Y,
Z
};
PDFPoint3D getCenterOfRotation() const { return m_centerOfRotation; }
PDFPoint3D getRotationAngles() const { return m_rotationAngles; }
Direction getPerpendicularDirection() const { return m_perpendicularDirection; }
PDFReal getCutPlaneOpacity() const { return m_cutPlaneOpacity; }
QColor getCutPlaneColor() const { return m_cutPlaneColor; }
bool getIntersectionVisibility() const { return m_intersectionVisibility; }
QColor getIntersectionColor() const { return m_intersectionColor; }
bool getShowTransparent() const { return m_showTransparent; }
bool getSectionCapping() const { return m_sectionCapping; }
/// Creates a new 3D cross section from the object. If data are invalid, then invalid object
/// is returned, no exception is thrown.
static PDF3DCrossSection parse(const PDFObjectStorage* storage, PDFObject object);
private:
PDFPoint3D m_centerOfRotation{};
PDFPoint3D m_rotationAngles{};
Direction m_perpendicularDirection = Direction::X;
PDFReal m_cutPlaneOpacity = 0.5;
QColor m_cutPlaneColor = Qt::white;
bool m_intersectionVisibility = false;
QColor m_intersectionColor = Qt::green;
bool m_showTransparent = false;
bool m_sectionCapping = false;
};
/// 3D PDF lighting scheme
class PDF3DLightingScheme
{
public:
explicit inline PDF3DLightingScheme() = default;
enum class LightingScheme
{
Artwork,
None,
White,
Day,
Night,
Hard,
Primary,
Blue,
Red,
Cube,
CAD,
Headlamp
};
LightingScheme getLightingScheme() const { return m_scheme; }
/// Creates a new 3D lighting scheme from the object. If data are invalid, then invalid object
/// is returned, no exception is thrown.
static PDF3DLightingScheme parse(const PDFObjectStorage* storage, PDFObject object);
private:
LightingScheme m_scheme = LightingScheme::Artwork;
};
/// 3D PDF background
class PDF3DBackground
{
public:
explicit inline PDF3DBackground() = default;
QColor getColor() const { return m_color; }
bool getEntireAnnotation() const { return m_entireAnnotation; }
/// Creates a new 3D background from the object. If data are invalid, then invalid object
/// is returned, no exception is thrown.
static PDF3DBackground parse(const PDFObjectStorage* storage, PDFObject object);
private:
QColor m_color;
bool m_entireAnnotation = false;
};
/// 3D PDF projection
class PDF3DProjection
{
public:
explicit inline PDF3DProjection() = default;
enum class Projection
{
Orthographic,
Perspective
};
enum class ClippingStyle
{
Explicit,
Automatic
};
enum class ScaleMode
{
W,
H,
Min,
Max,
Absolute,
Value ///< This means projection scaling diameter is defined
};
Projection getProjection() const { return m_projection; }
ClippingStyle getClippingStyle() const { return m_clippingStyle; }
PDFReal getNearPlane() const { return m_near; }
PDFReal getFarPlane() const { return m_far; }
PDFReal getFieldOfViewAngle() const { return m_fieldOfViewAngle; }
PDFReal getProjectionScalingDiameter() const { return m_projectionScalingDiameter; }
ScaleMode getProjectionScaleMode() const { return m_projectionScaleMode; }
PDFReal getScaleFactor() const { return m_scaleFactor; }
ScaleMode getScaleMode() const { return m_scaleMode; }
/// Creates a new 3D projection from the object. If data are invalid, then invalid object
/// is returned, no exception is thrown.
static PDF3DProjection parse(const PDFObjectStorage* storage, PDFObject object);
private:
Projection m_projection = Projection::Orthographic;
ClippingStyle m_clippingStyle = ClippingStyle::Automatic;
PDFReal m_near = 0.0;
PDFReal m_far = qInf();
PDFReal m_fieldOfViewAngle = 90.0;
PDFReal m_projectionScalingDiameter = 0.0;
ScaleMode m_projectionScaleMode = ScaleMode::W;
PDFReal m_scaleFactor = 1.0;
ScaleMode m_scaleMode = ScaleMode::Absolute;
};
} // namespace pdf
#endif // PDFMULTIMEDIA_H