//========================================================================
//
// Annot.h
//
// Copyright 2000-2003 Glyph & Cog, LLC
//
//========================================================================

//========================================================================
//
// Modified under the Poppler project - http://poppler.freedesktop.org
//
// All changes made under the Poppler project to this file are licensed
// under GPL version 2 or later
//
// Copyright (C) 2006 Scott Turner <scotty1024@mac.com>
// Copyright (C) 2007, 2008 Julien Rebetez <julienr@svn.gnome.org>
// Copyright (C) 2007-2011, 2013, 2015, 2018 Carlos Garcia Campos <carlosgc@gnome.org>
// Copyright (C) 2007, 2008 Iñigo Martínez <inigomartinez@gmail.com>
// Copyright (C) 2008 Michael Vrable <mvrable@cs.ucsd.edu>
// Copyright (C) 2008 Hugo Mercier <hmercier31@gmail.com>
// Copyright (C) 2008 Pino Toscano <pino@kde.org>
// Copyright (C) 2008 Tomas Are Haavet <tomasare@gmail.com>
// Copyright (C) 2009-2011, 2013, 2016-2020 Albert Astals Cid <aacid@kde.org>
// Copyright (C) 2012, 2013 Fabio D'Urso <fabiodurso@hotmail.it>
// Copyright (C) 2012, 2015 Tobias Koenig <tokoe@kdab.com>
// Copyright (C) 2013 Thomas Freitag <Thomas.Freitag@alfa.de>
// Copyright (C) 2013, 2017 Adrian Johnson <ajohnson@redneon.com>
// Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by the LiMux project of the city of Munich
// Copyright (C) 2018 Dileep Sankhla <sankhla.dileep96@gmail.com>
// Copyright (C) 2018-2020 Tobias Deiminger <haxtibal@posteo.de>
// Copyright (C) 2018, 2020 Oliver Sander <oliver.sander@tu-dresden.de>
// Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de>
// Copyright (C) 2019 Umang Malik <umang99m@gmail.com>
// Copyright (C) 2019 João Netto <joaonetto901@gmail.com>
// Copyright (C) 2020 Nelson Benítez León <nbenitezl@gmail.com>
// Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by Technische Universität Dresden
//
// To see a description of the changes please see the Changelog file that
// came with your tarball or type make ChangeLog if you are building from git
//
//========================================================================

#ifndef ANNOT_H
#define ANNOT_H

#include <memory>
#include <atomic>
#include <mutex>
#include <vector>

#include "Object.h"

class XRef;
class Gfx;
class CharCodeToUnicode;
class GfxFont;
class GfxResources;
class Page;
class PDFDoc;
class Form;
class FormWidget;
class FormField;
class FormFieldButton;
class FormFieldText;
class FormFieldChoice;
class PDFRectangle;
class Movie;
class LinkAction;
class Sound;
class FileSpec;

enum AnnotLineEndingStyle
{
    annotLineEndingSquare, // Square
    annotLineEndingCircle, // Circle
    annotLineEndingDiamond, // Diamond
    annotLineEndingOpenArrow, // OpenArrow
    annotLineEndingClosedArrow, // ClosedArrow
    annotLineEndingNone, // None
    annotLineEndingButt, // Butt
    annotLineEndingROpenArrow, // ROpenArrow
    annotLineEndingRClosedArrow, // RClosedArrow
    annotLineEndingSlash // Slash
};

enum AnnotExternalDataType
{
    annotExternalDataMarkupUnknown,
    annotExternalDataMarkup3D // Markup3D
};

//------------------------------------------------------------------------
// AnnotCoord
//------------------------------------------------------------------------

class AnnotCoord
{
public:
    AnnotCoord() : x(0), y(0) { }
    AnnotCoord(double _x, double _y) : x(_x), y(_y) { }

    double getX() const { return x; }
    double getY() const { return y; }

protected:
    double x, y;
};

//------------------------------------------------------------------------
// AnnotPath
//------------------------------------------------------------------------

class AnnotPath
{
public:
    AnnotPath();
    AnnotPath(Array *array);
    AnnotPath(std::vector<AnnotCoord> &&coords);
    ~AnnotPath();

    AnnotPath(const AnnotPath &) = delete;
    AnnotPath &operator=(const AnnotPath &other) = delete;

    double getX(int coord) const;
    double getY(int coord) const;
    AnnotCoord *getCoord(int coord);
    int getCoordsLength() const { return coords.size(); }

protected:
    std::vector<AnnotCoord> coords;

    void parsePathArray(Array *array);
};

//------------------------------------------------------------------------
// AnnotCalloutLine
//------------------------------------------------------------------------

class AnnotCalloutLine
{
public:
    AnnotCalloutLine(double x1, double y1, double x2, double y2);
    virtual ~AnnotCalloutLine();

    AnnotCalloutLine(const AnnotCalloutLine &) = delete;
    AnnotCalloutLine &operator=(const AnnotCalloutLine &other) = delete;

    double getX1() const { return coord1.getX(); }
    double getY1() const { return coord1.getY(); }
    double getX2() const { return coord2.getX(); }
    double getY2() const { return coord2.getY(); }

protected:
    AnnotCoord coord1, coord2;
};

//------------------------------------------------------------------------
// AnnotCalloutMultiLine
//------------------------------------------------------------------------

class AnnotCalloutMultiLine : public AnnotCalloutLine
{
public:
    AnnotCalloutMultiLine(double x1, double y1, double x2, double y2, double x3, double y3);
    ~AnnotCalloutMultiLine() override;

    double getX3() const { return coord3.getX(); }
    double getY3() const { return coord3.getY(); }

protected:
    AnnotCoord coord3;
};

//------------------------------------------------------------------------
// AnnotBorderEffect
//------------------------------------------------------------------------

class AnnotBorderEffect
{
public:
    enum AnnotBorderEffectType
    {
        borderEffectNoEffect, // S
        borderEffectCloudy // C
    };

    AnnotBorderEffect(Dict *dict);

    AnnotBorderEffectType getEffectType() const { return effectType; }
    double getIntensity() const { return intensity; }

private:
    AnnotBorderEffectType effectType; // S  (Default S)
    double intensity; // I  (Default 0)
};

//------------------------------------------------------------------------
// AnnotQuadrilateral
//------------------------------------------------------------------------

class AnnotQuadrilaterals
{
public:
    class AnnotQuadrilateral
    {
    public:
        AnnotQuadrilateral();
        AnnotQuadrilateral(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4);

        AnnotCoord coord1, coord2, coord3, coord4;
    };

    AnnotQuadrilaterals(Array *array, PDFRectangle *rect);
    AnnotQuadrilaterals(std::unique_ptr<AnnotQuadrilateral[]> &&quads, int quadsLength);
    ~AnnotQuadrilaterals();

    AnnotQuadrilaterals(const AnnotQuadrilaterals &) = delete;
    AnnotQuadrilaterals &operator=(const AnnotQuadrilaterals &other) = delete;

