1 /* poppler-annotation.cc: qt interface to poppler
2  * Copyright (C) 2006, 2009, 2012-2015, 2018-2021 Albert Astals Cid <aacid@kde.org>
3  * Copyright (C) 2006, 2008, 2010 Pino Toscano <pino@kde.org>
4  * Copyright (C) 2012, Guillermo A. Amaral B. <gamaral@kde.org>
5  * Copyright (C) 2012-2014 Fabio D'Urso <fabiodurso@hotmail.it>
6  * Copyright (C) 2012, 2015, Tobias Koenig <tobias.koenig@kdab.com>
7  * Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de>
8  * Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by the LiMux project of the city of Munich
9  * Copyright (C) 2018 Intevation GmbH <intevation@intevation.de>
10  * Copyright (C) 2018 Dileep Sankhla <sankhla.dileep96@gmail.com>
11  * Copyright (C) 2018, 2019 Tobias Deiminger <haxtibal@posteo.de>
12  * Copyright (C) 2018 Carlos Garcia Campos <carlosgc@gnome.org>
13  * Copyright (C) 2020 Oliver Sander <oliver.sander@tu-dresden.de>
14  * Copyright (C) 2020 Katarina Behrens <Katarina.Behrens@cib.de>
15  * Copyright (C) 2020 Thorsten Behrens <Thorsten.Behrens@CIB.de>
16  * Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by Technische Universität Dresden
17  * Copyright (C) 2021 Mahmoud Ahmed Khalil <mahmoudkhalil11@gmail.com>
18  * Adapting code from
19  *   Copyright (C) 2004 by Enrico Ros <eros.kde@email.it>
20  *
21  * This program is free software; you can redistribute it and/or modify
22  * it under the terms of the GNU General Public License as published by
23  * the Free Software Foundation; either version 2, or (at your option)
24  * any later version.
25  *
26  * This program is distributed in the hope that it will be useful,
27  * but WITHOUT ANY WARRANTY; without even the implied warranty of
28  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
29  * GNU General Public License for more details.
30  *
31  * You should have received a copy of the GNU General Public License
32  * along with this program; if not, write to the Free Software
33  * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
34  */
35 
36 // qt/kde includes
37 #include <QtCore/QRegExp>
38 #include <QtCore/QtAlgorithms>
39 #include <QtXml/QDomElement>
40 #include <QtGui/QColor>
41 #include <QtGui/QTransform>
42 #include <QImage>
43 
44 // local includes
45 #include "poppler-annotation.h"
46 #include "poppler-link.h"
47 #include "poppler-qt5.h"
48 #include "poppler-annotation-helper.h"
49 #include "poppler-annotation-private.h"
50 #include "poppler-page-private.h"
51 #include "poppler-private.h"
52 
53 // poppler includes
54 #include <Page.h>
55 #include <Annot.h>
56 #include <Gfx.h>
57 #include <Error.h>
58 #include <FileSpec.h>
59 #include <Link.h>
60 #include <DateInfo.h>
61 
62 /* Almost all getters directly query the underlying poppler annotation, with
63  * the exceptions of link, file attachment, sound, movie and screen annotations,
64  * Whose data retrieval logic has not been moved yet. Their getters return
65  * static data set at creation time by findAnnotations
66  */
67 
68 namespace Poppler {
69 
70 // BEGIN AnnotationUtils implementation
createAnnotation(const QDomElement & annElement)71 Annotation *AnnotationUtils::createAnnotation(const QDomElement &annElement)
72 {
73     // safety check on annotation element
74     if (!annElement.hasAttribute(QStringLiteral("type")))
75         return nullptr;
76 
77     // build annotation of given type
78     Annotation *annotation = nullptr;
79     int typeNumber = annElement.attribute(QStringLiteral("type")).toInt();
80     switch (typeNumber) {
81     case Annotation::AText:
82         annotation = new TextAnnotation(annElement);
83         break;
84     case Annotation::ALine:
85         annotation = new LineAnnotation(annElement);
86         break;
87     case Annotation::AGeom:
88         annotation = new GeomAnnotation(annElement);
89         break;
90     case Annotation::AHighlight:
91         annotation = new HighlightAnnotation(annElement);
92         break;
93     case Annotation::AStamp:
94         annotation = new StampAnnotation(annElement);
95         break;
96     case Annotation::AInk:
97         annotation = new InkAnnotation(annElement);
98         break;
99     case Annotation::ACaret:
100         annotation = new CaretAnnotation(annElement);
101         break;
102     }
103 
104     // return created annotation
105     return annotation;
106 }
107 
storeAnnotation(const Annotation * ann,QDomElement & annElement,QDomDocument & document)108 void AnnotationUtils::storeAnnotation(const Annotation *ann, QDomElement &annElement, QDomDocument &document)
109 {
110     // save annotation's type as element's attribute
111     annElement.setAttribute(QStringLiteral("type"), (uint)ann->subType());
112 
113     // append all annotation data as children of this node
114     ann->store(annElement, document);
115 }
116 
findChildElement(const QDomNode & parentNode,const QString & name)117 QDomElement AnnotationUtils::findChildElement(const QDomNode &parentNode, const QString &name)
118 {
119     // loop through the whole children and return a 'name' named element
120     QDomNode subNode = parentNode.firstChild();
121     while (subNode.isElement()) {
122         QDomElement element = subNode.toElement();
123         if (element.tagName() == name)
124             return element;
125         subNode = subNode.nextSibling();
126     }
127     // if the name can't be found, return a dummy null element
128     return QDomElement();
129 }
130 // END AnnotationUtils implementation
131 
132 // BEGIN AnnotationAppearancePrivate implementation
AnnotationAppearancePrivate(Annot * annot)133 AnnotationAppearancePrivate::AnnotationAppearancePrivate(Annot *annot)
134 {
135     if (annot) {
136         appearance = annot->getAppearance();
137     } else {
138         appearance.setToNull();
139     }
140 }
141 // END AnnotationAppearancePrivate implementation
142 
143 // BEGIN AnnotationAppearance implementation
AnnotationAppearance(AnnotationAppearancePrivate * annotationAppearancePrivate)144 AnnotationAppearance::AnnotationAppearance(AnnotationAppearancePrivate *annotationAppearancePrivate) : d(annotationAppearancePrivate) { }
145 
~AnnotationAppearance()146 AnnotationAppearance::~AnnotationAppearance()
147 {
148     delete d;
149 }
150 // END AnnotationAppearance implementation
151 
152 // BEGIN Annotation implementation
AnnotationPrivate()153 AnnotationPrivate::AnnotationPrivate() : flags(0), revisionScope(Annotation::Root), revisionType(Annotation::None), pdfAnnot(nullptr), pdfPage(nullptr), parentDoc(nullptr) { }
154 
getRawDataFromQImage(const QImage & qimg,int bitsPerPixel,QByteArray * data,QByteArray * sMaskData)155 void getRawDataFromQImage(const QImage &qimg, int bitsPerPixel, QByteArray *data, QByteArray *sMaskData)
156 {
157     const int height = qimg.height();
158     const int width = qimg.width();
159 
160     switch (bitsPerPixel) {
161     case 1:
162         for (int line = 0; line < height; line++) {
163             const char *lineData = reinterpret_cast<const char *>(qimg.scanLine(line));
164             for (int offset = 0; offset < (width + 7) / 8; offset++) {
165                 data->append(lineData[offset]);
166             }
167         }
168         break;
169     case 8:
170     case 24:
171 #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
172         data->append((const char *)qimg.bits(), static_cast<int>(qimg.sizeInBytes()));
173 #else
174         data->append((const char *)qimg.bits(), qimg.byteCount());
175 #endif
176         break;
177     case 32:
178         for (int line = 0; line < height; line++) {
179             const QRgb *lineData = reinterpret_cast<const QRgb *>(qimg.scanLine(line));
180             for (int offset = 0; offset < width; offset++) {
181                 char a = (char)qAlpha(lineData[offset]);
182                 char r = (char)qRed(lineData[offset]);
183                 char g = (char)qGreen(lineData[offset]);
184                 char b = (char)qBlue(lineData[offset]);
185 
186                 data->append(r);
187                 data->append(g);
188                 data->append(b);
189 
190                 sMaskData->append(a);
191             }
192         }
193         break;
194     }
195 }
196 
addRevision(Annotation * ann,Annotation::RevScope scope,Annotation::RevType type)197 void AnnotationPrivate::addRevision(Annotation *ann, Annotation::RevScope scope, Annotation::RevType type)
198 {
199     /* Since ownership stays with the caller, create an alias of ann */
200     revisions.append(ann->d_ptr->makeAlias());
201 
202     /* Set revision properties */
203     revisionScope = scope;
204     revisionType = type;
205 }
206 
~AnnotationPrivate()207 AnnotationPrivate::~AnnotationPrivate()
208 {
209     // Delete all children revisions
210     qDeleteAll(revisions);
211 
212     // Release Annot object
213     if (pdfAnnot)
214         pdfAnnot->decRefCnt();
215 }
216 
tieToNativeAnnot(Annot * ann,::Page * page,Poppler::DocumentData * doc)217 void AnnotationPrivate::tieToNativeAnnot(Annot *ann, ::Page *page, Poppler::DocumentData *doc)
218 {
219     if (pdfAnnot) {
220         error(errIO, -1, "Annotation is already tied");
221         return;
222     }
223 
224     pdfAnnot = ann;
225     pdfPage = page;
226     parentDoc = doc;
227 
228     pdfAnnot->incRefCnt();
229 }
230 
231 /* This method is called when a new annotation is created, after pdfAnnot and
232  * pdfPage have been set */
flushBaseAnnotationProperties()233 void AnnotationPrivate::flushBaseAnnotationProperties()
234 {
235     Q_ASSERT(pdfPage);
236 
237     Annotation *q = makeAlias(); // Setters are defined in the public class
238 
239     // Since pdfAnnot has been set, this calls will write in the Annot object
240     q->setAuthor(author);
241     q->setContents(contents);
242     q->setUniqueName(uniqueName);
243     q->setModificationDate(modDate);
244     q->setCreationDate(creationDate);
245     q->setFlags(flags);
246     // q->setBoundary(boundary); -- already set by subclass-specific code
247     q->setStyle(style);
248     q->setPopup(popup);
249 
250     // Flush revisions
251     foreach (Annotation *r, revisions) {
252         // TODO: Flush revision
253         delete r; // Object is no longer needed
254     }
255 
256     delete q;
257 
258     // Clear some members to save memory
259     author.clear();
260     contents.clear();
261     uniqueName.clear();
262     revisions.clear();
263 }
264 
265 // Returns matrix to convert from user space coords (oriented according to the
266 // specified rotation) to normalized coords
fillNormalizationMTX(::Page * pdfPage,double MTX[6],int pageRotation)267 static void fillNormalizationMTX(::Page *pdfPage, double MTX[6], int pageRotation)
268 {
269     Q_ASSERT(pdfPage);
270 
271     // build a normalized transform matrix for this page at 100% scale
272     GfxState *gfxState = new GfxState(72.0, 72.0, pdfPage->getCropBox(), pageRotation, true);
273     const double *gfxCTM = gfxState->getCTM();
274 
275     double w = pdfPage->getCropWidth();
276     double h = pdfPage->getCropHeight();
277 
278     // Swap width and height if the page is rotated landscape or seascape
279     if (pageRotation == 90 || pageRotation == 270) {
280         double t = w;
281         w = h;
282         h = t;
283     }
284 
285     for (int i = 0; i < 6; i += 2) {
286         MTX[i] = gfxCTM[i] / w;
287         MTX[i + 1] = gfxCTM[i + 1] / h;
288     }
289     delete gfxState;
290 }
291 
292 // Returns matrix to convert from user space coords (i.e. those that are stored
293 // in the PDF file) to normalized coords (i.e. those that we expose to clients).
294 // This method also applies a rotation around the top-left corner if the
295 // FixedRotation flag is set.
fillTransformationMTX(double MTX[6]) const296 void AnnotationPrivate::fillTransformationMTX(double MTX[6]) const
297 {
298     Q_ASSERT(pdfPage);
299     Q_ASSERT(pdfAnnot);
300 
301     const int pageRotate = pdfPage->getRotate();
302 
303     if (pageRotate == 0 || (pdfAnnot->getFlags() & Annot::flagNoRotate) == 0) {
304         // Use the normalization matrix for this page's rotation
305         fillNormalizationMTX(pdfPage, MTX, pageRotate);
306     } else {
307         // Clients expect coordinates relative to this page's rotation, but
308         // FixedRotation annotations internally use unrotated coordinates:
309         // construct matrix to both normalize and rotate coordinates using the
310         // top-left corner as rotation pivot
311 
312         double MTXnorm[6];
313         fillNormalizationMTX(pdfPage, MTXnorm, pageRotate);
314 
315         QTransform transform(MTXnorm[0], MTXnorm[1], MTXnorm[2], MTXnorm[3], MTXnorm[4], MTXnorm[5]);
316         transform.translate(+pdfAnnot->getXMin(), +pdfAnnot->getYMax());
317         transform.rotate(pageRotate);
318         transform.translate(-pdfAnnot->getXMin(), -pdfAnnot->getYMax());
319 
320         MTX[0] = transform.m11();
321         MTX[1] = transform.m12();
322         MTX[2] = transform.m21();
323         MTX[3] = transform.m22();
324         MTX[4] = transform.dx();
325         MTX[5] = transform.dy();
326     }
327 }
328 
fromPdfRectangle(const PDFRectangle & r) const329 QRectF AnnotationPrivate::fromPdfRectangle(const PDFRectangle &r) const
330 {
331     double swp, MTX[6];
332     fillTransformationMTX(MTX);
333 
334     QPointF p1, p2;
335     XPDFReader::transform(MTX, r.x1, r.y1, p1);
336     XPDFReader::transform(MTX, r.x2, r.y2, p2);
337 
338     double tl_x = p1.x();
339     double tl_y = p1.y();
340     double br_x = p2.x();
341     double br_y = p2.y();
342 
343     if (tl_x > br_x) {
344         swp = tl_x;
345         tl_x = br_x;
346         br_x = swp;
347     }
348 
349     if (tl_y > br_y) {
350         swp = tl_y;
351         tl_y = br_y;
352         br_y = swp;
353     }
354 
355     return QRectF(QPointF(tl_x, tl_y), QPointF(br_x, br_y));
356 }
357 
358 // This function converts a boundary QRectF in normalized coords to a
359 // PDFRectangle in user coords. If the FixedRotation flag is set, this function
360 // also applies a rotation around the top-left corner: it's the inverse of
361 // the transformation produced by fillTransformationMTX, but we can't use
362 // fillTransformationMTX here because it relies on the native annotation
363 // object's boundary rect to be already set up.
boundaryToPdfRectangle(::Page * pdfPage,const QRectF & r,int rFlags)364 PDFRectangle boundaryToPdfRectangle(::Page *pdfPage, const QRectF &r, int rFlags)
365 {
366     Q_ASSERT(pdfPage);
367 
368     const double w = pdfPage->getCropWidth();
369     const double h = pdfPage->getCropHeight();
370 
371     if (w == 0 || h == 0) {
372         // page is broken, there's nothing to transform
373         return {};
374     }
375 
376     const int pageRotate = pdfPage->getRotate();
377 
378     double MTX[6];
379     fillNormalizationMTX(pdfPage, MTX, pageRotate);
380 
381     double tl_x, tl_y, br_x, br_y, swp;
382     XPDFReader::invTransform(MTX, r.topLeft(), tl_x, tl_y);
383     XPDFReader::invTransform(MTX, r.bottomRight(), br_x, br_y);
384 
385     if (tl_x > br_x) {
386         swp = tl_x;
387         tl_x = br_x;
388         br_x = swp;
389     }
390 
391     if (tl_y > br_y) {
392         swp = tl_y;
393         tl_y = br_y;
394         br_y = swp;
395     }
396 
397     const int rotationFixUp = (rFlags & Annotation::FixedRotation) ? pageRotate : 0;
398     const double width = br_x - tl_x;
399     const double height = br_y - tl_y;
400 
401     if (rotationFixUp == 0)
402         return PDFRectangle(tl_x, tl_y, br_x, br_y);
403     else if (rotationFixUp == 90)
404         return PDFRectangle(tl_x, tl_y - width, tl_x + height, tl_y);
405     else if (rotationFixUp == 180)
406         return PDFRectangle(br_x, tl_y - height, br_x + width, tl_y);
407     else // rotationFixUp == 270
408         return PDFRectangle(br_x, br_y - width, br_x + height, br_y);
409 }
410 
boundaryToPdfRectangle(const QRectF & r,int rFlags) const411 PDFRectangle AnnotationPrivate::boundaryToPdfRectangle(const QRectF &r, int rFlags) const
412 {
413     return Poppler::boundaryToPdfRectangle(pdfPage, r, rFlags);
414 }
415 
toAnnotPath(const QLinkedList<QPointF> & list) const416 AnnotPath *AnnotationPrivate::toAnnotPath(const QLinkedList<QPointF> &list) const
417 {
418     const int count = list.size();
419     std::vector<AnnotCoord> ac;
420     ac.reserve(count);
421 
422     double MTX[6];
423     fillTransformationMTX(MTX);
424 
425     foreach (const QPointF &p, list) {
426         double x, y;
427         XPDFReader::invTransform(MTX, p, x, y);
428         ac.emplace_back(x, y);
429     }
430 
431     return new AnnotPath(std::move(ac));
432 }
433 
findAnnotations(::Page * pdfPage,DocumentData * doc,const QSet<Annotation::SubType> & subtypes,int parentID)434 QList<Annotation *> AnnotationPrivate::findAnnotations(::Page *pdfPage, DocumentData *doc, const QSet<Annotation::SubType> &subtypes, int parentID)
435 {
436     Annots *annots = pdfPage->getAnnots();
437     const uint numAnnotations = annots->getNumAnnots();
438     if (numAnnotations == 0) {
439         return QList<Annotation *>();
440     }
441 
442     const bool wantTextAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AText);
443     const bool wantLineAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::ALine);
444     const bool wantGeomAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AGeom);
445     const bool wantHighlightAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AHighlight);
446     const bool wantStampAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AStamp);
447     const bool wantInkAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AInk);
448     const bool wantLinkAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::ALink);
449     const bool wantCaretAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::ACaret);
450     const bool wantFileAttachmentAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AFileAttachment);
451     const bool wantSoundAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::ASound);
452     const bool wantMovieAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AMovie);
453     const bool wantScreenAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AScreen);
454     const bool wantWidgetAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AWidget);
455 
456     // Create Annotation objects and tie to their native Annot
457     QList<Annotation *> res;
458     for (uint k = 0; k < numAnnotations; k++) {
459         // get the j-th annotation
460         Annot *ann = annots->getAnnot(k);
461         if (!ann) {
462             error(errInternal, -1, "Annot {0:ud} is null", k);
463             continue;
464         }
465 
466         // Check parent annotation
467         AnnotMarkup *markupann = dynamic_cast<AnnotMarkup *>(ann);
468         if (!markupann) {
469             // Assume it's a root annotation, and skip if user didn't request it
470             if (parentID != -1)
471                 continue;
472         } else if (markupann->getInReplyToID() != parentID)
473             continue;
474 
475         /* Create Annotation of the right subclass */
476         Annotation *annotation = nullptr;
477         Annot::AnnotSubtype subType = ann->getType();
478 
479         switch (subType) {
480         case Annot::typeText:
481             if (!wantTextAnnotations)
482                 continue;
483             annotation = new TextAnnotation(TextAnnotation::Linked);
484             break;
485         case Annot::typeFreeText:
486             if (!wantTextAnnotations)
487                 continue;
488             annotation = new TextAnnotation(TextAnnotation::InPlace);
489             break;
490         case Annot::typeLine:
491             if (!wantLineAnnotations)
492                 continue;
493             annotation = new LineAnnotation(LineAnnotation::StraightLine);
494             break;
495         case Annot::typePolygon:
496         case Annot::typePolyLine:
497             if (!wantLineAnnotations)
498                 continue;
499             annotation = new LineAnnotation(LineAnnotation::Polyline);
500             break;
501         case Annot::typeSquare:
502         case Annot::typeCircle:
503             if (!wantGeomAnnotations)
504                 continue;
505             annotation = new GeomAnnotation();
506             break;
507         case Annot::typeHighlight:
508         case Annot::typeUnderline:
509         case Annot::typeSquiggly:
510         case Annot::typeStrikeOut:
511             if (!wantHighlightAnnotations)
512                 continue;
513             annotation = new HighlightAnnotation();
514             break;
515         case Annot::typeStamp:
516             if (!wantStampAnnotations)
517                 continue;
518             annotation = new StampAnnotation();
519             break;
520         case Annot::typeInk:
521             if (!wantInkAnnotations)
522                 continue;
523             annotation = new InkAnnotation();
524             break;
525         case Annot::typeLink: /* TODO: Move logic to getters */
526         {
527             if (!wantLinkAnnotations)
528                 continue;
529             // parse Link params
530             AnnotLink *linkann = static_cast<AnnotLink *>(ann);
531             LinkAnnotation *l = new LinkAnnotation();
532             annotation = l;
533 
534             // -> hlMode
535             l->setLinkHighlightMode((LinkAnnotation::HighlightMode)linkann->getLinkEffect());
536 
537             // -> link region
538             // TODO
539 
540             // reading link action
541             if (linkann->getAction()) {
542                 Link *popplerLink = PageData::convertLinkActionToLink(linkann->getAction(), doc, QRectF());
543                 if (popplerLink) {
544                     l->setLinkDestination(popplerLink);
545                 }
546             }
547             break;
548         }
549         case Annot::typeCaret:
550             if (!wantCaretAnnotations)
551                 continue;
552             annotation = new CaretAnnotation();
553             break;
554         case Annot::typeFileAttachment: /* TODO: Move logic to getters */
555         {
556             if (!wantFileAttachmentAnnotations)
557                 continue;
558             AnnotFileAttachment *attachann = static_cast<AnnotFileAttachment *>(ann);
559             FileAttachmentAnnotation *f = new FileAttachmentAnnotation();
560             annotation = f;
561             // -> fileIcon
562             f->setFileIconName(QString::fromLatin1(attachann->getName()->c_str()));
563             // -> embeddedFile
564             FileSpec *filespec = new FileSpec(attachann->getFile());
565             f->setEmbeddedFile(new EmbeddedFile(*new EmbeddedFileData(filespec)));
566             break;
567         }
568         case Annot::typeSound: /* TODO: Move logic to getters */
569         {
570             if (!wantSoundAnnotations)
571                 continue;
572             AnnotSound *soundann = static_cast<AnnotSound *>(ann);
573             SoundAnnotation *s = new SoundAnnotation();
574             annotation = s;
575 
576             // -> soundIcon
577             s->setSoundIconName(QString::fromLatin1(soundann->getName()->c_str()));
578             // -> sound
579             s->setSound(new SoundObject(soundann->getSound()));
580             break;
581         }
582         case Annot::typeMovie: /* TODO: Move logic to getters */
583         {
584             if (!wantMovieAnnotations)
585                 continue;
586             AnnotMovie *movieann = static_cast<AnnotMovie *>(ann);
587             MovieAnnotation *m = new MovieAnnotation();
588             annotation = m;
589 
590             // -> movie
591             MovieObject *movie = new MovieObject(movieann);
592             m->setMovie(movie);
593             // -> movieTitle
594             const GooString *movietitle = movieann->getTitle();
595             if (movietitle)
596                 m->setMovieTitle(QString::fromLatin1(movietitle->c_str()));
597             break;
598         }
599         case Annot::typeScreen: {
600             if (!wantScreenAnnotations)
601                 continue;
602             AnnotScreen *screenann = static_cast<AnnotScreen *>(ann);
603             // TODO Support other link types than Link::Rendition in ScreenAnnotation
604             if (!screenann->getAction() || screenann->getAction()->getKind() != actionRendition)
605                 continue;
606             ScreenAnnotation *s = new ScreenAnnotation();
607             annotation = s;
608 
609             // -> screen
610             Link *popplerLink = PageData::convertLinkActionToLink(screenann->getAction(), doc, QRectF());
611             s->setAction(static_cast<Poppler::LinkRendition *>(popplerLink));
612 
613             // -> screenTitle
614             const GooString *screentitle = screenann->getTitle();
615             if (screentitle)
616                 s->setScreenTitle(UnicodeParsedString(screentitle));
617             break;
618         }
619         case Annot::typePopup:
620             continue; // popups are parsed by Annotation's window() getter
621         case Annot::typeUnknown:
622             continue; // special case for ignoring unknown annotations
623         case Annot::typeWidget:
624             if (!wantWidgetAnnotations)
625                 continue;
626             annotation = new WidgetAnnotation();
627             break;
628         case Annot::typeRichMedia: {
629             const AnnotRichMedia *annotRichMedia = static_cast<AnnotRichMedia *>(ann);
630 
631             RichMediaAnnotation *richMediaAnnotation = new RichMediaAnnotation;
632 
633             const AnnotRichMedia::Settings *annotSettings = annotRichMedia->getSettings();
634             if (annotSettings) {
635                 RichMediaAnnotation::Settings *settings = new RichMediaAnnotation::Settings;
636 
637                 if (annotSettings->getActivation()) {
638                     RichMediaAnnotation::Activation *activation = new RichMediaAnnotation::Activation;
639 
640                     switch (annotSettings->getActivation()->getCondition()) {
641                     case AnnotRichMedia::Activation::conditionPageOpened:
642                         activation->setCondition(RichMediaAnnotation::Activation::PageOpened);
643                         break;
644                     case AnnotRichMedia::Activation::conditionPageVisible:
645                         activation->setCondition(RichMediaAnnotation::Activation::PageVisible);
646                         break;
647                     case AnnotRichMedia::Activation::conditionUserAction:
648                         activation->setCondition(RichMediaAnnotation::Activation::UserAction);
649                         break;
650                     }
651 
652                     settings->setActivation(activation);
653                 }
654 
655                 if (annotSettings->getDeactivation()) {
656                     RichMediaAnnotation::Deactivation *deactivation = new RichMediaAnnotation::Deactivation;
657 
658                     switch (annotSettings->getDeactivation()->getCondition()) {
659                     case AnnotRichMedia::Deactivation::conditionPageClosed:
660                         deactivation->setCondition(RichMediaAnnotation::Deactivation::PageClosed);
661                         break;
662                     case AnnotRichMedia::Deactivation::conditionPageInvisible:
663                         deactivation->setCondition(RichMediaAnnotation::Deactivation::PageInvisible);
664                         break;
665                     case AnnotRichMedia::Deactivation::conditionUserAction:
666                         deactivation->setCondition(RichMediaAnnotation::Deactivation::UserAction);
667                         break;
668                     }
669 
670                     settings->setDeactivation(deactivation);
671                 }
672 
673                 richMediaAnnotation->setSettings(settings);
674             }
675 
676             const AnnotRichMedia::Content *annotContent = annotRichMedia->getContent();
677             if (annotContent) {
678                 RichMediaAnnotation::Content *content = new RichMediaAnnotation::Content;
679 
680                 const int configurationsCount = annotContent->getConfigurationsCount();
681                 if (configurationsCount > 0) {
682                     QList<RichMediaAnnotation::Configuration *> configurations;
683 
684                     for (int i = 0; i < configurationsCount; ++i) {
685                         const AnnotRichMedia::Configuration *annotConfiguration = annotContent->getConfiguration(i);
686                         if (!annotConfiguration)
687                             continue;
688 
689                         RichMediaAnnotation::Configuration *configuration = new RichMediaAnnotation::Configuration;
690 
691                         if (annotConfiguration->getName())
692                             configuration->setName(UnicodeParsedString(annotConfiguration->getName()));
693 
694                         switch (annotConfiguration->getType()) {
695                         case AnnotRichMedia::Configuration::type3D:
696                             configuration->setType(RichMediaAnnotation::Configuration::Type3D);
697                             break;
698                         case AnnotRichMedia::Configuration::typeFlash:
699                             configuration->setType(RichMediaAnnotation::Configuration::TypeFlash);
700                             break;
701                         case AnnotRichMedia::Configuration::typeSound:
702                             configuration->setType(RichMediaAnnotation::Configuration::TypeSound);
703                             break;
704                         case AnnotRichMedia::Configuration::typeVideo:
705                             configuration->setType(RichMediaAnnotation::Configuration::TypeVideo);
706                             break;
707                         }
708 
709                         const int instancesCount = annotConfiguration->getInstancesCount();
710                         if (instancesCount > 0) {
711                             QList<RichMediaAnnotation::Instance *> instances;
712 
713                             for (int j = 0; j < instancesCount; ++j) {
714                                 const AnnotRichMedia::Instance *annotInstance = annotConfiguration->getInstance(j);
715                                 if (!annotInstance)
716                                     continue;
717 
718                                 RichMediaAnnotation::Instance *instance = new RichMediaAnnotation::Instance;
719 
720                                 switch (annotInstance->getType()) {
721                                 case AnnotRichMedia::Instance::type3D:
722                                     instance->setType(RichMediaAnnotation::Instance::Type3D);
723                                     break;
724                                 case AnnotRichMedia::Instance::typeFlash:
725                                     instance->setType(RichMediaAnnotation::Instance::TypeFlash);
726                                     break;
727                                 case AnnotRichMedia::Instance::typeSound:
728                                     instance->setType(RichMediaAnnotation::Instance::TypeSound);
729                                     break;
730                                 case AnnotRichMedia::Instance::typeVideo:
731                                     instance->setType(RichMediaAnnotation::Instance::TypeVideo);
732                                     break;
733                                 }
734 
735                                 const AnnotRichMedia::Params *annotParams = annotInstance->getParams();
736                                 if (annotParams) {
737                                     RichMediaAnnotation::Params *params = new RichMediaAnnotation::Params;
738 
739                                     if (annotParams->getFlashVars())
740                                         params->setFlashVars(UnicodeParsedString(annotParams->getFlashVars()));
741 
742                                     instance->setParams(params);
743                                 }
744 
745                                 instances.append(instance);
746                             }
747 
748                             configuration->setInstances(instances);
749                         }
750 
751                         configurations.append(configuration);
752                     }
753 
754                     content->setConfigurations(configurations);
755                 }
756 
757                 const int assetsCount = annotContent->getAssetsCount();
758                 if (assetsCount > 0) {
759                     QList<RichMediaAnnotation::Asset *> assets;
760 
761                     for (int i = 0; i < assetsCount; ++i) {
762                         const AnnotRichMedia::Asset *annotAsset = annotContent->getAsset(i);
763                         if (!annotAsset)
764                             continue;
765 
766                         RichMediaAnnotation::Asset *asset = new RichMediaAnnotation::Asset;
767 
768                         if (annotAsset->getName())
769                             asset->setName(UnicodeParsedString(annotAsset->getName()));
770 
771                         FileSpec *fileSpec = new FileSpec(annotAsset->getFileSpec());
772                         asset->setEmbeddedFile(new EmbeddedFile(*new EmbeddedFileData(fileSpec)));
773 
774                         assets.append(asset);
775                     }
776 
777                     content->setAssets(assets);
778                 }
779 
780                 richMediaAnnotation->setContent(content);
781             }
782 
783             annotation = richMediaAnnotation;
784 
785             break;
786         }
787         default: {
788 #define CASE_FOR_TYPE(thetype)                                                                                                                                                                                                                 \
789     case Annot::type##thetype:                                                                                                                                                                                                                 \
790         error(errUnimplemented, -1, "Annotation " #thetype " not supported");                                                                                                                                                                  \
791         break;
792             switch (subType) {
793                 CASE_FOR_TYPE(PrinterMark)
794                 CASE_FOR_TYPE(TrapNet)
795                 CASE_FOR_TYPE(Watermark)
796                 CASE_FOR_TYPE(3D)
797             default:
798                 error(errUnimplemented, -1, "Annotation {0:d} not supported", subType);
799             }
800             continue;
801 #undef CASE_FOR_TYPE
802         }
803         }
804 
805         annotation->d_ptr->tieToNativeAnnot(ann, pdfPage, doc);
806         res.append(annotation);
807     }
808 
809     return res;
810 }
811 
pdfObjectReference() const812 Ref AnnotationPrivate::pdfObjectReference() const
813 {
814     if (pdfAnnot == nullptr) {
815         return Ref::INVALID();
816     }
817 
818     return pdfAnnot->getRef();
819 }
820 
additionalAction(Annotation::AdditionalActionType type) const821 Link *AnnotationPrivate::additionalAction(Annotation::AdditionalActionType type) const
822 {
823     if (pdfAnnot->getType() != Annot::typeScreen && pdfAnnot->getType() != Annot::typeWidget)
824         return nullptr;
825 
826     const Annot::AdditionalActionsType actionType = toPopplerAdditionalActionType(type);
827 
828     std::unique_ptr<::LinkAction> linkAction = nullptr;
829     if (pdfAnnot->getType() == Annot::typeScreen)
830         linkAction = static_cast<AnnotScreen *>(pdfAnnot)->getAdditionalAction(actionType);
831     else
832         linkAction = static_cast<AnnotWidget *>(pdfAnnot)->getAdditionalAction(actionType);
833 
834     Link *link = nullptr;
835 
836     if (linkAction)
837         link = PageData::convertLinkActionToLink(linkAction.get(), parentDoc, QRectF());
838 
839     return link;
840 }
841 
addAnnotationToPage(::Page * pdfPage,DocumentData * doc,const Annotation * ann)842 void AnnotationPrivate::addAnnotationToPage(::Page *pdfPage, DocumentData *doc, const Annotation *ann)
843 {
844     if (ann->d_ptr->pdfAnnot != nullptr) {
845         error(errIO, -1, "Annotation is already tied");
846         return;
847     }
848 
849     // Unimplemented annotations can't be created by the user because their ctor
850     // is private. Therefore, createNativeAnnot will never return 0
851     Annot *nativeAnnot = ann->d_ptr->createNativeAnnot(pdfPage, doc);
852     Q_ASSERT(nativeAnnot);
853 
854     if (ann->d_ptr->annotationAppearance.isStream())
855         nativeAnnot->setNewAppearance(ann->d_ptr->annotationAppearance.copy());
856 
857     pdfPage->addAnnot(nativeAnnot);
858 }
859 
removeAnnotationFromPage(::Page * pdfPage,const Annotation * ann)860 void AnnotationPrivate::removeAnnotationFromPage(::Page *pdfPage, const Annotation *ann)
861 {
862     if (ann->d_ptr->pdfAnnot == nullptr) {
863         error(errIO, -1, "Annotation is not tied");
864         return;
865     }
866 
867     if (ann->d_ptr->pdfPage != pdfPage) {
868         error(errIO, -1, "Annotation doesn't belong to the specified page");
869         return;
870     }
871 
872     // Remove annotation
873     pdfPage->removeAnnot(ann->d_ptr->pdfAnnot);
874 
875     // Destroy object
876     delete ann;
877 }
878 
879 class Annotation::Style::Private : public QSharedData
880 {
881 public:
Private()882     Private() : opacity(1.0), width(1.0), lineStyle(Solid), xCorners(0.0), yCorners(0.0), lineEffect(NoEffect), effectIntensity(1.0)
883     {
884         dashArray.resize(1);
885         dashArray[0] = 3;
886     }
887 
888     QColor color;
889     double opacity;
890     double width;
891     Annotation::LineStyle lineStyle;
892     double xCorners;
893     double yCorners;
894     QVector<double> dashArray;
895     Annotation::LineEffect lineEffect;
896     double effectIntensity;
897 };
898 
Style()899 Annotation::Style::Style() : d(new Private) { }
900 
Style(const Style & other)901 Annotation::Style::Style(const Style &other) : d(other.d) { }
902 
operator =(const Style & other)903 Annotation::Style &Annotation::Style::operator=(const Style &other)
904 {
905     if (this != &other)
906         d = other.d;
907 
908     return *this;
909 }
910 
~Style()911 Annotation::Style::~Style() { }
912 
color() const913 QColor Annotation::Style::color() const
914 {
915     return d->color;
916 }
917 
setColor(const QColor & color)918 void Annotation::Style::setColor(const QColor &color)
919 {
920     d->color = color;
921 }
922 
opacity() const923 double Annotation::Style::opacity() const
924 {
925     return d->opacity;
926 }
927 
setOpacity(double opacity)928 void Annotation::Style::setOpacity(double opacity)
929 {
930     d->opacity = opacity;
931 }
932 
width() const933 double Annotation::Style::width() const
934 {
935     return d->width;
936 }
937 
setWidth(double width)938 void Annotation::Style::setWidth(double width)
939 {
940     d->width = width;
941 }
942 
lineStyle() const943 Annotation::LineStyle Annotation::Style::lineStyle() const
944 {
945     return d->lineStyle;
946 }
947 
setLineStyle(Annotation::LineStyle style)948 void Annotation::Style::setLineStyle(Annotation::LineStyle style)
949 {
950     d->lineStyle = style;
951 }
952 
xCorners() const953 double Annotation::Style::xCorners() const
954 {
955     return d->xCorners;
956 }
957 
setXCorners(double radius)958 void Annotation::Style::setXCorners(double radius)
959 {
960     d->xCorners = radius;
961 }
962 
yCorners() const963 double Annotation::Style::yCorners() const
964 {
965     return d->yCorners;
966 }
967 
setYCorners(double radius)968 void Annotation::Style::setYCorners(double radius)
969 {
970     d->yCorners = radius;
971 }
972 
dashArray() const973 const QVector<double> &Annotation::Style::dashArray() const
974 {
975     return d->dashArray;
976 }
977 
setDashArray(const QVector<double> & array)978 void Annotation::Style::setDashArray(const QVector<double> &array)
979 {
980     d->dashArray = array;
981 }
982 
lineEffect() const983 Annotation::LineEffect Annotation::Style::lineEffect() const
984 {
985     return d->lineEffect;
986 }
987 
setLineEffect(Annotation::LineEffect effect)988 void Annotation::Style::setLineEffect(Annotation::LineEffect effect)
989 {
990     d->lineEffect = effect;
991 }
992 
effectIntensity() const993 double Annotation::Style::effectIntensity() const
994 {
995     return d->effectIntensity;
996 }
997 
setEffectIntensity(double intens)998 void Annotation::Style::setEffectIntensity(double intens)
999 {
1000     d->effectIntensity = intens;
1001 }
1002 
1003 class Annotation::Popup::Private : public QSharedData
1004 {
1005 public:
Private()1006     Private() : flags(-1) { }
1007 
1008     int flags;
1009     QRectF geometry;
1010     QString title;
1011     QString summary;
1012     QString text;
1013 };
1014 
Popup()1015 Annotation::Popup::Popup() : d(new Private) { }
1016 
Popup(const Popup & other)1017 Annotation::Popup::Popup(const Popup &other) : d(other.d) { }
1018 
operator =(const Popup & other)1019 Annotation::Popup &Annotation::Popup::operator=(const Popup &other)
1020 {
1021     if (this != &other)
1022         d = other.d;
1023 
1024     return *this;
1025 }
1026 
~Popup()1027 Annotation::Popup::~Popup() { }
1028 
flags() const1029 int Annotation::Popup::flags() const
1030 {
1031     return d->flags;
1032 }
1033 
setFlags(int flags)1034 void Annotation::Popup::setFlags(int flags)
1035 {
1036     d->flags = flags;
1037 }
1038 
geometry() const1039 QRectF Annotation::Popup::geometry() const
1040 {
1041     return d->geometry;
1042 }
1043 
setGeometry(const QRectF & geom)1044 void Annotation::Popup::setGeometry(const QRectF &geom)
1045 {
1046     d->geometry = geom;
1047 }
1048 
title() const1049 QString Annotation::Popup::title() const
1050 {
1051     return d->title;
1052 }
1053 
setTitle(const QString & title)1054 void Annotation::Popup::setTitle(const QString &title)
1055 {
1056     d->title = title;
1057 }
1058 
summary() const1059 QString Annotation::Popup::summary() const
1060 {
1061     return d->summary;
1062 }
1063 
setSummary(const QString & summary)1064 void Annotation::Popup::setSummary(const QString &summary)
1065 {
1066     d->summary = summary;
1067 }
1068 
text() const1069 QString Annotation::Popup::text() const
1070 {
1071     return d->text;
1072 }
1073 
setText(const QString & text)1074 void Annotation::Popup::setText(const QString &text)
1075 {
1076     d->text = text;
1077 }
1078 
Annotation(AnnotationPrivate & dd)1079 Annotation::Annotation(AnnotationPrivate &dd) : d_ptr(&dd) { }
1080 
~Annotation()1081 Annotation::~Annotation() { }
1082 
Annotation(AnnotationPrivate & dd,const QDomNode & annNode)1083 Annotation::Annotation(AnnotationPrivate &dd, const QDomNode &annNode) : d_ptr(&dd)
1084 {
1085     Q_D(Annotation);
1086 
1087     // get the [base] element of the annotation node
1088     QDomElement e = AnnotationUtils::findChildElement(annNode, QStringLiteral("base"));
1089     if (e.isNull())
1090         return;
1091 
1092     Style s;
1093     Popup w;
1094 
1095     // parse -contents- attributes
1096     if (e.hasAttribute(QStringLiteral("author")))
1097         setAuthor(e.attribute(QStringLiteral("author")));
1098     if (e.hasAttribute(QStringLiteral("contents")))
1099         setContents(e.attribute(QStringLiteral("contents")));
1100     if (e.hasAttribute(QStringLiteral("uniqueName")))
1101         setUniqueName(e.attribute(QStringLiteral("uniqueName")));
1102     if (e.hasAttribute(QStringLiteral("modifyDate"))) {
1103         QDateTime dt = QDateTime::fromString(e.attribute(QStringLiteral("modifyDate")));
1104         if (!dt.isValid()) {
1105             dt = QDateTime::fromString(e.attribute(QStringLiteral("modifyDate")), Qt::ISODate);
1106         }
1107         setModificationDate(dt);
1108     }
1109     if (e.hasAttribute(QStringLiteral("creationDate"))) {
1110         QDateTime dt = QDateTime::fromString(e.attribute(QStringLiteral("creationDate")));
1111         if (!dt.isValid()) {
1112             dt = QDateTime::fromString(e.attribute(QStringLiteral("creationDate")), Qt::ISODate);
1113         }
1114         setCreationDate(dt);
1115     }
1116 
1117     // parse -other- attributes
1118     if (e.hasAttribute(QStringLiteral("flags")))
1119         setFlags(e.attribute(QStringLiteral("flags")).toInt());
1120     if (e.hasAttribute(QStringLiteral("color")))
1121         s.setColor(QColor(e.attribute(QStringLiteral("color"))));
1122     if (e.hasAttribute(QStringLiteral("opacity")))
1123         s.setOpacity(e.attribute(QStringLiteral("opacity")).toDouble());
1124 
1125     // parse -the-subnodes- (describing Style, Window, Revision(s) structures)
1126     // Note: all subnodes if present must be 'attributes complete'
1127     QDomNode eSubNode = e.firstChild();
1128     while (eSubNode.isElement()) {
1129         QDomElement ee = eSubNode.toElement();
1130         eSubNode = eSubNode.nextSibling();
1131 
1132         // parse boundary
1133         if (ee.tagName() == QLatin1String("boundary")) {
1134             QRectF brect;
1135             brect.setLeft(ee.attribute(QStringLiteral("l")).toDouble());
1136             brect.setTop(ee.attribute(QStringLiteral("t")).toDouble());
1137             brect.setRight(ee.attribute(QStringLiteral("r")).toDouble());
1138             brect.setBottom(ee.attribute(QStringLiteral("b")).toDouble());
1139             setBoundary(brect);
1140         }
1141         // parse penStyle if not default
1142         else if (ee.tagName() == QLatin1String("penStyle")) {
1143             s.setWidth(ee.attribute(QStringLiteral("width")).toDouble());
1144             s.setLineStyle((LineStyle)ee.attribute(QStringLiteral("style")).toInt());
1145             s.setXCorners(ee.attribute(QStringLiteral("xcr")).toDouble());
1146             s.setYCorners(ee.attribute(QStringLiteral("ycr")).toDouble());
1147 
1148             // Try to parse dash array (new format)
1149             QVector<double> dashArray;
1150 
1151             QDomNode eeSubNode = ee.firstChild();
1152             while (eeSubNode.isElement()) {
1153                 QDomElement eee = eeSubNode.toElement();
1154                 eeSubNode = eeSubNode.nextSibling();
1155 
1156                 if (eee.tagName() != QLatin1String("dashsegm"))
1157                     continue;
1158 
1159                 dashArray.append(eee.attribute(QStringLiteral("len")).toDouble());
1160             }
1161 
1162             // If no segments were found use marks/spaces (old format)
1163             if (dashArray.size() == 0) {
1164                 dashArray.append(ee.attribute(QStringLiteral("marks")).toDouble());
1165                 dashArray.append(ee.attribute(QStringLiteral("spaces")).toDouble());
1166             }
1167 
1168             s.setDashArray(dashArray);
1169         }
1170         // parse effectStyle if not default
1171         else if (ee.tagName() == QLatin1String("penEffect")) {
1172             s.setLineEffect((LineEffect)ee.attribute(QStringLiteral("effect")).toInt());
1173             s.setEffectIntensity(ee.attribute(QStringLiteral("intensity")).toDouble());
1174         }
1175         // parse window if present
1176         else if (ee.tagName() == QLatin1String("window")) {
1177             QRectF geom;
1178             geom.setX(ee.attribute(QStringLiteral("top")).toDouble());
1179             geom.setY(ee.attribute(QStringLiteral("left")).toDouble());
1180 
1181             if (ee.hasAttribute(QStringLiteral("widthDouble")))
1182                 geom.setWidth(ee.attribute(QStringLiteral("widthDouble")).toDouble());
1183             else
1184                 geom.setWidth(ee.attribute(QStringLiteral("width")).toDouble());
1185 
1186             if (ee.hasAttribute(QStringLiteral("widthDouble")))
1187                 geom.setHeight(ee.attribute(QStringLiteral("heightDouble")).toDouble());
1188             else
1189                 geom.setHeight(ee.attribute(QStringLiteral("height")).toDouble());
1190 
1191             w.setGeometry(geom);
1192 
1193             w.setFlags(ee.attribute(QStringLiteral("flags")).toInt());
1194             w.setTitle(ee.attribute(QStringLiteral("title")));
1195             w.setSummary(ee.attribute(QStringLiteral("summary")));
1196             // parse window subnodes
1197             QDomNode winNode = ee.firstChild();
1198             for (; winNode.isElement(); winNode = winNode.nextSibling()) {
1199                 QDomElement winElement = winNode.toElement();
1200                 if (winElement.tagName() == QLatin1String("text"))
1201                     w.setText(winElement.firstChild().toCDATASection().data());
1202             }
1203         }
1204     }
1205 
1206     setStyle(s); // assign parsed style
1207     setPopup(w); // assign parsed window
1208 
1209     // get the [revisions] element of the annotation node
1210     QDomNode revNode = annNode.firstChild();
1211     for (; revNode.isElement(); revNode = revNode.nextSibling()) {
1212         QDomElement revElement = revNode.toElement();
1213         if (revElement.tagName() != QLatin1String("revision"))
1214             continue;
1215 
1216         Annotation *reply = AnnotationUtils::createAnnotation(revElement);
1217 
1218         if (reply) // if annotation is valid, add as a revision of this annotation
1219         {
1220             RevScope scope = (RevScope)revElement.attribute(QStringLiteral("revScope")).toInt();
1221             RevType type = (RevType)revElement.attribute(QStringLiteral("revType")).toInt();
1222             d->addRevision(reply, scope, type);
1223             delete reply;
1224         }
1225     }
1226 }
1227 
storeBaseAnnotationProperties(QDomNode & annNode,QDomDocument & document) const1228 void Annotation::storeBaseAnnotationProperties(QDomNode &annNode, QDomDocument &document) const
1229 {
1230     // create [base] element of the annotation node
1231     QDomElement e = document.createElement(QStringLiteral("base"));
1232     annNode.appendChild(e);
1233 
1234     const Style s = style();
1235     const Popup w = popup();
1236 
1237     // store -contents- attributes
1238     if (!author().isEmpty())
1239         e.setAttribute(QStringLiteral("author"), author());
1240     if (!contents().isEmpty())
1241         e.setAttribute(QStringLiteral("contents"), contents());
1242     if (!uniqueName().isEmpty())
1243         e.setAttribute(QStringLiteral("uniqueName"), uniqueName());
1244     if (modificationDate().isValid())
1245         e.setAttribute(QStringLiteral("modifyDate"), modificationDate().toString());
1246     if (creationDate().isValid())
1247         e.setAttribute(QStringLiteral("creationDate"), creationDate().toString());
1248 
1249     // store -other- attributes
1250     if (flags())
1251         e.setAttribute(QStringLiteral("flags"), flags());
1252     if (s.color().isValid())
1253         e.setAttribute(QStringLiteral("color"), s.color().name());
1254     if (s.opacity() != 1.0)
1255         e.setAttribute(QStringLiteral("opacity"), QString::number(s.opacity()));
1256 
1257     // Sub-Node-1 - boundary
1258     const QRectF brect = boundary();
1259     QDomElement bE = document.createElement(QStringLiteral("boundary"));
1260     e.appendChild(bE);
1261     bE.setAttribute(QStringLiteral("l"), QString::number((double)brect.left()));
1262     bE.setAttribute(QStringLiteral("t"), QString::number((double)brect.top()));
1263     bE.setAttribute(QStringLiteral("r"), QString::number((double)brect.right()));
1264     bE.setAttribute(QStringLiteral("b"), QString::number((double)brect.bottom()));
1265 
1266     // Sub-Node-2 - penStyle
1267     const QVector<double> &dashArray = s.dashArray();
1268     if (s.width() != 1 || s.lineStyle() != Solid || s.xCorners() != 0 || s.yCorners() != 0.0 || dashArray.size() != 1 || dashArray[0] != 3) {
1269         QDomElement psE = document.createElement(QStringLiteral("penStyle"));
1270         e.appendChild(psE);
1271         psE.setAttribute(QStringLiteral("width"), QString::number(s.width()));
1272         psE.setAttribute(QStringLiteral("style"), (int)s.lineStyle());
1273         psE.setAttribute(QStringLiteral("xcr"), QString::number(s.xCorners()));
1274         psE.setAttribute(QStringLiteral("ycr"), QString::number(s.yCorners()));
1275 
1276         int marks = 3, spaces = 0; // Do not break code relying on marks/spaces
1277         if (dashArray.size() != 0)
1278             marks = (int)dashArray[0];
1279         if (dashArray.size() > 1)
1280             spaces = (int)dashArray[1];
1281 
1282         psE.setAttribute(QStringLiteral("marks"), marks);
1283         psE.setAttribute(QStringLiteral("spaces"), spaces);
1284 
1285         foreach (double segm, dashArray) {
1286             QDomElement pattE = document.createElement(QStringLiteral("dashsegm"));
1287             pattE.setAttribute(QStringLiteral("len"), QString::number(segm));
1288             psE.appendChild(pattE);
1289         }
1290     }
1291 
1292     // Sub-Node-3 - penEffect
1293     if (s.lineEffect() != NoEffect || s.effectIntensity() != 1.0) {
1294         QDomElement peE = document.createElement(QStringLiteral("penEffect"));
1295         e.appendChild(peE);
1296         peE.setAttribute(QStringLiteral("effect"), (int)s.lineEffect());
1297         peE.setAttribute(QStringLiteral("intensity"), QString::number(s.effectIntensity()));
1298     }
1299 
1300     // Sub-Node-4 - window
1301     if (w.flags() != -1 || !w.title().isEmpty() || !w.summary().isEmpty() || !w.text().isEmpty()) {
1302         QDomElement wE = document.createElement(QStringLiteral("window"));
1303         const QRectF geom = w.geometry();
1304         e.appendChild(wE);
1305         wE.setAttribute(QStringLiteral("flags"), w.flags());
1306         wE.setAttribute(QStringLiteral("top"), QString::number(geom.x()));
1307         wE.setAttribute(QStringLiteral("left"), QString::number(geom.y()));
1308         wE.setAttribute(QStringLiteral("width"), (int)geom.width());
1309         wE.setAttribute(QStringLiteral("height"), (int)geom.height());
1310         wE.setAttribute(QStringLiteral("widthDouble"), QString::number(geom.width()));
1311         wE.setAttribute(QStringLiteral("heightDouble"), QString::number(geom.height()));
1312         wE.setAttribute(QStringLiteral("title"), w.title());
1313         wE.setAttribute(QStringLiteral("summary"), w.summary());
1314         // store window.text as a subnode, because we need escaped data
1315         if (!w.text().isEmpty()) {
1316             QDomElement escapedText = document.createElement(QStringLiteral("text"));
1317             wE.appendChild(escapedText);
1318             QDomCDATASection textCData = document.createCDATASection(w.text());
1319             escapedText.appendChild(textCData);
1320         }
1321     }
1322 
1323     const QList<Annotation *> revs = revisions();
1324 
1325     // create [revision] element of the annotation node (if any)
1326     if (revs.isEmpty())
1327         return;
1328 
1329     // add all revisions as children of revisions element
1330     foreach (const Annotation *rev, revs) {
1331         QDomElement r = document.createElement(QStringLiteral("revision"));
1332         annNode.appendChild(r);
1333         // set element attributes
1334         r.setAttribute(QStringLiteral("revScope"), (int)rev->revisionScope());
1335         r.setAttribute(QStringLiteral("revType"), (int)rev->revisionType());
1336         // use revision as the annotation element, so fill it up
1337         AnnotationUtils::storeAnnotation(rev, r, document);
1338         delete rev;
1339     }
1340 }
1341 
author() const1342 QString Annotation::author() const
1343 {
1344     Q_D(const Annotation);
1345 
1346     if (!d->pdfAnnot)
1347         return d->author;
1348 
1349     const AnnotMarkup *markupann = dynamic_cast<const AnnotMarkup *>(d->pdfAnnot);
1350     return markupann ? UnicodeParsedString(markupann->getLabel()) : QString();
1351 }
1352 
setAuthor(const QString & author)1353 void Annotation::setAuthor(const QString &author)
1354 {
1355     Q_D(Annotation);
1356 
1357     if (!d->pdfAnnot) {
1358         d->author = author;
1359         return;
1360     }
1361 
1362     AnnotMarkup *markupann = dynamic_cast<AnnotMarkup *>(d->pdfAnnot);
1363     if (markupann) {
1364         GooString *s = QStringToUnicodeGooString(author);
1365         markupann->setLabel(s);
1366         delete s;
1367     }
1368 }
1369 
contents() const1370 QString Annotation::contents() const
1371 {
1372     Q_D(const Annotation);
1373 
1374     if (!d->pdfAnnot)
1375         return d->contents;
1376 
1377     return UnicodeParsedString(d->pdfAnnot->getContents());
1378 }
1379 
setContents(const QString & contents)1380 void Annotation::setContents(const QString &contents)
1381 {
1382     Q_D(Annotation);
1383 
1384     if (!d->pdfAnnot) {
1385         d->contents = contents;
1386         return;
1387     }
1388 
1389     GooString *s = QStringToUnicodeGooString(contents);
1390     d->pdfAnnot->setContents(s);
1391     delete s;
1392 }
1393 
uniqueName() const1394 QString Annotation::uniqueName() const
1395 {
1396     Q_D(const Annotation);
1397 
1398     if (!d->pdfAnnot)
1399         return d->uniqueName;
1400 
1401     return UnicodeParsedString(d->pdfAnnot->getName());
1402 }
1403 
setUniqueName(const QString & uniqueName)1404 void Annotation::setUniqueName(const QString &uniqueName)
1405 {
1406     Q_D(Annotation);
1407 
1408     if (!d->pdfAnnot) {
1409         d->uniqueName = uniqueName;
1410         return;
1411     }
1412 
1413     QByteArray ascii = uniqueName.toLatin1();
1414     GooString s(ascii.constData());
1415     d->pdfAnnot->setName(&s);
1416 }
1417 
modificationDate() const1418 QDateTime Annotation::modificationDate() const
1419 {
1420     Q_D(const Annotation);
1421 
1422     if (!d->pdfAnnot)
1423         return d->modDate;
1424 
1425     if (d->pdfAnnot->getModified())
1426         return convertDate(d->pdfAnnot->getModified()->c_str());
1427     else
1428         return QDateTime();
1429 }
1430 
setModificationDate(const QDateTime & date)1431 void Annotation::setModificationDate(const QDateTime &date)
1432 {
1433     Q_D(Annotation);
1434 
1435     if (!d->pdfAnnot) {
1436         d->modDate = date;
1437         return;
1438     }
1439 
1440     if (d->pdfAnnot) {
1441         if (date.isValid()) {
1442             const time_t t = date.toSecsSinceEpoch();
1443             GooString *s = timeToDateString(&t);
1444             d->pdfAnnot->setModified(s);
1445             delete s;
1446         } else {
1447             d->pdfAnnot->setModified(nullptr);
1448         }
1449     }
1450 }
1451 
creationDate() const1452 QDateTime Annotation::creationDate() const
1453 {
1454     Q_D(const Annotation);
1455 
1456     if (!d->pdfAnnot)
1457         return d->creationDate;
1458 
1459     const AnnotMarkup *markupann = dynamic_cast<const AnnotMarkup *>(d->pdfAnnot);
1460 
1461     if (markupann && markupann->getDate())
1462         return convertDate(markupann->getDate()->c_str());
1463 
1464     return modificationDate();
1465 }
1466 
setCreationDate(const QDateTime & date)1467 void Annotation::setCreationDate(const QDateTime &date)
1468 {
1469     Q_D(Annotation);
1470 
1471     if (!d->pdfAnnot) {
1472         d->creationDate = date;
1473         return;
1474     }
1475 
1476     AnnotMarkup *markupann = dynamic_cast<AnnotMarkup *>(d->pdfAnnot);
1477     if (markupann) {
1478         if (date.isValid()) {
1479             const time_t t = date.toSecsSinceEpoch();
1480             GooString *s = timeToDateString(&t);
1481             markupann->setDate(s);
1482             delete s;
1483         } else {
1484             markupann->setDate(nullptr);
1485         }
1486     }
1487 }
1488 
fromPdfFlags(int flags)1489 static int fromPdfFlags(int flags)
1490 {
1491     int qtflags = 0;
1492 
1493     if (flags & Annot::flagHidden)
1494         qtflags |= Annotation::Hidden;
1495     if (flags & Annot::flagNoZoom)
1496         qtflags |= Annotation::FixedSize;
1497     if (flags & Annot::flagNoRotate)
1498         qtflags |= Annotation::FixedRotation;
1499     if (!(flags & Annot::flagPrint))
1500         qtflags |= Annotation::DenyPrint;
1501     if (flags & Annot::flagReadOnly)
1502         qtflags |= (Annotation::DenyWrite | Annotation::DenyDelete);
1503     if (flags & Annot::flagLocked)
1504         qtflags |= Annotation::DenyDelete;
1505     if (flags & Annot::flagToggleNoView)
1506         qtflags |= Annotation::ToggleHidingOnMouse;
1507 
1508     return qtflags;
1509 }
1510 
toPdfFlags(int qtflags)1511 static int toPdfFlags(int qtflags)
1512 {
1513     int flags = 0;
1514 
1515     if (qtflags & Annotation::Hidden)
1516         flags |= Annot::flagHidden;
1517     if (qtflags & Annotation::FixedSize)
1518         flags |= Annot::flagNoZoom;
1519     if (qtflags & Annotation::FixedRotation)
1520         flags |= Annot::flagNoRotate;
1521     if (!(qtflags & Annotation::DenyPrint))
1522         flags |= Annot::flagPrint;
1523     if (qtflags & Annotation::DenyWrite)
1524         flags |= Annot::flagReadOnly;
1525     if (qtflags & Annotation::DenyDelete)
1526         flags |= Annot::flagLocked;
1527     if (qtflags & Annotation::ToggleHidingOnMouse)
1528         flags |= Annot::flagToggleNoView;
1529 
1530     return flags;
1531 }
1532 
flags() const1533 int Annotation::flags() const
1534 {
1535     Q_D(const Annotation);
1536 
1537     if (!d->pdfAnnot)
1538         return d->flags;
1539 
1540     return fromPdfFlags(d->pdfAnnot->getFlags());
1541 }
1542 
setFlags(int flags)1543 void Annotation::setFlags(int flags)
1544 {
1545     Q_D(Annotation);
1546 
1547     if (!d->pdfAnnot) {
1548         d->flags = flags;
1549         return;
1550     }
1551 
1552     d->pdfAnnot->setFlags(toPdfFlags(flags));
1553 }
1554 
boundary() const1555 QRectF Annotation::boundary() const
1556 {
1557     Q_D(const Annotation);
1558 
1559     if (!d->pdfAnnot)
1560         return d->boundary;
1561 
1562     const PDFRectangle *rect = d->pdfAnnot->getRect();
1563     return d->fromPdfRectangle(*rect);
1564 }
1565 
setBoundary(const QRectF & boundary)1566 void Annotation::setBoundary(const QRectF &boundary)
1567 {
1568     Q_D(Annotation);
1569 
1570     if (!d->pdfAnnot) {
1571         d->boundary = boundary;
1572         return;
1573     }
1574 
1575     PDFRectangle rect = d->boundaryToPdfRectangle(boundary, flags());
1576     d->pdfAnnot->setRect(&rect);
1577 }
1578 
style() const1579 Annotation::Style Annotation::style() const
1580 {
1581     Q_D(const Annotation);
1582 
1583     if (!d->pdfAnnot)
1584         return d->style;
1585 
1586     Style s;
1587     s.setColor(convertAnnotColor(d->pdfAnnot->getColor()));
1588 
1589     const AnnotMarkup *markupann = dynamic_cast<const AnnotMarkup *>(d->pdfAnnot);
1590     if (markupann)
1591         s.setOpacity(markupann->getOpacity());
1592 
1593     const AnnotBorder *border = d->pdfAnnot->getBorder();
1594     if (border) {
1595         if (border->getType() == AnnotBorder::typeArray) {
1596             const AnnotBorderArray *border_array = static_cast<const AnnotBorderArray *>(border);
1597             s.setXCorners(border_array->getHorizontalCorner());
1598             s.setYCorners(border_array->getVerticalCorner());
1599         }
1600 
1601         s.setWidth(border->getWidth());
1602         s.setLineStyle((Annotation::LineStyle)(1 << border->getStyle()));
1603 
1604         const int dashArrLen = border->getDashLength();
1605         const double *dashArrData = border->getDash();
1606         QVector<double> dashArrVect(dashArrLen);
1607         for (int i = 0; i < dashArrLen; ++i)
1608             dashArrVect[i] = dashArrData[i];
1609         s.setDashArray(dashArrVect);
1610     }
1611 
1612     AnnotBorderEffect *border_effect;
1613     switch (d->pdfAnnot->getType()) {
1614     case Annot::typeFreeText:
1615         border_effect = static_cast<AnnotFreeText *>(d->pdfAnnot)->getBorderEffect();
1616         break;
1617     case Annot::typeSquare:
1618     case Annot::typeCircle:
1619         border_effect = static_cast<AnnotGeometry *>(d->pdfAnnot)->getBorderEffect();
1620         break;
1621     default:
1622         border_effect = nullptr;
1623     }
1624     if (border_effect) {
1625         s.setLineEffect((Annotation::LineEffect)border_effect->getEffectType());
1626         s.setEffectIntensity(border_effect->getIntensity());
1627     }
1628 
1629     return s;
1630 }
1631 
setStyle(const Annotation::Style & style)1632 void Annotation::setStyle(const Annotation::Style &style)
1633 {
1634     Q_D(Annotation);
1635 
1636     if (!d->pdfAnnot) {
1637         d->style = style;
1638         return;
1639     }
1640 
1641     d->pdfAnnot->setColor(convertQColor(style.color()));
1642 
1643     AnnotMarkup *markupann = dynamic_cast<AnnotMarkup *>(d->pdfAnnot);
1644     if (markupann)
1645         markupann->setOpacity(style.opacity());
1646 
1647     auto border = std::make_unique<AnnotBorderArray>();
1648     border->setWidth(style.width());
1649     border->setHorizontalCorner(style.xCorners());
1650     border->setVerticalCorner(style.yCorners());
1651     d->pdfAnnot->setBorder(std::move(border));
1652 }
1653 
popup() const1654 Annotation::Popup Annotation::popup() const
1655 {
1656     Q_D(const Annotation);
1657 
1658     if (!d->pdfAnnot)
1659         return d->popup;
1660 
1661     Popup w;
1662     AnnotPopup *popup = nullptr;
1663     int flags = -1; // Not initialized
1664 
1665     const AnnotMarkup *markupann = dynamic_cast<const AnnotMarkup *>(d->pdfAnnot);
1666     if (markupann) {
1667         popup = markupann->getPopup();
1668         w.setSummary(UnicodeParsedString(markupann->getSubject()));
1669     }
1670 
1671     if (popup) {
1672         flags = fromPdfFlags(popup->getFlags()) & (Annotation::Hidden | Annotation::FixedSize | Annotation::FixedRotation);
1673 
1674         if (!popup->getOpen())
1675             flags |= Annotation::Hidden;
1676 
1677         const PDFRectangle *rect = popup->getRect();
1678         w.setGeometry(d->fromPdfRectangle(*rect));
1679     }
1680 
1681     if (d->pdfAnnot->getType() == Annot::typeText) {
1682         const AnnotText *textann = static_cast<const AnnotText *>(d->pdfAnnot);
1683 
1684         // Text annotations default to same rect as annotation
1685         if (flags == -1) {
1686             flags = 0;
1687             w.setGeometry(boundary());
1688         }
1689 
1690         // If text is not 'opened', force window hiding. if the window
1691         // was parsed from popup, the flag should already be set
1692         if (!textann->getOpen() && flags != -1)
1693             flags |= Annotation::Hidden;
1694     }
1695 
1696     w.setFlags(flags);
1697 
1698     return w;
1699 }
1700 
setPopup(const Annotation::Popup & popup)1701 void Annotation::setPopup(const Annotation::Popup &popup)
1702 {
1703     Q_D(Annotation);
1704 
1705     if (!d->pdfAnnot) {
1706         d->popup = popup;
1707         return;
1708     }
1709 
1710 #if 0 /* TODO: Remove old popup and add AnnotPopup to page */
1711     AnnotMarkup *markupann = dynamic_cast<AnnotMarkup*>(d->pdfAnnot);
1712     if (!markupann)
1713         return;
1714 
1715     // Create a new AnnotPopup and assign it to pdfAnnot
1716     PDFRectangle rect = d->toPdfRectangle( popup.geometry() );
1717     AnnotPopup * p = new AnnotPopup( d->pdfPage->getDoc(), &rect );
1718     p->setOpen( !(popup.flags() & Annotation::Hidden) );
1719     if (!popup.summary().isEmpty())
1720     {
1721         GooString *s = QStringToUnicodeGooString(popup.summary());
1722         markupann->setLabel(s);
1723         delete s;
1724     }
1725     markupann->setPopup(p);
1726 #endif
1727 }
1728 
revisionScope() const1729 Annotation::RevScope Annotation::revisionScope() const
1730 {
1731     Q_D(const Annotation);
1732 
1733     if (!d->pdfAnnot)
1734         return d->revisionScope;
1735 
1736     const AnnotMarkup *markupann = dynamic_cast<const AnnotMarkup *>(d->pdfAnnot);
1737 
1738     if (markupann && markupann->isInReplyTo()) {
1739         switch (markupann->getReplyTo()) {
1740         case AnnotMarkup::replyTypeR:
1741             return Annotation::Reply;
1742         case AnnotMarkup::replyTypeGroup:
1743             return Annotation::Group;
1744         }
1745     }
1746 
1747     return Annotation::Root; // It's not a revision
1748 }
1749 
revisionType() const1750 Annotation::RevType Annotation::revisionType() const
1751 {
1752     Q_D(const Annotation);
1753 
1754     if (!d->pdfAnnot)
1755         return d->revisionType;
1756 
1757     const AnnotText *textann = dynamic_cast<const AnnotText *>(d->pdfAnnot);
1758 
1759     if (textann && textann->isInReplyTo()) {
1760         switch (textann->getState()) {
1761         case AnnotText::stateMarked:
1762             return Annotation::Marked;
1763         case AnnotText::stateUnmarked:
1764             return Annotation::Unmarked;
1765         case AnnotText::stateAccepted:
1766             return Annotation::Accepted;
1767         case AnnotText::stateRejected:
1768             return Annotation::Rejected;
1769         case AnnotText::stateCancelled:
1770             return Annotation::Cancelled;
1771         case AnnotText::stateCompleted:
1772             return Annotation::Completed;
1773         default:
1774             break;
1775         }
1776     }
1777 
1778     return Annotation::None;
1779 }
1780 
revisions() const1781 QList<Annotation *> Annotation::revisions() const
1782 {
1783     Q_D(const Annotation);
1784 
1785     if (!d->pdfAnnot) {
1786         /* Return aliases, whose ownership goes to the caller */
1787         QList<Annotation *> res;
1788         foreach (Annotation *rev, d->revisions)
1789             res.append(rev->d_ptr->makeAlias());
1790         return res;
1791     }
1792 
1793     /* If the annotation doesn't live in a object on its own (eg bug51361), it
1794      * has no ref, therefore it can't have revisions */
1795     if (!d->pdfAnnot->getHasRef())
1796         return QList<Annotation *>();
1797 
1798     return AnnotationPrivate::findAnnotations(d->pdfPage, d->parentDoc, QSet<Annotation::SubType>(), d->pdfAnnot->getId());
1799 }
1800 
annotationAppearance() const1801 std::unique_ptr<AnnotationAppearance> Annotation::annotationAppearance() const
1802 {
1803     Q_D(const Annotation);
1804 
1805     return std::make_unique<AnnotationAppearance>(new AnnotationAppearancePrivate(d->pdfAnnot));
1806 }
1807 
setAnnotationAppearance(const AnnotationAppearance & annotationAppearance)1808 void Annotation::setAnnotationAppearance(const AnnotationAppearance &annotationAppearance)
1809 {
1810     Q_D(Annotation);
1811 
1812     if (!d->pdfAnnot) {
1813         d->annotationAppearance = annotationAppearance.d->appearance.copy();
1814         return;
1815     }
1816 
1817     // Moving the appearance object using std::move would result
1818     // in the object being completed moved from the AnnotationAppearancePrivate
1819     // class. So, we'll not be able to retrieve the stamp's original AP stream
1820     d->pdfAnnot->setNewAppearance(annotationAppearance.d->appearance.copy());
1821 }
1822 
1823 // END Annotation implementation
1824 
1825 /** TextAnnotation [Annotation] */
1826 class TextAnnotationPrivate : public AnnotationPrivate
1827 {
1828 public:
1829     TextAnnotationPrivate();
1830     Annotation *makeAlias() override;
1831     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
1832     void setDefaultAppearanceToNative();
1833     std::unique_ptr<DefaultAppearance> getDefaultAppearanceFromNative() const;
1834 
1835     // data fields
1836     TextAnnotation::TextType textType;
1837     QString textIcon;
1838     QFont textFont;
1839     QColor textColor;
1840     int inplaceAlign; // 0:left, 1:center, 2:right
1841     QVector<QPointF> inplaceCallout;
1842     TextAnnotation::InplaceIntent inplaceIntent;
1843 };
1844 
TextAnnotationPrivate()1845 TextAnnotationPrivate::TextAnnotationPrivate() : AnnotationPrivate(), textType(TextAnnotation::Linked), textIcon(QStringLiteral("Note")), inplaceAlign(0), inplaceIntent(TextAnnotation::Unknown) { }
1846 
makeAlias()1847 Annotation *TextAnnotationPrivate::makeAlias()
1848 {
1849     return new TextAnnotation(*this);
1850 }
1851 
createNativeAnnot(::Page * destPage,DocumentData * doc)1852 Annot *TextAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
1853 {
1854     // Setters are defined in the public class
1855     TextAnnotation *q = static_cast<TextAnnotation *>(makeAlias());
1856 
1857     // Set page and contents
1858     pdfPage = destPage;
1859     parentDoc = doc;
1860 
1861     // Set pdfAnnot
1862     PDFRectangle rect = boundaryToPdfRectangle(boundary, flags);
1863     if (textType == TextAnnotation::Linked) {
1864         pdfAnnot = new AnnotText { destPage->getDoc(), &rect };
1865     } else {
1866         DefaultAppearance da { { objName, "Invalid_font" }, static_cast<double>(textFont.pointSize()), std::unique_ptr<AnnotColor> { convertQColor(textColor) } };
1867         pdfAnnot = new AnnotFreeText { destPage->getDoc(), &rect, da };
1868     }
1869 
1870     // Set properties
1871     flushBaseAnnotationProperties();
1872     q->setTextIcon(textIcon);
1873     q->setInplaceAlign(inplaceAlign);
1874     q->setCalloutPoints(inplaceCallout);
1875     q->setInplaceIntent(inplaceIntent);
1876 
1877     delete q;
1878 
1879     inplaceCallout.clear(); // Free up memory
1880 
1881     return pdfAnnot;
1882 }
1883 
setDefaultAppearanceToNative()1884 void TextAnnotationPrivate::setDefaultAppearanceToNative()
1885 {
1886     if (pdfAnnot && pdfAnnot->getType() == Annot::typeFreeText) {
1887         AnnotFreeText *ftextann = static_cast<AnnotFreeText *>(pdfAnnot);
1888         DefaultAppearance da { { objName, "Invalid_font" }, static_cast<double>(textFont.pointSize()), std::unique_ptr<AnnotColor> { convertQColor(textColor) } };
1889         ftextann->setDefaultAppearance(da);
1890     }
1891 }
1892 
getDefaultAppearanceFromNative() const1893 std::unique_ptr<DefaultAppearance> TextAnnotationPrivate::getDefaultAppearanceFromNative() const
1894 {
1895     if (pdfAnnot && pdfAnnot->getType() == Annot::typeFreeText) {
1896         AnnotFreeText *ftextann = static_cast<AnnotFreeText *>(pdfAnnot);
1897         return ftextann->getDefaultAppearance();
1898     } else {
1899         return {};
1900     }
1901 }
1902 
TextAnnotation(TextAnnotation::TextType type)1903 TextAnnotation::TextAnnotation(TextAnnotation::TextType type) : Annotation(*new TextAnnotationPrivate())
1904 {
1905     setTextType(type);
1906 }
1907 
TextAnnotation(TextAnnotationPrivate & dd)1908 TextAnnotation::TextAnnotation(TextAnnotationPrivate &dd) : Annotation(dd) { }
1909 
TextAnnotation(const QDomNode & node)1910 TextAnnotation::TextAnnotation(const QDomNode &node) : Annotation(*new TextAnnotationPrivate, node)
1911 {
1912     // loop through the whole children looking for a 'text' element
1913     QDomNode subNode = node.firstChild();
1914     while (subNode.isElement()) {
1915         QDomElement e = subNode.toElement();
1916         subNode = subNode.nextSibling();
1917         if (e.tagName() != QLatin1String("text"))
1918             continue;
1919 
1920         // parse the attributes
1921         if (e.hasAttribute(QStringLiteral("type")))
1922             setTextType((TextAnnotation::TextType)e.attribute(QStringLiteral("type")).toInt());
1923         if (e.hasAttribute(QStringLiteral("icon")))
1924             setTextIcon(e.attribute(QStringLiteral("icon")));
1925         if (e.hasAttribute(QStringLiteral("font"))) {
1926             QFont font;
1927             font.fromString(e.attribute(QStringLiteral("font")));
1928             setTextFont(font);
1929             if (e.hasAttribute(QStringLiteral("fontColor"))) {
1930                 const QColor color = QColor(e.attribute(QStringLiteral("fontColor")));
1931                 setTextColor(color);
1932             }
1933         }
1934         if (e.hasAttribute(QStringLiteral("align")))
1935             setInplaceAlign(e.attribute(QStringLiteral("align")).toInt());
1936         if (e.hasAttribute(QStringLiteral("intent")))
1937             setInplaceIntent((TextAnnotation::InplaceIntent)e.attribute(QStringLiteral("intent")).toInt());
1938 
1939         // parse the subnodes
1940         QDomNode eSubNode = e.firstChild();
1941         while (eSubNode.isElement()) {
1942             QDomElement ee = eSubNode.toElement();
1943             eSubNode = eSubNode.nextSibling();
1944 
1945             if (ee.tagName() == QLatin1String("escapedText")) {
1946                 setContents(ee.firstChild().toCDATASection().data());
1947             } else if (ee.tagName() == QLatin1String("callout")) {
1948                 QVector<QPointF> points(3);
1949                 points[0] = QPointF(ee.attribute(QStringLiteral("ax")).toDouble(), ee.attribute(QStringLiteral("ay")).toDouble());
1950                 points[1] = QPointF(ee.attribute(QStringLiteral("bx")).toDouble(), ee.attribute(QStringLiteral("by")).toDouble());
1951                 points[2] = QPointF(ee.attribute(QStringLiteral("cx")).toDouble(), ee.attribute(QStringLiteral("cy")).toDouble());
1952                 setCalloutPoints(points);
1953             }
1954         }
1955 
1956         // loading complete
1957         break;
1958     }
1959 }
1960 
~TextAnnotation()1961 TextAnnotation::~TextAnnotation() { }
1962 
store(QDomNode & node,QDomDocument & document) const1963 void TextAnnotation::store(QDomNode &node, QDomDocument &document) const
1964 {
1965     // store base annotation properties
1966     storeBaseAnnotationProperties(node, document);
1967 
1968     // create [text] element
1969     QDomElement textElement = document.createElement(QStringLiteral("text"));
1970     node.appendChild(textElement);
1971 
1972     // store the optional attributes
1973     if (textType() != Linked)
1974         textElement.setAttribute(QStringLiteral("type"), (int)textType());
1975     if (textIcon() != QLatin1String("Note"))
1976         textElement.setAttribute(QStringLiteral("icon"), textIcon());
1977     if (inplaceAlign())
1978         textElement.setAttribute(QStringLiteral("align"), inplaceAlign());
1979     if (inplaceIntent() != Unknown)
1980         textElement.setAttribute(QStringLiteral("intent"), (int)inplaceIntent());
1981 
1982     textElement.setAttribute(QStringLiteral("font"), textFont().toString());
1983     textElement.setAttribute(QStringLiteral("fontColor"), textColor().name());
1984 
1985     // Sub-Node-1 - escapedText
1986     if (!contents().isEmpty()) {
1987         QDomElement escapedText = document.createElement(QStringLiteral("escapedText"));
1988         textElement.appendChild(escapedText);
1989         QDomCDATASection textCData = document.createCDATASection(contents());
1990         escapedText.appendChild(textCData);
1991     }
1992 
1993     // Sub-Node-2 - callout
1994     if (calloutPoint(0).x() != 0.0) {
1995         QDomElement calloutElement = document.createElement(QStringLiteral("callout"));
1996         textElement.appendChild(calloutElement);
1997         calloutElement.setAttribute(QStringLiteral("ax"), QString::number(calloutPoint(0).x()));
1998         calloutElement.setAttribute(QStringLiteral("ay"), QString::number(calloutPoint(0).y()));
1999         calloutElement.setAttribute(QStringLiteral("bx"), QString::number(calloutPoint(1).x()));
2000         calloutElement.setAttribute(QStringLiteral("by"), QString::number(calloutPoint(1).y()));
2001         calloutElement.setAttribute(QStringLiteral("cx"), QString::number(calloutPoint(2).x()));
2002         calloutElement.setAttribute(QStringLiteral("cy"), QString::number(calloutPoint(2).y()));
2003     }
2004 }
2005 
subType() const2006 Annotation::SubType TextAnnotation::subType() const
2007 {
2008     return AText;
2009 }
2010 
textType() const2011 TextAnnotation::TextType TextAnnotation::textType() const
2012 {
2013     Q_D(const TextAnnotation);
2014 
2015     if (!d->pdfAnnot)
2016         return d->textType;
2017 
2018     return d->pdfAnnot->getType() == Annot::typeText ? TextAnnotation::Linked : TextAnnotation::InPlace;
2019 }
2020 
setTextType(TextAnnotation::TextType type)2021 void TextAnnotation::setTextType(TextAnnotation::TextType type)
2022 {
2023     Q_D(TextAnnotation);
2024 
2025     if (!d->pdfAnnot) {
2026         d->textType = type;
2027         return;
2028     }
2029 
2030     // Type cannot be changed if annotation is already tied
2031 }
2032 
textIcon() const2033 QString TextAnnotation::textIcon() const
2034 {
2035     Q_D(const TextAnnotation);
2036 
2037     if (!d->pdfAnnot)
2038         return d->textIcon;
2039 
2040     if (d->pdfAnnot->getType() == Annot::typeText) {
2041         const AnnotText *textann = static_cast<const AnnotText *>(d->pdfAnnot);
2042         return QString::fromLatin1(textann->getIcon()->c_str());
2043     }
2044 
2045     return QString();
2046 }
2047 
setTextIcon(const QString & icon)2048 void TextAnnotation::setTextIcon(const QString &icon)
2049 {
2050     Q_D(TextAnnotation);
2051 
2052     if (!d->pdfAnnot) {
2053         d->textIcon = icon;
2054         return;
2055     }
2056 
2057     if (d->pdfAnnot->getType() == Annot::typeText) {
2058         AnnotText *textann = static_cast<AnnotText *>(d->pdfAnnot);
2059         QByteArray encoded = icon.toLatin1();
2060         GooString s(encoded.constData());
2061         textann->setIcon(&s);
2062     }
2063 }
2064 
textFont() const2065 QFont TextAnnotation::textFont() const
2066 {
2067     Q_D(const TextAnnotation);
2068 
2069     if (!d->pdfAnnot)
2070         return d->textFont;
2071 
2072     double fontSize { AnnotFreeText::undefinedFontPtSize };
2073     if (d->pdfAnnot->getType() == Annot::typeFreeText) {
2074         std::unique_ptr<DefaultAppearance> da { d->getDefaultAppearanceFromNative() };
2075         if (da && da->getFontPtSize() > 0) {
2076             fontSize = da->getFontPtSize();
2077         }
2078     }
2079 
2080     QFont font;
2081     font.setPointSizeF(fontSize);
2082     return font;
2083 }
2084 
setTextFont(const QFont & font)2085 void TextAnnotation::setTextFont(const QFont &font)
2086 {
2087     Q_D(TextAnnotation);
2088     d->textFont = font;
2089     d->textColor = Qt::black;
2090 
2091     d->setDefaultAppearanceToNative();
2092 }
2093 
textColor() const2094 QColor TextAnnotation::textColor() const
2095 {
2096     Q_D(const TextAnnotation);
2097 
2098     if (!d->pdfAnnot)
2099         return d->textColor;
2100 
2101     if (std::unique_ptr<DefaultAppearance> da { d->getDefaultAppearanceFromNative() }) {
2102         return convertAnnotColor(da->getFontColor());
2103     }
2104 
2105     return {};
2106 }
2107 
setTextColor(const QColor & color)2108 void TextAnnotation::setTextColor(const QColor &color)
2109 {
2110     Q_D(TextAnnotation);
2111     d->textColor = color;
2112 
2113     d->setDefaultAppearanceToNative();
2114 }
2115 
inplaceAlign() const2116 int TextAnnotation::inplaceAlign() const
2117 {
2118     Q_D(const TextAnnotation);
2119 
2120     if (!d->pdfAnnot)
2121         return d->inplaceAlign;
2122 
2123     if (d->pdfAnnot->getType() == Annot::typeFreeText) {
2124         const AnnotFreeText *ftextann = static_cast<const AnnotFreeText *>(d->pdfAnnot);
2125         return ftextann->getQuadding();
2126     }
2127 
2128     return 0;
2129 }
2130 
setInplaceAlign(int align)2131 void TextAnnotation::setInplaceAlign(int align)
2132 {
2133     Q_D(TextAnnotation);
2134 
2135     if (!d->pdfAnnot) {
2136         d->inplaceAlign = align;
2137         return;
2138     }
2139 
2140     if (d->pdfAnnot->getType() == Annot::typeFreeText) {
2141         AnnotFreeText *ftextann = static_cast<AnnotFreeText *>(d->pdfAnnot);
2142         ftextann->setQuadding((AnnotFreeText::AnnotFreeTextQuadding)align);
2143     }
2144 }
2145 
calloutPoint(int id) const2146 QPointF TextAnnotation::calloutPoint(int id) const
2147 {
2148     const QVector<QPointF> points = calloutPoints();
2149     if (id < 0 || id >= points.size())
2150         return QPointF();
2151     else
2152         return points[id];
2153 }
2154 
calloutPoints() const2155 QVector<QPointF> TextAnnotation::calloutPoints() const
2156 {
2157     Q_D(const TextAnnotation);
2158 
2159     if (!d->pdfAnnot)
2160         return d->inplaceCallout;
2161 
2162     if (d->pdfAnnot->getType() == Annot::typeText)
2163         return QVector<QPointF>();
2164 
2165     const AnnotFreeText *ftextann = static_cast<const AnnotFreeText *>(d->pdfAnnot);
2166     const AnnotCalloutLine *callout = ftextann->getCalloutLine();
2167 
2168     if (!callout)
2169         return QVector<QPointF>();
2170 
2171     double MTX[6];
2172     d->fillTransformationMTX(MTX);
2173 
2174     const AnnotCalloutMultiLine *callout_v6 = dynamic_cast<const AnnotCalloutMultiLine *>(callout);
2175     QVector<QPointF> res(callout_v6 ? 3 : 2);
2176     XPDFReader::transform(MTX, callout->getX1(), callout->getY1(), res[0]);
2177     XPDFReader::transform(MTX, callout->getX2(), callout->getY2(), res[1]);
2178     if (callout_v6)
2179         XPDFReader::transform(MTX, callout_v6->getX3(), callout_v6->getY3(), res[2]);
2180     return res;
2181 }
2182 
setCalloutPoints(const QVector<QPointF> & points)2183 void TextAnnotation::setCalloutPoints(const QVector<QPointF> &points)
2184 {
2185     Q_D(TextAnnotation);
2186     if (!d->pdfAnnot) {
2187         d->inplaceCallout = points;
2188         return;
2189     }
2190 
2191     if (d->pdfAnnot->getType() != Annot::typeFreeText)
2192         return;
2193 
2194     AnnotFreeText *ftextann = static_cast<AnnotFreeText *>(d->pdfAnnot);
2195     const int count = points.size();
2196 
2197     if (count == 0) {
2198         ftextann->setCalloutLine(nullptr);
2199         return;
2200     }
2201 
2202     if (count != 2 && count != 3) {
2203         error(errSyntaxError, -1, "Expected zero, two or three points for callout");
2204         return;
2205     }
2206 
2207     AnnotCalloutLine *callout;
2208     double x1, y1, x2, y2;
2209     double MTX[6];
2210     d->fillTransformationMTX(MTX);
2211 
2212     XPDFReader::invTransform(MTX, points[0], x1, y1);
2213     XPDFReader::invTransform(MTX, points[1], x2, y2);
2214     if (count == 3) {
2215         double x3, y3;
2216         XPDFReader::invTransform(MTX, points[2], x3, y3);
2217         callout = new AnnotCalloutMultiLine(x1, y1, x2, y2, x3, y3);
2218     } else {
2219         callout = new AnnotCalloutLine(x1, y1, x2, y2);
2220     }
2221 
2222     ftextann->setCalloutLine(callout);
2223     delete callout;
2224 }
2225 
inplaceIntent() const2226 TextAnnotation::InplaceIntent TextAnnotation::inplaceIntent() const
2227 {
2228     Q_D(const TextAnnotation);
2229 
2230     if (!d->pdfAnnot)
2231         return d->inplaceIntent;
2232 
2233     if (d->pdfAnnot->getType() == Annot::typeFreeText) {
2234         const AnnotFreeText *ftextann = static_cast<const AnnotFreeText *>(d->pdfAnnot);
2235         return (TextAnnotation::InplaceIntent)ftextann->getIntent();
2236     }
2237 
2238     return TextAnnotation::Unknown;
2239 }
2240 
setInplaceIntent(TextAnnotation::InplaceIntent intent)2241 void TextAnnotation::setInplaceIntent(TextAnnotation::InplaceIntent intent)
2242 {
2243     Q_D(TextAnnotation);
2244 
2245     if (!d->pdfAnnot) {
2246         d->inplaceIntent = intent;
2247         return;
2248     }
2249 
2250     if (d->pdfAnnot->getType() == Annot::typeFreeText) {
2251         AnnotFreeText *ftextann = static_cast<AnnotFreeText *>(d->pdfAnnot);
2252         ftextann->setIntent((AnnotFreeText::AnnotFreeTextIntent)intent);
2253     }
2254 }
2255 
2256 /** LineAnnotation [Annotation] */
2257 class LineAnnotationPrivate : public AnnotationPrivate
2258 {
2259 public:
2260     LineAnnotationPrivate();
2261     Annotation *makeAlias() override;
2262     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
2263 
2264     // data fields (note uses border for rendering style)
2265     QLinkedList<QPointF> linePoints;
2266     LineAnnotation::TermStyle lineStartStyle;
2267     LineAnnotation::TermStyle lineEndStyle;
2268     bool lineClosed : 1; // (if true draw close shape)
2269     bool lineShowCaption : 1;
2270     LineAnnotation::LineType lineType;
2271     QColor lineInnerColor;
2272     double lineLeadingFwdPt;
2273     double lineLeadingBackPt;
2274     LineAnnotation::LineIntent lineIntent;
2275 };
2276 
LineAnnotationPrivate()2277 LineAnnotationPrivate::LineAnnotationPrivate()
2278     : AnnotationPrivate(), lineStartStyle(LineAnnotation::None), lineEndStyle(LineAnnotation::None), lineClosed(false), lineShowCaption(false), lineLeadingFwdPt(0), lineLeadingBackPt(0), lineIntent(LineAnnotation::Unknown)
2279 {
2280 }
2281 
makeAlias()2282 Annotation *LineAnnotationPrivate::makeAlias()
2283 {
2284     return new LineAnnotation(*this);
2285 }
2286 
createNativeAnnot(::Page * destPage,DocumentData * doc)2287 Annot *LineAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
2288 {
2289     // Setters are defined in the public class
2290     LineAnnotation *q = static_cast<LineAnnotation *>(makeAlias());
2291 
2292     // Set page and document
2293     pdfPage = destPage;
2294     parentDoc = doc;
2295 
2296     // Set pdfAnnot
2297     PDFRectangle rect = boundaryToPdfRectangle(boundary, flags);
2298     if (lineType == LineAnnotation::StraightLine) {
2299         pdfAnnot = new AnnotLine(doc->doc, &rect);
2300     } else {
2301         pdfAnnot = new AnnotPolygon(doc->doc, &rect, lineClosed ? Annot::typePolygon : Annot::typePolyLine);
2302     }
2303 
2304     // Set properties
2305     flushBaseAnnotationProperties();
2306     q->setLinePoints(linePoints);
2307     q->setLineStartStyle(lineStartStyle);
2308     q->setLineEndStyle(lineEndStyle);
2309     q->setLineInnerColor(lineInnerColor);
2310     q->setLineLeadingForwardPoint(lineLeadingFwdPt);
2311     q->setLineLeadingBackPoint(lineLeadingBackPt);
2312     q->setLineShowCaption(lineShowCaption);
2313     q->setLineIntent(lineIntent);
2314 
2315     delete q;
2316 
2317     linePoints.clear(); // Free up memory
2318 
2319     return pdfAnnot;
2320 }
2321 
LineAnnotation(LineAnnotation::LineType type)2322 LineAnnotation::LineAnnotation(LineAnnotation::LineType type) : Annotation(*new LineAnnotationPrivate())
2323 {
2324     setLineType(type);
2325 }
2326 
LineAnnotation(LineAnnotationPrivate & dd)2327 LineAnnotation::LineAnnotation(LineAnnotationPrivate &dd) : Annotation(dd) { }
2328 
LineAnnotation(const QDomNode & node)2329 LineAnnotation::LineAnnotation(const QDomNode &node) : Annotation(*new LineAnnotationPrivate(), node)
2330 {
2331     // loop through the whole children looking for a 'line' element
2332     QDomNode subNode = node.firstChild();
2333     while (subNode.isElement()) {
2334         QDomElement e = subNode.toElement();
2335         subNode = subNode.nextSibling();
2336         if (e.tagName() != QLatin1String("line"))
2337             continue;
2338 
2339         // parse the attributes
2340         if (e.hasAttribute(QStringLiteral("startStyle")))
2341             setLineStartStyle((LineAnnotation::TermStyle)e.attribute(QStringLiteral("startStyle")).toInt());
2342         if (e.hasAttribute(QStringLiteral("endStyle")))
2343             setLineEndStyle((LineAnnotation::TermStyle)e.attribute(QStringLiteral("endStyle")).toInt());
2344         if (e.hasAttribute(QStringLiteral("closed")))
2345             setLineClosed(e.attribute(QStringLiteral("closed")).toInt());
2346         if (e.hasAttribute(QStringLiteral("innerColor")))
2347             setLineInnerColor(QColor(e.attribute(QStringLiteral("innerColor"))));
2348         if (e.hasAttribute(QStringLiteral("leadFwd")))
2349             setLineLeadingForwardPoint(e.attribute(QStringLiteral("leadFwd")).toDouble());
2350         if (e.hasAttribute(QStringLiteral("leadBack")))
2351             setLineLeadingBackPoint(e.attribute(QStringLiteral("leadBack")).toDouble());
2352         if (e.hasAttribute(QStringLiteral("showCaption")))
2353             setLineShowCaption(e.attribute(QStringLiteral("showCaption")).toInt());
2354         if (e.hasAttribute(QStringLiteral("intent")))
2355             setLineIntent((LineAnnotation::LineIntent)e.attribute(QStringLiteral("intent")).toInt());
2356 
2357         // parse all 'point' subnodes
2358         QLinkedList<QPointF> points;
2359         QDomNode pointNode = e.firstChild();
2360         while (pointNode.isElement()) {
2361             QDomElement pe = pointNode.toElement();
2362             pointNode = pointNode.nextSibling();
2363 
2364             if (pe.tagName() != QLatin1String("point"))
2365                 continue;
2366 
2367             QPointF p(pe.attribute(QStringLiteral("x"), QStringLiteral("0.0")).toDouble(), pe.attribute(QStringLiteral("y"), QStringLiteral("0.0")).toDouble());
2368             points.append(p);
2369         }
2370         setLinePoints(points);
2371         setLineType(points.size() == 2 ? StraightLine : Polyline);
2372 
2373         // loading complete
2374         break;
2375     }
2376 }
2377 
~LineAnnotation()2378 LineAnnotation::~LineAnnotation() { }
2379 
store(QDomNode & node,QDomDocument & document) const2380 void LineAnnotation::store(QDomNode &node, QDomDocument &document) const
2381 {
2382     // store base annotation properties
2383     storeBaseAnnotationProperties(node, document);
2384 
2385     // create [line] element
2386     QDomElement lineElement = document.createElement(QStringLiteral("line"));
2387     node.appendChild(lineElement);
2388 
2389     // store the attributes
2390     if (lineStartStyle() != None)
2391         lineElement.setAttribute(QStringLiteral("startStyle"), (int)lineStartStyle());
2392     if (lineEndStyle() != None)
2393         lineElement.setAttribute(QStringLiteral("endStyle"), (int)lineEndStyle());
2394     if (isLineClosed())
2395         lineElement.setAttribute(QStringLiteral("closed"), isLineClosed());
2396     if (lineInnerColor().isValid())
2397         lineElement.setAttribute(QStringLiteral("innerColor"), lineInnerColor().name());
2398     if (lineLeadingForwardPoint() != 0.0)
2399         lineElement.setAttribute(QStringLiteral("leadFwd"), QString::number(lineLeadingForwardPoint()));
2400     if (lineLeadingBackPoint() != 0.0)
2401         lineElement.setAttribute(QStringLiteral("leadBack"), QString::number(lineLeadingBackPoint()));
2402     if (lineShowCaption())
2403         lineElement.setAttribute(QStringLiteral("showCaption"), lineShowCaption());
2404     if (lineIntent() != Unknown)
2405         lineElement.setAttribute(QStringLiteral("intent"), lineIntent());
2406 
2407     // append the list of points
2408     const QLinkedList<QPointF> points = linePoints();
2409     if (points.count() > 1) {
2410         QLinkedList<QPointF>::const_iterator it = points.begin(), end = points.end();
2411         while (it != end) {
2412             const QPointF &p = *it;
2413             QDomElement pElement = document.createElement(QStringLiteral("point"));
2414             lineElement.appendChild(pElement);
2415             pElement.setAttribute(QStringLiteral("x"), QString::number(p.x()));
2416             pElement.setAttribute(QStringLiteral("y"), QString::number(p.y()));
2417             ++it;
2418         }
2419     }
2420 }
2421 
subType() const2422 Annotation::SubType LineAnnotation::subType() const
2423 {
2424     return ALine;
2425 }
2426 
lineType() const2427 LineAnnotation::LineType LineAnnotation::lineType() const
2428 {
2429     Q_D(const LineAnnotation);
2430 
2431     if (!d->pdfAnnot)
2432         return d->lineType;
2433 
2434     return (d->pdfAnnot->getType() == Annot::typeLine) ? LineAnnotation::StraightLine : LineAnnotation::Polyline;
2435 }
2436 
setLineType(LineAnnotation::LineType type)2437 void LineAnnotation::setLineType(LineAnnotation::LineType type)
2438 {
2439     Q_D(LineAnnotation);
2440 
2441     if (!d->pdfAnnot) {
2442         d->lineType = type;
2443         return;
2444     }
2445 
2446     // Type cannot be changed if annotation is already tied
2447 }
2448 
linePoints() const2449 QLinkedList<QPointF> LineAnnotation::linePoints() const
2450 {
2451     Q_D(const LineAnnotation);
2452 
2453     if (!d->pdfAnnot)
2454         return d->linePoints;
2455 
2456     double MTX[6];
2457     d->fillTransformationMTX(MTX);
2458 
2459     QLinkedList<QPointF> res;
2460     if (d->pdfAnnot->getType() == Annot::typeLine) {
2461         const AnnotLine *lineann = static_cast<const AnnotLine *>(d->pdfAnnot);
2462         QPointF p;
2463         XPDFReader::transform(MTX, lineann->getX1(), lineann->getY1(), p);
2464         res.append(p);
2465         XPDFReader::transform(MTX, lineann->getX2(), lineann->getY2(), p);
2466         res.append(p);
2467     } else {
2468         const AnnotPolygon *polyann = static_cast<const AnnotPolygon *>(d->pdfAnnot);
2469         const AnnotPath *vertices = polyann->getVertices();
2470 
2471         for (int i = 0; i < vertices->getCoordsLength(); ++i) {
2472             QPointF p;
2473             XPDFReader::transform(MTX, vertices->getX(i), vertices->getY(i), p);
2474             res.append(p);
2475         }
2476     }
2477 
2478     return res;
2479 }
2480 
setLinePoints(const QLinkedList<QPointF> & points)2481 void LineAnnotation::setLinePoints(const QLinkedList<QPointF> &points)
2482 {
2483     Q_D(LineAnnotation);
2484 
2485     if (!d->pdfAnnot) {
2486         d->linePoints = points;
2487         return;
2488     }
2489 
2490     if (d->pdfAnnot->getType() == Annot::typeLine) {
2491         AnnotLine *lineann = static_cast<AnnotLine *>(d->pdfAnnot);
2492         if (points.size() != 2) {
2493             error(errSyntaxError, -1, "Expected two points for a straight line");
2494             return;
2495         }
2496         double x1, y1, x2, y2;
2497         double MTX[6];
2498         d->fillTransformationMTX(MTX);
2499         XPDFReader::invTransform(MTX, points.first(), x1, y1);
2500         XPDFReader::invTransform(MTX, points.last(), x2, y2);
2501         lineann->setVertices(x1, y1, x2, y2);
2502     } else {
2503         AnnotPolygon *polyann = static_cast<AnnotPolygon *>(d->pdfAnnot);
2504         AnnotPath *p = d->toAnnotPath(points);
2505         polyann->setVertices(p);
2506         delete p;
2507     }
2508 }
2509 
lineStartStyle() const2510 LineAnnotation::TermStyle LineAnnotation::lineStartStyle() const
2511 {
2512     Q_D(const LineAnnotation);
2513 
2514     if (!d->pdfAnnot)
2515         return d->lineStartStyle;
2516 
2517     if (d->pdfAnnot->getType() == Annot::typeLine) {
2518         const AnnotLine *lineann = static_cast<const AnnotLine *>(d->pdfAnnot);
2519         return (LineAnnotation::TermStyle)lineann->getStartStyle();
2520     } else {
2521         const AnnotPolygon *polyann = static_cast<const AnnotPolygon *>(d->pdfAnnot);
2522         return (LineAnnotation::TermStyle)polyann->getStartStyle();
2523     }
2524 }
2525 
setLineStartStyle(LineAnnotation::TermStyle style)2526 void LineAnnotation::setLineStartStyle(LineAnnotation::TermStyle style)
2527 {
2528     Q_D(LineAnnotation);
2529 
2530     if (!d->pdfAnnot) {
2531         d->lineStartStyle = style;
2532         return;
2533     }
2534 
2535     if (d->pdfAnnot->getType() == Annot::typeLine) {
2536         AnnotLine *lineann = static_cast<AnnotLine *>(d->pdfAnnot);
2537         lineann->setStartEndStyle((AnnotLineEndingStyle)style, lineann->getEndStyle());
2538     } else {
2539         AnnotPolygon *polyann = static_cast<AnnotPolygon *>(d->pdfAnnot);
2540         polyann->setStartEndStyle((AnnotLineEndingStyle)style, polyann->getEndStyle());
2541     }
2542 }
2543 
lineEndStyle() const2544 LineAnnotation::TermStyle LineAnnotation::lineEndStyle() const
2545 {
2546     Q_D(const LineAnnotation);
2547 
2548     if (!d->pdfAnnot)
2549         return d->lineEndStyle;
2550 
2551     if (d->pdfAnnot->getType() == Annot::typeLine) {
2552         const AnnotLine *lineann = static_cast<const AnnotLine *>(d->pdfAnnot);
2553         return (LineAnnotation::TermStyle)lineann->getEndStyle();
2554     } else {
2555         const AnnotPolygon *polyann = static_cast<const AnnotPolygon *>(d->pdfAnnot);
2556         return (LineAnnotation::TermStyle)polyann->getEndStyle();
2557     }
2558 }
2559 
setLineEndStyle(LineAnnotation::TermStyle style)2560 void LineAnnotation::setLineEndStyle(LineAnnotation::TermStyle style)
2561 {
2562     Q_D(LineAnnotation);
2563 
2564     if (!d->pdfAnnot) {
2565         d->lineEndStyle = style;
2566         return;
2567     }
2568 
2569     if (d->pdfAnnot->getType() == Annot::typeLine) {
2570         AnnotLine *lineann = static_cast<AnnotLine *>(d->pdfAnnot);
2571         lineann->setStartEndStyle(lineann->getStartStyle(), (AnnotLineEndingStyle)style);
2572     } else {
2573         AnnotPolygon *polyann = static_cast<AnnotPolygon *>(d->pdfAnnot);
2574         polyann->setStartEndStyle(polyann->getStartStyle(), (AnnotLineEndingStyle)style);
2575     }
2576 }
2577 
isLineClosed() const2578 bool LineAnnotation::isLineClosed() const
2579 {
2580     Q_D(const LineAnnotation);
2581 
2582     if (!d->pdfAnnot)
2583         return d->lineClosed;
2584 
2585     return d->pdfAnnot->getType() == Annot::typePolygon;
2586 }
2587 
setLineClosed(bool closed)2588 void LineAnnotation::setLineClosed(bool closed)
2589 {
2590     Q_D(LineAnnotation);
2591 
2592     if (!d->pdfAnnot) {
2593         d->lineClosed = closed;
2594         return;
2595     }
2596 
2597     if (d->pdfAnnot->getType() != Annot::typeLine) {
2598         AnnotPolygon *polyann = static_cast<AnnotPolygon *>(d->pdfAnnot);
2599 
2600         // Set new subtype and switch intent if necessary
2601         if (closed) {
2602             polyann->setType(Annot::typePolygon);
2603             if (polyann->getIntent() == AnnotPolygon::polylineDimension)
2604                 polyann->setIntent(AnnotPolygon::polygonDimension);
2605         } else {
2606             polyann->setType(Annot::typePolyLine);
2607             if (polyann->getIntent() == AnnotPolygon::polygonDimension)
2608                 polyann->setIntent(AnnotPolygon::polylineDimension);
2609         }
2610     }
2611 }
2612 
lineInnerColor() const2613 QColor LineAnnotation::lineInnerColor() const
2614 {
2615     Q_D(const LineAnnotation);
2616 
2617     if (!d->pdfAnnot)
2618         return d->lineInnerColor;
2619 
2620     AnnotColor *c;
2621 
2622     if (d->pdfAnnot->getType() == Annot::typeLine) {
2623         const AnnotLine *lineann = static_cast<const AnnotLine *>(d->pdfAnnot);
2624         c = lineann->getInteriorColor();
2625     } else {
2626         const AnnotPolygon *polyann = static_cast<const AnnotPolygon *>(d->pdfAnnot);
2627         c = polyann->getInteriorColor();
2628     }
2629 
2630     return convertAnnotColor(c);
2631 }
2632 
setLineInnerColor(const QColor & color)2633 void LineAnnotation::setLineInnerColor(const QColor &color)
2634 {
2635     Q_D(LineAnnotation);
2636 
2637     if (!d->pdfAnnot) {
2638         d->lineInnerColor = color;
2639         return;
2640     }
2641 
2642     auto c = convertQColor(color);
2643 
2644     if (d->pdfAnnot->getType() == Annot::typeLine) {
2645         AnnotLine *lineann = static_cast<AnnotLine *>(d->pdfAnnot);
2646         lineann->setInteriorColor(std::move(c));
2647     } else {
2648         AnnotPolygon *polyann = static_cast<AnnotPolygon *>(d->pdfAnnot);
2649         polyann->setInteriorColor(std::move(c));
2650     }
2651 }
2652 
lineLeadingForwardPoint() const2653 double LineAnnotation::lineLeadingForwardPoint() const
2654 {
2655     Q_D(const LineAnnotation);
2656 
2657     if (!d->pdfAnnot)
2658         return d->lineLeadingFwdPt;
2659 
2660     if (d->pdfAnnot->getType() == Annot::typeLine) {
2661         const AnnotLine *lineann = static_cast<const AnnotLine *>(d->pdfAnnot);
2662         return lineann->getLeaderLineLength();
2663     }
2664 
2665     return 0;
2666 }
2667 
setLineLeadingForwardPoint(double point)2668 void LineAnnotation::setLineLeadingForwardPoint(double point)
2669 {
2670     Q_D(LineAnnotation);
2671 
2672     if (!d->pdfAnnot) {
2673         d->lineLeadingFwdPt = point;
2674         return;
2675     }
2676 
2677     if (d->pdfAnnot->getType() == Annot::typeLine) {
2678         AnnotLine *lineann = static_cast<AnnotLine *>(d->pdfAnnot);
2679         lineann->setLeaderLineLength(point);
2680     }
2681 }
2682 
lineLeadingBackPoint() const2683 double LineAnnotation::lineLeadingBackPoint() const
2684 {
2685     Q_D(const LineAnnotation);
2686 
2687     if (!d->pdfAnnot)
2688         return d->lineLeadingBackPt;
2689 
2690     if (d->pdfAnnot->getType() == Annot::typeLine) {
2691         const AnnotLine *lineann = static_cast<const AnnotLine *>(d->pdfAnnot);
2692         return lineann->getLeaderLineExtension();
2693     }
2694 
2695     return 0;
2696 }
2697 
setLineLeadingBackPoint(double point)2698 void LineAnnotation::setLineLeadingBackPoint(double point)
2699 {
2700     Q_D(LineAnnotation);
2701 
2702     if (!d->pdfAnnot) {
2703         d->lineLeadingBackPt = point;
2704         return;
2705     }
2706 
2707     if (d->pdfAnnot->getType() == Annot::typeLine) {
2708         AnnotLine *lineann = static_cast<AnnotLine *>(d->pdfAnnot);
2709         lineann->setLeaderLineExtension(point);
2710     }
2711 }
2712 
lineShowCaption() const2713 bool LineAnnotation::lineShowCaption() const
2714 {
2715     Q_D(const LineAnnotation);
2716 
2717     if (!d->pdfAnnot)
2718         return d->lineShowCaption;
2719 
2720     if (d->pdfAnnot->getType() == Annot::typeLine) {
2721         const AnnotLine *lineann = static_cast<const AnnotLine *>(d->pdfAnnot);
2722         return lineann->getCaption();
2723     }
2724 
2725     return false;
2726 }
2727 
setLineShowCaption(bool show)2728 void LineAnnotation::setLineShowCaption(bool show)
2729 {
2730     Q_D(LineAnnotation);
2731 
2732     if (!d->pdfAnnot) {
2733         d->lineShowCaption = show;
2734         return;
2735     }
2736 
2737     if (d->pdfAnnot->getType() == Annot::typeLine) {
2738         AnnotLine *lineann = static_cast<AnnotLine *>(d->pdfAnnot);
2739         lineann->setCaption(show);
2740     }
2741 }
2742 
lineIntent() const2743 LineAnnotation::LineIntent LineAnnotation::lineIntent() const
2744 {
2745     Q_D(const LineAnnotation);
2746 
2747     if (!d->pdfAnnot)
2748         return d->lineIntent;
2749 
2750     if (d->pdfAnnot->getType() == Annot::typeLine) {
2751         const AnnotLine *lineann = static_cast<const AnnotLine *>(d->pdfAnnot);
2752         return (LineAnnotation::LineIntent)(lineann->getIntent() + 1);
2753     } else {
2754         const AnnotPolygon *polyann = static_cast<const AnnotPolygon *>(d->pdfAnnot);
2755         if (polyann->getIntent() == AnnotPolygon::polygonCloud)
2756             return LineAnnotation::PolygonCloud;
2757         else // AnnotPolygon::polylineDimension, AnnotPolygon::polygonDimension
2758             return LineAnnotation::Dimension;
2759     }
2760 }
2761 
setLineIntent(LineAnnotation::LineIntent intent)2762 void LineAnnotation::setLineIntent(LineAnnotation::LineIntent intent)
2763 {
2764     Q_D(LineAnnotation);
2765 
2766     if (!d->pdfAnnot) {
2767         d->lineIntent = intent;
2768         return;
2769     }
2770 
2771     if (intent == LineAnnotation::Unknown)
2772         return; // Do not set (actually, it should clear the property)
2773 
2774     if (d->pdfAnnot->getType() == Annot::typeLine) {
2775         AnnotLine *lineann = static_cast<AnnotLine *>(d->pdfAnnot);
2776         lineann->setIntent((AnnotLine::AnnotLineIntent)(intent - 1));
2777     } else {
2778         AnnotPolygon *polyann = static_cast<AnnotPolygon *>(d->pdfAnnot);
2779         if (intent == LineAnnotation::PolygonCloud)
2780             polyann->setIntent(AnnotPolygon::polygonCloud);
2781         else // LineAnnotation::Dimension
2782         {
2783             if (d->pdfAnnot->getType() == Annot::typePolygon)
2784                 polyann->setIntent(AnnotPolygon::polygonDimension);
2785             else // Annot::typePolyLine
2786                 polyann->setIntent(AnnotPolygon::polylineDimension);
2787         }
2788     }
2789 }
2790 
2791 /** GeomAnnotation [Annotation] */
2792 class GeomAnnotationPrivate : public AnnotationPrivate
2793 {
2794 public:
2795     GeomAnnotationPrivate();
2796     Annotation *makeAlias() override;
2797     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
2798 
2799     // data fields (note uses border for rendering style)
2800     GeomAnnotation::GeomType geomType;
2801     QColor geomInnerColor;
2802 };
2803 
GeomAnnotationPrivate()2804 GeomAnnotationPrivate::GeomAnnotationPrivate() : AnnotationPrivate(), geomType(GeomAnnotation::InscribedSquare) { }
2805 
makeAlias()2806 Annotation *GeomAnnotationPrivate::makeAlias()
2807 {
2808     return new GeomAnnotation(*this);
2809 }
2810 
createNativeAnnot(::Page * destPage,DocumentData * doc)2811 Annot *GeomAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
2812 {
2813     // Setters are defined in the public class
2814     GeomAnnotation *q = static_cast<GeomAnnotation *>(makeAlias());
2815 
2816     // Set page and document
2817     pdfPage = destPage;
2818     parentDoc = doc;
2819 
2820     Annot::AnnotSubtype type;
2821     if (geomType == GeomAnnotation::InscribedSquare)
2822         type = Annot::typeSquare;
2823     else // GeomAnnotation::InscribedCircle
2824         type = Annot::typeCircle;
2825 
2826     // Set pdfAnnot
2827     PDFRectangle rect = boundaryToPdfRectangle(boundary, flags);
2828     pdfAnnot = new AnnotGeometry(destPage->getDoc(), &rect, type);
2829 
2830     // Set properties
2831     flushBaseAnnotationProperties();
2832     q->setGeomInnerColor(geomInnerColor);
2833 
2834     delete q;
2835     return pdfAnnot;
2836 }
2837 
GeomAnnotation()2838 GeomAnnotation::GeomAnnotation() : Annotation(*new GeomAnnotationPrivate()) { }
2839 
GeomAnnotation(GeomAnnotationPrivate & dd)2840 GeomAnnotation::GeomAnnotation(GeomAnnotationPrivate &dd) : Annotation(dd) { }
2841 
GeomAnnotation(const QDomNode & node)2842 GeomAnnotation::GeomAnnotation(const QDomNode &node) : Annotation(*new GeomAnnotationPrivate(), node)
2843 {
2844     // loop through the whole children looking for a 'geom' element
2845     QDomNode subNode = node.firstChild();
2846     while (subNode.isElement()) {
2847         QDomElement e = subNode.toElement();
2848         subNode = subNode.nextSibling();
2849         if (e.tagName() != QLatin1String("geom"))
2850             continue;
2851 
2852         // parse the attributes
2853         if (e.hasAttribute(QStringLiteral("type")))
2854             setGeomType((GeomAnnotation::GeomType)e.attribute(QStringLiteral("type")).toInt());
2855         if (e.hasAttribute(QStringLiteral("color")))
2856             setGeomInnerColor(QColor(e.attribute(QStringLiteral("color"))));
2857 
2858         // loading complete
2859         break;
2860     }
2861 }
2862 
~GeomAnnotation()2863 GeomAnnotation::~GeomAnnotation() { }
2864 
store(QDomNode & node,QDomDocument & document) const2865 void GeomAnnotation::store(QDomNode &node, QDomDocument &document) const
2866 {
2867     // store base annotation properties
2868     storeBaseAnnotationProperties(node, document);
2869 
2870     // create [geom] element
2871     QDomElement geomElement = document.createElement(QStringLiteral("geom"));
2872     node.appendChild(geomElement);
2873 
2874     // append the optional attributes
2875     if (geomType() != InscribedSquare)
2876         geomElement.setAttribute(QStringLiteral("type"), (int)geomType());
2877     if (geomInnerColor().isValid())
2878         geomElement.setAttribute(QStringLiteral("color"), geomInnerColor().name());
2879 }
2880 
subType() const2881 Annotation::SubType GeomAnnotation::subType() const
2882 {
2883     return AGeom;
2884 }
2885 
geomType() const2886 GeomAnnotation::GeomType GeomAnnotation::geomType() const
2887 {
2888     Q_D(const GeomAnnotation);
2889 
2890     if (!d->pdfAnnot)
2891         return d->geomType;
2892 
2893     if (d->pdfAnnot->getType() == Annot::typeSquare)
2894         return GeomAnnotation::InscribedSquare;
2895     else // Annot::typeCircle
2896         return GeomAnnotation::InscribedCircle;
2897 }
2898 
setGeomType(GeomAnnotation::GeomType type)2899 void GeomAnnotation::setGeomType(GeomAnnotation::GeomType type)
2900 {
2901     Q_D(GeomAnnotation);
2902 
2903     if (!d->pdfAnnot) {
2904         d->geomType = type;
2905         return;
2906     }
2907 
2908     AnnotGeometry *geomann = static_cast<AnnotGeometry *>(d->pdfAnnot);
2909     if (type == GeomAnnotation::InscribedSquare)
2910         geomann->setType(Annot::typeSquare);
2911     else // GeomAnnotation::InscribedCircle
2912         geomann->setType(Annot::typeCircle);
2913 }
2914 
geomInnerColor() const2915 QColor GeomAnnotation::geomInnerColor() const
2916 {
2917     Q_D(const GeomAnnotation);
2918 
2919     if (!d->pdfAnnot)
2920         return d->geomInnerColor;
2921 
2922     const AnnotGeometry *geomann = static_cast<const AnnotGeometry *>(d->pdfAnnot);
2923     return convertAnnotColor(geomann->getInteriorColor());
2924 }
2925 
setGeomInnerColor(const QColor & color)2926 void GeomAnnotation::setGeomInnerColor(const QColor &color)
2927 {
2928     Q_D(GeomAnnotation);
2929 
2930     if (!d->pdfAnnot) {
2931         d->geomInnerColor = color;
2932         return;
2933     }
2934 
2935     AnnotGeometry *geomann = static_cast<AnnotGeometry *>(d->pdfAnnot);
2936     geomann->setInteriorColor(convertQColor(color));
2937 }
2938 
2939 /** HighlightAnnotation [Annotation] */
2940 class HighlightAnnotationPrivate : public AnnotationPrivate
2941 {
2942 public:
2943     HighlightAnnotationPrivate();
2944     Annotation *makeAlias() override;
2945     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
2946 
2947     // data fields
2948     HighlightAnnotation::HighlightType highlightType;
2949     QList<HighlightAnnotation::Quad> highlightQuads; // not empty
2950 
2951     // helpers
2952     static Annot::AnnotSubtype toAnnotSubType(HighlightAnnotation::HighlightType type);
2953     QList<HighlightAnnotation::Quad> fromQuadrilaterals(AnnotQuadrilaterals *quads) const;
2954     AnnotQuadrilaterals *toQuadrilaterals(const QList<HighlightAnnotation::Quad> &quads) const;
2955 };
2956 
HighlightAnnotationPrivate()2957 HighlightAnnotationPrivate::HighlightAnnotationPrivate() : AnnotationPrivate(), highlightType(HighlightAnnotation::Highlight) { }
2958 
makeAlias()2959 Annotation *HighlightAnnotationPrivate::makeAlias()
2960 {
2961     return new HighlightAnnotation(*this);
2962 }
2963 
toAnnotSubType(HighlightAnnotation::HighlightType type)2964 Annot::AnnotSubtype HighlightAnnotationPrivate::toAnnotSubType(HighlightAnnotation::HighlightType type)
2965 {
2966     switch (type) {
2967     default: // HighlightAnnotation::Highlight:
2968         return Annot::typeHighlight;
2969     case HighlightAnnotation::Underline:
2970         return Annot::typeUnderline;
2971     case HighlightAnnotation::Squiggly:
2972         return Annot::typeSquiggly;
2973     case HighlightAnnotation::StrikeOut:
2974         return Annot::typeStrikeOut;
2975     }
2976 }
2977 
fromQuadrilaterals(AnnotQuadrilaterals * hlquads) const2978 QList<HighlightAnnotation::Quad> HighlightAnnotationPrivate::fromQuadrilaterals(AnnotQuadrilaterals *hlquads) const
2979 {
2980     QList<HighlightAnnotation::Quad> quads;
2981 
2982     if (!hlquads || !hlquads->getQuadrilateralsLength())
2983         return quads;
2984     const int quadsCount = hlquads->getQuadrilateralsLength();
2985 
2986     double MTX[6];
2987     fillTransformationMTX(MTX);
2988 
2989     quads.reserve(quadsCount);
2990     for (int q = 0; q < quadsCount; ++q) {
2991         HighlightAnnotation::Quad quad;
2992         XPDFReader::transform(MTX, hlquads->getX1(q), hlquads->getY1(q), quad.points[0]);
2993         XPDFReader::transform(MTX, hlquads->getX2(q), hlquads->getY2(q), quad.points[1]);
2994         XPDFReader::transform(MTX, hlquads->getX3(q), hlquads->getY3(q), quad.points[2]);
2995         XPDFReader::transform(MTX, hlquads->getX4(q), hlquads->getY4(q), quad.points[3]);
2996         // ### PDF1.6 specs says that point are in ccw order, but in fact
2997         // points 3 and 4 are swapped in every PDF around!
2998         QPointF tmpPoint = quad.points[2];
2999         quad.points[2] = quad.points[3];
3000         quad.points[3] = tmpPoint;
3001         // initialize other properties and append quad
3002         quad.capStart = true; // unlinked quads are always capped
3003         quad.capEnd = true; // unlinked quads are always capped
3004         quad.feather = 0.1; // default feather
3005         quads.append(quad);
3006     }
3007 
3008     return quads;
3009 }
3010 
toQuadrilaterals(const QList<HighlightAnnotation::Quad> & quads) const3011 AnnotQuadrilaterals *HighlightAnnotationPrivate::toQuadrilaterals(const QList<HighlightAnnotation::Quad> &quads) const
3012 {
3013     const int count = quads.size();
3014     auto ac = std::make_unique<AnnotQuadrilaterals::AnnotQuadrilateral[]>(count);
3015 
3016     double MTX[6];
3017     fillTransformationMTX(MTX);
3018 
3019     int pos = 0;
3020     foreach (const HighlightAnnotation::Quad &q, quads) {
3021         double x1, y1, x2, y2, x3, y3, x4, y4;
3022         XPDFReader::invTransform(MTX, q.points[0], x1, y1);
3023         XPDFReader::invTransform(MTX, q.points[1], x2, y2);
3024         // Swap points 3 and 4 (see HighlightAnnotationPrivate::fromQuadrilaterals)
3025         XPDFReader::invTransform(MTX, q.points[3], x3, y3);
3026         XPDFReader::invTransform(MTX, q.points[2], x4, y4);
3027         ac[pos++] = AnnotQuadrilaterals::AnnotQuadrilateral(x1, y1, x2, y2, x3, y3, x4, y4);
3028     }
3029 
3030     return new AnnotQuadrilaterals(std::move(ac), count);
3031 }
3032 
createNativeAnnot(::Page * destPage,DocumentData * doc)3033 Annot *HighlightAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
3034 {
3035     // Setters are defined in the public class
3036     HighlightAnnotation *q = static_cast<HighlightAnnotation *>(makeAlias());
3037 
3038     // Set page and document
3039     pdfPage = destPage;
3040     parentDoc = doc;
3041 
3042     // Set pdfAnnot
3043     PDFRectangle rect = boundaryToPdfRectangle(boundary, flags);
3044     pdfAnnot = new AnnotTextMarkup(destPage->getDoc(), &rect, toAnnotSubType(highlightType));
3045 
3046     // Set properties
3047     flushBaseAnnotationProperties();
3048     q->setHighlightQuads(highlightQuads);
3049 
3050     highlightQuads.clear(); // Free up memory
3051 
3052     delete q;
3053 
3054     return pdfAnnot;
3055 }
3056 
HighlightAnnotation()3057 HighlightAnnotation::HighlightAnnotation() : Annotation(*new HighlightAnnotationPrivate()) { }
3058 
HighlightAnnotation(HighlightAnnotationPrivate & dd)3059 HighlightAnnotation::HighlightAnnotation(HighlightAnnotationPrivate &dd) : Annotation(dd) { }
3060 
HighlightAnnotation(const QDomNode & node)3061 HighlightAnnotation::HighlightAnnotation(const QDomNode &node) : Annotation(*new HighlightAnnotationPrivate(), node)
3062 {
3063     // loop through the whole children looking for a 'hl' element
3064     QDomNode subNode = node.firstChild();
3065     while (subNode.isElement()) {
3066         QDomElement e = subNode.toElement();
3067         subNode = subNode.nextSibling();
3068         if (e.tagName() != QLatin1String("hl"))
3069             continue;
3070 
3071         // parse the attributes
3072         if (e.hasAttribute(QStringLiteral("type")))
3073             setHighlightType((HighlightAnnotation::HighlightType)e.attribute(QStringLiteral("type")).toInt());
3074 
3075         // parse all 'quad' subnodes
3076         QList<HighlightAnnotation::Quad> quads;
3077         QDomNode quadNode = e.firstChild();
3078         for (; quadNode.isElement(); quadNode = quadNode.nextSibling()) {
3079             QDomElement qe = quadNode.toElement();
3080             if (qe.tagName() != QLatin1String("quad"))
3081                 continue;
3082 
3083             Quad q;
3084             q.points[0].setX(qe.attribute(QStringLiteral("ax"), QStringLiteral("0.0")).toDouble());
3085             q.points[0].setY(qe.attribute(QStringLiteral("ay"), QStringLiteral("0.0")).toDouble());
3086             q.points[1].setX(qe.attribute(QStringLiteral("bx"), QStringLiteral("0.0")).toDouble());
3087             q.points[1].setY(qe.attribute(QStringLiteral("by"), QStringLiteral("0.0")).toDouble());
3088             q.points[2].setX(qe.attribute(QStringLiteral("cx"), QStringLiteral("0.0")).toDouble());
3089             q.points[2].setY(qe.attribute(QStringLiteral("cy"), QStringLiteral("0.0")).toDouble());
3090             q.points[3].setX(qe.attribute(QStringLiteral("dx"), QStringLiteral("0.0")).toDouble());
3091             q.points[3].setY(qe.attribute(QStringLiteral("dy"), QStringLiteral("0.0")).toDouble());
3092             q.capStart = qe.hasAttribute(QStringLiteral("start"));
3093             q.capEnd = qe.hasAttribute(QStringLiteral("end"));
3094             q.feather = qe.attribute(QStringLiteral("feather"), QStringLiteral("0.1")).toDouble();
3095             quads.append(q);
3096         }
3097         setHighlightQuads(quads);
3098 
3099         // loading complete
3100         break;
3101     }
3102 }
3103 
~HighlightAnnotation()3104 HighlightAnnotation::~HighlightAnnotation() { }
3105 
store(QDomNode & node,QDomDocument & document) const3106 void HighlightAnnotation::store(QDomNode &node, QDomDocument &document) const
3107 {
3108     // store base annotation properties
3109     storeBaseAnnotationProperties(node, document);
3110 
3111     // create [hl] element
3112     QDomElement hlElement = document.createElement(QStringLiteral("hl"));
3113     node.appendChild(hlElement);
3114 
3115     // append the optional attributes
3116     if (highlightType() != Highlight)
3117         hlElement.setAttribute(QStringLiteral("type"), (int)highlightType());
3118 
3119     const QList<HighlightAnnotation::Quad> quads = highlightQuads();
3120     if (quads.count() < 1)
3121         return;
3122     // append highlight quads, all children describe quads
3123     QList<HighlightAnnotation::Quad>::const_iterator it = quads.begin(), end = quads.end();
3124     for (; it != end; ++it) {
3125         QDomElement quadElement = document.createElement(QStringLiteral("quad"));
3126         hlElement.appendChild(quadElement);
3127         const Quad &q = *it;
3128         quadElement.setAttribute(QStringLiteral("ax"), QString::number(q.points[0].x()));
3129         quadElement.setAttribute(QStringLiteral("ay"), QString::number(q.points[0].y()));
3130         quadElement.setAttribute(QStringLiteral("bx"), QString::number(q.points[1].x()));
3131         quadElement.setAttribute(QStringLiteral("by"), QString::number(q.points[1].y()));
3132         quadElement.setAttribute(QStringLiteral("cx"), QString::number(q.points[2].x()));
3133         quadElement.setAttribute(QStringLiteral("cy"), QString::number(q.points[2].y()));
3134         quadElement.setAttribute(QStringLiteral("dx"), QString::number(q.points[3].x()));
3135         quadElement.setAttribute(QStringLiteral("dy"), QString::number(q.points[3].y()));
3136         if (q.capStart)
3137             quadElement.setAttribute(QStringLiteral("start"), 1);
3138         if (q.capEnd)
3139             quadElement.setAttribute(QStringLiteral("end"), 1);
3140         quadElement.setAttribute(QStringLiteral("feather"), QString::number(q.feather));
3141     }
3142 }
3143 
subType() const3144 Annotation::SubType HighlightAnnotation::subType() const
3145 {
3146     return AHighlight;
3147 }
3148 
highlightType() const3149 HighlightAnnotation::HighlightType HighlightAnnotation::highlightType() const
3150 {
3151     Q_D(const HighlightAnnotation);
3152 
3153     if (!d->pdfAnnot)
3154         return d->highlightType;
3155 
3156     Annot::AnnotSubtype subType = d->pdfAnnot->getType();
3157 
3158     if (subType == Annot::typeHighlight)
3159         return HighlightAnnotation::Highlight;
3160     else if (subType == Annot::typeUnderline)
3161         return HighlightAnnotation::Underline;
3162     else if (subType == Annot::typeSquiggly)
3163         return HighlightAnnotation::Squiggly;
3164     else // Annot::typeStrikeOut
3165         return HighlightAnnotation::StrikeOut;
3166 }
3167 
setHighlightType(HighlightAnnotation::HighlightType type)3168 void HighlightAnnotation::setHighlightType(HighlightAnnotation::HighlightType type)
3169 {
3170     Q_D(HighlightAnnotation);
3171 
3172     if (!d->pdfAnnot) {
3173         d->highlightType = type;
3174         return;
3175     }
3176 
3177     AnnotTextMarkup *hlann = static_cast<AnnotTextMarkup *>(d->pdfAnnot);
3178     hlann->setType(HighlightAnnotationPrivate::toAnnotSubType(type));
3179 }
3180 
highlightQuads() const3181 QList<HighlightAnnotation::Quad> HighlightAnnotation::highlightQuads() const
3182 {
3183     Q_D(const HighlightAnnotation);
3184 
3185     if (!d->pdfAnnot)
3186         return d->highlightQuads;
3187 
3188     const AnnotTextMarkup *hlann = static_cast<AnnotTextMarkup *>(d->pdfAnnot);
3189     return d->fromQuadrilaterals(hlann->getQuadrilaterals());
3190 }
3191 
setHighlightQuads(const QList<HighlightAnnotation::Quad> & quads)3192 void HighlightAnnotation::setHighlightQuads(const QList<HighlightAnnotation::Quad> &quads)
3193 {
3194     Q_D(HighlightAnnotation);
3195 
3196     if (!d->pdfAnnot) {
3197         d->highlightQuads = quads;
3198         return;
3199     }
3200 
3201     AnnotTextMarkup *hlann = static_cast<AnnotTextMarkup *>(d->pdfAnnot);
3202     AnnotQuadrilaterals *quadrilaterals = d->toQuadrilaterals(quads);
3203     hlann->setQuadrilaterals(quadrilaterals);
3204     delete quadrilaterals;
3205 }
3206 
3207 /** StampAnnotation [Annotation] */
3208 class StampAnnotationPrivate : public AnnotationPrivate
3209 {
3210 public:
3211     StampAnnotationPrivate();
3212     Annotation *makeAlias() override;
3213     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
3214 
3215     AnnotStampImageHelper *convertQImageToAnnotStampImageHelper(const QImage &qimg);
3216 
3217     // data fields
3218     QString stampIconName;
3219     QImage stampCustomImage;
3220 };
3221 
StampAnnotationPrivate()3222 StampAnnotationPrivate::StampAnnotationPrivate() : AnnotationPrivate(), stampIconName(QStringLiteral("Draft")) { }
3223 
makeAlias()3224 Annotation *StampAnnotationPrivate::makeAlias()
3225 {
3226     return new StampAnnotation(*this);
3227 }
3228 
createNativeAnnot(::Page * destPage,DocumentData * doc)3229 Annot *StampAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
3230 {
3231     StampAnnotation *q = static_cast<StampAnnotation *>(makeAlias());
3232 
3233     // Set page and document
3234     pdfPage = destPage;
3235     parentDoc = doc;
3236 
3237     // Set pdfAnnot
3238     PDFRectangle rect = boundaryToPdfRectangle(boundary, flags);
3239     pdfAnnot = new AnnotStamp(destPage->getDoc(), &rect);
3240 
3241     // Set properties
3242     flushBaseAnnotationProperties();
3243     q->setStampIconName(stampIconName);
3244     q->setStampCustomImage(stampCustomImage);
3245 
3246     delete q;
3247 
3248     stampIconName.clear(); // Free up memory
3249 
3250     return pdfAnnot;
3251 }
3252 
convertQImageToAnnotStampImageHelper(const QImage & qimg)3253 AnnotStampImageHelper *StampAnnotationPrivate::convertQImageToAnnotStampImageHelper(const QImage &qimg)
3254 {
3255     QImage convertedQImage = qimg;
3256 
3257     QByteArray data;
3258     QByteArray sMaskData;
3259     const int width = convertedQImage.width();
3260     const int height = convertedQImage.height();
3261     int bitsPerComponent = 1;
3262     ColorSpace colorSpace = ColorSpace::DeviceGray;
3263 
3264     switch (convertedQImage.format()) {
3265     case QImage::Format_MonoLSB:
3266         if (!convertedQImage.allGray()) {
3267             convertedQImage = convertedQImage.convertToFormat(QImage::Format_RGB888);
3268 
3269             colorSpace = ColorSpace::DeviceRGB;
3270             bitsPerComponent = 8;
3271         } else {
3272             convertedQImage = convertedQImage.convertToFormat(QImage::Format_Mono);
3273         }
3274         break;
3275     case QImage::Format_Mono:
3276         if (!convertedQImage.allGray()) {
3277             convertedQImage = convertedQImage.convertToFormat(QImage::Format_RGB888);
3278 
3279             colorSpace = ColorSpace::DeviceRGB;
3280             bitsPerComponent = 8;
3281         }
3282         break;
3283     case QImage::Format_RGB32:
3284     case QImage::Format_ARGB32_Premultiplied:
3285     case QImage::Format_ARGB8565_Premultiplied:
3286     case QImage::Format_ARGB6666_Premultiplied:
3287     case QImage::Format_ARGB8555_Premultiplied:
3288     case QImage::Format_ARGB4444_Premultiplied:
3289     case QImage::Format_Alpha8:
3290         convertedQImage = convertedQImage.convertToFormat(QImage::Format_ARGB32);
3291         colorSpace = ColorSpace::DeviceRGB;
3292         bitsPerComponent = 8;
3293         break;
3294     case QImage::Format_RGBA8888:
3295     case QImage::Format_RGBA8888_Premultiplied:
3296     case QImage::Format_RGBX8888:
3297     case QImage::Format_ARGB32:
3298         colorSpace = ColorSpace::DeviceRGB;
3299         bitsPerComponent = 8;
3300         break;
3301     case QImage::Format_Grayscale8:
3302         bitsPerComponent = 8;
3303         break;
3304 #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
3305     case QImage::Format_Grayscale16:
3306         convertedQImage = convertedQImage.convertToFormat(QImage::Format_Grayscale8);
3307 
3308         colorSpace = ColorSpace::DeviceGray;
3309         bitsPerComponent = 8;
3310         break;
3311 #endif
3312     case QImage::Format_RGB16:
3313     case QImage::Format_RGB666:
3314     case QImage::Format_RGB555:
3315     case QImage::Format_RGB444:
3316         convertedQImage = convertedQImage.convertToFormat(QImage::Format_RGB888);
3317         colorSpace = ColorSpace::DeviceRGB;
3318         bitsPerComponent = 8;
3319         break;
3320     case QImage::Format_RGB888:
3321         colorSpace = ColorSpace::DeviceRGB;
3322         bitsPerComponent = 8;
3323         break;
3324     default:
3325         convertedQImage = convertedQImage.convertToFormat(QImage::Format_ARGB32);
3326 
3327         colorSpace = ColorSpace::DeviceRGB;
3328         bitsPerComponent = 8;
3329         break;
3330     }
3331 
3332     getRawDataFromQImage(convertedQImage, convertedQImage.depth(), &data, &sMaskData);
3333 
3334     AnnotStampImageHelper *annotImg;
3335 
3336     if (sMaskData.count() > 0) {
3337         AnnotStampImageHelper sMask(parentDoc->doc, width, height, ColorSpace::DeviceGray, 8, sMaskData.data(), sMaskData.count());
3338         annotImg = new AnnotStampImageHelper(parentDoc->doc, width, height, colorSpace, bitsPerComponent, data.data(), data.count(), sMask.getRef());
3339     } else {
3340         annotImg = new AnnotStampImageHelper(parentDoc->doc, width, height, colorSpace, bitsPerComponent, data.data(), data.count());
3341     }
3342 
3343     return annotImg;
3344 }
3345 
StampAnnotation()3346 StampAnnotation::StampAnnotation() : Annotation(*new StampAnnotationPrivate()) { }
3347 
StampAnnotation(StampAnnotationPrivate & dd)3348 StampAnnotation::StampAnnotation(StampAnnotationPrivate &dd) : Annotation(dd) { }
3349 
StampAnnotation(const QDomNode & node)3350 StampAnnotation::StampAnnotation(const QDomNode &node) : Annotation(*new StampAnnotationPrivate(), node)
3351 {
3352     // loop through the whole children looking for a 'stamp' element
3353     QDomNode subNode = node.firstChild();
3354     while (subNode.isElement()) {
3355         QDomElement e = subNode.toElement();
3356         subNode = subNode.nextSibling();
3357         if (e.tagName() != QLatin1String("stamp"))
3358             continue;
3359 
3360         // parse the attributes
3361         if (e.hasAttribute(QStringLiteral("icon")))
3362             setStampIconName(e.attribute(QStringLiteral("icon")));
3363 
3364         // loading complete
3365         break;
3366     }
3367 }
3368 
~StampAnnotation()3369 StampAnnotation::~StampAnnotation() { }
3370 
store(QDomNode & node,QDomDocument & document) const3371 void StampAnnotation::store(QDomNode &node, QDomDocument &document) const
3372 {
3373     // store base annotation properties
3374     storeBaseAnnotationProperties(node, document);
3375 
3376     // create [stamp] element
3377     QDomElement stampElement = document.createElement(QStringLiteral("stamp"));
3378     node.appendChild(stampElement);
3379 
3380     // append the optional attributes
3381     if (stampIconName() != QLatin1String("Draft"))
3382         stampElement.setAttribute(QStringLiteral("icon"), stampIconName());
3383 }
3384 
subType() const3385 Annotation::SubType StampAnnotation::subType() const
3386 {
3387     return AStamp;
3388 }
3389 
stampIconName() const3390 QString StampAnnotation::stampIconName() const
3391 {
3392     Q_D(const StampAnnotation);
3393 
3394     if (!d->pdfAnnot)
3395         return d->stampIconName;
3396 
3397     const AnnotStamp *stampann = static_cast<const AnnotStamp *>(d->pdfAnnot);
3398     return QString::fromLatin1(stampann->getIcon()->c_str());
3399 }
3400 
setStampIconName(const QString & name)3401 void StampAnnotation::setStampIconName(const QString &name)
3402 {
3403     Q_D(StampAnnotation);
3404 
3405     if (!d->pdfAnnot) {
3406         d->stampIconName = name;
3407         return;
3408     }
3409 
3410     AnnotStamp *stampann = static_cast<AnnotStamp *>(d->pdfAnnot);
3411     QByteArray encoded = name.toLatin1();
3412     GooString s(encoded.constData());
3413     stampann->setIcon(&s);
3414 }
3415 
setStampCustomImage(const QImage & image)3416 void StampAnnotation::setStampCustomImage(const QImage &image)
3417 {
3418     if (image.isNull()) {
3419         return;
3420     }
3421 
3422     Q_D(StampAnnotation);
3423 
3424     if (!d->pdfAnnot) {
3425         d->stampCustomImage = QImage(image);
3426         return;
3427     }
3428 
3429     AnnotStamp *stampann = static_cast<AnnotStamp *>(d->pdfAnnot);
3430     AnnotStampImageHelper *annotCustomImage = d->convertQImageToAnnotStampImageHelper(image);
3431     stampann->setCustomImage(annotCustomImage);
3432 }
3433 
3434 /** InkAnnotation [Annotation] */
3435 class InkAnnotationPrivate : public AnnotationPrivate
3436 {
3437 public:
3438     InkAnnotationPrivate();
3439     Annotation *makeAlias() override;
3440     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
3441 
3442     // data fields
3443     QList<QLinkedList<QPointF>> inkPaths;
3444 
3445     // helper
3446     AnnotPath **toAnnotPaths(const QList<QLinkedList<QPointF>> &paths);
3447 };
3448 
InkAnnotationPrivate()3449 InkAnnotationPrivate::InkAnnotationPrivate() : AnnotationPrivate() { }
3450 
makeAlias()3451 Annotation *InkAnnotationPrivate::makeAlias()
3452 {
3453     return new InkAnnotation(*this);
3454 }
3455 
3456 // Note: Caller is required to delete array elements and the array itself after use
toAnnotPaths(const QList<QLinkedList<QPointF>> & paths)3457 AnnotPath **InkAnnotationPrivate::toAnnotPaths(const QList<QLinkedList<QPointF>> &paths)
3458 {
3459     const int pathsNumber = paths.size();
3460     AnnotPath **res = new AnnotPath *[pathsNumber];
3461     for (int i = 0; i < pathsNumber; ++i)
3462         res[i] = toAnnotPath(paths[i]);
3463     return res;
3464 }
3465 
createNativeAnnot(::Page * destPage,DocumentData * doc)3466 Annot *InkAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
3467 {
3468     // Setters are defined in the public class
3469     InkAnnotation *q = static_cast<InkAnnotation *>(makeAlias());
3470 
3471     // Set page and document
3472     pdfPage = destPage;
3473     parentDoc = doc;
3474 
3475     // Set pdfAnnot
3476     PDFRectangle rect = boundaryToPdfRectangle(boundary, flags);
3477     pdfAnnot = new AnnotInk(destPage->getDoc(), &rect);
3478 
3479     // Set properties
3480     flushBaseAnnotationProperties();
3481     q->setInkPaths(inkPaths);
3482 
3483     inkPaths.clear(); // Free up memory
3484 
3485     delete q;
3486 
3487     return pdfAnnot;
3488 }
3489 
InkAnnotation()3490 InkAnnotation::InkAnnotation() : Annotation(*new InkAnnotationPrivate()) { }
3491 
InkAnnotation(InkAnnotationPrivate & dd)3492 InkAnnotation::InkAnnotation(InkAnnotationPrivate &dd) : Annotation(dd) { }
3493 
InkAnnotation(const QDomNode & node)3494 InkAnnotation::InkAnnotation(const QDomNode &node) : Annotation(*new InkAnnotationPrivate(), node)
3495 {
3496     // loop through the whole children looking for a 'ink' element
3497     QDomNode subNode = node.firstChild();
3498     while (subNode.isElement()) {
3499         QDomElement e = subNode.toElement();
3500         subNode = subNode.nextSibling();
3501         if (e.tagName() != QLatin1String("ink"))
3502             continue;
3503 
3504         // parse the 'path' subnodes
3505         QList<QLinkedList<QPointF>> paths;
3506         QDomNode pathNode = e.firstChild();
3507         while (pathNode.isElement()) {
3508             QDomElement pathElement = pathNode.toElement();
3509             pathNode = pathNode.nextSibling();
3510 
3511             if (pathElement.tagName() != QLatin1String("path"))
3512                 continue;
3513 
3514             // build each path parsing 'point' subnodes
3515             QLinkedList<QPointF> path;
3516             QDomNode pointNode = pathElement.firstChild();
3517             while (pointNode.isElement()) {
3518                 QDomElement pointElement = pointNode.toElement();
3519                 pointNode = pointNode.nextSibling();
3520 
3521                 if (pointElement.tagName() != QLatin1String("point"))
3522                     continue;
3523 
3524                 QPointF p(pointElement.attribute(QStringLiteral("x"), QStringLiteral("0.0")).toDouble(), pointElement.attribute(QStringLiteral("y"), QStringLiteral("0.0")).toDouble());
3525                 path.append(p);
3526             }
3527 
3528             // add the path to the path list if it contains at least 2 nodes
3529             if (path.count() >= 2)
3530                 paths.append(path);
3531         }
3532         setInkPaths(paths);
3533 
3534         // loading complete
3535         break;
3536     }
3537 }
3538 
~InkAnnotation()3539 InkAnnotation::~InkAnnotation() { }
3540 
store(QDomNode & node,QDomDocument & document) const3541 void InkAnnotation::store(QDomNode &node, QDomDocument &document) const
3542 {
3543     // store base annotation properties
3544     storeBaseAnnotationProperties(node, document);
3545 
3546     // create [ink] element
3547     QDomElement inkElement = document.createElement(QStringLiteral("ink"));
3548     node.appendChild(inkElement);
3549 
3550     // append the optional attributes
3551     const QList<QLinkedList<QPointF>> paths = inkPaths();
3552     if (paths.count() < 1)
3553         return;
3554     QList<QLinkedList<QPointF>>::const_iterator pIt = paths.begin(), pEnd = paths.end();
3555     for (; pIt != pEnd; ++pIt) {
3556         QDomElement pathElement = document.createElement(QStringLiteral("path"));
3557         inkElement.appendChild(pathElement);
3558         const QLinkedList<QPointF> &path = *pIt;
3559         QLinkedList<QPointF>::const_iterator iIt = path.begin(), iEnd = path.end();
3560         for (; iIt != iEnd; ++iIt) {
3561             const QPointF &point = *iIt;
3562             QDomElement pointElement = document.createElement(QStringLiteral("point"));
3563             pathElement.appendChild(pointElement);
3564             pointElement.setAttribute(QStringLiteral("x"), QString::number(point.x()));
3565             pointElement.setAttribute(QStringLiteral("y"), QString::number(point.y()));
3566         }
3567     }
3568 }
3569 
subType() const3570 Annotation::SubType InkAnnotation::subType() const
3571 {
3572     return AInk;
3573 }
3574 
inkPaths() const3575 QList<QLinkedList<QPointF>> InkAnnotation::inkPaths() const
3576 {
3577     Q_D(const InkAnnotation);
3578 
3579     if (!d->pdfAnnot)
3580         return d->inkPaths;
3581 
3582     const AnnotInk *inkann = static_cast<const AnnotInk *>(d->pdfAnnot);
3583 
3584     const AnnotPath *const *paths = inkann->getInkList();
3585     if (!paths || !inkann->getInkListLength())
3586         return QList<QLinkedList<QPointF>>();
3587 
3588     double MTX[6];
3589     d->fillTransformationMTX(MTX);
3590 
3591     const int pathsNumber = inkann->getInkListLength();
3592     QList<QLinkedList<QPointF>> inkPaths;
3593     inkPaths.reserve(pathsNumber);
3594     for (int m = 0; m < pathsNumber; ++m) {
3595         // transform each path in a list of normalized points ..
3596         QLinkedList<QPointF> localList;
3597         const AnnotPath *path = paths[m];
3598         const int pointsNumber = path ? path->getCoordsLength() : 0;
3599         for (int n = 0; n < pointsNumber; ++n) {
3600             QPointF point;
3601             XPDFReader::transform(MTX, path->getX(n), path->getY(n), point);
3602             localList.append(point);
3603         }
3604         // ..and add it to the annotation
3605         inkPaths.append(localList);
3606     }
3607     return inkPaths;
3608 }
3609 
setInkPaths(const QList<QLinkedList<QPointF>> & paths)3610 void InkAnnotation::setInkPaths(const QList<QLinkedList<QPointF>> &paths)
3611 {
3612     Q_D(InkAnnotation);
3613 
3614     if (!d->pdfAnnot) {
3615         d->inkPaths = paths;
3616         return;
3617     }
3618 
3619     AnnotInk *inkann = static_cast<AnnotInk *>(d->pdfAnnot);
3620     AnnotPath **annotpaths = d->toAnnotPaths(paths);
3621     const int pathsNumber = paths.size();
3622     inkann->setInkList(annotpaths, pathsNumber);
3623 
3624     for (int i = 0; i < pathsNumber; ++i)
3625         delete annotpaths[i];
3626     delete[] annotpaths;
3627 }
3628 
3629 /** LinkAnnotation [Annotation] */
3630 class LinkAnnotationPrivate : public AnnotationPrivate
3631 {
3632 public:
3633     LinkAnnotationPrivate();
3634     ~LinkAnnotationPrivate() override;
3635     Annotation *makeAlias() override;
3636     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
3637 
3638     // data fields
3639     Link *linkDestination;
3640     LinkAnnotation::HighlightMode linkHLMode;
3641     QPointF linkRegion[4];
3642 };
3643 
LinkAnnotationPrivate()3644 LinkAnnotationPrivate::LinkAnnotationPrivate() : AnnotationPrivate(), linkDestination(nullptr), linkHLMode(LinkAnnotation::Invert) { }
3645 
~LinkAnnotationPrivate()3646 LinkAnnotationPrivate::~LinkAnnotationPrivate()
3647 {
3648     delete linkDestination;
3649 }
3650 
makeAlias()3651 Annotation *LinkAnnotationPrivate::makeAlias()
3652 {
3653     return new LinkAnnotation(*this);
3654 }
3655 
createNativeAnnot(::Page * destPage,DocumentData * doc)3656 Annot *LinkAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
3657 {
3658     return nullptr; // Not implemented
3659 }
3660 
LinkAnnotation()3661 LinkAnnotation::LinkAnnotation() : Annotation(*new LinkAnnotationPrivate()) { }
3662 
LinkAnnotation(LinkAnnotationPrivate & dd)3663 LinkAnnotation::LinkAnnotation(LinkAnnotationPrivate &dd) : Annotation(dd) { }
3664 
LinkAnnotation(const QDomNode & node)3665 LinkAnnotation::LinkAnnotation(const QDomNode &node) : Annotation(*new LinkAnnotationPrivate(), node)
3666 {
3667     // loop through the whole children looking for a 'link' element
3668     QDomNode subNode = node.firstChild();
3669     while (subNode.isElement()) {
3670         QDomElement e = subNode.toElement();
3671         subNode = subNode.nextSibling();
3672         if (e.tagName() != QLatin1String("link"))
3673             continue;
3674 
3675         // parse the attributes
3676         if (e.hasAttribute(QStringLiteral("hlmode")))
3677             setLinkHighlightMode((LinkAnnotation::HighlightMode)e.attribute(QStringLiteral("hlmode")).toInt());
3678 
3679         // parse all 'quad' subnodes
3680         QDomNode quadNode = e.firstChild();
3681         for (; quadNode.isElement(); quadNode = quadNode.nextSibling()) {
3682             QDomElement qe = quadNode.toElement();
3683             if (qe.tagName() == QLatin1String("quad")) {
3684                 setLinkRegionPoint(0, QPointF(qe.attribute(QStringLiteral("ax"), QStringLiteral("0.0")).toDouble(), qe.attribute(QStringLiteral("ay"), QStringLiteral("0.0")).toDouble()));
3685                 setLinkRegionPoint(1, QPointF(qe.attribute(QStringLiteral("bx"), QStringLiteral("0.0")).toDouble(), qe.attribute(QStringLiteral("by"), QStringLiteral("0.0")).toDouble()));
3686                 setLinkRegionPoint(2, QPointF(qe.attribute(QStringLiteral("cx"), QStringLiteral("0.0")).toDouble(), qe.attribute(QStringLiteral("cy"), QStringLiteral("0.0")).toDouble()));
3687                 setLinkRegionPoint(3, QPointF(qe.attribute(QStringLiteral("dx"), QStringLiteral("0.0")).toDouble(), qe.attribute(QStringLiteral("dy"), QStringLiteral("0.0")).toDouble()));
3688             } else if (qe.tagName() == QLatin1String("link")) {
3689                 QString type = qe.attribute(QStringLiteral("type"));
3690                 if (type == QLatin1String("GoTo")) {
3691                     Poppler::LinkGoto *go = new Poppler::LinkGoto(QRect(), qe.attribute(QStringLiteral("filename")), LinkDestination(qe.attribute(QStringLiteral("destination"))));
3692                     setLinkDestination(go);
3693                 } else if (type == QLatin1String("Exec")) {
3694                     Poppler::LinkExecute *exec = new Poppler::LinkExecute(QRect(), qe.attribute(QStringLiteral("filename")), qe.attribute(QStringLiteral("parameters")));
3695                     setLinkDestination(exec);
3696                 } else if (type == QLatin1String("Browse")) {
3697                     Poppler::LinkBrowse *browse = new Poppler::LinkBrowse(QRect(), qe.attribute(QStringLiteral("url")));
3698                     setLinkDestination(browse);
3699                 } else if (type == QLatin1String("Action")) {
3700                     Poppler::LinkAction::ActionType act;
3701                     QString actString = qe.attribute(QStringLiteral("action"));
3702                     bool found = true;
3703                     if (actString == QLatin1String("PageFirst"))
3704                         act = Poppler::LinkAction::PageFirst;
3705                     else if (actString == QLatin1String("PagePrev"))
3706                         act = Poppler::LinkAction::PagePrev;
3707                     else if (actString == QLatin1String("PageNext"))
3708                         act = Poppler::LinkAction::PageNext;
3709                     else if (actString == QLatin1String("PageLast"))
3710                         act = Poppler::LinkAction::PageLast;
3711                     else if (actString == QLatin1String("HistoryBack"))
3712                         act = Poppler::LinkAction::HistoryBack;
3713                     else if (actString == QLatin1String("HistoryForward"))
3714                         act = Poppler::LinkAction::HistoryForward;
3715                     else if (actString == QLatin1String("Quit"))
3716                         act = Poppler::LinkAction::Quit;
3717                     else if (actString == QLatin1String("Presentation"))
3718                         act = Poppler::LinkAction::Presentation;
3719                     else if (actString == QLatin1String("EndPresentation"))
3720                         act = Poppler::LinkAction::EndPresentation;
3721                     else if (actString == QLatin1String("Find"))
3722                         act = Poppler::LinkAction::Find;
3723                     else if (actString == QLatin1String("GoToPage"))
3724                         act = Poppler::LinkAction::GoToPage;
3725                     else if (actString == QLatin1String("Close"))
3726                         act = Poppler::LinkAction::Close;
3727                     else if (actString == QLatin1String("Print"))
3728                         act = Poppler::LinkAction::Print;
3729                     else
3730                         found = false;
3731                     if (found) {
3732                         Poppler::LinkAction *action = new Poppler::LinkAction(QRect(), act);
3733                         setLinkDestination(action);
3734                     }
3735                 } else {
3736                     qWarning("Loading annotations of type %s from DOM nodes is not yet implemented.", type.toLocal8Bit().constData());
3737                 }
3738             }
3739         }
3740 
3741         // loading complete
3742         break;
3743     }
3744 }
3745 
~LinkAnnotation()3746 LinkAnnotation::~LinkAnnotation() { }
3747 
store(QDomNode & node,QDomDocument & document) const3748 void LinkAnnotation::store(QDomNode &node, QDomDocument &document) const
3749 {
3750     // store base annotation properties
3751     storeBaseAnnotationProperties(node, document);
3752 
3753     // create [hl] element
3754     QDomElement linkElement = document.createElement(QStringLiteral("link"));
3755     node.appendChild(linkElement);
3756 
3757     // append the optional attributes
3758     if (linkHighlightMode() != Invert)
3759         linkElement.setAttribute(QStringLiteral("hlmode"), (int)linkHighlightMode());
3760 
3761     // saving region
3762     QDomElement quadElement = document.createElement(QStringLiteral("quad"));
3763     linkElement.appendChild(quadElement);
3764     quadElement.setAttribute(QStringLiteral("ax"), QString::number(linkRegionPoint(0).x()));
3765     quadElement.setAttribute(QStringLiteral("ay"), QString::number(linkRegionPoint(0).y()));
3766     quadElement.setAttribute(QStringLiteral("bx"), QString::number(linkRegionPoint(1).x()));
3767     quadElement.setAttribute(QStringLiteral("by"), QString::number(linkRegionPoint(1).y()));
3768     quadElement.setAttribute(QStringLiteral("cx"), QString::number(linkRegionPoint(2).x()));
3769     quadElement.setAttribute(QStringLiteral("cy"), QString::number(linkRegionPoint(2).y()));
3770     quadElement.setAttribute(QStringLiteral("dx"), QString::number(linkRegionPoint(3).x()));
3771     quadElement.setAttribute(QStringLiteral("dy"), QString::number(linkRegionPoint(3).y()));
3772 
3773     // saving link
3774     QDomElement hyperlinkElement = document.createElement(QStringLiteral("link"));
3775     linkElement.appendChild(hyperlinkElement);
3776     if (linkDestination()) {
3777         switch (linkDestination()->linkType()) {
3778         case Poppler::Link::Goto: {
3779             Poppler::LinkGoto *go = static_cast<Poppler::LinkGoto *>(linkDestination());
3780             hyperlinkElement.setAttribute(QStringLiteral("type"), QStringLiteral("GoTo"));
3781             hyperlinkElement.setAttribute(QStringLiteral("filename"), go->fileName());
3782             hyperlinkElement.setAttribute(QStringLiteral("destionation"), go->destination().toString()); // TODO remove for poppler 0.28
3783             hyperlinkElement.setAttribute(QStringLiteral("destination"), go->destination().toString());
3784             break;
3785         }
3786         case Poppler::Link::Execute: {
3787             Poppler::LinkExecute *exec = static_cast<Poppler::LinkExecute *>(linkDestination());
3788             hyperlinkElement.setAttribute(QStringLiteral("type"), QStringLiteral("Exec"));
3789             hyperlinkElement.setAttribute(QStringLiteral("filename"), exec->fileName());
3790             hyperlinkElement.setAttribute(QStringLiteral("parameters"), exec->parameters());
3791             break;
3792         }
3793         case Poppler::Link::Browse: {
3794             Poppler::LinkBrowse *browse = static_cast<Poppler::LinkBrowse *>(linkDestination());
3795             hyperlinkElement.setAttribute(QStringLiteral("type"), QStringLiteral("Browse"));
3796             hyperlinkElement.setAttribute(QStringLiteral("url"), browse->url());
3797             break;
3798         }
3799         case Poppler::Link::Action: {
3800             Poppler::LinkAction *action = static_cast<Poppler::LinkAction *>(linkDestination());
3801             hyperlinkElement.setAttribute(QStringLiteral("type"), QStringLiteral("Action"));
3802             switch (action->actionType()) {
3803             case Poppler::LinkAction::PageFirst:
3804                 hyperlinkElement.setAttribute(QStringLiteral("action"), QStringLiteral("PageFirst"));
3805                 break;
3806             case Poppler::LinkAction::PagePrev:
3807                 hyperlinkElement.setAttribute(QStringLiteral("action"), QStringLiteral("PagePrev"));
3808                 break;
3809             case Poppler::LinkAction::PageNext:
3810                 hyperlinkElement.setAttribute(QStringLiteral("action"), QStringLiteral("PageNext"));
3811                 break;
3812             case Poppler::LinkAction::PageLast:
3813                 hyperlinkElement.setAttribute(QStringLiteral("action"), QStringLiteral("PageLast"));
3814                 break;
3815             case Poppler::LinkAction::HistoryBack:
3816                 hyperlinkElement.setAttribute(QStringLiteral("action"), QStringLiteral("HistoryBack"));
3817                 break;
3818             case Poppler::LinkAction::HistoryForward:
3819                 hyperlinkElement.setAttribute(QStringLiteral("action"), QStringLiteral("HistoryForward"));
3820                 break;
3821             case Poppler::LinkAction::Quit:
3822                 hyperlinkElement.setAttribute(QStringLiteral("action"), QStringLiteral("Quit"));
3823                 break;
3824             case Poppler::LinkAction::Presentation:
3825                 hyperlinkElement.setAttribute(QStringLiteral("action"), QStringLiteral("Presentation"));
3826                 break;
3827             case Poppler::LinkAction::EndPresentation:
3828                 hyperlinkElement.setAttribute(QStringLiteral("action"), QStringLiteral("EndPresentation"));
3829                 break;
3830             case Poppler::LinkAction::Find:
3831                 hyperlinkElement.setAttribute(QStringLiteral("action"), QStringLiteral("Find"));
3832                 break;
3833             case Poppler::LinkAction::GoToPage:
3834                 hyperlinkElement.setAttribute(QStringLiteral("action"), QStringLiteral("GoToPage"));
3835                 break;
3836             case Poppler::LinkAction::Close:
3837                 hyperlinkElement.setAttribute(QStringLiteral("action"), QStringLiteral("Close"));
3838                 break;
3839             case Poppler::LinkAction::Print:
3840                 hyperlinkElement.setAttribute(QStringLiteral("action"), QStringLiteral("Print"));
3841                 break;
3842             }
3843             break;
3844         }
3845         case Poppler::Link::Movie: {
3846             hyperlinkElement.setAttribute(QStringLiteral("type"), QStringLiteral("Movie"));
3847             break;
3848         }
3849         case Poppler::Link::Rendition: {
3850             hyperlinkElement.setAttribute(QStringLiteral("type"), QStringLiteral("Rendition"));
3851             break;
3852         }
3853         case Poppler::Link::Sound: {
3854             hyperlinkElement.setAttribute(QStringLiteral("type"), QStringLiteral("Sound"));
3855             break;
3856         }
3857         case Poppler::Link::JavaScript: {
3858             hyperlinkElement.setAttribute(QStringLiteral("type"), QStringLiteral("JavaScript"));
3859             break;
3860         }
3861         case Poppler::Link::OCGState: {
3862             hyperlinkElement.setAttribute(QStringLiteral("type"), QStringLiteral("OCGState"));
3863             break;
3864         }
3865         case Poppler::Link::Hide: {
3866             hyperlinkElement.setAttribute(QStringLiteral("type"), QStringLiteral("Hide"));
3867             break;
3868         }
3869         case Poppler::Link::None:
3870             break;
3871         }
3872     }
3873 }
3874 
subType() const3875 Annotation::SubType LinkAnnotation::subType() const
3876 {
3877     return ALink;
3878 }
3879 
linkDestination() const3880 Link *LinkAnnotation::linkDestination() const
3881 {
3882     Q_D(const LinkAnnotation);
3883     return d->linkDestination;
3884 }
3885 
setLinkDestination(Link * link)3886 void LinkAnnotation::setLinkDestination(Link *link)
3887 {
3888     Q_D(LinkAnnotation);
3889     delete d->linkDestination;
3890     d->linkDestination = link;
3891 }
3892 
linkHighlightMode() const3893 LinkAnnotation::HighlightMode LinkAnnotation::linkHighlightMode() const
3894 {
3895     Q_D(const LinkAnnotation);
3896     return d->linkHLMode;
3897 }
3898 
setLinkHighlightMode(LinkAnnotation::HighlightMode mode)3899 void LinkAnnotation::setLinkHighlightMode(LinkAnnotation::HighlightMode mode)
3900 {
3901     Q_D(LinkAnnotation);
3902     d->linkHLMode = mode;
3903 }
3904 
linkRegionPoint(int id) const3905 QPointF LinkAnnotation::linkRegionPoint(int id) const
3906 {
3907     if (id < 0 || id >= 4)
3908         return QPointF();
3909 
3910     Q_D(const LinkAnnotation);
3911     return d->linkRegion[id];
3912 }
3913 
setLinkRegionPoint(int id,const QPointF & point)3914 void LinkAnnotation::setLinkRegionPoint(int id, const QPointF &point) // clazy:exclude=function-args-by-value
3915 {
3916     if (id < 0 || id >= 4)
3917         return;
3918 
3919     Q_D(LinkAnnotation);
3920     d->linkRegion[id] = point;
3921 }
3922 
3923 /** CaretAnnotation [Annotation] */
3924 class CaretAnnotationPrivate : public AnnotationPrivate
3925 {
3926 public:
3927     CaretAnnotationPrivate();
3928     Annotation *makeAlias() override;
3929     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
3930 
3931     // data fields
3932     CaretAnnotation::CaretSymbol symbol;
3933 };
3934 
caretSymbolToString(CaretAnnotation::CaretSymbol symbol)3935 static QString caretSymbolToString(CaretAnnotation::CaretSymbol symbol)
3936 {
3937     switch (symbol) {
3938     case CaretAnnotation::None:
3939         return QStringLiteral("None");
3940     case CaretAnnotation::P:
3941         return QStringLiteral("P");
3942     }
3943     return QString();
3944 }
3945 
caretSymbolFromString(const QString & symbol)3946 static CaretAnnotation::CaretSymbol caretSymbolFromString(const QString &symbol)
3947 {
3948     if (symbol == QLatin1String("None"))
3949         return CaretAnnotation::None;
3950     else if (symbol == QLatin1String("P"))
3951         return CaretAnnotation::P;
3952     return CaretAnnotation::None;
3953 }
3954 
CaretAnnotationPrivate()3955 CaretAnnotationPrivate::CaretAnnotationPrivate() : AnnotationPrivate(), symbol(CaretAnnotation::None) { }
3956 
makeAlias()3957 Annotation *CaretAnnotationPrivate::makeAlias()
3958 {
3959     return new CaretAnnotation(*this);
3960 }
3961 
createNativeAnnot(::Page * destPage,DocumentData * doc)3962 Annot *CaretAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
3963 {
3964     // Setters are defined in the public class
3965     CaretAnnotation *q = static_cast<CaretAnnotation *>(makeAlias());
3966 
3967     // Set page and document
3968     pdfPage = destPage;
3969     parentDoc = doc;
3970 
3971     // Set pdfAnnot
3972     PDFRectangle rect = boundaryToPdfRectangle(boundary, flags);
3973     pdfAnnot = new AnnotCaret(destPage->getDoc(), &rect);
3974 
3975     // Set properties
3976     flushBaseAnnotationProperties();
3977     q->setCaretSymbol(symbol);
3978 
3979     delete q;
3980     return pdfAnnot;
3981 }
3982 
CaretAnnotation()3983 CaretAnnotation::CaretAnnotation() : Annotation(*new CaretAnnotationPrivate()) { }
3984 
CaretAnnotation(CaretAnnotationPrivate & dd)3985 CaretAnnotation::CaretAnnotation(CaretAnnotationPrivate &dd) : Annotation(dd) { }
3986 
CaretAnnotation(const QDomNode & node)3987 CaretAnnotation::CaretAnnotation(const QDomNode &node) : Annotation(*new CaretAnnotationPrivate(), node)
3988 {
3989     // loop through the whole children looking for a 'caret' element
3990     QDomNode subNode = node.firstChild();
3991     while (subNode.isElement()) {
3992         QDomElement e = subNode.toElement();
3993         subNode = subNode.nextSibling();
3994         if (e.tagName() != QLatin1String("caret"))
3995             continue;
3996 
3997         // parse the attributes
3998         if (e.hasAttribute(QStringLiteral("symbol")))
3999             setCaretSymbol(caretSymbolFromString(e.attribute(QStringLiteral("symbol"))));
4000 
4001         // loading complete
4002         break;
4003     }
4004 }
4005 
~CaretAnnotation()4006 CaretAnnotation::~CaretAnnotation() { }
4007 
store(QDomNode & node,QDomDocument & document) const4008 void CaretAnnotation::store(QDomNode &node, QDomDocument &document) const
4009 {
4010     // store base annotation properties
4011     storeBaseAnnotationProperties(node, document);
4012 
4013     // create [caret] element
4014     QDomElement caretElement = document.createElement(QStringLiteral("caret"));
4015     node.appendChild(caretElement);
4016 
4017     // append the optional attributes
4018     if (caretSymbol() != CaretAnnotation::None)
4019         caretElement.setAttribute(QStringLiteral("symbol"), caretSymbolToString(caretSymbol()));
4020 }
4021 
subType() const4022 Annotation::SubType CaretAnnotation::subType() const
4023 {
4024     return ACaret;
4025 }
4026 
caretSymbol() const4027 CaretAnnotation::CaretSymbol CaretAnnotation::caretSymbol() const
4028 {
4029     Q_D(const CaretAnnotation);
4030 
4031     if (!d->pdfAnnot)
4032         return d->symbol;
4033 
4034     const AnnotCaret *caretann = static_cast<const AnnotCaret *>(d->pdfAnnot);
4035     return (CaretAnnotation::CaretSymbol)caretann->getSymbol();
4036 }
4037 
setCaretSymbol(CaretAnnotation::CaretSymbol symbol)4038 void CaretAnnotation::setCaretSymbol(CaretAnnotation::CaretSymbol symbol)
4039 {
4040     Q_D(CaretAnnotation);
4041 
4042     if (!d->pdfAnnot) {
4043         d->symbol = symbol;
4044         return;
4045     }
4046 
4047     AnnotCaret *caretann = static_cast<AnnotCaret *>(d->pdfAnnot);
4048     caretann->setSymbol((AnnotCaret::AnnotCaretSymbol)symbol);
4049 }
4050 
4051 /** FileAttachmentAnnotation [Annotation] */
4052 class FileAttachmentAnnotationPrivate : public AnnotationPrivate
4053 {
4054 public:
4055     FileAttachmentAnnotationPrivate();
4056     ~FileAttachmentAnnotationPrivate() override;
4057     Annotation *makeAlias() override;
4058     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
4059 
4060     // data fields
4061     QString icon;
4062     EmbeddedFile *embfile;
4063 };
4064 
FileAttachmentAnnotationPrivate()4065 FileAttachmentAnnotationPrivate::FileAttachmentAnnotationPrivate() : AnnotationPrivate(), icon(QStringLiteral("PushPin")), embfile(nullptr) { }
4066 
~FileAttachmentAnnotationPrivate()4067 FileAttachmentAnnotationPrivate::~FileAttachmentAnnotationPrivate()
4068 {
4069     delete embfile;
4070 }
4071 
makeAlias()4072 Annotation *FileAttachmentAnnotationPrivate::makeAlias()
4073 {
4074     return new FileAttachmentAnnotation(*this);
4075 }
4076 
createNativeAnnot(::Page * destPage,DocumentData * doc)4077 Annot *FileAttachmentAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
4078 {
4079     return nullptr; // Not implemented
4080 }
4081 
FileAttachmentAnnotation()4082 FileAttachmentAnnotation::FileAttachmentAnnotation() : Annotation(*new FileAttachmentAnnotationPrivate()) { }
4083 
FileAttachmentAnnotation(FileAttachmentAnnotationPrivate & dd)4084 FileAttachmentAnnotation::FileAttachmentAnnotation(FileAttachmentAnnotationPrivate &dd) : Annotation(dd) { }
4085 
FileAttachmentAnnotation(const QDomNode & node)4086 FileAttachmentAnnotation::FileAttachmentAnnotation(const QDomNode &node) : Annotation(*new FileAttachmentAnnotationPrivate(), node)
4087 {
4088     // loop through the whole children looking for a 'fileattachment' element
4089     QDomNode subNode = node.firstChild();
4090     while (subNode.isElement()) {
4091         QDomElement e = subNode.toElement();
4092         subNode = subNode.nextSibling();
4093         if (e.tagName() != QLatin1String("fileattachment"))
4094             continue;
4095 
4096         // loading complete
4097         break;
4098     }
4099 }
4100 
~FileAttachmentAnnotation()4101 FileAttachmentAnnotation::~FileAttachmentAnnotation() { }
4102 
store(QDomNode & node,QDomDocument & document) const4103 void FileAttachmentAnnotation::store(QDomNode &node, QDomDocument &document) const
4104 {
4105     // store base annotation properties
4106     storeBaseAnnotationProperties(node, document);
4107 
4108     // create [fileattachment] element
4109     QDomElement fileAttachmentElement = document.createElement(QStringLiteral("fileattachment"));
4110     node.appendChild(fileAttachmentElement);
4111 }
4112 
subType() const4113 Annotation::SubType FileAttachmentAnnotation::subType() const
4114 {
4115     return AFileAttachment;
4116 }
4117 
fileIconName() const4118 QString FileAttachmentAnnotation::fileIconName() const
4119 {
4120     Q_D(const FileAttachmentAnnotation);
4121     return d->icon;
4122 }
4123 
setFileIconName(const QString & icon)4124 void FileAttachmentAnnotation::setFileIconName(const QString &icon)
4125 {
4126     Q_D(FileAttachmentAnnotation);
4127     d->icon = icon;
4128 }
4129 
embeddedFile() const4130 EmbeddedFile *FileAttachmentAnnotation::embeddedFile() const
4131 {
4132     Q_D(const FileAttachmentAnnotation);
4133     return d->embfile;
4134 }
4135 
setEmbeddedFile(EmbeddedFile * ef)4136 void FileAttachmentAnnotation::setEmbeddedFile(EmbeddedFile *ef)
4137 {
4138     Q_D(FileAttachmentAnnotation);
4139     d->embfile = ef;
4140 }
4141 
4142 /** SoundAnnotation [Annotation] */
4143 class SoundAnnotationPrivate : public AnnotationPrivate
4144 {
4145 public:
4146     SoundAnnotationPrivate();
4147     ~SoundAnnotationPrivate() override;
4148     Annotation *makeAlias() override;
4149     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
4150 
4151     // data fields
4152     QString icon;
4153     SoundObject *sound;
4154 };
4155 
SoundAnnotationPrivate()4156 SoundAnnotationPrivate::SoundAnnotationPrivate() : AnnotationPrivate(), icon(QStringLiteral("Speaker")), sound(nullptr) { }
4157 
~SoundAnnotationPrivate()4158 SoundAnnotationPrivate::~SoundAnnotationPrivate()
4159 {
4160     delete sound;
4161 }
4162 
makeAlias()4163 Annotation *SoundAnnotationPrivate::makeAlias()
4164 {
4165     return new SoundAnnotation(*this);
4166 }
4167 
createNativeAnnot(::Page * destPage,DocumentData * doc)4168 Annot *SoundAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
4169 {
4170     return nullptr; // Not implemented
4171 }
4172 
SoundAnnotation()4173 SoundAnnotation::SoundAnnotation() : Annotation(*new SoundAnnotationPrivate()) { }
4174 
SoundAnnotation(SoundAnnotationPrivate & dd)4175 SoundAnnotation::SoundAnnotation(SoundAnnotationPrivate &dd) : Annotation(dd) { }
4176 
SoundAnnotation(const QDomNode & node)4177 SoundAnnotation::SoundAnnotation(const QDomNode &node) : Annotation(*new SoundAnnotationPrivate(), node)
4178 {
4179     // loop through the whole children looking for a 'sound' element
4180     QDomNode subNode = node.firstChild();
4181     while (subNode.isElement()) {
4182         QDomElement e = subNode.toElement();
4183         subNode = subNode.nextSibling();
4184         if (e.tagName() != QLatin1String("sound"))
4185             continue;
4186 
4187         // loading complete
4188         break;
4189     }
4190 }
4191 
~SoundAnnotation()4192 SoundAnnotation::~SoundAnnotation() { }
4193 
store(QDomNode & node,QDomDocument & document) const4194 void SoundAnnotation::store(QDomNode &node, QDomDocument &document) const
4195 {
4196     // store base annotation properties
4197     storeBaseAnnotationProperties(node, document);
4198 
4199     // create [sound] element
4200     QDomElement soundElement = document.createElement(QStringLiteral("sound"));
4201     node.appendChild(soundElement);
4202 }
4203 
subType() const4204 Annotation::SubType SoundAnnotation::subType() const
4205 {
4206     return ASound;
4207 }
4208 
soundIconName() const4209 QString SoundAnnotation::soundIconName() const
4210 {
4211     Q_D(const SoundAnnotation);
4212     return d->icon;
4213 }
4214 
setSoundIconName(const QString & icon)4215 void SoundAnnotation::setSoundIconName(const QString &icon)
4216 {
4217     Q_D(SoundAnnotation);
4218     d->icon = icon;
4219 }
4220 
sound() const4221 SoundObject *SoundAnnotation::sound() const
4222 {
4223     Q_D(const SoundAnnotation);
4224     return d->sound;
4225 }
4226 
setSound(SoundObject * s)4227 void SoundAnnotation::setSound(SoundObject *s)
4228 {
4229     Q_D(SoundAnnotation);
4230     d->sound = s;
4231 }
4232 
4233 /** MovieAnnotation [Annotation] */
4234 class MovieAnnotationPrivate : public AnnotationPrivate
4235 {
4236 public:
4237     MovieAnnotationPrivate();
4238     ~MovieAnnotationPrivate() override;
4239     Annotation *makeAlias() override;
4240     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
4241 
4242     // data fields
4243     MovieObject *movie;
4244     QString title;
4245 };
4246 
MovieAnnotationPrivate()4247 MovieAnnotationPrivate::MovieAnnotationPrivate() : AnnotationPrivate(), movie(nullptr) { }
4248 
~MovieAnnotationPrivate()4249 MovieAnnotationPrivate::~MovieAnnotationPrivate()
4250 {
4251     delete movie;
4252 }
4253 
makeAlias()4254 Annotation *MovieAnnotationPrivate::makeAlias()
4255 {
4256     return new MovieAnnotation(*this);
4257 }
4258 
createNativeAnnot(::Page * destPage,DocumentData * doc)4259 Annot *MovieAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
4260 {
4261     return nullptr; // Not implemented
4262 }
4263 
MovieAnnotation()4264 MovieAnnotation::MovieAnnotation() : Annotation(*new MovieAnnotationPrivate()) { }
4265 
MovieAnnotation(MovieAnnotationPrivate & dd)4266 MovieAnnotation::MovieAnnotation(MovieAnnotationPrivate &dd) : Annotation(dd) { }
4267 
MovieAnnotation(const QDomNode & node)4268 MovieAnnotation::MovieAnnotation(const QDomNode &node) : Annotation(*new MovieAnnotationPrivate(), node)
4269 {
4270     // loop through the whole children looking for a 'movie' element
4271     QDomNode subNode = node.firstChild();
4272     while (subNode.isElement()) {
4273         QDomElement e = subNode.toElement();
4274         subNode = subNode.nextSibling();
4275         if (e.tagName() != QLatin1String("movie"))
4276             continue;
4277 
4278         // loading complete
4279         break;
4280     }
4281 }
4282 
~MovieAnnotation()4283 MovieAnnotation::~MovieAnnotation() { }
4284 
store(QDomNode & node,QDomDocument & document) const4285 void MovieAnnotation::store(QDomNode &node, QDomDocument &document) const
4286 {
4287     // store base annotation properties
4288     storeBaseAnnotationProperties(node, document);
4289 
4290     // create [movie] element
4291     QDomElement movieElement = document.createElement(QStringLiteral("movie"));
4292     node.appendChild(movieElement);
4293 }
4294 
subType() const4295 Annotation::SubType MovieAnnotation::subType() const
4296 {
4297     return AMovie;
4298 }
4299 
movie() const4300 MovieObject *MovieAnnotation::movie() const
4301 {
4302     Q_D(const MovieAnnotation);
4303     return d->movie;
4304 }
4305 
setMovie(MovieObject * movie)4306 void MovieAnnotation::setMovie(MovieObject *movie)
4307 {
4308     Q_D(MovieAnnotation);
4309     d->movie = movie;
4310 }
4311 
movieTitle() const4312 QString MovieAnnotation::movieTitle() const
4313 {
4314     Q_D(const MovieAnnotation);
4315     return d->title;
4316 }
4317 
setMovieTitle(const QString & title)4318 void MovieAnnotation::setMovieTitle(const QString &title)
4319 {
4320     Q_D(MovieAnnotation);
4321     d->title = title;
4322 }
4323 
4324 /** ScreenAnnotation [Annotation] */
4325 class ScreenAnnotationPrivate : public AnnotationPrivate
4326 {
4327 public:
4328     ScreenAnnotationPrivate();
4329     ~ScreenAnnotationPrivate() override;
4330     Annotation *makeAlias() override;
4331     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
4332 
4333     // data fields
4334     LinkRendition *action;
4335     QString title;
4336 };
4337 
ScreenAnnotationPrivate()4338 ScreenAnnotationPrivate::ScreenAnnotationPrivate() : AnnotationPrivate(), action(nullptr) { }
4339 
~ScreenAnnotationPrivate()4340 ScreenAnnotationPrivate::~ScreenAnnotationPrivate()
4341 {
4342     delete action;
4343 }
4344 
ScreenAnnotation(ScreenAnnotationPrivate & dd)4345 ScreenAnnotation::ScreenAnnotation(ScreenAnnotationPrivate &dd) : Annotation(dd) { }
4346 
makeAlias()4347 Annotation *ScreenAnnotationPrivate::makeAlias()
4348 {
4349     return new ScreenAnnotation(*this);
4350 }
4351 
createNativeAnnot(::Page * destPage,DocumentData * doc)4352 Annot *ScreenAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
4353 {
4354     return nullptr; // Not implemented
4355 }
4356 
ScreenAnnotation()4357 ScreenAnnotation::ScreenAnnotation() : Annotation(*new ScreenAnnotationPrivate()) { }
4358 
~ScreenAnnotation()4359 ScreenAnnotation::~ScreenAnnotation() { }
4360 
store(QDomNode & node,QDomDocument & document) const4361 void ScreenAnnotation::store(QDomNode &node, QDomDocument &document) const
4362 {
4363     // store base annotation properties
4364     storeBaseAnnotationProperties(node, document);
4365 
4366     // create [screen] element
4367     QDomElement screenElement = document.createElement(QStringLiteral("screen"));
4368     node.appendChild(screenElement);
4369 }
4370 
subType() const4371 Annotation::SubType ScreenAnnotation::subType() const
4372 {
4373     return AScreen;
4374 }
4375 
action() const4376 LinkRendition *ScreenAnnotation::action() const
4377 {
4378     Q_D(const ScreenAnnotation);
4379     return d->action;
4380 }
4381 
setAction(LinkRendition * action)4382 void ScreenAnnotation::setAction(LinkRendition *action)
4383 {
4384     Q_D(ScreenAnnotation);
4385     d->action = action;
4386 }
4387 
screenTitle() const4388 QString ScreenAnnotation::screenTitle() const
4389 {
4390     Q_D(const ScreenAnnotation);
4391     return d->title;
4392 }
4393 
setScreenTitle(const QString & title)4394 void ScreenAnnotation::setScreenTitle(const QString &title)
4395 {
4396     Q_D(ScreenAnnotation);
4397     d->title = title;
4398 }
4399 
additionalAction(AdditionalActionType type) const4400 Link *ScreenAnnotation::additionalAction(AdditionalActionType type) const
4401 {
4402     Q_D(const ScreenAnnotation);
4403     return d->additionalAction(type);
4404 }
4405 
4406 /** WidgetAnnotation [Annotation] */
4407 class WidgetAnnotationPrivate : public AnnotationPrivate
4408 {
4409 public:
4410     Annotation *makeAlias() override;
4411     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
4412 };
4413 
makeAlias()4414 Annotation *WidgetAnnotationPrivate::makeAlias()
4415 {
4416     return new WidgetAnnotation(*this);
4417 }
4418 
createNativeAnnot(::Page * destPage,DocumentData * doc)4419 Annot *WidgetAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
4420 {
4421     return nullptr; // Not implemented
4422 }
4423 
WidgetAnnotation(WidgetAnnotationPrivate & dd)4424 WidgetAnnotation::WidgetAnnotation(WidgetAnnotationPrivate &dd) : Annotation(dd) { }
4425 
WidgetAnnotation()4426 WidgetAnnotation::WidgetAnnotation() : Annotation(*new WidgetAnnotationPrivate()) { }
4427 
~WidgetAnnotation()4428 WidgetAnnotation::~WidgetAnnotation() { }
4429 
store(QDomNode & node,QDomDocument & document) const4430 void WidgetAnnotation::store(QDomNode &node, QDomDocument &document) const
4431 {
4432     // store base annotation properties
4433     storeBaseAnnotationProperties(node, document);
4434 
4435     // create [widget] element
4436     QDomElement widgetElement = document.createElement(QStringLiteral("widget"));
4437     node.appendChild(widgetElement);
4438 }
4439 
subType() const4440 Annotation::SubType WidgetAnnotation::subType() const
4441 {
4442     return AWidget;
4443 }
4444 
additionalAction(AdditionalActionType type) const4445 Link *WidgetAnnotation::additionalAction(AdditionalActionType type) const
4446 {
4447     Q_D(const WidgetAnnotation);
4448     return d->additionalAction(type);
4449 }
4450 
4451 /** RichMediaAnnotation [Annotation] */
4452 class RichMediaAnnotation::Params::Private
4453 {
4454 public:
Private()4455     Private() { }
4456 
4457     QString flashVars;
4458 };
4459 
Params()4460 RichMediaAnnotation::Params::Params() : d(new Private) { }
4461 
~Params()4462 RichMediaAnnotation::Params::~Params() { }
4463 
setFlashVars(const QString & flashVars)4464 void RichMediaAnnotation::Params::setFlashVars(const QString &flashVars)
4465 {
4466     d->flashVars = flashVars;
4467 }
4468 
flashVars() const4469 QString RichMediaAnnotation::Params::flashVars() const
4470 {
4471     return d->flashVars;
4472 }
4473 
4474 class RichMediaAnnotation::Instance::Private
4475 {
4476 public:
Private()4477     Private() : params(nullptr) { }
4478 
~Private()4479     ~Private() { delete params; }
4480 
4481     Private(const Private &) = delete;
4482     Private &operator=(const Private &) = delete;
4483 
4484     RichMediaAnnotation::Instance::Type type;
4485     RichMediaAnnotation::Params *params;
4486 };
4487 
Instance()4488 RichMediaAnnotation::Instance::Instance() : d(new Private) { }
4489 
~Instance()4490 RichMediaAnnotation::Instance::~Instance() { }
4491 
setType(Type type)4492 void RichMediaAnnotation::Instance::setType(Type type)
4493 {
4494     d->type = type;
4495 }
4496 
type() const4497 RichMediaAnnotation::Instance::Type RichMediaAnnotation::Instance::type() const
4498 {
4499     return d->type;
4500 }
4501 
setParams(RichMediaAnnotation::Params * params)4502 void RichMediaAnnotation::Instance::setParams(RichMediaAnnotation::Params *params)
4503 {
4504     delete d->params;
4505     d->params = params;
4506 }
4507 
params() const4508 RichMediaAnnotation::Params *RichMediaAnnotation::Instance::params() const
4509 {
4510     return d->params;
4511 }
4512 
4513 class RichMediaAnnotation::Configuration::Private
4514 {
4515 public:
Private()4516     Private() { }
~Private()4517     ~Private()
4518     {
4519         qDeleteAll(instances);
4520         instances.clear();
4521     }
4522 
4523     Private(const Private &) = delete;
4524     Private &operator=(const Private &) = delete;
4525 
4526     RichMediaAnnotation::Configuration::Type type;
4527     QString name;
4528     QList<RichMediaAnnotation::Instance *> instances;
4529 };
4530 
Configuration()4531 RichMediaAnnotation::Configuration::Configuration() : d(new Private) { }
4532 
~Configuration()4533 RichMediaAnnotation::Configuration::~Configuration() { }
4534 
setType(Type type)4535 void RichMediaAnnotation::Configuration::setType(Type type)
4536 {
4537     d->type = type;
4538 }
4539 
type() const4540 RichMediaAnnotation::Configuration::Type RichMediaAnnotation::Configuration::type() const
4541 {
4542     return d->type;
4543 }
4544 
setName(const QString & name)4545 void RichMediaAnnotation::Configuration::setName(const QString &name)
4546 {
4547     d->name = name;
4548 }
4549 
name() const4550 QString RichMediaAnnotation::Configuration::name() const
4551 {
4552     return d->name;
4553 }
4554 
setInstances(const QList<RichMediaAnnotation::Instance * > & instances)4555 void RichMediaAnnotation::Configuration::setInstances(const QList<RichMediaAnnotation::Instance *> &instances)
4556 {
4557     qDeleteAll(d->instances);
4558     d->instances.clear();
4559 
4560     d->instances = instances;
4561 }
4562 
instances() const4563 QList<RichMediaAnnotation::Instance *> RichMediaAnnotation::Configuration::instances() const
4564 {
4565     return d->instances;
4566 }
4567 
4568 class RichMediaAnnotation::Asset::Private
4569 {
4570 public:
Private()4571     Private() : embeddedFile(nullptr) { }
4572 
~Private()4573     ~Private() { delete embeddedFile; }
4574 
4575     Private(const Private &) = delete;
4576     Private &operator=(const Private &) = delete;
4577 
4578     QString name;
4579     EmbeddedFile *embeddedFile;
4580 };
4581 
Asset()4582 RichMediaAnnotation::Asset::Asset() : d(new Private) { }
4583 
~Asset()4584 RichMediaAnnotation::Asset::~Asset() { }
4585 
setName(const QString & name)4586 void RichMediaAnnotation::Asset::setName(const QString &name)
4587 {
4588     d->name = name;
4589 }
4590 
name() const4591 QString RichMediaAnnotation::Asset::name() const
4592 {
4593     return d->name;
4594 }
4595 
setEmbeddedFile(EmbeddedFile * embeddedFile)4596 void RichMediaAnnotation::Asset::setEmbeddedFile(EmbeddedFile *embeddedFile)
4597 {
4598     delete d->embeddedFile;
4599     d->embeddedFile = embeddedFile;
4600 }
4601 
embeddedFile() const4602 EmbeddedFile *RichMediaAnnotation::Asset::embeddedFile() const
4603 {
4604     return d->embeddedFile;
4605 }
4606 
4607 class RichMediaAnnotation::Content::Private
4608 {
4609 public:
Private()4610     Private() { }
~Private()4611     ~Private()
4612     {
4613         qDeleteAll(configurations);
4614         configurations.clear();
4615 
4616         qDeleteAll(assets);
4617         assets.clear();
4618     }
4619 
4620     Private(const Private &) = delete;
4621     Private &operator=(const Private &) = delete;
4622 
4623     QList<RichMediaAnnotation::Configuration *> configurations;
4624     QList<RichMediaAnnotation::Asset *> assets;
4625 };
4626 
Content()4627 RichMediaAnnotation::Content::Content() : d(new Private) { }
4628 
~Content()4629 RichMediaAnnotation::Content::~Content() { }
4630 
setConfigurations(const QList<RichMediaAnnotation::Configuration * > & configurations)4631 void RichMediaAnnotation::Content::setConfigurations(const QList<RichMediaAnnotation::Configuration *> &configurations)
4632 {
4633     qDeleteAll(d->configurations);
4634     d->configurations.clear();
4635 
4636     d->configurations = configurations;
4637 }
4638 
configurations() const4639 QList<RichMediaAnnotation::Configuration *> RichMediaAnnotation::Content::configurations() const
4640 {
4641     return d->configurations;
4642 }
4643 
setAssets(const QList<RichMediaAnnotation::Asset * > & assets)4644 void RichMediaAnnotation::Content::setAssets(const QList<RichMediaAnnotation::Asset *> &assets)
4645 {
4646     qDeleteAll(d->assets);
4647     d->assets.clear();
4648 
4649     d->assets = assets;
4650 }
4651 
assets() const4652 QList<RichMediaAnnotation::Asset *> RichMediaAnnotation::Content::assets() const
4653 {
4654     return d->assets;
4655 }
4656 
4657 class RichMediaAnnotation::Activation::Private
4658 {
4659 public:
Private()4660     Private() : condition(RichMediaAnnotation::Activation::UserAction) { }
4661 
4662     RichMediaAnnotation::Activation::Condition condition;
4663 };
4664 
Activation()4665 RichMediaAnnotation::Activation::Activation() : d(new Private) { }
4666 
~Activation()4667 RichMediaAnnotation::Activation::~Activation() { }
4668 
setCondition(Condition condition)4669 void RichMediaAnnotation::Activation::setCondition(Condition condition)
4670 {
4671     d->condition = condition;
4672 }
4673 
condition() const4674 RichMediaAnnotation::Activation::Condition RichMediaAnnotation::Activation::condition() const
4675 {
4676     return d->condition;
4677 }
4678 
4679 class RichMediaAnnotation::Deactivation::Private : public QSharedData
4680 {
4681 public:
Private()4682     Private() : condition(RichMediaAnnotation::Deactivation::UserAction) { }
4683 
4684     RichMediaAnnotation::Deactivation::Condition condition;
4685 };
4686 
Deactivation()4687 RichMediaAnnotation::Deactivation::Deactivation() : d(new Private) { }
4688 
~Deactivation()4689 RichMediaAnnotation::Deactivation::~Deactivation() { }
4690 
setCondition(Condition condition)4691 void RichMediaAnnotation::Deactivation::setCondition(Condition condition)
4692 {
4693     d->condition = condition;
4694 }
4695 
condition() const4696 RichMediaAnnotation::Deactivation::Condition RichMediaAnnotation::Deactivation::condition() const
4697 {
4698     return d->condition;
4699 }
4700 
4701 class RichMediaAnnotation::Settings::Private : public QSharedData
4702 {
4703 public:
Private()4704     Private() : activation(nullptr), deactivation(nullptr) { }
4705 
4706     RichMediaAnnotation::Activation *activation;
4707     RichMediaAnnotation::Deactivation *deactivation;
4708 };
4709 
Settings()4710 RichMediaAnnotation::Settings::Settings() : d(new Private) { }
4711 
~Settings()4712 RichMediaAnnotation::Settings::~Settings() { }
4713 
setActivation(RichMediaAnnotation::Activation * activation)4714 void RichMediaAnnotation::Settings::setActivation(RichMediaAnnotation::Activation *activation)
4715 {
4716     delete d->activation;
4717     d->activation = activation;
4718 }
4719 
activation() const4720 RichMediaAnnotation::Activation *RichMediaAnnotation::Settings::activation() const
4721 {
4722     return d->activation;
4723 }
4724 
setDeactivation(RichMediaAnnotation::Deactivation * deactivation)4725 void RichMediaAnnotation::Settings::setDeactivation(RichMediaAnnotation::Deactivation *deactivation)
4726 {
4727     delete d->deactivation;
4728     d->deactivation = deactivation;
4729 }
4730 
deactivation() const4731 RichMediaAnnotation::Deactivation *RichMediaAnnotation::Settings::deactivation() const
4732 {
4733     return d->deactivation;
4734 }
4735 
4736 class RichMediaAnnotationPrivate : public AnnotationPrivate
4737 {
4738 public:
RichMediaAnnotationPrivate()4739     RichMediaAnnotationPrivate() : settings(nullptr), content(nullptr) { }
4740 
4741     ~RichMediaAnnotationPrivate() override;
4742 
makeAlias()4743     Annotation *makeAlias() override { return new RichMediaAnnotation(*this); }
4744 
createNativeAnnot(::Page * destPage,DocumentData * doc)4745     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override
4746     {
4747         Q_UNUSED(destPage);
4748         Q_UNUSED(doc);
4749 
4750         return nullptr;
4751     }
4752 
4753     RichMediaAnnotation::Settings *settings;
4754     RichMediaAnnotation::Content *content;
4755 };
4756 
~RichMediaAnnotationPrivate()4757 RichMediaAnnotationPrivate::~RichMediaAnnotationPrivate()
4758 {
4759     delete settings;
4760     delete content;
4761 }
4762 
RichMediaAnnotation()4763 RichMediaAnnotation::RichMediaAnnotation() : Annotation(*new RichMediaAnnotationPrivate()) { }
4764 
RichMediaAnnotation(RichMediaAnnotationPrivate & dd)4765 RichMediaAnnotation::RichMediaAnnotation(RichMediaAnnotationPrivate &dd) : Annotation(dd) { }
4766 
RichMediaAnnotation(const QDomNode & node)4767 RichMediaAnnotation::RichMediaAnnotation(const QDomNode &node) : Annotation(*new RichMediaAnnotationPrivate(), node)
4768 {
4769     // loop through the whole children looking for a 'richMedia' element
4770     QDomNode subNode = node.firstChild();
4771     while (subNode.isElement()) {
4772         QDomElement e = subNode.toElement();
4773         subNode = subNode.nextSibling();
4774         if (e.tagName() != QLatin1String("richMedia"))
4775             continue;
4776 
4777         // loading complete
4778         break;
4779     }
4780 }
4781 
~RichMediaAnnotation()4782 RichMediaAnnotation::~RichMediaAnnotation() { }
4783 
store(QDomNode & node,QDomDocument & document) const4784 void RichMediaAnnotation::store(QDomNode &node, QDomDocument &document) const
4785 {
4786     // store base annotation properties
4787     storeBaseAnnotationProperties(node, document);
4788 
4789     // create [richMedia] element
4790     QDomElement richMediaElement = document.createElement(QStringLiteral("richMedia"));
4791     node.appendChild(richMediaElement);
4792 }
4793 
subType() const4794 Annotation::SubType RichMediaAnnotation::subType() const
4795 {
4796     return ARichMedia;
4797 }
4798 
setSettings(RichMediaAnnotation::Settings * settings)4799 void RichMediaAnnotation::setSettings(RichMediaAnnotation::Settings *settings)
4800 {
4801     Q_D(RichMediaAnnotation);
4802 
4803     delete d->settings;
4804     d->settings = settings;
4805 }
4806 
settings() const4807 RichMediaAnnotation::Settings *RichMediaAnnotation::settings() const
4808 {
4809     Q_D(const RichMediaAnnotation);
4810 
4811     return d->settings;
4812 }
4813 
setContent(RichMediaAnnotation::Content * content)4814 void RichMediaAnnotation::setContent(RichMediaAnnotation::Content *content)
4815 {
4816     Q_D(RichMediaAnnotation);
4817 
4818     delete d->content;
4819     d->content = content;
4820 }
4821 
content() const4822 RichMediaAnnotation::Content *RichMediaAnnotation::content() const
4823 {
4824     Q_D(const RichMediaAnnotation);
4825 
4826     return d->content;
4827 }
4828 
4829 // BEGIN utility annotation functions
convertAnnotColor(const AnnotColor * color)4830 QColor convertAnnotColor(const AnnotColor *color)
4831 {
4832     if (!color)
4833         return QColor();
4834 
4835     QColor newcolor;
4836     const double *color_data = color->getValues();
4837     switch (color->getSpace()) {
4838     case AnnotColor::colorTransparent: // = 0,
4839         newcolor = Qt::transparent;
4840         break;
4841     case AnnotColor::colorGray: // = 1,
4842         newcolor.setRgbF(color_data[0], color_data[0], color_data[0]);
4843         break;
4844     case AnnotColor::colorRGB: // = 3,
4845         newcolor.setRgbF(color_data[0], color_data[1], color_data[2]);
4846         break;
4847     case AnnotColor::colorCMYK: // = 4
4848         newcolor.setCmykF(color_data[0], color_data[1], color_data[2], color_data[3]);
4849         break;
4850     }
4851     return newcolor;
4852 }
4853 
convertQColor(const QColor & c)4854 std::unique_ptr<AnnotColor> convertQColor(const QColor &c)
4855 {
4856     if (c.alpha() == 0)
4857         return {}; // Transparent
4858 
4859     switch (c.spec()) {
4860     case QColor::Rgb:
4861     case QColor::Hsl:
4862     case QColor::Hsv:
4863         return std::make_unique<AnnotColor>(c.redF(), c.greenF(), c.blueF());
4864     case QColor::Cmyk:
4865         return std::make_unique<AnnotColor>(c.cyanF(), c.magentaF(), c.yellowF(), c.blackF());
4866     case QColor::Invalid:
4867     default:
4868         return {};
4869     }
4870 }
4871 // END utility annotation functions
4872 
4873 }
4874