1/* poppler-link.cc: qt interface to poppler
2 * Copyright (C) 2006-2007, 2013, 2016-2021, Albert Astals Cid
3 * Copyright (C) 2007-2008, Pino Toscano <pino@kde.org>
4 * Copyright (C) 2010 Hib Eris <hib@hiberis.nl>
5 * Copyright (C) 2012, Tobias Koenig <tokoe@kdab.com>
6 * Copyright (C) 2012, Guillermo A. Amaral B. <gamaral@kde.org>
7 * Copyright (C) 2018 Intevation GmbH <intevation@intevation.de>
8 * Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de>
9 * Copyright (C) 2020, 2021 Oliver Sander <oliver.sander@tu-dresden.de>
10 * Adapting code from
11 * Copyright (C) 2004 by Enrico Ros <eros.kde@email.it>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2, or (at your option)
16 * any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
26 */
27
28#include <poppler-qt6.h>
29#include <poppler-link-private.h>
30#include <poppler-private.h>
31#include <poppler-media.h>
32
33#include <QtCore/QStringList>
34
35#include "poppler-annotation-private.h"
36
37#include "Link.h"
38#include "Rendition.h"
39
40namespace Poppler {
41
42class LinkDestinationPrivate : public QSharedData
43{
44public:
45 LinkDestinationPrivate();
46
47 LinkDestination::Kind kind; // destination type
48 QString name;
49 int pageNum; // page number
50 double left, bottom; // position
51 double right, top;
52 double zoom; // zoom factor
53 bool changeLeft : 1, changeTop : 1; // for destXYZ links, which position
54 bool changeZoom : 1; // components to change
55};
56
57LinkDestinationPrivate::LinkDestinationPrivate()
58{
59 // sane defaults
60 kind = LinkDestination::destXYZ;
61 pageNum = 0;
62 left = 0;
63 bottom = 0;
64 right = 0;
65 top = 0;
66 zoom = 1;
67 changeLeft = true;
68 changeTop = true;
69 changeZoom = false;
70}
71
72LinkPrivate::~LinkPrivate() = default;
73
74LinkOCGStatePrivate::~LinkOCGStatePrivate() = default;
75
76LinkHidePrivate::~LinkHidePrivate() = default;
77
78class LinkGotoPrivate : public LinkPrivate
79{
80public:
81 LinkGotoPrivate(const QRectF &area, const LinkDestination &dest);
82 ~LinkGotoPrivate() override;
83
84 QString extFileName;
85 LinkDestination destination;
86};
87
88LinkGotoPrivate::LinkGotoPrivate(const QRectF &area, const LinkDestination &dest) : LinkPrivate(area), destination(dest) { }
89
90LinkGotoPrivate::~LinkGotoPrivate() = default;
91
92class LinkExecutePrivate : public LinkPrivate
93{
94public:
95 explicit LinkExecutePrivate(const QRectF &area);
96 ~LinkExecutePrivate() override;
97
98 QString fileName;
99 QString parameters;
100};
101
102LinkExecutePrivate::LinkExecutePrivate(const QRectF &area) : LinkPrivate(area) { }
103
104LinkExecutePrivate::~LinkExecutePrivate() = default;
105
106class LinkBrowsePrivate : public LinkPrivate
107{
108public:
109 explicit LinkBrowsePrivate(const QRectF &area);
110 ~LinkBrowsePrivate() override;
111
112 QString url;
113};
114
115LinkBrowsePrivate::LinkBrowsePrivate(const QRectF &area) : LinkPrivate(area) { }
116
117LinkBrowsePrivate::~LinkBrowsePrivate() = default;
118
119class LinkActionPrivate : public LinkPrivate
120{
121public:
122 explicit LinkActionPrivate(const QRectF &area);
123 ~LinkActionPrivate() override;
124
125 LinkAction::ActionType type;
126};
127
128LinkActionPrivate::LinkActionPrivate(const QRectF &area) : LinkPrivate(area) { }
129
130LinkActionPrivate::~LinkActionPrivate() = default;
131
132class LinkSoundPrivate : public LinkPrivate
133{
134public:
135 explicit LinkSoundPrivate(const QRectF &area);
136 ~LinkSoundPrivate() override;
137
138 double volume;
139 bool sync : 1;
140 bool repeat : 1;
141 bool mix : 1;
142 SoundObject *sound;
143};
144
145LinkSoundPrivate::LinkSoundPrivate(const QRectF &area) : LinkPrivate(area), sound(nullptr) { }
146
147LinkSoundPrivate::~LinkSoundPrivate()
148{
149 delete sound;
150}
151
152class LinkRenditionPrivate : public LinkPrivate
153{
154public:
155 LinkRenditionPrivate(const QRectF &area, ::MediaRendition *rendition, ::LinkRendition::RenditionOperation operation, const QString &script, const Ref ref);
156 ~LinkRenditionPrivate() override;
157
158 MediaRendition *rendition;
159 LinkRendition::RenditionAction action;
160 QString script;
161 Ref annotationReference;
162};
163
164LinkRenditionPrivate::LinkRenditionPrivate(const QRectF &area, ::MediaRendition *r, ::LinkRendition::RenditionOperation operation, const QString &javaScript, const Ref ref)
165 : LinkPrivate(area), rendition(r ? new MediaRendition(r) : nullptr), action(LinkRendition::PlayRendition), script(javaScript), annotationReference(ref)
166{
167 switch (operation) {
168 case ::LinkRendition::NoRendition:
169 action = LinkRendition::NoRendition;
170 break;
171 case ::LinkRendition::PlayRendition:
172 action = LinkRendition::PlayRendition;
173 break;
174 case ::LinkRendition::StopRendition:
175 action = LinkRendition::StopRendition;
176 break;
177 case ::LinkRendition::PauseRendition:
178 action = LinkRendition::PauseRendition;
179 break;
180 case ::LinkRendition::ResumeRendition:
181 action = LinkRendition::ResumeRendition;
182 break;
183 }
184}
185
186LinkRenditionPrivate::~LinkRenditionPrivate()
187{
188 delete rendition;
189}
190
191class LinkJavaScriptPrivate : public LinkPrivate
192{
193public:
194 explicit LinkJavaScriptPrivate(const QRectF &area);
195 ~LinkJavaScriptPrivate() override;
196
197 QString js;
198};
199
200LinkJavaScriptPrivate::LinkJavaScriptPrivate(const QRectF &area) : LinkPrivate(area) { }
201
202LinkJavaScriptPrivate::~LinkJavaScriptPrivate() = default;
203
204class LinkMoviePrivate : public LinkPrivate
205{
206public:
207 LinkMoviePrivate(const QRectF &area, LinkMovie::Operation operation, const QString &title, const Ref reference);
208 ~LinkMoviePrivate() override;
209
210 LinkMovie::Operation operation;
211 QString annotationTitle;
212 Ref annotationReference;
213};
214
215LinkMoviePrivate::LinkMoviePrivate(const QRectF &area, LinkMovie::Operation _operation, const QString &title, const Ref reference) : LinkPrivate(area), operation(_operation), annotationTitle(title), annotationReference(reference) { }
216
217LinkMoviePrivate::~LinkMoviePrivate() = default;
218
219static void cvtUserToDev(::Page *page, double xu, double yu, int *xd, int *yd)
220{
221 double ctm[6];
222
223 page->getDefaultCTM(ctm, hDPI: 72.0, vDPI: 72.0, rotate: 0, useMediaBox: false, upsideDown: true);
224 *xd = (int)(ctm[0] * xu + ctm[2] * yu + ctm[4] + 0.5);
225 *yd = (int)(ctm[1] * xu + ctm[3] * yu + ctm[5] + 0.5);
226}
227
228LinkDestination::LinkDestination(const LinkDestinationData &data) : d(new LinkDestinationPrivate)
229{
230 bool deleteDest = false;
231 const LinkDest *ld = data.ld;
232
233 if (data.namedDest && !ld && !data.externalDest) {
234 deleteDest = true;
235 ld = data.doc->doc->findDest(name: data.namedDest).release();
236 }
237 // in case this destination was named one, and it was not resolved
238 if (data.namedDest && !ld) {
239 d->name = QString::fromLatin1(ba: data.namedDest->c_str());
240 }
241
242 if (!ld) {
243 return;
244 }
245
246 if (ld->getKind() == ::destXYZ) {
247 d->kind = destXYZ;
248 } else if (ld->getKind() == ::destFit) {
249 d->kind = destFit;
250 } else if (ld->getKind() == ::destFitH) {
251 d->kind = destFitH;
252 } else if (ld->getKind() == ::destFitV) {
253 d->kind = destFitV;
254 } else if (ld->getKind() == ::destFitR) {
255 d->kind = destFitR;
256 } else if (ld->getKind() == ::destFitB) {
257 d->kind = destFitB;
258 } else if (ld->getKind() == ::destFitBH) {
259 d->kind = destFitBH;
260 } else if (ld->getKind() == ::destFitBV) {
261 d->kind = destFitBV;
262 }
263
264 if (!ld->isPageRef()) {
265 d->pageNum = ld->getPageNum();
266 } else {
267 const Ref ref = ld->getPageRef();
268 d->pageNum = data.doc->doc->findPage(ref);
269 }
270 double left = ld->getLeft();
271 double bottom = ld->getBottom();
272 double right = ld->getRight();
273 double top = ld->getTop();
274 d->zoom = ld->getZoom();
275 d->changeLeft = ld->getChangeLeft();
276 d->changeTop = ld->getChangeTop();
277 d->changeZoom = ld->getChangeZoom();
278
279 int leftAux = 0, topAux = 0, rightAux = 0, bottomAux = 0;
280
281 if (!data.externalDest) {
282 ::Page *page;
283 if (d->pageNum > 0 && d->pageNum <= data.doc->doc->getNumPages() && (page = data.doc->doc->getPage(page: d->pageNum))) {
284 cvtUserToDev(page, xu: left, yu: top, xd: &leftAux, yd: &topAux);
285 cvtUserToDev(page, xu: right, yu: bottom, xd: &rightAux, yd: &bottomAux);
286
287 d->left = leftAux / (double)page->getCropWidth();
288 d->top = topAux / (double)page->getCropHeight();
289 d->right = rightAux / (double)page->getCropWidth();
290 d->bottom = bottomAux / (double)page->getCropHeight();
291 } else {
292 d->pageNum = 0;
293 }
294 }
295
296 if (deleteDest) {
297 delete ld;
298 }
299}
300
301LinkDestination::LinkDestination(const QString &description) : d(new LinkDestinationPrivate)
302{
303 const QStringList tokens = description.split(sep: ';');
304 if (tokens.size() >= 10) {
305 d->kind = static_cast<Kind>(tokens.at(i: 0).toInt());
306 d->pageNum = tokens.at(i: 1).toInt();
307 d->left = tokens.at(i: 2).toDouble();
308 d->bottom = tokens.at(i: 3).toDouble();
309 d->right = tokens.at(i: 4).toDouble();
310 d->top = tokens.at(i: 5).toDouble();
311 d->zoom = tokens.at(i: 6).toDouble();
312 d->changeLeft = static_cast<bool>(tokens.at(i: 7).toInt());
313 d->changeTop = static_cast<bool>(tokens.at(i: 8).toInt());
314 d->changeZoom = static_cast<bool>(tokens.at(i: 9).toInt());
315 }
316}
317
318LinkDestination::LinkDestination(const LinkDestination &other) : d(other.d) { }
319
320LinkDestination::~LinkDestination() { }
321
322LinkDestination::Kind LinkDestination::kind() const
323{
324 return d->kind;
325}
326
327int LinkDestination::pageNumber() const
328{
329 return d->pageNum;
330}
331
332double LinkDestination::left() const
333{
334 return d->left;
335}
336
337double LinkDestination::bottom() const
338{
339 return d->bottom;
340}
341
342double LinkDestination::right() const
343{
344 return d->right;
345}
346
347double LinkDestination::top() const
348{
349 return d->top;
350}
351
352double LinkDestination::zoom() const
353{
354 return d->zoom;
355}
356
357bool LinkDestination::isChangeLeft() const
358{
359 return d->changeLeft;
360}
361
362bool LinkDestination::isChangeTop() const
363{
364 return d->changeTop;
365}
366
367bool LinkDestination::isChangeZoom() const
368{
369 return d->changeZoom;
370}
371
372QString LinkDestination::toString() const
373{
374 QString s = QString::number((qint8)d->kind);
375 s += ";" + QString::number(d->pageNum);
376 s += ";" + QString::number(d->left);
377 s += ";" + QString::number(d->bottom);
378 s += ";" + QString::number(d->right);
379 s += ";" + QString::number(d->top);
380 s += ";" + QString::number(d->zoom);
381 s += ";" + QString::number((qint8)d->changeLeft);
382 s += ";" + QString::number((qint8)d->changeTop);
383 s += ";" + QString::number((qint8)d->changeZoom);
384 return s;
385}
386
387QString LinkDestination::destinationName() const
388{
389 return d->name;
390}
391
392LinkDestination &LinkDestination::operator=(const LinkDestination &other)
393{
394 if (this == &other) {
395 return *this;
396 }
397
398 d = other.d;
399 return *this;
400}
401
402// Link
403Link::~Link()
404{
405 delete d_ptr;
406}
407
408Link::Link(const QRectF &linkArea) : d_ptr(new LinkPrivate(linkArea)) { }
409
410Link::Link(LinkPrivate &dd) : d_ptr(&dd) { }
411
412Link::LinkType Link::linkType() const
413{
414 return None;
415}
416
417QRectF Link::linkArea() const
418{
419 Q_D(const Link);
420 return d->linkArea;
421}
422
423QVector<Link *> Link::nextLinks() const
424{
425 QVector<Link *> links(d_ptr->nextLinks.size());
426 for (qsizetype i = 0; i < links.size(); i++) {
427 links[i] = d_ptr->nextLinks[i].get();
428 }
429
430 return links;
431}
432
433// LinkGoto
434LinkGoto::LinkGoto(const QRectF &linkArea, const QString &extFileName, const LinkDestination &destination) : Link(*new LinkGotoPrivate(linkArea, destination))
435{
436 Q_D(LinkGoto);
437 d->extFileName = extFileName;
438}
439
440LinkGoto::~LinkGoto() { }
441
442bool LinkGoto::isExternal() const
443{
444 Q_D(const LinkGoto);
445 return !d->extFileName.isEmpty();
446}
447
448QString LinkGoto::fileName() const
449{
450 Q_D(const LinkGoto);
451 return d->extFileName;
452}
453
454LinkDestination LinkGoto::destination() const
455{
456 Q_D(const LinkGoto);
457 return d->destination;
458}
459
460Link::LinkType LinkGoto::linkType() const
461{
462 return Goto;
463}
464
465// LinkExecute
466LinkExecute::LinkExecute(const QRectF &linkArea, const QString &file, const QString &params) : Link(*new LinkExecutePrivate(linkArea))
467{
468 Q_D(LinkExecute);
469 d->fileName = file;
470 d->parameters = params;
471}
472
473LinkExecute::~LinkExecute() { }
474
475QString LinkExecute::fileName() const
476{
477 Q_D(const LinkExecute);
478 return d->fileName;
479}
480QString LinkExecute::parameters() const
481{
482 Q_D(const LinkExecute);
483 return d->parameters;
484}
485
486Link::LinkType LinkExecute::linkType() const
487{
488 return Execute;
489}
490
491// LinkBrowse
492LinkBrowse::LinkBrowse(const QRectF &linkArea, const QString &url) : Link(*new LinkBrowsePrivate(linkArea))
493{
494 Q_D(LinkBrowse);
495 d->url = url;
496}
497
498LinkBrowse::~LinkBrowse() { }
499
500QString LinkBrowse::url() const
501{
502 Q_D(const LinkBrowse);
503 return d->url;
504}
505
506Link::LinkType LinkBrowse::linkType() const
507{
508 return Browse;
509}
510
511// LinkAction
512LinkAction::LinkAction(const QRectF &linkArea, ActionType actionType) : Link(*new LinkActionPrivate(linkArea))
513{
514 Q_D(LinkAction);
515 d->type = actionType;
516}
517
518LinkAction::~LinkAction() { }
519
520LinkAction::ActionType LinkAction::actionType() const
521{
522 Q_D(const LinkAction);
523 return d->type;
524}
525
526Link::LinkType LinkAction::linkType() const
527{
528 return Action;
529}
530
531// LinkSound
532LinkSound::LinkSound(const QRectF &linkArea, double volume, bool sync, bool repeat, bool mix, SoundObject *sound) : Link(*new LinkSoundPrivate(linkArea))
533{
534 Q_D(LinkSound);
535 d->volume = volume;
536 d->sync = sync;
537 d->repeat = repeat;
538 d->mix = mix;
539 d->sound = sound;
540}
541
542LinkSound::~LinkSound() { }
543
544Link::LinkType LinkSound::linkType() const
545{
546 return Sound;
547}
548
549double LinkSound::volume() const
550{
551 Q_D(const LinkSound);
552 return d->volume;
553}
554
555bool LinkSound::synchronous() const
556{
557 Q_D(const LinkSound);
558 return d->sync;
559}
560
561bool LinkSound::repeat() const
562{
563 Q_D(const LinkSound);
564 return d->repeat;
565}
566
567bool LinkSound::mix() const
568{
569 Q_D(const LinkSound);
570 return d->mix;
571}
572
573SoundObject *LinkSound::sound() const
574{
575 Q_D(const LinkSound);
576 return d->sound;
577}
578
579// LinkRendition
580LinkRendition::LinkRendition(const QRectF &linkArea, ::MediaRendition *rendition, int operation, const QString &script, const Ref annotationReference)
581 : Link(*new LinkRenditionPrivate(linkArea, rendition, static_cast<enum ::LinkRendition::RenditionOperation>(operation), script, annotationReference))
582{
583}
584
585LinkRendition::~LinkRendition() { }
586
587Link::LinkType LinkRendition::linkType() const
588{
589 return Rendition;
590}
591
592MediaRendition *LinkRendition::rendition() const
593{
594 Q_D(const LinkRendition);
595 return d->rendition;
596}
597
598LinkRendition::RenditionAction LinkRendition::action() const
599{
600 Q_D(const LinkRendition);
601 return d->action;
602}
603
604QString LinkRendition::script() const
605{
606 Q_D(const LinkRendition);
607 return d->script;
608}
609
610bool LinkRendition::isReferencedAnnotation(const ScreenAnnotation *annotation) const
611{
612 Q_D(const LinkRendition);
613 if (d->annotationReference != Ref::INVALID() && d->annotationReference == annotation->d_ptr->pdfObjectReference()) {
614 return true;
615 }
616
617 return false;
618}
619
620// LinkJavaScript
621LinkJavaScript::LinkJavaScript(const QRectF &linkArea, const QString &js) : Link(*new LinkJavaScriptPrivate(linkArea))
622{
623 Q_D(LinkJavaScript);
624 d->js = js;
625}
626
627LinkJavaScript::~LinkJavaScript() { }
628
629Link::LinkType LinkJavaScript::linkType() const
630{
631 return JavaScript;
632}
633
634QString LinkJavaScript::script() const
635{
636 Q_D(const LinkJavaScript);
637 return d->js;
638}
639
640// LinkMovie
641LinkMovie::LinkMovie(const QRectF &linkArea, Operation operation, const QString &annotationTitle, const Ref annotationReference) : Link(*new LinkMoviePrivate(linkArea, operation, annotationTitle, annotationReference)) { }
642
643LinkMovie::~LinkMovie() { }
644
645Link::LinkType LinkMovie::linkType() const
646{
647 return Movie;
648}
649
650LinkMovie::Operation LinkMovie::operation() const
651{
652 Q_D(const LinkMovie);
653 return d->operation;
654}
655
656bool LinkMovie::isReferencedAnnotation(const MovieAnnotation *annotation) const
657{
658 Q_D(const LinkMovie);
659 if (d->annotationReference != Ref::INVALID() && d->annotationReference == annotation->d_ptr->pdfObjectReference()) {
660 return true;
661 } else if (!d->annotationTitle.isNull()) {
662 return (annotation->movieTitle() == d->annotationTitle);
663 }
664
665 return false;
666}
667
668LinkOCGState::LinkOCGState(LinkOCGStatePrivate *ocgp) : Link(*ocgp) { }
669
670LinkOCGState::~LinkOCGState() { }
671
672Link::LinkType LinkOCGState::linkType() const
673{
674 return OCGState;
675}
676
677// LinkHide
678LinkHide::LinkHide(LinkHidePrivate *lhidep) : Link(*lhidep) { }
679
680LinkHide::~LinkHide() { }
681
682Link::LinkType LinkHide::linkType() const
683{
684 return Hide;
685}
686
687QVector<QString> LinkHide::targets() const
688{
689 Q_D(const LinkHide);
690 return QVector<QString>() << d->targetName;
691}
692
693bool LinkHide::isShowAction() const
694{
695 Q_D(const LinkHide);
696 return d->isShow;
697}
698}
699

source code of poppler/qt6/src/poppler-link.cc