    double getX1(int quadrilateral);
    double getY1(int quadrilateral);
    double getX2(int quadrilateral);
    double getY2(int quadrilateral);
    double getX3(int quadrilateral);
    double getY3(int quadrilateral);
    double getX4(int quadrilateral);
    double getY4(int quadrilateral);
    int getQuadrilateralsLength() const { return quadrilateralsLength; }

protected:
    std::unique_ptr<AnnotQuadrilateral[]> quadrilaterals;
    int quadrilateralsLength;
};

//------------------------------------------------------------------------
// AnnotBorder
//------------------------------------------------------------------------

class AnnotBorder
{
public:
    enum AnnotBorderType
    {
        typeArray,
        typeBS
    };

    enum AnnotBorderStyle
    {
        borderSolid, // Solid
        borderDashed, // Dashed
        borderBeveled, // Beveled
        borderInset, // Inset
        borderUnderlined // Underlined
    };

    virtual ~AnnotBorder();

    AnnotBorder(const AnnotBorder &) = delete;
    AnnotBorder &operator=(const AnnotBorder &other) = delete;

    virtual void setWidth(double new_width) { width = new_width; }

    virtual AnnotBorderType getType() const = 0;
    virtual double getWidth() const { return width; }
    virtual int getDashLength() const { return dashLength; }
    virtual double *getDash() const { return dash; }
    virtual AnnotBorderStyle getStyle() const { return style; }

    virtual Object writeToObject(XRef *xref) const = 0;

protected:
    AnnotBorder();

    bool parseDashArray(Object *dashObj);

    AnnotBorderType type;
    double width;
    static const int DASH_LIMIT = 10; // implementation note 82 in Appendix H.
    int dashLength;
    double *dash;
    AnnotBorderStyle style;
};

//------------------------------------------------------------------------
// AnnotBorderArray
//------------------------------------------------------------------------

class AnnotBorderArray : public AnnotBorder
{
public:
    AnnotBorderArray();
    AnnotBorderArray(Array *array);

    void setHorizontalCorner(double hc) { horizontalCorner = hc; }
    void setVerticalCorner(double vc) { verticalCorner = vc; }

    double getHorizontalCorner() const { return horizontalCorner; }
    double getVerticalCorner() const { return verticalCorner; }

private:
    AnnotBorderType getType() const override { return typeArray; }
    Object writeToObject(XRef *xref) const override;

    double horizontalCorner; // (Default 0)
    double verticalCorner; // (Default 0)
    // double width;                  // (Default 1)  (inherited from AnnotBorder)
};

//------------------------------------------------------------------------
// AnnotBorderBS
//------------------------------------------------------------------------

class AnnotBorderBS : public AnnotBorder
{
public:
    AnnotBorderBS();
    AnnotBorderBS(Dict *dict);

private:
    AnnotBorderType getType() const override { return typeBS; }
    Object writeToObject(XRef *xref) const override;

    const char *getStyleName() const;

    // double width;           // W  (Default 1)   (inherited from AnnotBorder)
    // AnnotBorderStyle style; // S  (Default S)   (inherited from AnnotBorder)
    // double *dash;           // D  (Default [3]) (inherited from AnnotBorder)
};

//------------------------------------------------------------------------
// AnnotColor
//------------------------------------------------------------------------

class AnnotColor
{
public:
    enum AnnotColorSpace
    {
        colorTransparent = 0,
        colorGray = 1,
        colorRGB = 3,
        colorCMYK = 4
    };

    AnnotColor();
    AnnotColor(double gray);
    AnnotColor(double r, double g, double b);
    AnnotColor(double c, double m, double y, double k);
    AnnotColor(Array *array, int adjust = 0);

    void adjustColor(int adjust);

    AnnotColorSpace getSpace() const { return (AnnotColorSpace)length; }
    const double *getValues() const { return values; }

    Object writeToObject(XRef *xref) const;

private:
    double values[4];
    int length;
};

//------------------------------------------------------------------------
// DefaultAppearance
//------------------------------------------------------------------------

class DefaultAppearance
{
public:
    DefaultAppearance(Object &&fontNameA, double fontPtSizeA, std::unique_ptr<AnnotColor> fontColorA);
    DefaultAppearance(GooString *da);
    void setFontName(Object &&fontNameA);
    const Object &getFontName() const { return fontName; }
    void setFontPtSize(double fontPtSizeA);
    double getFontPtSize() const { return fontPtSize; }
    void setFontColor(std::unique_ptr<AnnotColor> fontColorA);
    const AnnotColor *getFontColor() const { return fontColor.get(); }
    GooString *toAppearanceString() const;

    DefaultAppearance(const DefaultAppearance &) = delete;
    DefaultAppearance &operator=(const DefaultAppearance &) = delete;

private:
    Object fontName;
    double fontPtSize;
    std::unique_ptr<AnnotColor> fontColor;
};

//------------------------------------------------------------------------
// AnnotIconFit
//------------------------------------------------------------------------

class AnnotIconFit
{
public:
    enum AnnotIconFitScaleWhen
    {
        scaleAlways, // A
        scaleBigger, // B
        scaleSmaller, // S
        scaleNever // N
    };

    enum AnnotIconFitScale
    {
        scaleAnamorphic, // A
        scaleProportional // P
    };

    AnnotIconFit(Dict *dict);

    AnnotIconFitScaleWhen getScaleWhen() { return scaleWhen; }
    AnnotIconFitScale getScale() { return scale; }
    double getLeft() { return left; }
    double getBottom() { return bottom; }
    bool getFullyBounds() { return fullyBounds; }

protected:
    AnnotIconFitScaleWhen scaleWhen; // SW (Default A)
    AnnotIconFitScale scale; // S  (Default P)
    double left; // A  (Default [0.5 0.5]
    double bottom; // Only if scale is P
    bool fullyBounds; // FB (Default false)
};

//------------------------------------------------------------------------
// AnnotAppearance
//------------------------------------------------------------------------

class AnnotAppearance
{
public:
    enum AnnotAppearanceType
    {
        appearNormal,
        appearRollover,
        appearDown
    };

    AnnotAppearance(PDFDoc *docA, Object *dict);
    ~AnnotAppearance();

    // State is ignored if no subdictionary is present
    Object getAppearanceStream(AnnotAppearanceType type, const char *state);

    // Access keys in normal appearance subdictionary (N)
    std::unique_ptr<GooString> getStateKey(int i);
    int getNumStates();

    // Removes all associated streams in the xref table. Caller is required to
    // reset parent annotation's AP and AS after this call.
    void removeAllStreams();

    // Test if this AnnotAppearance references the specified stream
    bool referencesStream(Ref refToStream);

private:
    static bool referencesStream(const Object *stateObj, Ref refToStream);
    void removeStream(Ref refToStream);
    void removeStateStreams(const Object *state);

protected:
    PDFDoc *doc;
    Object appearDict; // Annotation's AP
};

