Parsing of annotations

This commit is contained in:
Jakub Melka 2020-03-06 19:25:34 +01:00
parent 758ed1590f
commit 92c58f68ff
4 changed files with 1018 additions and 3 deletions

View File

@ -268,6 +268,258 @@ PDFAnnotationPtr PDFAnnotation::parse(const PDFDocument* document, PDFObject obj
lineAnnotation->m_captionOffset == QPointF(captionOffset[0], captionOffset[1]);
}
}
else if (subtype == "Square" || subtype == "Circle")
{
PDFSimpleGeometryAnnotation* annotation = new PDFSimpleGeometryAnnotation((subtype == "Square") ? AnnotationType::Square : AnnotationType::Circle);
result.reset(annotation);
annotation->m_interiorColor = loader.readNumberArrayFromDictionary(dictionary, "IC");
annotation->m_effect = PDFAnnotationBorderEffect::parse(document, dictionary->get("BE"));
std::vector<PDFReal> differenceRectangle = loader.readNumberArrayFromDictionary(dictionary, "RD");
if (differenceRectangle.size() == 4)
{
annotation->m_geometryRectangle = annotationsRectangle.adjusted(differenceRectangle[0], differenceRectangle[1], -differenceRectangle[2], -differenceRectangle[3]);
if (!annotation->m_geometryRectangle.isValid())
{
annotation->m_geometryRectangle = QRectF();
}
}
}
else if (subtype == "Polygon" || subtype == "PolyLine")
{
PDFPolygonalGeometryAnnotation* annotation = new PDFPolygonalGeometryAnnotation((subtype == "Polygon") ? AnnotationType::Polygon : AnnotationType::Polyline);
result.reset(annotation);
std::vector<PDFReal> vertices = loader.readNumberArrayFromDictionary(dictionary, "Vertices");
const size_t count = vertices.size() / 2;
annotation->m_vertices.reserve(count);
for (size_t i = 0; i < count; ++i)
{
annotation->m_vertices.emplace_back(vertices[2 * i], vertices[2 * i + 1]);
}
std::vector<QByteArray> lineEndings = loader.readNameArrayFromDictionary(dictionary, "LE");
if (lineEndings.size() == 2)
{
annotation->m_startLineEnding = convertNameToLineEnding(lineEndings[0]);
annotation->m_endLineEnding = convertNameToLineEnding(lineEndings[1]);
}
annotation->m_interiorColor = loader.readNumberArrayFromDictionary(dictionary, "IC");
annotation->m_effect = PDFAnnotationBorderEffect::parse(document, dictionary->get("BE"));
constexpr const std::array<std::pair<const char*, PDFPolygonalGeometryAnnotation::Intent>, 3> intents = {
std::pair<const char*, PDFPolygonalGeometryAnnotation::Intent>{ "PolygonCloud", PDFPolygonalGeometryAnnotation::Intent::Cloud },
std::pair<const char*, PDFPolygonalGeometryAnnotation::Intent>{ "PolyLineDimension", PDFPolygonalGeometryAnnotation::Intent::Dimension },
std::pair<const char*, PDFPolygonalGeometryAnnotation::Intent>{ "PolygonDimension", PDFPolygonalGeometryAnnotation::Intent::Dimension }
};
annotation->m_intent = loader.readEnumByName(dictionary->get("IT"), intents.begin(), intents.end(), PDFPolygonalGeometryAnnotation::Intent::None);
annotation->m_measure = document->getObject(dictionary->get("Measure"));
}
else if (subtype == "Highlight" ||
subtype == "Underline" ||
subtype == "Squiggly" ||
subtype == "StrikeOut")
{
AnnotationType type = AnnotationType::Highlight;
if (subtype == "Underline")
{
type = AnnotationType::Underline;
}
else if (subtype == "Squiggly")
{
type = AnnotationType::Squiggly;
}
else if (subtype == "StrikeOut")
{
type = AnnotationType::StrikeOut;
}
PDFHighlightAnnotation* annotation = new PDFHighlightAnnotation(type);
result.reset(annotation);
annotation->m_highlightArea = parseQuadrilaterals(document, dictionary->get("QuadPoints"), annotationsRectangle);
}
else if (subtype == "Caret")
{
PDFCaretAnnotation* annotation = new PDFCaretAnnotation();
result.reset(annotation);
std::vector<PDFReal> differenceRectangle = loader.readNumberArrayFromDictionary(dictionary, "RD");
if (differenceRectangle.size() == 4)
{
annotation->m_caretRectangle = annotationsRectangle.adjusted(differenceRectangle[0], differenceRectangle[1], -differenceRectangle[2], -differenceRectangle[3]);
if (!annotation->m_caretRectangle.isValid())
{
annotation->m_caretRectangle = QRectF();
}
}
annotation->m_symbol = (loader.readNameFromDictionary(dictionary, "Sy") == "P") ? PDFCaretAnnotation::Symbol::Paragraph : PDFCaretAnnotation::Symbol::None;
}
else if (subtype == "Stamp")
{
PDFStampAnnotation* annotation = new PDFStampAnnotation();
result.reset(annotation);
constexpr const std::array<std::pair<const char*, PDFStampAnnotation::Stamp>, 14> stamps = {
std::pair<const char*, PDFStampAnnotation::Stamp>{ "Approved", PDFStampAnnotation::Stamp::Approved },
std::pair<const char*, PDFStampAnnotation::Stamp>{ "AsIs", PDFStampAnnotation::Stamp::AsIs },
std::pair<const char*, PDFStampAnnotation::Stamp>{ "Confidential", PDFStampAnnotation::Stamp::Confidential },
std::pair<const char*, PDFStampAnnotation::Stamp>{ "Departmental", PDFStampAnnotation::Stamp::Departmental },
std::pair<const char*, PDFStampAnnotation::Stamp>{ "Draft", PDFStampAnnotation::Stamp::Draft },
std::pair<const char*, PDFStampAnnotation::Stamp>{ "Experimental", PDFStampAnnotation::Stamp::Experimental },
std::pair<const char*, PDFStampAnnotation::Stamp>{ "Expired", PDFStampAnnotation::Stamp::Expired },
std::pair<const char*, PDFStampAnnotation::Stamp>{ "Final", PDFStampAnnotation::Stamp::Final },
std::pair<const char*, PDFStampAnnotation::Stamp>{ "ForComment", PDFStampAnnotation::Stamp::ForComment },
std::pair<const char*, PDFStampAnnotation::Stamp>{ "ForPublicRelease", PDFStampAnnotation::Stamp::ForPublicRelease },
std::pair<const char*, PDFStampAnnotation::Stamp>{ "NotApproved", PDFStampAnnotation::Stamp::NotApproved },
std::pair<const char*, PDFStampAnnotation::Stamp>{ "NotForPublicRelease", PDFStampAnnotation::Stamp::NotForPublicRelease },
std::pair<const char*, PDFStampAnnotation::Stamp>{ "Sold", PDFStampAnnotation::Stamp::Sold },
std::pair<const char*, PDFStampAnnotation::Stamp>{ "TopSecret", PDFStampAnnotation::Stamp::TopSecret }
};
annotation->m_stamp = loader.readEnumByName(dictionary->get("Name"), stamps.begin(), stamps.end(), PDFStampAnnotation::Stamp::Draft);
}
else if (subtype == "Ink")
{
PDFInkAnnotation* annotation = new PDFInkAnnotation();
result.reset(annotation);
PDFObject inkList = document->getObject(dictionary->get("InkList"));
if (inkList.isArray())
{
const PDFArray* inkListArray = inkList.getArray();
for (size_t i = 0, count = inkListArray->getCount(); i < count; ++i)
{
std::vector<PDFReal> points = loader.readNumberArray(inkListArray->getItem(i));
const size_t pointCount = points.size() / 2;
for (size_t j = 0; j < pointCount; ++j)
{
QPointF point(points[j * 2], points[j * 2 + 1]);
if (j == 0)
{
annotation->m_inkPath.moveTo(point);
}
else
{
annotation->m_inkPath.lineTo(point);
}
}
annotation->m_inkPath.closeSubpath();
}
}
}
else if (subtype == "Popup")
{
PDFPopupAnnotation* annotation = new PDFPopupAnnotation();
result.reset(annotation);
annotation->m_opened = loader.readBooleanFromDictionary(dictionary, "Open", false);
}
else if (subtype == "FileAttachment")
{
PDFFileAttachmentAnnotation* annotation = new PDFFileAttachmentAnnotation();
result.reset(annotation);
annotation->m_fileSpecification = PDFFileSpecification::parse(document, dictionary->get("FS"));
constexpr const std::array<std::pair<const char*, PDFFileAttachmentAnnotation::Icon>, 4> icons = {
std::pair<const char*, PDFFileAttachmentAnnotation::Icon>{ "Graph", PDFFileAttachmentAnnotation::Icon::Graph },
std::pair<const char*, PDFFileAttachmentAnnotation::Icon>{ "Paperclip", PDFFileAttachmentAnnotation::Icon::Paperclip },
std::pair<const char*, PDFFileAttachmentAnnotation::Icon>{ "PushPin", PDFFileAttachmentAnnotation::Icon::PushPin },
std::pair<const char*, PDFFileAttachmentAnnotation::Icon>{ "Tag", PDFFileAttachmentAnnotation::Icon::Tag }
};
annotation->m_icon = loader.readEnumByName(dictionary->get("Name"), icons.begin(), icons.end(), PDFFileAttachmentAnnotation::Icon::PushPin);
}
else if (subtype == "Sound")
{
PDFSoundAnnotation* annotation = new PDFSoundAnnotation();
result.reset(annotation);
annotation->m_sound = PDFSound::parse(document, dictionary->get("Sound"));
constexpr const std::array<std::pair<const char*, PDFSoundAnnotation::Icon>, 2> icons = {
std::pair<const char*, PDFSoundAnnotation::Icon>{ "Speaker", PDFSoundAnnotation::Icon::Speaker },
std::pair<const char*, PDFSoundAnnotation::Icon>{ "Mic", PDFSoundAnnotation::Icon::Microphone }
};
annotation->m_icon = loader.readEnumByName(dictionary->get("Name"), icons.begin(), icons.end(), PDFSoundAnnotation::Icon::Speaker);
}
else if (subtype == "Movie")
{
PDFMovieAnnotation* annotation = new PDFMovieAnnotation();
result.reset(annotation);
annotation->m_movieTitle = loader.readStringFromDictionary(dictionary, "T");
annotation->m_movie = PDFMovie::parse(document, dictionary->get("Movie"));
PDFObject activation = document->getObject(dictionary->get("A"));
if (activation.isBool())
{
annotation->m_playMovie = activation.getBool();
}
else if (activation.isDictionary())
{
annotation->m_playMovie = true;
annotation->m_movieActivation = PDFMovieActivation::parse(document, activation);
}
}
else if (subtype == "Screen")
{
PDFScreenAnnotation* annotation = new PDFScreenAnnotation();
result.reset(annotation);
annotation->m_screenTitle = loader.readTextStringFromDictionary(dictionary, "T", QString());
annotation->m_appearanceCharacteristics = PDFAnnotationAppearanceCharacteristics::parse(document, dictionary->get("MK"));
annotation->m_action = PDFAction::parse(document, dictionary->get("A"));
annotation->m_additionalActions = PDFAnnotationAdditionalActions::parse(document, dictionary->get("AA"));
}
else if (subtype == "Widget")
{
PDFWidgetAnnotation* annotation = new PDFWidgetAnnotation();
result.reset(annotation);
constexpr const std::array<std::pair<const char*, PDFWidgetAnnotation::HighlightMode>, 5> highlightModes = {
std::pair<const char*, PDFWidgetAnnotation::HighlightMode>{ "N", PDFWidgetAnnotation::HighlightMode::None },
std::pair<const char*, PDFWidgetAnnotation::HighlightMode>{ "I", PDFWidgetAnnotation::HighlightMode::Invert },
std::pair<const char*, PDFWidgetAnnotation::HighlightMode>{ "O", PDFWidgetAnnotation::HighlightMode::Outline },
std::pair<const char*, PDFWidgetAnnotation::HighlightMode>{ "P", PDFWidgetAnnotation::HighlightMode::Push },
std::pair<const char*, PDFWidgetAnnotation::HighlightMode>{ "T", PDFWidgetAnnotation::HighlightMode::Toggle }
};
annotation->m_highlightMode = loader.readEnumByName(dictionary->get("H"), highlightModes.begin(), highlightModes.end(), PDFWidgetAnnotation::HighlightMode::Invert);
annotation->m_appearanceCharacteristics = PDFAnnotationAppearanceCharacteristics::parse(document, dictionary->get("MK"));
annotation->m_action = PDFAction::parse(document, dictionary->get("A"));
annotation->m_additionalActions = PDFAnnotationAdditionalActions::parse(document, dictionary->get("AA"));
}
else if (subtype == "PrinterMark")
{
PDFPrinterMarkAnnotation* annotation = new PDFPrinterMarkAnnotation();
result.reset(annotation);
}
else if (subtype == "TrapNet")
{
PDFTrapNetworkAnnotation* annotation = new PDFTrapNetworkAnnotation();
result.reset(annotation);
}
else if (subtype == "Watermark")
{
PDFWatermarkAnnotation* annotation = new PDFWatermarkAnnotation();
result.reset(annotation);
if (const PDFDictionary* fixedPrintDictionary = document->getDictionaryFromObject(dictionary->get("FixedPrint")))
{
annotation->m_matrix = loader.readMatrixFromDictionary(fixedPrintDictionary, "Matrix", QMatrix());
annotation->m_relativeHorizontalOffset = loader.readNumberFromDictionary(fixedPrintDictionary, "H", 0.0);
annotation->m_relativeVerticalOffset = loader.readNumberFromDictionary(fixedPrintDictionary, "V", 0.0);
}
}
if (!result)
{
@ -401,4 +653,83 @@ PDFAnnotationCalloutLine PDFAnnotationCalloutLine::parse(const PDFDocument* docu
return PDFAnnotationCalloutLine();
}
PDFAnnotationAppearanceCharacteristics PDFAnnotationAppearanceCharacteristics::parse(const PDFDocument* document, PDFObject object)
{
PDFAnnotationAppearanceCharacteristics result;
if (const PDFDictionary* dictionary = document->getDictionaryFromObject(object))
{
PDFDocumentDataLoaderDecorator loader(document);
result.m_rotation = loader.readIntegerFromDictionary(dictionary, "R", 0);
result.m_borderColor = loader.readNumberArrayFromDictionary(dictionary, "BC");
result.m_backgroundColor = loader.readNumberArrayFromDictionary(dictionary, "BG");
result.m_normalCaption = loader.readTextStringFromDictionary(dictionary, "CA", QString());
result.m_rolloverCaption = loader.readTextStringFromDictionary(dictionary, "RC", QString());
result.m_downCaption = loader.readTextStringFromDictionary(dictionary, "AC", QString());
result.m_normalIcon = document->getObject(dictionary->get("I"));
result.m_rolloverIcon = document->getObject(dictionary->get("RI"));
result.m_downIcon = document->getObject(dictionary->get("IX"));
result.m_iconFit = PDFAnnotationIconFitInfo::parse(document, dictionary->get("IF"));
result.m_pushButtonMode = static_cast<PushButtonMode>(loader.readIntegerFromDictionary(dictionary, "TP", PDFInteger(PDFAnnotationAppearanceCharacteristics::PushButtonMode::NoIcon)));
}
return result;
}
PDFAnnotationIconFitInfo PDFAnnotationIconFitInfo::parse(const PDFDocument* document, PDFObject object)
{
PDFAnnotationIconFitInfo info;
if (const PDFDictionary* dictionary = document->getDictionaryFromObject(object))
{
PDFDocumentDataLoaderDecorator loader(document);
constexpr const std::array<std::pair<const char*, PDFAnnotationIconFitInfo::ScaleCondition>, 4> scaleConditions = {
std::pair<const char*, PDFAnnotationIconFitInfo::ScaleCondition>{ "A", PDFAnnotationIconFitInfo::ScaleCondition::Always },
std::pair<const char*, PDFAnnotationIconFitInfo::ScaleCondition>{ "B", PDFAnnotationIconFitInfo::ScaleCondition::ScaleBigger },
std::pair<const char*, PDFAnnotationIconFitInfo::ScaleCondition>{ "S", PDFAnnotationIconFitInfo::ScaleCondition::ScaleSmaller },
std::pair<const char*, PDFAnnotationIconFitInfo::ScaleCondition>{ "N", PDFAnnotationIconFitInfo::ScaleCondition::Never }
};
constexpr const std::array<std::pair<const char*, PDFAnnotationIconFitInfo::ScaleType>, 2> scaleTypes = {
std::pair<const char*, PDFAnnotationIconFitInfo::ScaleType>{ "A", PDFAnnotationIconFitInfo::ScaleType::Anamorphic },
std::pair<const char*, PDFAnnotationIconFitInfo::ScaleType>{ "P", PDFAnnotationIconFitInfo::ScaleType::Proportional }
};
std::vector<PDFReal> point = loader.readNumberArrayFromDictionary(dictionary, "A");
if (point.size() != 2)
{
point.resize(2, 0.5);
}
info.m_scaleCondition = loader.readEnumByName(dictionary->get("SW"), scaleConditions.begin(), scaleConditions.end(), PDFAnnotationIconFitInfo::ScaleCondition::Always);
info.m_scaleType = loader.readEnumByName(dictionary->get("S"), scaleTypes.begin(), scaleTypes.end(), PDFAnnotationIconFitInfo::ScaleType::Proportional);
info.m_relativeProportionalPosition = QPointF(point[0], point[1]);
info.m_fullBox = loader.readBooleanFromDictionary(dictionary, "FB", false);
}
return info;
}
PDFAnnotationAdditionalActions PDFAnnotationAdditionalActions::parse(const PDFDocument* document, PDFObject object)
{
PDFAnnotationAdditionalActions result;
if (const PDFDictionary* dictionary = document->getDictionaryFromObject(object))
{
result.m_actions[CursorEnter] = PDFAction::parse(document, dictionary->get("E"));
result.m_actions[CursorLeave] = PDFAction::parse(document, dictionary->get("X"));
result.m_actions[MousePressed] = PDFAction::parse(document, dictionary->get("D"));
result.m_actions[MouseReleased] = PDFAction::parse(document, dictionary->get("U"));
result.m_actions[FocusIn] = PDFAction::parse(document, dictionary->get("Fo"));
result.m_actions[FocusOut] = PDFAction::parse(document, dictionary->get("Bl"));
result.m_actions[PageOpened] = PDFAction::parse(document, dictionary->get("PO"));
result.m_actions[PageClosed] = PDFAction::parse(document, dictionary->get("PC"));
result.m_actions[PageShow] = PDFAction::parse(document, dictionary->get("PV"));
result.m_actions[PageHide] = PDFAction::parse(document, dictionary->get("PI"));
}
return result;
}
} // namespace pdf

