aboutsummaryrefslogtreecommitdiff
path: root/xpdf-qt/QtPDFCore.cc
diff options
context:
space:
mode:
authorCalvin Morrison <calvin@pobox.com>2023-04-05 14:13:39 -0400
committerCalvin Morrison <calvin@pobox.com>2023-04-05 14:13:39 -0400
commit835e373b3eeaabcd0621ed6798ab500f37982fae (patch)
treedfa16b0e2e1b4956b38f693220eac4e607802133 /xpdf-qt/QtPDFCore.cc
xpdf-no-select-disableHEADmaster
Diffstat (limited to 'xpdf-qt/QtPDFCore.cc')
-rw-r--r--xpdf-qt/QtPDFCore.cc1176
1 files changed, 1176 insertions, 0 deletions
diff --git a/xpdf-qt/QtPDFCore.cc b/xpdf-qt/QtPDFCore.cc
new file mode 100644
index 0000000..e945f88
--- /dev/null
+++ b/xpdf-qt/QtPDFCore.cc
@@ -0,0 +1,1176 @@
+//========================================================================
+//
+// QtPDFCore.cc
+//
+// Copyright 2009-2014 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <math.h>
+#include <string.h>
+#include <QApplication>
+#include <QClipboard>
+#include <QDesktopServices>
+#include <QFileInfo>
+#include <QImage>
+#include <QInputDialog>
+#include <QMessageBox>
+#include <QPainter>
+#include <QProcess>
+#include <QScrollBar>
+#include <QStyle>
+#include <QUrl>
+#include <QWidget>
+#include "gmem.h"
+#include "gmempp.h"
+#include "gfile.h"
+#include "GString.h"
+#include "GList.h"
+#include "Error.h"
+#include "GlobalParams.h"
+#include "PDFDoc.h"
+#include "Link.h"
+#include "ErrorCodes.h"
+#include "GfxState.h"
+#include "PSOutputDev.h"
+#include "TextOutputDev.h"
+#include "SplashBitmap.h"
+#include "DisplayState.h"
+#include "TileMap.h"
+#include "QtPDFCore.h"
+
+//------------------------------------------------------------------------
+// QtPDFCore
+//------------------------------------------------------------------------
+
+QtPDFCore::QtPDFCore(QWidget *viewportA,
+ QScrollBar *hScrollBarA, QScrollBar *vScrollBarA,
+ SplashColorPtr paperColor, SplashColorPtr matteColor,
+ GBool reverseVideo):
+ PDFCore(splashModeRGB8, 4, reverseVideo, paperColor)
+{
+ int dpiX, dpiY;
+
+ viewport = viewportA;
+ hScrollBar = hScrollBarA;
+ vScrollBar = vScrollBarA;
+ hScrollBar->setRange(0, 0);
+ hScrollBar->setSingleStep(16);
+ vScrollBar->setRange(0, 0);
+ vScrollBar->setSingleStep(16);
+ viewport->setMouseTracking(true);
+
+ state->setMatteColor(matteColor);
+
+ oldFirstPage = -1;
+ oldMidPage = -1;
+
+ linkAction = NULL;
+ lastLinkAction = NULL;
+
+ dragging = gFalse;
+
+ panning = gFalse;
+
+ inUpdateScrollbars = gFalse;
+
+ updateCbk = NULL;
+ midPageChangedCbk = NULL;
+ preLoadCbk = NULL;
+ postLoadCbk = NULL;
+ actionCbk = NULL;
+ linkCbk = NULL;
+ selectDoneCbk = NULL;
+
+ // optional features default to on
+ hyperlinksEnabled = gTrue;
+ externalHyperlinksEnabled = gTrue;
+ selectEnabled = gTrue;
+ panEnabled = gTrue;
+ showPasswordDialog = gTrue;
+
+ // get Qt's HiDPI scale factor
+#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
+ scaleFactor = viewport->devicePixelRatioF();
+#elif QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
+ scaleFactor = viewport->devicePixelRatio();
+#else
+ scaleFactor = 1;
+#endif
+
+ // get the display resolution (used for HiDPI scaling)
+ dpiX = viewport->logicalDpiX();
+ dpiY = viewport->logicalDpiY();
+ displayDpi = dpiX < dpiY ? dpiX : dpiY;
+ displayDpi = (int)(displayDpi * scaleFactor);
+}
+
+QtPDFCore::~QtPDFCore() {
+}
+
+//------------------------------------------------------------------------
+// loadFile / displayPage / displayDest
+//------------------------------------------------------------------------
+
+int QtPDFCore::loadFile(GString *fileName, GString *ownerPassword,
+ GString *userPassword) {
+ int err;
+
+ err = PDFCore::loadFile(fileName, ownerPassword, userPassword);
+ if (err == errNone) {
+ // save the modification time
+ modTime = QFileInfo(doc->getFileName()->getCString()).lastModified();
+
+ // update the parent window
+ if (updateCbk) {
+ (*updateCbk)(updateCbkData, doc->getFileName(), -1,
+ doc->getNumPages(), NULL);
+ }
+ oldFirstPage = oldMidPage = -1;
+ }
+ return err;
+}
+
+#ifdef _WIN32
+int QtPDFCore::loadFile(wchar_t *fileName, int fileNameLen,
+ GString *ownerPassword,
+ GString *userPassword) {
+ int err;
+
+ err = PDFCore::loadFile(fileName, fileNameLen, ownerPassword, userPassword);
+ if (err == errNone) {
+ // save the modification time
+ modTime = QFileInfo(doc->getFileName()->getCString()).lastModified();
+
+ // update the parent window
+ if (updateCbk) {
+ (*updateCbk)(updateCbkData, doc->getFileName(), -1,
+ doc->getNumPages(), NULL);
+ }
+ oldFirstPage = oldMidPage = -1;
+ }
+ return err;
+}
+#endif
+
+int QtPDFCore::loadFile(BaseStream *stream, GString *ownerPassword,
+ GString *userPassword) {
+ int err;
+
+ err = PDFCore::loadFile(stream, ownerPassword, userPassword);
+ if (err == errNone) {
+ // no file
+ modTime = QDateTime();
+
+ // update the parent window
+ if (updateCbk) {
+ (*updateCbk)(updateCbkData, doc->getFileName(), -1,
+ doc->getNumPages(), NULL);
+ }
+ oldFirstPage = oldMidPage = -1;
+ }
+ return err;
+}
+
+void QtPDFCore::loadDoc(PDFDoc *docA) {
+ PDFCore::loadDoc(docA);
+
+ // save the modification time
+ if (doc->getFileName()) {
+ modTime = QFileInfo(doc->getFileName()->getCString()).lastModified();
+ } else {
+ modTime = QDateTime();
+ }
+
+ // update the parent window
+ if (updateCbk) {
+ (*updateCbk)(updateCbkData, doc->getFileName(), -1,
+ doc->getNumPages(), NULL);
+ }
+ oldFirstPage = oldMidPage = -1;
+}
+
+int QtPDFCore::reload() {
+ int err;
+
+ err = PDFCore::reload();
+ if (err == errNone) {
+ // save the modification time
+ modTime = QFileInfo(doc->getFileName()->getCString()).lastModified();
+
+ // update the parent window
+ if (updateCbk) {
+ (*updateCbk)(updateCbkData, doc->getFileName(), -1,
+ doc->getNumPages(), NULL);
+ }
+ oldFirstPage = oldMidPage = -1;
+ }
+ return err;
+}
+
+void QtPDFCore::finishUpdate(GBool addToHist, GBool checkForChangedFile) {
+ int firstPage, midPage;
+
+ PDFCore::finishUpdate(addToHist, checkForChangedFile);
+ firstPage = getPageNum();
+ if (doc && firstPage != oldFirstPage && updateCbk) {
+ (*updateCbk)(updateCbkData, NULL, firstPage, -1, "");
+ }
+ oldFirstPage = firstPage;
+ midPage = getMidPageNum();
+ if (doc && midPage != oldMidPage && midPageChangedCbk) {
+ (*midPageChangedCbk)(midPageChangedCbkData, midPage);
+ }
+ oldMidPage = midPage;
+
+ linkAction = NULL;
+ lastLinkAction = NULL;
+}
+
+//------------------------------------------------------------------------
+// panning and selection
+//------------------------------------------------------------------------
+
+void QtPDFCore::startPan(int wx, int wy) {
+ if (!panEnabled) {
+ return;
+ }
+ panning = gTrue;
+ panMX = wx;
+ panMY = wy;
+}
+
+void QtPDFCore::endPan(int wx, int wy) {
+ panning = gFalse;
+}
+
+void QtPDFCore::startSelection(int wx, int wy, GBool extend) {
+ int pg, x, y;
+
+ takeFocus();
+ if (!doc || doc->getNumPages() == 0 || !selectEnabled) {
+ return;
+ }
+ if (!cvtWindowToDev(wx, wy, &pg, &x, &y)) {
+ return;
+ }
+ if (extend && hasSelection()) {
+ moveSelectionDrag(pg, x, y);
+ } else {
+ startSelectionDrag(pg, x, y);
+ }
+ if (getSelectMode() == selectModeBlock) {
+ doSetCursor(Qt::CrossCursor);
+ }
+ dragging = gTrue;
+}
+
+void QtPDFCore::endSelection(int wx, int wy) {
+ LinkAction *action;
+ int pg, x, y;
+ double xu, yu;
+ GBool ok;
+
+ if (!doc || doc->getNumPages() == 0) {
+ return;
+ }
+ ok = cvtWindowToDev(wx, wy, &pg, &x, &y);
+ if (dragging) {
+ dragging = gFalse;
+ doUnsetCursor();
+ if (ok) {
+ moveSelectionDrag(pg, x, y);
+ }
+ finishSelectionDrag();
+ if (selectDoneCbk) {
+ (*selectDoneCbk)(selectDoneCbkData);
+ }
+#ifndef NO_TEXT_SELECT
+ if (hasSelection()) {
+ copySelection(gFalse);
+ }
+#endif
+ }
+ if (ok) {
+ if (hasSelection()) {
+ action = NULL;
+ } else {
+ cvtDevToUser(pg, x, y, &xu, &yu);
+ action = findLink(pg, xu, yu);
+ }
+ if (linkCbk && action) {
+ doLinkCbk(action);
+ }
+ if (hyperlinksEnabled && action) {
+ doAction(action);
+ }
+ }
+}
+
+void QtPDFCore::mouseMove(int wx, int wy) {
+ LinkAction *action;
+ int pg, x, y;
+ double xu, yu;
+ const char *s;
+ GBool ok, mouseOverText;
+
+ if (!doc || doc->getNumPages() == 0) {
+ return;
+ }
+ ok = cvtWindowToDev(wx, wy, &pg, &x, &y);
+ if (dragging) {
+ if (ok) {
+ moveSelectionDrag(pg, x, y);
+ }
+ } else {
+ cvtDevToUser(pg, x, y, &xu, &yu);
+
+ // check for a link
+ action = NULL;
+ if (hyperlinksEnabled && ok) {
+ action = findLink(pg, xu, yu);
+ }
+
+ // check for text
+ mouseOverText = gFalse;
+ if (!action && getSelectMode() == selectModeLinear && ok) {
+ mouseOverText = overText(pg, x, y);
+ }
+
+ // update the cursor
+ if (action) {
+ doSetCursor(Qt::PointingHandCursor);
+ } else if (mouseOverText) {
+ doSetCursor(Qt::IBeamCursor);
+ } else {
+ doUnsetCursor();
+ }
+
+ // update the link info
+ if (action != linkAction) {
+ linkAction = action;
+ if (updateCbk) {
+ //~ should pass a QString to updateCbk()
+ if (linkAction) {
+ s = getLinkInfo(linkAction).toLocal8Bit().constData();
+ } else {
+ s = "";
+ }
+ (*updateCbk)(updateCbkData, NULL, -1, -1, s);
+ }
+ }
+ }
+
+ if (panning) {
+ scrollTo(getScrollX() - (wx - panMX),
+ getScrollY() - (wy - panMY));
+ panMX = wx;
+ panMY = wy;
+ }
+}
+
+void QtPDFCore::selectWord(int wx, int wy) {
+ int pg, x, y;
+
+ takeFocus();
+ if (!doc || doc->getNumPages() == 0 || !selectEnabled) {
+ return;
+ }
+ if (getSelectMode() != selectModeLinear) {
+ return;
+ }
+ if (!cvtWindowToDev(wx, wy, &pg, &x, &y)) {
+ return;
+ }
+ PDFCore::selectWord(pg, x, y);
+#ifndef NO_TEXT_SELECT
+ if (hasSelection()) {
+ copySelection(gFalse);
+ }
+#endif
+}
+
+void QtPDFCore::selectLine(int wx, int wy) {
+ int pg, x, y;
+
+ takeFocus();
+ if (!doc || doc->getNumPages() == 0 || !selectEnabled) {
+ return;
+ }
+ if (getSelectMode() != selectModeLinear) {
+ return;
+ }
+ if (!cvtWindowToDev(wx, wy, &pg, &x, &y)) {
+ return;
+ }
+ PDFCore::selectLine(pg, x, y);
+#ifndef NO_TEXT_SELECT
+ if (hasSelection()) {
+ copySelection(gFalse);
+ }
+#endif
+}
+
+void QtPDFCore::doLinkCbk(LinkAction *action) {
+ LinkDest *dest;
+ GString *namedDest;
+ Ref pageRef;
+ int pg;
+ GString *cmd, *params;
+ char *s;
+
+ if (!linkCbk) {
+ return;
+ }
+
+ switch (action->getKind()) {
+
+ case actionGoTo:
+ dest = NULL;
+ if ((dest = ((LinkGoTo *)action)->getDest())) {
+ dest = dest->copy();
+ } else if ((namedDest = ((LinkGoTo *)action)->getNamedDest())) {
+ dest = doc->findDest(namedDest);
+ }
+ pg = 0;
+ if (dest) {
+ if (dest->isPageRef()) {
+ pageRef = dest->getPageRef();
+ pg = doc->findPage(pageRef.num, pageRef.gen);
+ } else {
+ pg = dest->getPageNum();
+ }
+ delete dest;
+ }
+ (*linkCbk)(linkCbkData, "goto", NULL, pg);
+ break;
+
+ case actionGoToR:
+ (*linkCbk)(linkCbkData, "pdf",
+ ((LinkGoToR *)action)->getFileName()->getCString(), 0);
+ break;
+
+ case actionLaunch:
+ cmd = ((LinkLaunch *)action)->getFileName()->copy();
+ s = cmd->getCString();
+ if (strcmp(s + cmd->getLength() - 4, ".pdf") &&
+ strcmp(s + cmd->getLength() - 4, ".PDF") &&
+ (params = ((LinkLaunch *)action)->getParams())) {
+ cmd->append(' ')->append(params);
+ }
+ (*linkCbk)(linkCbkData, "launch", cmd->getCString(), 0);
+ delete cmd;
+ break;
+
+ case actionURI:
+ (*linkCbk)(linkCbkData, "url",
+ ((LinkURI *)action)->getURI()->getCString(), 0);
+ break;
+
+ case actionNamed:
+ (*linkCbk)(linkCbkData, "named",
+ ((LinkNamed *)action)->getName()->getCString(), 0);
+ break;
+
+ case actionMovie:
+ case actionJavaScript:
+ case actionSubmitForm:
+ case actionHide:
+ case actionUnknown:
+ (*linkCbk)(linkCbkData, "unknown", NULL, 0);
+ break;
+ }
+}
+
+QString QtPDFCore::getSelectedTextQString() {
+ GString *s, *enc;
+ QString qs;
+ int i;
+
+ if (!(s = getSelectedText())) {
+ return "";
+ }
+ enc = globalParams->getTextEncodingName();
+ if (!enc->cmp("UTF-8")) {
+ qs = QString::fromUtf8(s->getCString());
+ } else if (!enc->cmp("UCS-2")) {
+ for (i = 0; i+1 < s->getLength(); i += 2) {
+ qs.append((QChar)(((s->getChar(i) & 0xff) << 8) +
+ (s->getChar(i+1) & 0xff)));
+ }
+ } else {
+ qs = QString(s->getCString());
+ }
+ delete s;
+ delete enc;
+ return qs;
+}
+
+void QtPDFCore::copySelection(GBool toClipboard) {
+ QString qs;
+
+ if (hasSelection()) {
+ QApplication::clipboard()->setText(getSelectedTextQString(),
+ toClipboard ? QClipboard::Clipboard
+ : QClipboard::Selection);
+ }
+}
+
+//------------------------------------------------------------------------
+// hyperlinks
+//------------------------------------------------------------------------
+
+GBool QtPDFCore::doAction(LinkAction *action) {
+ LinkActionKind kind;
+ LinkDest *dest;
+ GString *namedDest;
+ char *s;
+ GString *fileName, *fileName2, *params;
+ GString *cmd;
+ GString *actionName;
+ Object movieAnnot, obj1, obj2;
+ GString *msg;
+ int i;
+
+ switch (kind = action->getKind()) {
+
+ // GoTo / GoToR action
+ case actionGoTo:
+ case actionGoToR:
+ if (kind == actionGoTo) {
+ dest = NULL;
+ namedDest = NULL;
+ if ((dest = ((LinkGoTo *)action)->getDest())) {
+ dest = dest->copy();
+ } else if ((namedDest = ((LinkGoTo *)action)->getNamedDest())) {
+ namedDest = namedDest->copy();
+ }
+ } else {
+ if (!externalHyperlinksEnabled) {
+ return gFalse;
+ }
+ dest = NULL;
+ namedDest = NULL;
+ if ((dest = ((LinkGoToR *)action)->getDest())) {
+ dest = dest->copy();
+ } else if ((namedDest = ((LinkGoToR *)action)->getNamedDest())) {
+ namedDest = namedDest->copy();
+ }
+ s = ((LinkGoToR *)action)->getFileName()->getCString();
+ if (isAbsolutePath(s)) {
+ fileName = new GString(s);
+ } else {
+ fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);
+ }
+ if (loadFile(fileName) != errNone) {
+ if (dest) {
+ delete dest;
+ }
+ if (namedDest) {
+ delete namedDest;
+ }
+ delete fileName;
+ return gFalse;
+ }
+ delete fileName;
+ }
+ if (namedDest) {
+ dest = doc->findDest(namedDest);
+ delete namedDest;
+ }
+ if (dest) {
+ displayDest(dest);
+ delete dest;
+ } else {
+ if (kind == actionGoToR) {
+ displayPage(1, gFalse, gFalse, gTrue);
+ }
+ }
+ break;
+
+ // Launch action
+ case actionLaunch:
+ if (!externalHyperlinksEnabled) {
+ return gFalse;
+ }
+ fileName = ((LinkLaunch *)action)->getFileName();
+ s = fileName->getCString();
+ if (fileName->getLength() >= 4 &&
+ (!strcmp(s + fileName->getLength() - 4, ".pdf") ||
+ !strcmp(s + fileName->getLength() - 4, ".PDF"))) {
+ if (isAbsolutePath(s)) {
+ fileName = fileName->copy();
+ } else {
+ fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);
+ }
+ if (loadFile(fileName) != errNone) {
+ delete fileName;
+ return gFalse;
+ }
+ delete fileName;
+ displayPage(1, gFalse, gFalse, gTrue);
+ } else {
+ cmd = fileName->copy();
+ if ((params = ((LinkLaunch *)action)->getParams())) {
+ cmd->append(' ')->append(params);
+ }
+ if (globalParams->getLaunchCommand()) {
+ cmd->insert(0, ' ');
+ cmd->insert(0, globalParams->getLaunchCommand());
+#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
+ QString cmdStr(cmd->getCString());
+ QStringList tokens = QProcess::splitCommand(cmdStr);
+ if (!tokens.isEmpty()) {
+ QString program = tokens[0];
+ tokens.removeFirst();
+ QProcess::startDetached(program, tokens);
+ }
+#else
+ QProcess::startDetached(cmd->getCString());
+#endif
+ } else {
+ msg = new GString("About to execute the command:\n");
+ msg->append(cmd);
+ if (QMessageBox::question(viewport, "PDF Launch Link",
+ msg->getCString(),
+ QMessageBox::Ok | QMessageBox::Cancel,
+ QMessageBox::Ok)
+ == QMessageBox::Ok) {
+#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
+ QString cmdStr(cmd->getCString());
+ QStringList tokens = QProcess::splitCommand(cmdStr);
+ if (!tokens.isEmpty()) {
+ QString program = tokens[0];
+ tokens.removeFirst();
+ QProcess::startDetached(program, tokens);
+ }
+#else
+ QProcess::startDetached(cmd->getCString());
+#endif
+ }
+ delete msg;
+ }
+ delete cmd;
+ }
+ break;
+
+ // URI action
+ case actionURI:
+ if (!externalHyperlinksEnabled) {
+ return gFalse;
+ }
+ QDesktopServices::openUrl(QUrl(((LinkURI *)action)->getURI()->getCString(),
+ QUrl::TolerantMode));
+ break;
+
+ // Named action
+ case actionNamed:
+ actionName = ((LinkNamed *)action)->getName();
+ if (!actionName->cmp("NextPage")) {
+ gotoNextPage(1, gTrue);
+ } else if (!actionName->cmp("PrevPage")) {
+ gotoPrevPage(1, gTrue, gFalse);
+ } else if (!actionName->cmp("FirstPage")) {
+ displayPage(1, gTrue, gFalse, gTrue);
+ } else if (!actionName->cmp("LastPage")) {
+ displayPage(doc->getNumPages(), gTrue, gFalse, gTrue);
+ } else if (!actionName->cmp("GoBack")) {
+ goBackward();
+ } else if (!actionName->cmp("GoForward")) {
+ goForward();
+ } else if (!actionName->cmp("Quit")) {
+ if (actionCbk) {
+ (*actionCbk)(actionCbkData, actionName->getCString());
+ }
+ } else {
+ error(errSyntaxError, -1,
+ "Unknown named action: '{0:t}'", actionName);
+ return gFalse;
+ }
+ break;
+
+ // Movie action
+ case actionMovie:
+ if (!externalHyperlinksEnabled) {
+ return gFalse;
+ }
+ if (!(cmd = globalParams->getMovieCommand())) {
+ error(errConfig, -1, "No movieCommand defined in config file");
+ return gFalse;
+ }
+ if (((LinkMovie *)action)->hasAnnotRef()) {
+ doc->getXRef()->fetch(((LinkMovie *)action)->getAnnotRef()->num,
+ ((LinkMovie *)action)->getAnnotRef()->gen,
+ &movieAnnot);
+ } else {
+ //~ need to use the correct page num here
+ doc->getCatalog()->getPage(tileMap->getFirstPage())->getAnnots(&obj1);
+ if (obj1.isArray()) {
+ for (i = 0; i < obj1.arrayGetLength(); ++i) {
+ if (obj1.arrayGet(i, &movieAnnot)->isDict()) {
+ if (movieAnnot.dictLookup("Subtype", &obj2)->isName("Movie")) {
+ obj2.free();
+ break;
+ }
+ obj2.free();
+ }
+ movieAnnot.free();
+ }
+ obj1.free();
+ }
+ }
+ if (movieAnnot.isDict()) {
+ if (movieAnnot.dictLookup("Movie", &obj1)->isDict()) {
+ if (obj1.dictLookup("F", &obj2)) {
+ if ((fileName = LinkAction::getFileSpecName(&obj2))) {
+ if (!isAbsolutePath(fileName->getCString())) {
+ fileName2 = appendToPath(
+ grabPath(doc->getFileName()->getCString()),
+ fileName->getCString());
+ delete fileName;
+ fileName = fileName2;
+ }
+ runCommand(cmd, fileName);
+ delete fileName;
+ }
+ obj2.free();
+ }
+ obj1.free();
+ }
+ }
+ movieAnnot.free();
+ break;
+
+ // unimplemented actions
+ case actionJavaScript:
+ case actionSubmitForm:
+ case actionHide:
+ return gFalse;
+
+ // unknown action type
+ case actionUnknown:
+ error(errSyntaxError, -1, "Unknown link action type: '{0:t}'",
+ ((LinkUnknown *)action)->getAction());
+ return gFalse;
+ }
+
+ return gTrue;
+}
+
+QString QtPDFCore::getLinkInfo(LinkAction *action) {
+ LinkDest *dest;
+ GString *namedDest;
+ Ref pageRef;
+ int pg;
+ QString info;
+
+ if (action == lastLinkAction && !lastLinkActionInfo.isEmpty()) {
+ return lastLinkActionInfo;
+ }
+
+ switch (action->getKind()) {
+ case actionGoTo:
+ dest = NULL;
+ if ((dest = ((LinkGoTo *)action)->getDest())) {
+ dest = dest->copy();
+ } else if ((namedDest = ((LinkGoTo *)action)->getNamedDest())) {
+ dest = doc->findDest(namedDest);
+ }
+ pg = 0;
+ if (dest) {
+ if (dest->isPageRef()) {
+ pageRef = dest->getPageRef();
+ pg = doc->findPage(pageRef.num, pageRef.gen);
+ } else {
+ pg = dest->getPageNum();
+ }
+ delete dest;
+ }
+ if (pg) {
+ info = QString("[page ") + QString::number(pg) + QString("]");
+ } else {
+ info = "[internal]";
+ }
+ break;
+ case actionGoToR:
+ info = QString(((LinkGoToR *)action)->getFileName()->getCString());
+ break;
+ case actionLaunch:
+ info = QString(((LinkLaunch *)action)->getFileName()->getCString());
+ break;
+ case actionURI:
+ info = QString(((LinkURI *)action)->getURI()->getCString());
+ break;
+ case actionNamed:
+ info = QString(((LinkNamed *)action)->getName()->getCString());
+ break;
+ case actionMovie:
+ info = "[movie]";
+ break;
+ case actionJavaScript:
+ case actionSubmitForm:
+ case actionHide:
+ case actionUnknown:
+ default:
+ info = "[unknown]";
+ break;
+ }
+
+ lastLinkAction = action;
+ lastLinkActionInfo = info;
+
+ return info;
+}
+
+// Run a command, given a <cmdFmt> string with one '%s' in it, and an
+// <arg> string to insert in place of the '%s'.
+void QtPDFCore::runCommand(GString *cmdFmt, GString *arg) {
+ GString *cmd;
+ char *s;
+
+ if ((s = strstr(cmdFmt->getCString(), "%s"))) {
+ cmd = mungeURL(arg);
+ cmd->insert(0, cmdFmt->getCString(),
+ (int)(s - cmdFmt->getCString()));
+ cmd->append(s + 2);
+ } else {
+ cmd = cmdFmt->copy();
+ }
+#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
+ QString cmdStr(cmd->getCString());
+ QStringList tokens = QProcess::splitCommand(cmdStr);
+ if (!tokens.isEmpty()) {
+ QString program = tokens[0];
+ tokens.removeFirst();
+ QProcess::startDetached(program, tokens);
+ }
+#else
+ QProcess::startDetached(cmd->getCString());
+#endif
+ delete cmd;
+}
+
+// Escape any characters in a URL which might cause problems when
+// calling system().
+GString *QtPDFCore::mungeURL(GString *url) {
+ static const char *allowed = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789"
+ "-_.~/?:@&=+,#%";
+ GString *newURL;
+ char c;
+ int i;
+
+ newURL = new GString();
+ for (i = 0; i < url->getLength(); ++i) {
+ c = url->getChar(i);
+ if (strchr(allowed, c)) {
+ newURL->append(c);
+ } else {
+ newURL->appendf("%{0:02x}", c & 0xff);
+ }
+ }
+ return newURL;
+}
+
+//------------------------------------------------------------------------
+// find
+//------------------------------------------------------------------------
+
+GBool QtPDFCore::find(char *s, GBool caseSensitive, GBool next,
+ GBool backward, GBool wholeWord, GBool onePageOnly) {
+ if (!PDFCore::find(s, caseSensitive, next,
+ backward, wholeWord, onePageOnly)) {
+ return gFalse;
+ }
+#ifndef NO_TEXT_SELECT
+ copySelection(gFalse);
+#endif
+ return gTrue;
+}
+
+GBool QtPDFCore::findU(Unicode *u, int len, GBool caseSensitive,
+ GBool next, GBool backward,
+ GBool wholeWord, GBool onePageOnly) {
+ if (!PDFCore::findU(u, len, caseSensitive, next,
+ backward, wholeWord, onePageOnly)) {
+ return gFalse;
+ }
+#ifndef NO_TEXT_SELECT
+ copySelection(gFalse);
+#endif
+ return gTrue;
+}
+
+//------------------------------------------------------------------------
+// misc access
+//------------------------------------------------------------------------
+
+void QtPDFCore::setBusyCursor(GBool busy) {
+ if (busy) {
+ doSetCursor(Qt::WaitCursor);
+ } else {
+ doUnsetCursor();
+ }
+}
+
+void QtPDFCore::doSetCursor(const QCursor &cursor) {
+#ifndef QT_NO_CURSOR
+ viewport->setCursor(cursor);
+#endif
+}
+
+void QtPDFCore::doUnsetCursor() {
+#ifndef QT_NO_CURSOR
+ viewport->unsetCursor();
+#endif
+}
+
+void QtPDFCore::takeFocus() {
+ viewport->setFocus(Qt::OtherFocusReason);
+}
+
+QSize QtPDFCore::getBestSize() {
+ DisplayMode mode;
+ double zoomPercent;
+ int w, h, pg, rot;
+
+ if (!doc || doc->getNumPages() == 0) {
+ //~ what should this return?
+ return QSize(612, 792);
+ }
+ mode = state->getDisplayMode();
+ pg = tileMap->getFirstPage();
+ rot = (state->getRotate() + doc->getPageRotate(pg)) % 360;
+ zoomPercent = state->getZoom();
+ if (zoomPercent < 0) {
+ zoomPercent = globalParams->getDefaultFitZoom();
+ if (zoomPercent <= 0) {
+ zoomPercent = (int)((100 * displayDpi) / 72.0 + 0.5);
+ if (zoomPercent < 100) {
+ zoomPercent = 100;
+ }
+ }
+ }
+ if (rot == 90 || rot == 270) {
+ w = (int)(doc->getPageCropHeight(pg) * 0.01 * zoomPercent + 0.5);
+ h = (int)(doc->getPageCropWidth(pg) * 0.01 * zoomPercent + 0.5);
+ } else {
+ w = (int)(doc->getPageCropWidth(pg) * 0.01 * zoomPercent + 0.5);
+ h = (int)(doc->getPageCropHeight(pg) * 0.01 * zoomPercent + 0.5);
+ }
+ if (mode == displayContinuous) {
+ w += QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent);
+ h += tileMap->getContinuousPageSpacing();
+ } else if (mode == displaySideBySideContinuous) {
+ w = w * 2
+ + tileMap->getHorizContinuousPageSpacing()
+ + QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent);
+ h += tileMap->getContinuousPageSpacing();
+ } else if (mode == displayHorizontalContinuous) {
+ w += tileMap->getHorizContinuousPageSpacing();
+ h += QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent);
+ } else if (mode == displaySideBySideSingle) {
+ w = w * 2 + tileMap->getHorizContinuousPageSpacing();
+ }
+ //~ these additions are a kludge to make this work -- 2 pixels are
+ //~ padding in the QAbstractScrollArea; not sure where the rest go
+#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
+ w += 6;
+ h += 2;
+#else
+ w += 10;
+ h += 4;
+#endif
+ return QSize((int)(w / scaleFactor), (int)(h / scaleFactor));
+}
+
+//------------------------------------------------------------------------
+// GUI code
+//------------------------------------------------------------------------
+
+void QtPDFCore::resizeEvent() {
+ setWindowSize((int)(viewport->width() * scaleFactor),
+ (int)(viewport->height() * scaleFactor));
+}
+
+void QtPDFCore::paintEvent(int x, int y, int w, int h) {
+ SplashBitmap *bitmap;
+ GBool wholeWindow;
+
+ QPainter painter(viewport);
+ wholeWindow = x == 0 && y == 0 &&
+ w == viewport->width() && h == viewport->height();
+ bitmap = getWindowBitmap(wholeWindow);
+ QImage image(bitmap->getDataPtr(), bitmap->getWidth(),
+ bitmap->getHeight(), QImage::Format_RGB888);
+ if (scaleFactor == 1) {
+ painter.drawImage(QRect(x, y, w, h), image, QRect(x, y, w, h));
+ } else {
+ painter.drawImage(QRectF(x, y, w, h), image,
+ QRectF(x * scaleFactor, y * scaleFactor,
+ w * scaleFactor, h * scaleFactor));
+ }
+ if (paintDoneCbk) {
+ (*paintDoneCbk)(paintDoneCbkData, (bool)isBitmapFinished());
+ }
+}
+
+void QtPDFCore::scrollEvent() {
+ // avoid loops, e.g., scrollTo -> finishUpdate -> updateScrollbars ->
+ // hScrollbar.setValue -> scrollContentsBy -> scrollEvent -> scrollTo
+ if (inUpdateScrollbars) {
+ return;
+ }
+ scrollTo(hScrollBar->value(), vScrollBar->value());
+}
+
+void QtPDFCore::tick() {
+ PDFCore::tick();
+}
+
+void QtPDFCore::invalidate(int x, int y, int w, int h) {
+ int xx, yy, ww, hh;
+
+ if (scaleFactor == 1) {
+ viewport->update(x, y, w, h);
+ } else {
+ xx = (int)(x / scaleFactor);
+ yy = (int)(y / scaleFactor);
+ ww = (int)ceil((x + w) / scaleFactor) - xx;
+ hh = (int)ceil((y + h) / scaleFactor) - yy;
+ viewport->update(xx, yy, ww, hh);
+ }
+}
+
+void QtPDFCore::updateScrollbars() {
+ int winW, winH, horizLimit, vertLimit, horizMax, vertMax;
+ bool vScrollBarVisible, hScrollBarVisible;
+
+ inUpdateScrollbars = gTrue;
+
+ winW = state->getWinW();
+ winH = state->getWinH();
+ tileMap->getScrollLimits(&horizLimit, &vertLimit);
+
+ if (horizLimit > winW) {
+ horizMax = horizLimit - winW;
+ } else {
+ horizMax = 0;
+ }
+ if (vertLimit > winH) {
+ vertMax = vertLimit - winH;
+ } else {
+ vertMax = 0;
+ }
+
+ // Problem case A: in fixed zoom, there is a case where the page
+ // just barely fits in the window; if the scrollbars are visible,
+ // they reduce the available window size enough that they are
+ // necessary, i.e., the scrollbars are only necessary if they're
+ // visible -- so check for that situation and force the scrollbars
+ // to be hidden.
+ // NB: {h,v}ScrollBar->isVisible() are unreliable at startup, so
+ // we compare the viewport size to the ScrollArea size (with
+ // some slack for margins)
+ vScrollBarVisible =
+ viewport->parentWidget()->width() - viewport->width() > 8;
+ hScrollBarVisible =
+ viewport->parentWidget()->height() - viewport->height() > 8;
+ if (state->getZoom() >= 0 &&
+ vScrollBarVisible &&
+ hScrollBarVisible &&
+ horizMax <= vScrollBar->width() &&
+ vertMax <= hScrollBar->height()) {
+ horizMax = 0;
+ vertMax = 0;
+ }
+
+ // Problem case B: in fit-to-width mode, with the vertical scrollbar
+ // visible, if the window is just tall enough to fit the page, then
+ // the vertical scrollbar will be hidden, resulting in a wider
+ // window, resulting in a taller page (because of fit-to-width),
+ // resulting in the scrollbar being unhidden, in an infinite loop --
+ // so always force the vertical scroll bar to be visible in
+ // fit-to-width mode (and in fit-to-page cases where the vertical
+ // scrollbar is potentially visible).
+ if (state->getZoom() == zoomWidth ||
+ (state->getZoom() == zoomPage &&
+ (state->getDisplayMode() == displayContinuous ||
+ state->getDisplayMode() == displaySideBySideContinuous))) {
+ if (vertMax == 0) {
+ vertMax = 1;
+ }
+
+ // Problem case C: same as case B, but for fit-to-height mode and
+ // the horizontal scrollbar.
+ } else if (state->getZoom() == zoomHeight ||
+ (state->getZoom() == zoomPage &&
+ state->getDisplayMode() == displayHorizontalContinuous)) {
+ if (horizMax == 0) {
+ horizMax = 1;
+ }
+ }
+
+ hScrollBar->setMaximum(horizMax);
+ hScrollBar->setPageStep(winW);
+ hScrollBar->setValue(state->getScrollX());
+
+ vScrollBar->setMaximum(vertMax);
+ vScrollBar->setPageStep(winH);
+ vScrollBar->setValue(state->getScrollY());
+
+ inUpdateScrollbars = gFalse;
+}
+
+GBool QtPDFCore::checkForNewFile() {
+ QDateTime newModTime;
+
+ if (doc->getFileName()) {
+ newModTime = QFileInfo(doc->getFileName()->getCString()).lastModified();
+ if (newModTime != modTime) {
+ modTime = newModTime;
+ return gTrue;
+ }
+ }
+ return gFalse;
+}
+
+void QtPDFCore::preLoad() {
+ if (preLoadCbk) {
+ (*preLoadCbk)(preLoadCbkData);
+ }
+}
+
+void QtPDFCore::postLoad() {
+ if (postLoadCbk) {
+ (*postLoadCbk)(postLoadCbkData);
+ }
+}
+
+//------------------------------------------------------------------------
+// password dialog
+//------------------------------------------------------------------------
+
+GString *QtPDFCore::getPassword() {
+ QString s;
+ bool ok;
+
+ if (!showPasswordDialog) {
+ return NULL;
+ }
+ s = QInputDialog::getText(viewport, "PDF Password",
+ "This document requires a password",
+ QLineEdit::Password, "", &ok, Qt::Dialog);
+ if (ok) {
+ return new GString(s.toLocal8Bit().constData());
+ } else {
+ return NULL;
+ }
+}