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