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

KDE's Doxygen guidelines are available online.