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

KDE's Doxygen guidelines are available online.