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, 2021 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/QtAlgorithms>
38 #include <QtGui/QColor>
39 #include <QtGui/QTransform>
40 #include <QImage>
41 
42 // local includes
43 #include "poppler-annotation.h"
44 #include "poppler-link.h"
45 #include "poppler-qt6.h"
46 #include "poppler-annotation-helper.h"
47 #include "poppler-annotation-private.h"
48 #include "poppler-page-private.h"
49 #include "poppler-private.h"
50 
51 // poppler includes
52 #include <Page.h>
53 #include <Annot.h>
54 #include <Gfx.h>
55 #include <Error.h>
56 #include <FileSpec.h>
57 #include <Link.h>
58 #include <DateInfo.h>
59 
60 /* Almost all getters directly query the underlying poppler annotation, with
61  * the exceptions of link, file attachment, sound, movie and screen annotations,
62  * Whose data retrieval logic has not been moved yet. Their getters return
63  * static data set at creation time by findAnnotations
64  */
65 
66 namespace Poppler {
67 
68 // BEGIN AnnotationAppearancePrivate implementation
AnnotationAppearancePrivate(Annot * annot)69 AnnotationAppearancePrivate::AnnotationAppearancePrivate(Annot *annot)
70 {
71     if (annot) {
72         appearance = annot->getAppearance();
73     } else {
74         appearance.setToNull();
75     }
76 }
77 // END AnnotationAppearancePrivate implementation
78 
79 // BEGIN AnnotationAppearance implementation
AnnotationAppearance(AnnotationAppearancePrivate * annotationAppearancePrivate)80 AnnotationAppearance::AnnotationAppearance(AnnotationAppearancePrivate *annotationAppearancePrivate) : d(annotationAppearancePrivate) { }
81 
~AnnotationAppearance()82 AnnotationAppearance::~AnnotationAppearance()
83 {
84     delete d;
85 }
86 // END AnnotationAppearance implementation
87 
88 // BEGIN Annotation implementation
AnnotationPrivate()89 AnnotationPrivate::AnnotationPrivate() : revisionScope(Annotation::Root), revisionType(Annotation::None), pdfAnnot(nullptr), pdfPage(nullptr), parentDoc(nullptr) { }
90 
getRawDataFromQImage(const QImage & qimg,int bitsPerPixel,QByteArray * data,QByteArray * sMaskData)91 void getRawDataFromQImage(const QImage &qimg, int bitsPerPixel, QByteArray *data, QByteArray *sMaskData)
92 {
93     const int height = qimg.height();
94     const int width = qimg.width();
95 
96     switch (bitsPerPixel) {
97     case 1:
98         for (int line = 0; line < height; line++) {
99             const char *lineData = reinterpret_cast<const char *>(qimg.scanLine(line));
100             for (int offset = 0; offset < (width + 7) / 8; offset++) {
101                 data->append(lineData[offset]);
102             }
103         }
104         break;
105     case 8:
106     case 24:
107         data->append((const char *)qimg.bits(), static_cast<int>(qimg.sizeInBytes()));
108         break;
109     case 32:
110         for (int line = 0; line < height; line++) {
111             const QRgb *lineData = reinterpret_cast<const QRgb *>(qimg.scanLine(line));
112             for (int offset = 0; offset < width; offset++) {
113                 char a = (char)qAlpha(lineData[offset]);
114                 char r = (char)qRed(lineData[offset]);
115                 char g = (char)qGreen(lineData[offset]);
116                 char b = (char)qBlue(lineData[offset]);
117 
118                 data->append(r);
119                 data->append(g);
120                 data->append(b);
121 
122                 sMaskData->append(a);
123             }
124         }
125         break;
126     }
127 }
128 
addRevision(Annotation * ann,Annotation::RevScope scope,Annotation::RevType type)129 void AnnotationPrivate::addRevision(Annotation *ann, Annotation::RevScope scope, Annotation::RevType type)
130 {
131     /* Since ownership stays with the caller, create an alias of ann */
132     revisions.append(ann->d_ptr->makeAlias());
133 
134     /* Set revision properties */
135     revisionScope = scope;
136     revisionType = type;
137 }
138 
~AnnotationPrivate()139 AnnotationPrivate::~AnnotationPrivate()
140 {
141     // Delete all children revisions
142     qDeleteAll(revisions);
143 
144     // Release Annot object
145     if (pdfAnnot)
146         pdfAnnot->decRefCnt();
147 }
148 
tieToNativeAnnot(Annot * ann,::Page * page,Poppler::DocumentData * doc)149 void AnnotationPrivate::tieToNativeAnnot(Annot *ann, ::Page *page, Poppler::DocumentData *doc)
150 {
151     if (pdfAnnot) {
152         error(errIO, -1, "Annotation is already tied");
153         return;
154     }
155 
156     pdfAnnot = ann;
157     pdfPage = page;
158     parentDoc = doc;
159 
160     pdfAnnot->incRefCnt();
161 }
162 
163 /* This method is called when a new annotation is created, after pdfAnnot and
164  * pdfPage have been set */
flushBaseAnnotationProperties()165 void AnnotationPrivate::flushBaseAnnotationProperties()
166 {
167     Q_ASSERT(pdfPage);
168 
169     Annotation *q = makeAlias(); // Setters are defined in the public class
170 
171     // Since pdfAnnot has been set, this calls will write in the Annot object
172     q->setAuthor(author);
173     q->setContents(contents);
174     q->setUniqueName(uniqueName);
175     q->setModificationDate(modDate);
176     q->setCreationDate(creationDate);
177     q->setFlags(flags);
178     // q->setBoundary(boundary); -- already set by subclass-specific code
179     q->setStyle(style);
180     q->setPopup(popup);
181 
182     // Flush revisions
183     foreach (Annotation *r, revisions) {
184         // TODO: Flush revision
185         delete r; // Object is no longer needed
186     }
187 
188     delete q;
189 
190     // Clear some members to save memory
191     author.clear();
192     contents.clear();
193     uniqueName.clear();
194     revisions.clear();
195 }
196 
197 // Returns matrix to convert from user space coords (oriented according to the
198 // specified rotation) to normalized coords
fillNormalizationMTX(::Page * pdfPage,double MTX[6],int pageRotation)199 static void fillNormalizationMTX(::Page *pdfPage, double MTX[6], int pageRotation)
200 {
201     Q_ASSERT(pdfPage);
202 
203     // build a normalized transform matrix for this page at 100% scale
204     GfxState *gfxState = new GfxState(72.0, 72.0, pdfPage->getCropBox(), pageRotation, true);
205     const double *gfxCTM = gfxState->getCTM();
206 
207     double w = pdfPage->getCropWidth();
208     double h = pdfPage->getCropHeight();
209 
210     // Swap width and height if the page is rotated landscape or seascape
211     if (pageRotation == 90 || pageRotation == 270) {
212         double t = w;
213         w = h;
214         h = t;
215     }
216 
217     for (int i = 0; i < 6; i += 2) {
218         MTX[i] = gfxCTM[i] / w;
219         MTX[i + 1] = gfxCTM[i + 1] / h;
220     }
221     delete gfxState;
222 }
223 
224 // Returns matrix to convert from user space coords (i.e. those that are stored
225 // in the PDF file) to normalized coords (i.e. those that we expose to clients).
226 // This method also applies a rotation around the top-left corner if the
227 // FixedRotation flag is set.
fillTransformationMTX(double MTX[6]) const228 void AnnotationPrivate::fillTransformationMTX(double MTX[6]) const
229 {
230     Q_ASSERT(pdfPage);
231     Q_ASSERT(pdfAnnot);
232 
233     const int pageRotate = pdfPage->getRotate();
234 
235     if (pageRotate == 0 || (pdfAnnot->getFlags() & Annot::flagNoRotate) == 0) {
236         // Use the normalization matrix for this page's rotation
237         fillNormalizationMTX(pdfPage, MTX, pageRotate);
238     } else {
239         // Clients expect coordinates relative to this page's rotation, but
240         // FixedRotation annotations internally use unrotated coordinates:
241         // construct matrix to both normalize and rotate coordinates using the
242         // top-left corner as rotation pivot
243 
244         double MTXnorm[6];
245         fillNormalizationMTX(pdfPage, MTXnorm, pageRotate);
246 
247         QTransform transform(MTXnorm[0], MTXnorm[1], MTXnorm[2], MTXnorm[3], MTXnorm[4], MTXnorm[5]);
248         transform.translate(+pdfAnnot->getXMin(), +pdfAnnot->getYMax());
249         transform.rotate(pageRotate);
250         transform.translate(-pdfAnnot->getXMin(), -pdfAnnot->getYMax());
251 
252         MTX[0] = transform.m11();
253         MTX[1] = transform.m12();
254         MTX[2] = transform.m21();
255         MTX[3] = transform.m22();
256         MTX[4] = transform.dx();
257         MTX[5] = transform.dy();
258     }
259 }
260 
fromPdfRectangle(const PDFRectangle & r) const261 QRectF AnnotationPrivate::fromPdfRectangle(const PDFRectangle &r) const
262 {
263     double swp, MTX[6];
264     fillTransformationMTX(MTX);
265 
266     QPointF p1, p2;
267     XPDFReader::transform(MTX, r.x1, r.y1, p1);
268     XPDFReader::transform(MTX, r.x2, r.y2, p2);
269 
270     double tl_x = p1.x();
271     double tl_y = p1.y();
272     double br_x = p2.x();
273     double br_y = p2.y();
274 
275     if (tl_x > br_x) {
276         swp = tl_x;
277         tl_x = br_x;
278         br_x = swp;
279     }
280 
281     if (tl_y > br_y) {
282         swp = tl_y;
283         tl_y = br_y;
284         br_y = swp;
285     }
286 
287     return QRectF(QPointF(tl_x, tl_y), QPointF(br_x, br_y));
288 }
289 
290 // This function converts a boundary QRectF in normalized coords to a
291 // PDFRectangle in user coords. If the FixedRotation flag is set, this function
292 // also applies a rotation around the top-left corner: it's the inverse of
293 // the transformation produced by fillTransformationMTX, but we can't use
294 // fillTransformationMTX here because it relies on the native annotation
295 // object's boundary rect to be already set up.
boundaryToPdfRectangle(::Page * pdfPage,const QRectF & r,int rFlags)296 PDFRectangle boundaryToPdfRectangle(::Page *pdfPage, const QRectF &r, int rFlags)
297 {
298     Q_ASSERT(pdfPage);
299 
300     const int pageRotate = pdfPage->getRotate();
301 
302     double MTX[6];
303     fillNormalizationMTX(pdfPage, MTX, pageRotate);
304 
305     double tl_x, tl_y, br_x, br_y, swp;
306     XPDFReader::invTransform(MTX, r.topLeft(), tl_x, tl_y);
307     XPDFReader::invTransform(MTX, r.bottomRight(), br_x, br_y);
308 
309     if (tl_x > br_x) {
310         swp = tl_x;
311         tl_x = br_x;
312         br_x = swp;
313     }
314 
315     if (tl_y > br_y) {
316         swp = tl_y;
317         tl_y = br_y;
318         br_y = swp;
319     }
320 
321     const int rotationFixUp = (rFlags & Annotation::FixedRotation) ? pageRotate : 0;
322     const double width = br_x - tl_x;
323     const double height = br_y - tl_y;
324 
325     if (rotationFixUp == 0)
326         return PDFRectangle(tl_x, tl_y, br_x, br_y);
327     else if (rotationFixUp == 90)
328         return PDFRectangle(tl_x, tl_y - width, tl_x + height, tl_y);
329     else if (rotationFixUp == 180)
330         return PDFRectangle(br_x, tl_y - height, br_x + width, tl_y);
331     else // rotationFixUp == 270
332         return PDFRectangle(br_x, br_y - width, br_x + height, br_y);
333 }
334 
boundaryToPdfRectangle(const QRectF & r,int rFlags) const335 PDFRectangle AnnotationPrivate::boundaryToPdfRectangle(const QRectF &r, int rFlags) const
336 {
337     return Poppler::boundaryToPdfRectangle(pdfPage, r, rFlags);
338 }
339 
toAnnotPath(const QVector<QPointF> & list) const340 AnnotPath *AnnotationPrivate::toAnnotPath(const QVector<QPointF> &list) const
341 {
342     const int count = list.size();
343     std::vector<AnnotCoord> ac;
344     ac.reserve(count);
345 
346     double MTX[6];
347     fillTransformationMTX(MTX);
348 
349     foreach (const QPointF &p, list) {
350         double x, y;
351         XPDFReader::invTransform(MTX, p, x, y);
352         ac.emplace_back(x, y);
353     }
354 
355     return new AnnotPath(std::move(ac));
356 }
357 
findAnnotations(::Page * pdfPage,DocumentData * doc,const QSet<Annotation::SubType> & subtypes,int parentID)358 std::vector<std::unique_ptr<Annotation>> AnnotationPrivate::findAnnotations(::Page *pdfPage, DocumentData *doc, const QSet<Annotation::SubType> &subtypes, int parentID)
359 {
360     Annots *annots = pdfPage->getAnnots();
361     const uint numAnnotations = annots->getNumAnnots();
362     if (numAnnotations == 0) {
363         return std::vector<std::unique_ptr<Annotation>>();
364     }
365 
366     const bool wantTextAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AText);
367     const bool wantLineAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::ALine);
368     const bool wantGeomAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AGeom);
369     const bool wantHighlightAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AHighlight);
370     const bool wantStampAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AStamp);
371     const bool wantInkAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AInk);
372     const bool wantLinkAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::ALink);
373     const bool wantCaretAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::ACaret);
374     const bool wantFileAttachmentAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AFileAttachment);
375     const bool wantSoundAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::ASound);
376     const bool wantMovieAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AMovie);
377     const bool wantScreenAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AScreen);
378     const bool wantWidgetAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AWidget);
379 
380     // Create Annotation objects and tie to their native Annot
381     std::vector<std::unique_ptr<Annotation>> res;
382     for (uint k = 0; k < numAnnotations; k++) {
383         // get the j-th annotation
384         Annot *ann = annots->getAnnot(k);
385         if (!ann) {
386             error(errInternal, -1, "Annot {0:ud} is null", k);
387             continue;
388         }
389 
390         // Check parent annotation
391         AnnotMarkup *markupann = dynamic_cast<AnnotMarkup *>(ann);
392         if (!markupann) {
393             // Assume it's a root annotation, and skip if user didn't request it
394             if (parentID != -1)
395                 continue;
396         } else if (markupann->getInReplyToID() != parentID)
397             continue;
398 
399         /* Create Annotation of the right subclass */
400         std::unique_ptr<Annotation> annotation;
401         Annot::AnnotSubtype subType = ann->getType();
402 
403         switch (subType) {
404         case Annot::typeText:
405             if (!wantTextAnnotations)
406                 continue;
407             annotation = std::make_unique<TextAnnotation>(TextAnnotation::Linked);
408             break;
409         case Annot::typeFreeText:
410             if (!wantTextAnnotations)
411                 continue;
412             annotation = std::make_unique<TextAnnotation>(TextAnnotation::InPlace);
413             break;
414         case Annot::typeLine:
415             if (!wantLineAnnotations)
416                 continue;
417             annotation = std::make_unique<LineAnnotation>(LineAnnotation::StraightLine);
418             break;
419         case Annot::typePolygon:
420         case Annot::typePolyLine:
421             if (!wantLineAnnotations)
422                 continue;
423             annotation = std::make_unique<LineAnnotation>(LineAnnotation::Polyline);
424             break;
425         case Annot::typeSquare:
426         case Annot::typeCircle:
427             if (!wantGeomAnnotations)
428                 continue;
429             annotation = std::make_unique<GeomAnnotation>();
430             break;
431         case Annot::typeHighlight:
432         case Annot::typeUnderline:
433         case Annot::typeSquiggly:
434         case Annot::typeStrikeOut:
435             if (!wantHighlightAnnotations)
436                 continue;
437             annotation = std::make_unique<HighlightAnnotation>();
438             break;
439         case Annot::typeStamp:
440             if (!wantStampAnnotations)
441                 continue;
442             annotation = std::make_unique<StampAnnotation>();
443             break;
444         case Annot::typeInk:
445             if (!wantInkAnnotations)
446                 continue;
447             annotation = std::make_unique<InkAnnotation>();
448             break;
449         case Annot::typeLink: /* TODO: Move logic to getters */
450         {
451             if (!wantLinkAnnotations)
452                 continue;
453             // parse Link params
454             AnnotLink *linkann = static_cast<AnnotLink *>(ann);
455             LinkAnnotation *l = new LinkAnnotation();
456 
457             // -> hlMode
458             l->setLinkHighlightMode((LinkAnnotation::HighlightMode)linkann->getLinkEffect());
459 
460             // -> link region
461             // TODO
462 
463             // reading link action
464             if (linkann->getAction()) {
465                 std::unique_ptr<Link> popplerLink = PageData::convertLinkActionToLink(linkann->getAction(), doc, QRectF());
466                 if (popplerLink) {
467                     l->setLinkDestination(std::move(popplerLink));
468                 }
469             }
470             annotation.reset(l);
471             break;
472         }
473         case Annot::typeCaret:
474             if (!wantCaretAnnotations)
475                 continue;
476             annotation = std::make_unique<CaretAnnotation>();
477             break;
478         case Annot::typeFileAttachment: /* TODO: Move logic to getters */
479         {
480             if (!wantFileAttachmentAnnotations)
481                 continue;
482             AnnotFileAttachment *attachann = static_cast<AnnotFileAttachment *>(ann);
483             FileAttachmentAnnotation *f = new FileAttachmentAnnotation();
484             // -> fileIcon
485             f->setFileIconName(QString::fromLatin1(attachann->getName()->c_str()));
486             // -> embeddedFile
487             FileSpec *filespec = new FileSpec(attachann->getFile());
488             f->setEmbeddedFile(new EmbeddedFile(*new EmbeddedFileData(filespec)));
489             annotation.reset(f);
490             break;
491         }
492         case Annot::typeSound: /* TODO: Move logic to getters */
493         {
494             if (!wantSoundAnnotations)
495                 continue;
496             AnnotSound *soundann = static_cast<AnnotSound *>(ann);
497             SoundAnnotation *s = new SoundAnnotation();
498 
499             // -> soundIcon
500             s->setSoundIconName(QString::fromLatin1(soundann->getName()->c_str()));
501             // -> sound
502             s->setSound(new SoundObject(soundann->getSound()));
503             annotation.reset(s);
504             break;
505         }
506         case Annot::typeMovie: /* TODO: Move logic to getters */
507         {
508             if (!wantMovieAnnotations)
509                 continue;
510             AnnotMovie *movieann = static_cast<AnnotMovie *>(ann);
511             MovieAnnotation *m = new MovieAnnotation();
512 
513             // -> movie
514             MovieObject *movie = new MovieObject(movieann);
515             m->setMovie(movie);
516             // -> movieTitle
517             const GooString *movietitle = movieann->getTitle();
518             if (movietitle)
519                 m->setMovieTitle(QString::fromLatin1(movietitle->c_str()));
520             annotation.reset(m);
521             break;
522         }
523         case Annot::typeScreen: {
524             if (!wantScreenAnnotations)
525                 continue;
526             AnnotScreen *screenann = static_cast<AnnotScreen *>(ann);
527             // TODO Support other link types than Link::Rendition in ScreenAnnotation
528             if (!screenann->getAction() || screenann->getAction()->getKind() != actionRendition)
529                 continue;
530             ScreenAnnotation *s = new ScreenAnnotation();
531 
532             // -> screen
533             std::unique_ptr<Link> popplerLink = PageData::convertLinkActionToLink(screenann->getAction(), doc, QRectF());
534             s->setAction(static_cast<Poppler::LinkRendition *>(popplerLink.release()));
535 
536             // -> screenTitle
537             const GooString *screentitle = screenann->getTitle();
538             if (screentitle)
539                 s->setScreenTitle(UnicodeParsedString(screentitle));
540             annotation.reset(s);
541             break;
542         }
543         case Annot::typePopup:
544             continue; // popups are parsed by Annotation's window() getter
545         case Annot::typeUnknown:
546             continue; // special case for ignoring unknown annotations
547         case Annot::typeWidget:
548             if (!wantWidgetAnnotations)
549                 continue;
550             annotation.reset(new WidgetAnnotation());
551             break;
552         case Annot::typeRichMedia: {
553             const AnnotRichMedia *annotRichMedia = static_cast<AnnotRichMedia *>(ann);
554 
555             RichMediaAnnotation *richMediaAnnotation = new RichMediaAnnotation;
556 
557             const AnnotRichMedia::Settings *annotSettings = annotRichMedia->getSettings();
558             if (annotSettings) {
559                 RichMediaAnnotation::Settings *settings = new RichMediaAnnotation::Settings;
560 
561                 if (annotSettings->getActivation()) {
562                     RichMediaAnnotation::Activation *activation = new RichMediaAnnotation::Activation;
563 
564                     switch (annotSettings->getActivation()->getCondition()) {
565                     case AnnotRichMedia::Activation::conditionPageOpened:
566                         activation->setCondition(RichMediaAnnotation::Activation::PageOpened);
567                         break;
568                     case AnnotRichMedia::Activation::conditionPageVisible:
569                         activation->setCondition(RichMediaAnnotation::Activation::PageVisible);
570                         break;
571                     case AnnotRichMedia::Activation::conditionUserAction:
572                         activation->setCondition(RichMediaAnnotation::Activation::UserAction);
573                         break;
574                     }
575 
576                     settings->setActivation(activation);
577                 }
578 
579                 if (annotSettings->getDeactivation()) {
580                     RichMediaAnnotation::Deactivation *deactivation = new RichMediaAnnotation::Deactivation;
581 
582                     switch (annotSettings->getDeactivation()->getCondition()) {
583                     case AnnotRichMedia::Deactivation::conditionPageClosed:
584                         deactivation->setCondition(RichMediaAnnotation::Deactivation::PageClosed);
585                         break;
586                     case AnnotRichMedia::Deactivation::conditionPageInvisible:
587                         deactivation->setCondition(RichMediaAnnotation::Deactivation::PageInvisible);
588                         break;
589                     case AnnotRichMedia::Deactivation::conditionUserAction:
590                         deactivation->setCondition(RichMediaAnnotation::Deactivation::UserAction);
591                         break;
592                     }
593 
594                     settings->setDeactivation(deactivation);
595                 }
596 
597                 richMediaAnnotation->setSettings(settings);
598             }
599 
600             const AnnotRichMedia::Content *annotContent = annotRichMedia->getContent();
601             if (annotContent) {
602                 RichMediaAnnotation::Content *content = new RichMediaAnnotation::Content;
603 
604                 const int configurationsCount = annotContent->getConfigurationsCount();
605                 if (configurationsCount > 0) {
606                     QList<RichMediaAnnotation::Configuration *> configurations;
607 
608                     for (int i = 0; i < configurationsCount; ++i) {
609                         const AnnotRichMedia::Configuration *annotConfiguration = annotContent->getConfiguration(i);
610                         if (!annotConfiguration)
611                             continue;
612 
613                         RichMediaAnnotation::Configuration *configuration = new RichMediaAnnotation::Configuration;
614 
615                         if (annotConfiguration->getName())
616                             configuration->setName(UnicodeParsedString(annotConfiguration->getName()));
617 
618                         switch (annotConfiguration->getType()) {
619                         case AnnotRichMedia::Configuration::type3D:
620                             configuration->setType(RichMediaAnnotation::Configuration::Type3D);
621                             break;
622                         case AnnotRichMedia::Configuration::typeFlash:
623                             configuration->setType(RichMediaAnnotation::Configuration::TypeFlash);
624                             break;
625                         case AnnotRichMedia::Configuration::typeSound:
626                             configuration->setType(RichMediaAnnotation::Configuration::TypeSound);
627                             break;
628                         case AnnotRichMedia::Configuration::typeVideo:
629                             configuration->setType(RichMediaAnnotation::Configuration::TypeVideo);
630                             break;
631                         }
632 
633                         const int instancesCount = annotConfiguration->getInstancesCount();
634                         if (instancesCount > 0) {
635                             QList<RichMediaAnnotation::Instance *> instances;
636 
637                             for (int j = 0; j < instancesCount; ++j) {
638                                 const AnnotRichMedia::Instance *annotInstance = annotConfiguration->getInstance(j);
639                                 if (!annotInstance)
640                                     continue;
641 
642                                 RichMediaAnnotation::Instance *instance = new RichMediaAnnotation::Instance;
643 
644                                 switch (annotInstance->getType()) {
645                                 case AnnotRichMedia::Instance::type3D:
646                                     instance->setType(RichMediaAnnotation::Instance::Type3D);
647                                     break;
648                                 case AnnotRichMedia::Instance::typeFlash:
649                                     instance->setType(RichMediaAnnotation::Instance::TypeFlash);
650                                     break;
651                                 case AnnotRichMedia::Instance::typeSound:
652                                     instance->setType(RichMediaAnnotation::Instance::TypeSound);
653                                     break;
654                                 case AnnotRichMedia::Instance::typeVideo:
655                                     instance->setType(RichMediaAnnotation::Instance::TypeVideo);
656                                     break;
657                                 }
658 
659                                 const AnnotRichMedia::Params *annotParams = annotInstance->getParams();
660                                 if (annotParams) {
661                                     RichMediaAnnotation::Params *params = new RichMediaAnnotation::Params;
662 
663                                     if (annotParams->getFlashVars())
664                                         params->setFlashVars(UnicodeParsedString(annotParams->getFlashVars()));
665 
666                                     instance->setParams(params);
667                                 }
668 
669                                 instances.append(instance);
670                             }
671 
672                             configuration->setInstances(instances);
673                         }
674 
675                         configurations.append(configuration);
676                     }
677 
678                     content->setConfigurations(configurations);
679                 }
680 
681                 const int assetsCount = annotContent->getAssetsCount();
682                 if (assetsCount > 0) {
683                     QList<RichMediaAnnotation::Asset *> assets;
684 
685                     for (int i = 0; i < assetsCount; ++i) {
686                         const AnnotRichMedia::Asset *annotAsset = annotContent->getAsset(i);
687                         if (!annotAsset)
688                             continue;
689 
690                         RichMediaAnnotation::Asset *asset = new RichMediaAnnotation::Asset;
691 
692                         if (annotAsset->getName())
693                             asset->setName(UnicodeParsedString(annotAsset->getName()));
694 
695                         FileSpec *fileSpec = new FileSpec(annotAsset->getFileSpec());
696                         asset->setEmbeddedFile(new EmbeddedFile(*new EmbeddedFileData(fileSpec)));
697 
698                         assets.append(asset);
699                     }
700 
701                     content->setAssets(assets);
702                 }
703 
704                 richMediaAnnotation->setContent(content);
705             }
706 
707             annotation.reset(richMediaAnnotation);
708 
709             break;
710         }
711         default: {
712 #define CASE_FOR_TYPE(thetype)                                                                                                                                                                                                                 \
713     case Annot::type##thetype:                                                                                                                                                                                                                 \
714         error(errUnimplemented, -1, "Annotation " #thetype " not supported");                                                                                                                                                                  \
715         break;
716             switch (subType) {
717                 CASE_FOR_TYPE(PrinterMark)
718                 CASE_FOR_TYPE(TrapNet)
719                 CASE_FOR_TYPE(Watermark)
720                 CASE_FOR_TYPE(3D)
721             default:
722                 error(errUnimplemented, -1, "Annotation {0:d} not supported", subType);
723             }
724             continue;
725 #undef CASE_FOR_TYPE
726         }
727         }
728 
729         annotation->d_ptr->tieToNativeAnnot(ann, pdfPage, doc);
730         res.push_back(std::move(annotation));
731     }
732 
733     return res;
734 }
735 
pdfObjectReference() const736 Ref AnnotationPrivate::pdfObjectReference() const
737 {
738     if (pdfAnnot == nullptr) {
739         return Ref::INVALID();
740     }
741 
742     return pdfAnnot->getRef();
743 }
744 
additionalAction(Annotation::AdditionalActionType type) const745 std::unique_ptr<Link> AnnotationPrivate::additionalAction(Annotation::AdditionalActionType type) const
746 {
747     if (pdfAnnot->getType() != Annot::typeScreen && pdfAnnot->getType() != Annot::typeWidget)
748         return {};
749 
750     const Annot::AdditionalActionsType actionType = toPopplerAdditionalActionType(type);
751 
752     std::unique_ptr<::LinkAction> linkAction;
753     if (pdfAnnot->getType() == Annot::typeScreen)
754         linkAction = static_cast<AnnotScreen *>(pdfAnnot)->getAdditionalAction(actionType);
755     else
756         linkAction = static_cast<AnnotWidget *>(pdfAnnot)->getAdditionalAction(actionType);
757 
758     if (linkAction)
759         return PageData::convertLinkActionToLink(linkAction.get(), parentDoc, QRectF());
760 
761     return {};
762 }
763 
addAnnotationToPage(::Page * pdfPage,DocumentData * doc,const Annotation * ann)764 void AnnotationPrivate::addAnnotationToPage(::Page *pdfPage, DocumentData *doc, const Annotation *ann)
765 {
766     if (ann->d_ptr->pdfAnnot != nullptr) {
767         error(errIO, -1, "Annotation is already tied");
768         return;
769     }
770 
771     // Unimplemented annotations can't be created by the user because their ctor
772     // is private. Therefore, createNativeAnnot will never return 0
773     Annot *nativeAnnot = ann->d_ptr->createNativeAnnot(pdfPage, doc);
774     Q_ASSERT(nativeAnnot);
775 
776     if (ann->d_ptr->annotationAppearance.isStream())
777         nativeAnnot->setNewAppearance(ann->d_ptr->annotationAppearance.copy());
778 
779     pdfPage->addAnnot(nativeAnnot);
780 }
781 
removeAnnotationFromPage(::Page * pdfPage,const Annotation * ann)782 void AnnotationPrivate::removeAnnotationFromPage(::Page *pdfPage, const Annotation *ann)
783 {
784     if (ann->d_ptr->pdfAnnot == nullptr) {
785         error(errIO, -1, "Annotation is not tied");
786         return;
787     }
788 
789     if (ann->d_ptr->pdfPage != pdfPage) {
790         error(errIO, -1, "Annotation doesn't belong to the specified page");
791         return;
792     }
793 
794     // Remove annotation
795     pdfPage->removeAnnot(ann->d_ptr->pdfAnnot);
796 
797     // Destroy object
798     delete ann;
799 }
800 
801 class Annotation::Style::Private : public QSharedData
802 {
803 public:
Private()804     Private() : opacity(1.0), width(1.0), lineStyle(Solid), xCorners(0.0), yCorners(0.0), lineEffect(NoEffect), effectIntensity(1.0)
805     {
806         dashArray.resize(1);
807         dashArray[0] = 3;
808     }
809 
810     QColor color;
811     double opacity;
812     double width;
813     Annotation::LineStyle lineStyle;
814     double xCorners;
815     double yCorners;
816     QVector<double> dashArray;
817     Annotation::LineEffect lineEffect;
818     double effectIntensity;
819 };
820 
Style()821 Annotation::Style::Style() : d(new Private) { }
822 
Style(const Style & other)823 Annotation::Style::Style(const Style &other) : d(other.d) { }
824 
operator =(const Style & other)825 Annotation::Style &Annotation::Style::operator=(const Style &other)
826 {
827     if (this != &other)
828         d = other.d;
829 
830     return *this;
831 }
832 
~Style()833 Annotation::Style::~Style() { }
834 
color() const835 QColor Annotation::Style::color() const
836 {
837     return d->color;
838 }
839 
setColor(const QColor & color)840 void Annotation::Style::setColor(const QColor &color)
841 {
842     d->color = color;
843 }
844 
opacity() const845 double Annotation::Style::opacity() const
846 {
847     return d->opacity;
848 }
849 
setOpacity(double opacity)850 void Annotation::Style::setOpacity(double opacity)
851 {
852     d->opacity = opacity;
853 }
854 
width() const855 double Annotation::Style::width() const
856 {
857     return d->width;
858 }
859 
setWidth(double width)860 void Annotation::Style::setWidth(double width)
861 {
862     d->width = width;
863 }
864 
lineStyle() const865 Annotation::LineStyle Annotation::Style::lineStyle() const
866 {
867     return d->lineStyle;
868 }
869 
setLineStyle(Annotation::LineStyle style)870 void Annotation::Style::setLineStyle(Annotation::LineStyle style)
871 {
872     d->lineStyle = style;
873 }
874 
xCorners() const875 double Annotation::Style::xCorners() const
876 {
877     return d->xCorners;
878 }
879 
setXCorners(double radius)880 void Annotation::Style::setXCorners(double radius)
881 {
882     d->xCorners = radius;
883 }
884 
yCorners() const885 double Annotation::Style::yCorners() const
886 {
887     return d->yCorners;
888 }
889 
setYCorners(double radius)890 void Annotation::Style::setYCorners(double radius)
891 {
892     d->yCorners = radius;
893 }
894 
dashArray() const895 const QVector<double> &Annotation::Style::dashArray() const
896 {
897     return d->dashArray;
898 }
899 
setDashArray(const QVector<double> & array)900 void Annotation::Style::setDashArray(const QVector<double> &array)
901 {
902     d->dashArray = array;
903 }
904 
lineEffect() const905 Annotation::LineEffect Annotation::Style::lineEffect() const
906 {
907     return d->lineEffect;
908 }
909 
setLineEffect(Annotation::LineEffect effect)910 void Annotation::Style::setLineEffect(Annotation::LineEffect effect)
911 {
912     d->lineEffect = effect;
913 }
914 
effectIntensity() const915 double Annotation::Style::effectIntensity() const
916 {
917     return d->effectIntensity;
918 }
919 
setEffectIntensity(double intens)920 void Annotation::Style::setEffectIntensity(double intens)
921 {
922     d->effectIntensity = intens;
923 }
924 
925 class Annotation::Popup::Private : public QSharedData
926 {
927 public:
Private()928     Private() : flags(-1) { }
929 
930     int flags;
931     QRectF geometry;
932     QString title;
933     QString summary;
934     QString text;
935 };
936 
Popup()937 Annotation::Popup::Popup() : d(new Private) { }
938 
Popup(const Popup & other)939 Annotation::Popup::Popup(const Popup &other) : d(other.d) { }
940 
operator =(const Popup & other)941 Annotation::Popup &Annotation::Popup::operator=(const Popup &other)
942 {
943     if (this != &other)
944         d = other.d;
945 
946     return *this;
947 }
948 
~Popup()949 Annotation::Popup::~Popup() { }
950 
flags() const951 int Annotation::Popup::flags() const
952 {
953     return d->flags;
954 }
955 
setFlags(int flags)956 void Annotation::Popup::setFlags(int flags)
957 {
958     d->flags = flags;
959 }
960 
geometry() const961 QRectF Annotation::Popup::geometry() const
962 {
963     return d->geometry;
964 }
965 
setGeometry(const QRectF & geom)966 void Annotation::Popup::setGeometry(const QRectF &geom)
967 {
968     d->geometry = geom;
969 }
970 
title() const971 QString Annotation::Popup::title() const
972 {
973     return d->title;
974 }
975 
setTitle(const QString & title)976 void Annotation::Popup::setTitle(const QString &title)
977 {
978     d->title = title;
979 }
980 
summary() const981 QString Annotation::Popup::summary() const
982 {
983     return d->summary;
984 }
985 
setSummary(const QString & summary)986 void Annotation::Popup::setSummary(const QString &summary)
987 {
988     d->summary = summary;
989 }
990 
text() const991 QString Annotation::Popup::text() const
992 {
993     return d->text;
994 }
995 
setText(const QString & text)996 void Annotation::Popup::setText(const QString &text)
997 {
998     d->text = text;
999 }
1000 
Annotation(AnnotationPrivate & dd)1001 Annotation::Annotation(AnnotationPrivate &dd) : d_ptr(&dd) { }
1002 
~Annotation()1003 Annotation::~Annotation() { }
1004 
author() const1005 QString Annotation::author() const
1006 {
1007     Q_D(const Annotation);
1008 
1009     if (!d->pdfAnnot)
1010         return d->author;
1011 
1012     const AnnotMarkup *markupann = dynamic_cast<const AnnotMarkup *>(d->pdfAnnot);
1013     return markupann ? UnicodeParsedString(markupann->getLabel()) : QString();
1014 }
1015 
setAuthor(const QString & author)1016 void Annotation::setAuthor(const QString &author)
1017 {
1018     Q_D(Annotation);
1019 
1020     if (!d->pdfAnnot) {
1021         d->author = author;
1022         return;
1023     }
1024 
1025     AnnotMarkup *markupann = dynamic_cast<AnnotMarkup *>(d->pdfAnnot);
1026     if (markupann) {
1027         GooString *s = QStringToUnicodeGooString(author);
1028         markupann->setLabel(s);
1029         delete s;
1030     }
1031 }
1032 
contents() const1033 QString Annotation::contents() const
1034 {
1035     Q_D(const Annotation);
1036 
1037     if (!d->pdfAnnot)
1038         return d->contents;
1039 
1040     return UnicodeParsedString(d->pdfAnnot->getContents());
1041 }
1042 
setContents(const QString & contents)1043 void Annotation::setContents(const QString &contents)
1044 {
1045     Q_D(Annotation);
1046 
1047     if (!d->pdfAnnot) {
1048         d->contents = contents;
1049         return;
1050     }
1051 
1052     GooString *s = QStringToUnicodeGooString(contents);
1053     d->pdfAnnot->setContents(s);
1054     delete s;
1055 }
1056 
uniqueName() const1057 QString Annotation::uniqueName() const
1058 {
1059     Q_D(const Annotation);
1060 
1061     if (!d->pdfAnnot)
1062         return d->uniqueName;
1063 
1064     return UnicodeParsedString(d->pdfAnnot->getName());
1065 }
1066 
setUniqueName(const QString & uniqueName)1067 void Annotation::setUniqueName(const QString &uniqueName)
1068 {
1069     Q_D(Annotation);
1070 
1071     if (!d->pdfAnnot) {
1072         d->uniqueName = uniqueName;
1073         return;
1074     }
1075 
1076     QByteArray ascii = uniqueName.toLatin1();
1077     GooString s(ascii.constData());
1078     d->pdfAnnot->setName(&s);
1079 }
1080 
modificationDate() const1081 QDateTime Annotation::modificationDate() const
1082 {
1083     Q_D(const Annotation);
1084 
1085     if (!d->pdfAnnot)
1086         return d->modDate;
1087 
1088     if (d->pdfAnnot->getModified())
1089         return convertDate(d->pdfAnnot->getModified()->c_str());
1090     else
1091         return QDateTime();
1092 }
1093 
setModificationDate(const QDateTime & date)1094 void Annotation::setModificationDate(const QDateTime &date)
1095 {
1096     Q_D(Annotation);
1097 
1098     if (!d->pdfAnnot) {
1099         d->modDate = date;
1100         return;
1101     }
1102 
1103     if (d->pdfAnnot) {
1104         if (date.isValid()) {
1105             const time_t t = date.toSecsSinceEpoch();
1106             GooString *s = timeToDateString(&t);
1107             d->pdfAnnot->setModified(s);
1108             delete s;
1109         } else {
1110             d->pdfAnnot->setModified(nullptr);
1111         }
1112     }
1113 }
1114 
creationDate() const1115 QDateTime Annotation::creationDate() const
1116 {
1117     Q_D(const Annotation);
1118 
1119     if (!d->pdfAnnot)
1120         return d->creationDate;
1121 
1122     const AnnotMarkup *markupann = dynamic_cast<const AnnotMarkup *>(d->pdfAnnot);
1123 
1124     if (markupann && markupann->getDate())
1125         return convertDate(markupann->getDate()->c_str());
1126 
1127     return modificationDate();
1128 }
1129 
setCreationDate(const QDateTime & date)1130 void Annotation::setCreationDate(const QDateTime &date)
1131 {
1132     Q_D(Annotation);
1133 
1134     if (!d->pdfAnnot) {
1135         d->creationDate = date;
1136         return;
1137     }
1138 
1139     AnnotMarkup *markupann = dynamic_cast<AnnotMarkup *>(d->pdfAnnot);
1140     if (markupann) {
1141         if (date.isValid()) {
1142             const time_t t = date.toSecsSinceEpoch();
1143             GooString *s = timeToDateString(&t);
1144             markupann->setDate(s);
1145             delete s;
1146         } else {
1147             markupann->setDate(nullptr);
1148         }
1149     }
1150 }
1151 
fromPdfFlags(int flags)1152 static Annotation::Flags fromPdfFlags(int flags)
1153 {
1154     Annotation::Flags qtflags;
1155 
1156     if (flags & Annot::flagHidden)
1157         qtflags |= Annotation::Hidden;
1158     if (flags & Annot::flagNoZoom)
1159         qtflags |= Annotation::FixedSize;
1160     if (flags & Annot::flagNoRotate)
1161         qtflags |= Annotation::FixedRotation;
1162     if (!(flags & Annot::flagPrint))
1163         qtflags |= Annotation::DenyPrint;
1164     if (flags & Annot::flagReadOnly) {
1165         qtflags |= Annotation::DenyWrite;
1166         qtflags |= Annotation::DenyDelete;
1167     }
1168     if (flags & Annot::flagLocked)
1169         qtflags |= Annotation::DenyDelete;
1170     if (flags & Annot::flagToggleNoView)
1171         qtflags |= Annotation::ToggleHidingOnMouse;
1172 
1173     return qtflags;
1174 }
1175 
toPdfFlags(Annotation::Flags qtflags)1176 static int toPdfFlags(Annotation::Flags qtflags)
1177 {
1178     int flags = 0;
1179 
1180     if (qtflags & Annotation::Hidden)
1181         flags |= Annot::flagHidden;
1182     if (qtflags & Annotation::FixedSize)
1183         flags |= Annot::flagNoZoom;
1184     if (qtflags & Annotation::FixedRotation)
1185         flags |= Annot::flagNoRotate;
1186     if (!(qtflags & Annotation::DenyPrint))
1187         flags |= Annot::flagPrint;
1188     if (qtflags & Annotation::DenyWrite)
1189         flags |= Annot::flagReadOnly;
1190     if (qtflags & Annotation::DenyDelete)
1191         flags |= Annot::flagLocked;
1192     if (qtflags & Annotation::ToggleHidingOnMouse)
1193         flags |= Annot::flagToggleNoView;
1194 
1195     return flags;
1196 }
1197 
flags() const1198 Annotation::Flags Annotation::flags() const
1199 {
1200     Q_D(const Annotation);
1201 
1202     if (!d->pdfAnnot)
1203         return d->flags;
1204 
1205     return fromPdfFlags(d->pdfAnnot->getFlags());
1206 }
1207 
setFlags(Annotation::Flags flags)1208 void Annotation::setFlags(Annotation::Flags flags)
1209 {
1210     Q_D(Annotation);
1211 
1212     if (!d->pdfAnnot) {
1213         d->flags = flags;
1214         return;
1215     }
1216 
1217     d->pdfAnnot->setFlags(toPdfFlags(flags));
1218 }
1219 
boundary() const1220 QRectF Annotation::boundary() const
1221 {
1222     Q_D(const Annotation);
1223 
1224     if (!d->pdfAnnot)
1225         return d->boundary;
1226 
1227     const PDFRectangle *rect = d->pdfAnnot->getRect();
1228     return d->fromPdfRectangle(*rect);
1229 }
1230 
setBoundary(const QRectF & boundary)1231 void Annotation::setBoundary(const QRectF &boundary)
1232 {
1233     Q_D(Annotation);
1234 
1235     if (!d->pdfAnnot) {
1236         d->boundary = boundary;
1237         return;
1238     }
1239 
1240     PDFRectangle rect = d->boundaryToPdfRectangle(boundary, flags());
1241     d->pdfAnnot->setRect(&rect);
1242 }
1243 
style() const1244 Annotation::Style Annotation::style() const
1245 {
1246     Q_D(const Annotation);
1247 
1248     if (!d->pdfAnnot)
1249         return d->style;
1250 
1251     Style s;
1252     s.setColor(convertAnnotColor(d->pdfAnnot->getColor()));
1253 
1254     const AnnotMarkup *markupann = dynamic_cast<const AnnotMarkup *>(d->pdfAnnot);
1255     if (markupann)
1256         s.setOpacity(markupann->getOpacity());
1257 
1258     const AnnotBorder *border = d->pdfAnnot->getBorder();
1259     if (border) {
1260         if (border->getType() == AnnotBorder::typeArray) {
1261             const AnnotBorderArray *border_array = static_cast<const AnnotBorderArray *>(border);
1262             s.setXCorners(border_array->getHorizontalCorner());
1263             s.setYCorners(border_array->getVerticalCorner());
1264         }
1265 
1266         s.setWidth(border->getWidth());
1267         s.setLineStyle((Annotation::LineStyle)(1 << border->getStyle()));
1268 
1269         const int dashArrLen = border->getDashLength();
1270         const double *dashArrData = border->getDash();
1271         QVector<double> dashArrVect(dashArrLen);
1272         for (int i = 0; i < dashArrLen; ++i)
1273             dashArrVect[i] = dashArrData[i];
1274         s.setDashArray(dashArrVect);
1275     }
1276 
1277     AnnotBorderEffect *border_effect;
1278     switch (d->pdfAnnot->getType()) {
1279     case Annot::typeFreeText:
1280         border_effect = static_cast<AnnotFreeText *>(d->pdfAnnot)->getBorderEffect();
1281         break;
1282     case Annot::typeSquare:
1283     case Annot::typeCircle:
1284         border_effect = static_cast<AnnotGeometry *>(d->pdfAnnot)->getBorderEffect();
1285         break;
1286     default:
1287         border_effect = nullptr;
1288     }
1289     if (border_effect) {
1290         s.setLineEffect((Annotation::LineEffect)border_effect->getEffectType());
1291         s.setEffectIntensity(border_effect->getIntensity());
1292     }
1293 
1294     return s;
1295 }
1296 
setStyle(const Annotation::Style & style)1297 void Annotation::setStyle(const Annotation::Style &style)
1298 {
1299     Q_D(Annotation);
1300 
1301     if (!d->pdfAnnot) {
1302         d->style = style;
1303         return;
1304     }
1305 
1306     d->pdfAnnot->setColor(convertQColor(style.color()));
1307 
1308     AnnotMarkup *markupann = dynamic_cast<AnnotMarkup *>(d->pdfAnnot);
1309     if (markupann)
1310         markupann->setOpacity(style.opacity());
1311 
1312     auto border = std::make_unique<AnnotBorderArray>();
1313     border->setWidth(style.width());
1314     border->setHorizontalCorner(style.xCorners());
1315     border->setVerticalCorner(style.yCorners());
1316     d->pdfAnnot->setBorder(std::move(border));
1317 }
1318 
popup() const1319 Annotation::Popup Annotation::popup() const
1320 {
1321     Q_D(const Annotation);
1322 
1323     if (!d->pdfAnnot)
1324         return d->popup;
1325 
1326     Popup w;
1327     AnnotPopup *popup = nullptr;
1328     int flags = -1; // Not initialized
1329 
1330     const AnnotMarkup *markupann = dynamic_cast<const AnnotMarkup *>(d->pdfAnnot);
1331     if (markupann) {
1332         popup = markupann->getPopup();
1333         w.setSummary(UnicodeParsedString(markupann->getSubject()));
1334     }
1335 
1336     if (popup) {
1337         flags = fromPdfFlags(popup->getFlags()) & (Annotation::Hidden | Annotation::FixedSize | Annotation::FixedRotation);
1338 
1339         if (!popup->getOpen())
1340             flags |= Annotation::Hidden;
1341 
1342         const PDFRectangle *rect = popup->getRect();
1343         w.setGeometry(d->fromPdfRectangle(*rect));
1344     }
1345 
1346     if (d->pdfAnnot->getType() == Annot::typeText) {
1347         const AnnotText *textann = static_cast<const AnnotText *>(d->pdfAnnot);
1348 
1349         // Text annotations default to same rect as annotation
1350         if (flags == -1) {
1351             flags = 0;
1352             w.setGeometry(boundary());
1353         }
1354 
1355         // If text is not 'opened', force window hiding. if the window
1356         // was parsed from popup, the flag should already be set
1357         if (!textann->getOpen() && flags != -1)
1358             flags |= Annotation::Hidden;
1359     }
1360 
1361     w.setFlags(flags);
1362 
1363     return w;
1364 }
1365 
setPopup(const Annotation::Popup & popup)1366 void Annotation::setPopup(const Annotation::Popup &popup)
1367 {
1368     Q_D(Annotation);
1369 
1370     if (!d->pdfAnnot) {
1371         d->popup = popup;
1372         return;
1373     }
1374 
1375 #if 0 /* TODO: Remove old popup and add AnnotPopup to page */
1376     AnnotMarkup *markupann = dynamic_cast<AnnotMarkup*>(d->pdfAnnot);
1377     if (!markupann)
1378         return;
1379 
1380     // Create a new AnnotPopup and assign it to pdfAnnot
1381     PDFRectangle rect = d->toPdfRectangle( popup.geometry() );
1382     AnnotPopup * p = new AnnotPopup( d->pdfPage->getDoc(), &rect );
1383     p->setOpen( !(popup.flags() & Annotation::Hidden) );
1384     if (!popup.summary().isEmpty())
1385     {
1386         GooString *s = QStringToUnicodeGooString(popup.summary());
1387         markupann->setLabel(s);
1388         delete s;
1389     }
1390     markupann->setPopup(p);
1391 #endif
1392 }
1393 
revisionScope() const1394 Annotation::RevScope Annotation::revisionScope() const
1395 {
1396     Q_D(const Annotation);
1397 
1398     if (!d->pdfAnnot)
1399         return d->revisionScope;
1400 
1401     const AnnotMarkup *markupann = dynamic_cast<const AnnotMarkup *>(d->pdfAnnot);
1402 
1403     if (markupann && markupann->isInReplyTo()) {
1404         switch (markupann->getReplyTo()) {
1405         case AnnotMarkup::replyTypeR:
1406             return Annotation::Reply;
1407         case AnnotMarkup::replyTypeGroup:
1408             return Annotation::Group;
1409         }
1410     }
1411 
1412     return Annotation::Root; // It's not a revision
1413 }
1414 
revisionType() const1415 Annotation::RevType Annotation::revisionType() const
1416 {
1417     Q_D(const Annotation);
1418 
1419     if (!d->pdfAnnot)
1420         return d->revisionType;
1421 
1422     const AnnotText *textann = dynamic_cast<const AnnotText *>(d->pdfAnnot);
1423 
1424     if (textann && textann->isInReplyTo()) {
1425         switch (textann->getState()) {
1426         case AnnotText::stateMarked:
1427             return Annotation::Marked;
1428         case AnnotText::stateUnmarked:
1429             return Annotation::Unmarked;
1430         case AnnotText::stateAccepted:
1431             return Annotation::Accepted;
1432         case AnnotText::stateRejected:
1433             return Annotation::Rejected;
1434         case AnnotText::stateCancelled:
1435             return Annotation::Cancelled;
1436         case AnnotText::stateCompleted:
1437             return Annotation::Completed;
1438         default:
1439             break;
1440         }
1441     }
1442 
1443     return Annotation::None;
1444 }
1445 
revisions() const1446 std::vector<std::unique_ptr<Annotation>> Annotation::revisions() const
1447 {
1448     Q_D(const Annotation);
1449 
1450     if (!d->pdfAnnot) {
1451         /* Return aliases, whose ownership goes to the caller */
1452         std::vector<std::unique_ptr<Annotation>> res;
1453         foreach (Annotation *rev, d->revisions)
1454             res.push_back(std::unique_ptr<Annotation>(rev->d_ptr->makeAlias()));
1455         return res;
1456     }
1457 
1458     /* If the annotation doesn't live in a object on its own (eg bug51361), it
1459      * has no ref, therefore it can't have revisions */
1460     if (!d->pdfAnnot->getHasRef())
1461         return std::vector<std::unique_ptr<Annotation>>();
1462 
1463     return AnnotationPrivate::findAnnotations(d->pdfPage, d->parentDoc, QSet<Annotation::SubType>(), d->pdfAnnot->getId());
1464 }
1465 
annotationAppearance() const1466 std::unique_ptr<AnnotationAppearance> Annotation::annotationAppearance() const
1467 {
1468     Q_D(const Annotation);
1469 
1470     return std::make_unique<AnnotationAppearance>(new AnnotationAppearancePrivate(d->pdfAnnot));
1471 }
1472 
setAnnotationAppearance(const AnnotationAppearance & annotationAppearance)1473 void Annotation::setAnnotationAppearance(const AnnotationAppearance &annotationAppearance)
1474 {
1475     Q_D(Annotation);
1476 
1477     if (!d->pdfAnnot) {
1478         d->annotationAppearance = annotationAppearance.d->appearance.copy();
1479         return;
1480     }
1481 
1482     // Moving the appearance object using std::move would result
1483     // in the object being completed moved from the AnnotationAppearancePrivate
1484     // class. So, we'll not be able to retrieve the stamp's original AP stream
1485     d->pdfAnnot->setNewAppearance(annotationAppearance.d->appearance.copy());
1486 }
1487 
1488 // END Annotation implementation
1489 
1490 /** TextAnnotation [Annotation] */
1491 class TextAnnotationPrivate : public AnnotationPrivate
1492 {
1493 public:
1494     TextAnnotationPrivate();
1495     Annotation *makeAlias() override;
1496     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
1497     void setDefaultAppearanceToNative();
1498     std::unique_ptr<DefaultAppearance> getDefaultAppearanceFromNative() const;
1499 
1500     // data fields
1501     TextAnnotation::TextType textType;
1502     QString textIcon;
1503     QFont textFont;
1504     QColor textColor;
1505     TextAnnotation::InplaceAlignPosition inplaceAlign;
1506     QVector<QPointF> inplaceCallout;
1507     TextAnnotation::InplaceIntent inplaceIntent;
1508 };
1509 
TextAnnotationPrivate()1510 TextAnnotationPrivate::TextAnnotationPrivate() : AnnotationPrivate(), textType(TextAnnotation::Linked), textIcon(QStringLiteral("Note")), inplaceAlign(TextAnnotation::InplaceAlignLeft), inplaceIntent(TextAnnotation::Unknown) { }
1511 
makeAlias()1512 Annotation *TextAnnotationPrivate::makeAlias()
1513 {
1514     return new TextAnnotation(*this);
1515 }
1516 
createNativeAnnot(::Page * destPage,DocumentData * doc)1517 Annot *TextAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
1518 {
1519     // Setters are defined in the public class
1520     TextAnnotation *q = static_cast<TextAnnotation *>(makeAlias());
1521 
1522     // Set page and contents
1523     pdfPage = destPage;
1524     parentDoc = doc;
1525 
1526     // Set pdfAnnot
1527     PDFRectangle rect = boundaryToPdfRectangle(boundary, flags);
1528     if (textType == TextAnnotation::Linked) {
1529         pdfAnnot = new AnnotText { destPage->getDoc(), &rect };
1530     } else {
1531         DefaultAppearance da { { objName, "Invalid_font" }, static_cast<double>(textFont.pointSize()), std::unique_ptr<AnnotColor> { convertQColor(textColor) } };
1532         pdfAnnot = new AnnotFreeText { destPage->getDoc(), &rect, da };
1533     }
1534 
1535     // Set properties
1536     flushBaseAnnotationProperties();
1537     q->setTextIcon(textIcon);
1538     q->setInplaceAlign(inplaceAlign);
1539     q->setCalloutPoints(inplaceCallout);
1540     q->setInplaceIntent(inplaceIntent);
1541 
1542     delete q;
1543 
1544     inplaceCallout.clear(); // Free up memory
1545 
1546     return pdfAnnot;
1547 }
1548 
setDefaultAppearanceToNative()1549 void TextAnnotationPrivate::setDefaultAppearanceToNative()
1550 {
1551     if (pdfAnnot && pdfAnnot->getType() == Annot::typeFreeText) {
1552         AnnotFreeText *ftextann = static_cast<AnnotFreeText *>(pdfAnnot);
1553         DefaultAppearance da { { objName, "Invalid_font" }, static_cast<double>(textFont.pointSize()), std::unique_ptr<AnnotColor> { convertQColor(textColor) } };
1554         ftextann->setDefaultAppearance(da);
1555     }
1556 }
1557 
getDefaultAppearanceFromNative() const1558 std::unique_ptr<DefaultAppearance> TextAnnotationPrivate::getDefaultAppearanceFromNative() const
1559 {
1560     if (pdfAnnot && pdfAnnot->getType() == Annot::typeFreeText) {
1561         AnnotFreeText *ftextann = static_cast<AnnotFreeText *>(pdfAnnot);
1562         return ftextann->getDefaultAppearance();
1563     } else {
1564         return {};
1565     }
1566 }
1567 
TextAnnotation(TextAnnotation::TextType type)1568 TextAnnotation::TextAnnotation(TextAnnotation::TextType type) : Annotation(*new TextAnnotationPrivate())
1569 {
1570     setTextType(type);
1571 }
1572 
TextAnnotation(TextAnnotationPrivate & dd)1573 TextAnnotation::TextAnnotation(TextAnnotationPrivate &dd) : Annotation(dd) { }
1574 
~TextAnnotation()1575 TextAnnotation::~TextAnnotation() { }
1576 
subType() const1577 Annotation::SubType TextAnnotation::subType() const
1578 {
1579     return AText;
1580 }
1581 
textType() const1582 TextAnnotation::TextType TextAnnotation::textType() const
1583 {
1584     Q_D(const TextAnnotation);
1585 
1586     if (!d->pdfAnnot)
1587         return d->textType;
1588 
1589     return d->pdfAnnot->getType() == Annot::typeText ? TextAnnotation::Linked : TextAnnotation::InPlace;
1590 }
1591 
setTextType(TextAnnotation::TextType type)1592 void TextAnnotation::setTextType(TextAnnotation::TextType type)
1593 {
1594     Q_D(TextAnnotation);
1595 
1596     if (!d->pdfAnnot) {
1597         d->textType = type;
1598         return;
1599     }
1600 
1601     // Type cannot be changed if annotation is already tied
1602 }
1603 
textIcon() const1604 QString TextAnnotation::textIcon() const
1605 {
1606     Q_D(const TextAnnotation);
1607 
1608     if (!d->pdfAnnot)
1609         return d->textIcon;
1610 
1611     if (d->pdfAnnot->getType() == Annot::typeText) {
1612         const AnnotText *textann = static_cast<const AnnotText *>(d->pdfAnnot);
1613         return QString::fromLatin1(textann->getIcon()->c_str());
1614     }
1615 
1616     return QString();
1617 }
1618 
setTextIcon(const QString & icon)1619 void TextAnnotation::setTextIcon(const QString &icon)
1620 {
1621     Q_D(TextAnnotation);
1622 
1623     if (!d->pdfAnnot) {
1624         d->textIcon = icon;
1625         return;
1626     }
1627 
1628     if (d->pdfAnnot->getType() == Annot::typeText) {
1629         AnnotText *textann = static_cast<AnnotText *>(d->pdfAnnot);
1630         QByteArray encoded = icon.toLatin1();
1631         GooString s(encoded.constData());
1632         textann->setIcon(&s);
1633     }
1634 }
1635 
textFont() const1636 QFont TextAnnotation::textFont() const
1637 {
1638     Q_D(const TextAnnotation);
1639 
1640     if (!d->pdfAnnot)
1641         return d->textFont;
1642 
1643     double fontSize { AnnotFreeText::undefinedFontPtSize };
1644     if (d->pdfAnnot->getType() == Annot::typeFreeText) {
1645         std::unique_ptr<DefaultAppearance> da { d->getDefaultAppearanceFromNative() };
1646         if (da && da->getFontPtSize() > 0) {
1647             fontSize = da->getFontPtSize();
1648         }
1649     }
1650 
1651     QFont font;
1652     font.setPointSizeF(fontSize);
1653     return font;
1654 }
1655 
setTextFont(const QFont & font)1656 void TextAnnotation::setTextFont(const QFont &font)
1657 {
1658     Q_D(TextAnnotation);
1659     d->textFont = font;
1660     d->textColor = Qt::black;
1661 
1662     d->setDefaultAppearanceToNative();
1663 }
1664 
textColor() const1665 QColor TextAnnotation::textColor() const
1666 {
1667     Q_D(const TextAnnotation);
1668 
1669     if (!d->pdfAnnot)
1670         return d->textColor;
1671 
1672     if (std::unique_ptr<DefaultAppearance> da { d->getDefaultAppearanceFromNative() }) {
1673         return convertAnnotColor(da->getFontColor());
1674     }
1675 
1676     return {};
1677 }
1678 
setTextColor(const QColor & color)1679 void TextAnnotation::setTextColor(const QColor &color)
1680 {
1681     Q_D(TextAnnotation);
1682     d->textColor = color;
1683 
1684     d->setDefaultAppearanceToNative();
1685 }
1686 
inplaceAlign() const1687 TextAnnotation::InplaceAlignPosition TextAnnotation::inplaceAlign() const
1688 {
1689     Q_D(const TextAnnotation);
1690 
1691     if (!d->pdfAnnot)
1692         return d->inplaceAlign;
1693 
1694     if (d->pdfAnnot->getType() == Annot::typeFreeText) {
1695         const AnnotFreeText *ftextann = static_cast<const AnnotFreeText *>(d->pdfAnnot);
1696         switch (ftextann->getQuadding()) {
1697         case AnnotFreeText::quaddingLeftJustified:
1698             return InplaceAlignLeft;
1699         case AnnotFreeText::quaddingCentered:
1700             return InplaceAlignCenter;
1701         case AnnotFreeText::quaddingRightJustified:
1702             return InplaceAlignRight;
1703         }
1704     }
1705 
1706     return InplaceAlignLeft;
1707 }
1708 
alignToQuadding(TextAnnotation::InplaceAlignPosition align)1709 static AnnotFreeText::AnnotFreeTextQuadding alignToQuadding(TextAnnotation::InplaceAlignPosition align)
1710 {
1711     switch (align) {
1712     case TextAnnotation::InplaceAlignLeft:
1713         return AnnotFreeText::quaddingLeftJustified;
1714     case TextAnnotation::InplaceAlignCenter:
1715         return AnnotFreeText::quaddingCentered;
1716     case TextAnnotation::InplaceAlignRight:
1717         return AnnotFreeText::quaddingRightJustified;
1718     }
1719     return AnnotFreeText::quaddingLeftJustified;
1720 }
1721 
setInplaceAlign(InplaceAlignPosition align)1722 void TextAnnotation::setInplaceAlign(InplaceAlignPosition align)
1723 {
1724     Q_D(TextAnnotation);
1725 
1726     if (!d->pdfAnnot) {
1727         d->inplaceAlign = align;
1728         return;
1729     }
1730 
1731     if (d->pdfAnnot->getType() == Annot::typeFreeText) {
1732         AnnotFreeText *ftextann = static_cast<AnnotFreeText *>(d->pdfAnnot);
1733         ftextann->setQuadding(alignToQuadding(align));
1734     }
1735 }
1736 
calloutPoint(int id) const1737 QPointF TextAnnotation::calloutPoint(int id) const
1738 {
1739     const QVector<QPointF> points = calloutPoints();
1740     if (id < 0 || id >= points.size())
1741         return QPointF();
1742     else
1743         return points[id];
1744 }
1745 
calloutPoints() const1746 QVector<QPointF> TextAnnotation::calloutPoints() const
1747 {
1748     Q_D(const TextAnnotation);
1749 
1750     if (!d->pdfAnnot)
1751         return d->inplaceCallout;
1752 
1753     if (d->pdfAnnot->getType() == Annot::typeText)
1754         return QVector<QPointF>();
1755 
1756     const AnnotFreeText *ftextann = static_cast<const AnnotFreeText *>(d->pdfAnnot);
1757     const AnnotCalloutLine *callout = ftextann->getCalloutLine();
1758 
1759     if (!callout)
1760         return QVector<QPointF>();
1761 
1762     double MTX[6];
1763     d->fillTransformationMTX(MTX);
1764 
1765     const AnnotCalloutMultiLine *callout_v6 = dynamic_cast<const AnnotCalloutMultiLine *>(callout);
1766     QVector<QPointF> res(callout_v6 ? 3 : 2);
1767     XPDFReader::transform(MTX, callout->getX1(), callout->getY1(), res[0]);
1768     XPDFReader::transform(MTX, callout->getX2(), callout->getY2(), res[1]);
1769     if (callout_v6)
1770         XPDFReader::transform(MTX, callout_v6->getX3(), callout_v6->getY3(), res[2]);
1771     return res;
1772 }
1773 
setCalloutPoints(const QVector<QPointF> & points)1774 void TextAnnotation::setCalloutPoints(const QVector<QPointF> &points)
1775 {
1776     Q_D(TextAnnotation);
1777     if (!d->pdfAnnot) {
1778         d->inplaceCallout = points;
1779         return;
1780     }
1781 
1782     if (d->pdfAnnot->getType() != Annot::typeFreeText)
1783         return;
1784 
1785     AnnotFreeText *ftextann = static_cast<AnnotFreeText *>(d->pdfAnnot);
1786     const int count = points.size();
1787 
1788     if (count == 0) {
1789         ftextann->setCalloutLine(nullptr);
1790         return;
1791     }
1792 
1793     if (count != 2 && count != 3) {
1794         error(errSyntaxError, -1, "Expected zero, two or three points for callout");
1795         return;
1796     }
1797 
1798     AnnotCalloutLine *callout;
1799     double x1, y1, x2, y2;
1800     double MTX[6];
1801     d->fillTransformationMTX(MTX);
1802 
1803     XPDFReader::invTransform(MTX, points[0], x1, y1);
1804     XPDFReader::invTransform(MTX, points[1], x2, y2);
1805     if (count == 3) {
1806         double x3, y3;
1807         XPDFReader::invTransform(MTX, points[2], x3, y3);
1808         callout = new AnnotCalloutMultiLine(x1, y1, x2, y2, x3, y3);
1809     } else {
1810         callout = new AnnotCalloutLine(x1, y1, x2, y2);
1811     }
1812 
1813     ftextann->setCalloutLine(callout);
1814     delete callout;
1815 }
1816 
inplaceIntent() const1817 TextAnnotation::InplaceIntent TextAnnotation::inplaceIntent() const
1818 {
1819     Q_D(const TextAnnotation);
1820 
1821     if (!d->pdfAnnot)
1822         return d->inplaceIntent;
1823 
1824     if (d->pdfAnnot->getType() == Annot::typeFreeText) {
1825         const AnnotFreeText *ftextann = static_cast<const AnnotFreeText *>(d->pdfAnnot);
1826         return (TextAnnotation::InplaceIntent)ftextann->getIntent();
1827     }
1828 
1829     return TextAnnotation::Unknown;
1830 }
1831 
setInplaceIntent(TextAnnotation::InplaceIntent intent)1832 void TextAnnotation::setInplaceIntent(TextAnnotation::InplaceIntent intent)
1833 {
1834     Q_D(TextAnnotation);
1835 
1836     if (!d->pdfAnnot) {
1837         d->inplaceIntent = intent;
1838         return;
1839     }
1840 
1841     if (d->pdfAnnot->getType() == Annot::typeFreeText) {
1842         AnnotFreeText *ftextann = static_cast<AnnotFreeText *>(d->pdfAnnot);
1843         ftextann->setIntent((AnnotFreeText::AnnotFreeTextIntent)intent);
1844     }
1845 }
1846 
1847 /** LineAnnotation [Annotation] */
1848 class LineAnnotationPrivate : public AnnotationPrivate
1849 {
1850 public:
1851     LineAnnotationPrivate();
1852     Annotation *makeAlias() override;
1853     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
1854 
1855     // data fields (note uses border for rendering style)
1856     QVector<QPointF> linePoints;
1857     LineAnnotation::TermStyle lineStartStyle;
1858     LineAnnotation::TermStyle lineEndStyle;
1859     bool lineClosed : 1; // (if true draw close shape)
1860     bool lineShowCaption : 1;
1861     LineAnnotation::LineType lineType;
1862     QColor lineInnerColor;
1863     double lineLeadingFwdPt;
1864     double lineLeadingBackPt;
1865     LineAnnotation::LineIntent lineIntent;
1866 };
1867 
LineAnnotationPrivate()1868 LineAnnotationPrivate::LineAnnotationPrivate()
1869     : AnnotationPrivate(), lineStartStyle(LineAnnotation::None), lineEndStyle(LineAnnotation::None), lineClosed(false), lineShowCaption(false), lineLeadingFwdPt(0), lineLeadingBackPt(0), lineIntent(LineAnnotation::Unknown)
1870 {
1871 }
1872 
makeAlias()1873 Annotation *LineAnnotationPrivate::makeAlias()
1874 {
1875     return new LineAnnotation(*this);
1876 }
1877 
createNativeAnnot(::Page * destPage,DocumentData * doc)1878 Annot *LineAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
1879 {
1880     // Setters are defined in the public class
1881     LineAnnotation *q = static_cast<LineAnnotation *>(makeAlias());
1882 
1883     // Set page and document
1884     pdfPage = destPage;
1885     parentDoc = doc;
1886 
1887     // Set pdfAnnot
1888     PDFRectangle rect = boundaryToPdfRectangle(boundary, flags);
1889     if (lineType == LineAnnotation::StraightLine) {
1890         pdfAnnot = new AnnotLine(doc->doc, &rect);
1891     } else {
1892         pdfAnnot = new AnnotPolygon(doc->doc, &rect, lineClosed ? Annot::typePolygon : Annot::typePolyLine);
1893     }
1894 
1895     // Set properties
1896     flushBaseAnnotationProperties();
1897     q->setLinePoints(linePoints);
1898     q->setLineStartStyle(lineStartStyle);
1899     q->setLineEndStyle(lineEndStyle);
1900     q->setLineInnerColor(lineInnerColor);
1901     q->setLineLeadingForwardPoint(lineLeadingFwdPt);
1902     q->setLineLeadingBackPoint(lineLeadingBackPt);
1903     q->setLineShowCaption(lineShowCaption);
1904     q->setLineIntent(lineIntent);
1905 
1906     delete q;
1907 
1908     linePoints.clear(); // Free up memory
1909 
1910     return pdfAnnot;
1911 }
1912 
LineAnnotation(LineAnnotation::LineType type)1913 LineAnnotation::LineAnnotation(LineAnnotation::LineType type) : Annotation(*new LineAnnotationPrivate())
1914 {
1915     setLineType(type);
1916 }
1917 
LineAnnotation(LineAnnotationPrivate & dd)1918 LineAnnotation::LineAnnotation(LineAnnotationPrivate &dd) : Annotation(dd) { }
1919 
~LineAnnotation()1920 LineAnnotation::~LineAnnotation() { }
1921 
subType() const1922 Annotation::SubType LineAnnotation::subType() const
1923 {
1924     return ALine;
1925 }
1926 
lineType() const1927 LineAnnotation::LineType LineAnnotation::lineType() const
1928 {
1929     Q_D(const LineAnnotation);
1930 
1931     if (!d->pdfAnnot)
1932         return d->lineType;
1933 
1934     return (d->pdfAnnot->getType() == Annot::typeLine) ? LineAnnotation::StraightLine : LineAnnotation::Polyline;
1935 }
1936 
setLineType(LineAnnotation::LineType type)1937 void LineAnnotation::setLineType(LineAnnotation::LineType type)
1938 {
1939     Q_D(LineAnnotation);
1940 
1941     if (!d->pdfAnnot) {
1942         d->lineType = type;
1943         return;
1944     }
1945 
1946     // Type cannot be changed if annotation is already tied
1947 }
1948 
linePoints() const1949 QVector<QPointF> LineAnnotation::linePoints() const
1950 {
1951     Q_D(const LineAnnotation);
1952 
1953     if (!d->pdfAnnot)
1954         return d->linePoints;
1955 
1956     double MTX[6];
1957     d->fillTransformationMTX(MTX);
1958 
1959     QVector<QPointF> res;
1960     if (d->pdfAnnot->getType() == Annot::typeLine) {
1961         const AnnotLine *lineann = static_cast<const AnnotLine *>(d->pdfAnnot);
1962         QPointF p;
1963         XPDFReader::transform(MTX, lineann->getX1(), lineann->getY1(), p);
1964         res.append(p);
1965         XPDFReader::transform(MTX, lineann->getX2(), lineann->getY2(), p);
1966         res.append(p);
1967     } else {
1968         const AnnotPolygon *polyann = static_cast<const AnnotPolygon *>(d->pdfAnnot);
1969         const AnnotPath *vertices = polyann->getVertices();
1970 
1971         for (int i = 0; i < vertices->getCoordsLength(); ++i) {
1972             QPointF p;
1973             XPDFReader::transform(MTX, vertices->getX(i), vertices->getY(i), p);
1974             res.append(p);
1975         }
1976     }
1977 
1978     return res;
1979 }
1980 
setLinePoints(const QVector<QPointF> & points)1981 void LineAnnotation::setLinePoints(const QVector<QPointF> &points)
1982 {
1983     Q_D(LineAnnotation);
1984 
1985     if (!d->pdfAnnot) {
1986         d->linePoints = points;
1987         return;
1988     }
1989 
1990     if (d->pdfAnnot->getType() == Annot::typeLine) {
1991         AnnotLine *lineann = static_cast<AnnotLine *>(d->pdfAnnot);
1992         if (points.size() != 2) {
1993             error(errSyntaxError, -1, "Expected two points for a straight line");
1994             return;
1995         }
1996         double x1, y1, x2, y2;
1997         double MTX[6];
1998         d->fillTransformationMTX(MTX);
1999         XPDFReader::invTransform(MTX, points.first(), x1, y1);
2000         XPDFReader::invTransform(MTX, points.last(), x2, y2);
2001         lineann->setVertices(x1, y1, x2, y2);
2002     } else {
2003         AnnotPolygon *polyann = static_cast<AnnotPolygon *>(d->pdfAnnot);
2004         AnnotPath *p = d->toAnnotPath(points);
2005         polyann->setVertices(p);
2006         delete p;
2007     }
2008 }
2009 
lineStartStyle() const2010 LineAnnotation::TermStyle LineAnnotation::lineStartStyle() const
2011 {
2012     Q_D(const LineAnnotation);
2013 
2014     if (!d->pdfAnnot)
2015         return d->lineStartStyle;
2016 
2017     if (d->pdfAnnot->getType() == Annot::typeLine) {
2018         const AnnotLine *lineann = static_cast<const AnnotLine *>(d->pdfAnnot);
2019         return (LineAnnotation::TermStyle)lineann->getStartStyle();
2020     } else {
2021         const AnnotPolygon *polyann = static_cast<const AnnotPolygon *>(d->pdfAnnot);
2022         return (LineAnnotation::TermStyle)polyann->getStartStyle();
2023     }
2024 }
2025 
setLineStartStyle(LineAnnotation::TermStyle style)2026 void LineAnnotation::setLineStartStyle(LineAnnotation::TermStyle style)
2027 {
2028     Q_D(LineAnnotation);
2029 
2030     if (!d->pdfAnnot) {
2031         d->lineStartStyle = style;
2032         return;
2033     }
2034 
2035     if (d->pdfAnnot->getType() == Annot::typeLine) {
2036         AnnotLine *lineann = static_cast<AnnotLine *>(d->pdfAnnot);
2037         lineann->setStartEndStyle((AnnotLineEndingStyle)style, lineann->getEndStyle());
2038     } else {
2039         AnnotPolygon *polyann = static_cast<AnnotPolygon *>(d->pdfAnnot);
2040         polyann->setStartEndStyle((AnnotLineEndingStyle)style, polyann->getEndStyle());
2041     }
2042 }
2043 
lineEndStyle() const2044 LineAnnotation::TermStyle LineAnnotation::lineEndStyle() const
2045 {
2046     Q_D(const LineAnnotation);
2047 
2048     if (!d->pdfAnnot)
2049         return d->lineEndStyle;
2050 
2051     if (d->pdfAnnot->getType() == Annot::typeLine) {
2052         const AnnotLine *lineann = static_cast<const AnnotLine *>(d->pdfAnnot);
2053         return (LineAnnotation::TermStyle)lineann->getEndStyle();
2054     } else {
2055         const AnnotPolygon *polyann = static_cast<const AnnotPolygon *>(d->pdfAnnot);
2056         return (LineAnnotation::TermStyle)polyann->getEndStyle();
2057     }
2058 }
2059 
setLineEndStyle(LineAnnotation::TermStyle style)2060 void LineAnnotation::setLineEndStyle(LineAnnotation::TermStyle style)
2061 {
2062     Q_D(LineAnnotation);
2063 
2064     if (!d->pdfAnnot) {
2065         d->lineEndStyle = style;
2066         return;
2067     }
2068 
2069     if (d->pdfAnnot->getType() == Annot::typeLine) {
2070         AnnotLine *lineann = static_cast<AnnotLine *>(d->pdfAnnot);
2071         lineann->setStartEndStyle(lineann->getStartStyle(), (AnnotLineEndingStyle)style);
2072     } else {
2073         AnnotPolygon *polyann = static_cast<AnnotPolygon *>(d->pdfAnnot);
2074         polyann->setStartEndStyle(polyann->getStartStyle(), (AnnotLineEndingStyle)style);
2075     }
2076 }
2077 
isLineClosed() const2078 bool LineAnnotation::isLineClosed() const
2079 {
2080     Q_D(const LineAnnotation);
2081 
2082     if (!d->pdfAnnot)
2083         return d->lineClosed;
2084 
2085     return d->pdfAnnot->getType() == Annot::typePolygon;
2086 }
2087 
setLineClosed(bool closed)2088 void LineAnnotation::setLineClosed(bool closed)
2089 {
2090     Q_D(LineAnnotation);
2091 
2092     if (!d->pdfAnnot) {
2093         d->lineClosed = closed;
2094         return;
2095     }
2096 
2097     if (d->pdfAnnot->getType() != Annot::typeLine) {
2098         AnnotPolygon *polyann = static_cast<AnnotPolygon *>(d->pdfAnnot);
2099 
2100         // Set new subtype and switch intent if necessary
2101         if (closed) {
2102             polyann->setType(Annot::typePolygon);
2103             if (polyann->getIntent() == AnnotPolygon::polylineDimension)
2104                 polyann->setIntent(AnnotPolygon::polygonDimension);
2105         } else {
2106             polyann->setType(Annot::typePolyLine);
2107             if (polyann->getIntent() == AnnotPolygon::polygonDimension)
2108                 polyann->setIntent(AnnotPolygon::polylineDimension);
2109         }
2110     }
2111 }
2112 
lineInnerColor() const2113 QColor LineAnnotation::lineInnerColor() const
2114 {
2115     Q_D(const LineAnnotation);
2116 
2117     if (!d->pdfAnnot)
2118         return d->lineInnerColor;
2119 
2120     AnnotColor *c;
2121 
2122     if (d->pdfAnnot->getType() == Annot::typeLine) {
2123         const AnnotLine *lineann = static_cast<const AnnotLine *>(d->pdfAnnot);
2124         c = lineann->getInteriorColor();
2125     } else {
2126         const AnnotPolygon *polyann = static_cast<const AnnotPolygon *>(d->pdfAnnot);
2127         c = polyann->getInteriorColor();
2128     }
2129 
2130     return convertAnnotColor(c);
2131 }
2132 
setLineInnerColor(const QColor & color)2133 void LineAnnotation::setLineInnerColor(const QColor &color)
2134 {
2135     Q_D(LineAnnotation);
2136 
2137     if (!d->pdfAnnot) {
2138         d->lineInnerColor = color;
2139         return;
2140     }
2141 
2142     auto c = convertQColor(color);
2143 
2144     if (d->pdfAnnot->getType() == Annot::typeLine) {
2145         AnnotLine *lineann = static_cast<AnnotLine *>(d->pdfAnnot);
2146         lineann->setInteriorColor(std::move(c));
2147     } else {
2148         AnnotPolygon *polyann = static_cast<AnnotPolygon *>(d->pdfAnnot);
2149         polyann->setInteriorColor(std::move(c));
2150     }
2151 }
2152 
lineLeadingForwardPoint() const2153 double LineAnnotation::lineLeadingForwardPoint() const
2154 {
2155     Q_D(const LineAnnotation);
2156 
2157     if (!d->pdfAnnot)
2158         return d->lineLeadingFwdPt;
2159 
2160     if (d->pdfAnnot->getType() == Annot::typeLine) {
2161         const AnnotLine *lineann = static_cast<const AnnotLine *>(d->pdfAnnot);
2162         return lineann->getLeaderLineLength();
2163     }
2164 
2165     return 0;
2166 }
2167 
setLineLeadingForwardPoint(double point)2168 void LineAnnotation::setLineLeadingForwardPoint(double point)
2169 {
2170     Q_D(LineAnnotation);
2171 
2172     if (!d->pdfAnnot) {
2173         d->lineLeadingFwdPt = point;
2174         return;
2175     }
2176 
2177     if (d->pdfAnnot->getType() == Annot::typeLine) {
2178         AnnotLine *lineann = static_cast<AnnotLine *>(d->pdfAnnot);
2179         lineann->setLeaderLineLength(point);
2180     }
2181 }
2182 
lineLeadingBackPoint() const2183 double LineAnnotation::lineLeadingBackPoint() const
2184 {
2185     Q_D(const LineAnnotation);
2186 
2187     if (!d->pdfAnnot)
2188         return d->lineLeadingBackPt;
2189 
2190     if (d->pdfAnnot->getType() == Annot::typeLine) {
2191         const AnnotLine *lineann = static_cast<const AnnotLine *>(d->pdfAnnot);
2192         return lineann->getLeaderLineExtension();
2193     }
2194 
2195     return 0;
2196 }
2197 
setLineLeadingBackPoint(double point)2198 void LineAnnotation::setLineLeadingBackPoint(double point)
2199 {
2200     Q_D(LineAnnotation);
2201 
2202     if (!d->pdfAnnot) {
2203         d->lineLeadingBackPt = point;
2204         return;
2205     }
2206 
2207     if (d->pdfAnnot->getType() == Annot::typeLine) {
2208         AnnotLine *lineann = static_cast<AnnotLine *>(d->pdfAnnot);
2209         lineann->setLeaderLineExtension(point);
2210     }
2211 }
2212 
lineShowCaption() const2213 bool LineAnnotation::lineShowCaption() const
2214 {
2215     Q_D(const LineAnnotation);
2216 
2217     if (!d->pdfAnnot)
2218         return d->lineShowCaption;
2219 
2220     if (d->pdfAnnot->getType() == Annot::typeLine) {
2221         const AnnotLine *lineann = static_cast<const AnnotLine *>(d->pdfAnnot);
2222         return lineann->getCaption();
2223     }
2224 
2225     return false;
2226 }
2227 
setLineShowCaption(bool show)2228 void LineAnnotation::setLineShowCaption(bool show)
2229 {
2230     Q_D(LineAnnotation);
2231 
2232     if (!d->pdfAnnot) {
2233         d->lineShowCaption = show;
2234         return;
2235     }
2236 
2237     if (d->pdfAnnot->getType() == Annot::typeLine) {
2238         AnnotLine *lineann = static_cast<AnnotLine *>(d->pdfAnnot);
2239         lineann->setCaption(show);
2240     }
2241 }
2242 
lineIntent() const2243 LineAnnotation::LineIntent LineAnnotation::lineIntent() const
2244 {
2245     Q_D(const LineAnnotation);
2246 
2247     if (!d->pdfAnnot)
2248         return d->lineIntent;
2249 
2250     if (d->pdfAnnot->getType() == Annot::typeLine) {
2251         const AnnotLine *lineann = static_cast<const AnnotLine *>(d->pdfAnnot);
2252         return (LineAnnotation::LineIntent)(lineann->getIntent() + 1);
2253     } else {
2254         const AnnotPolygon *polyann = static_cast<const AnnotPolygon *>(d->pdfAnnot);
2255         if (polyann->getIntent() == AnnotPolygon::polygonCloud)
2256             return LineAnnotation::PolygonCloud;
2257         else // AnnotPolygon::polylineDimension, AnnotPolygon::polygonDimension
2258             return LineAnnotation::Dimension;
2259     }
2260 }
2261 
setLineIntent(LineAnnotation::LineIntent intent)2262 void LineAnnotation::setLineIntent(LineAnnotation::LineIntent intent)
2263 {
2264     Q_D(LineAnnotation);
2265 
2266     if (!d->pdfAnnot) {
2267         d->lineIntent = intent;
2268         return;
2269     }
2270 
2271     if (intent == LineAnnotation::Unknown)
2272         return; // Do not set (actually, it should clear the property)
2273 
2274     if (d->pdfAnnot->getType() == Annot::typeLine) {
2275         AnnotLine *lineann = static_cast<AnnotLine *>(d->pdfAnnot);
2276         lineann->setIntent((AnnotLine::AnnotLineIntent)(intent - 1));
2277     } else {
2278         AnnotPolygon *polyann = static_cast<AnnotPolygon *>(d->pdfAnnot);
2279         if (intent == LineAnnotation::PolygonCloud)
2280             polyann->setIntent(AnnotPolygon::polygonCloud);
2281         else // LineAnnotation::Dimension
2282         {
2283             if (d->pdfAnnot->getType() == Annot::typePolygon)
2284                 polyann->setIntent(AnnotPolygon::polygonDimension);
2285             else // Annot::typePolyLine
2286                 polyann->setIntent(AnnotPolygon::polylineDimension);
2287         }
2288     }
2289 }
2290 
2291 /** GeomAnnotation [Annotation] */
2292 class GeomAnnotationPrivate : public AnnotationPrivate
2293 {
2294 public:
2295     GeomAnnotationPrivate();
2296     Annotation *makeAlias() override;
2297     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
2298 
2299     // data fields (note uses border for rendering style)
2300     GeomAnnotation::GeomType geomType;
2301     QColor geomInnerColor;
2302 };
2303 
GeomAnnotationPrivate()2304 GeomAnnotationPrivate::GeomAnnotationPrivate() : AnnotationPrivate(), geomType(GeomAnnotation::InscribedSquare) { }
2305 
makeAlias()2306 Annotation *GeomAnnotationPrivate::makeAlias()
2307 {
2308     return new GeomAnnotation(*this);
2309 }
2310 
createNativeAnnot(::Page * destPage,DocumentData * doc)2311 Annot *GeomAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
2312 {
2313     // Setters are defined in the public class
2314     GeomAnnotation *q = static_cast<GeomAnnotation *>(makeAlias());
2315 
2316     // Set page and document
2317     pdfPage = destPage;
2318     parentDoc = doc;
2319 
2320     Annot::AnnotSubtype type;
2321     if (geomType == GeomAnnotation::InscribedSquare)
2322         type = Annot::typeSquare;
2323     else // GeomAnnotation::InscribedCircle
2324         type = Annot::typeCircle;
2325 
2326     // Set pdfAnnot
2327     PDFRectangle rect = boundaryToPdfRectangle(boundary, flags);
2328     pdfAnnot = new AnnotGeometry(destPage->getDoc(), &rect, type);
2329 
2330     // Set properties
2331     flushBaseAnnotationProperties();
2332     q->setGeomInnerColor(geomInnerColor);
2333 
2334     delete q;
2335     return pdfAnnot;
2336 }
2337 
GeomAnnotation()2338 GeomAnnotation::GeomAnnotation() : Annotation(*new GeomAnnotationPrivate()) { }
2339 
GeomAnnotation(GeomAnnotationPrivate & dd)2340 GeomAnnotation::GeomAnnotation(GeomAnnotationPrivate &dd) : Annotation(dd) { }
2341 
~GeomAnnotation()2342 GeomAnnotation::~GeomAnnotation() { }
2343 
subType() const2344 Annotation::SubType GeomAnnotation::subType() const
2345 {
2346     return AGeom;
2347 }
2348 
geomType() const2349 GeomAnnotation::GeomType GeomAnnotation::geomType() const
2350 {
2351     Q_D(const GeomAnnotation);
2352 
2353     if (!d->pdfAnnot)
2354         return d->geomType;
2355 
2356     if (d->pdfAnnot->getType() == Annot::typeSquare)
2357         return GeomAnnotation::InscribedSquare;
2358     else // Annot::typeCircle
2359         return GeomAnnotation::InscribedCircle;
2360 }
2361 
setGeomType(GeomAnnotation::GeomType type)2362 void GeomAnnotation::setGeomType(GeomAnnotation::GeomType type)
2363 {
2364     Q_D(GeomAnnotation);
2365 
2366     if (!d->pdfAnnot) {
2367         d->geomType = type;
2368         return;
2369     }
2370 
2371     AnnotGeometry *geomann = static_cast<AnnotGeometry *>(d->pdfAnnot);
2372     if (type == GeomAnnotation::InscribedSquare)
2373         geomann->setType(Annot::typeSquare);
2374     else // GeomAnnotation::InscribedCircle
2375         geomann->setType(Annot::typeCircle);
2376 }
2377 
geomInnerColor() const2378 QColor GeomAnnotation::geomInnerColor() const
2379 {
2380     Q_D(const GeomAnnotation);
2381 
2382     if (!d->pdfAnnot)
2383         return d->geomInnerColor;
2384 
2385     const AnnotGeometry *geomann = static_cast<const AnnotGeometry *>(d->pdfAnnot);
2386     return convertAnnotColor(geomann->getInteriorColor());
2387 }
2388 
setGeomInnerColor(const QColor & color)2389 void GeomAnnotation::setGeomInnerColor(const QColor &color)
2390 {
2391     Q_D(GeomAnnotation);
2392 
2393     if (!d->pdfAnnot) {
2394         d->geomInnerColor = color;
2395         return;
2396     }
2397 
2398     AnnotGeometry *geomann = static_cast<AnnotGeometry *>(d->pdfAnnot);
2399     geomann->setInteriorColor(convertQColor(color));
2400 }
2401 
2402 /** HighlightAnnotation [Annotation] */
2403 class HighlightAnnotationPrivate : public AnnotationPrivate
2404 {
2405 public:
2406     HighlightAnnotationPrivate();
2407     Annotation *makeAlias() override;
2408     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
2409 
2410     // data fields
2411     HighlightAnnotation::HighlightType highlightType;
2412     QList<HighlightAnnotation::Quad> highlightQuads; // not empty
2413 
2414     // helpers
2415     static Annot::AnnotSubtype toAnnotSubType(HighlightAnnotation::HighlightType type);
2416     QList<HighlightAnnotation::Quad> fromQuadrilaterals(AnnotQuadrilaterals *quads) const;
2417     AnnotQuadrilaterals *toQuadrilaterals(const QList<HighlightAnnotation::Quad> &quads) const;
2418 };
2419 
HighlightAnnotationPrivate()2420 HighlightAnnotationPrivate::HighlightAnnotationPrivate() : AnnotationPrivate(), highlightType(HighlightAnnotation::Highlight) { }
2421 
makeAlias()2422 Annotation *HighlightAnnotationPrivate::makeAlias()
2423 {
2424     return new HighlightAnnotation(*this);
2425 }
2426 
toAnnotSubType(HighlightAnnotation::HighlightType type)2427 Annot::AnnotSubtype HighlightAnnotationPrivate::toAnnotSubType(HighlightAnnotation::HighlightType type)
2428 {
2429     switch (type) {
2430     default: // HighlightAnnotation::Highlight:
2431         return Annot::typeHighlight;
2432     case HighlightAnnotation::Underline:
2433         return Annot::typeUnderline;
2434     case HighlightAnnotation::Squiggly:
2435         return Annot::typeSquiggly;
2436     case HighlightAnnotation::StrikeOut:
2437         return Annot::typeStrikeOut;
2438     }
2439 }
2440 
fromQuadrilaterals(AnnotQuadrilaterals * hlquads) const2441 QList<HighlightAnnotation::Quad> HighlightAnnotationPrivate::fromQuadrilaterals(AnnotQuadrilaterals *hlquads) const
2442 {
2443     QList<HighlightAnnotation::Quad> quads;
2444 
2445     if (!hlquads || !hlquads->getQuadrilateralsLength())
2446         return quads;
2447     const int quadsCount = hlquads->getQuadrilateralsLength();
2448 
2449     double MTX[6];
2450     fillTransformationMTX(MTX);
2451 
2452     quads.reserve(quadsCount);
2453     for (int q = 0; q < quadsCount; ++q) {
2454         HighlightAnnotation::Quad quad;
2455         XPDFReader::transform(MTX, hlquads->getX1(q), hlquads->getY1(q), quad.points[0]);
2456         XPDFReader::transform(MTX, hlquads->getX2(q), hlquads->getY2(q), quad.points[1]);
2457         XPDFReader::transform(MTX, hlquads->getX3(q), hlquads->getY3(q), quad.points[2]);
2458         XPDFReader::transform(MTX, hlquads->getX4(q), hlquads->getY4(q), quad.points[3]);
2459         // ### PDF1.6 specs says that point are in ccw order, but in fact
2460         // points 3 and 4 are swapped in every PDF around!
2461         QPointF tmpPoint = quad.points[2];
2462         quad.points[2] = quad.points[3];
2463         quad.points[3] = tmpPoint;
2464         // initialize other properties and append quad
2465         quad.capStart = true; // unlinked quads are always capped
2466         quad.capEnd = true; // unlinked quads are always capped
2467         quad.feather = 0.1; // default feather
2468         quads.append(quad);
2469     }
2470 
2471     return quads;
2472 }
2473 
toQuadrilaterals(const QList<HighlightAnnotation::Quad> & quads) const2474 AnnotQuadrilaterals *HighlightAnnotationPrivate::toQuadrilaterals(const QList<HighlightAnnotation::Quad> &quads) const
2475 {
2476     const int count = quads.size();
2477     auto ac = std::make_unique<AnnotQuadrilaterals::AnnotQuadrilateral[]>(count);
2478 
2479     double MTX[6];
2480     fillTransformationMTX(MTX);
2481 
2482     int pos = 0;
2483     foreach (const HighlightAnnotation::Quad &q, quads) {
2484         double x1, y1, x2, y2, x3, y3, x4, y4;
2485         XPDFReader::invTransform(MTX, q.points[0], x1, y1);
2486         XPDFReader::invTransform(MTX, q.points[1], x2, y2);
2487         // Swap points 3 and 4 (see HighlightAnnotationPrivate::fromQuadrilaterals)
2488         XPDFReader::invTransform(MTX, q.points[3], x3, y3);
2489         XPDFReader::invTransform(MTX, q.points[2], x4, y4);
2490         ac[pos++] = AnnotQuadrilaterals::AnnotQuadrilateral(x1, y1, x2, y2, x3, y3, x4, y4);
2491     }
2492 
2493     return new AnnotQuadrilaterals(std::move(ac), count);
2494 }
2495 
createNativeAnnot(::Page * destPage,DocumentData * doc)2496 Annot *HighlightAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
2497 {
2498     // Setters are defined in the public class
2499     HighlightAnnotation *q = static_cast<HighlightAnnotation *>(makeAlias());
2500 
2501     // Set page and document
2502     pdfPage = destPage;
2503     parentDoc = doc;
2504 
2505     // Set pdfAnnot
2506     PDFRectangle rect = boundaryToPdfRectangle(boundary, flags);
2507     pdfAnnot = new AnnotTextMarkup(destPage->getDoc(), &rect, toAnnotSubType(highlightType));
2508 
2509     // Set properties
2510     flushBaseAnnotationProperties();
2511     q->setHighlightQuads(highlightQuads);
2512 
2513     highlightQuads.clear(); // Free up memory
2514 
2515     delete q;
2516 
2517     return pdfAnnot;
2518 }
2519 
HighlightAnnotation()2520 HighlightAnnotation::HighlightAnnotation() : Annotation(*new HighlightAnnotationPrivate()) { }
2521 
HighlightAnnotation(HighlightAnnotationPrivate & dd)2522 HighlightAnnotation::HighlightAnnotation(HighlightAnnotationPrivate &dd) : Annotation(dd) { }
2523 
~HighlightAnnotation()2524 HighlightAnnotation::~HighlightAnnotation() { }
2525 
subType() const2526 Annotation::SubType HighlightAnnotation::subType() const
2527 {
2528     return AHighlight;
2529 }
2530 
highlightType() const2531 HighlightAnnotation::HighlightType HighlightAnnotation::highlightType() const
2532 {
2533     Q_D(const HighlightAnnotation);
2534 
2535     if (!d->pdfAnnot)
2536         return d->highlightType;
2537 
2538     Annot::AnnotSubtype subType = d->pdfAnnot->getType();
2539 
2540     if (subType == Annot::typeHighlight)
2541         return HighlightAnnotation::Highlight;
2542     else if (subType == Annot::typeUnderline)
2543         return HighlightAnnotation::Underline;
2544     else if (subType == Annot::typeSquiggly)
2545         return HighlightAnnotation::Squiggly;
2546     else // Annot::typeStrikeOut
2547         return HighlightAnnotation::StrikeOut;
2548 }
2549 
setHighlightType(HighlightAnnotation::HighlightType type)2550 void HighlightAnnotation::setHighlightType(HighlightAnnotation::HighlightType type)
2551 {
2552     Q_D(HighlightAnnotation);
2553 
2554     if (!d->pdfAnnot) {
2555         d->highlightType = type;
2556         return;
2557     }
2558 
2559     AnnotTextMarkup *hlann = static_cast<AnnotTextMarkup *>(d->pdfAnnot);
2560     hlann->setType(HighlightAnnotationPrivate::toAnnotSubType(type));
2561 }
2562 
highlightQuads() const2563 QList<HighlightAnnotation::Quad> HighlightAnnotation::highlightQuads() const
2564 {
2565     Q_D(const HighlightAnnotation);
2566 
2567     if (!d->pdfAnnot)
2568         return d->highlightQuads;
2569 
2570     const AnnotTextMarkup *hlann = static_cast<AnnotTextMarkup *>(d->pdfAnnot);
2571     return d->fromQuadrilaterals(hlann->getQuadrilaterals());
2572 }
2573 
setHighlightQuads(const QList<HighlightAnnotation::Quad> & quads)2574 void HighlightAnnotation::setHighlightQuads(const QList<HighlightAnnotation::Quad> &quads)
2575 {
2576     Q_D(HighlightAnnotation);
2577 
2578     if (!d->pdfAnnot) {
2579         d->highlightQuads = quads;
2580         return;
2581     }
2582 
2583     AnnotTextMarkup *hlann = static_cast<AnnotTextMarkup *>(d->pdfAnnot);
2584     AnnotQuadrilaterals *quadrilaterals = d->toQuadrilaterals(quads);
2585     hlann->setQuadrilaterals(quadrilaterals);
2586     delete quadrilaterals;
2587 }
2588 
2589 /** StampAnnotation [Annotation] */
2590 class StampAnnotationPrivate : public AnnotationPrivate
2591 {
2592 public:
2593     StampAnnotationPrivate();
2594     Annotation *makeAlias() override;
2595     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
2596 
2597     AnnotStampImageHelper *convertQImageToAnnotStampImageHelper(const QImage &qimg);
2598 
2599     // data fields
2600     QString stampIconName;
2601     QImage stampCustomImage;
2602 };
2603 
StampAnnotationPrivate()2604 StampAnnotationPrivate::StampAnnotationPrivate() : AnnotationPrivate(), stampIconName(QStringLiteral("Draft")) { }
2605 
makeAlias()2606 Annotation *StampAnnotationPrivate::makeAlias()
2607 {
2608     return new StampAnnotation(*this);
2609 }
2610 
createNativeAnnot(::Page * destPage,DocumentData * doc)2611 Annot *StampAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
2612 {
2613     StampAnnotation *q = static_cast<StampAnnotation *>(makeAlias());
2614 
2615     // Set page and document
2616     pdfPage = destPage;
2617     parentDoc = doc;
2618 
2619     // Set pdfAnnot
2620     PDFRectangle rect = boundaryToPdfRectangle(boundary, flags);
2621     pdfAnnot = new AnnotStamp(destPage->getDoc(), &rect);
2622 
2623     // Set properties
2624     flushBaseAnnotationProperties();
2625     q->setStampIconName(stampIconName);
2626     q->setStampCustomImage(stampCustomImage);
2627 
2628     delete q;
2629 
2630     stampIconName.clear(); // Free up memory
2631 
2632     return pdfAnnot;
2633 }
2634 
convertQImageToAnnotStampImageHelper(const QImage & qimg)2635 AnnotStampImageHelper *StampAnnotationPrivate::convertQImageToAnnotStampImageHelper(const QImage &qimg)
2636 {
2637     QImage convertedQImage = qimg;
2638 
2639     QByteArray data;
2640     QByteArray sMaskData;
2641     const int width = convertedQImage.width();
2642     const int height = convertedQImage.height();
2643     int bitsPerComponent = 1;
2644     ColorSpace colorSpace = ColorSpace::DeviceGray;
2645 
2646     switch (convertedQImage.format()) {
2647     case QImage::Format_MonoLSB:
2648         if (!convertedQImage.allGray()) {
2649             convertedQImage = convertedQImage.convertToFormat(QImage::Format_RGB888);
2650 
2651             colorSpace = ColorSpace::DeviceRGB;
2652             bitsPerComponent = 8;
2653         } else {
2654             convertedQImage = convertedQImage.convertToFormat(QImage::Format_Mono);
2655         }
2656         break;
2657     case QImage::Format_Mono:
2658         if (!convertedQImage.allGray()) {
2659             convertedQImage = convertedQImage.convertToFormat(QImage::Format_RGB888);
2660 
2661             colorSpace = ColorSpace::DeviceRGB;
2662             bitsPerComponent = 8;
2663         }
2664         break;
2665     case QImage::Format_RGB32:
2666     case QImage::Format_ARGB32_Premultiplied:
2667     case QImage::Format_ARGB8565_Premultiplied:
2668     case QImage::Format_ARGB6666_Premultiplied:
2669     case QImage::Format_ARGB8555_Premultiplied:
2670     case QImage::Format_ARGB4444_Premultiplied:
2671     case QImage::Format_Alpha8:
2672         convertedQImage = convertedQImage.convertToFormat(QImage::Format_ARGB32);
2673         colorSpace = ColorSpace::DeviceRGB;
2674         bitsPerComponent = 8;
2675         break;
2676     case QImage::Format_RGBA8888:
2677     case QImage::Format_RGBA8888_Premultiplied:
2678     case QImage::Format_RGBX8888:
2679     case QImage::Format_ARGB32:
2680         colorSpace = ColorSpace::DeviceRGB;
2681         bitsPerComponent = 8;
2682         break;
2683     case QImage::Format_Grayscale8:
2684         bitsPerComponent = 8;
2685         break;
2686     case QImage::Format_Grayscale16:
2687         convertedQImage = convertedQImage.convertToFormat(QImage::Format_Grayscale8);
2688 
2689         colorSpace = ColorSpace::DeviceGray;
2690         bitsPerComponent = 8;
2691         break;
2692     case QImage::Format_RGB16:
2693     case QImage::Format_RGB666:
2694     case QImage::Format_RGB555:
2695     case QImage::Format_RGB444:
2696         convertedQImage = convertedQImage.convertToFormat(QImage::Format_RGB888);
2697         colorSpace = ColorSpace::DeviceRGB;
2698         bitsPerComponent = 8;
2699         break;
2700     case QImage::Format_RGB888:
2701         colorSpace = ColorSpace::DeviceRGB;
2702         bitsPerComponent = 8;
2703         break;
2704     default:
2705         convertedQImage = convertedQImage.convertToFormat(QImage::Format_ARGB32);
2706 
2707         colorSpace = ColorSpace::DeviceRGB;
2708         bitsPerComponent = 8;
2709         break;
2710     }
2711 
2712     getRawDataFromQImage(convertedQImage, convertedQImage.depth(), &data, &sMaskData);
2713 
2714     AnnotStampImageHelper *annotImg;
2715 
2716     if (sMaskData.count() > 0) {
2717         AnnotStampImageHelper sMask(parentDoc->doc, width, height, ColorSpace::DeviceGray, 8, sMaskData.data(), sMaskData.count());
2718         annotImg = new AnnotStampImageHelper(parentDoc->doc, width, height, colorSpace, bitsPerComponent, data.data(), data.count(), sMask.getRef());
2719     } else {
2720         annotImg = new AnnotStampImageHelper(parentDoc->doc, width, height, colorSpace, bitsPerComponent, data.data(), data.count());
2721     }
2722 
2723     return annotImg;
2724 }
2725 
StampAnnotation()2726 StampAnnotation::StampAnnotation() : Annotation(*new StampAnnotationPrivate()) { }
2727 
StampAnnotation(StampAnnotationPrivate & dd)2728 StampAnnotation::StampAnnotation(StampAnnotationPrivate &dd) : Annotation(dd) { }
2729 
~StampAnnotation()2730 StampAnnotation::~StampAnnotation() { }
2731 
subType() const2732 Annotation::SubType StampAnnotation::subType() const
2733 {
2734     return AStamp;
2735 }
2736 
stampIconName() const2737 QString StampAnnotation::stampIconName() const
2738 {
2739     Q_D(const StampAnnotation);
2740 
2741     if (!d->pdfAnnot)
2742         return d->stampIconName;
2743 
2744     const AnnotStamp *stampann = static_cast<const AnnotStamp *>(d->pdfAnnot);
2745     return QString::fromLatin1(stampann->getIcon()->c_str());
2746 }
2747 
setStampIconName(const QString & name)2748 void StampAnnotation::setStampIconName(const QString &name)
2749 {
2750     Q_D(StampAnnotation);
2751 
2752     if (!d->pdfAnnot) {
2753         d->stampIconName = name;
2754         return;
2755     }
2756 
2757     AnnotStamp *stampann = static_cast<AnnotStamp *>(d->pdfAnnot);
2758     QByteArray encoded = name.toLatin1();
2759     GooString s(encoded.constData());
2760     stampann->setIcon(&s);
2761 }
2762 
setStampCustomImage(const QImage & image)2763 void StampAnnotation::setStampCustomImage(const QImage &image)
2764 {
2765     if (image.isNull()) {
2766         return;
2767     }
2768 
2769     Q_D(StampAnnotation);
2770 
2771     if (!d->pdfAnnot) {
2772         d->stampCustomImage = QImage(image);
2773         return;
2774     }
2775 
2776     AnnotStamp *stampann = static_cast<AnnotStamp *>(d->pdfAnnot);
2777     AnnotStampImageHelper *annotCustomImage = d->convertQImageToAnnotStampImageHelper(image);
2778     stampann->setCustomImage(annotCustomImage);
2779 }
2780 
2781 /** InkAnnotation [Annotation] */
2782 class InkAnnotationPrivate : public AnnotationPrivate
2783 {
2784 public:
2785     InkAnnotationPrivate();
2786     Annotation *makeAlias() override;
2787     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
2788 
2789     // data fields
2790     QList<QVector<QPointF>> inkPaths;
2791 
2792     // helper
2793     AnnotPath **toAnnotPaths(const QList<QVector<QPointF>> &paths);
2794 };
2795 
InkAnnotationPrivate()2796 InkAnnotationPrivate::InkAnnotationPrivate() : AnnotationPrivate() { }
2797 
makeAlias()2798 Annotation *InkAnnotationPrivate::makeAlias()
2799 {
2800     return new InkAnnotation(*this);
2801 }
2802 
2803 // Note: Caller is required to delete array elements and the array itself after use
toAnnotPaths(const QList<QVector<QPointF>> & paths)2804 AnnotPath **InkAnnotationPrivate::toAnnotPaths(const QList<QVector<QPointF>> &paths)
2805 {
2806     const int pathsNumber = paths.size();
2807     AnnotPath **res = new AnnotPath *[pathsNumber];
2808     for (int i = 0; i < pathsNumber; ++i)
2809         res[i] = toAnnotPath(paths[i]);
2810     return res;
2811 }
2812 
createNativeAnnot(::Page * destPage,DocumentData * doc)2813 Annot *InkAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
2814 {
2815     // Setters are defined in the public class
2816     InkAnnotation *q = static_cast<InkAnnotation *>(makeAlias());
2817 
2818     // Set page and document
2819     pdfPage = destPage;
2820     parentDoc = doc;
2821 
2822     // Set pdfAnnot
2823     PDFRectangle rect = boundaryToPdfRectangle(boundary, flags);
2824     pdfAnnot = new AnnotInk(destPage->getDoc(), &rect);
2825 
2826     // Set properties
2827     flushBaseAnnotationProperties();
2828     q->setInkPaths(inkPaths);
2829 
2830     inkPaths.clear(); // Free up memory
2831 
2832     delete q;
2833 
2834     return pdfAnnot;
2835 }
2836 
InkAnnotation()2837 InkAnnotation::InkAnnotation() : Annotation(*new InkAnnotationPrivate()) { }
2838 
InkAnnotation(InkAnnotationPrivate & dd)2839 InkAnnotation::InkAnnotation(InkAnnotationPrivate &dd) : Annotation(dd) { }
2840 
~InkAnnotation()2841 InkAnnotation::~InkAnnotation() { }
2842 
subType() const2843 Annotation::SubType InkAnnotation::subType() const
2844 {
2845     return AInk;
2846 }
2847 
inkPaths() const2848 QList<QVector<QPointF>> InkAnnotation::inkPaths() const
2849 {
2850     Q_D(const InkAnnotation);
2851 
2852     if (!d->pdfAnnot)
2853         return d->inkPaths;
2854 
2855     const AnnotInk *inkann = static_cast<const AnnotInk *>(d->pdfAnnot);
2856 
2857     const AnnotPath *const *paths = inkann->getInkList();
2858     if (!paths || !inkann->getInkListLength())
2859         return {};
2860 
2861     double MTX[6];
2862     d->fillTransformationMTX(MTX);
2863 
2864     const int pathsNumber = inkann->getInkListLength();
2865     QList<QVector<QPointF>> inkPaths;
2866     inkPaths.reserve(pathsNumber);
2867     for (int m = 0; m < pathsNumber; ++m) {
2868         // transform each path in a list of normalized points ..
2869         QVector<QPointF> localList;
2870         const AnnotPath *path = paths[m];
2871         const int pointsNumber = path ? path->getCoordsLength() : 0;
2872         for (int n = 0; n < pointsNumber; ++n) {
2873             QPointF point;
2874             XPDFReader::transform(MTX, path->getX(n), path->getY(n), point);
2875             localList.append(point);
2876         }
2877         // ..and add it to the annotation
2878         inkPaths.append(localList);
2879     }
2880     return inkPaths;
2881 }
2882 
setInkPaths(const QList<QVector<QPointF>> & paths)2883 void InkAnnotation::setInkPaths(const QList<QVector<QPointF>> &paths)
2884 {
2885     Q_D(InkAnnotation);
2886 
2887     if (!d->pdfAnnot) {
2888         d->inkPaths = paths;
2889         return;
2890     }
2891 
2892     AnnotInk *inkann = static_cast<AnnotInk *>(d->pdfAnnot);
2893     AnnotPath **annotpaths = d->toAnnotPaths(paths);
2894     const int pathsNumber = paths.size();
2895     inkann->setInkList(annotpaths, pathsNumber);
2896 
2897     for (int i = 0; i < pathsNumber; ++i)
2898         delete annotpaths[i];
2899     delete[] annotpaths;
2900 }
2901 
2902 /** LinkAnnotation [Annotation] */
2903 class LinkAnnotationPrivate : public AnnotationPrivate
2904 {
2905 public:
2906     LinkAnnotationPrivate();
2907     ~LinkAnnotationPrivate() override;
2908     Annotation *makeAlias() override;
2909     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
2910 
2911     // data fields
2912     std::unique_ptr<Link> linkDestination;
2913     LinkAnnotation::HighlightMode linkHLMode;
2914     QPointF linkRegion[4];
2915 };
2916 
LinkAnnotationPrivate()2917 LinkAnnotationPrivate::LinkAnnotationPrivate() : AnnotationPrivate(), linkHLMode(LinkAnnotation::Invert) { }
2918 
~LinkAnnotationPrivate()2919 LinkAnnotationPrivate::~LinkAnnotationPrivate() { }
2920 
makeAlias()2921 Annotation *LinkAnnotationPrivate::makeAlias()
2922 {
2923     return new LinkAnnotation(*this);
2924 }
2925 
createNativeAnnot(::Page * destPage,DocumentData * doc)2926 Annot *LinkAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
2927 {
2928     return nullptr; // Not implemented
2929 }
2930 
LinkAnnotation()2931 LinkAnnotation::LinkAnnotation() : Annotation(*new LinkAnnotationPrivate()) { }
2932 
LinkAnnotation(LinkAnnotationPrivate & dd)2933 LinkAnnotation::LinkAnnotation(LinkAnnotationPrivate &dd) : Annotation(dd) { }
2934 
~LinkAnnotation()2935 LinkAnnotation::~LinkAnnotation() { }
2936 
subType() const2937 Annotation::SubType LinkAnnotation::subType() const
2938 {
2939     return ALink;
2940 }
2941 
linkDestination() const2942 Link *LinkAnnotation::linkDestination() const
2943 {
2944     Q_D(const LinkAnnotation);
2945     return d->linkDestination.get();
2946 }
2947 
setLinkDestination(std::unique_ptr<Link> && link)2948 void LinkAnnotation::setLinkDestination(std::unique_ptr<Link> &&link)
2949 {
2950     Q_D(LinkAnnotation);
2951     d->linkDestination = std::move(link);
2952 }
2953 
linkHighlightMode() const2954 LinkAnnotation::HighlightMode LinkAnnotation::linkHighlightMode() const
2955 {
2956     Q_D(const LinkAnnotation);
2957     return d->linkHLMode;
2958 }
2959 
setLinkHighlightMode(LinkAnnotation::HighlightMode mode)2960 void LinkAnnotation::setLinkHighlightMode(LinkAnnotation::HighlightMode mode)
2961 {
2962     Q_D(LinkAnnotation);
2963     d->linkHLMode = mode;
2964 }
2965 
linkRegionPoint(int id) const2966 QPointF LinkAnnotation::linkRegionPoint(int id) const
2967 {
2968     if (id < 0 || id >= 4)
2969         return QPointF();
2970 
2971     Q_D(const LinkAnnotation);
2972     return d->linkRegion[id];
2973 }
2974 
setLinkRegionPoint(int id,const QPointF point)2975 void LinkAnnotation::setLinkRegionPoint(int id, const QPointF point)
2976 {
2977     if (id < 0 || id >= 4)
2978         return;
2979 
2980     Q_D(LinkAnnotation);
2981     d->linkRegion[id] = point;
2982 }
2983 
2984 /** CaretAnnotation [Annotation] */
2985 class CaretAnnotationPrivate : public AnnotationPrivate
2986 {
2987 public:
2988     CaretAnnotationPrivate();
2989     Annotation *makeAlias() override;
2990     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
2991 
2992     // data fields
2993     CaretAnnotation::CaretSymbol symbol;
2994 };
2995 
CaretAnnotationPrivate()2996 CaretAnnotationPrivate::CaretAnnotationPrivate() : AnnotationPrivate(), symbol(CaretAnnotation::None) { }
2997 
makeAlias()2998 Annotation *CaretAnnotationPrivate::makeAlias()
2999 {
3000     return new CaretAnnotation(*this);
3001 }
3002 
createNativeAnnot(::Page * destPage,DocumentData * doc)3003 Annot *CaretAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
3004 {
3005     // Setters are defined in the public class
3006     CaretAnnotation *q = static_cast<CaretAnnotation *>(makeAlias());
3007 
3008     // Set page and document
3009     pdfPage = destPage;
3010     parentDoc = doc;
3011 
3012     // Set pdfAnnot
3013     PDFRectangle rect = boundaryToPdfRectangle(boundary, flags);
3014     pdfAnnot = new AnnotCaret(destPage->getDoc(), &rect);
3015 
3016     // Set properties
3017     flushBaseAnnotationProperties();
3018     q->setCaretSymbol(symbol);
3019 
3020     delete q;
3021     return pdfAnnot;
3022 }
3023 
CaretAnnotation()3024 CaretAnnotation::CaretAnnotation() : Annotation(*new CaretAnnotationPrivate()) { }
3025 
CaretAnnotation(CaretAnnotationPrivate & dd)3026 CaretAnnotation::CaretAnnotation(CaretAnnotationPrivate &dd) : Annotation(dd) { }
3027 
~CaretAnnotation()3028 CaretAnnotation::~CaretAnnotation() { }
3029 
subType() const3030 Annotation::SubType CaretAnnotation::subType() const
3031 {
3032     return ACaret;
3033 }
3034 
caretSymbol() const3035 CaretAnnotation::CaretSymbol CaretAnnotation::caretSymbol() const
3036 {
3037     Q_D(const CaretAnnotation);
3038 
3039     if (!d->pdfAnnot)
3040         return d->symbol;
3041 
3042     const AnnotCaret *caretann = static_cast<const AnnotCaret *>(d->pdfAnnot);
3043     return (CaretAnnotation::CaretSymbol)caretann->getSymbol();
3044 }
3045 
setCaretSymbol(CaretAnnotation::CaretSymbol symbol)3046 void CaretAnnotation::setCaretSymbol(CaretAnnotation::CaretSymbol symbol)
3047 {
3048     Q_D(CaretAnnotation);
3049 
3050     if (!d->pdfAnnot) {
3051         d->symbol = symbol;
3052         return;
3053     }
3054 
3055     AnnotCaret *caretann = static_cast<AnnotCaret *>(d->pdfAnnot);
3056     caretann->setSymbol((AnnotCaret::AnnotCaretSymbol)symbol);
3057 }
3058 
3059 /** FileAttachmentAnnotation [Annotation] */
3060 class FileAttachmentAnnotationPrivate : public AnnotationPrivate
3061 {
3062 public:
3063     FileAttachmentAnnotationPrivate();
3064     ~FileAttachmentAnnotationPrivate() override;
3065     Annotation *makeAlias() override;
3066     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
3067 
3068     // data fields
3069     QString icon;
3070     EmbeddedFile *embfile;
3071 };
3072 
FileAttachmentAnnotationPrivate()3073 FileAttachmentAnnotationPrivate::FileAttachmentAnnotationPrivate() : AnnotationPrivate(), icon(QStringLiteral("PushPin")), embfile(nullptr) { }
3074 
~FileAttachmentAnnotationPrivate()3075 FileAttachmentAnnotationPrivate::~FileAttachmentAnnotationPrivate()
3076 {
3077     delete embfile;
3078 }
3079 
makeAlias()3080 Annotation *FileAttachmentAnnotationPrivate::makeAlias()
3081 {
3082     return new FileAttachmentAnnotation(*this);
3083 }
3084 
createNativeAnnot(::Page * destPage,DocumentData * doc)3085 Annot *FileAttachmentAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
3086 {
3087     return nullptr; // Not implemented
3088 }
3089 
FileAttachmentAnnotation()3090 FileAttachmentAnnotation::FileAttachmentAnnotation() : Annotation(*new FileAttachmentAnnotationPrivate()) { }
3091 
FileAttachmentAnnotation(FileAttachmentAnnotationPrivate & dd)3092 FileAttachmentAnnotation::FileAttachmentAnnotation(FileAttachmentAnnotationPrivate &dd) : Annotation(dd) { }
3093 
~FileAttachmentAnnotation()3094 FileAttachmentAnnotation::~FileAttachmentAnnotation() { }
3095 
subType() const3096 Annotation::SubType FileAttachmentAnnotation::subType() const
3097 {
3098     return AFileAttachment;
3099 }
3100 
fileIconName() const3101 QString FileAttachmentAnnotation::fileIconName() const
3102 {
3103     Q_D(const FileAttachmentAnnotation);
3104     return d->icon;
3105 }
3106 
setFileIconName(const QString & icon)3107 void FileAttachmentAnnotation::setFileIconName(const QString &icon)
3108 {
3109     Q_D(FileAttachmentAnnotation);
3110     d->icon = icon;
3111 }
3112 
embeddedFile() const3113 EmbeddedFile *FileAttachmentAnnotation::embeddedFile() const
3114 {
3115     Q_D(const FileAttachmentAnnotation);
3116     return d->embfile;
3117 }
3118 
setEmbeddedFile(EmbeddedFile * ef)3119 void FileAttachmentAnnotation::setEmbeddedFile(EmbeddedFile *ef)
3120 {
3121     Q_D(FileAttachmentAnnotation);
3122     d->embfile = ef;
3123 }
3124 
3125 /** SoundAnnotation [Annotation] */
3126 class SoundAnnotationPrivate : public AnnotationPrivate
3127 {
3128 public:
3129     SoundAnnotationPrivate();
3130     ~SoundAnnotationPrivate() override;
3131     Annotation *makeAlias() override;
3132     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
3133 
3134     // data fields
3135     QString icon;
3136     SoundObject *sound;
3137 };
3138 
SoundAnnotationPrivate()3139 SoundAnnotationPrivate::SoundAnnotationPrivate() : AnnotationPrivate(), icon(QStringLiteral("Speaker")), sound(nullptr) { }
3140 
~SoundAnnotationPrivate()3141 SoundAnnotationPrivate::~SoundAnnotationPrivate()
3142 {
3143     delete sound;
3144 }
3145 
makeAlias()3146 Annotation *SoundAnnotationPrivate::makeAlias()
3147 {
3148     return new SoundAnnotation(*this);
3149 }
3150 
createNativeAnnot(::Page * destPage,DocumentData * doc)3151 Annot *SoundAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
3152 {
3153     return nullptr; // Not implemented
3154 }
3155 
SoundAnnotation()3156 SoundAnnotation::SoundAnnotation() : Annotation(*new SoundAnnotationPrivate()) { }
3157 
SoundAnnotation(SoundAnnotationPrivate & dd)3158 SoundAnnotation::SoundAnnotation(SoundAnnotationPrivate &dd) : Annotation(dd) { }
3159 
~SoundAnnotation()3160 SoundAnnotation::~SoundAnnotation() { }
3161 
subType() const3162 Annotation::SubType SoundAnnotation::subType() const
3163 {
3164     return ASound;
3165 }
3166 
soundIconName() const3167 QString SoundAnnotation::soundIconName() const
3168 {
3169     Q_D(const SoundAnnotation);
3170     return d->icon;
3171 }
3172 
setSoundIconName(const QString & icon)3173 void SoundAnnotation::setSoundIconName(const QString &icon)
3174 {
3175     Q_D(SoundAnnotation);
3176     d->icon = icon;
3177 }
3178 
sound() const3179 SoundObject *SoundAnnotation::sound() const
3180 {
3181     Q_D(const SoundAnnotation);
3182     return d->sound;
3183 }
3184 
setSound(SoundObject * s)3185 void SoundAnnotation::setSound(SoundObject *s)
3186 {
3187     Q_D(SoundAnnotation);
3188     d->sound = s;
3189 }
3190 
3191 /** MovieAnnotation [Annotation] */
3192 class MovieAnnotationPrivate : public AnnotationPrivate
3193 {
3194 public:
3195     MovieAnnotationPrivate();
3196     ~MovieAnnotationPrivate() override;
3197     Annotation *makeAlias() override;
3198     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
3199 
3200     // data fields
3201     MovieObject *movie;
3202     QString title;
3203 };
3204 
MovieAnnotationPrivate()3205 MovieAnnotationPrivate::MovieAnnotationPrivate() : AnnotationPrivate(), movie(nullptr) { }
3206 
~MovieAnnotationPrivate()3207 MovieAnnotationPrivate::~MovieAnnotationPrivate()
3208 {
3209     delete movie;
3210 }
3211 
makeAlias()3212 Annotation *MovieAnnotationPrivate::makeAlias()
3213 {
3214     return new MovieAnnotation(*this);
3215 }
3216 
createNativeAnnot(::Page * destPage,DocumentData * doc)3217 Annot *MovieAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
3218 {
3219     return nullptr; // Not implemented
3220 }
3221 
MovieAnnotation()3222 MovieAnnotation::MovieAnnotation() : Annotation(*new MovieAnnotationPrivate()) { }
3223 
MovieAnnotation(MovieAnnotationPrivate & dd)3224 MovieAnnotation::MovieAnnotation(MovieAnnotationPrivate &dd) : Annotation(dd) { }
3225 
~MovieAnnotation()3226 MovieAnnotation::~MovieAnnotation() { }
3227 
subType() const3228 Annotation::SubType MovieAnnotation::subType() const
3229 {
3230     return AMovie;
3231 }
3232 
movie() const3233 MovieObject *MovieAnnotation::movie() const
3234 {
3235     Q_D(const MovieAnnotation);
3236     return d->movie;
3237 }
3238 
setMovie(MovieObject * movie)3239 void MovieAnnotation::setMovie(MovieObject *movie)
3240 {
3241     Q_D(MovieAnnotation);
3242     d->movie = movie;
3243 }
3244 
movieTitle() const3245 QString MovieAnnotation::movieTitle() const
3246 {
3247     Q_D(const MovieAnnotation);
3248     return d->title;
3249 }
3250 
setMovieTitle(const QString & title)3251 void MovieAnnotation::setMovieTitle(const QString &title)
3252 {
3253     Q_D(MovieAnnotation);
3254     d->title = title;
3255 }
3256 
3257 /** ScreenAnnotation [Annotation] */
3258 class ScreenAnnotationPrivate : public AnnotationPrivate
3259 {
3260 public:
3261     ScreenAnnotationPrivate();
3262     ~ScreenAnnotationPrivate() override;
3263     Annotation *makeAlias() override;
3264     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
3265 
3266     // data fields
3267     LinkRendition *action;
3268     QString title;
3269 };
3270 
ScreenAnnotationPrivate()3271 ScreenAnnotationPrivate::ScreenAnnotationPrivate() : AnnotationPrivate(), action(nullptr) { }
3272 
~ScreenAnnotationPrivate()3273 ScreenAnnotationPrivate::~ScreenAnnotationPrivate()
3274 {
3275     delete action;
3276 }
3277 
ScreenAnnotation(ScreenAnnotationPrivate & dd)3278 ScreenAnnotation::ScreenAnnotation(ScreenAnnotationPrivate &dd) : Annotation(dd) { }
3279 
makeAlias()3280 Annotation *ScreenAnnotationPrivate::makeAlias()
3281 {
3282     return new ScreenAnnotation(*this);
3283 }
3284 
createNativeAnnot(::Page * destPage,DocumentData * doc)3285 Annot *ScreenAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
3286 {
3287     return nullptr; // Not implemented
3288 }
3289 
ScreenAnnotation()3290 ScreenAnnotation::ScreenAnnotation() : Annotation(*new ScreenAnnotationPrivate()) { }
3291 
~ScreenAnnotation()3292 ScreenAnnotation::~ScreenAnnotation() { }
3293 
subType() const3294 Annotation::SubType ScreenAnnotation::subType() const
3295 {
3296     return AScreen;
3297 }
3298 
action() const3299 LinkRendition *ScreenAnnotation::action() const
3300 {
3301     Q_D(const ScreenAnnotation);
3302     return d->action;
3303 }
3304 
setAction(LinkRendition * action)3305 void ScreenAnnotation::setAction(LinkRendition *action)
3306 {
3307     Q_D(ScreenAnnotation);
3308     d->action = action;
3309 }
3310 
screenTitle() const3311 QString ScreenAnnotation::screenTitle() const
3312 {
3313     Q_D(const ScreenAnnotation);
3314     return d->title;
3315 }
3316 
setScreenTitle(const QString & title)3317 void ScreenAnnotation::setScreenTitle(const QString &title)
3318 {
3319     Q_D(ScreenAnnotation);
3320     d->title = title;
3321 }
3322 
additionalAction(AdditionalActionType type) const3323 std::unique_ptr<Link> ScreenAnnotation::additionalAction(AdditionalActionType type) const
3324 {
3325     Q_D(const ScreenAnnotation);
3326     return d->additionalAction(type);
3327 }
3328 
3329 /** WidgetAnnotation [Annotation] */
3330 class WidgetAnnotationPrivate : public AnnotationPrivate
3331 {
3332 public:
3333     Annotation *makeAlias() override;
3334     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
3335 };
3336 
makeAlias()3337 Annotation *WidgetAnnotationPrivate::makeAlias()
3338 {
3339     return new WidgetAnnotation(*this);
3340 }
3341 
createNativeAnnot(::Page * destPage,DocumentData * doc)3342 Annot *WidgetAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
3343 {
3344     return nullptr; // Not implemented
3345 }
3346 
WidgetAnnotation(WidgetAnnotationPrivate & dd)3347 WidgetAnnotation::WidgetAnnotation(WidgetAnnotationPrivate &dd) : Annotation(dd) { }
3348 
WidgetAnnotation()3349 WidgetAnnotation::WidgetAnnotation() : Annotation(*new WidgetAnnotationPrivate()) { }
3350 
~WidgetAnnotation()3351 WidgetAnnotation::~WidgetAnnotation() { }
3352 
subType() const3353 Annotation::SubType WidgetAnnotation::subType() const
3354 {
3355     return AWidget;
3356 }
3357 
additionalAction(AdditionalActionType type) const3358 std::unique_ptr<Link> WidgetAnnotation::additionalAction(AdditionalActionType type) const
3359 {
3360     Q_D(const WidgetAnnotation);
3361     return d->additionalAction(type);
3362 }
3363 
3364 /** RichMediaAnnotation [Annotation] */
3365 class RichMediaAnnotation::Params::Private
3366 {
3367 public:
Private()3368     Private() { }
3369 
3370     QString flashVars;
3371 };
3372 
Params()3373 RichMediaAnnotation::Params::Params() : d(new Private) { }
3374 
~Params()3375 RichMediaAnnotation::Params::~Params() { }
3376 
setFlashVars(const QString & flashVars)3377 void RichMediaAnnotation::Params::setFlashVars(const QString &flashVars)
3378 {
3379     d->flashVars = flashVars;
3380 }
3381 
flashVars() const3382 QString RichMediaAnnotation::Params::flashVars() const
3383 {
3384     return d->flashVars;
3385 }
3386 
3387 class RichMediaAnnotation::Instance::Private
3388 {
3389 public:
Private()3390     Private() : params(nullptr) { }
3391 
~Private()3392     ~Private() { delete params; }
3393 
3394     Private(const Private &) = delete;
3395     Private &operator=(const Private &) = delete;
3396 
3397     RichMediaAnnotation::Instance::Type type;
3398     RichMediaAnnotation::Params *params;
3399 };
3400 
Instance()3401 RichMediaAnnotation::Instance::Instance() : d(new Private) { }
3402 
~Instance()3403 RichMediaAnnotation::Instance::~Instance() { }
3404 
setType(Type type)3405 void RichMediaAnnotation::Instance::setType(Type type)
3406 {
3407     d->type = type;
3408 }
3409 
type() const3410 RichMediaAnnotation::Instance::Type RichMediaAnnotation::Instance::type() const
3411 {
3412     return d->type;
3413 }
3414 
setParams(RichMediaAnnotation::Params * params)3415 void RichMediaAnnotation::Instance::setParams(RichMediaAnnotation::Params *params)
3416 {
3417     delete d->params;
3418     d->params = params;
3419 }
3420 
params() const3421 RichMediaAnnotation::Params *RichMediaAnnotation::Instance::params() const
3422 {
3423     return d->params;
3424 }
3425 
3426 class RichMediaAnnotation::Configuration::Private
3427 {
3428 public:
Private()3429     Private() { }
~Private()3430     ~Private()
3431     {
3432         qDeleteAll(instances);
3433         instances.clear();
3434     }
3435 
3436     Private(const Private &) = delete;
3437     Private &operator=(const Private &) = delete;
3438 
3439     RichMediaAnnotation::Configuration::Type type;
3440     QString name;
3441     QList<RichMediaAnnotation::Instance *> instances;
3442 };
3443 
Configuration()3444 RichMediaAnnotation::Configuration::Configuration() : d(new Private) { }
3445 
~Configuration()3446 RichMediaAnnotation::Configuration::~Configuration() { }
3447 
setType(Type type)3448 void RichMediaAnnotation::Configuration::setType(Type type)
3449 {
3450     d->type = type;
3451 }
3452 
type() const3453 RichMediaAnnotation::Configuration::Type RichMediaAnnotation::Configuration::type() const
3454 {
3455     return d->type;
3456 }
3457 
setName(const QString & name)3458 void RichMediaAnnotation::Configuration::setName(const QString &name)
3459 {
3460     d->name = name;
3461 }
3462 
name() const3463 QString RichMediaAnnotation::Configuration::name() const
3464 {
3465     return d->name;
3466 }
3467 
setInstances(const QList<RichMediaAnnotation::Instance * > & instances)3468 void RichMediaAnnotation::Configuration::setInstances(const QList<RichMediaAnnotation::Instance *> &instances)
3469 {
3470     qDeleteAll(d->instances);
3471     d->instances.clear();
3472 
3473     d->instances = instances;
3474 }
3475 
instances() const3476 QList<RichMediaAnnotation::Instance *> RichMediaAnnotation::Configuration::instances() const
3477 {
3478     return d->instances;
3479 }
3480 
3481 class RichMediaAnnotation::Asset::Private
3482 {
3483 public:
Private()3484     Private() : embeddedFile(nullptr) { }
3485 
~Private()3486     ~Private() { delete embeddedFile; }
3487 
3488     Private(const Private &) = delete;
3489     Private &operator=(const Private &) = delete;
3490 
3491     QString name;
3492     EmbeddedFile *embeddedFile;
3493 };
3494 
Asset()3495 RichMediaAnnotation::Asset::Asset() : d(new Private) { }
3496 
~Asset()3497 RichMediaAnnotation::Asset::~Asset() { }
3498 
setName(const QString & name)3499 void RichMediaAnnotation::Asset::setName(const QString &name)
3500 {
3501     d->name = name;
3502 }
3503 
name() const3504 QString RichMediaAnnotation::Asset::name() const
3505 {
3506     return d->name;
3507 }
3508 
setEmbeddedFile(EmbeddedFile * embeddedFile)3509 void RichMediaAnnotation::Asset::setEmbeddedFile(EmbeddedFile *embeddedFile)
3510 {
3511     delete d->embeddedFile;
3512     d->embeddedFile = embeddedFile;
3513 }
3514 
embeddedFile() const3515 EmbeddedFile *RichMediaAnnotation::Asset::embeddedFile() const
3516 {
3517     return d->embeddedFile;
3518 }
3519 
3520 class RichMediaAnnotation::Content::Private
3521 {
3522 public:
Private()3523     Private() { }
~Private()3524     ~Private()
3525     {
3526         qDeleteAll(configurations);
3527         configurations.clear();
3528 
3529         qDeleteAll(assets);
3530         assets.clear();
3531     }
3532 
3533     Private(const Private &) = delete;
3534     Private &operator=(const Private &) = delete;
3535 
3536     QList<RichMediaAnnotation::Configuration *> configurations;
3537     QList<RichMediaAnnotation::Asset *> assets;
3538 };
3539 
Content()3540 RichMediaAnnotation::Content::Content() : d(new Private) { }
3541 
~Content()3542 RichMediaAnnotation::Content::~Content() { }
3543 
setConfigurations(const QList<RichMediaAnnotation::Configuration * > & configurations)3544 void RichMediaAnnotation::Content::setConfigurations(const QList<RichMediaAnnotation::Configuration *> &configurations)
3545 {
3546     qDeleteAll(d->configurations);
3547     d->configurations.clear();
3548 
3549     d->configurations = configurations;
3550 }
3551 
configurations() const3552 QList<RichMediaAnnotation::Configuration *> RichMediaAnnotation::Content::configurations() const
3553 {
3554     return d->configurations;
3555 }
3556 
setAssets(const QList<RichMediaAnnotation::Asset * > & assets)3557 void RichMediaAnnotation::Content::setAssets(const QList<RichMediaAnnotation::Asset *> &assets)
3558 {
3559     qDeleteAll(d->assets);
3560     d->assets.clear();
3561 
3562     d->assets = assets;
3563 }
3564 
assets() const3565 QList<RichMediaAnnotation::Asset *> RichMediaAnnotation::Content::assets() const
3566 {
3567     return d->assets;
3568 }
3569 
3570 class RichMediaAnnotation::Activation::Private
3571 {
3572 public:
Private()3573     Private() : condition(RichMediaAnnotation::Activation::UserAction) { }
3574 
3575     RichMediaAnnotation::Activation::Condition condition;
3576 };
3577 
Activation()3578 RichMediaAnnotation::Activation::Activation() : d(new Private) { }
3579 
~Activation()3580 RichMediaAnnotation::Activation::~Activation() { }
3581 
setCondition(Condition condition)3582 void RichMediaAnnotation::Activation::setCondition(Condition condition)
3583 {
3584     d->condition = condition;
3585 }
3586 
condition() const3587 RichMediaAnnotation::Activation::Condition RichMediaAnnotation::Activation::condition() const
3588 {
3589     return d->condition;
3590 }
3591 
3592 class RichMediaAnnotation::Deactivation::Private : public QSharedData
3593 {
3594 public:
Private()3595     Private() : condition(RichMediaAnnotation::Deactivation::UserAction) { }
3596 
3597     RichMediaAnnotation::Deactivation::Condition condition;
3598 };
3599 
Deactivation()3600 RichMediaAnnotation::Deactivation::Deactivation() : d(new Private) { }
3601 
~Deactivation()3602 RichMediaAnnotation::Deactivation::~Deactivation() { }
3603 
setCondition(Condition condition)3604 void RichMediaAnnotation::Deactivation::setCondition(Condition condition)
3605 {
3606     d->condition = condition;
3607 }
3608 
condition() const3609 RichMediaAnnotation::Deactivation::Condition RichMediaAnnotation::Deactivation::condition() const
3610 {
3611     return d->condition;
3612 }
3613 
3614 class RichMediaAnnotation::Settings::Private : public QSharedData
3615 {
3616 public:
Private()3617     Private() : activation(nullptr), deactivation(nullptr) { }
3618 
3619     RichMediaAnnotation::Activation *activation;
3620     RichMediaAnnotation::Deactivation *deactivation;
3621 };
3622 
Settings()3623 RichMediaAnnotation::Settings::Settings() : d(new Private) { }
3624 
~Settings()3625 RichMediaAnnotation::Settings::~Settings() { }
3626 
setActivation(RichMediaAnnotation::Activation * activation)3627 void RichMediaAnnotation::Settings::setActivation(RichMediaAnnotation::Activation *activation)
3628 {
3629     delete d->activation;
3630     d->activation = activation;
3631 }
3632 
activation() const3633 RichMediaAnnotation::Activation *RichMediaAnnotation::Settings::activation() const
3634 {
3635     return d->activation;
3636 }
3637 
setDeactivation(RichMediaAnnotation::Deactivation * deactivation)3638 void RichMediaAnnotation::Settings::setDeactivation(RichMediaAnnotation::Deactivation *deactivation)
3639 {
3640     delete d->deactivation;
3641     d->deactivation = deactivation;
3642 }
3643 
deactivation() const3644 RichMediaAnnotation::Deactivation *RichMediaAnnotation::Settings::deactivation() const
3645 {
3646     return d->deactivation;
3647 }
3648 
3649 class RichMediaAnnotationPrivate : public AnnotationPrivate
3650 {
3651 public:
RichMediaAnnotationPrivate()3652     RichMediaAnnotationPrivate() : settings(nullptr), content(nullptr) { }
3653 
3654     ~RichMediaAnnotationPrivate() override;
3655 
makeAlias()3656     Annotation *makeAlias() override { return new RichMediaAnnotation(*this); }
3657 
createNativeAnnot(::Page * destPage,DocumentData * doc)3658     Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override
3659     {
3660         Q_UNUSED(destPage);
3661         Q_UNUSED(doc);
3662 
3663         return nullptr;
3664     }
3665 
3666     RichMediaAnnotation::Settings *settings;
3667     RichMediaAnnotation::Content *content;
3668 };
3669 
~RichMediaAnnotationPrivate()3670 RichMediaAnnotationPrivate::~RichMediaAnnotationPrivate()
3671 {
3672     delete settings;
3673     delete content;
3674 }
3675 
RichMediaAnnotation()3676 RichMediaAnnotation::RichMediaAnnotation() : Annotation(*new RichMediaAnnotationPrivate()) { }
3677 
RichMediaAnnotation(RichMediaAnnotationPrivate & dd)3678 RichMediaAnnotation::RichMediaAnnotation(RichMediaAnnotationPrivate &dd) : Annotation(dd) { }
3679 
~RichMediaAnnotation()3680 RichMediaAnnotation::~RichMediaAnnotation() { }
3681 
subType() const3682 Annotation::SubType RichMediaAnnotation::subType() const
3683 {
3684     return ARichMedia;
3685 }
3686 
setSettings(RichMediaAnnotation::Settings * settings)3687 void RichMediaAnnotation::setSettings(RichMediaAnnotation::Settings *settings)
3688 {
3689     Q_D(RichMediaAnnotation);
3690 
3691     delete d->settings;
3692     d->settings = settings;
3693 }
3694 
settings() const3695 RichMediaAnnotation::Settings *RichMediaAnnotation::settings() const
3696 {
3697     Q_D(const RichMediaAnnotation);
3698 
3699     return d->settings;
3700 }
3701 
setContent(RichMediaAnnotation::Content * content)3702 void RichMediaAnnotation::setContent(RichMediaAnnotation::Content *content)
3703 {
3704     Q_D(RichMediaAnnotation);
3705 
3706     delete d->content;
3707     d->content = content;
3708 }
3709 
content() const3710 RichMediaAnnotation::Content *RichMediaAnnotation::content() const
3711 {
3712     Q_D(const RichMediaAnnotation);
3713 
3714     return d->content;
3715 }
3716 
3717 // BEGIN utility annotation functions
convertAnnotColor(const AnnotColor * color)3718 QColor convertAnnotColor(const AnnotColor *color)
3719 {
3720     if (!color)
3721         return QColor();
3722 
3723     QColor newcolor;
3724     const double *color_data = color->getValues();
3725     switch (color->getSpace()) {
3726     case AnnotColor::colorTransparent: // = 0,
3727         newcolor = Qt::transparent;
3728         break;
3729     case AnnotColor::colorGray: // = 1,
3730         newcolor.setRgbF(color_data[0], color_data[0], color_data[0]);
3731         break;
3732     case AnnotColor::colorRGB: // = 3,
3733         newcolor.setRgbF(color_data[0], color_data[1], color_data[2]);
3734         break;
3735     case AnnotColor::colorCMYK: // = 4
3736         newcolor.setCmykF(color_data[0], color_data[1], color_data[2], color_data[3]);
3737         break;
3738     }
3739     return newcolor;
3740 }
3741 
convertQColor(const QColor & c)3742 std::unique_ptr<AnnotColor> convertQColor(const QColor &c)
3743 {
3744     if (c.alpha() == 0)
3745         return {}; // Transparent
3746 
3747     switch (c.spec()) {
3748     case QColor::Rgb:
3749     case QColor::Hsl:
3750     case QColor::Hsv:
3751         return std::make_unique<AnnotColor>(c.redF(), c.greenF(), c.blueF());
3752     case QColor::Cmyk:
3753         return std::make_unique<AnnotColor>(c.cyanF(), c.magentaF(), c.yellowF(), c.blackF());
3754     case QColor::Invalid:
3755     default:
3756         return {};
3757     }
3758 }
3759 // END utility annotation functions
3760 
3761 }
3762