Okular

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

KDE's Doxygen guidelines are available online.