View File

@ -1,4 +1,4 @@
// Copyright (C) 2020 Jakub Melka
// Copyright (C) 2020 Jakub Melka
//
// This file is part of PdfForQt.
//
@ -21,6 +21,8 @@
#include "pdfglobal.h"
#include "pdfobject.h"
#include "pdfaction.h"
#include "pdffile.h"
#include "pdfmultimedia.h"
#include <QPainterPath>
@ -51,7 +53,7 @@ enum class AnnotationType
Popup,
FileAttachment,
Sound,
Moview,
Movie,
Widget,
Screen,
PrinterMark,
@ -241,7 +243,7 @@ public:
/// Parses annotation callout line from the object. If object is invalid, then
/// invalid callout line is constructed.
/// \param document Document
/// \param object Appearance streams object
/// \param object Callout line object
static PDFAnnotationCalloutLine parse(const PDFDocument* document, PDFObject object);
bool isValid() const { return m_type != Type::Invalid; }
@ -254,6 +256,128 @@ private:
std::array<QPointF, 3> m_points;
};
/// Information about annotation icon fitting (in the widget)
class PDFAnnotationIconFitInfo
{
public:
inline explicit PDFAnnotationIconFitInfo() = default;
enum class ScaleCondition
{
Always,
ScaleBigger,
ScaleSmaller,
Never
};
enum class ScaleType
{
Anamorphic, ///< Do not keep aspect ratio, fit whole annotation rectangle
Proportional ///< Keep aspect ratio, annotation rectangle may not be filled fully with icon
};
/// Parses annotation appearance icon fit info from the object. If object is invalid, then
/// default appearance icon fit info is constructed.
/// \param document Document
/// \param object Appearance icon fit info object
static PDFAnnotationIconFitInfo parse(const PDFDocument* document, PDFObject object);
private:
ScaleCondition m_scaleCondition = ScaleCondition::Always;
ScaleType m_scaleType = ScaleType::Proportional;
QPointF m_relativeProportionalPosition = QPointF(0.5, 0.5);
bool m_fullBox = false;
};
/// Additional appearance characteristics used for constructing of appearance
/// stream to display annotation on the screen (or just paint it).
class PDFAnnotationAppearanceCharacteristics
{
public:
inline explicit PDFAnnotationAppearanceCharacteristics() = default;
enum class PushButtonMode
{
NoIcon,
NoCaption,
IconWithCaptionBelow,
IconWithCaptionAbove,
IconWithCaptionRight,
IconWithCaptionLeft,
IconWithCaptionOverlaid
};
/// Number of degrees by which the widget annotation is rotated
/// counterclockwise relative to the page.
PDFInteger getRotation() const { return m_rotation; }
const std::vector<PDFReal>& getBorderColor() const { return m_borderColor; }
const std::vector<PDFReal>& getBackgroundColor() const { return m_backgroundColor; }
const QString& getNormalCaption() const { return m_normalCaption; }
const QString& getRolloverCaption() const { return m_rolloverCaption; }
const QString& getDownCaption() const { return m_downCaption; }
const PDFObject& getNormalIcon() const { return m_normalIcon; }
const PDFObject& getRolloverIcon() const { return m_rolloverIcon; }
const PDFObject& getDownIcon() const { return m_downIcon; }
const PDFAnnotationIconFitInfo& getIconFit() const { return m_iconFit; }
PushButtonMode getPushButtonMode() const { return m_pushButtonMode; }
/// Parses annotation appearance characteristics from the object. If object is invalid, then
/// default appearance characteristics is constructed.
/// \param document Document
/// \param object Appearance characteristics object
static PDFAnnotationAppearanceCharacteristics parse(const PDFDocument* document, PDFObject object);
private:
PDFInteger m_rotation = 0;
std::vector<PDFReal> m_borderColor;
std::vector<PDFReal> m_backgroundColor;
QString m_normalCaption;
QString m_rolloverCaption;
QString m_downCaption;
PDFObject m_normalIcon;
PDFObject m_rolloverIcon;
PDFObject m_downIcon;
PDFAnnotationIconFitInfo m_iconFit;
PushButtonMode m_pushButtonMode = PushButtonMode::NoIcon;
};
/// Storage for annotation additional actions
class PDFAnnotationAdditionalActions
{
public:
enum Action
{
CursorEnter,
CursorLeave,
MousePressed,
MouseReleased,
FocusIn,
FocusOut,
PageOpened,
PageClosed,
PageShow,
PageHide,
End
};
inline explicit PDFAnnotationAdditionalActions() = default;
/// Returns action for given type. If action is invalid,
/// or not present, nullptr is returned.
/// \param action Action type
const PDFAction* getAction(Action action) const { return m_actions.at(action).get(); }
/// Parses annotation additional actions from the object. If object is invalid, then
/// empty additional actions is constructed.
/// \param document Document
/// \param object Additional actions object
static PDFAnnotationAdditionalActions parse(const PDFDocument* document, PDFObject object);
private:
std::array<PDFActionPtr, End> m_actions;
};
class PDFAnnotation;
class PDFMarkupAnnotation;
class PDFTextAnnotation;
@ -538,6 +662,365 @@ private:
QPointF m_captionOffset;
};
/// Simple geometry annotation.
/// Square and circle annotations displays rectangle or ellipse on the page.
/// Name is a bit strange (because rectangle may not be a square or circle is not ellipse),
/// but it is defined in PDF specification, so we will use these terms.
class PDFSimpleGeometryAnnotation : public PDFMarkupAnnotation
{
public:
inline explicit PDFSimpleGeometryAnnotation(AnnotationType type) :
m_type(type)
{
}
virtual AnnotationType getType() const override { return m_type; }
const std::vector<PDFReal>& getInteriorColor() const { return m_interiorColor; }
const PDFAnnotationBorderEffect& getBorderEffect() const { return m_effect; }
const QRectF& getGeometryRectangle() const { return m_geometryRectangle; }
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFDocument* document, PDFObject object);
AnnotationType m_type;
std::vector<PDFReal> m_interiorColor;
PDFAnnotationBorderEffect m_effect;
QRectF m_geometryRectangle;
};
/// Polygonal geometry, consists of polygon or polyline geometry. Polygon annotation
/// displays closed polygon (potentially filled), polyline annotation displays
/// polyline, which is not closed.
class PDFPolygonalGeometryAnnotation : public PDFMarkupAnnotation
{
public:
enum class Intent
{
None,
Cloud,
Dimension
};
inline explicit PDFPolygonalGeometryAnnotation(AnnotationType type) :
m_type(type),
m_intent(Intent::None)
{
}
virtual AnnotationType getType() const override { return m_type; }
const std::vector<QPointF>& getVertices() const { return m_vertices; }
AnnotationLineEnding getStartLineEnding() const { return m_startLineEnding; }
AnnotationLineEnding getEndLineEnding() const { return m_endLineEnding; }
const std::vector<PDFReal>& getInteriorColor() const { return m_interiorColor; }
const PDFAnnotationBorderEffect& getBorderEffect() const { return m_effect; }
Intent getIntent() const { return m_intent; }
const PDFObject& getMeasure() const { return m_measure; }
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFDocument* document, PDFObject object);
AnnotationType m_type;
std::vector<QPointF> m_vertices;
AnnotationLineEnding m_startLineEnding = AnnotationLineEnding::None;
AnnotationLineEnding m_endLineEnding = AnnotationLineEnding::None;
std::vector<PDFReal> m_interiorColor;
PDFAnnotationBorderEffect m_effect;
Intent m_intent;
PDFObject m_measure;
};
/// Annotation for text highlighting. Can highlight, underline, strikeout,
/// or squiggly underline the text.
class PDFHighlightAnnotation : public PDFMarkupAnnotation
{
public:
inline explicit PDFHighlightAnnotation(AnnotationType type) :
m_type(type)
{
}
virtual AnnotationType getType() const override { return m_type; }
const PDFAnnotationQuadrilaterals& getHiglightArea() const { return m_highlightArea; }
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFDocument* document, PDFObject object);
AnnotationType m_type;
PDFAnnotationQuadrilaterals m_highlightArea;
};
/// Annotation for visual symbol that indicates presence of text edits.
class PDFCaretAnnotation : public PDFMarkupAnnotation
{
public:
inline explicit PDFCaretAnnotation() = default;
enum class Symbol
{
None,
Paragraph
};
virtual AnnotationType getType() const override { return AnnotationType::Caret; }
const QRectF& getCaretRectangle() const { return m_caretRectangle; }
Symbol getSymbol() const { return m_symbol; }
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFDocument* document, PDFObject object);
QRectF m_caretRectangle;
Symbol m_symbol = Symbol::None;
};
/// Annotation for stamps. Displays text or graphics intended to look
/// as if they were stamped on the paper.
class PDFStampAnnotation : public PDFMarkupAnnotation
{
public:
inline explicit PDFStampAnnotation() = default;
enum class Stamp
{
Approved,
AsIs,
Confidential,
Departmental,
Draft,
Experimental,
Expired,
Final,
ForComment,
ForPublicRelease,
NotApproved,
NotForPublicRelease,
Sold,
TopSecret
};
virtual AnnotationType getType() const override { return AnnotationType::Stamp; }
Stamp getStamp() const { return m_stamp; }
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFDocument* document, PDFObject object);
Stamp m_stamp = Stamp::Draft;
};
/// Ink annotation. Represents a path composed of disjoint polygons.
class PDFInkAnnotation : public PDFMarkupAnnotation
{
public:
inline explicit PDFInkAnnotation() = default;
virtual AnnotationType getType() const override { return AnnotationType::Ink; }
const QPainterPath& getInkPath() const { return m_inkPath; }
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFDocument* document, PDFObject object);
QPainterPath m_inkPath;
};
/// Popup annotation. Displays text in popup window for markup annotations.
/// This annotation contains field to associated annotation, for which
/// is window displayed, and window state (open/closed).
class PDFPopupAnnotation : public PDFAnnotation
{
public:
inline explicit PDFPopupAnnotation() = default;
virtual AnnotationType getType() const override { return AnnotationType::Popup; }
bool isOpened() const { return m_opened; }
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFDocument* document, PDFObject object);
bool m_opened = false;
};
/// File attachment annotation contains reference to (embedded or external) file.
/// So it is a link to specified file. Activating annotation enables user to view
/// or store attached file in the filesystem.
class PDFFileAttachmentAnnotation : public PDFMarkupAnnotation
{
public:
inline explicit PDFFileAttachmentAnnotation() = default;
enum class Icon
{
Graph,
Paperclip,
PushPin,
Tag
};
virtual AnnotationType getType() const override { return AnnotationType::FileAttachment; }
const PDFFileSpecification& getFileSpecification() const { return m_fileSpecification; }
Icon getIcon() const { return m_icon; }
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFDocument* document, PDFObject object);
PDFFileSpecification m_fileSpecification;
Icon m_icon = Icon::PushPin;
};
/// Sound annotation contains sound, which is played, when
/// annotation is activated.
class PDFSoundAnnotation : public PDFMarkupAnnotation
{
public:
inline explicit PDFSoundAnnotation() = default;
enum class Icon
{
Speaker,
Microphone
};
virtual AnnotationType getType() const override { return AnnotationType::Sound; }
const PDFSound& getSound() const { return m_sound; }
Icon getIcon() const { return m_icon; }
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFDocument* document, PDFObject object);
PDFSound m_sound;
Icon m_icon = Icon::Speaker;
};
/// Movie annotation contains movie or sound, which is played, when
/// annotation is activated.
class PDFMovieAnnotation : public PDFAnnotation
{
public:
inline explicit PDFMovieAnnotation() = default;
virtual AnnotationType getType() const override { return AnnotationType::Movie; }
const QString& getMovieTitle() const { return m_movieTitle; }
bool isMovieToBePlayed() const { return m_playMovie; }
const PDFMovie& getMovie() const { return m_movie; }
const PDFMovieActivation& getMovieActivation() const { return m_movieActivation; }
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFDocument* document, PDFObject object);
QString m_movieTitle;
bool m_playMovie = true;
PDFMovie m_movie;
PDFMovieActivation m_movieActivation;
};
/// Screen action represents area of page in which is media played.
/// See also Rendition actions and their relationship to this annotation.
class PDFScreenAnnotation : public PDFAnnotation
{
public:
inline explicit PDFScreenAnnotation() = default;
virtual AnnotationType getType() const override { return AnnotationType::Screen; }
const QString& getScreenTitle() const { return m_screenTitle; }
const PDFAnnotationAppearanceCharacteristics& getAppearanceCharacteristics() const { return m_appearanceCharacteristics; }
const PDFAction* getAction() const { return m_action.get(); }
const PDFAnnotationAdditionalActions& getAdditionalActions() const { return m_additionalActions; }
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFDocument* document, PDFObject object);
QString m_screenTitle;
PDFAnnotationAppearanceCharacteristics m_appearanceCharacteristics;
PDFActionPtr m_action;
PDFAnnotationAdditionalActions m_additionalActions;
};
/// Widget annotation represents form fileds for interactive forms.
/// Annotation's dictionary is merged with form field dictionary,
/// it can be done, because dictionaries doesn't overlap.
class PDFWidgetAnnotation : public PDFAnnotation
{
public:
inline explicit PDFWidgetAnnotation() = default;
enum class HighlightMode
{
None,
Invert,
Outline,
Push,
Toggle
};
virtual AnnotationType getType() const override { return AnnotationType::Widget; }
HighlightMode getHighlightMode() const { return m_highlightMode; }
const PDFAnnotationAppearanceCharacteristics& getAppearanceCharacteristics() const { return m_appearanceCharacteristics; }
const PDFAction* getAction() const { return m_action.get(); }
const PDFAnnotationAdditionalActions& getAdditionalActions() const { return m_additionalActions; }
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFDocument* document, PDFObject object);
HighlightMode m_highlightMode = HighlightMode::Invert;
PDFAnnotationAppearanceCharacteristics m_appearanceCharacteristics;
PDFActionPtr m_action;
PDFAnnotationAdditionalActions m_additionalActions;
};
/// Printer mark annotation represents graphics symbol, mark, or other
/// graphic feature to assist printing production.
class PDFPrinterMarkAnnotation : public PDFAnnotation
{
public:
inline explicit PDFPrinterMarkAnnotation() = default;
virtual AnnotationType getType() const override { return AnnotationType::PrinterMark; }
};
/// Trapping characteristics for the page
class PDFTrapNetworkAnnotation : public PDFAnnotation
{
public:
inline explicit PDFTrapNetworkAnnotation() = default;
virtual AnnotationType getType() const override { return AnnotationType::TrapNet; }
};
/// Watermark annotation represents watermark displayed on the page,
/// for example, if it is printed. Watermarks are displayed at fixed
/// position and size on the page.
class PDFWatermarkAnnotation : public PDFAnnotation
{
public:
inline explicit PDFWatermarkAnnotation() = default;
virtual AnnotationType getType() const override { return AnnotationType::Watermark; }
const QMatrix& getMatrix() const { return m_matrix; }
PDFReal getRelativeHorizontalOffset() const { return m_relativeHorizontalOffset; }
PDFReal getRelativeVerticalOffset() const { return m_relativeVerticalOffset; }
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFDocument* document, PDFObject object);
QMatrix m_matrix;
PDFReal m_relativeHorizontalOffset = 0.0;
PDFReal m_relativeVerticalOffset = 0.0;
};
} // namespace pdf
#endif // PDFANNOTATION_H

