Okular

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

KDE's Doxygen guidelines are available online.