//------------------------------------------------------------------------
// AnnotAppearanceCharacs
//------------------------------------------------------------------------

class AnnotAppearanceCharacs
{
public:
    enum AnnotAppearanceCharacsTextPos
    {
        captionNoIcon, // 0
        captionNoCaption, // 1
        captionBelow, // 2
        captionAbove, // 3
        captionRight, // 4
        captionLeft, // 5
        captionOverlaid // 6
    };

    AnnotAppearanceCharacs(Dict *dict);
    ~AnnotAppearanceCharacs();

    AnnotAppearanceCharacs(const AnnotAppearanceCharacs &) = delete;
    AnnotAppearanceCharacs &operator=(const AnnotAppearanceCharacs &) = delete;

    int getRotation() const { return rotation; }
    const AnnotColor *getBorderColor() const { return borderColor.get(); }
    const AnnotColor *getBackColor() const { return backColor.get(); }
    const GooString *getNormalCaption() const { return normalCaption.get(); }
    const GooString *getRolloverCaption() { return rolloverCaption.get(); }
    const GooString *getAlternateCaption() { return alternateCaption.get(); }
    const AnnotIconFit *getIconFit() { return iconFit.get(); }
    AnnotAppearanceCharacsTextPos getPosition() const { return position; }

protected:
    int rotation; // R  (Default 0)
    std::unique_ptr<AnnotColor> borderColor; // BC
    std::unique_ptr<AnnotColor> backColor; // BG
    std::unique_ptr<GooString> normalCaption; // CA
    std::unique_ptr<GooString> rolloverCaption; // RC
    std::unique_ptr<GooString> alternateCaption; // AC
    // I
    // RI
    // IX
    std::unique_ptr<AnnotIconFit> iconFit; // IF
    AnnotAppearanceCharacsTextPos position; // TP (Default 0)
};

//------------------------------------------------------------------------
// AnnotAppearanceBBox
//------------------------------------------------------------------------

class AnnotAppearanceBBox
{
public:
    AnnotAppearanceBBox(PDFRectangle *rect);

    void setBorderWidth(double w) { borderWidth = w; }

    // The following functions operate on coords relative to [origX origY]
    void extendTo(double x, double y);
    void getBBoxRect(double bbox[4]) const;

    // Get boundaries in page coordinates
    double getPageXMin() const;
    double getPageYMin() const;
    double getPageXMax() const;
    double getPageYMax() const;

private:
    double origX, origY, borderWidth;
    double minX, minY, maxX, maxY;
};

//------------------------------------------------------------------------
// AnnotAppearanceBuilder
//------------------------------------------------------------------------
class Matrix;
class AnnotAppearanceBuilder
{
public:
    AnnotAppearanceBuilder();
    ~AnnotAppearanceBuilder();

    AnnotAppearanceBuilder(const AnnotAppearanceBuilder &) = delete;
    AnnotAppearanceBuilder &operator=(const AnnotAppearanceBuilder &) = delete;

    void setDrawColor(const AnnotColor *color, bool fill);
    void setLineStyleForBorder(const AnnotBorder *border);
    void setTextFont(const Object &fontName, double fontSize);
    void drawCircle(double cx, double cy, double r, bool fill);
    void drawEllipse(double cx, double cy, double rx, double ry, bool fill, bool stroke);
    void drawCircleTopLeft(double cx, double cy, double r);
    void drawCircleBottomRight(double cx, double cy, double r);
    void drawLineEnding(AnnotLineEndingStyle endingStyle, double x, double y, double size, bool fill, const Matrix &m);
    void drawLineEndSquare(double x, double y, double size, bool fill, const Matrix &m);
    void drawLineEndCircle(double x, double y, double size, bool fill, const Matrix &m);
    void drawLineEndDiamond(double x, double y, double size, bool fill, const Matrix &m);
    void drawLineEndArrow(double x, double y, double size, int orientation, bool isOpen, bool fill, const Matrix &m);
    void drawLineEndSlash(double x, double y, double size, const Matrix &m);
    void drawFieldBorder(const FormField *field, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect);
    bool drawFormField(const FormField *field, const Form *form, const GfxResources *resources, const GooString *da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect,
                       const GooString *appearState, XRef *xref, bool *addedDingbatsResource);
    static double lineEndingXShorten(AnnotLineEndingStyle endingStyle, double size);
    static double lineEndingXExtendBBox(AnnotLineEndingStyle endingStyle, double size);
    void writeString(const GooString &str);

    void append(const char *text);
    void appendf(const char *fmt, ...) GOOSTRING_FORMAT;

    const GooString *buffer() const;

private:
    bool drawListBox(const FormFieldChoice *fieldChoice, const AnnotBorder *border, const PDFRectangle *rect, const GooString *da, const GfxResources *resources, int quadding);
    bool drawFormFieldButton(const FormFieldButton *field, const GfxResources *resources, const GooString *da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect, const GooString *appearState,
                             XRef *xref, bool *addedDingbatsResource);
    bool drawFormFieldText(const FormFieldText *fieldText, const Form *form, const GfxResources *resources, const GooString *da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect);
    bool drawFormFieldChoice(const FormFieldChoice *fieldChoice, const Form *form, const GfxResources *resources, const GooString *da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect);
    bool drawText(const GooString *text, const GooString *da, const GfxResources *resources, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect, bool multiline, int comb, int quadding,
                  bool txField, bool forceZapfDingbats, XRef *xref, bool *addedDingbatsResource, // xref and addedDingbatsResource both must not be null if forceZapfDingbats is passed
                  bool password);
    void drawArrowPath(double x, double y, const Matrix &m, int orientation = 1);

    GooString *appearBuf;
};

//------------------------------------------------------------------------
// Annot
//------------------------------------------------------------------------

class Annot
{
    friend class Annots;
    friend class Page;

public:
    enum AnnotFlag
    {
        flagUnknown = 0x0000,
        flagInvisible = 0x0001,
        flagHidden = 0x0002,
        flagPrint = 0x0004,
        flagNoZoom = 0x0008,
        flagNoRotate = 0x0010,
        flagNoView = 0x0020,
        flagReadOnly = 0x0040,
        flagLocked = 0x0080,
        flagToggleNoView = 0x0100,
        flagLockedContents = 0x0200
    };

    enum AnnotSubtype
    {
        typeUnknown, //                 0
        typeText, // Text            1
        typeLink, // Link            2
        typeFreeText, // FreeText        3
        typeLine, // Line            4
        typeSquare, // Square          5
        typeCircle, // Circle          6
        typePolygon, // Polygon         7
        typePolyLine, // PolyLine        8
        typeHighlight, // Highlight       9
        typeUnderline, // Underline      10
        typeSquiggly, // Squiggly       11
        typeStrikeOut, // StrikeOut      12
        typeStamp, // Stamp          13
        typeCaret, // Caret          14
        typeInk, // Ink            15
        typePopup, // Popup          16
        typeFileAttachment, // FileAttachment 17
        typeSound, // Sound          18
        typeMovie, // Movie          19
        typeWidget, // Widget         20
        typeScreen, // Screen         21
        typePrinterMark, // PrinterMark    22
        typeTrapNet, // TrapNet        23
        typeWatermark, // Watermark      24
        type3D, // 3D             25
        typeRichMedia // RichMedia      26
    };

