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