//======================================================================== // // QtPDFCore.cc // // Copyright 2009-2014 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 string with one '%s' in it, and an // 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; } }