View File

@ -18,6 +18,8 @@
#include "pdfmultimedia.h"
#include "pdfdocument.h"
#include <QtEndian>
namespace pdf
{
@ -537,4 +539,127 @@ PDFMediaScreenParameters PDFMediaScreenParameters::parse(const PDFDocument* docu
return PDFMediaScreenParameters();
}
PDFMovie PDFMovie::parse(const PDFDocument* document, PDFObject object)
{
if (const PDFDictionary* dictionary = document->getDictionaryFromObject(object))
{
PDFMovie result;
PDFDocumentDataLoaderDecorator loader(document);
result.m_movieFile = PDFFileSpecification::parse(document, dictionary->get("F"));
std::vector<PDFInteger> windowSizeArray = loader.readIntegerArrayFromDictionary(dictionary, "Aspect");
if (windowSizeArray.size() == 2)
{
result.m_windowSize = QSize(windowSizeArray[0], windowSizeArray[1]);
}
result.m_rotationAngle = loader.readIntegerFromDictionary(dictionary, "Rotate", 0);
PDFObject posterObject = document->getObject(dictionary->get("Poster"));
if (posterObject.isBool())
{
result.m_showPoster = posterObject.getBool();
}
else if (posterObject.isStream())
{
result.m_showPoster = true;
result.m_poster = posterObject;
}
return result;
}
return PDFMovie();
}
PDFMovieActivation PDFMovieActivation::parse(const PDFDocument* document, PDFObject object)
{
PDFMovieActivation result;
if (const PDFDictionary* dictionary = document->getDictionaryFromObject(object))
{
PDFDocumentDataLoaderDecorator loader(document);
constexpr const std::array<std::pair<const char*, Mode>, 4> modes = {
std::pair<const char*, Mode>{ "Once", Mode::Once },
std::pair<const char*, Mode>{ "Open", Mode::Open },
std::pair<const char*, Mode>{ "Repeat", Mode::Repeat },
std::pair<const char*, Mode>{ "Palindrome", Mode::Palindrome }
};
std::vector<PDFInteger> scale = loader.readIntegerArrayFromDictionary(dictionary, "FWScale");
if (scale.size() != 2)
{
scale.resize(2, 0);
}
std::vector<PDFReal> relativePosition = loader.readNumberArrayFromDictionary(dictionary, "FWPosition");
if (relativePosition.size() != 2)
{
relativePosition.resize(2, 0.5);
}
result.m_start = parseMovieTime(document, dictionary->get("Start"));
result.m_duration = parseMovieTime(document, dictionary->get("Duration"));
result.m_rate = loader.readNumberFromDictionary(dictionary, "Rate", 1.0);
result.m_volume = loader.readNumberFromDictionary(dictionary, "Volume", 1.0);
result.m_showControls = loader.readBooleanFromDictionary(dictionary, "ShowControls", false);
result.m_synchronous = loader.readBooleanFromDictionary(dictionary, "Synchronous", false);
result.m_mode = loader.readEnumByName(dictionary->get("Mode"), modes.cbegin(), modes.cend(), Mode::Once);
result.m_scaleNumerator = scale[0];
result.m_scaleDenominator = scale[1];
result.m_relativeWindowPosition = QPointF(relativePosition[0], relativePosition[1]);
}
return result;
}
PDFMovieActivation::MovieTime PDFMovieActivation::parseMovieTime(const PDFDocument* document, PDFObject object)
{
MovieTime result;
object = document->getObject(object);
if (object.isInt())
{
result.value = object.getInteger();
}
else if (object.isString())
{
result.value = parseMovieTimeFromString(object.getString());
}
else if (object.isArray())
{
const PDFArray* objectArray = object.getArray();
if (objectArray->getCount() == 2)
{
PDFDocumentDataLoaderDecorator loader(document);
result.unitsPerSecond = loader.readInteger(objectArray->getItem(1), 0);
object = document->getObject(objectArray->getItem(0));
if (object.isInt())
{
result.value = object.getInteger();
}
else if (object.isString())
{
result.value = parseMovieTimeFromString(object.getString());
}
}
}
return result;
}
PDFInteger PDFMovieActivation::parseMovieTimeFromString(const QByteArray& string)
{
// According to the specification, the string contains 64-bit signed integer,
// in big-endian format.
if (string.size() == sizeof(quint64))
{
quint64 result = reinterpret_cast<quint64>(string.data());
qFromBigEndian<decltype(result)>(&result, qsizetype(sizeof(decltype(result))), &result);
return static_cast<PDFInteger>(result);
}
return 0;
}
} // namespace pdf