    /**
     * Describes the additional actions of a screen or widget annotation.
     */
    enum AdditionalActionsType
    {
        actionCursorEntering, ///< Performed when the cursor enters the annotation's active area
        actionCursorLeaving, ///< Performed when the cursor exists the annotation's active area
        actionMousePressed, ///< Performed when the mouse button is pressed inside the annotation's active area
        actionMouseReleased, ///< Performed when the mouse button is released inside the annotation's active area
        actionFocusIn, ///< Performed when the annotation receives the input focus
        actionFocusOut, ///< Performed when the annotation loses the input focus
        actionPageOpening, ///< Performed when the page containing the annotation is opened
        actionPageClosing, ///< Performed when the page containing the annotation is closed
        actionPageVisible, ///< Performed when the page containing the annotation becomes visible
        actionPageInvisible ///< Performed when the page containing the annotation becomes invisible
    };

    enum FormAdditionalActionsType
    {
        actionFieldModified, ///< Performed when the when the user modifies the field
        actionFormatField, ///< Performed before the field is formatted to display its value
        actionValidateField, ///< Performed when the field value changes
        actionCalculateField, ///< Performed when the field needs to be recalculated
    };

    Annot(PDFDoc *docA, PDFRectangle *rectA);
    Annot(PDFDoc *docA, Object &&dictObject);
    Annot(PDFDoc *docA, Object &&dictObject, const Object *obj);
    bool isOk() { return ok; }

    void incRefCnt();
    void decRefCnt();

    virtual void draw(Gfx *gfx, bool printing);
    // Get the resource dict of the appearance stream
    virtual Object getAppearanceResDict();

    bool match(const Ref *refA) const { return ref == *refA; }

    double getXMin();
    double getYMin();
    double getXMax();
    double getYMax();

    void setRect(const PDFRectangle *rect);
    void setRect(double x1, double y1, double x2, double y2);

    // Sets the annot contents to new_content
    // new_content should never be NULL
    virtual void setContents(GooString *new_content);
    void setName(GooString *new_name);
    void setModified(GooString *new_modified);
    void setFlags(unsigned int new_flags);

    void setBorder(std::unique_ptr<AnnotBorder> &&new_border);
    void setColor(std::unique_ptr<AnnotColor> &&new_color);

    void setAppearanceState(const char *state);

    // getters
    PDFDoc *getDoc() const { return doc; }
    bool getHasRef() const { return hasRef; }
    Ref getRef() const { return ref; }
    const Object &getAnnotObj() const { return annotObj; }
    AnnotSubtype getType() const { return type; }
    PDFRectangle *getRect() const { return rect.get(); }
    void getRect(double *x1, double *y1, double *x2, double *y2) const;
    const GooString *getContents() const { return contents.get(); }
    int getPageNum() const { return page; }
    const GooString *getName() const { return name.get(); }
    const GooString *getModified() const { return modified.get(); }
    unsigned int getFlags() const { return flags; }
    AnnotAppearance *getAppearStreams() const { return appearStreams.get(); }
    const GooString *getAppearState() const { return appearState.get(); }
    AnnotBorder *getBorder() const { return border.get(); }
    AnnotColor *getColor() const { return color.get(); }
    int getTreeKey() const { return treeKey; }

    int getId() { return ref.num; }

    // Check if point is inside the annot rectangle.
    bool inRect(double x, double y) const;

    static void layoutText(const GooString *text, GooString *outBuf, int *i, const GfxFont *font, double *width, double widthLimit, int *charCount, bool noReencode);

private:
    void readArrayNum(Object *pdfArray, int key, double *value);
    // write vStr[i:j[ in appearBuf

    void initialize(PDFDoc *docA, Dict *dict);
    void setPage(int pageIndex, bool updateP); // Called by Page::addAnnot and Annots ctor

protected:
    virtual ~Annot();
    virtual void removeReferencedObjects(); // Called by Page::removeAnnot
    Object createForm(const GooString *appearBuf, double *bbox, bool transparencyGroup, Dict *resDict);
    Object createForm(const GooString *appearBuf, double *bbox, bool transparencyGroup, Object &&resDictObject); // overload to support incRef/decRef
    Dict *createResourcesDict(const char *formName, Object &&formStream, const char *stateName, double opacity, const char *blendMode);
    bool isVisible(bool printing);
    int getRotation() const;

    // Updates the field key of the annotation dictionary
    // and sets M to the current time
    void update(const char *key, Object &&value);

    // Delete appearance streams and reset appearance state
    void invalidateAppearance();

    Object annotObj;

    std::atomic_int refCnt;

    // required data
    AnnotSubtype type; // Annotation type
    std::unique_ptr<PDFRectangle> rect; // Rect

    // optional data
    std::unique_ptr<GooString> contents; // Contents
    std::unique_ptr<GooString> name; // NM
    std::unique_ptr<GooString> modified; // M
    int page; // P
    unsigned int flags; // F (must be a 32 bit unsigned int)
    std::unique_ptr<AnnotAppearance> appearStreams; // AP
    Object appearance; // a reference to the Form XObject stream
                       //   for the normal appearance
    std::unique_ptr<AnnotAppearanceBBox> appearBBox; // BBox of generated appearance
    std::unique_ptr<GooString> appearState; // AS
    int treeKey; // Struct Parent;
    Object oc; // OC

    PDFDoc *doc;
    Ref ref; // object ref identifying this annotation
    std::unique_ptr<AnnotBorder> border; // Border, BS
    std::unique_ptr<AnnotColor> color; // C
    bool ok;

    bool hasRef;
    mutable std::recursive_mutex mutex;
};

//------------------------------------------------------------------------
// AnnotPopup
//------------------------------------------------------------------------

class AnnotPopup : public Annot
{
public:
    AnnotPopup(PDFDoc *docA, PDFRectangle *rect);
    AnnotPopup(PDFDoc *docA, Object &&dictObject, const Object *obj);
    ~AnnotPopup() override;

    bool hasParent() const { return parentRef != Ref::INVALID(); }
    void setParent(Annot *parentA);
    bool getOpen() const { return open; }
    void setOpen(bool openA);

protected:
    void initialize(PDFDoc *docA, Dict *dict);

    Ref parentRef; // Parent
    bool open; // Open
};

//------------------------------------------------------------------------
// AnnotMarkup
//------------------------------------------------------------------------

class AnnotMarkup : public Annot
{
public:
    enum AnnotMarkupReplyType
    {
        replyTypeR, // R
        replyTypeGroup // Group
    };

