"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.
For more information about "QtPDFCore.cc" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
4.03_vs_4.04.
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 }