"Fossies" - the Fresh Open Source Software Archive 
Member "xpdf-4.04/xpdf-qt/QtPDFCore.cc" (18 Apr 2022, 30301 Bytes) of package /linux/misc/xpdf-4.04.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
1 //========================================================================
2 //
3 // QtPDFCore.cc
4 //
5 // Copyright 2009-2014 Glyph & Cog, LLC
6 //
7 //========================================================================
8
9 #include <aconf.h>
10
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
13 #endif
14
15 #include <math.h>
16 #include <string.h>
17 #include <QApplication>
18 #include <QClipboard>
19 #include <QDesktopServices>
20 #include <QFileInfo>
21 #include <QImage>
22 #include <QInputDialog>
23 #include <QMessageBox>
24 #include <QPainter>
25 #include <QProcess>
26 #include <QScrollBar>
27 #include <QStyle>
28 #include <QUrl>
29 #include <QWidget>
30 #include "gmem.h"
31 #include "gmempp.h"
32 #include "gfile.h"
33 #include "GString.h"
34 #include "GList.h"
35 #include "Error.h"
36 #include "GlobalParams.h"
37 #include "PDFDoc.h"
38 #include "Link.h"
39 #include "ErrorCodes.h"
40 #include "GfxState.h"
41 #include "PSOutputDev.h"
42 #include "TextOutputDev.h"
43 #include "SplashBitmap.h"
44 #include "DisplayState.h"
45 #include "TileMap.h"
46 #include "QtPDFCore.h"
47
48 //------------------------------------------------------------------------
49 // QtPDFCore
50 //------------------------------------------------------------------------
51
52 QtPDFCore::QtPDFCore(QWidget *viewportA,
53 QScrollBar *hScrollBarA, QScrollBar *vScrollBarA,
54 SplashColorPtr paperColor, SplashColorPtr matteColor,
55 GBool reverseVideo):
56 PDFCore(splashModeRGB8, 4, reverseVideo, paperColor)
57 {
58 int dpiX, dpiY;
59
60 viewport = viewportA;
61 hScrollBar = hScrollBarA;
62 vScrollBar = vScrollBarA;
63 hScrollBar->setRange(0, 0);
64 hScrollBar->setSingleStep(16);
65 vScrollBar->setRange(0, 0);
66 vScrollBar->setSingleStep(16);
67 viewport->setMouseTracking(true);
68
69 state->setMatteColor(matteColor);
70
71 oldFirstPage = -1;
72 oldMidPage = -1;
73
74 linkAction = NULL;
75 lastLinkAction = NULL;
76
77 dragging = gFalse;
78
79 panning = gFalse;
80
81 inUpdateScrollbars = gFalse;
82
83 updateCbk = NULL;
84 midPageChangedCbk = NULL;
85 preLoadCbk = NULL;
86 postLoadCbk = NULL;
87 actionCbk = NULL;
88 linkCbk = NULL;
89 selectDoneCbk = NULL;
90
91 // optional features default to on
92 hyperlinksEnabled = gTrue;
93 externalHyperlinksEnabled = gTrue;
94 selectEnabled = gTrue;
95 panEnabled = gTrue;
96 showPasswordDialog = gTrue;
97
98 // get Qt's HiDPI scale factor
99 #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
100 scaleFactor = viewport->devicePixelRatioF();
101 #elif QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
102 scaleFactor = viewport->devicePixelRatio();
103 #else
104 scaleFactor = 1;
105 #endif
106
107 // get the display resolution (used for HiDPI scaling)
108 dpiX = viewport->logicalDpiX();
109 dpiY = viewport->logicalDpiY();
110 displayDpi = dpiX < dpiY ? dpiX : dpiY;
111 displayDpi = (int)(displayDpi * scaleFactor);
112 }
113
114 QtPDFCore::~QtPDFCore() {
115 }
116
117 //------------------------------------------------------------------------
118 // loadFile / displayPage / displayDest
119 //------------------------------------------------------------------------
120
121 int QtPDFCore::loadFile(GString *fileName, GString *ownerPassword,
122 GString *userPassword) {
123 int err;
124
125 err = PDFCore::loadFile(fileName, ownerPassword, userPassword);
126 if (err == errNone) {
127 // save the modification time
128 modTime = QFileInfo(doc->getFileName()->getCString()).lastModified();
129
130 // update the parent window
131 if (updateCbk) {
132 (*updateCbk)(updateCbkData, doc->getFileName(), -1,
133 doc->getNumPages(), NULL);
134 }
135 oldFirstPage = oldMidPage = -1;
136 }
137 return err;
138 }
139
140 #ifdef _WIN32
141 int QtPDFCore::loadFile(wchar_t *fileName, int fileNameLen,
142 GString *ownerPassword,
143 GString *userPassword) {
144 int err;
145
146 err = PDFCore::loadFile(fileName, fileNameLen, ownerPassword, userPassword);
147 if (err == errNone) {
148 // save the modification time
149 modTime = QFileInfo(doc->getFileName()->getCString()).lastModified();
150
151 // update the parent window
152 if (updateCbk) {
153 (*updateCbk)(updateCbkData, doc->getFileName(), -1,
154 doc->getNumPages(), NULL);
155 }
156 oldFirstPage = oldMidPage = -1;
157 }
158 return err;
159 }
160 #endif
161
162 int QtPDFCore::loadFile(BaseStream *stream, GString *ownerPassword,
163 GString *userPassword) {
164 int err;
165
166 err = PDFCore::loadFile(stream, ownerPassword, userPassword);
167 if (err == errNone) {
168 // no file
169 modTime = QDateTime();
170
171 // update the parent window
172 if (updateCbk) {
173 (*updateCbk)(updateCbkData, doc->getFileName(), -1,
174 doc->getNumPages(), NULL);
175 }
176 oldFirstPage = oldMidPage = -1;
177 }
178 return err;
179 }
180
181 void QtPDFCore::loadDoc(PDFDoc *docA) {
182 PDFCore::loadDoc(docA);
183
184 // save the modification time
185 if (doc->getFileName()) {
186 modTime = QFileInfo(doc->getFileName()->getCString()).lastModified();
187 } else {
188 modTime = QDateTime();
189 }
190
191 // update the parent window
192 if (updateCbk) {
193 (*updateCbk)(updateCbkData, doc->getFileName(), -1,
194 doc->getNumPages(), NULL);
195 }
196 oldFirstPage = oldMidPage = -1;
197 }
198
199 int QtPDFCore::reload() {
200 int err;
201
202 err = PDFCore::reload();
203 if (err == errNone) {
204 // save the modification time
205 modTime = QFileInfo(doc->getFileName()->getCString()).lastModified();
206
207 // update the parent window
208 if (updateCbk) {
209 (*updateCbk)(updateCbkData, doc->getFileName(), -1,
210 doc->getNumPages(), NULL);
211 }
212 oldFirstPage = oldMidPage = -1;
213 }
214 return err;
215 }
216
217 void QtPDFCore::finishUpdate(GBool addToHist, GBool checkForChangedFile) {
218 int firstPage, midPage;
219
220 PDFCore::finishUpdate(addToHist, checkForChangedFile);
221 firstPage = getPageNum();
222 if (doc && firstPage != oldFirstPage && updateCbk) {
223 (*updateCbk)(updateCbkData, NULL, firstPage, -1, "");
224 }
225 oldFirstPage = firstPage;
226 midPage = getMidPageNum();
227 if (doc && midPage != oldMidPage && midPageChangedCbk) {
228 (*midPageChangedCbk)(midPageChangedCbkData, midPage);
229 }
230 oldMidPage = midPage;
231
232 linkAction = NULL;
233 lastLinkAction = NULL;
234 }
235
236 //------------------------------------------------------------------------
237 // panning and selection
238 //------------------------------------------------------------------------
239
240 void QtPDFCore::startPan(int wx, int wy) {
241 if (!panEnabled) {
242 return;
243 }
244 panning = gTrue;
245 panMX = wx;
246 panMY = wy;
247 }
248
249 void QtPDFCore::endPan(int wx, int wy) {
250 panning = gFalse;
251 }
252
253 void QtPDFCore::startSelection(int wx, int wy, GBool extend) {
254 int pg, x, y;
255
256 takeFocus();
257 if (!doc || doc->getNumPages() == 0 || !selectEnabled) {
258 return;
259 }
260 if (!cvtWindowToDev(wx, wy, &pg, &x, &y)) {
261 return;
262 }
263 if (extend && hasSelection()) {
264 moveSelectionDrag(pg, x, y);
265 } else {
266 startSelectionDrag(pg, x, y);
267 }
268 if (getSelectMode() == selectModeBlock) {
269 doSetCursor(Qt::CrossCursor);
270 }
271 dragging = gTrue;
272 }
273
274 void QtPDFCore::endSelection(int wx, int wy) {
275 LinkAction *action;
276 int pg, x, y;
277 double xu, yu;
278 GBool ok;
279
280 if (!doc || doc->getNumPages() == 0) {
281 return;
282 }
283 ok = cvtWindowToDev(wx, wy, &pg, &x, &y);
284 if (dragging) {
285 dragging = gFalse;
286 doUnsetCursor();
287 if (ok) {
288 moveSelectionDrag(pg, x, y);
289 }
290 finishSelectionDrag();
291 if (selectDoneCbk) {
292 (*selectDoneCbk)(selectDoneCbkData);
293 }
294 #ifndef NO_TEXT_SELECT
295 if (hasSelection()) {
296 copySelection(gFalse);
297 }
298 #endif
299 }
300 if (ok) {
301 if (hasSelection()) {
302 action = NULL;
303 } else {
304 cvtDevToUser(pg, x, y, &xu, &yu);
305 action = findLink(pg, xu, yu);
306 }
307 if (linkCbk && action) {
308 doLinkCbk(action);
309 }
310 if (hyperlinksEnabled && action) {
311 doAction(action);
312 }
313 }
314 }
315
316 void QtPDFCore::mouseMove(int wx, int wy) {
317 LinkAction *action;
318 int pg, x, y;
319 double xu, yu;
320 const char *s;
321 GBool ok, mouseOverText;
322
323 if (!doc || doc->getNumPages() == 0) {
324 return;
325 }
326 ok = cvtWindowToDev(wx, wy, &pg, &x, &y);
327 if (dragging) {
328 if (ok) {
329 moveSelectionDrag(pg, x, y);
330 }
331 } else {
332 cvtDevToUser(pg, x, y, &xu, &yu);
333
334 // check for a link
335 action = NULL;
336 if (hyperlinksEnabled && ok) {
337 action = findLink(pg, xu, yu);
338 }
339
340 // check for text
341 mouseOverText = gFalse;
342 if (!action && getSelectMode() == selectModeLinear && ok) {
343 mouseOverText = overText(pg, x, y);
344 }
345
346 // update the cursor
347 if (action) {
348 doSetCursor(Qt::PointingHandCursor);
349 } else if (mouseOverText) {
350 doSetCursor(Qt::IBeamCursor);
351 } else {
352 doUnsetCursor();
353 }
354
355 // update the link info
356 if (action != linkAction) {
357 linkAction = action;
358 if (updateCbk) {
359 //~ should pass a QString to updateCbk()
360 if (linkAction) {
361 s = getLinkInfo(linkAction).toLocal8Bit().constData();
362 } else {
363 s = "";
364 }
365 (*updateCbk)(updateCbkData, NULL, -1, -1, s);
366 }
367 }
368 }
369
370 if (panning) {
371 scrollTo(getScrollX() - (wx - panMX),
372 getScrollY() - (wy - panMY));
373 panMX = wx;
374 panMY = wy;
375 }
376 }
377
378 void QtPDFCore::selectWord(int wx, int wy) {
379 int pg, x, y;
380
381 takeFocus();
382 if (!doc || doc->getNumPages() == 0 || !selectEnabled) {
383 return;
384 }
385 if (getSelectMode() != selectModeLinear) {
386 return;
387 }
388 if (!cvtWindowToDev(wx, wy, &pg, &x, &y)) {
389 return;
390 }
391 PDFCore::selectWord(pg, x, y);
392 #ifndef NO_TEXT_SELECT
393 if (hasSelection()) {
394 copySelection(gFalse);
395 }
396 #endif
397 }
398
399 void QtPDFCore::selectLine(int wx, int wy) {
400 int pg, x, y;
401
402 takeFocus();
403 if (!doc || doc->getNumPages() == 0 || !selectEnabled) {
404 return;
405 }
406 if (getSelectMode() != selectModeLinear) {
407 return;
408 }
409 if (!cvtWindowToDev(wx, wy, &pg, &x, &y)) {
410 return;
411 }
412 PDFCore::selectLine(pg, x, y);
413 #ifndef NO_TEXT_SELECT
414 if (hasSelection()) {
415 copySelection(gFalse);
416 }
417 #endif
418 }
419
420 void QtPDFCore::doLinkCbk(LinkAction *action) {
421 LinkDest *dest;
422 GString *namedDest;
423 Ref pageRef;
424 int pg;
425 GString *cmd, *params;
426 char *s;
427
428 if (!linkCbk) {
429 return;
430 }
431
432 switch (action->getKind()) {
433
434 case actionGoTo:
435 dest = NULL;
436 if ((dest = ((LinkGoTo *)action)->getDest())) {
437 dest = dest->copy();
438 } else if ((namedDest = ((LinkGoTo *)action)->getNamedDest())) {
439 dest = doc->findDest(namedDest);
440 }
441 pg = 0;
442 if (dest) {
443 if (dest->isPageRef()) {
444 pageRef = dest->getPageRef();
445 pg = doc->findPage(pageRef.num, pageRef.gen);
446 } else {
447 pg = dest->getPageNum();
448 }
449 delete dest;
450 }
451 (*linkCbk)(linkCbkData, "goto", NULL, pg);
452 break;
453
454 case actionGoToR:
455 (*linkCbk)(linkCbkData, "pdf",
456 ((LinkGoToR *)action)->getFileName()->getCString(), 0);
457 break;
458
459 case actionLaunch:
460 cmd = ((LinkLaunch *)action)->getFileName()->copy();
461 s = cmd->getCString();
462 if (strcmp(s + cmd->getLength() - 4, ".pdf") &&
463 strcmp(s + cmd->getLength() - 4, ".PDF") &&
464 (params = ((LinkLaunch *)action)->getParams())) {
465 cmd->append(' ')->append(params);
466 }
467 (*linkCbk)(linkCbkData, "launch", cmd->getCString(), 0);
468 delete cmd;
469 break;
470
471 case actionURI:
472 (*linkCbk)(linkCbkData, "url",
473 ((LinkURI *)action)->getURI()->getCString(), 0);
474 break;
475
476 case actionNamed:
477 (*linkCbk)(linkCbkData, "named",
478 ((LinkNamed *)action)->getName()->getCString(), 0);
479 break;
480
481 case actionMovie:
482 case actionJavaScript:
483 case actionSubmitForm:
484 case actionHide:
485 case actionUnknown:
486 (*linkCbk)(linkCbkData, "unknown", NULL, 0);
487 break;
488 }
489 }
490
491 QString QtPDFCore::getSelectedTextQString() {
492 GString *s, *enc;
493 QString qs;
494 int i;
495
496 if (!doc->okToCopy()) {
497 return "";
498 }
499 if (!(s = getSelectedText())) {
500 return "";
501 }
502 enc = globalParams->getTextEncodingName();
503 if (!enc->cmp("UTF-8")) {
504 qs = QString::fromUtf8(s->getCString());
505 } else if (!enc->cmp("UCS-2")) {
506 for (i = 0; i+1 < s->getLength(); i += 2) {
507 qs.append((QChar)(((s->getChar(i) & 0xff) << 8) +
508 (s->getChar(i+1) & 0xff)));
509 }
510 } else {
511 qs = QString(s->getCString());
512 }
513 delete s;
514 delete enc;
515 return qs;
516 }
517
518 void QtPDFCore::copySelection(GBool toClipboard) {
519 QString qs;
520
521 // only X11 has the copy-on-select behavior
522 if (!toClipboard && !QApplication::clipboard()->supportsSelection()) {
523 return;
524 }
525 if (!doc->okToCopy()) {
526 return;
527 }
528 if (hasSelection()) {
529 QApplication::clipboard()->setText(getSelectedTextQString(),
530 toClipboard ? QClipboard::Clipboard
531 : QClipboard::Selection);
532 }
533 }
534
535 //------------------------------------------------------------------------
536 // hyperlinks
537 //------------------------------------------------------------------------
538
539 GBool QtPDFCore::doAction(LinkAction *action) {
540 LinkActionKind kind;
541 LinkDest *dest;
542 GString *namedDest;
543 char *s;
544 GString *fileName, *fileName2, *params;
545 GString *cmd;
546 GString *actionName;
547 Object movieAnnot, obj1, obj2;
548 GString *msg;
549 int i;
550
551 switch (kind = action->getKind()) {
552
553 // GoTo / GoToR action
554 case actionGoTo:
555 case actionGoToR:
556 if (kind == actionGoTo) {
557 dest = NULL;
558 namedDest = NULL;
559 if ((dest = ((LinkGoTo *)action)->getDest())) {
560 dest = dest->copy();
561 } else if ((namedDest = ((LinkGoTo *)action)->getNamedDest())) {
562 namedDest = namedDest->copy();
563 }
564 } else {
565 if (!externalHyperlinksEnabled) {
566 return gFalse;
567 }
568 dest = NULL;
569 namedDest = NULL;
570 if ((dest = ((LinkGoToR *)action)->getDest())) {
571 dest = dest->copy();
572 } else if ((namedDest = ((LinkGoToR *)action)->getNamedDest())) {
573 namedDest = namedDest->copy();
574 }
575 s = ((LinkGoToR *)action)->getFileName()->getCString();
576 if (isAbsolutePath(s)) {
577 fileName = new GString(s);
578 } else {
579 fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);
580 }
581 if (loadFile(fileName) != errNone) {
582 if (dest) {
583 delete dest;
584 }
585 if (namedDest) {
586 delete namedDest;
587 }
588 delete fileName;
589 return gFalse;
590 }
591 delete fileName;
592 }
593 if (namedDest) {
594 dest = doc->findDest(namedDest);
595 delete namedDest;
596 }
597 if (dest) {
598 displayDest(dest);
599 delete dest;
600 } else {
601 if (kind == actionGoToR) {
602 displayPage(1, gFalse, gFalse, gTrue);
603 }
604 }
605 break;
606
607 // Launch action
608 case actionLaunch:
609 if (!externalHyperlinksEnabled) {
610 return gFalse;
611 }
612 fileName = ((LinkLaunch *)action)->getFileName();
613 s = fileName->getCString();
614 if (fileName->getLength() >= 4 &&
615 (!strcmp(s + fileName->getLength() - 4, ".pdf") ||
616 !strcmp(s + fileName->getLength() - 4, ".PDF"))) {
617 if (isAbsolutePath(s)) {
618 fileName = fileName->copy();
619 } else {
620 fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);
621 }
622 if (loadFile(fileName) != errNone) {
623 delete fileName;
624 return gFalse;
625 }
626 delete fileName;
627 displayPage(1, gFalse, gFalse, gTrue);
628 } else {
629 cmd = fileName->copy();
630 if ((params = ((LinkLaunch *)action)->getParams())) {
631 cmd->append(' ')->append(params);
632 }
633 if (globalParams->getLaunchCommand()) {
634 cmd->insert(0, ' ');
635 cmd->insert(0, globalParams->getLaunchCommand());
636 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
637 QString cmdStr(cmd->getCString());
638 QStringList tokens = QProcess::splitCommand(cmdStr);
639 if (!tokens.isEmpty()) {
640 QString program = tokens[0];
641 tokens.removeFirst();
642 QProcess::startDetached(program, tokens);
643 }
644 #else
645 QProcess::startDetached(cmd->getCString());
646 #endif
647 } else {
648 msg = new GString("About to execute the command:\n");
649 msg->append(cmd);
650 if (QMessageBox::question(viewport, "PDF Launch Link",
651 msg->getCString(),
652 QMessageBox::Ok | QMessageBox::Cancel,
653 QMessageBox::Ok)
654 == QMessageBox::Ok) {
655 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
656 QString cmdStr(cmd->getCString());
657 QStringList tokens = QProcess::splitCommand(cmdStr);
658 if (!tokens.isEmpty()) {
659 QString program = tokens[0];
660 tokens.removeFirst();
661 QProcess::startDetached(program, tokens);
662 }
663 #else
664 QProcess::startDetached(cmd->getCString());
665 #endif
666 }
667 delete msg;
668 }
669 delete cmd;
670 }
671 break;
672
673 // URI action
674 case actionURI:
675 if (!externalHyperlinksEnabled) {
676 return gFalse;
677 }
678 QDesktopServices::openUrl(QUrl(((LinkURI *)action)->getURI()->getCString(),
679 QUrl::TolerantMode));
680 break;
681
682 // Named action
683 case actionNamed:
684 actionName = ((LinkNamed *)action)->getName();
685 if (!actionName->cmp("NextPage")) {
686 gotoNextPage(1, gTrue);
687 } else if (!actionName->cmp("PrevPage")) {
688 gotoPrevPage(1, gTrue, gFalse);
689 } else if (!actionName->cmp("FirstPage")) {
690 displayPage(1, gTrue, gFalse, gTrue);
691 } else if (!actionName->cmp("LastPage")) {
692 displayPage(doc->getNumPages(), gTrue, gFalse, gTrue);
693 } else if (!actionName->cmp("GoBack")) {
694 goBackward();
695 } else if (!actionName->cmp("GoForward")) {
696 goForward();
697 } else if (!actionName->cmp("Quit")) {
698 if (actionCbk) {
699 (*actionCbk)(actionCbkData, actionName->getCString());
700 }
701 } else {
702 error(errSyntaxError, -1,
703 "Unknown named action: '{0:t}'", actionName);
704 return gFalse;
705 }
706 break;
707
708 // Movie action
709 case actionMovie:
710 if (!externalHyperlinksEnabled) {
711 return gFalse;
712 }
713 if (!(cmd = globalParams->getMovieCommand())) {
714 error(errConfig, -1, "No movieCommand defined in config file");
715 return gFalse;
716 }
717 if (((LinkMovie *)action)->hasAnnotRef()) {
718 doc->getXRef()->fetch(((LinkMovie *)action)->getAnnotRef()->num,
719 ((LinkMovie *)action)->getAnnotRef()->gen,
720 &movieAnnot);
721 } else {
722 //~ need to use the correct page num here
723 doc->getCatalog()->getPage(tileMap->getFirstPage())->getAnnots(&obj1);
724 if (obj1.isArray()) {
725 for (i = 0; i < obj1.arrayGetLength(); ++i) {
726 if (obj1.arrayGet(i, &movieAnnot)->isDict()) {
727 if (movieAnnot.dictLookup("Subtype", &obj2)->isName("Movie")) {
728 obj2.free();
729 break;
730 }
731 obj2.free();
732 }
733 movieAnnot.free();
734 }
735 obj1.free();
736 }
737 }
738 if (movieAnnot.isDict()) {
739 if (movieAnnot.dictLookup("Movie", &obj1)->isDict()) {
740 if (obj1.dictLookup("F", &obj2)) {
741 if ((fileName = LinkAction::getFileSpecName(&obj2))) {
742 if (!isAbsolutePath(fileName->getCString())) {
743 fileName2 = appendToPath(
744 grabPath(doc->getFileName()->getCString()),
745 fileName->getCString());
746 delete fileName;
747 fileName = fileName2;
748 }
749 runCommand(cmd, fileName);
750 delete fileName;
751 }
752 obj2.free();
753 }
754 obj1.free();
755 }
756 }
757 movieAnnot.free();
758 break;
759
760 // unimplemented actions
761 case actionJavaScript:
762 case actionSubmitForm:
763 case actionHide:
764 return gFalse;
765
766 // unknown action type
767 case actionUnknown:
768 error(errSyntaxError, -1, "Unknown link action type: '{0:t}'",
769 ((LinkUnknown *)action)->getAction());
770 return gFalse;
771 }
772
773 return gTrue;
774 }
775
776 QString QtPDFCore::getLinkInfo(LinkAction *action) {
777 LinkDest *dest;
778 GString *namedDest;
779 Ref pageRef;
780 int pg;
781 QString info;
782
783 if (action == lastLinkAction && !lastLinkActionInfo.isEmpty()) {
784 return lastLinkActionInfo;
785 }
786
787 switch (action->getKind()) {
788 case actionGoTo:
789 dest = NULL;
790 if ((dest = ((LinkGoTo *)action)->getDest())) {
791 dest = dest->copy();
792 } else if ((namedDest = ((LinkGoTo *)action)->getNamedDest())) {
793 dest = doc->findDest(namedDest);
794 }
795 pg = 0;
796 if (dest) {
797 if (dest->isPageRef()) {
798 pageRef = dest->getPageRef();
799 pg = doc->findPage(pageRef.num, pageRef.gen);
800 } else {
801 pg = dest->getPageNum();
802 }
803 delete dest;
804 }
805 if (pg) {
806 info = QString("[page ") + QString::number(pg) + QString("]");
807 } else {
808 info = "[internal]";
809 }
810 break;
811 case actionGoToR:
812 info = QString(((LinkGoToR *)action)->getFileName()->getCString());
813 break;
814 case actionLaunch:
815 info = QString(((LinkLaunch *)action)->getFileName()->getCString());
816 break;
817 case actionURI:
818 info = QString(((LinkURI *)action)->getURI()->getCString());
819 break;
820 case actionNamed:
821 info = QString(((LinkNamed *)action)->getName()->getCString());
822 break;
823 case actionMovie:
824 info = "[movie]";
825 break;
826 case actionJavaScript:
827 case actionSubmitForm:
828 case actionHide:
829 case actionUnknown:
830 default:
831 info = "[unknown]";
832 break;
833 }
834
835 lastLinkAction = action;
836 lastLinkActionInfo = info;
837
838 return info;
839 }
840
841 // Run a command, given a <cmdFmt> string with one '%s' in it, and an
842 // <arg> string to insert in place of the '%s'.
843 void QtPDFCore::runCommand(GString *cmdFmt, GString *arg) {
844 GString *cmd;
845 char *s;
846
847 if ((s = strstr(cmdFmt->getCString(), "%s"))) {
848 cmd = mungeURL(arg);
849 cmd->insert(0, cmdFmt->getCString(),
850 (int)(s - cmdFmt->getCString()));
851 cmd->append(s + 2);
852 } else {
853 cmd = cmdFmt->copy();
854 }
855 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
856 QString cmdStr(cmd->getCString());
857 QStringList tokens = QProcess::splitCommand(cmdStr);
858 if (!tokens.isEmpty()) {
859 QString program = tokens[0];
860 tokens.removeFirst();
861 QProcess::startDetached(program, tokens);
862 }
863 #else
864 QProcess::startDetached(cmd->getCString());
865 #endif
866 delete cmd;
867 }
868
869 // Escape any characters in a URL which might cause problems when
870 // calling system().
871 GString *QtPDFCore::mungeURL(GString *url) {
872 static const char *allowed = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
873 "abcdefghijklmnopqrstuvwxyz"
874 "0123456789"
875 "-_.~/?:@&=+,#%";
876 GString *newURL;
877 char c;
878 int i;
879
880 newURL = new GString();
881 for (i = 0; i < url->getLength(); ++i) {
882 c = url->getChar(i);
883 if (strchr(allowed, c)) {
884 newURL->append(c);
885 } else {
886 newURL->appendf("%{0:02x}", c & 0xff);
887 }
888 }
889 return newURL;
890 }
891
892 //------------------------------------------------------------------------
893 // find
894 //------------------------------------------------------------------------
895
896 GBool QtPDFCore::find(char *s, GBool caseSensitive, GBool next,
897 GBool backward, GBool wholeWord, GBool onePageOnly) {
898 if (!PDFCore::find(s, caseSensitive, next,
899 backward, wholeWord, onePageOnly)) {
900 return gFalse;
901 }
902 #ifndef NO_TEXT_SELECT
903 copySelection(gFalse);
904 #endif
905 return gTrue;
906 }
907
908 GBool QtPDFCore::findU(Unicode *u, int len, GBool caseSensitive,
909 GBool next, GBool backward,
910 GBool wholeWord, GBool onePageOnly) {
911 if (!PDFCore::findU(u, len, caseSensitive, next,
912 backward, wholeWord, onePageOnly)) {
913 return gFalse;
914 }
915 #ifndef NO_TEXT_SELECT
916 copySelection(gFalse);
917 #endif
918 return gTrue;
919 }
920
921 //------------------------------------------------------------------------
922 // misc access
923 //------------------------------------------------------------------------
924
925 void QtPDFCore::setBusyCursor(GBool busy) {
926 if (busy) {
927 doSetCursor(Qt::WaitCursor);
928 } else {
929 doUnsetCursor();
930 }
931 }
932
933 void QtPDFCore::doSetCursor(const QCursor &cursor) {
934 #ifndef QT_NO_CURSOR
935 viewport->setCursor(cursor);
936 #endif
937 }
938
939 void QtPDFCore::doUnsetCursor() {
940 #ifndef QT_NO_CURSOR
941 viewport->unsetCursor();
942 #endif
943 }
944
945 void QtPDFCore::takeFocus() {
946 viewport->setFocus(Qt::OtherFocusReason);
947 }
948
949 QSize QtPDFCore::getBestSize() {
950 DisplayMode mode;
951 double zoomPercent;
952 int w, h, pg, rot;
953
954 if (!doc || doc->getNumPages() == 0) {
955 //~ what should this return?
956 return QSize(612, 792);
957 }
958 mode = state->getDisplayMode();
959 pg = tileMap->getFirstPage();
960 rot = (state->getRotate() + doc->getPageRotate(pg)) % 360;
961 zoomPercent = state->getZoom();
962 if (zoomPercent < 0) {
963 zoomPercent = globalParams->getDefaultFitZoom();
964 if (zoomPercent <= 0) {
965 zoomPercent = (int)((100 * displayDpi) / 72.0 + 0.5);
966 if (zoomPercent < 100) {
967 zoomPercent = 100;
968 }
969 }
970 }
971 if (rot == 90 || rot == 270) {
972 w = (int)(doc->getPageCropHeight(pg) * 0.01 * zoomPercent + 0.5);
973 h = (int)(doc->getPageCropWidth(pg) * 0.01 * zoomPercent + 0.5);
974 } else {
975 w = (int)(doc->getPageCropWidth(pg) * 0.01 * zoomPercent + 0.5);
976 h = (int)(doc->getPageCropHeight(pg) * 0.01 * zoomPercent + 0.5);
977 }
978 if (mode == displayContinuous) {
979 w += QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent);
980 h += tileMap->getContinuousPageSpacing();
981 } else if (mode == displaySideBySideContinuous) {
982 w = w * 2
983 + tileMap->getHorizContinuousPageSpacing()
984 + QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent);
985 h += tileMap->getContinuousPageSpacing();
986 } else if (mode == displayHorizontalContinuous) {
987 w += tileMap->getHorizContinuousPageSpacing();
988 h += QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent);
989 } else if (mode == displaySideBySideSingle) {
990 w = w * 2 + tileMap->getHorizContinuousPageSpacing();
991 }
992 //~ these additions are a kludge to make this work -- 2 pixels are
993 //~ padding in the QAbstractScrollArea; not sure where the rest go
994 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
995 w += 6;
996 h += 2;
997 #else
998 w += 10;
999 h += 4;
1000 #endif
1001 return QSize((int)(w / scaleFactor), (int)(h / scaleFactor));
1002 }
1003
1004 //------------------------------------------------------------------------
1005 // GUI code
1006 //------------------------------------------------------------------------
1007
1008 void QtPDFCore::resizeEvent() {
1009 setWindowSize((int)(viewport->width() * scaleFactor),
1010 (int)(viewport->height() * scaleFactor));
1011 }
1012
1013 void QtPDFCore::paintEvent(int x, int y, int w, int h) {
1014 SplashBitmap *bitmap;
1015 GBool wholeWindow;
1016
1017 QPainter painter(viewport);
1018 wholeWindow = x == 0 && y == 0 &&
1019 w == viewport->width() && h == viewport->height();
1020 bitmap = getWindowBitmap(wholeWindow);
1021 QImage image(bitmap->getDataPtr(), bitmap->getWidth(),
1022 bitmap->getHeight(), QImage::Format_RGB888);
1023 if (scaleFactor == 1) {
1024 painter.drawImage(QRect(x, y, w, h), image, QRect(x, y, w, h));
1025 } else {
1026 painter.drawImage(QRectF(x, y, w, h), image,
1027 QRectF(x * scaleFactor, y * scaleFactor,
1028 w * scaleFactor, h * scaleFactor));
1029 }
1030 if (paintDoneCbk) {
1031 (*paintDoneCbk)(paintDoneCbkData, (bool)isBitmapFinished());
1032 }
1033 }
1034
1035 void QtPDFCore::scrollEvent() {
1036 // avoid loops, e.g., scrollTo -> finishUpdate -> updateScrollbars ->
1037 // hScrollbar.setValue -> scrollContentsBy -> scrollEvent -> scrollTo
1038 if (inUpdateScrollbars) {
1039 return;
1040 }
1041 scrollTo(hScrollBar->value(), vScrollBar->value());
1042 }
1043
1044 void QtPDFCore::tick() {
1045 PDFCore::tick();
1046 }
1047
1048 void QtPDFCore::invalidate(int x, int y, int w, int h) {
1049 int xx, yy, ww, hh;
1050
1051 if (scaleFactor == 1) {
1052 viewport->update(x, y, w, h);
1053 } else {
1054 xx = (int)(x / scaleFactor);
1055 yy = (int)(y / scaleFactor);
1056 ww = (int)ceil((x + w) / scaleFactor) - xx;
1057 hh = (int)ceil((y + h) / scaleFactor) - yy;
1058 viewport->update(xx, yy, ww, hh);
1059 }
1060 }
1061
1062 void QtPDFCore::updateScrollbars() {
1063 int winW, winH, horizLimit, vertLimit, horizMax, vertMax;
1064 bool vScrollBarVisible, hScrollBarVisible;
1065
1066 inUpdateScrollbars = gTrue;
1067
1068 winW = state->getWinW();
1069 winH = state->getWinH();
1070 tileMap->getScrollLimits(&horizLimit, &vertLimit);
1071
1072 if (horizLimit > winW) {
1073 horizMax = horizLimit - winW;
1074 } else {
1075 horizMax = 0;
1076 }
1077 if (vertLimit > winH) {
1078 vertMax = vertLimit - winH;
1079 } else {
1080 vertMax = 0;
1081 }
1082
1083 // Problem case A: in fixed zoom, there is a case where the page
1084 // just barely fits in the window; if the scrollbars are visible,
1085 // they reduce the available window size enough that they are
1086 // necessary, i.e., the scrollbars are only necessary if they're
1087 // visible -- so check for that situation and force the scrollbars
1088 // to be hidden.
1089 // NB: {h,v}ScrollBar->isVisible() are unreliable at startup, so
1090 // we compare the viewport size to the ScrollArea size (with
1091 // some slack for margins)
1092 vScrollBarVisible =
1093 viewport->parentWidget()->width() - viewport->width() > 8;
1094 hScrollBarVisible =
1095 viewport->parentWidget()->height() - viewport->height() > 8;
1096 if (state->getZoom() >= 0 &&
1097 vScrollBarVisible &&
1098 hScrollBarVisible &&
1099 horizMax <= vScrollBar->width() &&
1100 vertMax <= hScrollBar->height()) {
1101 horizMax = 0;
1102 vertMax = 0;
1103 }
1104
1105 // Problem case B: in fit-to-width mode, with the vertical scrollbar
1106 // visible, if the window is just tall enough to fit the page, then
1107 // the vertical scrollbar will be hidden, resulting in a wider
1108 // window, resulting in a taller page (because of fit-to-width),
1109 // resulting in the scrollbar being unhidden, in an infinite loop --
1110 // so always force the vertical scroll bar to be visible in
1111 // fit-to-width mode (and in fit-to-page cases where the vertical
1112 // scrollbar is potentially visible).
1113 if (state->getZoom() == zoomWidth ||
1114 (state->getZoom() == zoomPage &&
1115 (state->getDisplayMode() == displayContinuous ||
1116 state->getDisplayMode() == displaySideBySideContinuous))) {
1117 if (vertMax == 0) {
1118 vertMax = 1;
1119 }
1120
1121 // Problem case C: same as case B, but for fit-to-height mode and
1122 // the horizontal scrollbar.
1123 } else if (state->getZoom() == zoomHeight ||
1124 (state->getZoom() == zoomPage &&
1125 state->getDisplayMode() == displayHorizontalContinuous)) {
1126 if (horizMax == 0) {
1127 horizMax = 1;
1128 }
1129 }
1130
1131 hScrollBar->setMaximum(horizMax);
1132 hScrollBar->setPageStep(winW);
1133 hScrollBar->setValue(state->getScrollX());
1134
1135 vScrollBar->setMaximum(vertMax);
1136 vScrollBar->setPageStep(winH);
1137 vScrollBar->setValue(state->getScrollY());
1138
1139 inUpdateScrollbars = gFalse;
1140 }
1141
1142 GBool QtPDFCore::checkForNewFile() {
1143 QDateTime newModTime;
1144
1145 if (doc->getFileName()) {
1146 newModTime = QFileInfo(doc->getFileName()->getCString()).lastModified();
1147 if (newModTime != modTime) {
1148 modTime = newModTime;
1149 return gTrue;
1150 }
1151 }
1152 return gFalse;
1153 }
1154
1155 void QtPDFCore::preLoad() {
1156 if (preLoadCbk) {
1157 (*preLoadCbk)(preLoadCbkData);
1158 }
1159 }
1160
1161 void QtPDFCore::postLoad() {
1162 if (postLoadCbk) {
1163 (*postLoadCbk)(postLoadCbkData);
1164 }
1165 }
1166
1167 //------------------------------------------------------------------------
1168 // password dialog
1169 //------------------------------------------------------------------------
1170
1171 GString *QtPDFCore::getPassword() {
1172 QString s;
1173 bool ok;
1174
1175 if (!showPasswordDialog) {
1176 return NULL;
1177 }
1178 s = QInputDialog::getText(viewport, "PDF Password",
1179 "This document requires a password",
1180 QLineEdit::Password, "", &ok, Qt::Dialog);
1181 if (ok) {
1182 return new GString(s.toLocal8Bit().constData());
1183 } else {
1184 return NULL;
1185 }
1186 }