    AnnotMarkup(PDFDoc *docA, PDFRectangle *rect);
    AnnotMarkup(PDFDoc *docA, Object &&dictObject, const Object *obj);
    ~AnnotMarkup() override;

    // getters
    const GooString *getLabel() const { return label.get(); }
    AnnotPopup *getPopup() const { return popup.get(); }
    double getOpacity() const { return opacity; }
    // getRC
    const GooString *getDate() const { return date.get(); }
    bool isInReplyTo() const { return inReplyTo != Ref::INVALID(); }
    int getInReplyToID() const { return inReplyTo.num; }
    const GooString *getSubject() const { return subject.get(); }
    AnnotMarkupReplyType getReplyTo() const { return replyTo; }
    AnnotExternalDataType getExData() const { return exData; }

    // The annotation takes the ownership of new_popup
    void setPopup(std::unique_ptr<AnnotPopup> &&new_popup);
    void setLabel(GooString *new_label);
    void setOpacity(double opacityA);
    void setDate(GooString *new_date);

protected:
    void removeReferencedObjects() override;

    std::unique_ptr<GooString> label; // T            (Default author)
    std::unique_ptr<AnnotPopup> popup; // Popup
    double opacity; // CA           (Default 1.0)
    // RC
    std::unique_ptr<GooString> date; // CreationDate
    Ref inReplyTo; // IRT
    std::unique_ptr<GooString> subject; // Subj
    AnnotMarkupReplyType replyTo; // RT           (Default R)
    // this object is overridden by the custom intent fields defined in some
    // annotation types.
    // GooString *intent;                // IT
    AnnotExternalDataType exData; // ExData

private:
    void initialize(PDFDoc *docA, Dict *dict);
};

//------------------------------------------------------------------------
// AnnotText
//------------------------------------------------------------------------

class AnnotText : public AnnotMarkup
{
public:
    enum AnnotTextState
    {
        stateUnknown,
        // Marked state model
        stateMarked, // Marked
        stateUnmarked, // Unmarked
        // Review state model
        stateAccepted, // Accepted
        stateRejected, // Rejected
        stateCancelled, // Cancelled
        stateCompleted, // Completed
        stateNone // None
    };

    AnnotText(PDFDoc *docA, PDFRectangle *rect);
    AnnotText(PDFDoc *docA, Object &&dictObject, const Object *obj);
    ~AnnotText() override;

    void draw(Gfx *gfx, bool printing) override;

    // getters
    bool getOpen() const { return open; }
    const GooString *getIcon() const { return icon.get(); }
    AnnotTextState getState() const { return state; }

    void setOpen(bool openA);
    void setIcon(GooString *new_icon);

private:
    void initialize(PDFDoc *docA, Dict *dict);

    bool open; // Open       (Default false)
    std::unique_ptr<GooString> icon; // Name       (Default Note)
    AnnotTextState state; // State      (Default Umarked if
                          //             StateModel Marked
                          //             None if StareModel Review)
};

//------------------------------------------------------------------------
// AnnotMovie
//------------------------------------------------------------------------

class AnnotMovie : public Annot
{
public:
    AnnotMovie(PDFDoc *docA, PDFRectangle *rect, Movie *movieA);
    AnnotMovie(PDFDoc *docA, Object &&dictObject, const Object *obj);
    ~AnnotMovie() override;

    void draw(Gfx *gfx, bool printing) override;

    const GooString *getTitle() const { return title.get(); }
    Movie *getMovie() { return movie.get(); }

private:
    void initialize(PDFDoc *docA, Dict *dict);

    std::unique_ptr<GooString> title; // T
    std::unique_ptr<Movie> movie; // Movie + A
};

//------------------------------------------------------------------------
// AnnotScreen
//------------------------------------------------------------------------

class AnnotScreen : public Annot
{
public:
    AnnotScreen(PDFDoc *docA, PDFRectangle *rect);
    AnnotScreen(PDFDoc *docA, Object &&dictObject, const Object *obj);
    ~AnnotScreen() override;

    const GooString *getTitle() const { return title.get(); }

    AnnotAppearanceCharacs *getAppearCharacs() { return appearCharacs.get(); }
    LinkAction *getAction() { return action.get(); } // The caller should not delete the result
    std::unique_ptr<LinkAction> getAdditionalAction(AdditionalActionsType type);

private:
    void initialize(PDFDoc *docA, Dict *dict);

    std::unique_ptr<GooString> title; // T

    std::unique_ptr<AnnotAppearanceCharacs> appearCharacs; // MK

    std::unique_ptr<LinkAction> action; // A
    Object additionalActions; // AA
};

//------------------------------------------------------------------------
// AnnotLink
//------------------------------------------------------------------------

class AnnotLink : public Annot
{
public:
    enum AnnotLinkEffect
    {
        effectNone, // N
        effectInvert, // I
        effectOutline, // O
        effectPush // P
    };

    AnnotLink(PDFDoc *docA, PDFRectangle *rect);
    AnnotLink(PDFDoc *docA, Object &&dictObject, const Object *obj);
    ~AnnotLink() override;

    void draw(Gfx *gfx, bool printing) override;

    // getters
    LinkAction *getAction() const { return action.get(); }
    AnnotLinkEffect getLinkEffect() const { return linkEffect; }
    AnnotQuadrilaterals *getQuadrilaterals() const { return quadrilaterals.get(); }

protected:
    void initialize(PDFDoc *docA, Dict *dict);

    std::unique_ptr<LinkAction> action; // A, Dest
    AnnotLinkEffect linkEffect; // H          (Default I)
    // Dict *uriAction;                                   // PA

    std::unique_ptr<AnnotQuadrilaterals> quadrilaterals; // QuadPoints
};

//------------------------------------------------------------------------
// AnnotFreeText
//------------------------------------------------------------------------

class AnnotFreeText : public AnnotMarkup
{
public:
    enum AnnotFreeTextQuadding
    {
        quaddingLeftJustified, // 0
        quaddingCentered, // 1
        quaddingRightJustified // 2
    };

    enum AnnotFreeTextIntent
    {
        intentFreeText, // FreeText
        intentFreeTextCallout, // FreeTextCallout
        intentFreeTextTypeWriter // FreeTextTypeWriter
    };

    static const double undefinedFontPtSize;

    AnnotFreeText(PDFDoc *docA, PDFRectangle *rect, const DefaultAppearance &da);
    AnnotFreeText(PDFDoc *docA, Object &&dictObject, const Object *obj);
    ~AnnotFreeText() override;

    void draw(Gfx *gfx, bool printing) override;
    Object getAppearanceResDict() override;
    void setContents(GooString *new_content) override;

    void setDefaultAppearance(const DefaultAppearance &da);
    void setQuadding(AnnotFreeTextQuadding new_quadding);
    void setStyleString(GooString *new_string);
    void setCalloutLine(AnnotCalloutLine *line);
    void setIntent(AnnotFreeTextIntent new_intent);