View File

@ -532,6 +532,82 @@ private:
PDFObject m_streamObject;
};
/// Movie object, see chapter 9.3 in PDF 1.7 reference
class PDFMovie
{
public:
explicit inline PDFMovie() = default;
const PDFFileSpecification* getMovieFileSpecification() const { return &m_movieFile; }
QSize getWindowSize() const { return m_windowSize; }
PDFInteger getRotationAngle() const { return m_rotationAngle; }
bool isPosterVisible() const { return m_showPoster; }
const PDFObject& getPosterObject() const { return m_poster; }
/// Creates a new movie from the object. If data are invalid, then invalid object
/// is returned, no exception is thrown.
static PDFMovie parse(const PDFDocument* document, PDFObject object);
private:
PDFFileSpecification m_movieFile;
QSize m_windowSize;
PDFInteger m_rotationAngle = 0;
bool m_showPoster = false;
PDFObject m_poster;
};
/// Movie activation object, see table 9.31 in PDF 1.7 reference
class PDFMovieActivation
{
public:
explicit inline PDFMovieActivation() = default;
struct MovieTime
{
PDFInteger value = 0;
std::optional<PDFInteger> unitsPerSecond;
};
enum class Mode
{
Once,
Open,
Repeat,
Palindrome
};
MovieTime getStartTime() const { return m_start; }
MovieTime getDuration() const { return m_duration; }
PDFReal getRate() const { return m_rate; }
PDFReal getVolume() const { return m_volume; }
bool isShowControls() const { return m_showControls; }
bool isSynchronous() const { return m_synchronous; }
Mode getMode() const { return m_mode; }
bool hasScale() const { return m_scaleDenominator != 0; }
PDFInteger getScaleNumerator() const { return m_scaleNumerator; }
PDFInteger getScaleDenominator() const { return m_scaleDenominator; }
QPointF getRelativeWindowPosition() const { return m_relativeWindowPosition; }
/// Creates a new moview from the object. If data are invalid, then invalid object
/// is returned, no exception is thrown.
static PDFMovieActivation parse(const PDFDocument* document, PDFObject object);
private:
static MovieTime parseMovieTime(const PDFDocument* document, PDFObject object);
static PDFInteger parseMovieTimeFromString(const QByteArray& string);
MovieTime m_start;
MovieTime m_duration;
PDFReal m_rate = 1.0;
PDFReal m_volume = 1.0;
bool m_showControls = false;
bool m_synchronous = false;
Mode m_mode = Mode::Once;
PDFInteger m_scaleNumerator = 0;
PDFInteger m_scaleDenominator = 0;
QPointF m_relativeWindowPosition;
};
} // namespace pdf
#endif // PDFMULTIMEDIA_H