blob: cb114eaecb14e2d27e156b4ffc68604c4cd9877e [file] [log] [blame]
/* poppler-link.cc: qt interface to poppler
* Copyright (C) 2006-2007, 2013, 2016-2021, Albert Astals Cid
* Copyright (C) 2007-2008, Pino Toscano <pino@kde.org>
* Copyright (C) 2010 Hib Eris <hib@hiberis.nl>
* Copyright (C) 2012, Tobias Koenig <tokoe@kdab.com>
* Copyright (C) 2012, Guillermo A. Amaral B. <gamaral@kde.org>
* Copyright (C) 2018 Intevation GmbH <intevation@intevation.de>
* Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de>
* Copyright (C) 2020 Oliver Sander <oliver.sander@tu-dresden.de>
* Adapting code from
* Copyright (C) 2004 by Enrico Ros <eros.kde@email.it>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <poppler-qt5.h>
#include <poppler-link-private.h>
#include <poppler-private.h>
#include <poppler-media.h>
#include <QtCore/QStringList>
#include "poppler-annotation-private.h"
#include "Link.h"
#include "Rendition.h"
namespace Poppler {
class LinkDestinationPrivate : public QSharedData
{
public:
LinkDestinationPrivate();
LinkDestination::Kind kind; // destination type
QString name;
int pageNum; // page number
double left, bottom; // position
double right, top;
double zoom; // zoom factor
bool changeLeft : 1, changeTop : 1; // for destXYZ links, which position
bool changeZoom : 1; // components to change
};
LinkDestinationPrivate::LinkDestinationPrivate()
{
// sane defaults
kind = LinkDestination::destXYZ;
pageNum = 0;
left = 0;
bottom = 0;
right = 0;
top = 0;
zoom = 1;
changeLeft = true;
changeTop = true;
changeZoom = false;
}
LinkPrivate::~LinkPrivate()
{
qDeleteAll(nextLinks);
}
LinkOCGStatePrivate::~LinkOCGStatePrivate() = default;
LinkHidePrivate::~LinkHidePrivate() = default;
class LinkGotoPrivate : public LinkPrivate
{
public:
LinkGotoPrivate(const QRectF &area, const LinkDestination &dest);
~LinkGotoPrivate() override;
QString extFileName;
LinkDestination destination;
};
LinkGotoPrivate::LinkGotoPrivate(const QRectF &area, const LinkDestination &dest) : LinkPrivate(area), destination(dest) { }
LinkGotoPrivate::~LinkGotoPrivate() = default;
class LinkExecutePrivate : public LinkPrivate
{
public:
explicit LinkExecutePrivate(const QRectF &area);
~LinkExecutePrivate() override;
QString fileName;
QString parameters;
};
LinkExecutePrivate::LinkExecutePrivate(const QRectF &area) : LinkPrivate(area) { }
LinkExecutePrivate::~LinkExecutePrivate() = default;
class LinkBrowsePrivate : public LinkPrivate
{
public:
explicit LinkBrowsePrivate(const QRectF &area);
~LinkBrowsePrivate() override;
QString url;
};
LinkBrowsePrivate::LinkBrowsePrivate(const QRectF &area) : LinkPrivate(area) { }
LinkBrowsePrivate::~LinkBrowsePrivate() = default;
class LinkActionPrivate : public LinkPrivate
{
public:
explicit LinkActionPrivate(const QRectF &area);
~LinkActionPrivate() override;
LinkAction::ActionType type;
};
LinkActionPrivate::LinkActionPrivate(const QRectF &area) : LinkPrivate(area) { }
LinkActionPrivate::~LinkActionPrivate() = default;
class LinkSoundPrivate : public LinkPrivate
{
public:
explicit LinkSoundPrivate(const QRectF &area);
~LinkSoundPrivate() override;
double volume;
bool sync : 1;
bool repeat : 1;
bool mix : 1;
SoundObject *sound;
};
LinkSoundPrivate::LinkSoundPrivate(const QRectF &area) : LinkPrivate(area), sound(nullptr) { }
LinkSoundPrivate::~LinkSoundPrivate()
{
delete sound;
}
class LinkRenditionPrivate : public LinkPrivate
{
public:
explicit LinkRenditionPrivate(const QRectF &area, ::MediaRendition *rendition, ::LinkRendition::RenditionOperation operation, const QString &script, const Ref ref);
~LinkRenditionPrivate() override;
MediaRendition *rendition;
LinkRendition::RenditionAction action;
QString script;
Ref annotationReference;
};
LinkRenditionPrivate::LinkRenditionPrivate(const QRectF &area, ::MediaRendition *r, ::LinkRendition::RenditionOperation operation, const QString &javaScript, const Ref ref)
: LinkPrivate(area), rendition(r ? new MediaRendition(r) : nullptr), action(LinkRendition::PlayRendition), script(javaScript), annotationReference(ref)
{
switch (operation) {
case ::LinkRendition::NoRendition:
action = LinkRendition::NoRendition;
break;
case ::LinkRendition::PlayRendition:
action = LinkRendition::PlayRendition;
break;
case ::LinkRendition::StopRendition:
action = LinkRendition::StopRendition;
break;
case ::LinkRendition::PauseRendition:
action = LinkRendition::PauseRendition;
break;
case ::LinkRendition::ResumeRendition:
action = LinkRendition::ResumeRendition;
break;
}
}
LinkRenditionPrivate::~LinkRenditionPrivate()
{
delete rendition;
}
class LinkJavaScriptPrivate : public LinkPrivate
{
public:
explicit LinkJavaScriptPrivate(const QRectF &area);
~LinkJavaScriptPrivate() override;
QString js;
};
LinkJavaScriptPrivate::LinkJavaScriptPrivate(const QRectF &area) : LinkPrivate(area) { }
LinkJavaScriptPrivate::~LinkJavaScriptPrivate() = default;
class LinkMoviePrivate : public LinkPrivate
{
public:
LinkMoviePrivate(const QRectF &area, LinkMovie::Operation operation, const QString &title, const Ref reference);
~LinkMoviePrivate() override;
LinkMovie::Operation operation;
QString annotationTitle;
Ref annotationReference;
};
LinkMoviePrivate::LinkMoviePrivate(const QRectF &area, LinkMovie::Operation _operation, const QString &title, const Ref reference) : LinkPrivate(area), operation(_operation), annotationTitle(title), annotationReference(reference) { }
LinkMoviePrivate::~LinkMoviePrivate() = default;
static void cvtUserToDev(::Page *page, double xu, double yu, int *xd, int *yd)
{
double ctm[6];
page->getDefaultCTM(ctm, 72.0, 72.0, 0, false, true);
*xd = (int)(ctm[0] * xu + ctm[2] * yu + ctm[4] + 0.5);
*yd = (int)(ctm[1] * xu + ctm[3] * yu + ctm[5] + 0.5);
}
LinkDestination::LinkDestination(const LinkDestinationData &data) : d(new LinkDestinationPrivate)
{
bool deleteDest = false;
const LinkDest *ld = data.ld;
if (data.namedDest && !ld && !data.externalDest) {
deleteDest = true;
ld = data.doc->doc->findDest(data.namedDest).release();
}
// in case this destination was named one, and it was not resolved
if (data.namedDest && !ld) {
d->name = QString::fromLatin1(data.namedDest->c_str());
}
if (!ld) {
return;
}
if (ld->getKind() == ::destXYZ) {
d->kind = destXYZ;
} else if (ld->getKind() == ::destFit) {
d->kind = destFit;
} else if (ld->getKind() == ::destFitH) {
d->kind = destFitH;
} else if (ld->getKind() == ::destFitV) {
d->kind = destFitV;
} else if (ld->getKind() == ::destFitR) {
d->kind = destFitR;
} else if (ld->getKind() == ::destFitB) {
d->kind = destFitB;
} else if (ld->getKind() == ::destFitBH) {
d->kind = destFitBH;
} else if (ld->getKind() == ::destFitBV) {
d->kind = destFitBV;
}
if (!ld->isPageRef()) {
d->pageNum = ld->getPageNum();
} else {
const Ref ref = ld->getPageRef();
d->pageNum = data.doc->doc->findPage(ref);
}
double left = ld->getLeft();
double bottom = ld->getBottom();
double right = ld->getRight();
double top = ld->getTop();
d->zoom = ld->getZoom();
d->changeLeft = ld->getChangeLeft();
d->changeTop = ld->getChangeTop();
d->changeZoom = ld->getChangeZoom();
int leftAux = 0, topAux = 0, rightAux = 0, bottomAux = 0;
if (!data.externalDest) {
::Page *page;
if (d->pageNum > 0 && d->pageNum <= data.doc->doc->getNumPages() && (page = data.doc->doc->getPage(d->pageNum))) {
cvtUserToDev(page, left, top, &leftAux, &topAux);
cvtUserToDev(page, right, bottom, &rightAux, &bottomAux);
d->left = leftAux / (double)page->getCropWidth();
d->top = topAux / (double)page->getCropHeight();
d->right = rightAux / (double)page->getCropWidth();
d->bottom = bottomAux / (double)page->getCropHeight();
} else {
d->pageNum = 0;
}
}
if (deleteDest) {
delete ld;
}
}
LinkDestination::LinkDestination(const QString &description) : d(new LinkDestinationPrivate)
{
const QStringList tokens = description.split(';');
if (tokens.size() >= 10) {
d->kind = static_cast<Kind>(tokens.at(0).toInt());
d->pageNum = tokens.at(1).toInt();
d->left = tokens.at(2).toDouble();
d->bottom = tokens.at(3).toDouble();
d->right = tokens.at(4).toDouble();
d->top = tokens.at(5).toDouble();
d->zoom = tokens.at(6).toDouble();
d->changeLeft = static_cast<bool>(tokens.at(7).toInt());
d->changeTop = static_cast<bool>(tokens.at(8).toInt());
d->changeZoom = static_cast<bool>(tokens.at(9).toInt());
}
}
LinkDestination::LinkDestination(const LinkDestination &other) : d(other.d) { }
LinkDestination::~LinkDestination() { }
LinkDestination::Kind LinkDestination::kind() const
{
return d->kind;
}
int LinkDestination::pageNumber() const
{
return d->pageNum;
}
double LinkDestination::left() const
{
return d->left;
}
double LinkDestination::bottom() const
{
return d->bottom;
}
double LinkDestination::right() const
{
return d->right;
}
double LinkDestination::top() const
{
return d->top;
}
double LinkDestination::zoom() const
{
return d->zoom;
}
bool LinkDestination::isChangeLeft() const
{
return d->changeLeft;
}
bool LinkDestination::isChangeTop() const
{
return d->changeTop;
}
bool LinkDestination::isChangeZoom() const
{
return d->changeZoom;
}
QString LinkDestination::toString() const
{
QString s = QString::number((qint8)d->kind);
s += ";" + QString::number(d->pageNum);
s += ";" + QString::number(d->left);
s += ";" + QString::number(d->bottom);
s += ";" + QString::number(d->right);
s += ";" + QString::number(d->top);
s += ";" + QString::number(d->zoom);
s += ";" + QString::number((qint8)d->changeLeft);
s += ";" + QString::number((qint8)d->changeTop);
s += ";" + QString::number((qint8)d->changeZoom);
return s;
}
QString LinkDestination::destinationName() const
{
return d->name;
}
LinkDestination &LinkDestination::operator=(const LinkDestination &other)
{
if (this == &other) {
return *this;
}
d = other.d;
return *this;
}
// Link
Link::~Link()
{
delete d_ptr;
}
Link::Link(const QRectF &linkArea) : d_ptr(new LinkPrivate(linkArea)) { }
Link::Link(LinkPrivate &dd) : d_ptr(&dd) { }
Link::LinkType Link::linkType() const
{
return None;
}
QRectF Link::linkArea() const
{
Q_D(const Link);
return d->linkArea;
}
QVector<Link *> Link::nextLinks() const
{
return d_ptr->nextLinks;
}
// LinkGoto
LinkGoto::LinkGoto(const QRectF &linkArea, QString extFileName, const LinkDestination &destination) // clazy:exclude=function-args-by-ref
: Link(*new LinkGotoPrivate(linkArea, destination))
{
Q_D(LinkGoto);
d->extFileName = std::move(extFileName); // TODO remove when extFileName moves to be a const &
}
LinkGoto::~LinkGoto() { }
bool LinkGoto::isExternal() const
{
Q_D(const LinkGoto);
return !d->extFileName.isEmpty();
}
QString LinkGoto::fileName() const
{
Q_D(const LinkGoto);
return d->extFileName;
}
LinkDestination LinkGoto::destination() const
{
Q_D(const LinkGoto);
return d->destination;
}
Link::LinkType LinkGoto::linkType() const
{
return Goto;
}
// LinkExecute
LinkExecute::LinkExecute(const QRectF &linkArea, const QString &file, const QString &params) : Link(*new LinkExecutePrivate(linkArea))
{
Q_D(LinkExecute);
d->fileName = file;
d->parameters = params;
}
LinkExecute::~LinkExecute() { }
QString LinkExecute::fileName() const
{
Q_D(const LinkExecute);
return d->fileName;
}
QString LinkExecute::parameters() const
{
Q_D(const LinkExecute);
return d->parameters;
}
Link::LinkType LinkExecute::linkType() const
{
return Execute;
}
// LinkBrowse
LinkBrowse::LinkBrowse(const QRectF &linkArea, const QString &url) : Link(*new LinkBrowsePrivate(linkArea))
{
Q_D(LinkBrowse);
d->url = url;
}
LinkBrowse::~LinkBrowse() { }
QString LinkBrowse::url() const
{
Q_D(const LinkBrowse);
return d->url;
}
Link::LinkType LinkBrowse::linkType() const
{
return Browse;
}
// LinkAction
LinkAction::LinkAction(const QRectF &linkArea, ActionType actionType) : Link(*new LinkActionPrivate(linkArea))
{
Q_D(LinkAction);
d->type = actionType;
}
LinkAction::~LinkAction() { }
LinkAction::ActionType LinkAction::actionType() const
{
Q_D(const LinkAction);
return d->type;
}
Link::LinkType LinkAction::linkType() const
{
return Action;
}
// LinkSound
LinkSound::LinkSound(const QRectF &linkArea, double volume, bool sync, bool repeat, bool mix, SoundObject *sound) : Link(*new LinkSoundPrivate(linkArea))
{
Q_D(LinkSound);
d->volume = volume;
d->sync = sync;
d->repeat = repeat;
d->mix = mix;
d->sound = sound;
}
LinkSound::~LinkSound() { }
Link::LinkType LinkSound::linkType() const
{
return Sound;
}
double LinkSound::volume() const
{
Q_D(const LinkSound);
return d->volume;
}
bool LinkSound::synchronous() const
{
Q_D(const LinkSound);
return d->sync;
}
bool LinkSound::repeat() const
{
Q_D(const LinkSound);
return d->repeat;
}
bool LinkSound::mix() const
{
Q_D(const LinkSound);
return d->mix;
}
SoundObject *LinkSound::sound() const
{
Q_D(const LinkSound);
return d->sound;
}
// LinkRendition
LinkRendition::LinkRendition(const QRectF &linkArea, ::MediaRendition *rendition, int operation, const QString &script, const Ref &annotationReference) // clazy:exclude=function-args-by-value
: Link(*new LinkRenditionPrivate(linkArea, rendition, static_cast<enum ::LinkRendition::RenditionOperation>(operation), script, annotationReference))
{
}
LinkRendition::~LinkRendition() { }
Link::LinkType LinkRendition::linkType() const
{
return Rendition;
}
MediaRendition *LinkRendition::rendition() const
{
Q_D(const LinkRendition);
return d->rendition;
}
LinkRendition::RenditionAction LinkRendition::action() const
{
Q_D(const LinkRendition);
return d->action;
}
QString LinkRendition::script() const
{
Q_D(const LinkRendition);
return d->script;
}
bool LinkRendition::isReferencedAnnotation(const ScreenAnnotation *annotation) const
{
Q_D(const LinkRendition);
if (d->annotationReference != Ref::INVALID() && d->annotationReference == annotation->d_ptr->pdfObjectReference()) {
return true;
}
return false;
}
// LinkJavaScript
LinkJavaScript::LinkJavaScript(const QRectF &linkArea, const QString &js) : Link(*new LinkJavaScriptPrivate(linkArea))
{
Q_D(LinkJavaScript);
d->js = js;
}
LinkJavaScript::~LinkJavaScript() { }
Link::LinkType LinkJavaScript::linkType() const
{
return JavaScript;
}
QString LinkJavaScript::script() const
{
Q_D(const LinkJavaScript);
return d->js;
}
// LinkMovie
LinkMovie::LinkMovie(const QRectF &linkArea, Operation operation, const QString &annotationTitle, const Ref &annotationReference) // clazy:exclude=function-args-by-value
: Link(*new LinkMoviePrivate(linkArea, operation, annotationTitle, annotationReference))
{
}
LinkMovie::~LinkMovie() { }
Link::LinkType LinkMovie::linkType() const
{
return Movie;
}
LinkMovie::Operation LinkMovie::operation() const
{
Q_D(const LinkMovie);
return d->operation;
}
bool LinkMovie::isReferencedAnnotation(const MovieAnnotation *annotation) const
{
Q_D(const LinkMovie);
if (d->annotationReference != Ref::INVALID() && d->annotationReference == annotation->d_ptr->pdfObjectReference()) {
return true;
} else if (!d->annotationTitle.isNull()) {
return (annotation->movieTitle() == d->annotationTitle);
}
return false;
}
LinkOCGState::LinkOCGState(LinkOCGStatePrivate *ocgp) : Link(*ocgp) { }
LinkOCGState::~LinkOCGState() { }
Link::LinkType LinkOCGState::linkType() const
{
return OCGState;
}
// LinkHide
LinkHide::LinkHide(LinkHidePrivate *lhidep) : Link(*lhidep) { }
LinkHide::~LinkHide() { }
Link::LinkType LinkHide::linkType() const
{
return Hide;
}
QVector<QString> LinkHide::targets() const
{
Q_D(const LinkHide);
return QVector<QString>() << d->targetName;
}
bool LinkHide::isShowAction() const
{
Q_D(const LinkHide);
return d->isShow;
}
}