First few annotations parsing

This commit is contained in:
Jakub Melka 2020-03-05 18:28:07 +01:00
parent de9e6b9807
commit 758ed1590f
4 changed files with 600 additions and 7 deletions

View File

@ -17,16 +17,11 @@
#include "pdfannotation.h"
#include "pdfdocument.h"
#include "pdfencoding.h"
namespace pdf
{
PDFAnnotation::PDFAnnotation() :
m_structParent(0)
{
}
PDFAnnotationBorder PDFAnnotationBorder::parseBorder(const PDFDocument* document, PDFObject object)
{
PDFAnnotationBorder result;
@ -150,4 +145,260 @@ PDFObject PDFAppeareanceStreams::getAppearance(Appearance appearance, const QByt
return PDFObject();
}
PDFAnnotation::PDFAnnotation() :
m_flags(),
m_structParent(0)
{
}
PDFAnnotationPtr PDFAnnotation::parse(const PDFDocument* document, PDFObject object)
{
PDFAnnotationPtr result;
const PDFDictionary* dictionary = document->getDictionaryFromObject(object);
if (!dictionary)
{
return result;
}
PDFDocumentDataLoaderDecorator loader(document);
QRectF annotationsRectangle = loader.readRectangle(dictionary->get("Rect"), QRectF());
// Determine type of annotation
QByteArray subtype = loader.readNameFromDictionary(dictionary, "Subtype");
if (subtype == "Text")
{
PDFTextAnnotation* textAnnotation = new PDFTextAnnotation;
result.reset(textAnnotation);
textAnnotation->m_open = loader.readBooleanFromDictionary(dictionary, "Open", false);
textAnnotation->m_iconName = loader.readNameFromDictionary(dictionary, "Name");
textAnnotation->m_state = loader.readTextStringFromDictionary(dictionary, "State", "Unmarked");
textAnnotation->m_stateModel = loader.readTextStringFromDictionary(dictionary, "StateModel", "Marked");
}
else if (subtype == "Link")
{
PDFLinkAnnotation* linkAnnotation = new PDFLinkAnnotation;
result.reset(linkAnnotation);
linkAnnotation->m_action = PDFAction::parse(document, dictionary->get("A"));
if (!linkAnnotation->m_action)
{
PDFDestination destination = PDFDestination::parse(document, dictionary->get("Dest"));
linkAnnotation->m_action.reset(new PDFActionGoTo(destination));
}
linkAnnotation->m_previousAction = PDFAction::parse(document, dictionary->get("PA"));
constexpr const std::array<std::pair<const char*, PDFLinkAnnotation::HighlightMode>, 4> highlightMode = {
std::pair<const char*, PDFLinkAnnotation::HighlightMode>{ "N", PDFLinkAnnotation::HighlightMode::None },
std::pair<const char*, PDFLinkAnnotation::HighlightMode>{ "I", PDFLinkAnnotation::HighlightMode::Invert },
std::pair<const char*, PDFLinkAnnotation::HighlightMode>{ "O", PDFLinkAnnotation::HighlightMode::Outline },
std::pair<const char*, PDFLinkAnnotation::HighlightMode>{ "P", PDFLinkAnnotation::HighlightMode::Push }
};
linkAnnotation->m_highlightMode = loader.readEnumByName(dictionary->get("H"), highlightMode.begin(), highlightMode.end(), PDFLinkAnnotation::HighlightMode::Invert);
linkAnnotation->m_activationRegion = parseQuadrilaterals(document, dictionary->get("QuadPoints"), annotationsRectangle);
}
else if (subtype == "FreeText")
{
PDFFreeTextAnnotation* freeTextAnnotation = new PDFFreeTextAnnotation;
result.reset(freeTextAnnotation);
constexpr const std::array<std::pair<const char*, PDFFreeTextAnnotation::Intent>, 2> intents = {
std::pair<const char*, PDFFreeTextAnnotation::Intent>{ "FreeTextCallout", PDFFreeTextAnnotation::Intent::Callout },
std::pair<const char*, PDFFreeTextAnnotation::Intent>{ "FreeTextTypeWriter", PDFFreeTextAnnotation::Intent::TypeWriter }
};
freeTextAnnotation->m_defaultAppearance = loader.readStringFromDictionary(dictionary, "DA");
freeTextAnnotation->m_justification = static_cast<PDFFreeTextAnnotation::Justification>(loader.readIntegerFromDictionary(dictionary, "Q", 0));
freeTextAnnotation->m_defaultStyleString = loader.readTextStringFromDictionary(dictionary, "DS", QString());
freeTextAnnotation->m_calloutLine = PDFAnnotationCalloutLine::parse(document, dictionary->get("CL"));
freeTextAnnotation->m_intent = loader.readEnumByName(dictionary->get("IT"), intents.begin(), intents.end(), PDFFreeTextAnnotation::Intent::None);
freeTextAnnotation->m_effect = PDFAnnotationBorderEffect::parse(document, dictionary->get("BE"));
std::vector<PDFReal> differenceRectangle = loader.readNumberArrayFromDictionary(dictionary, "RD");
if (differenceRectangle.size() == 4)
{
freeTextAnnotation->m_textRectangle = annotationsRectangle.adjusted(differenceRectangle[0], differenceRectangle[1], -differenceRectangle[2], -differenceRectangle[3]);
if (!freeTextAnnotation->m_textRectangle.isValid())
{
freeTextAnnotation->m_textRectangle = QRectF();
}
}
std::vector<QByteArray> lineEndings = loader.readNameArrayFromDictionary(dictionary, "LE");
if (lineEndings.size() == 2)
{
freeTextAnnotation->m_startLineEnding = convertNameToLineEnding(lineEndings[0]);
freeTextAnnotation->m_endLineEnding = convertNameToLineEnding(lineEndings[1]);
}
}
else if (subtype == "Line")
{
PDFLineAnnotation* lineAnnotation = new PDFLineAnnotation;
result.reset(lineAnnotation);
std::vector<PDFReal> line = loader.readNumberArrayFromDictionary(dictionary, "L");
if (line.size() == 4)
{
lineAnnotation->m_line = QLineF(line[0], line[1], line[2], line[3]);
}
std::vector<QByteArray> lineEndings = loader.readNameArrayFromDictionary(dictionary, "LE");
if (lineEndings.size() == 2)
{
lineAnnotation->m_startLineEnding = convertNameToLineEnding(lineEndings[0]);
lineAnnotation->m_endLineEnding = convertNameToLineEnding(lineEndings[1]);
}
lineAnnotation->m_interiorColor = loader.readNumberArrayFromDictionary(dictionary, "IC");
lineAnnotation->m_leaderLineLength = loader.readNumberFromDictionary(dictionary, "LL", 0.0);
lineAnnotation->m_leaderLineExtension = loader.readNumberFromDictionary(dictionary, "LLE", 0.0);
lineAnnotation->m_leaderLineOffset = loader.readNumberFromDictionary(dictionary, "LLO", 0.0);
lineAnnotation->m_captionRendered = loader.readBooleanFromDictionary(dictionary, "Cap", false);
lineAnnotation->m_intent = (loader.readNameFromDictionary(dictionary, "IT") == "LineDimension") ? PDFLineAnnotation::Intent::Dimension : PDFLineAnnotation::Intent::Arrow;
lineAnnotation->m_captionPosition = (loader.readNameFromDictionary(dictionary, "CP") == "Top") ? PDFLineAnnotation::CaptionPosition::Top : PDFLineAnnotation::CaptionPosition::Inline;
lineAnnotation->m_measureDictionary = document->getObject(dictionary->get("Measure"));
std::vector<PDFReal> captionOffset = loader.readNumberArrayFromDictionary(dictionary, "CO");
if (captionOffset.size() == 2)
{
lineAnnotation->m_captionOffset == QPointF(captionOffset[0], captionOffset[1]);
}
}
if (!result)
{
// Invalid annotation type
return result;
}
// Load common data for annotation
result->m_rectangle = annotationsRectangle;
result->m_contents = loader.readTextStringFromDictionary(dictionary, "Contents", QString());
result->m_pageReference = loader.readReferenceFromDictionary(dictionary, "P");
result->m_name = loader.readTextStringFromDictionary(dictionary, "NM", QString());
QByteArray string = loader.readStringFromDictionary(dictionary, "M");
result->m_lastModified = PDFEncoding::convertToDateTime(string);
if (!result->m_lastModified.isValid())
{
result->m_lastModifiedString = loader.readTextStringFromDictionary(dictionary, "M", QString());
}
result->m_flags = Flags(loader.readIntegerFromDictionary(dictionary, "F", 0));
result->m_appearanceStreams = PDFAppeareanceStreams::parse(document, dictionary->get("AP"));
result->m_appearanceState = loader.readNameFromDictionary(dictionary, "AS");
result->m_annotationBorder = PDFAnnotationBorder::parseBS(document, dictionary->get("BS"));
if (!result->m_annotationBorder.isValid())
{
result->m_annotationBorder = PDFAnnotationBorder::parseBorder(document, dictionary->get("Border"));
}
result->m_color = loader.readNumberArrayFromDictionary(dictionary, "C");
result->m_structParent = loader.readIntegerFromDictionary(dictionary, "StructParent", 0);
result->m_optionalContentReference = loader.readReferenceFromDictionary(dictionary, "OC");
if (PDFMarkupAnnotation* markupAnnotation = result->asMarkupAnnotation())
{
markupAnnotation->m_windowTitle = loader.readTextStringFromDictionary(dictionary, "T", QString());
markupAnnotation->m_popupAnnotation = loader.readReferenceFromDictionary(dictionary, "Popup");
markupAnnotation->m_opacity = loader.readNumberFromDictionary(dictionary, "CA", 1.0);
markupAnnotation->m_richTextString = loader.readTextStringFromDictionary(dictionary, "RC", QString());
markupAnnotation->m_creationDate = PDFEncoding::convertToDateTime(loader.readStringFromDictionary(dictionary, "CreationDate"));
markupAnnotation->m_inReplyTo = loader.readReferenceFromDictionary(dictionary, "IRT");
markupAnnotation->m_subject = loader.readTextStringFromDictionary(dictionary, "Subj", QString());
markupAnnotation->m_replyType = (loader.readNameFromDictionary(dictionary, "RT") == "Group") ? PDFMarkupAnnotation::ReplyType::Group : PDFMarkupAnnotation::ReplyType::Reply;
markupAnnotation->m_intent = loader.readNameFromDictionary(dictionary, "IT");
markupAnnotation->m_externalData = document->getObject(dictionary->get("ExData"));
}
return result;
}
PDFAnnotationQuadrilaterals PDFAnnotation::parseQuadrilaterals(const PDFDocument* document, PDFObject quadrilateralsObject, const QRectF annotationRect)
{
QPainterPath path;
std::vector<QLineF> underlines;
PDFDocumentDataLoaderDecorator loader(document);
std::vector<PDFReal> points = loader.readNumberArray(quadrilateralsObject);
const size_t quadrilateralCount = points.size() % 8;
path.reserve(int(quadrilateralCount) + 5);
underlines.reserve(quadrilateralCount);
for (size_t i = 0; i < quadrilateralCount; ++i)
{
const size_t offset = i * 8;
QPointF p1(points[offset + 0], points[offset + 1]);
QPointF p2(points[offset + 2], points[offset + 3]);
QPointF p3(points[offset + 4], points[offset + 5]);
QPointF p4(points[offset + 6], points[offset + 7]);
path.moveTo(p1);
path.lineTo(p2);
path.lineTo(p3);
path.lineTo(p4);
path.lineTo(p1);
path.closeSubpath();
underlines.emplace_back(p1, p2);
}
if (path.isEmpty() && annotationRect.isValid())
{
// Jakub Melka: we are using points at the top, because PDF has inverted y axis
// against the Qt's y axis.
path.addRect(annotationRect);
underlines.emplace_back(annotationRect.topLeft(), annotationRect.topRight());
}
return PDFAnnotationQuadrilaterals(qMove(path), qMove(underlines));
}
AnnotationLineEnding PDFAnnotation::convertNameToLineEnding(const QByteArray& name)
{
constexpr const std::array<std::pair<AnnotationLineEnding, const char*>, 10> lineEndings = {
std::pair<AnnotationLineEnding, const char*>{ AnnotationLineEnding::None, "None" },
std::pair<AnnotationLineEnding, const char*>{ AnnotationLineEnding::Square, "Square" },
std::pair<AnnotationLineEnding, const char*>{ AnnotationLineEnding::Circle, "Circle" },
std::pair<AnnotationLineEnding, const char*>{ AnnotationLineEnding::Diamond, "Diamond" },
std::pair<AnnotationLineEnding, const char*>{ AnnotationLineEnding::OpenArrow, "OpenArrow" },
std::pair<AnnotationLineEnding, const char*>{ AnnotationLineEnding::ClosedArrow, "ClosedArrow" },
std::pair<AnnotationLineEnding, const char*>{ AnnotationLineEnding::Butt, "Butt" },
std::pair<AnnotationLineEnding, const char*>{ AnnotationLineEnding::ROpenArrow, "ROpenArrow" },
std::pair<AnnotationLineEnding, const char*>{ AnnotationLineEnding::RClosedArrow, "RClosedArrow" },
std::pair<AnnotationLineEnding, const char*>{ AnnotationLineEnding::Slash, "Slash" }
};
auto it = std::find_if(lineEndings.cbegin(), lineEndings.cend(), [&name](const auto& item) { return name == item.second; });
if (it != lineEndings.cend())
{
return it->first;
}
return AnnotationLineEnding::None;
}
PDFAnnotationCalloutLine PDFAnnotationCalloutLine::parse(const PDFDocument* document, PDFObject object)
{
PDFDocumentDataLoaderDecorator loader(document);
std::vector<PDFReal> points = loader.readNumberArray(object);
switch (points.size())
{
case 4:
return PDFAnnotationCalloutLine(QPointF(points[0], points[1]), QPointF(points[2], points[3]));
case 6:
return PDFAnnotationCalloutLine(QPointF(points[0], points[1]), QPointF(points[2], points[3]), QPointF(points[4], points[5]));
default:
break;
}
return PDFAnnotationCalloutLine();
}
} // namespace pdf

View File

@ -20,6 +20,11 @@
#include "pdfglobal.h"
#include "pdfobject.h"
#include "pdfaction.h"
#include <QPainterPath>
#include <array>
namespace pdf
{
@ -55,6 +60,20 @@ enum class AnnotationType
_3D
};
enum class AnnotationLineEnding
{
None,
Square,
Circle,
Diamond,
OpenArrow,
ClosedArrow,
Butt,
ROpenArrow,
RClosedArrow,
Slash
};
/// Represents annotation's border. Two main definition exists, one is older,
/// called \p Simple, the other one is defined in BS dictionary of the annotation.
class PDFAnnotationBorder
@ -171,6 +190,76 @@ private:
std::map<Key, PDFObject> m_appearanceStreams;
};
/// Represents annotation's active region, it is used also to
/// determine underline lines.
class PDFAnnotationQuadrilaterals
{
public:
inline explicit PDFAnnotationQuadrilaterals() = default;
inline explicit PDFAnnotationQuadrilaterals(QPainterPath&& path, std::vector<QLineF>&& underLines) :
m_path(qMove(path)),
m_underLines(qMove(underLines))
{
}
const QPainterPath& getPath() const { return m_path; }
const std::vector<QLineF>& getUnderlines() const { return m_underLines; }
private:
QPainterPath m_path;
std::vector<QLineF> m_underLines;
};
/// Represents callout line (line from annotation to some point)
class PDFAnnotationCalloutLine
{
public:
enum class Type
{
Invalid,
StartEnd,
StartKneeEnd
};
inline explicit PDFAnnotationCalloutLine() = default;
inline explicit PDFAnnotationCalloutLine(QPointF start, QPointF end) :
m_type(Type::StartEnd),
m_points({start, end})
{
}
inline explicit PDFAnnotationCalloutLine(QPointF start, QPointF knee, QPointF end) :
m_type(Type::StartKneeEnd),
m_points({start, knee, end})
{
}
/// 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
static PDFAnnotationCalloutLine parse(const PDFDocument* document, PDFObject object);
bool isValid() const { return m_type != Type::Invalid; }
Type getType() const { return m_type; }
QPointF getPoint(int index) const { return m_points.at(index); }
private:
Type m_type = Type::Invalid;
std::array<QPointF, 3> m_points;
};
class PDFAnnotation;
class PDFMarkupAnnotation;
class PDFTextAnnotation;
using PDFAnnotationPtr = QSharedPointer<PDFAnnotation>;
/// Base class for all annotation types. Represents PDF annotation object.
/// Annotations are various enhancements to pages graphical representation,
/// such as graphics, text, highlight or multimedia content, such as sounds,
@ -195,9 +284,45 @@ public:
ToggleNoView = 0x0100, ///< If set, invert the interpretation of NoView flag
LockedContents = 0x0200, ///< Do not allow to modify contents of the annotation
};
Q_DECLARE_FLAGS(Flags, Flag)
virtual AnnotationType getType() const = 0;
virtual PDFMarkupAnnotation* asMarkupAnnotation() { return nullptr; }
virtual const PDFMarkupAnnotation* asMarkupAnnotation() const { return nullptr; }
const QRectF& getRectangle() const { return m_rectangle; }
const QString& getContents() const { return m_contents; }
PDFObjectReference getPageReference() const { return m_pageReference; }
const QString& getName() const { return m_name; }
const QDateTime& getLastModifiedDateTime() const { return m_lastModified; }
const QString& getLastModifiedString() const { return m_lastModifiedString; }
Flags getFlags() const { return m_flags; }
const PDFAppeareanceStreams& getAppearanceStreams() const { return m_appearanceStreams; }
const QByteArray& getAppearanceState() const { return m_appearanceState; }
const PDFAnnotationBorder& getBorder() const { return m_annotationBorder; }
const std::vector<PDFReal>& getColor() const { return m_color; }
PDFInteger getStructuralParent() const { return m_structParent; }
PDFObjectReference getOptionalContent() const { return m_optionalContentReference; }
/// Parses annotation from the object. If error occurs, then nullptr is returned.
/// \param document Document
/// \param object Annotation object
static PDFAnnotationPtr parse(const PDFDocument* document, PDFObject object);
/// Parses quadrilaterals and fills them in the painter path. If no quadrilaterals are defined,
/// then annotation rectangle is used. If annotation rectangle is also invalid,
/// then empty painter path is used.
/// \param document Document
/// \param quadrilateralsObject Object with quadrilaterals definition
/// \param annotationRect Annotation rectangle
static PDFAnnotationQuadrilaterals parseQuadrilaterals(const PDFDocument* document, PDFObject quadrilateralsObject, const QRectF annotationRect);
/// Converts name to line ending. If appropriate line ending for name is not found,
/// then None line ending is returned.
/// \param name Name of the line ending
static AnnotationLineEnding convertNameToLineEnding(const QByteArray& name);
private:
QRectF m_rectangle; ///< Annotation rectangle, in page coordinates, "Rect" entry
QString m_contents; ///< Text to be displayed to the user (or alternate text), "Content" entry
@ -205,6 +330,7 @@ private:
QString m_name; ///< Unique name (in page context) for the annotation, "NM" entry
QDateTime m_lastModified; ///< Date and time, when annotation was last modified, "M" entry
QString m_lastModifiedString; ///< Date and time, in text format
Flags m_flags; ///< Annotation flags
PDFAppeareanceStreams m_appearanceStreams; ///< Appearance streams, "AP" entry
QByteArray m_appearanceState; ///< Appearance state, "AS" entry
PDFAnnotationBorder m_annotationBorder; ///< Annotation border, "Border" entry
@ -213,6 +339,205 @@ private:
PDFObjectReference m_optionalContentReference; ///< Reference to optional content, "OC" entry
};
/// Markup annotation object, used to mark up contents of PDF documents. Markup annotations
/// can have various types, as free text (just text displayed on page), annotations with popup
/// windows, and special annotations, such as multimedia annotations.
class PDFMarkupAnnotation : public PDFAnnotation
{
public:
explicit inline PDFMarkupAnnotation() = default;
virtual PDFMarkupAnnotation* asMarkupAnnotation() override { return this; }
virtual const PDFMarkupAnnotation* asMarkupAnnotation() const override { return this; }
enum class ReplyType
{
Reply,
Group
};
const QString& getWindowTitle() const { return m_windowTitle; }
PDFObjectReference getPopupAnnotation() const { return m_popupAnnotation; }
PDFReal getOpacity() const { return m_opacity; }
const QString& getRichTextString() const { return m_richTextString; }
const QDateTime& getCreationDate() const { return m_creationDate; }
PDFObjectReference getInReplyTo() const { return m_inReplyTo; }
const QString& getSubject() const { return m_subject; }
ReplyType getReplyType() const { return m_replyType; }
const QByteArray& getIntent() const { return m_intent; }
const PDFObject& getExternalData() const { return m_externalData; }
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFDocument* document, PDFObject object);
QString m_windowTitle;
PDFObjectReference m_popupAnnotation;
PDFReal m_opacity = 1.0;
QString m_richTextString;
QDateTime m_creationDate;
PDFObjectReference m_inReplyTo;
QString m_subject;
ReplyType m_replyType = ReplyType::Reply;
QByteArray m_intent;
PDFObject m_externalData;
};
/// Text annotation represents note attached to a specific point in the PDF
/// document. It appears as icon, and it is not zoomed, or rotated (behaves
/// as if flag NoZoom and NoRotate were set). When this annotation is opened,
/// it displays popup window containing the text of the note, font and size
/// is implementation dependent by viewer application.
class PDFTextAnnotation : public PDFMarkupAnnotation
{
public:
inline explicit PDFTextAnnotation() = default;
virtual AnnotationType getType() const override { return AnnotationType::Text; }
bool isOpen() const { return m_open; }
const QByteArray& getIconName() const { return m_iconName; }
const QString& getState() const { return m_state; }
const QString& getStateModel() const { return m_stateModel; }
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFDocument* document, PDFObject object);
bool m_open = false;
QByteArray m_iconName;
QString m_state;
QString m_stateModel;
};
/// Link annotation represents hypertext link to a destination to elsewhere
/// in the document, or action to be performed.
class PDFLinkAnnotation : public PDFAnnotation
{
public:
inline explicit PDFLinkAnnotation() = default;
virtual AnnotationType getType() const override { return AnnotationType::Link; }
enum class HighlightMode
{
None,
Invert,
Outline,
Push
};
const PDFAction* getAction() const { return m_action.data(); }
HighlightMode getHighlightMode() const { return m_highlightMode; }
const PDFAction* getURIAction() const { return m_previousAction.data(); }
const PDFAnnotationQuadrilaterals& getActivationRegion() const { return m_activationRegion; }
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFDocument* document, PDFObject object);
PDFActionPtr m_action;
HighlightMode m_highlightMode = HighlightMode::Invert;
PDFActionPtr m_previousAction;
PDFAnnotationQuadrilaterals m_activationRegion;
};
/// Free text annotation displays text directly on the page. Free text doesn't have
/// open/close state, text is always visible.
class PDFFreeTextAnnotation : public PDFMarkupAnnotation
{
public:
inline explicit PDFFreeTextAnnotation() = default;
virtual AnnotationType getType() const override { return AnnotationType::FreeText; }
enum class Justification
{
Left,
Centered,
Right
};
enum class Intent
{
None,
Callout,
TypeWriter
};
const QByteArray& getDefaultAppearance() const { return m_defaultAppearance; }
Justification getJustification() const { return m_justification; }
const QString& getDefaultStyle() const { return m_defaultStyleString; }
const PDFAnnotationCalloutLine& getCalloutLine() const { return m_calloutLine; }
Intent getIntent() const { return m_intent; }
const QRectF& getTextRectangle() const { return m_textRectangle; }
const PDFAnnotationBorderEffect& getBorderEffect() const { return m_effect; }
AnnotationLineEnding getStartLineEnding() const { return m_startLineEnding; }
AnnotationLineEnding getEndLineEnding() const { return m_endLineEnding; }
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFDocument* document, PDFObject object);
QByteArray m_defaultAppearance;
Justification m_justification = Justification::Left;
QString m_defaultStyleString;
PDFAnnotationCalloutLine m_calloutLine;
Intent m_intent = Intent::None;
QRectF m_textRectangle;
PDFAnnotationBorderEffect m_effect;
AnnotationLineEnding m_startLineEnding = AnnotationLineEnding::None;
AnnotationLineEnding m_endLineEnding = AnnotationLineEnding::None;
};
/// Line annotation, draws straight line on the page (in most simple form), or
/// it can display, for example, dimensions with perpendicular lines at the line
/// endings. Caption text can also be displayed.
class PDFLineAnnotation : public PDFMarkupAnnotation
{
public:
inline explicit PDFLineAnnotation() = default;
virtual AnnotationType getType() const override { return AnnotationType::Line; }
enum class Intent
{
Arrow,
Dimension
};
enum class CaptionPosition
{
Inline,
Top
};
const QLineF& getLine() const { return m_line; }
AnnotationLineEnding getStartLineEnding() const { return m_startLineEnding; }
AnnotationLineEnding getEndLineEnding() const { return m_endLineEnding; }
const std::vector<PDFReal>& getInteriorColor() const { return m_interiorColor; }
PDFReal getLeaderLineLength() const { return m_leaderLineLength; }
PDFReal getLeaderLineExtension() const { return m_leaderLineExtension; }
PDFReal getLeaderLineOffset() const { return m_leaderLineOffset; }
bool isCaptionRendered() const { return m_captionRendered; }
Intent getIntent() const { return m_intent; }
CaptionPosition getCaptionPosition() const { return m_captionPosition; }
const PDFObject& getMeasureDictionary() const { return m_measureDictionary; }
const QPointF& getCaptionOffset() const { return m_captionOffset; }
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFDocument* document, PDFObject object);
QLineF m_line;
AnnotationLineEnding m_startLineEnding = AnnotationLineEnding::None;
AnnotationLineEnding m_endLineEnding = AnnotationLineEnding::None;
std::vector<PDFReal> m_interiorColor;
PDFReal m_leaderLineLength = 0.0;
PDFReal m_leaderLineExtension = 0.0;
PDFReal m_leaderLineOffset = 0.0;
bool m_captionRendered = false;
Intent m_intent = Intent::Arrow;
CaptionPosition m_captionPosition = CaptionPosition::Inline;
PDFObject m_measureDictionary;
QPointF m_captionOffset;
};
} // namespace pdf
#endif // PDFANNOTATION_H

