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