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

KDE's Doxygen guidelines are available online.