View File

@ -481,6 +481,18 @@ std::vector<PDFInteger> PDFDocumentDataLoaderDecorator::readIntegerArray(const P
return std::vector<PDFInteger>();
}
PDFObjectReference PDFDocumentDataLoaderDecorator::readReferenceFromDictionary(const PDFDictionary* dictionary, const char* key) const
{
const PDFObject& object = dictionary->get(key);
if (object.isReference())
{
return object.getReference();
}
return PDFObjectReference();
}
std::vector<PDFObjectReference> PDFDocumentDataLoaderDecorator::readReferenceArray(const PDFObject& object) const
{
const PDFObject& dereferencedObject = m_document->getObject(object);

View File

@ -248,7 +248,12 @@ public:
/// \param object Object containing array of numbers
std::vector<PDFInteger> readIntegerArray(const PDFObject& object) const;
/// Reads reference array from dictionary. Reads all values. If error occurs,
/// Reads reference from dictionary. If error occurs, then invalid reference is returned.
/// \param dictionary Dictionary containing desired data
/// \param key Entry key
PDFObjectReference readReferenceFromDictionary(const PDFDictionary* dictionary, const char* key) const;
/// Reads reference array. Reads all values. If error occurs,
/// then empty array is returned.
/// \param object Object containing array of references
std::vector<PDFObjectReference> readReferenceArray(const PDFObject& object) const;