text: /**
   * Copyright (C) 2013-2019  Stefan Löffler
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License as published by the Free
   * Software Foundation; either version 2, or (at your option) any later
   * version.
   *
   * This program 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 General Public License for
   * more details.
   */
  #ifndef PDFAnnotations_H
  #define PDFAnnotations_H
  
  #include "PDFActions.h"
  
  #include <QColor>
  #include <QDateTime>
  #include <QFlags>
  #include <QPolygonF>
  #include <QRectF>
  #include <QString>
  #include <QWeakPointer>
  
  namespace QtPDF {
  
  namespace Backend {
    class Page;
  }
  
  namespace Annotation {
  
  class Popup;
  
  // ABC for annotations
  // Modelled after sec. 8.4.1 of the PDF 1.7 specifications
  class AbstractAnnotation
  {
  public:
    enum AnnotationFlag {
      Annotation_Default = 0x0,
      Annotation_Invisible = 0x1,
      Annotation_Hidden = 0x2,
      Annotation_Print = 0x4,
      Annotation_NoZoom = 0x8,
      Annotation_NoRotate = 0x10,
      Annotation_NoView = 0x20,
      Annotation_ReadOnly = 0x40,
      Annotation_Locked = 0x80,
      Annotation_ToggleNoView = 0x100,
      Annotation_LockedContents = 0x200
    };
    Q_DECLARE_FLAGS(AnnotationFlags, AnnotationFlag)
  
    enum AnnotationType {
      AnnotationTypeText, AnnotationTypeLink, AnnotationTypeFreeText,
      AnnotationTypeLine, AnnotationTypeSquare, AnnotationTypeCircle,
      AnnotationTypePolygon, AnnotationTypePolyLine, AnnotationTypeHighlight,
      AnnotationTypeUnderline, AnnotationTypeSquiggly, AnnotationTypeStrikeOut,
      AnnotationTypeStamp, AnnotationTypeCaret, AnnotationTypeInk,
      AnnotationTypePopup, AnnotationTypeFileAttachment, AnnotationTypeSound,
      AnnotationTypeMovie, AnnotationTypeWidget, AnnotationTypeScreen,
      AnnotationTypePrinterMark, AnnotationTypeTrapNet, AnnotationTypeWatermark,
      AnnotationType3D
    };
  
    AbstractAnnotation() = default;
    virtual ~AbstractAnnotation() = default;
  
    virtual AnnotationType type() const = 0;
    virtual bool isMarkup() const { return false; }
  
    // Declare all the getter/setter methods virtual so derived classes can
    // override them
    virtual QRectF rect() const { return _rect; }
    virtual QString contents() const { return _contents; }
    virtual QWeakPointer<Backend::Page> page() const { return _page; }
    virtual QString name() const { return _name; }
    virtual QDateTime lastModified() const { return _lastModified; }
    virtual AnnotationFlags flags() const { return _flags; }
    virtual AnnotationFlags& flags() { return _flags; }
    virtual QColor color() const { return _color; }
  
    virtual void setRect(const QRectF rect) { _rect = rect; }
    virtual void setContents(const QString contents) { _contents = contents; }
    virtual void setPage(QWeakPointer<Backend::Page> page) { _page = page; }
    virtual void setName(const QString name) { _name = name; }
    virtual void setLastModified(const QDateTime lastModified) { _lastModified = lastModified; }
    virtual void setColor(const QColor color) { _color = color; }
  
    virtual bool operator==(const AbstractAnnotation & o) const;
  
  protected:
    QRectF _rect; // required, in pdf coordinates
    QString _contents; // optional
    QWeakPointer<Backend::Page> _page; // optional; since PDF 1.3
    QString _name; // optional; since PDF 1.4
    QDateTime _lastModified; // optional; since PDF 1.1
    // TODO: _appearance, _appearanceState, _border, _structParent, _optContent
    AnnotationFlags _flags;
    // QList<???> _appearance;
    // ??? _appearanceState;
    // ??? _border;
    QColor _color;
    // ??? _structParent;
    // ??? _optContent;
  };
  Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractAnnotation::AnnotationFlags)
  
  // Markup Annotation are:
  // Text, FreeText, Line, Square, Circle, Polygon, PolyLine, Highlight, Underline
  // Squiggly, StrikeOut, Stamp, Caret, Ink, FileAttachment, Sound
  class Markup : public AbstractAnnotation
  {
  public:
    Markup() : AbstractAnnotation() { }
    ~Markup() override;
    Markup(const Markup & o);
    Markup & operator=(const Markup & o);
  
    bool isMarkup() const override { return true; }
  
    virtual QString title() const { return _title; }
    // Synonym for title(), but easier to read
    virtual QString author() const { return _title; }
    virtual QString richContents() const { return (!_richContents.isEmpty() ? _richContents : _contents); }
    virtual QDateTime creationDate() const { return _creationDate; }
    virtual QString subject() const { return _subject; }
    virtual Popup * popup() const { return _popup; }
  
    virtual void setTitle(const QString title) { _title = title; }
    // Synonym for setTitle(), but easier to read
    virtual void setAuthor(const QString author) { _title = author; }
    virtual void setRichContents(const QString contents) { _richContents = contents; }
    virtual void setCreationDate(const QDateTime timestamp) { _creationDate = timestamp; }
    virtual void setSubject(const QString subject) { _subject = subject; }
    // Note: the Markup takes ownership of `popup`
    virtual void setPopup(Popup * popup);
  
    bool operator==(const AbstractAnnotation & o) const override;
  
  protected:
    QString _title; // optional; since PDF 1.1; by convention identifies the annotation author
    Popup * _popup{nullptr};
    // float _opacity;
    QString _richContents; // optional; since PDF 1.5; may contain some HTML tags
    QDateTime _creationDate; // optional; since PDF 1.5
    // AbstractAnnotation * _inReplyTo;
    // enum _replyType;
    QString _subject; // optional; since PDF 1.5
    // enum/int _intent;
    // _externalData; // currently only Markup3D
  };
  
  class Link : public AbstractAnnotation
  {
  public:
    enum HighlightingMode { HighlightingNone, HighlightingInvert, HighlightingOutline, HighlightingPush };
  
    Link() : AbstractAnnotation() { }
    ~Link() override;
    Link(const Link & other) : AbstractAnnotation(other), _highlightingMode(other._highlightingMode), _quadPoints(other._quadPoints) {
      _actionOnActivation = (other._actionOnActivation ? other._actionOnActivation->clone() : nullptr);
    }
    Link & operator =(const Link & other) {
      if (&other == this) return *this;
      AbstractAnnotation::operator =(other);
      _highlightingMode = other._highlightingMode;
      _quadPoints = other._quadPoints;
      if (_actionOnActivation) delete _actionOnActivation;
      _actionOnActivation = (other._actionOnActivation ? other._actionOnActivation->clone() : nullptr);
      return *this;
    }
  
    AnnotationType type() const override { return AnnotationTypeLink; }
  
    HighlightingMode highlightingMode() const { return _highlightingMode; }
    QPolygonF quadPoints() const;
    PDFAction * actionOnActivation() const { return _actionOnActivation; }
  
    void setHighlightingMode(const HighlightingMode mode) { _highlightingMode = mode; }
    void setQuadPoints(const QPolygonF quadPoints) { _quadPoints = quadPoints; }
    // Note: Link takes ownership of PDFAction pointers
    void setActionOnActivation(PDFAction * const action);
  
    bool operator==(const AbstractAnnotation & o) const override;
  
  private:
    // Note: the PA member of the link annotation dict is deliberately ommitted
    // because we don't support WebCapture at the moment
    // Note: The PDF specs include a "destination" field for LinkAnnotations;
    // In this implementation this case should be handled by a PDFGoToAction
    HighlightingMode _highlightingMode{HighlightingNone};
    QPolygonF _quadPoints;
    PDFAction * _actionOnActivation{nullptr};
  };
  
  class Text : public Markup
  {
  public:
    AnnotationType type() const override { return AnnotationTypeText; }
  private:
    //bool _open;
    QString _iconName;
    QString _state;
    QString _stateModel;
  };
  
  class FreeText : public Markup
  {
  public:
    AnnotationType type() const override { return AnnotationTypeFreeText; }
    // TODO: members
  };
  
  class Caret : public Markup
  {
  public:
    AnnotationType type() const override { return AnnotationTypeCaret; }
  private:
    QRectF _rectDiff;
    // enum _symbol;
  };
  
  class Popup : public AbstractAnnotation
  {
  public:
    AnnotationType type() const override { return AnnotationTypePopup; }
  
    Markup * parent() { return _parent; }
    bool isOpen() const { return _open; }
  
    void setParent(Markup * parent) { _parent = parent; }
    void setOpen(const bool open = true) { _open = open; }
  
    QString contents() const override { return (_parent != nullptr ? _parent->contents() : _contents); }
    QDateTime lastModified() const override { return (_parent != nullptr ? _parent->lastModified() : _lastModified); }
    QColor color() const override { return (_parent != nullptr ? _parent->color() : _color); }
    QString title() const { return (_parent != nullptr ? _parent->title() : _title); }
  
    void setTitle(const QString & title) { _title = title; }
  
    bool operator==(const AbstractAnnotation & o) const override;
  
  private:
    Markup * _parent{nullptr};
    bool _open{false};
    QString _title;
  };
  
  class Highlight : public Markup
  {
  public:
    AnnotationType type() const override { return AnnotationTypeHighlight; }
    // TODO: members
  };
  
  class Underline: public Markup
  {
  public:
    AnnotationType type() const override { return AnnotationTypeUnderline; }
    // TODO: members
  };
  
  class Squiggly: public Markup
  {
  public:
    AnnotationType type() const override { return AnnotationTypeSquiggly; }
    // TODO: members
  };
  
  class StrikeOut: public Markup
  {
  public:
    AnnotationType type() const override { return AnnotationTypeStrikeOut; }
    // TODO: members
  };
  
  // Line, Square, Circle, Polygon, PolyLine, Stamp, Ink, FileAttachment, Sound
  
  } // namespace Annotation
  
  } // namespace QtPDF
  
  #endif // End header guard
  // vim: set sw=2 ts=2 et
  
  