    // getters
    std::unique_ptr<DefaultAppearance> getDefaultAppearance() const;
    AnnotFreeTextQuadding getQuadding() const { return quadding; }
    // return rc
    const GooString *getStyleString() const { return styleString.get(); }
    AnnotCalloutLine *getCalloutLine() const { return calloutLine.get(); }
    AnnotFreeTextIntent getIntent() const { return intent; }
    AnnotBorderEffect *getBorderEffect() const { return borderEffect.get(); }
    PDFRectangle *getRectangle() const { return rectangle.get(); }
    AnnotLineEndingStyle getEndStyle() const { return endStyle; }

protected:
    void initialize(PDFDoc *docA, Dict *dict);
    void generateFreeTextAppearance();

    // required
    std::unique_ptr<GooString> appearanceString; // DA

    // optional
    AnnotFreeTextQuadding quadding; // Q  (Default 0)
    // RC
    std::unique_ptr<GooString> styleString; // DS
    std::unique_ptr<AnnotCalloutLine> calloutLine; // CL
    AnnotFreeTextIntent intent; // IT
    std::unique_ptr<AnnotBorderEffect> borderEffect; // BE
    std::unique_ptr<PDFRectangle> rectangle; // RD
    // inherited  from Annot
    // AnnotBorderBS border;                          // BS
    AnnotLineEndingStyle endStyle; // LE (Default None)
};

//------------------------------------------------------------------------
// AnnotLine
//------------------------------------------------------------------------

class AnnotLine : public AnnotMarkup
{
public:
    enum AnnotLineIntent
    {
        intentLineArrow, // LineArrow
        intentLineDimension // LineDimension
    };

    enum AnnotLineCaptionPos
    {
        captionPosInline, // Inline
        captionPosTop // Top
    };

    AnnotLine(PDFDoc *docA, PDFRectangle *rect);
    AnnotLine(PDFDoc *docA, Object &&dictObject, const Object *obj);
    ~AnnotLine() override;

    void draw(Gfx *gfx, bool printing) override;
    Object getAppearanceResDict() override;
    void setContents(GooString *new_content) override;

    void setVertices(double x1, double y1, double x2, double y2);
    void setStartEndStyle(AnnotLineEndingStyle start, AnnotLineEndingStyle end);
    void setInteriorColor(std::unique_ptr<AnnotColor> &&new_color);
    void setLeaderLineLength(double len);
    void setLeaderLineExtension(double len);
    void setCaption(bool new_cap);
    void setIntent(AnnotLineIntent new_intent);

    // getters
    AnnotLineEndingStyle getStartStyle() const { return startStyle; }
    AnnotLineEndingStyle getEndStyle() const { return endStyle; }
    AnnotColor *getInteriorColor() const { return interiorColor.get(); }
    double getLeaderLineLength() const { return leaderLineLength; }
    double getLeaderLineExtension() const { return leaderLineExtension; }
    bool getCaption() const { return caption; }
    AnnotLineIntent getIntent() const { return intent; }
    double getLeaderLineOffset() const { return leaderLineOffset; }
    AnnotLineCaptionPos getCaptionPos() const { return captionPos; }
    Dict *getMeasure() const { return measure; }
    double getCaptionTextHorizontal() const { return captionTextHorizontal; }
    double getCaptionTextVertical() const { return captionTextVertical; }
    double getX1() const { return coord1->getX(); }
    double getY1() const { return coord1->getY(); }
    double getX2() const { return coord2->getX(); }
    double getY2() const { return coord2->getY(); }

protected:
    void initialize(PDFDoc *docA, Dict *dict);
    void generateLineAppearance();

    // required
    std::unique_ptr<AnnotCoord> coord1;
    std::unique_ptr<AnnotCoord> coord2;

    // optional
    // inherited  from Annot
    // AnnotBorderBS border;                   // BS
    AnnotLineEndingStyle startStyle; // LE       (Default [/None /None])
    AnnotLineEndingStyle endStyle; //
    std::unique_ptr<AnnotColor> interiorColor; // IC
    double leaderLineLength; // LL       (Default 0)
    double leaderLineExtension; // LLE      (Default 0)
    bool caption; // Cap      (Default false)
    AnnotLineIntent intent; // IT
    double leaderLineOffset; // LLO
    AnnotLineCaptionPos captionPos; // CP       (Default Inline)
    Dict *measure; // Measure
    double captionTextHorizontal; // CO       (Default [0, 0])
    double captionTextVertical; //
};

//------------------------------------------------------------------------
// AnnotTextMarkup
//------------------------------------------------------------------------

class AnnotTextMarkup : public AnnotMarkup
{
public:
    AnnotTextMarkup(PDFDoc *docA, PDFRectangle *rect, AnnotSubtype subType);
    AnnotTextMarkup(PDFDoc *docA, Object &&dictObject, const Object *obj);
    ~AnnotTextMarkup() override;

    void draw(Gfx *gfx, bool printing) override;

    // typeHighlight, typeUnderline, typeSquiggly or typeStrikeOut
    void setType(AnnotSubtype new_type);

    void setQuadrilaterals(AnnotQuadrilaterals *quadPoints);

    AnnotQuadrilaterals *getQuadrilaterals() const { return quadrilaterals.get(); }

protected:
    void initialize(PDFDoc *docA, Dict *dict);

    std::unique_ptr<AnnotQuadrilaterals> quadrilaterals; // QuadPoints

private:
    bool shouldCreateApperance(Gfx *gfx) const;
};

//------------------------------------------------------------------------
// AnnotStamp
//------------------------------------------------------------------------

class AnnotStamp : public AnnotMarkup
{
public:
    AnnotStamp(PDFDoc *docA, PDFRectangle *rect);
    AnnotStamp(PDFDoc *docA, Object &&dictObject, const Object *obj);
    ~AnnotStamp() override;

    void setIcon(GooString *new_icon);

    // getters
    const GooString *getIcon() const { return icon.get(); }

private:
    void initialize(PDFDoc *docA, Dict *dict);

    std::unique_ptr<GooString> icon; // Name       (Default Draft)
};

//------------------------------------------------------------------------
// AnnotGeometry
//------------------------------------------------------------------------

class AnnotGeometry : public AnnotMarkup
{
public:
    AnnotGeometry(PDFDoc *docA, PDFRectangle *rect, AnnotSubtype subType);
    AnnotGeometry(PDFDoc *docA, Object &&dictObject, const Object *obj);
    ~AnnotGeometry() override;

    void draw(Gfx *gfx, bool printing) override;

    void setType(AnnotSubtype new_type); // typeSquare or typeCircle
    void setInteriorColor(std::unique_ptr<AnnotColor> &&new_color);

    // getters
    AnnotColor *getInteriorColor() const { return interiorColor.get(); }
    AnnotBorderEffect *getBorderEffect() const { return borderEffect.get(); }
    PDFRectangle *getGeometryRect() const { return geometryRect.get(); }

private:
    void initialize(PDFDoc *docA, Dict *dict);

