Okular

annotations.cpp
1 /*
2  SPDX-FileCopyrightText: 2005 Enrico Ros <[email protected]>
3 
4  SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "annotations.h"
8 #include "annotations_p.h"
9 
10 // qt/kde includes
11 #include <QApplication>
12 #include <QColor>
13 #include <QIcon>
14 #include <QPainter>
15 #include <QStandardPaths>
16 #include <QSvgRenderer>
17 
18 // DBL_MAX
19 #include <float.h>
20 
21 // local includes
22 #include "action.h"
23 #include "document.h"
24 #include "document_p.h"
25 #include "movie.h"
26 #include "page_p.h"
27 #include "sound.h"
28 
29 using namespace Okular;
30 
31 /**
32  * True, if point @p c lies to the left of the vector from @p a to @p b
33  * @internal
34  */
35 static bool isLeftOfVector(const NormalizedPoint &a, const NormalizedPoint &b, const NormalizedPoint &c)
36 {
37  // cross product
38  return ((b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x)) > 0;
39 }
40 
41 /**
42  * @brief Calculates distance of the given point @p x @p y @p xScale @p yScale to the @p path
43  *
44  * Does piecewise comparison and selects the distance to the closest segment
45  */
46 static double distanceSqr(double x, double y, double xScale, double yScale, const QList<NormalizedPoint> &path)
47 {
48  double distance = DBL_MAX;
49  double thisDistance;
51  NormalizedPoint lastPoint = *i;
52 
53  for (++i; i != path.constEnd(); ++i) {
54  thisDistance = NormalizedPoint::distanceSqr(x, y, xScale, yScale, lastPoint, (*i));
55 
56  if (thisDistance < distance) {
57  distance = thisDistance;
58  }
59 
60  lastPoint = *i;
61  }
62  return distance;
63 }
64 
65 /**
66  * Given the squared @p distance from the idealized 0-width line and a pen width @p penWidth,
67  * (not squared!), returns the final distance
68  *
69  * @warning The returned distance is not exact:
70  * We calculate an (exact) squared distance to the ideal (centered) line, and then subtract
71  * the squared width of the pen:
72  * a^2 - b^2 where a = "distance from idealized 0-width line" b = "pen width"
73  * For an exact result, we would want to calculate "(a - b)^2" but that would require
74  * a square root operation because we only know the squared distance a^2.
75  *
76  * However, the approximation is feasible, because:
77  * error = (a-b)^2 - (a^2 - b^2) = -2ab + 2b^2 = 2b(b - a)
78  * Therefore:
79  * lim_{a->b} a^2 - b^2 - a^2 + 2ab - b^2 --> 0
80  *
81  * In other words, this approximation will estimate the distance to be slightly more than it actually is
82  * for as long as we are far "outside" the line, becoming more accurate the closer we get to the line
83  * boundary. Trivially, it also fulfils (a1 < a2) => ((a1^2 - b^2) < (a2^2 - b^2)) making it monotonic.
84  * "Inside" of the drawn line, the distance is 0 anyway.
85  */
86 static double strokeDistance(double distance, double penWidth)
87 {
88  return fmax(distance - pow(penWidth, 2), 0);
89 }
90 
91 // BEGIN AnnotationUtils implementation
93 {
94  // safety check on annotation element
95  if (!annElement.hasAttribute(QStringLiteral("type"))) {
96  return nullptr;
97  }
98 
99  // build annotation of given type
100  Annotation *annotation = nullptr;
101  int typeNumber = annElement.attribute(QStringLiteral("type")).toInt();
102  switch (typeNumber) {
103  case Annotation::AText:
104  annotation = new TextAnnotation(annElement);
105  break;
106  case Annotation::ALine:
107  annotation = new LineAnnotation(annElement);
108  break;
109  case Annotation::AGeom:
110  annotation = new GeomAnnotation(annElement);
111  break;
113  annotation = new HighlightAnnotation(annElement);
114  break;
115  case Annotation::AStamp:
116  annotation = new StampAnnotation(annElement);
117  break;
118  case Annotation::AInk:
119  annotation = new InkAnnotation(annElement);
120  break;
121  case Annotation::ACaret:
122  annotation = new CaretAnnotation(annElement);
123  break;
124  }
125 
126  // return created annotation
127  return annotation;
128 }
129 
130 void AnnotationUtils::storeAnnotation(const Annotation *ann, QDomElement &annElement, QDomDocument &document)
131 {
132  // save annotation's type as element's attribute
133  annElement.setAttribute(QStringLiteral("type"), (uint)ann->subType());
134 
135  // append all annotation data as children of this node
136  ann->store(annElement, document);
137 }
138 
140 {
141  // loop through the whole children and return a 'name' named element
142  QDomNode subNode = parentNode.firstChild();
143  while (subNode.isElement()) {
144  QDomElement element = subNode.toElement();
145  if (element.tagName() == name) {
146  return element;
147  }
148  subNode = subNode.nextSibling();
149  }
150  // if the name can't be found, return a dummy null element
151  return QDomElement();
152 }
153 
154 QRect AnnotationUtils::annotationGeometry(const Annotation *annotation, double scaleX, double scaleY)
155 {
156  const QRect rect = annotation->transformedBoundingRectangle().geometry((int)scaleX, (int)scaleY);
157  if (annotation->subType() == Annotation::AText && (((TextAnnotation *)annotation)->textType() == TextAnnotation::Linked)) {
158  // To be honest i have no clue of why the 24,24 is here, maybe to make sure it's not too small?
159  // But why only for linked text?
160  const QRect rect24 = QRect((int)(annotation->transformedBoundingRectangle().left * scaleX), (int)(annotation->transformedBoundingRectangle().top * scaleY), 24, 24);
161  return rect24.united(rect);
162  }
163 
164  return rect;
165 }
166 
167 QPixmap AnnotationUtils::loadStamp(const QString &nameOrPath, int size, bool keepAspectRatio)
168 {
169  const QString name = nameOrPath.toLower();
170 
171  static std::unique_ptr<QSvgRenderer> svgStampFile;
172  if (!svgStampFile.get()) {
173  const QString stampFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("okular/pics/stamps.svg"));
174  if (!stampFile.isEmpty()) {
175  svgStampFile = std::make_unique<QSvgRenderer>(stampFile);
176  if (!svgStampFile->isValid()) {
177  svgStampFile.reset();
178  }
179  }
180  }
181 
182  QSvgRenderer *r = svgStampFile.get();
183  if (r && r->isValid() && r->elementExists(name)) {
184  const QSize stampSize = r->boundsOnElement(name).size().toSize();
185  const QSize pixmapSize = stampSize.scaled(size, size, keepAspectRatio ? Qt::KeepAspectRatioByExpanding : Qt::IgnoreAspectRatio);
186  QPixmap pixmap(pixmapSize);
187  pixmap.fill(Qt::transparent);
188  QPainter p(&pixmap);
189  r->render(&p, name);
190  p.end();
191  return pixmap;
192  }
193 
194  // _name is a path (do this before loading as icon name to avoid some rare weirdness )
195  QPixmap pixmap;
196  pixmap.load(nameOrPath);
197  if (!pixmap.isNull()) {
198  pixmap = pixmap.scaled(size, size, keepAspectRatio ? Qt::KeepAspectRatioByExpanding : Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
199  return pixmap;
200  }
201 
202  // _name is an icon name
203  return QIcon::fromTheme(name).pixmap(size);
204 }
205 // END AnnotationUtils implementation
206 
207 AnnotationProxy::AnnotationProxy()
208 {
209 }
210 
212 {
213 }
214 
215 // BEGIN Annotation implementation
216 
217 class Annotation::Style::Private
218 {
219 public:
220  Private()
221  : m_opacity(1.0)
222  , m_width(1.0)
223  , m_style(Solid)
224  , m_xCorners(0.0)
225  , m_yCorners(0.0)
226  , m_marks(3)
227  , m_spaces(0)
228  , m_effect(NoEffect)
229  , m_effectIntensity(1.0)
230  {
231  }
232 
233  QColor m_color;
234  double m_opacity;
235  double m_width;
236  LineStyle m_style;
237  double m_xCorners;
238  double m_yCorners;
239  int m_marks;
240  int m_spaces;
241  LineEffect m_effect;
242  double m_effectIntensity;
243 };
244 
246  : d(new Private)
247 {
248 }
249 
251 {
252  delete d;
253 }
254 
255 Annotation::Style::Style(const Style &other)
256  : d(new Private)
257 {
258  *d = *other.d;
259 }
260 
261 Annotation::Style &Annotation::Style::operator=(const Style &other)
262 {
263  if (this != &other) {
264  *d = *other.d;
265  }
266 
267  return *this;
268 }
269 
271 {
272  d->m_color = color;
273 }
274 
276 {
277  return d->m_color;
278 }
279 
280 void Annotation::Style::setOpacity(double opacity)
281 {
282  d->m_opacity = opacity;
283 }
284 
286 {
287  return d->m_opacity;
288 }
289 
290 void Annotation::Style::setWidth(double width)
291 {
292  d->m_width = width;
293 }
294 
296 {
297  return d->m_width;
298 }
299 
301 {
302  d->m_style = style;
303 }
304 
306 {
307  return d->m_style;
308 }
309 
310 void Annotation::Style::setXCorners(double xCorners)
311 {
312  d->m_xCorners = xCorners;
313 }
314 
316 {
317  return d->m_xCorners;
318 }
319 
320 void Annotation::Style::setYCorners(double yCorners)
321 {
322  d->m_yCorners = yCorners;
323 }
324 
326 {
327  return d->m_yCorners;
328 }
329 
331 {
332  d->m_marks = marks;
333 }
334 
336 {
337  return d->m_marks;
338 }
339 
341 {
342  d->m_spaces = spaces;
343 }
344 
346 {
347  return d->m_spaces;
348 }
349 
351 {
352  d->m_effect = effect;
353 }
354 
356 {
357  return d->m_effect;
358 }
359 
361 {
362  d->m_effectIntensity = intensity;
363 }
364 
366 {
367  return d->m_effectIntensity;
368 }
369 
370 class Annotation::Window::Private
371 {
372 public:
373  Private()
374  : m_flags(-1)
375  , m_width(0)
376  , m_height(0)
377  {
378  }
379 
380  int m_flags;
381  NormalizedPoint m_topLeft;
382  int m_width;
383  int m_height;
384  QString m_title;
385  QString m_summary;
386 };
387 
389  : d(new Private)
390 {
391 }
392 
394 {
395  delete d;
396 }
397 
399  : d(new Private)
400 {
401  *d = *other.d;
402 }
403 
404 Annotation::Window &Annotation::Window::operator=(const Window &other)
405 {
406  if (this != &other) {
407  *d = *other.d;
408  }
409 
410  return *this;
411 }
412 
414 {
415  d->m_flags = flags;
416 }
417 
419 {
420  return d->m_flags;
421 }
422 
424 {
425  d->m_topLeft = point;
426 }
427 
429 {
430  return d->m_topLeft;
431 }
432 
434 {
435  d->m_width = width;
436 }
437 
439 {
440  return d->m_width;
441 }
442 
444 {
445  d->m_height = height;
446 }
447 
449 {
450  return d->m_height;
451 }
452 
454 {
455  d->m_title = title;
456 }
457 
459 {
460  return d->m_title;
461 }
462 
464 {
465  d->m_summary = summary;
466 }
467 
469 {
470  return d->m_summary;
471 }
472 
473 class Annotation::Revision::Private
474 {
475 public:
476  Private()
477  : m_annotation(nullptr)
478  , m_scope(Reply)
479  , m_type(None)
480  {
481  }
482 
483  Annotation *m_annotation;
484  RevisionScope m_scope;
485  RevisionType m_type;
486 };
487 
489  : d(new Private)
490 {
491 }
492 
494 {
495  delete d;
496 }
497 
499  : d(new Private)
500 {
501  *d = *other.d;
502 }
503 
504 Annotation::Revision &Annotation::Revision::operator=(const Revision &other)
505 {
506  if (this != &other) {
507  *d = *other.d;
508  }
509 
510  return *this;
511 }
512 
514 {
515  d->m_annotation = annotation;
516 }
517 
519 {
520  return d->m_annotation;
521 }
522 
524 {
525  d->m_scope = scope;
526 }
527 
529 {
530  return d->m_scope;
531 }
532 
534 {
535  d->m_type = type;
536 }
537 
539 {
540  return d->m_type;
541 }
542 
543 AnnotationPrivate::AnnotationPrivate()
544  : m_page(nullptr)
545  , m_flags(0)
546  , m_disposeFunc(nullptr)
547 {
548 }
549 
550 AnnotationPrivate::~AnnotationPrivate()
551 {
552  // delete all children revisions
553  if (m_revisions.isEmpty()) {
554  return;
555  }
556 
557  for (const Annotation::Revision &revision : qAsConst(m_revisions)) {
558  delete revision.annotation();
559  }
560 }
561 
562 AnnotationPrivate *AnnotationPrivate::get(Annotation *a)
563 {
564  return a ? a->d_ptr : nullptr;
565 }
566 
567 Annotation::Annotation(AnnotationPrivate &dd)
568  : d_ptr(&dd)
569 {
570 }
571 
572 Annotation::Annotation(AnnotationPrivate &dd, const QDomNode &description)
573  : d_ptr(&dd)
574 {
575  d_ptr->setAnnotationProperties(description);
576 }
577 
579 {
580  if (d_ptr->m_disposeFunc) {
581  d_ptr->m_disposeFunc(this);
582  }
583 
584  delete d_ptr;
585 }
586 
588 {
589  Q_D(Annotation);
590  d->m_author = author;
591 }
592 
594 {
595  Q_D(const Annotation);
596  return d->m_author;
597 }
598 
600 {
601  Q_D(Annotation);
602  d->m_contents = contents;
603 }
604 
606 {
607  Q_D(const Annotation);
608  return d->m_contents;
609 }
610 
612 {
613  Q_D(Annotation);
614  d->m_uniqueName = name;
615 }
616 
618 {
619  Q_D(const Annotation);
620  return d->m_uniqueName;
621 }
622 
624 {
625  Q_D(Annotation);
626  d->m_modifyDate = date;
627 }
628 
630 {
631  Q_D(const Annotation);
632  return d->m_modifyDate;
633 }
634 
636 {
637  Q_D(Annotation);
638  d->m_creationDate = date;
639 }
640 
642 {
643  Q_D(const Annotation);
644  return d->m_creationDate;
645 }
646 
648 {
649  Q_D(Annotation);
650  d->m_flags = flags;
651 }
652 
653 int Annotation::flags() const
654 {
655  Q_D(const Annotation);
656  return d->m_flags;
657 }
658 
660 {
661  Q_D(Annotation);
662  d->m_boundary = rectangle;
663  d->resetTransformation();
664  if (d->m_page) {
665  d->transform(d->m_page->rotationMatrix());
666  }
667 }
668 
670 {
671  Q_D(const Annotation);
672  return d->m_boundary;
673 }
674 
676 {
677  Q_D(const Annotation);
678  return d->m_transformedBoundary;
679 }
680 
682 {
683  Q_D(Annotation);
684  d->translate(coord);
685  d->resetTransformation();
686  if (d->m_page) {
687  d->transform(d->m_page->rotationMatrix());
688  }
689 }
690 
691 void Annotation::adjust(const NormalizedPoint &deltaCoord1, const NormalizedPoint &deltaCoord2)
692 {
693  Q_D(Annotation);
694  d->adjust(deltaCoord1, deltaCoord2);
695  d->resetTransformation();
696  if (d->m_page) {
697  d->transform(d->m_page->rotationMatrix());
698  }
699 }
700 
702 {
703  Q_D(const Annotation);
704  return d->openDialogAfterCreation();
705 }
706 
708 {
709  Q_D(Annotation);
710  return d->m_style;
711 }
712 
714 {
715  Q_D(const Annotation);
716  return d->m_style;
717 }
718 
720 {
721  Q_D(Annotation);
722  return d->m_window;
723 }
724 
726 {
727  Q_D(const Annotation);
728  return d->m_window;
729 }
730 
732 {
733  Q_D(Annotation);
734  return d->m_revisions;
735 }
736 
738 {
739  Q_D(const Annotation);
740  return d->m_revisions;
741 }
742 
744 {
745  Q_D(Annotation);
746  d->m_nativeId = id;
747 }
748 
750 {
751  Q_D(const Annotation);
752  return d->m_nativeId;
753 }
754 
756 {
757  Q_D(Annotation);
758  d->m_disposeFunc = func;
759 }
760 
762 {
763  Q_D(const Annotation);
764 
765  // Don't move annotations if they cannot be modified
766  if (!d->m_page || !d->m_page->m_doc->m_parent->canModifyPageAnnotation(this)) {
767  return false;
768  }
769 
770  // highlight "requires" to be "bounded" to text, and that's tricky for now
771  if (subType() == AHighlight) {
772  return false;
773  }
774 
775  return true;
776 }
777 
779 {
780  Q_D(const Annotation);
781 
782  // Don't resize annotations if they cannot be modified
783  if (!d->m_page || !d->m_page->m_doc->m_parent->canModifyPageAnnotation(this)) {
784  return false;
785  }
786 
787  return d->canBeResized();
788 }
789 
790 void Annotation::store(QDomNode &annNode, QDomDocument &document) const
791 {
792  Q_D(const Annotation);
793  // create [base] element of the annotation node
794  QDomElement e = document.createElement(QStringLiteral("base"));
795  annNode.appendChild(e);
796 
797  // store -contents- attributes
798  if (!d->m_author.isEmpty()) {
799  e.setAttribute(QStringLiteral("author"), d->m_author);
800  }
801  if (!d->m_contents.isEmpty()) {
802  e.setAttribute(QStringLiteral("contents"), d->m_contents);
803  }
804  if (!d->m_uniqueName.isEmpty()) {
805  e.setAttribute(QStringLiteral("uniqueName"), d->m_uniqueName);
806  }
807  if (d->m_modifyDate.isValid()) {
808  e.setAttribute(QStringLiteral("modifyDate"), d->m_modifyDate.toString(Qt::ISODate));
809  }
810  if (d->m_creationDate.isValid()) {
811  e.setAttribute(QStringLiteral("creationDate"), d->m_creationDate.toString(Qt::ISODate));
812  }
813 
814  // store -other- attributes
815  if (d->m_flags) { // Strip internal flags
816  e.setAttribute(QStringLiteral("flags"), d->m_flags & ~(External | ExternallyDrawn | BeingMoved | BeingResized));
817  }
818  if (d->m_style.color().isValid()) {
819  e.setAttribute(QStringLiteral("color"), d->m_style.color().name(QColor::HexArgb));
820  }
821  if (d->m_style.opacity() != 1.0) {
822  e.setAttribute(QStringLiteral("opacity"), QString::number(d->m_style.opacity()));
823  }
824 
825  // Sub-Node-1 - boundary
826  QDomElement bE = document.createElement(QStringLiteral("boundary"));
827  e.appendChild(bE);
828  bE.setAttribute(QStringLiteral("l"), QString::number(d->m_boundary.left));
829  bE.setAttribute(QStringLiteral("t"), QString::number(d->m_boundary.top));
830  bE.setAttribute(QStringLiteral("r"), QString::number(d->m_boundary.right));
831  bE.setAttribute(QStringLiteral("b"), QString::number(d->m_boundary.bottom));
832 
833  // Sub-Node-2 - penStyle
834  if (d->m_style.width() != 1 || d->m_style.lineStyle() != Solid || d->m_style.xCorners() != 0 || d->m_style.yCorners() != 0.0 || d->m_style.marks() != 3 || d->m_style.spaces() != 0) {
835  QDomElement psE = document.createElement(QStringLiteral("penStyle"));
836  e.appendChild(psE);
837  psE.setAttribute(QStringLiteral("width"), QString::number(d->m_style.width()));
838  psE.setAttribute(QStringLiteral("style"), (int)d->m_style.lineStyle());
839  psE.setAttribute(QStringLiteral("xcr"), QString::number(d->m_style.xCorners()));
840  psE.setAttribute(QStringLiteral("ycr"), QString::number(d->m_style.yCorners()));
841  psE.setAttribute(QStringLiteral("marks"), d->m_style.marks());
842  psE.setAttribute(QStringLiteral("spaces"), d->m_style.spaces());
843  }
844 
845  // Sub-Node-3 - penEffect
846  if (d->m_style.lineEffect() != NoEffect || d->m_style.effectIntensity() != 1.0) {
847  QDomElement peE = document.createElement(QStringLiteral("penEffect"));
848  e.appendChild(peE);
849  peE.setAttribute(QStringLiteral("effect"), (int)d->m_style.lineEffect());
850  peE.setAttribute(QStringLiteral("intensity"), QString::number(d->m_style.effectIntensity()));
851  }
852 
853  // Sub-Node-4 - window
854  if (d->m_window.flags() != -1 || !d->m_window.title().isEmpty() || !d->m_window.summary().isEmpty()) {
855  QDomElement wE = document.createElement(QStringLiteral("window"));
856  e.appendChild(wE);
857  wE.setAttribute(QStringLiteral("flags"), d->m_window.flags());
858  wE.setAttribute(QStringLiteral("top"), QString::number(d->m_window.topLeft().x));
859  wE.setAttribute(QStringLiteral("left"), QString::number(d->m_window.topLeft().y));
860  wE.setAttribute(QStringLiteral("width"), d->m_window.width());
861  wE.setAttribute(QStringLiteral("height"), d->m_window.height());
862  wE.setAttribute(QStringLiteral("title"), d->m_window.title());
863  wE.setAttribute(QStringLiteral("summary"), d->m_window.summary());
864  }
865 
866  // create [revision] element of the annotation node (if any)
867  if (d->m_revisions.isEmpty()) {
868  return;
869  }
870 
871  // add all revisions as children of revisions element
872  for (const Revision &revision : qAsConst(d->m_revisions)) {
873  // create revision element
874  QDomElement r = document.createElement(QStringLiteral("revision"));
875  annNode.appendChild(r);
876  // set element attributes
877  r.setAttribute(QStringLiteral("revScope"), (int)revision.scope());
878  r.setAttribute(QStringLiteral("revType"), (int)revision.type());
879  // use revision as the annotation element, so fill it up
880  AnnotationUtils::storeAnnotation(revision.annotation(), r, document);
881  }
882 }
883 
885 {
886  QDomDocument doc(QStringLiteral("documentInfo"));
887  QDomElement node = doc.createElement(QStringLiteral("annotation"));
888 
889  store(node, doc);
890  return node;
891 }
892 
894 {
895  // Save off internal properties that aren't contained in node
896  Okular::PagePrivate *p = d_ptr->m_page;
897  QVariant nativeID = d_ptr->m_nativeId;
898  const int internalFlags = d_ptr->m_flags & (External | ExternallyDrawn | BeingMoved | BeingResized);
899  Annotation::DisposeDataFunction disposeFunc = d_ptr->m_disposeFunc;
900 
901  // Replace AnnotationPrivate object with a fresh copy
902  AnnotationPrivate *new_d_ptr = d_ptr->getNewAnnotationPrivate();
903  delete (d_ptr);
904  d_ptr = new_d_ptr;
905 
906  // Set the annotations properties from node
907  d_ptr->setAnnotationProperties(node);
908 
909  // Restore internal properties
910  d_ptr->m_page = p;
911  d_ptr->m_nativeId = nativeID;
912  d_ptr->m_flags = d_ptr->m_flags | internalFlags;
913  d_ptr->m_disposeFunc = disposeFunc;
914 
915  // Transform annotation to current page rotation
916  d_ptr->transform(d_ptr->m_page->rotationMatrix());
917 }
918 
919 double AnnotationPrivate::distanceSqr(double x, double y, double xScale, double yScale) const
920 {
921  return m_transformedBoundary.distanceSqr(x, y, xScale, yScale);
922 }
923 
924 void AnnotationPrivate::annotationTransform(const QTransform &matrix)
925 {
926  resetTransformation();
927  transform(matrix);
928 }
929 
930 void AnnotationPrivate::transform(const QTransform &matrix)
931 {
932  m_transformedBoundary.transform(matrix);
933 }
934 
935 void AnnotationPrivate::baseTransform(const QTransform &matrix)
936 {
937  m_boundary.transform(matrix);
938 }
939 
940 void AnnotationPrivate::resetTransformation()
941 {
942  m_transformedBoundary = m_boundary;
943 }
944 
945 void AnnotationPrivate::translate(const NormalizedPoint &coord)
946 {
947  m_boundary.left = m_boundary.left + coord.x;
948  m_boundary.right = m_boundary.right + coord.x;
949  m_boundary.top = m_boundary.top + coord.y;
950  m_boundary.bottom = m_boundary.bottom + coord.y;
951 }
952 
953 void AnnotationPrivate::adjust(const NormalizedPoint &deltaCoord1, const NormalizedPoint &deltaCoord2)
954 {
955  m_boundary.left = m_boundary.left + qBound(-m_boundary.left, deltaCoord1.x, m_boundary.right - m_boundary.left);
956  m_boundary.top = m_boundary.top + qBound(-m_boundary.top, deltaCoord1.y, m_boundary.bottom - m_boundary.top);
957  ;
958  m_boundary.right = m_boundary.right + qBound(m_boundary.left - m_boundary.right, deltaCoord2.x, 1. - m_boundary.right);
959  m_boundary.bottom = m_boundary.bottom + qBound(m_boundary.top - m_boundary.bottom, deltaCoord2.y, 1. - m_boundary.bottom);
960 }
961 
962 bool AnnotationPrivate::openDialogAfterCreation() const
963 {
964  return false;
965 }
966 
967 void AnnotationPrivate::setAnnotationProperties(const QDomNode &node)
968 {
969  // get the [base] element of the annotation node
970  QDomElement e = AnnotationUtils::findChildElement(node, QStringLiteral("base"));
971  if (e.isNull()) {
972  return;
973  }
974 
975  // parse -contents- attributes
976  if (e.hasAttribute(QStringLiteral("author"))) {
977  m_author = e.attribute(QStringLiteral("author"));
978  }
979  if (e.hasAttribute(QStringLiteral("contents"))) {
980  m_contents = e.attribute(QStringLiteral("contents"));
981  }
982  if (e.hasAttribute(QStringLiteral("uniqueName"))) {
983  m_uniqueName = e.attribute(QStringLiteral("uniqueName"));
984  }
985  if (e.hasAttribute(QStringLiteral("modifyDate"))) {
986  m_modifyDate = QDateTime::fromString(e.attribute(QStringLiteral("modifyDate")), Qt::ISODate);
987  }
988  if (e.hasAttribute(QStringLiteral("creationDate"))) {
989  m_creationDate = QDateTime::fromString(e.attribute(QStringLiteral("creationDate")), Qt::ISODate);
990  }
991 
992  // parse -other- attributes
993  if (e.hasAttribute(QStringLiteral("flags"))) {
994  m_flags = e.attribute(QStringLiteral("flags")).toInt();
995  }
996  if (e.hasAttribute(QStringLiteral("color"))) {
997  m_style.setColor(QColor(e.attribute(QStringLiteral("color"))));
998  }
999  if (e.hasAttribute(QStringLiteral("opacity"))) {
1000  m_style.setOpacity(e.attribute(QStringLiteral("opacity")).toDouble());
1001  }
1002 
1003  // parse -the-subnodes- (describing Style, Window, Revision(s) structures)
1004  // Note: all subnodes if present must be 'attributes complete'
1005  QDomNode eSubNode = e.firstChild();
1006  while (eSubNode.isElement()) {
1007  QDomElement ee = eSubNode.toElement();
1008  eSubNode = eSubNode.nextSibling();
1009 
1010  // parse boundary
1011  if (ee.tagName() == QLatin1String("boundary")) {
1012  m_boundary = NormalizedRect(ee.attribute(QStringLiteral("l")).toDouble(), ee.attribute(QStringLiteral("t")).toDouble(), ee.attribute(QStringLiteral("r")).toDouble(), ee.attribute(QStringLiteral("b")).toDouble());
1013  }
1014  // parse penStyle if not default
1015  else if (ee.tagName() == QLatin1String("penStyle")) {
1016  m_style.setWidth(ee.attribute(QStringLiteral("width")).toDouble());
1017  m_style.setLineStyle((Annotation::LineStyle)ee.attribute(QStringLiteral("style")).toInt());
1018  m_style.setXCorners(ee.attribute(QStringLiteral("xcr")).toDouble());
1019  m_style.setYCorners(ee.attribute(QStringLiteral("ycr")).toDouble());
1020  m_style.setMarks(ee.attribute(QStringLiteral("marks")).toInt());
1021  m_style.setSpaces(ee.attribute(QStringLiteral("spaces")).toInt());
1022  }
1023  // parse effectStyle if not default
1024  else if (ee.tagName() == QLatin1String("penEffect")) {
1025  m_style.setLineEffect((Annotation::LineEffect)ee.attribute(QStringLiteral("effect")).toInt());
1026  m_style.setEffectIntensity(ee.attribute(QStringLiteral("intensity")).toDouble());
1027  }
1028  // parse window if present
1029  else if (ee.tagName() == QLatin1String("window")) {
1030  m_window.setFlags(ee.attribute(QStringLiteral("flags")).toInt());
1031  m_window.setTopLeft(NormalizedPoint(ee.attribute(QStringLiteral("top")).toDouble(), ee.attribute(QStringLiteral("left")).toDouble()));
1032  m_window.setWidth(ee.attribute(QStringLiteral("width")).toInt());
1033  m_window.setHeight(ee.attribute(QStringLiteral("height")).toInt());
1034  m_window.setTitle(ee.attribute(QStringLiteral("title")));
1035  m_window.setSummary(ee.attribute(QStringLiteral("summary")));
1036  }
1037  }
1038 
1039  // get the [revisions] element of the annotation node
1040  QDomNode revNode = node.firstChild();
1041  for (; revNode.isElement(); revNode = revNode.nextSibling()) {
1042  QDomElement revElement = revNode.toElement();
1043  if (revElement.tagName() != QLatin1String("revision")) {
1044  continue;
1045  }
1046 
1047  // compile the Revision structure crating annotation
1048  Annotation::Revision revision;
1049  revision.setScope((Annotation::RevisionScope)revElement.attribute(QStringLiteral("revScope")).toInt());
1050  revision.setType((Annotation::RevisionType)revElement.attribute(QStringLiteral("revType")).toInt());
1051  revision.setAnnotation(AnnotationUtils::createAnnotation(revElement));
1052 
1053  // if annotation is valid, add revision to internal list
1054  if (revision.annotation()) {
1055  m_revisions.append(revision);
1056  }
1057  }
1058 
1059  m_transformedBoundary = m_boundary;
1060 }
1061 
1062 bool AnnotationPrivate::canBeResized() const
1063 {
1064  return false;
1065 }
1066 
1067 // END Annotation implementation
1068 
1069 /** TextAnnotation [Annotation] */
1070 
1071 class Okular::TextAnnotationPrivate : public Okular::AnnotationPrivate
1072 {
1073 public:
1074  TextAnnotationPrivate()
1075  : AnnotationPrivate()
1076  , m_textType(TextAnnotation::Linked)
1077  , m_textIcon(QStringLiteral("Comment"))
1078  , m_inplaceAlign(0)
1079  , m_inplaceIntent(TextAnnotation::Unknown)
1080  {
1081  }
1082 
1083  void transform(const QTransform &matrix) override;
1084  void baseTransform(const QTransform &matrix) override;
1085  void resetTransformation() override;
1086  void translate(const NormalizedPoint &coord) override;
1087  bool openDialogAfterCreation() const override;
1088  void setAnnotationProperties(const QDomNode &node) override;
1089  bool canBeResized() const override;
1090  AnnotationPrivate *getNewAnnotationPrivate() override;
1091 
1092  TextAnnotation::TextType m_textType;
1093  QString m_textIcon;
1094  QFont m_textFont;
1095  QColor m_textColor;
1096  int m_inplaceAlign;
1097  NormalizedPoint m_inplaceCallout[3];
1098  NormalizedPoint m_transformedInplaceCallout[3];
1099  TextAnnotation::InplaceIntent m_inplaceIntent;
1100 };
1101 
1102 /*
1103  The default textIcon for text annotation is Note as the PDF Reference says
1104 */
1105 TextAnnotation::TextAnnotation()
1106  : Annotation(*new TextAnnotationPrivate())
1107 {
1108 }
1109 
1110 TextAnnotation::TextAnnotation(const QDomNode &description)
1111  : Annotation(*new TextAnnotationPrivate(), description)
1112 {
1113 }
1114 
1115 TextAnnotation::~TextAnnotation()
1116 {
1117 }
1118 
1119 void TextAnnotation::setTextType(TextType textType)
1120 {
1121  Q_D(TextAnnotation);
1122  d->m_textType = textType;
1123 }
1124 
1125 TextAnnotation::TextType TextAnnotation::textType() const
1126 {
1127  Q_D(const TextAnnotation);
1128  return d->m_textType;
1129 }
1130 
1131 void TextAnnotation::setTextIcon(const QString &icon)
1132 {
1133  Q_D(TextAnnotation);
1134  d->m_textIcon = icon;
1135 }
1136 
1137 QString TextAnnotation::textIcon() const
1138 {
1139  Q_D(const TextAnnotation);
1140  return d->m_textIcon;
1141 }
1142 
1143 void TextAnnotation::setTextFont(const QFont &font)
1144 {
1145  Q_D(TextAnnotation);
1146  d->m_textFont = font;
1147 }
1148 
1149 QFont TextAnnotation::textFont() const
1150 {
1151  Q_D(const TextAnnotation);
1152  return d->m_textFont;
1153 }
1154 
1155 void TextAnnotation::setTextColor(const QColor &color)
1156 {
1157  Q_D(TextAnnotation);
1158  d->m_textColor = color;
1159 }
1160 
1161 QColor TextAnnotation::textColor() const
1162 {
1163  Q_D(const TextAnnotation);
1164  return d->m_textColor;
1165 }
1166 
1167 void TextAnnotation::setInplaceAlignment(int alignment)
1168 {
1169  Q_D(TextAnnotation);
1170  d->m_inplaceAlign = alignment;
1171 }
1172 
1173 int TextAnnotation::inplaceAlignment() const
1174 {
1175  Q_D(const TextAnnotation);
1176  return d->m_inplaceAlign;
1177 }
1178 
1179 void TextAnnotation::setInplaceCallout(const NormalizedPoint &point, int index)
1180 {
1181  if (index < 0 || index > 2) {
1182  return;
1183  }
1184 
1185  Q_D(TextAnnotation);
1186  d->m_inplaceCallout[index] = point;
1187 }
1188 
1189 NormalizedPoint TextAnnotation::inplaceCallout(int index) const
1190 {
1191  if (index < 0 || index > 2) {
1192  return NormalizedPoint();
1193  }
1194 
1195  Q_D(const TextAnnotation);
1196  return d->m_inplaceCallout[index];
1197 }
1198 
1199 NormalizedPoint TextAnnotation::transformedInplaceCallout(int index) const
1200 {
1201  if (index < 0 || index > 2) {
1202  return NormalizedPoint();
1203  }
1204 
1205  Q_D(const TextAnnotation);
1206  return d->m_transformedInplaceCallout[index];
1207 }
1208 
1209 void TextAnnotation::setInplaceIntent(InplaceIntent intent)
1210 {
1211  Q_D(TextAnnotation);
1212  d->m_inplaceIntent = intent;
1213 }
1214 
1215 TextAnnotation::InplaceIntent TextAnnotation::inplaceIntent() const
1216 {
1217  Q_D(const TextAnnotation);
1218  return d->m_inplaceIntent;
1219 }
1220 
1221 Annotation::SubType TextAnnotation::subType() const
1222 {
1223  return AText;
1224 }
1225 
1226 void TextAnnotation::store(QDomNode &node, QDomDocument &document) const
1227 {
1228  Q_D(const TextAnnotation);
1229  // recurse to parent objects storing properties
1230  Annotation::store(node, document);
1231 
1232  // create [text] element
1233  QDomElement textElement = document.createElement(QStringLiteral("text"));
1234  node.appendChild(textElement);
1235 
1236  // store the optional attributes
1237  if (d->m_textType != Linked) {
1238  textElement.setAttribute(QStringLiteral("type"), (int)d->m_textType);
1239  }
1240  if (!d->m_textIcon.isEmpty()) {
1241  textElement.setAttribute(QStringLiteral("icon"), d->m_textIcon);
1242  }
1243  if (d->m_textFont != QApplication::font()) {
1244  textElement.setAttribute(QStringLiteral("font"), d->m_textFont.toString());
1245  }
1246  if (d->m_textColor.isValid()) {
1247  textElement.setAttribute(QStringLiteral("fontColor"), d->m_textColor.name());
1248  }
1249  if (d->m_inplaceAlign) {
1250  textElement.setAttribute(QStringLiteral("align"), d->m_inplaceAlign);
1251  }
1252  if (d->m_inplaceIntent != Unknown) {
1253  textElement.setAttribute(QStringLiteral("intent"), (int)d->m_inplaceIntent);
1254  }
1255 
1256  // Sub-Node - callout
1257  if (d->m_inplaceCallout[0].x != 0.0) {
1258  QDomElement calloutElement = document.createElement(QStringLiteral("callout"));
1259  textElement.appendChild(calloutElement);
1260  calloutElement.setAttribute(QStringLiteral("ax"), QString::number(d->m_inplaceCallout[0].x));
1261  calloutElement.setAttribute(QStringLiteral("ay"), QString::number(d->m_inplaceCallout[0].y));
1262  calloutElement.setAttribute(QStringLiteral("bx"), QString::number(d->m_inplaceCallout[1].x));
1263  calloutElement.setAttribute(QStringLiteral("by"), QString::number(d->m_inplaceCallout[1].y));
1264  calloutElement.setAttribute(QStringLiteral("cx"), QString::number(d->m_inplaceCallout[2].x));
1265  calloutElement.setAttribute(QStringLiteral("cy"), QString::number(d->m_inplaceCallout[2].y));
1266  }
1267 }
1268 
1269 void TextAnnotationPrivate::transform(const QTransform &matrix)
1270 {
1271  AnnotationPrivate::transform(matrix);
1272 
1273  for (NormalizedPoint &np : m_transformedInplaceCallout) {
1274  np.transform(matrix);
1275  }
1276 }
1277 
1278 void TextAnnotationPrivate::baseTransform(const QTransform &matrix)
1279 {
1280  AnnotationPrivate::baseTransform(matrix);
1281 
1282  for (NormalizedPoint &np : m_inplaceCallout) {
1283  np.transform(matrix);
1284  }
1285 }
1286 
1287 void TextAnnotationPrivate::resetTransformation()
1288 {
1289  AnnotationPrivate::resetTransformation();
1290 
1291  for (int i = 0; i < 3; ++i) {
1292  m_transformedInplaceCallout[i] = m_inplaceCallout[i];
1293  }
1294 }
1295 
1296 void TextAnnotationPrivate::translate(const NormalizedPoint &coord)
1297 {
1298  AnnotationPrivate::translate(coord);
1299 
1300 #define ADD_COORD(c1, c2) \
1301  { \
1302  c1.x = c1.x + c2.x; \
1303  c1.y = c1.y + c2.y; \
1304  }
1305  ADD_COORD(m_inplaceCallout[0], coord)
1306  ADD_COORD(m_inplaceCallout[1], coord)
1307  ADD_COORD(m_inplaceCallout[2], coord)
1308 #undef ADD_COORD
1309 }
1310 
1311 bool TextAnnotationPrivate::openDialogAfterCreation() const
1312 {
1313  return (m_textType == Okular::TextAnnotation::Linked) || (m_inplaceIntent == TextAnnotation::InplaceIntent::Unknown);
1314 }
1315 
1316 void TextAnnotationPrivate::setAnnotationProperties(const QDomNode &node)
1317 {
1318  Okular::AnnotationPrivate::setAnnotationProperties(node);
1319 
1320  // loop through the whole children looking for a 'text' element
1321  QDomNode subNode = node.firstChild();
1322  while (subNode.isElement()) {
1323  QDomElement e = subNode.toElement();
1324  subNode = subNode.nextSibling();
1325  if (e.tagName() != QLatin1String("text")) {
1326  continue;
1327  }
1328 
1329  // parse the attributes
1330  if (e.hasAttribute(QStringLiteral("type"))) {
1331  m_textType = (TextAnnotation::TextType)e.attribute(QStringLiteral("type")).toInt();
1332  }
1333  if (e.hasAttribute(QStringLiteral("icon"))) {
1334  m_textIcon = e.attribute(QStringLiteral("icon"));
1335  }
1336  if (e.hasAttribute(QStringLiteral("font"))) {
1337  m_textFont.fromString(e.attribute(QStringLiteral("font")));
1338  }
1339  if (e.hasAttribute(QStringLiteral("fontColor"))) {
1340  m_textColor = QColor(e.attribute(QStringLiteral("fontColor")));
1341  }
1342  if (e.hasAttribute(QStringLiteral("align"))) {
1343  m_inplaceAlign = e.attribute(QStringLiteral("align")).toInt();
1344  }
1345  if (e.hasAttribute(QStringLiteral("intent"))) {
1346  m_inplaceIntent = (TextAnnotation::InplaceIntent)e.attribute(QStringLiteral("intent")).toInt();
1347  }
1348 
1349  // parse the subnodes
1350  QDomNode eSubNode = e.firstChild();
1351  while (eSubNode.isElement()) {
1352  QDomElement ee = eSubNode.toElement();
1353  eSubNode = eSubNode.nextSibling();
1354 
1355  if (ee.tagName() == QLatin1String("escapedText")) {
1356  m_contents = ee.firstChild().toCDATASection().data();
1357  } else if (ee.tagName() == QLatin1String("callout")) {
1358  m_inplaceCallout[0].x = ee.attribute(QStringLiteral("ax")).toDouble();
1359  m_inplaceCallout[0].y = ee.attribute(QStringLiteral("ay")).toDouble();
1360  m_inplaceCallout[1].x = ee.attribute(QStringLiteral("bx")).toDouble();
1361  m_inplaceCallout[1].y = ee.attribute(QStringLiteral("by")).toDouble();
1362  m_inplaceCallout[2].x = ee.attribute(QStringLiteral("cx")).toDouble();
1363  m_inplaceCallout[2].y = ee.attribute(QStringLiteral("cy")).toDouble();
1364  }
1365  }
1366 
1367  // loading complete
1368  break;
1369  }
1370 
1371  for (int i = 0; i < 3; ++i) {
1372  m_transformedInplaceCallout[i] = m_inplaceCallout[i];
1373  }
1374 }
1375 
1376 bool TextAnnotationPrivate::canBeResized() const
1377 {
1378  if (m_textType != TextAnnotation::Linked) {
1379  return true;
1380  }
1381  return false;
1382 }
1383 
1384 AnnotationPrivate *TextAnnotationPrivate::getNewAnnotationPrivate()
1385 {
1386  return new TextAnnotationPrivate();
1387 }
1388 
1389 /** LineAnnotation [Annotation] */
1390 
1391 class Okular::LineAnnotationPrivate : public Okular::AnnotationPrivate
1392 {
1393 public:
1394  LineAnnotationPrivate()
1395  : AnnotationPrivate()
1396  , m_lineStartStyle(LineAnnotation::None)
1397  , m_lineEndStyle(LineAnnotation::None)
1398  , m_lineClosed(false)
1399  , m_lineShowCaption(false)
1400  , m_lineLeadingFwdPt(0)
1401  , m_lineLeadingBackPt(0)
1402  , m_lineIntent(LineAnnotation::Unknown)
1403  {
1404  }
1405 
1406  void transform(const QTransform &matrix) override;
1407  void baseTransform(const QTransform &matrix) override;
1408  void resetTransformation() override;
1409  void translate(const NormalizedPoint &coord) override;
1410  double distanceSqr(double x, double y, double xScale, double yScale) const override;
1411  void setAnnotationProperties(const QDomNode &node) override;
1412  AnnotationPrivate *getNewAnnotationPrivate() override;
1413 
1414  QList<NormalizedPoint> m_linePoints;
1415  QList<NormalizedPoint> m_transformedLinePoints;
1416  LineAnnotation::TermStyle m_lineStartStyle;
1417  LineAnnotation::TermStyle m_lineEndStyle;
1418  bool m_lineClosed : 1;
1419  bool m_lineShowCaption : 1;
1420  QColor m_lineInnerColor;
1421  double m_lineLeadingFwdPt;
1422  double m_lineLeadingBackPt;
1423  LineAnnotation::LineIntent m_lineIntent;
1424 };
1425 
1426 LineAnnotation::LineAnnotation()
1427  : Annotation(*new LineAnnotationPrivate())
1428 {
1429 }
1430 
1431 LineAnnotation::LineAnnotation(const QDomNode &description)
1432  : Annotation(*new LineAnnotationPrivate(), description)
1433 {
1434 }
1435 
1436 LineAnnotation::~LineAnnotation()
1437 {
1438 }
1439 
1440 void LineAnnotation::setLinePoints(const QList<NormalizedPoint> &points)
1441 {
1442  Q_D(LineAnnotation);
1443  d->m_linePoints = points;
1444 }
1445 
1446 QList<NormalizedPoint> LineAnnotation::linePoints() const
1447 {
1448  Q_D(const LineAnnotation);
1449  return d->m_linePoints;
1450 }
1451 
1452 QList<NormalizedPoint> LineAnnotation::transformedLinePoints() const
1453 {
1454  Q_D(const LineAnnotation);
1455  return d->m_transformedLinePoints;
1456 }
1457 
1458 void LineAnnotation::setLineStartStyle(TermStyle style)
1459 {
1460  Q_D(LineAnnotation);
1461  d->m_lineStartStyle = style;
1462 }
1463 
1464 LineAnnotation::TermStyle LineAnnotation::lineStartStyle() const
1465 {
1466  Q_D(const LineAnnotation);
1467  return d->m_lineStartStyle;
1468 }
1469 
1470 void LineAnnotation::setLineEndStyle(TermStyle style)
1471 {
1472  Q_D(LineAnnotation);
1473  d->m_lineEndStyle = style;
1474 }
1475 
1476 LineAnnotation::TermStyle LineAnnotation::lineEndStyle() const
1477 {
1478  Q_D(const LineAnnotation);
1479  return d->m_lineEndStyle;
1480 }
1481 
1482 void LineAnnotation::setLineClosed(bool closed)
1483 {
1484  Q_D(LineAnnotation);
1485  d->m_lineClosed = closed;
1486 }
1487 
1488 bool LineAnnotation::lineClosed() const
1489 {
1490  Q_D(const LineAnnotation);
1491  return d->m_lineClosed;
1492 }
1493 
1494 void LineAnnotation::setLineInnerColor(const QColor &color)
1495 {
1496  Q_D(LineAnnotation);
1497  d->m_lineInnerColor = color;
1498 }
1499 
1500 QColor LineAnnotation::lineInnerColor() const
1501 {
1502  Q_D(const LineAnnotation);
1503  return d->m_lineInnerColor;
1504 }
1505 
1506 void LineAnnotation::setLineLeadingForwardPoint(double point)
1507 {
1508  Q_D(LineAnnotation);
1509  d->m_lineLeadingFwdPt = point;
1510 }
1511 
1512 double LineAnnotation::lineLeadingForwardPoint() const
1513 {
1514  Q_D(const LineAnnotation);
1515  return d->m_lineLeadingFwdPt;
1516 }
1517 
1518 void LineAnnotation::setLineLeadingBackwardPoint(double point)
1519 {
1520  Q_D(LineAnnotation);
1521  d->m_lineLeadingBackPt = point;
1522 }
1523 
1524 double LineAnnotation::lineLeadingBackwardPoint() const
1525 {
1526  Q_D(const LineAnnotation);
1527  return d->m_lineLeadingBackPt;
1528 }
1529 
1530 void LineAnnotation::setShowCaption(bool show)
1531 {
1532  Q_D(LineAnnotation);
1533  d->m_lineShowCaption = show;
1534 }
1535 
1536 bool LineAnnotation::showCaption() const
1537 {
1538  Q_D(const LineAnnotation);
1539  return d->m_lineShowCaption;
1540 }
1541 
1542 void LineAnnotation::setLineIntent(LineIntent intent)
1543 {
1544  Q_D(LineAnnotation);
1545  d->m_lineIntent = intent;
1546 }
1547 
1548 LineAnnotation::LineIntent LineAnnotation::lineIntent() const
1549 {
1550  Q_D(const LineAnnotation);
1551  return d->m_lineIntent;
1552 }
1553 
1554 Annotation::SubType LineAnnotation::subType() const
1555 {
1556  return ALine;
1557 }
1558 
1559 void LineAnnotation::store(QDomNode &node, QDomDocument &document) const
1560 {
1561  Q_D(const LineAnnotation);
1562  // recurse to parent objects storing properties
1563  Annotation::store(node, document);
1564 
1565  // create [line] element
1566  QDomElement lineElement = document.createElement(QStringLiteral("line"));
1567  node.appendChild(lineElement);
1568 
1569  // store the attributes
1570  if (d->m_lineStartStyle != None) {
1571  lineElement.setAttribute(QStringLiteral("startStyle"), (int)d->m_lineStartStyle);
1572  }
1573  if (d->m_lineEndStyle != None) {
1574  lineElement.setAttribute(QStringLiteral("endStyle"), (int)d->m_lineEndStyle);
1575  }
1576  if (d->m_lineClosed) {
1577  lineElement.setAttribute(QStringLiteral("closed"), d->m_lineClosed);
1578  }
1579  if (d->m_lineInnerColor.isValid()) {
1580  lineElement.setAttribute(QStringLiteral("innerColor"), d->m_lineInnerColor.name());
1581  }
1582  if (d->m_lineLeadingFwdPt != 0.0) {
1583  lineElement.setAttribute(QStringLiteral("leadFwd"), QString::number(d->m_lineLeadingFwdPt));
1584  }
1585  if (d->m_lineLeadingBackPt != 0.0) {
1586  lineElement.setAttribute(QStringLiteral("leadBack"), QString::number(d->m_lineLeadingBackPt));
1587  }
1588  if (d->m_lineShowCaption) {
1589  lineElement.setAttribute(QStringLiteral("showCaption"), d->m_lineShowCaption);
1590  }
1591  if (d->m_lineIntent != Unknown) {
1592  lineElement.setAttribute(QStringLiteral("intent"), d->m_lineIntent);
1593  }
1594 
1595  // append the list of points
1596  int points = d->m_linePoints.count();
1597  if (points > 1) {
1598  QList<NormalizedPoint>::const_iterator it = d->m_linePoints.begin(), end = d->m_linePoints.end();
1599  while (it != end) {
1600  const NormalizedPoint &p = *it;
1601  QDomElement pElement = document.createElement(QStringLiteral("point"));
1602  lineElement.appendChild(pElement);
1603  pElement.setAttribute(QStringLiteral("x"), QString::number(p.x));
1604  pElement.setAttribute(QStringLiteral("y"), QString::number(p.y));
1605  it++; // to avoid loop
1606  }
1607  }
1608 }
1609 
1610 void LineAnnotationPrivate::transform(const QTransform &matrix)
1611 {
1612  AnnotationPrivate::transform(matrix);
1613 
1614  QMutableListIterator<NormalizedPoint> it(m_transformedLinePoints);
1615  while (it.hasNext()) {
1616  it.next().transform(matrix);
1617  }
1618 }
1619 
1620 void LineAnnotationPrivate::baseTransform(const QTransform &matrix)
1621 {
1622  AnnotationPrivate::baseTransform(matrix);
1623 
1624  QMutableListIterator<NormalizedPoint> it(m_linePoints);
1625  while (it.hasNext()) {
1626  it.next().transform(matrix);
1627  }
1628 }
1629 
1630 void LineAnnotationPrivate::resetTransformation()
1631 {
1632  AnnotationPrivate::resetTransformation();
1633 
1634  m_transformedLinePoints = m_linePoints;
1635 }
1636 
1637 void LineAnnotationPrivate::translate(const NormalizedPoint &coord)
1638 {
1639  AnnotationPrivate::translate(coord);
1640 
1641  QMutableListIterator<NormalizedPoint> it(m_linePoints);
1642  while (it.hasNext()) {
1643  NormalizedPoint &p = it.next();
1644  p.x = p.x + coord.x;
1645  p.y = p.y + coord.y;
1646  }
1647 }
1648 
1649 void LineAnnotationPrivate::setAnnotationProperties(const QDomNode &node)
1650 {
1651  Okular::AnnotationPrivate::setAnnotationProperties(node);
1652 
1653  // loop through the whole children looking for a 'line' element
1654  QDomNode subNode = node.firstChild();
1655  while (subNode.isElement()) {
1656  QDomElement e = subNode.toElement();
1657  subNode = subNode.nextSibling();
1658  if (e.tagName() != QLatin1String("line")) {
1659  continue;
1660  }
1661 
1662  // parse the attributes
1663  if (e.hasAttribute(QStringLiteral("startStyle"))) {
1664  m_lineStartStyle = (LineAnnotation::TermStyle)e.attribute(QStringLiteral("startStyle")).toInt();
1665  }
1666  if (e.hasAttribute(QStringLiteral("endStyle"))) {
1667  m_lineEndStyle = (LineAnnotation::TermStyle)e.attribute(QStringLiteral("endStyle")).toInt();
1668  }
1669  if (e.hasAttribute(QStringLiteral("closed"))) {
1670  m_lineClosed = e.attribute(QStringLiteral("closed")).toInt();
1671  }
1672  if (e.hasAttribute(QStringLiteral("innerColor"))) {
1673  m_lineInnerColor = QColor(e.attribute(QStringLiteral("innerColor")));
1674  }
1675  if (e.hasAttribute(QStringLiteral("leadFwd"))) {
1676  m_lineLeadingFwdPt = e.attribute(QStringLiteral("leadFwd")).toDouble();
1677  }
1678  if (e.hasAttribute(QStringLiteral("leadBack"))) {
1679  m_lineLeadingBackPt = e.attribute(QStringLiteral("leadBack")).toDouble();
1680  }
1681  if (e.hasAttribute(QStringLiteral("showCaption"))) {
1682  m_lineShowCaption = e.attribute(QStringLiteral("showCaption")).toInt();
1683  }
1684  if (e.hasAttribute(QStringLiteral("intent"))) {
1685  m_lineIntent = (LineAnnotation::LineIntent)e.attribute(QStringLiteral("intent")).toInt();
1686  }
1687 
1688  // parse all 'point' subnodes
1689  QDomNode pointNode = e.firstChild();
1690  while (pointNode.isElement()) {
1691  QDomElement pe = pointNode.toElement();
1692  pointNode = pointNode.nextSibling();
1693 
1694  if (pe.tagName() != QLatin1String("point")) {
1695  continue;
1696  }
1697 
1698  NormalizedPoint p;
1699  p.x = pe.attribute(QStringLiteral("x"), QStringLiteral("0.0")).toDouble();
1700  p.y = pe.attribute(QStringLiteral("y"), QStringLiteral("0.0")).toDouble();
1701  m_linePoints.append(p);
1702  }
1703 
1704  // loading complete
1705  break;
1706  }
1707 
1708  m_transformedLinePoints = m_linePoints;
1709 }
1710 
1711 AnnotationPrivate *LineAnnotationPrivate::getNewAnnotationPrivate()
1712 {
1713  return new LineAnnotationPrivate();
1714 }
1715 
1716 double LineAnnotationPrivate::distanceSqr(double x, double y, double xScale, double yScale) const
1717 {
1718  QList<NormalizedPoint> transformedLinePoints = m_transformedLinePoints;
1719 
1720  if (m_lineClosed) { // Close the path
1721  transformedLinePoints.append(transformedLinePoints.first());
1722  }
1723 
1724  if (m_lineInnerColor.isValid()) {
1725  QPolygonF polygon;
1726  for (const NormalizedPoint &p : qAsConst(transformedLinePoints)) {
1727  polygon.append(QPointF(p.x, p.y));
1728  }
1729 
1730  if (polygon.containsPoint(QPointF(x, y), Qt::WindingFill)) {
1731  return 0;
1732  }
1733  }
1734 
1735  return strokeDistance(::distanceSqr(x, y, xScale, yScale, transformedLinePoints), m_style.width() * xScale / (m_page->m_width * 2));
1736 }
1737 
1738 /** GeomAnnotation [Annotation] */
1739 
1740 class Okular::GeomAnnotationPrivate : public Okular::AnnotationPrivate
1741 {
1742 public:
1743  GeomAnnotationPrivate()
1744  : AnnotationPrivate()
1745  , m_geomType(GeomAnnotation::InscribedSquare)
1746  {
1747  }
1748  void setAnnotationProperties(const QDomNode &node) override;
1749  bool canBeResized() const override;
1750  AnnotationPrivate *getNewAnnotationPrivate() override;
1751  double distanceSqr(double x, double y, double xScale, double yScale) const override;
1752 
1753  GeomAnnotation::GeomType m_geomType;
1754  QColor m_geomInnerColor;
1755 };
1756 
1757 GeomAnnotation::GeomAnnotation()
1758  : Annotation(*new GeomAnnotationPrivate())
1759 {
1760 }
1761 
1762 GeomAnnotation::GeomAnnotation(const QDomNode &description)
1763  : Annotation(*new GeomAnnotationPrivate(), description)
1764 {
1765 }
1766 
1767 GeomAnnotation::~GeomAnnotation()
1768 {
1769 }
1770 
1771 void GeomAnnotation::setGeometricalType(GeomType type)
1772 {
1773  Q_D(GeomAnnotation);
1774  d->m_geomType = type;
1775 }
1776 
1777 GeomAnnotation::GeomType GeomAnnotation::geometricalType() const
1778 {
1779  Q_D(const GeomAnnotation);
1780  return d->m_geomType;
1781 }
1782 
1783 void GeomAnnotation::setGeometricalInnerColor(const QColor &color)
1784 {
1785  Q_D(GeomAnnotation);
1786  d->m_geomInnerColor = color;
1787 }
1788 
1789 QColor GeomAnnotation::geometricalInnerColor() const
1790 {
1791  Q_D(const GeomAnnotation);
1792  return d->m_geomInnerColor;
1793 }
1794 
1795 Annotation::SubType GeomAnnotation::subType() const
1796 {
1797  return AGeom;
1798 }
1799 
1800 void GeomAnnotation::store(QDomNode &node, QDomDocument &document) const
1801 {
1802  Q_D(const GeomAnnotation);
1803  // recurse to parent objects storing properties
1804  Annotation::store(node, document);
1805 
1806  // create [geom] element
1807  QDomElement geomElement = document.createElement(QStringLiteral("geom"));
1808  node.appendChild(geomElement);
1809 
1810  // append the optional attributes
1811  if (d->m_geomType != InscribedSquare) {
1812  geomElement.setAttribute(QStringLiteral("type"), (int)d->m_geomType);
1813  }
1814  if (d->m_geomInnerColor.isValid()) {
1815  geomElement.setAttribute(QStringLiteral("color"), d->m_geomInnerColor.name());
1816  }
1817 }
1818 
1819 void GeomAnnotationPrivate::setAnnotationProperties(const QDomNode &node)
1820 {
1821  Okular::AnnotationPrivate::setAnnotationProperties(node);
1822  // loop through the whole children looking for a 'geom' element
1823  QDomNode subNode = node.firstChild();
1824  while (subNode.isElement()) {
1825  QDomElement e = subNode.toElement();
1826  subNode = subNode.nextSibling();
1827  if (e.tagName() != QLatin1String("geom")) {
1828  continue;
1829  }
1830 
1831  // parse the attributes
1832  if (e.hasAttribute(QStringLiteral("type"))) {
1833  m_geomType = (GeomAnnotation::GeomType)e.attribute(QStringLiteral("type")).toInt();
1834  }
1835  if (e.hasAttribute(QStringLiteral("color"))) {
1836  m_geomInnerColor = QColor(e.attribute(QStringLiteral("color")));
1837  }
1838  // compatibility
1839  if (e.hasAttribute(QStringLiteral("width"))) {
1840  m_style.setWidth(e.attribute(QStringLiteral("width")).toInt());
1841  }
1842 
1843  // loading complete
1844  break;
1845  }
1846 }
1847 
1848 bool GeomAnnotationPrivate::canBeResized() const
1849 {
1850  return true;
1851 }
1852 
1853 AnnotationPrivate *GeomAnnotationPrivate::getNewAnnotationPrivate()
1854 {
1855  return new GeomAnnotationPrivate();
1856 }
1857 
1858 double GeomAnnotationPrivate::distanceSqr(double x, double y, double xScale, double yScale) const
1859 {
1860  double distance = 0;
1861  // the line thickness is applied unevenly (only on the "inside") - account for this
1862  bool withinShape = false;
1863  switch (m_geomType) {
1864  case GeomAnnotation::InscribedCircle: {
1865  // calculate the center point and focus lengths of the ellipse
1866  const double centerX = (m_transformedBoundary.left + m_transformedBoundary.right) / 2.0;
1867  const double centerY = (m_transformedBoundary.top + m_transformedBoundary.bottom) / 2.0;
1868  const double focusX = (m_transformedBoundary.right - centerX);
1869  const double focusY = (m_transformedBoundary.bottom - centerY);
1870 
1871  const double focusXSqr = pow(focusX, 2);
1872  const double focusYSqr = pow(focusY, 2);
1873 
1874  // to calculate the distance from the ellipse, we will first find the point "projection"
1875  // that lies on the ellipse and is closest to the point (x,y)
1876  // This point can obviously be written as "center + lambda(inputPoint - center)".
1877  // Because the point lies on the ellipse, we know that:
1878  // 1 = ((center.x - projection.x)/focusX)^2 + ((center.y - projection.y)/focusY)^2
1879  // After filling in projection.x = center.x + lambda * (inputPoint.x - center.x)
1880  // and its y-equivalent, we can solve for lambda:
1881  const double lambda = sqrt(focusXSqr * focusYSqr / (focusYSqr * pow(x - centerX, 2) + focusXSqr * pow(y - centerY, 2)));
1882 
1883  // if the ellipse is filled, we treat all points within as "on" it
1884  if (lambda > 1) {
1885  if (m_geomInnerColor.isValid()) {
1886  return 0;
1887  } else {
1888  withinShape = true;
1889  }
1890  }
1891 
1892  // otherwise we calculate the squared distance from the projected point on the ellipse
1893  NormalizedPoint projection(centerX, centerY);
1894  projection.x += lambda * (x - centerX);
1895  projection.y += lambda * (y - centerY);
1896 
1897  distance = projection.distanceSqr(x, y, xScale, yScale);
1898  break;
1899  }
1900 
1901  case GeomAnnotation::InscribedSquare:
1902  // if the square is filled, only check the bounding box
1903  if (m_geomInnerColor.isValid()) {
1904  return AnnotationPrivate::distanceSqr(x, y, xScale, yScale);
1905  }
1906 
1907  const QList<NormalizedPoint> edges = {NormalizedPoint(m_transformedBoundary.left, m_transformedBoundary.top),
1908  NormalizedPoint(m_transformedBoundary.right, m_transformedBoundary.top),
1909  NormalizedPoint(m_transformedBoundary.right, m_transformedBoundary.bottom),
1910  NormalizedPoint(m_transformedBoundary.left, m_transformedBoundary.bottom),
1911  NormalizedPoint(m_transformedBoundary.left, m_transformedBoundary.top)};
1912  distance = ::distanceSqr(x, y, xScale, yScale, edges);
1913 
1914  if (m_transformedBoundary.contains(x, y)) {
1915  withinShape = true;
1916  }
1917 
1918  break;
1919  }
1920  if (withinShape) {
1921  distance = strokeDistance(distance, m_style.width() * xScale / m_page->m_width);
1922  }
1923 
1924  return distance;
1925 }
1926 
1927 /** HighlightAnnotation [Annotation] */
1928 
1929 class HighlightAnnotation::Quad::Private
1930 {
1931 public:
1932  Private()
1933  {
1934  }
1935 
1936  NormalizedPoint m_points[4];
1937  NormalizedPoint m_transformedPoints[4];
1938  bool m_capStart : 1;
1939  bool m_capEnd : 1;
1940  double m_feather;
1941 };
1942 
1944  : d(new Private)
1945 {
1946 }
1947 
1949 {
1950  delete d;
1951 }
1952 
1954  : d(new Private)
1955 {
1956  *d = *other.d;
1957 }
1958 
1959 HighlightAnnotation::Quad &HighlightAnnotation::Quad::operator=(const Quad &other)
1960 {
1961  if (this != &other) {
1962  *d = *other.d;
1963  }
1964 
1965  return *this;
1966 }
1967 
1969 {
1970  if (index < 0 || index > 3) {
1971  return;
1972  }
1973 
1974  d->m_points[index] = point;
1975 }
1976 
1978 {
1979  if (index < 0 || index > 3) {
1980  return NormalizedPoint();
1981  }
1982 
1983  return d->m_points[index];
1984 }
1985 
1987 {
1988  if (index < 0 || index > 3) {
1989  return NormalizedPoint();
1990  }
1991 
1992  return d->m_transformedPoints[index];
1993 }
1994 
1996 {
1997  d->m_capStart = value;
1998 }
1999 
2001 {
2002  return d->m_capStart;
2003 }
2004 
2006 {
2007  d->m_capEnd = value;
2008 }
2009 
2011 {
2012  return d->m_capEnd;
2013 }
2014 
2016 {
2017  d->m_feather = width;
2018 }
2019 
2021 {
2022  return d->m_feather;
2023 }
2024 
2026 {
2027  for (int i = 0; i < 4; ++i) {
2028  d->m_transformedPoints[i] = d->m_points[i];
2029  d->m_transformedPoints[i].transform(matrix);
2030  }
2031 }
2032 
2033 class Okular::HighlightAnnotationPrivate : public Okular::AnnotationPrivate
2034 {
2035 public:
2036  HighlightAnnotationPrivate()
2037  : AnnotationPrivate()
2038  , m_highlightType(HighlightAnnotation::Highlight)
2039  {
2040  }
2041 
2042  void transform(const QTransform &matrix) override;
2043  void baseTransform(const QTransform &matrix) override;
2044  double distanceSqr(double x, double y, double xScale, double yScale) const override;
2045  void setAnnotationProperties(const QDomNode &node) override;
2046  AnnotationPrivate *getNewAnnotationPrivate() override;
2047 
2048  HighlightAnnotation::HighlightType m_highlightType;
2049  QList<HighlightAnnotation::Quad> m_highlightQuads;
2050 };
2051 
2052 HighlightAnnotation::HighlightAnnotation()
2053  : Annotation(*new HighlightAnnotationPrivate())
2054 {
2055 }
2056 
2057 HighlightAnnotation::HighlightAnnotation(const QDomNode &description)
2058  : Annotation(*new HighlightAnnotationPrivate(), description)
2059 {
2060 }
2061 
2062 HighlightAnnotation::~HighlightAnnotation()
2063 {
2064 }
2065 
2066 void HighlightAnnotation::setHighlightType(HighlightType type)
2067 {
2068  Q_D(HighlightAnnotation);
2069  d->m_highlightType = type;
2070 }
2071 
2072 HighlightAnnotation::HighlightType HighlightAnnotation::highlightType() const
2073 {
2074  Q_D(const HighlightAnnotation);
2075  return d->m_highlightType;
2076 }
2077 
2078 QList<HighlightAnnotation::Quad> &HighlightAnnotation::highlightQuads()
2079 {
2080  Q_D(HighlightAnnotation);
2081  return d->m_highlightQuads;
2082 }
2083 
2084 const QList<HighlightAnnotation::Quad> &HighlightAnnotation::highlightQuads() const
2085 {
2086  Q_D(const HighlightAnnotation);
2087  return d->m_highlightQuads;
2088 }
2089 
2090 void HighlightAnnotation::store(QDomNode &node, QDomDocument &document) const
2091 {
2092  Q_D(const HighlightAnnotation);
2093  // recurse to parent objects storing properties
2094  Annotation::store(node, document);
2095 
2096  // create [hl] element
2097  QDomElement hlElement = document.createElement(QStringLiteral("hl"));
2098  node.appendChild(hlElement);
2099 
2100  // append the optional attributes
2101  if (d->m_highlightType != Highlight) {
2102  hlElement.setAttribute(QStringLiteral("type"), (int)d->m_highlightType);
2103  }
2104  if (d->m_highlightQuads.count() < 1) {
2105  return;
2106  }
2107  // append highlight quads, all children describe quads
2108  QList<Quad>::const_iterator it = d->m_highlightQuads.begin(), end = d->m_highlightQuads.end();
2109  for (; it != end; ++it) {
2110  QDomElement quadElement = document.createElement(QStringLiteral("quad"));
2111  hlElement.appendChild(quadElement);
2112  const Quad &q = *it;
2113  quadElement.setAttribute(QStringLiteral("ax"), QString::number(q.point(0).x));
2114  quadElement.setAttribute(QStringLiteral("ay"), QString::number(q.point(0).y));
2115  quadElement.setAttribute(QStringLiteral("bx"), QString::number(q.point(1).x));
2116  quadElement.setAttribute(QStringLiteral("by"), QString::number(q.point(1).y));
2117  quadElement.setAttribute(QStringLiteral("cx"), QString::number(q.point(2).x));
2118  quadElement.setAttribute(QStringLiteral("cy"), QString::number(q.point(2).y));
2119  quadElement.setAttribute(QStringLiteral("dx"), QString::number(q.point(3).x));
2120  quadElement.setAttribute(QStringLiteral("dy"), QString::number(q.point(3).y));
2121  if (q.capStart()) {
2122  quadElement.setAttribute(QStringLiteral("start"), 1);
2123  }
2124  if (q.capEnd()) {
2125  quadElement.setAttribute(QStringLiteral("end"), 1);
2126  }
2127  quadElement.setAttribute(QStringLiteral("feather"), QString::number(q.feather()));
2128  }
2129 }
2130 
2131 Annotation::SubType HighlightAnnotation::subType() const
2132 {
2133  return AHighlight;
2134 }
2135 
2136 void HighlightAnnotationPrivate::transform(const QTransform &matrix)
2137 {
2138  AnnotationPrivate::transform(matrix);
2139 
2141  while (it.hasNext()) {
2142  it.next().transform(matrix);
2143  }
2144 }
2145 
2146 void HighlightAnnotationPrivate::baseTransform(const QTransform &matrix)
2147 {
2148  AnnotationPrivate::baseTransform(matrix);
2149 
2151  while (it.hasNext()) {
2152  it.next().transform(matrix);
2153  }
2154 }
2155 
2156 void HighlightAnnotationPrivate::setAnnotationProperties(const QDomNode &node)
2157 {
2158  Okular::AnnotationPrivate::setAnnotationProperties(node);
2159  m_highlightQuads.clear();
2160 
2161  // loop through the whole children looking for a 'hl' element
2162  QDomNode subNode = node.firstChild();
2163  while (subNode.isElement()) {
2164  QDomElement e = subNode.toElement();
2165  subNode = subNode.nextSibling();
2166  if (e.tagName() != QLatin1String("hl")) {
2167  continue;
2168  }
2169 
2170  // parse the attributes
2171  if (e.hasAttribute(QStringLiteral("type"))) {
2172  m_highlightType = (HighlightAnnotation::HighlightType)e.attribute(QStringLiteral("type")).toInt();
2173  }
2174 
2175  // parse all 'quad' subnodes
2176  QDomNode quadNode = e.firstChild();
2177  for (; quadNode.isElement(); quadNode = quadNode.nextSibling()) {
2178  QDomElement qe = quadNode.toElement();
2179  if (qe.tagName() != QLatin1String("quad")) {
2180  continue;
2181  }
2182 
2184  q.setPoint(NormalizedPoint(qe.attribute(QStringLiteral("ax"), QStringLiteral("0.0")).toDouble(), qe.attribute(QStringLiteral("ay"), QStringLiteral("0.0")).toDouble()), 0);
2185  q.setPoint(NormalizedPoint(qe.attribute(QStringLiteral("bx"), QStringLiteral("0.0")).toDouble(), qe.attribute(QStringLiteral("by"), QStringLiteral("0.0")).toDouble()), 1);
2186  q.setPoint(NormalizedPoint(qe.attribute(QStringLiteral("cx"), QStringLiteral("0.0")).toDouble(), qe.attribute(QStringLiteral("cy"), QStringLiteral("0.0")).toDouble()), 2);
2187  q.setPoint(NormalizedPoint(qe.attribute(QStringLiteral("dx"), QStringLiteral("0.0")).toDouble(), qe.attribute(QStringLiteral("dy"), QStringLiteral("0.0")).toDouble()), 3);
2188  q.setCapStart(qe.hasAttribute(QStringLiteral("start")));
2189  q.setCapEnd(qe.hasAttribute(QStringLiteral("end")));
2190  q.setFeather(qe.attribute(QStringLiteral("feather"), QStringLiteral("0.1")).toDouble());
2191 
2192  q.transform(QTransform());
2193 
2194  m_highlightQuads.append(q);
2195  }
2196 
2197  // loading complete
2198  break;
2199  }
2200 }
2201 
2202 AnnotationPrivate *HighlightAnnotationPrivate::getNewAnnotationPrivate()
2203 {
2204  return new HighlightAnnotationPrivate();
2205 }
2206 
2207 double HighlightAnnotationPrivate::distanceSqr(double x, double y, double xScale, double yScale) const
2208 {
2209  NormalizedPoint point(x, y);
2210  double outsideDistance = DBL_MAX;
2211  for (const HighlightAnnotation::Quad &quad : m_highlightQuads) {
2212  QList<NormalizedPoint> pathPoints;
2213 
2214  // first, we check if the point is within the area described by the 4 quads
2215  // this is the case, if the point is always on one side of each segments delimiting the polygon:
2216  pathPoints << quad.transformedPoint(0);
2217  int directionVote = 0;
2218  for (int i = 1; i < 5; ++i) {
2219  NormalizedPoint thisPoint = quad.transformedPoint(i % 4);
2220  directionVote += (isLeftOfVector(pathPoints.back(), thisPoint, point)) ? 1 : -1;
2221  pathPoints << thisPoint;
2222  }
2223  if (abs(directionVote) == 4) {
2224  return 0;
2225  }
2226 
2227  // if that's not the case, we treat the outline as path and simply determine
2228  // the distance from the path to the point
2229  const double thisOutsideDistance = ::distanceSqr(x, y, xScale, yScale, pathPoints);
2230  if (thisOutsideDistance < outsideDistance) {
2231  outsideDistance = thisOutsideDistance;
2232  }
2233  }
2234 
2235  return outsideDistance;
2236 }
2237 
2238 /** StampAnnotation [Annotation] */
2239 
2240 class Okular::StampAnnotationPrivate : public Okular::AnnotationPrivate
2241 {
2242 public:
2243  StampAnnotationPrivate()
2244  : AnnotationPrivate()
2245  , m_stampIconName(QStringLiteral("Draft"))
2246  {
2247  }
2248  void setAnnotationProperties(const QDomNode &node) override;
2249  bool canBeResized() const override;
2250  AnnotationPrivate *getNewAnnotationPrivate() override;
2251 
2252  QString m_stampIconName;
2253 };
2254 
2255 StampAnnotation::StampAnnotation()
2256  : Annotation(*new StampAnnotationPrivate())
2257 {
2258 }
2259 
2260 StampAnnotation::StampAnnotation(const QDomNode &description)
2261  : Annotation(*new StampAnnotationPrivate(), description)
2262 {
2263 }
2264 
2265 StampAnnotation::~StampAnnotation()
2266 {
2267 }
2268 
2269 void StampAnnotation::setStampIconName(const QString &name)
2270 {
2271  Q_D(StampAnnotation);
2272  d->m_stampIconName = name;
2273 }
2274 
2275 QString StampAnnotation::stampIconName() const
2276 {
2277  Q_D(const StampAnnotation);
2278  return d->m_stampIconName;
2279 }
2280 
2281 Annotation::SubType StampAnnotation::subType() const
2282 {
2283  return AStamp;
2284 }
2285 
2286 void StampAnnotation::store(QDomNode &node, QDomDocument &document) const
2287 {
2288  Q_D(const StampAnnotation);
2289  // recurse to parent objects storing properties
2290  Annotation::store(node, document);
2291 
2292  // create [stamp] element
2293  QDomElement stampElement = document.createElement(QStringLiteral("stamp"));
2294  node.appendChild(stampElement);
2295 
2296  // append the optional attributes
2297  if (d->m_stampIconName != QLatin1String("Draft")) {
2298  stampElement.setAttribute(QStringLiteral("icon"), d->m_stampIconName);
2299  }
2300 }
2301 
2302 void StampAnnotationPrivate::setAnnotationProperties(const QDomNode &node)
2303 {
2304  Okular::AnnotationPrivate::setAnnotationProperties(node);
2305 
2306  // loop through the whole children looking for a 'stamp' element
2307  QDomNode subNode = node.firstChild();
2308  while (subNode.isElement()) {
2309  QDomElement e = subNode.toElement();
2310  subNode = subNode.nextSibling();
2311  if (e.tagName() != QLatin1String("stamp")) {
2312  continue;
2313  }
2314 
2315  // parse the attributes
2316  if (e.hasAttribute(QStringLiteral("icon"))) {
2317  m_stampIconName = e.attribute(QStringLiteral("icon"));
2318  }
2319 
2320  // loading complete
2321  break;
2322  }
2323 }
2324 
2325 bool StampAnnotationPrivate::canBeResized() const
2326 {
2327  return true;
2328 }
2329 
2330 AnnotationPrivate *StampAnnotationPrivate::getNewAnnotationPrivate()
2331 {
2332  return new StampAnnotationPrivate();
2333 }
2334 
2335 /** InkAnnotation [Annotation] */
2336 
2337 class Okular::InkAnnotationPrivate : public Okular::AnnotationPrivate
2338 {
2339 public:
2340  InkAnnotationPrivate()
2341  : AnnotationPrivate()
2342  {
2343  }
2344 
2345  void transform(const QTransform &matrix) override;
2346  void baseTransform(const QTransform &matrix) override;
2347  void resetTransformation() override;
2348  double distanceSqr(double x, double y, double xScale, double yScale) const override;
2349  void translate(const NormalizedPoint &coord) override;
2350  void setAnnotationProperties(const QDomNode &node) override;
2351  AnnotationPrivate *getNewAnnotationPrivate() override;
2352 
2353  QList<QList<NormalizedPoint>> m_inkPaths;
2354  QList<QList<NormalizedPoint>> m_transformedInkPaths;
2355 };
2356 
2357 InkAnnotation::InkAnnotation()
2358  : Annotation(*new InkAnnotationPrivate())
2359 {
2360 }
2361 
2362 InkAnnotation::InkAnnotation(const QDomNode &description)
2363  : Annotation(*new InkAnnotationPrivate(), description)
2364 {
2365 }
2366 
2367 InkAnnotation::~InkAnnotation()
2368 {
2369 }
2370 
2371 void InkAnnotation::setInkPaths(const QList<QList<NormalizedPoint>> &paths)
2372 {
2373  Q_D(InkAnnotation);
2374  d->m_inkPaths = paths;
2375 }
2376 
2377 QList<QList<NormalizedPoint>> InkAnnotation::inkPaths() const
2378 {
2379  Q_D(const InkAnnotation);
2380  return d->m_inkPaths;
2381 }
2382 
2383 QList<QList<NormalizedPoint>> InkAnnotation::transformedInkPaths() const
2384 {
2385  Q_D(const InkAnnotation);
2386  return d->m_transformedInkPaths;
2387 }
2388 
2389 Annotation::SubType InkAnnotation::subType() const
2390 {
2391  return AInk;
2392 }
2393 
2394 void InkAnnotation::store(QDomNode &node, QDomDocument &document) const
2395 {
2396  Q_D(const InkAnnotation);
2397  // recurse to parent objects storing properties
2398  Annotation::store(node, document);
2399 
2400  // create [ink] element
2401  QDomElement inkElement = document.createElement(QStringLiteral("ink"));
2402  node.appendChild(inkElement);
2403 
2404  // append the optional attributes
2405  if (d->m_inkPaths.count() < 1) {
2406  return;
2407  }
2408 
2409  QList<QList<NormalizedPoint>>::const_iterator pIt = d->m_inkPaths.begin(), pEnd = d->m_inkPaths.end();
2410  for (; pIt != pEnd; ++pIt) {
2411  QDomElement pathElement = document.createElement(QStringLiteral("path"));
2412  inkElement.appendChild(pathElement);
2413  const QList<NormalizedPoint> &path = *pIt;
2414  for (const NormalizedPoint &point : path) {
2415  QDomElement pointElement = document.createElement(QStringLiteral("point"));
2416  pathElement.appendChild(pointElement);
2417  pointElement.setAttribute(QStringLiteral("x"), QString::number(point.x));
2418  pointElement.setAttribute(QStringLiteral("y"), QString::number(point.y));
2419  }
2420  }
2421 }
2422 
2423 double InkAnnotationPrivate::distanceSqr(double x, double y, double xScale, double yScale) const
2424 {
2425  double distance = DBL_MAX;
2426  for (const QList<NormalizedPoint> &path : m_transformedInkPaths) {
2427  const double thisDistance = ::distanceSqr(x, y, xScale, yScale, path);
2428  if (thisDistance < distance) {
2429  distance = thisDistance;
2430  }
2431  }
2432  return strokeDistance(distance, m_style.width() * xScale / (m_page->m_width * 2));
2433 }
2434 
2435 void InkAnnotationPrivate::transform(const QTransform &matrix)
2436 {
2437  AnnotationPrivate::transform(matrix);
2438 
2439  for (int i = 0; i < m_transformedInkPaths.count(); ++i) {
2440  QMutableListIterator<NormalizedPoint> it(m_transformedInkPaths[i]);
2441  while (it.hasNext()) {
2442  it.next().transform(matrix);
2443  }
2444  }
2445 }
2446 
2447 void InkAnnotationPrivate::baseTransform(const QTransform &matrix)
2448 {
2449  AnnotationPrivate::baseTransform(matrix);
2450 
2451  for (int i = 0; i < m_inkPaths.count(); ++i) {
2452  QMutableListIterator<NormalizedPoint> it(m_inkPaths[i]);
2453  while (it.hasNext()) {
2454  it.next().transform(matrix);
2455  }
2456  }
2457 }
2458 
2459 void InkAnnotationPrivate::resetTransformation()
2460 {
2461  AnnotationPrivate::resetTransformation();
2462 
2463  m_transformedInkPaths = m_inkPaths;
2464 }
2465 
2466 void InkAnnotationPrivate::translate(const NormalizedPoint &coord)
2467 {
2468  AnnotationPrivate::translate(coord);
2469 
2470  for (int i = 0; i < m_inkPaths.count(); ++i) {
2471  QMutableListIterator<NormalizedPoint> it(m_inkPaths[i]);
2472  while (it.hasNext()) {
2473  NormalizedPoint &p = it.next();
2474  p.x = p.x + coord.x;
2475  p.y = p.y + coord.y;
2476  }
2477  }
2478 }
2479 
2480 void InkAnnotationPrivate::setAnnotationProperties(const QDomNode &node)
2481 {
2482  Okular::AnnotationPrivate::setAnnotationProperties(node);
2483  m_inkPaths.clear();
2484 
2485  // loop through the whole children looking for a 'ink' element
2486  QDomNode subNode = node.firstChild();
2487  while (subNode.isElement()) {
2488  QDomElement e = subNode.toElement();
2489  subNode = subNode.nextSibling();
2490  if (e.tagName() != QLatin1String("ink")) {
2491  continue;
2492  }
2493 
2494  // parse the 'path' subnodes
2495  QDomNode pathNode = e.firstChild();
2496  while (pathNode.isElement()) {
2497  QDomElement pathElement = pathNode.toElement();
2498  pathNode = pathNode.nextSibling();
2499 
2500  if (pathElement.tagName() != QLatin1String("path")) {
2501  continue;
2502  }
2503 
2504  // build each path parsing 'point' subnodes
2506  QDomNode pointNode = pathElement.firstChild();
2507  while (pointNode.isElement()) {
2508  QDomElement pointElement = pointNode.toElement();
2509  pointNode = pointNode.nextSibling();
2510 
2511  if (pointElement.tagName() != QLatin1String("point")) {
2512  continue;
2513  }
2514 
2515  NormalizedPoint p;
2516  p.x = pointElement.attribute(QStringLiteral("x"), QStringLiteral("0.0")).toDouble();
2517  p.y = pointElement.attribute(QStringLiteral("y"), QStringLiteral("0.0")).toDouble();
2518  path.append(p);
2519  }
2520 
2521  // add the path to the path list if it contains at least 2 nodes
2522  if (path.count() >= 2) {
2523  m_inkPaths.append(path);
2524  }
2525  }
2526 
2527  // loading complete
2528  break;
2529  }
2530 
2531  m_transformedInkPaths = m_inkPaths;
2532 }
2533 
2534 AnnotationPrivate *InkAnnotationPrivate::getNewAnnotationPrivate()
2535 {
2536  return new InkAnnotationPrivate();
2537 }
2538 
2539 /** CaretAnnotation [Annotation] */
2540 
2541 class Okular::CaretAnnotationPrivate : public Okular::AnnotationPrivate
2542 {
2543 public:
2544  CaretAnnotationPrivate()
2545  : AnnotationPrivate()
2546  , m_symbol(CaretAnnotation::None)
2547  {
2548  }
2549 
2550  void setAnnotationProperties(const QDomNode &node) override;
2551  AnnotationPrivate *getNewAnnotationPrivate() override;
2552 
2553  CaretAnnotation::CaretSymbol m_symbol;
2554 };
2555 
2556 static QString caretSymbolToString(CaretAnnotation::CaretSymbol symbol)
2557 {
2558  switch (symbol) {
2559  case CaretAnnotation::None:
2560  return QStringLiteral("None");
2561  case CaretAnnotation::P:
2562  return QStringLiteral("P");
2563  }
2564  return QString();
2565 }
2566 
2567 static CaretAnnotation::CaretSymbol caretSymbolFromString(const QString &symbol)
2568 {
2569  if (symbol == QLatin1String("None")) {
2570  return CaretAnnotation::None;
2571  } else if (symbol == QLatin1String("P")) {
2572  return CaretAnnotation::P;
2573  }
2574  return CaretAnnotation::None;
2575 }
2576 
2577 void CaretAnnotationPrivate::setAnnotationProperties(const QDomNode &node)
2578 {
2579  Okular::AnnotationPrivate::setAnnotationProperties(node);
2580 
2581  // loop through the whole children looking for a 'caret' element
2582  QDomNode subNode = node.firstChild();
2583  while (subNode.isElement()) {
2584  QDomElement e = subNode.toElement();
2585  subNode = subNode.nextSibling();
2586  if (e.tagName() != QLatin1String("caret")) {
2587  continue;
2588  }
2589 
2590  // parse the attributes
2591  if (e.hasAttribute(QStringLiteral("symbol"))) {
2592  m_symbol = caretSymbolFromString(e.attribute(QStringLiteral("symbol")));
2593  }
2594 
2595  // loading complete
2596  break;
2597  }
2598 }
2599 
2600 AnnotationPrivate *CaretAnnotationPrivate::getNewAnnotationPrivate()
2601 {
2602  return new CaretAnnotationPrivate();
2603 }
2604 
2605 CaretAnnotation::CaretAnnotation()
2606  : Annotation(*new CaretAnnotationPrivate())
2607 {
2608 }
2609 
2610 CaretAnnotation::CaretAnnotation(const QDomNode &description)
2611  : Annotation(*new CaretAnnotationPrivate(), description)
2612 {
2613 }
2614 
2615 CaretAnnotation::~CaretAnnotation()
2616 {
2617 }
2618 
2619 void CaretAnnotation::setCaretSymbol(CaretAnnotation::CaretSymbol symbol)
2620 {
2621  Q_D(CaretAnnotation);
2622  d->m_symbol = symbol;
2623 }
2624 
2625 CaretAnnotation::CaretSymbol CaretAnnotation::caretSymbol() const
2626 {
2627  Q_D(const CaretAnnotation);
2628  return d->m_symbol;
2629 }
2630 
2631 Annotation::SubType CaretAnnotation::subType() const
2632 {
2633  return ACaret;
2634 }
2635 
2636 void CaretAnnotation::store(QDomNode &node, QDomDocument &document) const
2637 {
2638  Q_D(const CaretAnnotation);
2639  // recurse to parent objects storing properties
2640  Annotation::store(node, document);
2641 
2642  // create [caret] element
2643  QDomElement caretElement = document.createElement(QStringLiteral("caret"));
2644  node.appendChild(caretElement);
2645 
2646  // append the optional attributes
2647  if (d->m_symbol != None) {
2648  caretElement.setAttribute(QStringLiteral("symbol"), caretSymbolToString(d->m_symbol));
2649  }
2650 }
2651 
2652 /** FileAttachmentAnnotation [Annotation] */
2653 
2654 class Okular::FileAttachmentAnnotationPrivate : public Okular::AnnotationPrivate
2655 {
2656 public:
2657  FileAttachmentAnnotationPrivate()
2658  : AnnotationPrivate()
2659  , icon(QStringLiteral("PushPin"))
2660  , embfile(nullptr)
2661  {
2662  }
2663  ~FileAttachmentAnnotationPrivate() override
2664  {
2665  delete embfile;
2666  }
2667 
2668  void setAnnotationProperties(const QDomNode &node) override;
2669  AnnotationPrivate *getNewAnnotationPrivate() override;
2670 
2671  // data fields
2672  QString icon;
2673  EmbeddedFile *embfile;
2674 };
2675 
2676 void FileAttachmentAnnotationPrivate::setAnnotationProperties(const QDomNode &node)
2677 {
2678  Okular::AnnotationPrivate::setAnnotationProperties(node);
2679 
2680  // loop through the whole children looking for a 'fileattachment' element
2681  QDomNode subNode = node.firstChild();
2682  while (subNode.isElement()) {
2683  QDomElement e = subNode.toElement();
2684  subNode = subNode.nextSibling();
2685  if (e.tagName() != QLatin1String("fileattachment")) {
2686  continue;
2687  }
2688 
2689  // loading complete
2690  break;
2691  }
2692 }
2693 
2694 AnnotationPrivate *FileAttachmentAnnotationPrivate::getNewAnnotationPrivate()
2695 {
2696  return new FileAttachmentAnnotationPrivate();
2697 }
2698 
2699 FileAttachmentAnnotation::FileAttachmentAnnotation()
2700  : Annotation(*new FileAttachmentAnnotationPrivate())
2701 {
2702 }
2703 
2704 FileAttachmentAnnotation::FileAttachmentAnnotation(const QDomNode &description)
2705  : Annotation(*new FileAttachmentAnnotationPrivate(), description)
2706 {
2707 }
2708 
2709 FileAttachmentAnnotation::~FileAttachmentAnnotation()
2710 {
2711 }
2712 
2713 void FileAttachmentAnnotation::store(QDomNode &node, QDomDocument &document) const
2714 {
2715  // recurse to parent objects storing properties
2716  Annotation::store(node, document);
2717 
2718  // create [fileattachment] element
2719  QDomElement fileAttachmentElement = document.createElement(QStringLiteral("fileattachment"));
2720  node.appendChild(fileAttachmentElement);
2721 }
2722 
2723 Annotation::SubType FileAttachmentAnnotation::subType() const
2724 {
2725  return AFileAttachment;
2726 }
2727 
2728 QString FileAttachmentAnnotation::fileIconName() const
2729 {
2730  Q_D(const FileAttachmentAnnotation);
2731  return d->icon;
2732 }
2733 
2734 void FileAttachmentAnnotation::setFileIconName(const QString &iconName)
2735 {
2736  Q_D(FileAttachmentAnnotation);
2737  d->icon = iconName;
2738 }
2739 
2740 EmbeddedFile *FileAttachmentAnnotation::embeddedFile() const
2741 {
2742  Q_D(const FileAttachmentAnnotation);
2743  return d->embfile;
2744 }
2745 
2746 void FileAttachmentAnnotation::setEmbeddedFile(EmbeddedFile *ef)
2747 {
2748  Q_D(FileAttachmentAnnotation);
2749  d->embfile = ef;
2750 }
2751 
2752 /** SoundAnnotation [Annotation] */
2753 
2754 class Okular::SoundAnnotationPrivate : public Okular::AnnotationPrivate
2755 {
2756 public:
2757  SoundAnnotationPrivate()
2758  : AnnotationPrivate()
2759  , icon(QStringLiteral("Speaker"))
2760  , sound(nullptr)
2761  {
2762  }
2763  ~SoundAnnotationPrivate() override
2764  {
2765  delete sound;
2766  }
2767 
2768  void setAnnotationProperties(const QDomNode &node) override;
2769  AnnotationPrivate *getNewAnnotationPrivate() override;
2770 
2771  // data fields
2772  QString icon;
2773  Sound *sound;
2774 };
2775 
2776 void SoundAnnotationPrivate::setAnnotationProperties(const QDomNode &node)
2777 {
2778  Okular::AnnotationPrivate::setAnnotationProperties(node);
2779 
2780  // loop through the whole children looking for a 'sound' element
2781  QDomNode subNode = node.firstChild();
2782  while (subNode.isElement()) {
2783  QDomElement e = subNode.toElement();
2784  subNode = subNode.nextSibling();
2785  if (e.tagName() != QLatin1String("sound")) {
2786  continue;
2787  }
2788 
2789  // loading complete
2790  break;
2791  }
2792 }
2793 
2794 AnnotationPrivate *SoundAnnotationPrivate::getNewAnnotationPrivate()
2795 {
2796  return new SoundAnnotationPrivate();
2797 }
2798 
2800  : Annotation(*new SoundAnnotationPrivate())
2801 {
2802 }
2803 
2805  : Annotation(*new SoundAnnotationPrivate(), description)
2806 {
2807 }
2808 
2810 {
2811 }
2812 
2813 void SoundAnnotation::store(QDomNode &node, QDomDocument &document) const
2814 {
2815  // recurse to parent objects storing properties
2816  Annotation::store(node, document);
2817 
2818  // create [sound] element
2819  QDomElement soundElement = document.createElement(QStringLiteral("sound"));
2820  node.appendChild(soundElement);
2821 }
2822 
2824 {
2825  return ASound;
2826 }
2827 
2829 {
2830  Q_D(const SoundAnnotation);
2831  return d->icon;
2832 }
2833 
2835 {
2837  d->icon = iconName;
2838 }
2839 
2841 {
2842  Q_D(const SoundAnnotation);
2843  return d->sound;
2844 }
2845 
2847 {
2849  d->sound = s;
2850 }
2851 
2852 /** MovieAnnotation [Annotation] */
2853 
2854 class Okular::MovieAnnotationPrivate : public Okular::AnnotationPrivate
2855 {
2856 public:
2857  MovieAnnotationPrivate()
2858  : AnnotationPrivate()
2859  , movie(nullptr)
2860  {
2861  }
2862  ~MovieAnnotationPrivate() override
2863  {
2864  delete movie;
2865  }
2866 
2867  void setAnnotationProperties(const QDomNode &node) override;
2868  AnnotationPrivate *getNewAnnotationPrivate() override;
2869 
2870  // data fields
2871  Movie *movie;
2872 };
2873 
2874 void MovieAnnotationPrivate::setAnnotationProperties(const QDomNode &node)
2875 {
2876  Okular::AnnotationPrivate::setAnnotationProperties(node);
2877 
2878  // loop through the whole children looking for a 'movie' element
2879  QDomNode subNode = node.firstChild();
2880  while (subNode.isElement()) {
2881  QDomElement e = subNode.toElement();
2882  subNode = subNode.nextSibling();
2883  if (e.tagName() != QLatin1String("movie")) {
2884  continue;
2885  }
2886 
2887  // loading complete
2888  break;
2889  }
2890 }
2891 
2892 AnnotationPrivate *MovieAnnotationPrivate::getNewAnnotationPrivate()
2893 {
2894  return new MovieAnnotationPrivate();
2895 }
2896 
2898  : Annotation(*new MovieAnnotationPrivate())
2899 {
2900 }
2901 
2903  : Annotation(*new MovieAnnotationPrivate(), description)
2904 {
2905 }
2906 
2908 {
2909 }
2910 
2911 void MovieAnnotation::store(QDomNode &node, QDomDocument &document) const
2912 {
2913  // recurse to parent objects storing properties
2914  Annotation::store(node, document);
2915 
2916  // create [movie] element
2917  QDomElement movieElement = document.createElement(QStringLiteral("movie"));
2918  node.appendChild(movieElement);
2919 }
2920 
2922 {
2923  return AMovie;
2924 }
2925 
2927 {
2928  Q_D(const MovieAnnotation);
2929  return d->movie;
2930 }
2931 
2933 {
2935  d->movie = movie;
2936 }
2937 
2938 /** ScreenAnnotation [Annotation] */
2939 
2940 class Okular::ScreenAnnotationPrivate : public Okular::AnnotationPrivate
2941 {
2942 public:
2943  ScreenAnnotationPrivate();
2944  ~ScreenAnnotationPrivate() override;
2945 
2946  void setAnnotationProperties(const QDomNode &node) override;
2947  AnnotationPrivate *getNewAnnotationPrivate() override;
2948 
2949  Okular::Action *m_action;
2951 };
2952 
2953 ScreenAnnotationPrivate::ScreenAnnotationPrivate()
2954  : m_action(nullptr)
2955 {
2956 }
2957 
2958 ScreenAnnotationPrivate::~ScreenAnnotationPrivate()
2959 {
2960  delete m_action;
2961  qDeleteAll(m_additionalActions);
2962 }
2963 
2964 void ScreenAnnotationPrivate::setAnnotationProperties(const QDomNode &node)
2965 {
2966  Okular::AnnotationPrivate::setAnnotationProperties(node);
2967 
2968  // loop through the whole children looking for a 'screen' element
2969  QDomNode subNode = node.firstChild();
2970  while (subNode.isElement()) {
2971  QDomElement e = subNode.toElement();
2972  subNode = subNode.nextSibling();
2973  if (e.tagName() != QLatin1String("screen")) {
2974  continue;
2975  }
2976 
2977  // loading complete
2978  break;
2979  }
2980 }
2981 
2982 AnnotationPrivate *ScreenAnnotationPrivate::getNewAnnotationPrivate()
2983 {
2984  return new ScreenAnnotationPrivate();
2985 }
2986 
2988  : Annotation(*new ScreenAnnotationPrivate())
2989 {
2990 }
2991 
2993  : Annotation(*new ScreenAnnotationPrivate(), description)
2994 {
2995 }
2996 
2998 {
2999 }
3000 
3001 void ScreenAnnotation::store(QDomNode &node, QDomDocument &document) const
3002 {
3003  // recurse to parent objects storing properties
3004  Annotation::store(node, document);
3005 
3006  // create [screen] element
3007  QDomElement movieElement = document.createElement(QStringLiteral("screen"));
3008  node.appendChild(movieElement);
3009 }
3010 
3012 {
3013  return AScreen;
3014 }
3015 
3017 {
3019  if (d->m_additionalActions.contains(type)) {
3020  delete d->m_additionalActions.value(type);
3021  }
3022 
3023  d->m_additionalActions.insert(type, action);
3024 }
3025 
3027 {
3028  Q_D(const ScreenAnnotation);
3029  if (!d->m_additionalActions.contains(type)) {
3030  return nullptr;
3031  } else {
3032  return d->m_additionalActions.value(type);
3033  }
3034 }
3035 
3037 {
3039 
3040  delete d->m_action;
3041  d->m_action = action;
3042 }
3043 
3045 {
3046  Q_D(const ScreenAnnotation);
3047  return d->m_action;
3048 }
3049 
3050 /** WidgetAnnotation [Annotation] */
3051 
3052 class Okular::WidgetAnnotationPrivate : public Okular::AnnotationPrivate
3053 {
3054 public:
3055  ~WidgetAnnotationPrivate() override;
3056  void setAnnotationProperties(const QDomNode &node) override;
3057  AnnotationPrivate *getNewAnnotationPrivate() override;
3058 
3060 };
3061 
3062 WidgetAnnotationPrivate::~WidgetAnnotationPrivate()
3063 {
3064  qDeleteAll(m_additionalActions);
3065 }
3066 
3067 void WidgetAnnotationPrivate::setAnnotationProperties(const QDomNode &node)
3068 {
3069  Okular::AnnotationPrivate::setAnnotationProperties(node);
3070 
3071  // loop through the whole children looking for a 'widget' element
3072  QDomNode subNode = node.firstChild();
3073  while (subNode.isElement()) {
3074  QDomElement e = subNode.toElement();
3075  subNode = subNode.nextSibling();
3076  if (e.tagName() != QLatin1String("widget")) {
3077  continue;
3078  }
3079 
3080  // loading complete
3081  break;
3082  }
3083 }
3084 
3085 AnnotationPrivate *WidgetAnnotationPrivate::getNewAnnotationPrivate()
3086 {
3087  return new WidgetAnnotationPrivate();
3088 }
3089 
3091  : Annotation(*new WidgetAnnotationPrivate())
3092 {
3093 }
3094 
3096  : Annotation(*new WidgetAnnotationPrivate, description)
3097 {
3098 }
3099 
3101 {
3102 }
3103 
3104 void WidgetAnnotation::store(QDomNode &node, QDomDocument &document) const
3105 {
3106  // recurse to parent objects storing properties
3107  Annotation::store(node, document);
3108 
3109  // create [widget] element
3110  QDomElement movieElement = document.createElement(QStringLiteral("widget"));
3111  node.appendChild(movieElement);
3112 }
3113 
3115 {
3116  return AWidget;
3117 }
3118 
3120 {
3122  if (d->m_additionalActions.contains(type)) {
3123  delete d->m_additionalActions.value(type);
3124  }
3125 
3126  d->m_additionalActions.insert(type, action);
3127 }
3128 
3130 {
3131  Q_D(const WidgetAnnotation);
3132  if (!d->m_additionalActions.contains(type)) {
3133  return nullptr;
3134  } else {
3135  return d->m_additionalActions.value(type);
3136  }
3137 }
3138 
3139 /** RichMediaAnnotation [Annotation] */
3140 
3141 class Okular::RichMediaAnnotationPrivate : public Okular::AnnotationPrivate
3142 {
3143 public:
3144  RichMediaAnnotationPrivate();
3145  ~RichMediaAnnotationPrivate() override;
3146  void setAnnotationProperties(const QDomNode &node) override;
3147  AnnotationPrivate *getNewAnnotationPrivate() override;
3148 
3149  // data fields
3150  Movie *movie;
3151  EmbeddedFile *embeddedFile;
3152 };
3153 
3154 RichMediaAnnotationPrivate::RichMediaAnnotationPrivate()
3155  : movie(nullptr)
3156  , embeddedFile(nullptr)
3157 {
3158 }
3159 
3160 RichMediaAnnotationPrivate::~RichMediaAnnotationPrivate()
3161 {
3162  delete movie;
3163  delete embeddedFile;
3164 }
3165 
3166 void RichMediaAnnotationPrivate::setAnnotationProperties(const QDomNode &node)
3167 {
3168  Okular::AnnotationPrivate::setAnnotationProperties(node);
3169 
3170  // loop through the whole children looking for a 'richMedia' element
3171  QDomNode subNode = node.firstChild();
3172  while (subNode.isElement()) {
3173  QDomElement e = subNode.toElement();
3174  subNode = subNode.nextSibling();
3175  if (e.tagName() != QLatin1String("richMedia")) {
3176  continue;
3177  }
3178 
3179  // loading complete
3180  break;
3181  }
3182 }
3183 
3184 AnnotationPrivate *RichMediaAnnotationPrivate::getNewAnnotationPrivate()
3185 {
3186  return new RichMediaAnnotationPrivate();
3187 }
3188 
3190  : Annotation(*new RichMediaAnnotationPrivate())
3191 {
3192 }
3193 
3195  : Annotation(*new RichMediaAnnotationPrivate, description)
3196 {
3197 }
3198 
3200 {
3201 }
3202 
3204 {
3205  // recurse to parent objects storing properties
3206  Annotation::store(node, document);
3207 
3208  // create [richMedia] element
3209  QDomElement movieElement = document.createElement(QStringLiteral("richMedia"));
3210  node.appendChild(movieElement);
3211 }
3212 
3214 {
3215  return ARichMedia;
3216 }
3217 
3219 {
3221 
3222  delete d->movie;
3223  d->movie = movie;
3224 }
3225 
3227 {
3228  Q_D(const RichMediaAnnotation);
3229 
3230  return d->movie;
3231 }
3232 
3234 {
3235  Q_D(const RichMediaAnnotation);
3236 
3237  return d->embeddedFile;
3238 }
3239 
3241 {
3243 
3244  delete d->embeddedFile;
3245  d->embeddedFile = embeddedFile;
3246 }
void append(const T &value)
T & first()
KDOCTOOLS_EXPORT QString transform(const QString &file, const QString &stylesheet, const QVector< const char * > &params=QVector< const char * >())
double distanceSqr(double x, double y, double xScale, double yScale) const
Returns squared distance to normalized point (x, y) on a reference area of size xScale x yScale.
Definition: area.cpp:51
RevisionScope scope() const
Returns the scope of the revision.
void store(QDomNode &parentNode, QDomDocument &document) const override
Stores the movie annotation as xml in document under the given parentNode.
void setAnnotation(Annotation *annotation)
Sets the annotation the revision belongs to.
int marks() const
Returns the marks of the style.
QDomNode getAnnotationPropertiesDomNode() const
Retrieve the QDomNode representing this annotation's properties.
void setCapEnd(bool value)
Sets whether a cap should be used at the end.
void setSoundIconName(const QString &iconName)
Sets the iconName of the icon for the sound annotation.
@ ExternallyDrawn
Is drawn externally (by the generator which provided it)
Definition: annotations.h:139
Contains information about a sound object.
Definition: sound.h:23
void store(QDomNode &parentNode, QDomDocument &document) const override
Stores the rich media annotation as xml in document under the given parentNode.
~Revision()
Destroys the revision.
ScreenAnnotation()
Creates a new screen annotation.
double y
The normalized y coordinate.
Definition: area.h:171
QDomNode firstChild() const const
QDomElement toElement() const const
@ AInk
An ink annotation.
Definition: annotations.h:116
void render(QPainter *painter)
RevisionScope
Describes the scope of revision information.
Definition: annotations.h:166
void setOpacity(double opacity)
Sets the opacity of the style.
QString number(int n, int base)
Widget annotation.
Definition: annotations.h:1667
bool containsPoint(const QPointF &point, Qt::FillRule fillRule) const const
The documentation to the global Okular namespace.
Definition: action.h:16
void setAuthor(const QString &author)
Sets the author of the annotation.
int width() const
Returns the width of the window.
QString tagName() const const
NormalizedRect boundingRectangle() const
Returns the bounding rectangle of the annotation.
SubType subType() const override
Returns the sub type of the screen annotation.
Type type(const QSqlDatabase &db)
Annotation struct holds properties shared by all annotations.
Definition: annotations.h:95
static void storeAnnotation(const Annotation *annotation, QDomElement &element, QDomDocument &document)
Saves the annotation as a child of element taking care of saving all revisions if it has any.
Encapsulates data that describes an action.
Definition: action.h:40
void adjust(const NormalizedPoint &deltaCoord1, const NormalizedPoint &deltaCoord2)
Adjust the annotation by the specified coordinates.
int count(const T &value) const const
QString summary() const
Returns the summary of the window.
void setXCorners(double xCorners)
Sets the x-corners of the style.
void setSpaces(int spaces)
Sets the spaces of the style.
bool isNull() const const
AdditionalActionType
Describes the type of additional actions.
Definition: annotations.h:190
QList< Revision > & revisions()
Returns a reference to the revision list of the annotation.
QColor color() const
Returns the color of the style.
void setColor(const QColor &color)
Sets the color of the style.
void append(const T &value)
@ AHighlight
A highlight annotation.
Definition: annotations.h:114
SubType subType() const override
Returns the sub type of the sound annotation.
void setAdditionalAction(AdditionalActionType type, Action *action)
Sets the additional action of the given type.
void store(QDomNode &node, QDomDocument &document) const override
Stores the sound annotation as xml in document under the given parent node.
RichMediaAnnotation()
Creates a new rich media annotation.
QIcon fromTheme(const QString &name)
NormalizedPoint is a helper class which stores the coordinates of a normalized point.
Definition: area.h:116
void setWidth(int width)
Sets the width of the window.
void fill(const QColor &color)
QString author() const
Returns the author of the annotation.
EmbeddedFile * embeddedFile() const
Gets the embedded file object.
~Window()
Destroys the window.
~ScreenAnnotation() override
Destroys the screen annotation.
bool canBeMoved() const
Returns whether the annotation can be moved.
Screen annotation.
Definition: annotations.h:1598
@ AWidget
A widget annotation.
Definition: annotations.h:122
~MovieAnnotation() override
Destroys the movie annotation.
SubType subType() const override
Returns the sub type of the rich media annotation.
@ ASound
A sound annotation.
Definition: annotations.h:119
bool isValid() const const
QDomElement createElement(const QString &tagName)
QRect united(const QRect &rectangle) const const
void setHeight(int height)
Sets the height of the window.
QString title() const
Returns the title of the window.
QString locate(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options)
bool capStart() const
Returns whether a cap should be used at the start.
RevisionType type() const
Returns the type of the revision.
bool capEnd() const
Returns whether a cap should be used at the end.
SoundAnnotation()
Creates a new sound annotation.
void setCreationDate(const QDateTime &date)
Sets the creation date of the annotation.
void setAdditionalAction(AdditionalActionType type, Action *action)
Sets the additional action of the given type.
void setAnnotationProperties(const QDomNode &node)
Sets annotations internal properties according to the contents of node.
The Style class contains all information about style of the annotation.
Definition: annotations.h:320
@ NoEffect
No effect.
Definition: annotations.h:159
bool elementExists(const QString &id) const const
void setSound(Sound *s)
Sets the s representing the sound of the file attachment annotation.
double opacity() const
Returns the opacity of the style.
double xCorners() const
Returns the x-corners of the style.
Action * action() const
Returns the action that is executed when the annotation is triggered or 0 if not action has been defi...
void setAttribute(const QString &name, const QString &value)
QRect geometry(int xScale, int yScale) const
Returns the rectangle mapped to a reference area of xScale x yScale.
Definition: area.cpp:232
@ ACaret
A caret annotation.
Definition: annotations.h:117
void setMovie(Movie *movie)
Sets the new movie object.
@ AStamp
A stamp annotation.
Definition: annotations.h:115
int flags() const
Returns the flags of the annotation.
NormalizedPoint transformedPoint(int index) const
Returns the transformed (e.g.
Movie * movie() const
Gets the movie object.
~RichMediaAnnotation() override
Destroys the rich media annotation.
void setYCorners(double yCorners)
Sets the y-corners of the style.
void setEmbeddedFile(EmbeddedFile *embeddedFile)
Sets the embeddedFile representing the embedded file.
bool end()
int flags() const
Returns the flags of the window.
QString::const_iterator constBegin() const const
KeepAspectRatioByExpanding
void setPoint(const NormalizedPoint &point, int index)
Sets the normalized point at index.
WindingFill
KOSM_EXPORT double distance(const std::vector< const OSM::Node * > &path, Coordinate coord)
void(* DisposeDataFunction)(const Okular::Annotation *)
A function to be called when the annotation is destroyed.
Definition: annotations.h:209
LineEffect
Describes possible line effects for.
Definition: annotations.h:158
int height() const
Returns the height of the window.
void setTopLeft(const NormalizedPoint &point)
Sets the top-left point of the window.
~WidgetAnnotation() override
Destroys the widget annotation.
Movie * movie() const
Gets the movie object.
virtual ~Annotation()
Destroys the annotation.
void store(QDomNode &parentNode, QDomDocument &document) const override
Stores the screen annotation as xml in document under the given parentNode.
The Revision class contains all information about the revision of the annotation.
Definition: annotations.h:550
QVariant nativeId() const
Returns the "native" id of the annotation.
bool isEmpty() const const
void setAction(Action *action)
Sets the action that is executed when the annotation is triggered.
@ AFileAttachment
A file attachment annotation.
Definition: annotations.h:118
WidgetAnnotation()
Creates a new widget annotation.
Style & style()
Returns a reference to the style object of the annotation.
Window & window()
Returns a reference to the window object of the annotation.
Contains information about a movie object.
Definition: movie.h:25
void setBoundingRectangle(const NormalizedRect &rectangle)
Sets the bounding rectangle of the annotation.
QString uniqueName() const
Returns the unique name of the annotation.
QSize toSize() const const
QRectF boundsOnElement(const QString &id) const const
int toInt(bool *ok, int base) const const
void setDisposeDataFunction(DisposeDataFunction func)
Sets a function to be called when the annotation is destroyed.
void setUniqueName(const QString &name)
Sets the unique name of the annotation.
void setCapStart(bool value)
Sets whether a cap should be used at the start.
QString contents() const
Returns the contents of the annotation.
int spaces() const
Returns the spaces of the style.
void setScope(RevisionScope scope)
Sets the scope of the revision.
void setTitle(const QString &title)
Sets the title of the window.
QString::const_iterator constEnd() const const
LineStyle
Describes possible line styles for.
Definition: annotations.h:147
QDateTime fromString(const QString &string, Qt::DateFormat format)
Revision()
Creates a new revision.
A NormalizedRect is a rectangle which can be defined by two NormalizedPoints.
Definition: area.h:188
NormalizedPoint point(int index) const
Returns the normalized point at index.
@ AText
A textual annotation.
Definition: annotations.h:111
static QRect annotationGeometry(const Annotation *annotation, double scaleX, double scaleY)
Returns the geometry of the given annotation scaled by scaleX and scaleY.
T & back()
double toDouble(bool *ok) const const
QDateTime creationDate() const
Returns the creation date of the annotation.
double width() const
Returns the width of the style.
@ BeingMoved
Is being moved (mouse drag and drop). If ExternallyDrawn, the generator must not draw it.
Definition: annotations.h:140
void setLineEffect(LineEffect effect)
Sets the line effect of the style.
double yCorners() const
Returns the y-corners of the style.
static Annotation * createAnnotation(const QDomElement &element)
Restore an annotation (with revisions if needed) from the dom element.
Definition: annotations.cpp:92
void store(QDomNode &parentNode, QDomDocument &document) const override
Stores the widget annotation as xml in document under the given parentNode.
void setEffectIntensity(double intensity)
Sets the effect intensity of the style.
@ AScreen
A screen annotation.
Definition: annotations.h:121
bool isNull() const const
bool load(const QString &fileName, const char *format, Qt::ImageConversionFlags flags)
QString toLower() const const
Action * additionalAction(AdditionalActionType type) const
Returns the additional action of the given type or 0 if no action has been defined.
bool hasAttribute(const QString &name) const const
QPixmap scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
double x
The normalized x coordinate.
Definition: area.h:166
void setSummary(const QString &summary)
Sets the summary of the window.
SubType subType() const override
Returns the sub type of the widget annotation.
Sound annotation.
Definition: annotations.h:1492
RichMedia annotation.
Definition: annotations.h:1722
int count() const const
QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) const const
void setFlags(int flags)
Sets the flags of the window.
void transform(const QTransform &matrix)
Transforms the normalized rectangle with the operations defined by matrix.
Definition: area.cpp:253
Describes a highlight quad of a text markup annotation.
Definition: annotations.h:1172
@ AMovie
A movie annotation.
Definition: annotations.h:120
QDomNode appendChild(const QDomNode &newChild)
NormalizedPoint topLeft() const
Returns the top-left point of the window.
QString path(const QString &relativePath)
The Window class contains all information about the popup window of the annotation that is used to ed...
Definition: annotations.h:455
@ ARichMedia
A rich media annotation.
Definition: annotations.h:123
QFont font()
@ None
Not specified.
Definition: annotations.h:176
static QPixmap loadStamp(const QString &nameOrPath, int size, bool keepAspectRatio=true)
Returns a pixmap for a stamp symbol.
QSize scaled(int width, int height, Qt::AspectRatioMode mode) const const
@ Reply
Belongs to a reply.
Definition: annotations.h:167
const char * name(StandardAction id)
@ BeingResized
Is being resized (mouse drag and drop). If ExternallyDrawn, the generator must not draw it.
Definition: annotations.h:141
RevisionType
Describes the type of revision information.
Definition: annotations.h:175
NormalizedRect transformedBoundingRectangle() const
Returns the transformed bounding rectangle of the annotation.
SubType subType() const override
Returns the sub type of the movie annotation.
LineStyle lineStyle() const
Returns the line style of the style.
void setFlags(int flags)
Sets the flags of the annotation.
QDomNode nextSibling() const const
Style()
Creates a new style.
QList::iterator begin()
QSizeF size() const const
SubType
Describes the type of annotation as defined in PDF standard.
Definition: annotations.h:110
virtual void store(QDomNode &node, QDomDocument &document) const
Stores the annotation as xml in document under the given parent node.
~SoundAnnotation() override
Destroys the sound annotation.
void setNativeId(const QVariant &id)
Sets the "native" id of the annotation.
void setWidth(double width)
Sets the width of the style.
void setMarks(int marks)
Sets the marks of the style.
@ External
Is stored external.
Definition: annotations.h:138
bool openDialogAfterCreation() const
Returns whether the annotation dialog should be open after creation of the annotation or not.
QString attribute(const QString &name, const QString &defValue) const const
void setFeather(double width)
Sets the width of the drawing feather.
QDateTime modificationDate() const
Returns the last modification date of the annotation.
LineEffect lineEffect() const
Returns the line effect of the style.
@ AGeom
A geometrical annotation.
Definition: annotations.h:113
double effectIntensity() const
Returns the effect intensity of the style.
bool isElement() const const
SmoothTransformation
void setContents(const QString &contents)
Sets the contents of the annotation.
Sound * sound() const
Gets the sound object.
QString soundIconName() const
Gets the name of the icon.
@ ALine
A line annotation.
Definition: annotations.h:112
void setModificationDate(const QDateTime &date)
Sets the last modification date of the annotation.
virtual SubType subType() const =0
Returns the sub type of the annotation.
QDomCDATASection toCDATASection() const const
MovieAnnotation()
Creates a new movie annotation.
void setType(RevisionType type)
Sets the type of the revision.
void setLineStyle(LineStyle style)
Sets the line style of the style.
const QList< QKeySequence > & end()
QString & append(QChar ch)
transparent
static QDomElement findChildElement(const QDomNode &parentNode, const QString &name)
Returns the child element with the given name from the direct children of parentNode or a null elemen...
bool canBeResized() const
Returns whether the annotation can be resized.
Movie annotation.
Definition: annotations.h:1552
Q_D(Todo)
double feather() const
Returns the width of the drawing feather.
~Style()
Destroys the style.
virtual QVariant get(ScriptableExtension *callerPrincipal, quint64 objId, const QString &propName)
Window()
Creates a new window.
Action * additionalAction(AdditionalActionType type) const
Returns the additional action of the given type or 0 if no action has been defined.
void setMovie(Movie *movie)
Sets the new movie object.
Annotation * annotation() const
Returns the annotation the revision belongs to.
void translate(const NormalizedPoint &coord)
Move the annotation by the specified coordinates.
virtual ~AnnotationProxy()
Destroys the annotation proxy.
An embedded file into the document.
Definition: document.h:1456
QString data() const const
void transform(const QTransform &matrix)
Transforms the quad coordinates with the transformation defined by matrix.
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Mar 27 2023 04:09:00 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.