// Copyright (C) 2020 Jakub Melka
//
// This file is part of PdfForQt.
//
// PdfForQt is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// PdfForQt is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDFForQt. If not, see .
#ifndef PDFANNOTATION_H
#define PDFANNOTATION_H
#include "pdfutils.h"
#include "pdfobject.h"
#include "pdfaction.h"
#include "pdffile.h"
#include "pdfcms.h"
#include "pdfmultimedia.h"
#include "pdfmeshqualitysettings.h"
#include "pdfdocumentdrawinterface.h"
#include "pdfrenderer.h"
#include
#include
#include
class QKeyEvent;
class QMouseEvent;
class QWheelEvent;
namespace pdf
{
class PDFWidget;
class PDFObjectStorage;
class PDFDrawWidgetProxy;
class PDFFontCache;
class PDFFormManager;
class PDFModifiedDocument;
class PDFOptionalContentActivity;
class PDFFormFieldWidgetEditor;
using TextAlignment = Qt::Alignment;
using Polygons = std::vector;
enum class AnnotationType
{
Invalid,
Text,
Link,
FreeText,
Line,
Square,
Circle,
Polygon,
Polyline,
Highlight,
Underline,
Squiggly,
StrikeOut,
Stamp,
Caret,
Ink,
Popup,
FileAttachment,
Sound,
Movie,
Widget,
Screen,
PrinterMark,
TrapNet,
Watermark,
_3D,
RichMedia
};
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
{
public:
explicit inline PDFAnnotationBorder() = default;
enum class Definition
{
Invalid,
Simple,
BorderStyle
};
enum class Style
{
Solid,
Dashed,
Beveled,
Inset,
Underline
};
/// Parses the annotation border from the array. If object contains invalid annotation border,
/// then default annotation border is returned. If object is empty, empty annotation border is returned.
/// \param storage Object storage
/// \param object Border object
static PDFAnnotationBorder parseBorder(const PDFObjectStorage* storage, PDFObject object);
/// Parses the annotation border from the BS dictionary. If object contains invalid annotation border,
/// then default annotation border is returned. If object is empty, empty annotation border is returned.
/// \param storage Object storage
/// \param object Border object
static PDFAnnotationBorder parseBS(const PDFObjectStorage* storage, PDFObject object);
/// Returns true, if object is correctly defined
bool isValid() const { return m_definition != Definition::Invalid; }
Definition getDefinition() const { return m_definition; }
Style getStyle() const { return m_style; }
PDFReal getHorizontalCornerRadius() const { return m_hCornerRadius; }
PDFReal getVerticalCornerRadius() const { return m_vCornerRadius; }
PDFReal getWidth() const { return m_width; }
const std::vector& getDashPattern() const { return m_dashPattern; }
private:
Definition m_definition = Definition::Invalid;
Style m_style = Style::Solid;
PDFReal m_hCornerRadius = 0.0;
PDFReal m_vCornerRadius = 0.0;
PDFReal m_width = 1.0;
std::vector m_dashPattern;
};
using AnnotationBorderStyle = PDFAnnotationBorder::Style;
/// Annotation border effect
class PDFAnnotationBorderEffect
{
public:
explicit inline PDFAnnotationBorderEffect() = default;
enum class Effect
{
None,
Cloudy
};
/// Parses the annotation border effect from the object. If object contains invalid annotation border effect,
/// then default annotation border effect is returned. If object is empty, also default annotation border effect is returned.
/// \param storage Object storage
/// \param object Border effect object
static PDFAnnotationBorderEffect parse(const PDFObjectStorage* storage, PDFObject object);
private:
Effect m_effect = Effect::None;
PDFReal m_intensity = 0.0;
};
/// Storage which handles appearance streams of annotations. Appeareance streams are divided
/// to three main categories - normal, rollower and down. Each category can have different
/// states, for example, checkbox can have on/off state. This container can also resolve
/// queries to obtain appropriate appearance stream.
class PDFAppeareanceStreams
{
public:
explicit inline PDFAppeareanceStreams() = default;
enum class Appearance
{
Normal,
Rollover,
Down
};
using Key = std::pair;
/// Parses annotation appearance streams from the object. If object is invalid, then
/// empty appearance stream is constructed.
/// \param storage Object storage
/// \param object Appearance streams object
static PDFAppeareanceStreams parse(const PDFObjectStorage* storage, PDFObject object);
/// Tries to search for appearance stream for given appearance. If no appearance is found,
/// then null object is returned.
/// \param appearance Appearance type
PDFObject getAppearance(Appearance appearance = Appearance::Normal) const { return getAppearance(appearance, QByteArray()); }
/// Tries to resolve appearance stream for given appearance and state. If no appearance is found,
/// then null object is returned.
/// \param appearance Appearance type
/// \param state State name
PDFObject getAppearance(Appearance appearance, const QByteArray& state) const;
/// Returns list of appearance states for given appearance
/// \param appearance Appearance
QByteArrayList getAppearanceStates(Appearance appearance) const;
/// Returns list of appearance keys
std::vector getAppearanceKeys() const;
private:
std::map m_appearanceStreams;
};
/// Represents annotation's active region, it is used also to
/// determine underline lines.
class PDFAnnotationQuadrilaterals
{
public:
using Quadrilateral = std::array;
using Quadrilaterals = std::vector;
inline explicit PDFAnnotationQuadrilaterals() = default;
inline explicit PDFAnnotationQuadrilaterals(QPainterPath&& path, Quadrilaterals&& quadrilaterals) :
m_path(qMove(path)),
m_quadrilaterals(qMove(quadrilaterals))
{
}
const QPainterPath& getPath() const { return m_path; }
const Quadrilaterals& getQuadrilaterals() const { return m_quadrilaterals; }
bool isEmpty() const { return m_path.isEmpty(); }
private:
QPainterPath m_path;
Quadrilaterals m_quadrilaterals;
};
/// 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 storage Object storage
/// \param object Callout line object
static PDFAnnotationCalloutLine parse(const PDFObjectStorage* storage, 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 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 storage Object storage
/// \param object Appearance icon fit info object
static PDFAnnotationIconFitInfo parse(const PDFObjectStorage* storage, 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& getBorderColor() const { return m_borderColor; }
const std::vector& 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 storage Object storage
/// \param object Appearance characteristics object
static PDFAnnotationAppearanceCharacteristics parse(const PDFObjectStorage* storage, PDFObject object);
private:
PDFInteger m_rotation = 0;
std::vector m_borderColor;
std::vector 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,
Default,
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 storage Object storage
/// \param object Additional actions object
/// \param defaultAction Default action of object (defined in "A" entry of annotation dictionary)
static PDFAnnotationAdditionalActions parse(const PDFObjectStorage* storage, PDFObject object, PDFObject defaultAction);
private:
std::array m_actions;
};
/// Annotation default appearance
class PDFAnnotationDefaultAppearance
{
public:
explicit inline PDFAnnotationDefaultAppearance() = default;
/// Parses appearance string. If error occurs, then default appearance
/// string is constructed.
static PDFAnnotationDefaultAppearance parse(const QByteArray& string);
const QByteArray& getFontName() const { return m_fontName; }
PDFReal getFontSize() const { return m_fontSize; }
QColor getFontColor() const { return m_fontColor; }
private:
QByteArray m_fontName;
PDFReal m_fontSize = 8.0;
QColor m_fontColor = Qt::black;
};
class PDFAnnotation;
class PDFMarkupAnnotation;
class PDFTextAnnotation;
using PDFAnnotationPtr = QSharedPointer;
struct AnnotationDrawParameters
{
/// Painter, onto which is annotation graphics drawn
QPainter* painter = nullptr;
/// Pointer to annotation (if draw is delegated to other objects,
/// for example, form manager, then maybe pointer to annotation
/// is needed).
PDFAnnotation* annotation = nullptr;
/// Pointer to form manager (if forms are drawn)
const PDFFormManager* formManager = nullptr;
/// Output parameter. Marks annotation's graphics bounding
/// rectangle (it can be different/adjusted from original
/// annotation bounding rectangle, in that case, it must be adjusted).
/// If this rectangle is invalid, then appearance content stream
/// is assumed to be empty.
QRectF boundingRectangle;
/// Appeareance mode (normal/rollover/down, and appearance state)
PDFAppeareanceStreams::Key key;
/// Invert colors?
bool invertColors = false;
};
/// 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,
/// videos and 3D annotations.
class PDFAnnotation
{
public:
explicit PDFAnnotation();
virtual ~PDFAnnotation() = default;
enum Flag : uint
{
None = 0x0000,
Invisible = 0x0001, ///< If set, do not display unknown annotations using their AP dictionary
Hidden = 0x0002, ///< If set, do not display annotation and do not show popup windows (completely hidden)
Print = 0x0004, ///< If set, print annotation
NoZoom = 0x0008, ///< Do not apply page zoom while displaying annotation rectangle
NoRotate = 0x0010, ///< Do not rotate annotation's appearance to match orientation of the page
NoView = 0x0020, ///< Do not display annotation on the screen (it still can be printed)
ReadOnly = 0x0040, ///< Do not allow interacting with the user (and disallow also mouse interaction)
Locked = 0x0080, ///< Do not allow to delete/modify annotation by user
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; }
virtual bool isReplyTo() const { return false; }
/// Draws the annotation using parameters. Annotation is drawn onto painter,
/// but actual graphics can be drawn outside of annotation's rectangle.
/// In that case, adjusted annotation's rectangle is passed to the parameters.
/// Painter must use annotation's coordinate system (for example, line points
/// must match in both in painter and this annotation).
/// \param parameters Graphics parameters
virtual void draw(AnnotationDrawParameters& parameters) const;
/// Returns a list of appearance states, which must be created for this annotation
virtual std::vector getDrawKeys(const PDFFormManager* formManager) const;
/// Returns effective flags (some annotations can behave as they have always
/// set some flags, such as NoZoom and NoRotate)
virtual Flags getEffectiveFlags() const { return getFlags(); }
PDFObjectReference getSelfReference() const { return m_selfReference; }
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& 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 storage Object storage
/// \param reference Annotation object reference
static PDFAnnotationPtr parse(const PDFObjectStorage* storage, PDFObjectReference reference);
/// 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 storage Object storage
/// \param quadrilateralsObject Object with quadrilaterals definition
/// \param annotationRect Annotation rectangle
static PDFAnnotationQuadrilaterals parseQuadrilaterals(const PDFObjectStorage* storage, 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);
/// Returns draw color from defined annotation color
static QColor getDrawColorFromAnnotationColor(const std::vector& color);
protected:
virtual QColor getStrokeColor() const;
virtual QColor getFillColor() const;
struct LineGeometryInfo
{
/// Original line
QLineF originalLine;
/// Transformed line
QLineF transformedLine;
/// Matrix LCStoGCS is local coordinate system of line originalLine. It transforms
/// points on the line to the global coordinate system. So, point (0, 0) will
/// map onto p1 and point (originalLine.length(), 0) will map onto p2.
QMatrix LCStoGCS;
/// Inverted matrix of LCStoGCS. It maps global coordinate system to local
/// coordinate system of the original line.
QMatrix GCStoLCS;
static LineGeometryInfo create(QLineF line);
};
/// Returns pen from border settings and annotation color
QPen getPen() const;
/// Returns brush from interior color. If annotation doesn't have
/// a brush, then empty brush is returned.
QBrush getBrush() const;
/// Draw line using given parameters and painter. Line is specified
/// by its geometry information. Painter must be set to global coordinates.
/// Bounding path is also updated, it is specified in global coordinates,
/// not in line local coordinate system. We consider p1 as start point of
/// the line, and p2 as the end point. The painter must have proper QPen
/// and QBrush setted, this function uses current pen/brush to paint the line.
/// \param info Line geometry info
/// \param painter Painter
/// \param lineEndingSize Line ending size
/// \param p1Ending Line endpoint graphics at p1
/// \param p2Ending Line endpoint graphics at p2
/// \param boundingPath Bounding path in global coordinate system
/// \param textOffset Additional text offset
/// \param text Text, which should be printed along the line
/// \param textIsAboveLine Text should be printed above line
void drawLine(const LineGeometryInfo& info,
QPainter& painter,
PDFReal lineEndingSize,
AnnotationLineEnding p1Ending,
AnnotationLineEnding p2Ending,
QPainterPath& boundingPath,
QPointF textOffset,
const QString& text,
bool textIsAboveLine) const;
/// Draws character unicode symbol using text
/// \param text Text to be drawn
/// \param opacity Opacity
/// \param parameters Draw parameters
void drawCharacterSymbol(QString text, PDFReal opacity, AnnotationDrawParameters& parameters) const;
private:
PDFObjectReference m_selfReference; ///< Reference to self
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
PDFObjectReference m_pageReference; ///< Reference to annotation's page, "P" entry
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
std::vector m_color; ///< Color (for example, title bar of popup window), "C" entry
PDFInteger m_structParent; ///< Structural parent identifier, "StructParent" entry
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; }
virtual bool isReplyTo() const override;
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; }
protected:
virtual QColor getStrokeColor() const override;
virtual QColor getFillColor() const override;
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObjectReference reference);
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;
};
enum class TextAnnotationIcon
{
Comment,
Help,
Insert,
Key,
NewParagraph,
Note,
Paragraph
};
/// 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; }
virtual std::vector getDrawKeys(const PDFFormManager* formManager) const override;
virtual void draw(AnnotationDrawParameters& parameters) const override;
virtual Flags getEffectiveFlags() const override;
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 PDFObjectStorage* storage, PDFObjectReference reference);
bool m_open = false;
QByteArray m_iconName;
QString m_state;
QString m_stateModel;
};
enum class LinkHighlightMode
{
None,
Invert,
Outline,
Push
};
/// 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; }
virtual std::vector getDrawKeys(const PDFFormManager* formManager) const override;
virtual void draw(AnnotationDrawParameters& parameters) const override;
const PDFAction* getAction() const { return m_action.data(); }
LinkHighlightMode 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 PDFObjectStorage* storage, PDFObjectReference reference);
PDFActionPtr m_action;
LinkHighlightMode m_highlightMode = LinkHighlightMode::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; }
virtual void draw(AnnotationDrawParameters& parameters) const override;
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 PDFObjectStorage* storage, PDFObjectReference reference);
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; }
virtual void draw(AnnotationDrawParameters& parameters) const override;
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& 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; }
protected:
virtual QColor getFillColor() const override;
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObjectReference reference);
QLineF m_line;
AnnotationLineEnding m_startLineEnding = AnnotationLineEnding::None;
AnnotationLineEnding m_endLineEnding = AnnotationLineEnding::None;
std::vector 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;
};
/// 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; }
virtual void draw(AnnotationDrawParameters& parameters) const override;
const std::vector& getInteriorColor() const { return m_interiorColor; }
const PDFAnnotationBorderEffect& getBorderEffect() const { return m_effect; }
const QRectF& getGeometryRectangle() const { return m_geometryRectangle; }
protected:
virtual QColor getFillColor() const override;
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObjectReference reference);
AnnotationType m_type;
std::vector 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; }
virtual void draw(AnnotationDrawParameters& parameters) const override;
const std::vector& getVertices() const { return m_vertices; }
AnnotationLineEnding getStartLineEnding() const { return m_startLineEnding; }
AnnotationLineEnding getEndLineEnding() const { return m_endLineEnding; }
const std::vector& 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; }
protected:
virtual QColor getFillColor() const override;
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObjectReference reference);
AnnotationType m_type;
std::vector m_vertices;
AnnotationLineEnding m_startLineEnding = AnnotationLineEnding::None;
AnnotationLineEnding m_endLineEnding = AnnotationLineEnding::None;
std::vector 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; }
virtual void draw(AnnotationDrawParameters& parameters) const override;
const PDFAnnotationQuadrilaterals& getHiglightArea() const { return m_highlightArea; }
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObjectReference reference);
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; }
virtual void draw(AnnotationDrawParameters& parameters) const override;
const QRectF& getCaretRectangle() const { return m_caretRectangle; }
Symbol getSymbol() const { return m_symbol; }
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObjectReference reference);
QRectF m_caretRectangle;
Symbol m_symbol = Symbol::None;
};
enum class Stamp
{
Approved,
AsIs,
Confidential,
Departmental,
Draft,
Experimental,
Expired,
Final,
ForComment,
ForPublicRelease,
NotApproved,
NotForPublicRelease,
Sold,
TopSecret
};
/// 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;
virtual AnnotationType getType() const override { return AnnotationType::Stamp; }
virtual void draw(AnnotationDrawParameters& parameters) const override;
Stamp getStamp() const { return m_stamp; }
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObjectReference reference);
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; }
virtual void draw(AnnotationDrawParameters& parameters) const override;
const QPainterPath& getInkPath() const { return m_inkPath; }
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObjectReference reference);
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 PDFObjectStorage* storage, PDFObjectReference reference);
bool m_opened = false;
};
enum class FileAttachmentIcon
{
Graph,
Paperclip,
PushPin,
Tag
};
/// 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;
virtual AnnotationType getType() const override { return AnnotationType::FileAttachment; }
virtual void draw(AnnotationDrawParameters& parameters) const override;
const PDFFileSpecification& getFileSpecification() const { return m_fileSpecification; }
FileAttachmentIcon getIcon() const { return m_icon; }
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObjectReference reference);
PDFFileSpecification m_fileSpecification;
FileAttachmentIcon m_icon = FileAttachmentIcon::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; }
virtual void draw(AnnotationDrawParameters& parameters) const override;
const PDFSound& getSound() const { return m_sound; }
Icon getIcon() const { return m_icon; }
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObjectReference reference);
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 PDFObjectStorage* storage, PDFObjectReference reference);
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 PDFObjectStorage* storage, PDFObjectReference reference);
QString m_screenTitle;
PDFAnnotationAppearanceCharacteristics m_appearanceCharacteristics;
PDFActionPtr m_action;
PDFAnnotationAdditionalActions m_additionalActions;
};
/// Widget annotation represents form fields 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; }
virtual void draw(AnnotationDrawParameters& parameters) const override;
virtual std::vector getDrawKeys(const PDFFormManager* formManager) const override;
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 PDFObjectStorage* storage, PDFObjectReference reference);
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 PDFObjectStorage* storage, PDFObjectReference reference);
QMatrix m_matrix;
PDFReal m_relativeHorizontalOffset = 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& 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, PDFObjectReference reference);
PDF3DStream m_stream;
std::optional m_defaultView;
PDF3DActivation m_activation;
bool m_interactive = true;
QRectF m_viewBox;
};
/// Rich media annotations can be video, audio, or other multimedia presentations.
/// The application should provide additional functionality to control rich media,
/// such as buttons to play/pause/stop video etc. This annotation consists
/// of contents and settings, settings are optional.
class PDFRichMediaAnnotation : public PDFAnnotation
{
public:
inline explicit PDFRichMediaAnnotation() = default;
virtual AnnotationType getType() const override { return AnnotationType::RichMedia; }
const PDFRichMediaContent* getContent() const { return &m_content; }
const PDFRichMediaSettings* getSettings() const { return &m_settings; }
private:
friend static PDFAnnotationPtr PDFAnnotation::parse(const PDFObjectStorage* storage, PDFObjectReference reference);
PDFRichMediaContent m_content;
PDFRichMediaSettings m_settings;
};
/// Annotation manager manages annotations for document's pages. Each page
/// can have multiple annotations, and this object caches them. Also,
/// this object builds annotation's appearance streams, if necessary. This
/// manager is intended to non-gui rendering. If widget annotation manager is used,
/// then this object is not thread safe.
class PDFFORQTLIBSHARED_EXPORT PDFAnnotationManager : public QObject, public IDocumentDrawInterface
{
Q_OBJECT
private:
using BaseClass = QObject;
void drawWidgetAnnotationHighlight(QRectF annotationRectangle, PDFObjectReference annotationReference, QPainter* painter, QMatrix userSpaceToDeviceSpace) const;
public:
enum class Target
{
View,
Print
};
explicit PDFAnnotationManager(PDFFontCache* fontCache,
const PDFCMSManager* cmsManager,
const PDFOptionalContentActivity* optionalActivity,
PDFMeshQualitySettings meshQualitySettings,
PDFRenderer::Features features,
Target target,
QObject* parent);
virtual ~PDFAnnotationManager() override;
virtual void drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QMatrix& pagePointToDevicePointMatrix,
QList& errors) const override;
/// Set document
/// \param document New document
void setDocument(const PDFModifiedDocument& document);
Target getTarget() const;
void setTarget(Target target);
const PDFOptionalContentActivity* getOptionalActivity() const;
void setOptionalActivity(const PDFOptionalContentActivity* optionalActivity);
PDFFontCache* getFontCache() const;
void setFontCache(PDFFontCache* fontCache);
PDFMeshQualitySettings getMeshQualitySettings() const;
void setMeshQualitySettings(const PDFMeshQualitySettings& meshQualitySettings);
PDFRenderer::Features getFeatures() const;
void setFeatures(PDFRenderer::Features features);
PDFFormManager* getFormManager() const;
void setFormManager(PDFFormManager* formManager);
struct PageAnnotation
{
PDFAppeareanceStreams::Appearance appearance = PDFAppeareanceStreams::Appearance::Normal;
PDFAnnotationPtr annotation;
/// This mutable appearance stream is protected by main mutex
mutable PDFCachedItem appearanceStream;
};
struct PageAnnotations
{
bool isEmpty() const { return annotations.empty(); }
/// Returns popup annotation for given annotation, if annotation doesn't have
/// popup annotation attached, then nullptr is returned.
/// \param pageAnnotation Page annotation
/// \returns Popup annotation or nullptr
const PageAnnotation* getPopupAnnotation(const PageAnnotation& pageAnnotation) const;
/// Returns a list of page annotations, which are replies to this page annotation.
/// If page annotation doesn't have any replies, then empty list is returned. Replies
/// are returned in order, in which they were created.
/// \param pageAnnotation Page annotation
/// \returns List of replies
std::vector getReplies(const PageAnnotation& pageAnnotation) const;
std::vector annotations;
};
/// Prepares annotation transformations for rendering
/// \param pagePointToDevicePointMatrix Page point to device point matrix
/// \param device Paint device, onto which will be annotation rendered
/// \param annotationFlags Annotation flags
/// \param page Page
/// \param[in,out] annotationRectangle Input/output annotation rectangle
QMatrix prepareTransformations(const QMatrix& pagePointToDevicePointMatrix,
QPaintDevice* device,
const PDFAnnotation::Flags annotationFlags,
const PDFPage* page,
QRectF& annotationRectangle) const;
/// Returns current appearance stream for given page annotation
/// \param pageAnnotation Page annotation
PDFObject getAppearanceStream(const PageAnnotation& pageAnnotation) const;
/// Returns constant reference to page annotation for given page index.
/// This function requires, that pointer to m_document is valid.
/// \param pageIndex Page index (must point to valid page)
const PageAnnotations& getPageAnnotations(PDFInteger pageIndex) const;
/// Returns reference to page annotation for given page index.
/// This function requires, that pointer to m_document is valid.
/// \param pageIndex Page index (must point to valid page)
PageAnnotations& getPageAnnotations(PDFInteger pageIndex);
/// Returns true, if given page has any annotation
bool hasAnnotation(PDFInteger pageIndex) const;
/// Returns true, if any page in the given indices has annotation
bool hasAnyPageAnnotation(const std::vector& pageIndices) const;
protected:
void drawWidgetAnnotationHighlight(QRectF annotationRectangle,
const PDFAnnotation* annotation,
QPainter* painter,
QMatrix userSpaceToDeviceSpace) const;
const PDFDocument* m_document;
PDFFontCache* m_fontCache;
const PDFCMSManager* m_cmsManager;
const PDFOptionalContentActivity* m_optionalActivity;
PDFFormManager* m_formManager;
PDFMeshQualitySettings m_meshQualitySettings;
PDFRenderer::Features m_features;
mutable QMutex m_mutex;
mutable std::map m_pageAnnotations;
Target m_target = Target::View;
};
/// Annotation manager for GUI rendering, it also manages annotations widgets
/// for parent widget.
class PDFFORQTLIBSHARED_EXPORT PDFWidgetAnnotationManager : public PDFAnnotationManager, public IDrawWidgetInputInterface
{
Q_OBJECT
private:
using BaseClass = PDFAnnotationManager;
public:
explicit PDFWidgetAnnotationManager(PDFDrawWidgetProxy* proxy, QObject* parent);
virtual ~PDFWidgetAnnotationManager() override;
virtual void shortcutOverrideEvent(QWidget* widget, QKeyEvent* event) override;
virtual void keyPressEvent(QWidget* widget, QKeyEvent* event) override;
virtual void keyReleaseEvent(QWidget* widget, QKeyEvent* event) override;
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseDoubleClickEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event) override;
virtual void wheelEvent(QWidget* widget, QWheelEvent* event) override;
/// Returns tooltip generated from annotation
virtual QString getTooltip() const override { return m_tooltip; }
/// Returns current cursor
virtual const std::optional& getCursor() const override { return m_cursor; }
virtual int getInputPriority() const override { return AnnotationPriority; }
signals:
void actionTriggered(const PDFAction* action);
private:
void updateFromMouseEvent(QMouseEvent* event);
/// Creates dialog for markup annotations. This function is used only for markup annotations,
/// do not use them for other annotations (function can crash).
/// \param widget Dialog's parent widget
/// \param pageAnnotation Markup annotation
/// \param pageAnnotations Page annotations
QDialog* createDialogForMarkupAnnotations(PDFWidget* widget,
const PageAnnotation& pageAnnotation,
const PageAnnotations& pageAnnotations);
/// Creates widgets for markup annotation main popup widget. Also sets
/// default size of parent widget.
/// \param parentWidget Parent widget, where widgets are created
/// \param pageAnnotation Markup annotation
/// \param pageAnnotations Page annotations
void createWidgetsForMarkupAnnotations(QWidget* parentWidget,
const PageAnnotation& pageAnnotation,
const PageAnnotations& pageAnnotations);
PDFDrawWidgetProxy* m_proxy;
QString m_tooltip;
std::optional m_cursor;
};
} // namespace pdf
#endif // PDFANNOTATION_H