    std::unique_ptr<AnnotColor> interiorColor; // IC
    std::unique_ptr<AnnotBorderEffect> borderEffect; // BE
    std::unique_ptr<PDFRectangle> geometryRect; // RD (combined with Rect)
};

//------------------------------------------------------------------------
// AnnotPolygon
//------------------------------------------------------------------------

class AnnotPolygon : public AnnotMarkup
{
public:
    enum AnnotPolygonIntent
    {
        polygonCloud, // PolygonCloud
        polylineDimension, // PolyLineDimension
        polygonDimension // PolygonDimension
    };

    AnnotPolygon(PDFDoc *docA, PDFRectangle *rect, AnnotSubtype subType);
    AnnotPolygon(PDFDoc *docA, Object &&dictObject, const Object *obj);
    ~AnnotPolygon() override;

    void draw(Gfx *gfx, bool printing) override;
    void generatePolyLineAppearance(AnnotAppearanceBuilder *appearBuilder);
    void setType(AnnotSubtype new_type); // typePolygon or typePolyLine
    void setVertices(AnnotPath *path);
    void setStartEndStyle(AnnotLineEndingStyle start, AnnotLineEndingStyle end);
    void setInteriorColor(std::unique_ptr<AnnotColor> &&new_color);
    void setIntent(AnnotPolygonIntent new_intent);

    // getters
    AnnotPath *getVertices() const { return vertices.get(); }
    AnnotLineEndingStyle getStartStyle() const { return startStyle; }
    AnnotLineEndingStyle getEndStyle() const { return endStyle; }
    AnnotColor *getInteriorColor() const { return interiorColor.get(); }
    AnnotBorderEffect *getBorderEffect() const { return borderEffect.get(); }
    AnnotPolygonIntent getIntent() const { return intent; }

private:
    void initialize(PDFDoc *docA, Dict *dict);

    // required
    std::unique_ptr<AnnotPath> vertices; // Vertices

    // optional
    AnnotLineEndingStyle startStyle; // LE       (Default [/None /None])
    AnnotLineEndingStyle endStyle; //
    // inherited  from Annot
    // AnnotBorderBS border;                          // BS
    std::unique_ptr<AnnotColor> interiorColor; // IC
    std::unique_ptr<AnnotBorderEffect> borderEffect; // BE
    AnnotPolygonIntent intent; // IT
    // Measure
};

//------------------------------------------------------------------------
// AnnotCaret
//------------------------------------------------------------------------

class AnnotCaret : public AnnotMarkup
{
public:
    enum AnnotCaretSymbol
    {
        symbolNone, // None
        symbolP // P
    };

    AnnotCaret(PDFDoc *docA, PDFRectangle *rect);
    AnnotCaret(PDFDoc *docA, Object &&dictObject, const Object *obj);
    ~AnnotCaret() override;

    void setSymbol(AnnotCaretSymbol new_symbol);

    // getters
    AnnotCaretSymbol getSymbol() const { return symbol; }
    PDFRectangle *getCaretRect() const { return caretRect.get(); }

private:
    void initialize(PDFDoc *docA, Dict *dict);

    AnnotCaretSymbol symbol; // Sy         (Default None)
    std::unique_ptr<PDFRectangle> caretRect; // RD (combined with Rect)
};

//------------------------------------------------------------------------
// AnnotInk
//------------------------------------------------------------------------

class AnnotInk : public AnnotMarkup
{
public:
    AnnotInk(PDFDoc *docA, PDFRectangle *rect);
    AnnotInk(PDFDoc *docA, Object &&dictObject, const Object *obj);
    ~AnnotInk() override;

    void draw(Gfx *gfx, bool printing) override;

    void setInkList(AnnotPath **paths, int n_paths);

    // getters
    AnnotPath **getInkList() const { return inkList; }
    int getInkListLength() const { return inkListLength; }

private:
    void initialize(PDFDoc *docA, Dict *dict);
    void writeInkList(AnnotPath **paths, int n_paths, Array *dest_array);
    void parseInkList(Array *src_array);
    void freeInkList();

    // required
    AnnotPath **inkList; // InkList
    int inkListLength;

    // optional
    // inherited from Annot
    // AnnotBorderBS border;  // BS
};

//------------------------------------------------------------------------
// AnnotFileAttachment
//------------------------------------------------------------------------

class AnnotFileAttachment : public AnnotMarkup
{
public:
    AnnotFileAttachment(PDFDoc *docA, PDFRectangle *rect, GooString *filename);
    AnnotFileAttachment(PDFDoc *docA, Object &&dictObject, const Object *obj);
    ~AnnotFileAttachment() override;

    void draw(Gfx *gfx, bool printing) override;

    // getters
    Object *getFile() { return &file; }
    const GooString *getName() const { return name.get(); }

private:
    void initialize(PDFDoc *docA, Dict *dict);

    // required
    Object file; // FS

    // optional
    std::unique_ptr<GooString> name; // Name
};

//------------------------------------------------------------------------
// AnnotSound
//------------------------------------------------------------------------

class AnnotSound : public AnnotMarkup
{
public:
    AnnotSound(PDFDoc *docA, PDFRectangle *rect, Sound *soundA);
    AnnotSound(PDFDoc *docA, Object &&dictObject, const Object *obj);
    ~AnnotSound() override;

    void draw(Gfx *gfx, bool printing) override;

    // getters
    Sound *getSound() { return sound.get(); }
    const GooString *getName() const { return name.get(); }

private:
    void initialize(PDFDoc *docA, Dict *dict);

    // required
    std::unique_ptr<Sound> sound; // Sound

    // optional
    std::unique_ptr<GooString> name; // Name
};

//------------------------------------------------------------------------
// AnnotWidget
//------------------------------------------------------------------------

class AnnotWidget : public Annot
{
public:
    enum AnnotWidgetHighlightMode
    {
        highlightModeNone, // N
        highlightModeInvert, // I
        highlightModeOutline, // O
        highlightModePush // P,T
    };

    AnnotWidget(PDFDoc *docA, Object &&dictObject, const Object *obj);
    AnnotWidget(PDFDoc *docA, Object *dictObject, Object *obj, FormField *fieldA);
    ~AnnotWidget() override;

    void draw(Gfx *gfx, bool printing) override;

    void generateFieldAppearance(bool *addedDingbatsResource);
    void updateAppearanceStream();

    AnnotWidgetHighlightMode getMode() { return mode; }
    AnnotAppearanceCharacs *getAppearCharacs() { return appearCharacs.get(); }
    LinkAction *getAction() { return action.get(); } // The caller should not delete the result
    std::unique_ptr<LinkAction> getAdditionalAction(AdditionalActionsType type);
    std::unique_ptr<LinkAction> getFormAdditionalAction(FormAdditionalActionsType type);
    Dict *getParent() { return parent; }
    void setNewAppearance(Object &&newAppearance);

