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

KDE's Doxygen guidelines are available online.