Okular

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

KDE's Doxygen guidelines are available online.