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