    bool setFormAdditionalAction(FormAdditionalActionsType type, const GooString &js);

    void setField(FormField *f) { field = f; };

private:
    void initialize(PDFDoc *docA, Dict *dict);

    Form *form;
    FormField *field; // FormField object for this annotation
    AnnotWidgetHighlightMode mode; // H  (Default I)
    std::unique_ptr<AnnotAppearanceCharacs> appearCharacs; // MK
    std::unique_ptr<LinkAction> action; // A
    Object additionalActions; // AA
    // inherited  from Annot
    // AnnotBorderBS border;                // BS
    Dict *parent; // Parent
    Ref updatedAppearanceStream; // {-1,-1} if updateAppearanceStream has never been called
};

//------------------------------------------------------------------------
// Annot3D
//------------------------------------------------------------------------

class Annot3D : public Annot
{
    class Activation
    {
    public:
        enum ActivationATrigger
        {
            aTriggerUnknown,
            aTriggerPageOpened, // PO
            aTriggerPageVisible, // PV
            aTriggerUserAction // XA
        };

        enum ActivationAState
        {
            aStateUnknown,
            aStateEnabled, // I
            aStateDisabled // L
        };

        enum ActivationDTrigger
        {
            dTriggerUnknown,
            dTriggerPageClosed, // PC
            dTriggerPageInvisible, // PI
            dTriggerUserAction // XD
        };

        enum ActivationDState
        {
            dStateUnknown,
            dStateUninstantiaded, // U
            dStateInstantiated, // I
            dStateLive // L
        };

        Activation(Dict *dict);

    private:
        ActivationATrigger aTrigger; // A   (Default XA)
        ActivationAState aState; // AIS (Default L)
        ActivationDTrigger dTrigger; // D   (Default PI)
        ActivationDState dState; // DIS (Default U)
        bool displayToolbar; // TB  (Default true)
        bool displayNavigation; // NP  (Default false);
    };

public:
    Annot3D(PDFDoc *docA, PDFRectangle *rect);
    Annot3D(PDFDoc *docA, Object &&dictObject, const Object *obj);
    ~Annot3D() override;

    // getters

private:
    void initialize(PDFDoc *docA, Dict *dict);

    std::unique_ptr<Activation> activation; // 3DA
};

//------------------------------------------------------------------------
// AnnotRichMedia
//------------------------------------------------------------------------

class AnnotRichMedia : public Annot
{
public:
    class Params
    {
    public:
        Params(Dict *dict);
        ~Params();

        Params(const Params &) = delete;
        Params &operator=(const Params &) = delete;

        const GooString *getFlashVars() const;

    private:
        // optional
        std::unique_ptr<GooString> flashVars; // FlashVars
    };

    class Instance
    {
    public:
        enum Type
        {
            type3D, // 3D
            typeFlash, // Flash
            typeSound, // Sound
            typeVideo // Video
        };

        Instance(Dict *dict);
        ~Instance();

        Instance(const Instance &) = delete;
        Instance &operator=(const Instance &) = delete;

        Type getType() const;
        Params *getParams() const;

    private:
        // optional
        Type type; // Subtype
        std::unique_ptr<Params> params; // Params
    };

    class Configuration
    {
    public:
        enum Type
        {
            type3D, // 3D
            typeFlash, // Flash
            typeSound, // Sound
            typeVideo // Video
        };

        Configuration(Dict *dict);
        ~Configuration();

        Configuration(const Configuration &) = delete;
        Configuration &operator=(const Configuration &) = delete;

        Type getType() const;
        const GooString *getName() const;
        int getInstancesCount() const;
        Instance *getInstance(int index) const;

    private:
        // optional
        Type type; // Subtype
        std::unique_ptr<GooString> name; // Name
        Instance **instances; // Instances
        int nInstances;
    };

    class Content;

    class Asset
    {
    public:
        Asset();
        ~Asset();

        Asset(const Asset &) = delete;
        Asset &operator=(const Asset &) = delete;

        const GooString *getName() const;
        Object *getFileSpec() const;

    private:
        friend class AnnotRichMedia::Content;

        std::unique_ptr<GooString> name;
        Object fileSpec;
    };

    class Content
    {
    public:
        Content(Dict *dict);
        ~Content();

        Content(const Content &) = delete;
        Content &operator=(const Content &) = delete;

        int getConfigurationsCount() const;
        Configuration *getConfiguration(int index) const;

        int getAssetsCount() const;
        Asset *getAsset(int index) const;

    private:
        // optional
        Configuration **configurations; // Configurations
        int nConfigurations;

        Asset **assets; // Assets
        int nAssets;
    };

    class Activation
    {
    public:
        enum Condition
        {
            conditionPageOpened, // PO
            conditionPageVisible, // PV
            conditionUserAction // XA
        };

        Activation(Dict *dict);

        Condition getCondition() const;

    private:
        // optional
        Condition condition;
    };

    class Deactivation
    {
    public:
        enum Condition
        {
            conditionPageClosed, // PC
            conditionPageInvisible, // PI
            conditionUserAction // XD
        };

        Deactivation(Dict *dict);

        Condition getCondition() const;

    private:
        // optional
        Condition condition;
    };

    class Settings
    {
    public:
        Settings(Dict *dict);
        ~Settings();

        Settings(const Settings &) = delete;
        Settings &operator=(const Settings &) = delete;

        Activation *getActivation() const;
        Deactivation *getDeactivation() const;

    private:
        // optional
        std::unique_ptr<Activation> activation;
        std::unique_ptr<Deactivation> deactivation;
    };

    AnnotRichMedia(PDFDoc *docA, PDFRectangle *rect);
    AnnotRichMedia(PDFDoc *docA, Object &&dictObject, const Object *obj);
    ~AnnotRichMedia() override;

    Content *getContent() const;

    Settings *getSettings() const;

private:
    void initialize(PDFDoc *docA, Dict *dict);

    // required
    std::unique_ptr<Content> content; // RichMediaContent

    // optional
    std::unique_ptr<Settings> settings; // RichMediaSettings
};

//------------------------------------------------------------------------
// Annots
//------------------------------------------------------------------------

class Annots
{
public:
    // Build a list of Annot objects and call setPage on them
    Annots(PDFDoc *docA, int page, Object *annotsObj);

    ~Annots();

    Annots(const Annots &) = delete;
    Annots &operator=(const Annots &) = delete;

    // Iterate through list of annotations.
    int getNumAnnots() const { return annots.size(); }
    Annot *getAnnot(int i) { return annots[i]; }
    void appendAnnot(Annot *annot);
    bool removeAnnot(Annot *annot);

private:
    Annot *createAnnot(Object &&dictObject, const Object *obj);
    Annot *findAnnot(Ref *ref);

    PDFDoc *doc;
    std::vector<Annot *> annots;
};

#endif
