1 //========================================================================
2 //
3 // PDFCore.cc
4 //
5 // Copyright 2004-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 "gmempp.h"
17 #include "GString.h"
18 #include "GList.h"
19 #include "GlobalParams.h"
20 #include "Splash.h"
21 #include "SplashBitmap.h"
22 #include "SplashPattern.h"
23 #include "SplashPath.h"
24 #include "Error.h"
25 #include "ErrorCodes.h"
26 #include "PDFDoc.h"
27 #include "Link.h"
28 #include "Annot.h"
29 #include "AcroForm.h"
30 #include "OptionalContent.h"
31 #include "TileMap.h"
32 #include "TileCache.h"
33 #include "TileCompositor.h"
34 #include "PDFCore.h"
35
36 //------------------------------------------------------------------------
37 // PDFCore
38 //------------------------------------------------------------------------
39
PDFCore(SplashColorMode colorMode,int bitmapRowPad,GBool reverseVideo,SplashColorPtr paperColor)40 PDFCore::PDFCore(SplashColorMode colorMode, int bitmapRowPad,
41 GBool reverseVideo, SplashColorPtr paperColor) {
42 GString *initialZoom, *initialDisplayMode;
43 int z, i;
44
45 doc = NULL;
46
47 linksPage = 0;
48 links = NULL;
49
50 annotsPage = 0;
51 annots = NULL;
52
53 textPage = 0;
54 textDPI = 0;
55 textRotate = 0;
56 textOutCtrl.mode = textOutPhysLayout;
57 text = NULL;
58
59 state = new DisplayState(globalParams->getMaxTileWidth(),
60 globalParams->getMaxTileHeight(),
61 globalParams->getTileCacheSize(),
62 globalParams->getWorkerThreads(),
63 colorMode, bitmapRowPad);
64 tileMap = new TileMap(state);
65 tileCache = new TileCache(state);
66 tileCompositor = new TileCompositor(state, tileMap, tileCache);
67 bitmapFinished = gTrue;
68
69 state->setReverseVideo(reverseVideo);
70 state->setPaperColor(paperColor);
71 initialZoom = globalParams->getInitialZoom();
72 if (!initialZoom->cmp("page")) {
73 state->setZoom(zoomPage);
74 } else if (!initialZoom->cmp("width")) {
75 state->setZoom(zoomWidth);
76 } else {
77 z = atoi(initialZoom->getCString());
78 if (z <= 0) {
79 z = zoomWidth;
80 }
81 state->setZoom(z);
82 }
83 delete initialZoom;
84 initialDisplayMode = globalParams->getInitialDisplayMode();
85 if (!initialDisplayMode->cmp("single")) {
86 state->setDisplayMode(displaySingle);
87 } else if (!initialDisplayMode->cmp("sideBySideSingle")) {
88 state->setDisplayMode(displaySideBySideSingle);
89 } else if (!initialDisplayMode->cmp("sideBySideContinuous")) {
90 state->setDisplayMode(displaySideBySideContinuous);
91 } else if (!initialDisplayMode->cmp("horizontalContinuous")) {
92 state->setDisplayMode(displayHorizontalContinuous);
93 } else {
94 state->setDisplayMode(displayContinuous);
95 }
96 delete initialDisplayMode;
97
98 selectMode = selectModeBlock;
99 selectPage = 0;
100 selectStartX = selectStartY = 0;
101
102 historyCur = pdfHistorySize - 1;
103 historyBLen = historyFLen = 0;
104 for (i = 0; i < pdfHistorySize; ++i) {
105 history[i].fileName = NULL;
106 history[i].page = 0;
107 }
108 }
109
~PDFCore()110 PDFCore::~PDFCore() {
111 int i;
112
113 delete tileCompositor;
114 delete tileCache;
115 delete tileMap;
116 delete state;
117 clearPage();
118 if (doc) {
119 delete doc;
120 }
121 for (i = 0; i < pdfHistorySize; ++i) {
122 if (history[i].fileName) {
123 #ifdef _WIN32
124 delete[] history[i].fileName;
125 #else
126 delete history[i].fileName;
127 #endif
128 }
129 }
130 }
131
loadFile(GString * fileName,GString * ownerPassword,GString * userPassword)132 int PDFCore::loadFile(GString *fileName, GString *ownerPassword,
133 GString *userPassword) {
134 int err;
135
136 setBusyCursor(gTrue);
137 err = loadFile2(new PDFDoc(fileName->copy(), ownerPassword, userPassword,
138 this));
139 setBusyCursor(gFalse);
140 return err;
141 }
142
143 #ifdef _WIN32
loadFile(wchar_t * fileName,int fileNameLen,GString * ownerPassword,GString * userPassword)144 int PDFCore::loadFile(wchar_t *fileName, int fileNameLen,
145 GString *ownerPassword, GString *userPassword) {
146 int err;
147
148 setBusyCursor(gTrue);
149 err = loadFile2(new PDFDoc(fileName, fileNameLen,
150 ownerPassword, userPassword, this));
151 setBusyCursor(gFalse);
152 return err;
153 }
154 #endif
155
loadFile(BaseStream * stream,GString * ownerPassword,GString * userPassword)156 int PDFCore::loadFile(BaseStream *stream, GString *ownerPassword,
157 GString *userPassword) {
158 int err;
159
160 setBusyCursor(gTrue);
161 err = loadFile2(new PDFDoc(stream, ownerPassword, userPassword, this));
162 setBusyCursor(gFalse);
163 return err;
164 }
165
loadDoc(PDFDoc * docA)166 void PDFCore::loadDoc(PDFDoc *docA) {
167 setBusyCursor(gTrue);
168 loadFile2(docA);
169 setBusyCursor(gFalse);
170 }
171
reload()172 int PDFCore::reload() {
173 int err;
174
175 if (!doc->getFileName()) {
176 return errOpenFile;
177 }
178 setBusyCursor(gTrue);
179 err = loadFile2(new PDFDoc(doc->getFileName()->copy(), NULL, NULL, this));
180 setBusyCursor(gFalse);
181 startUpdate();
182 finishUpdate(gTrue, gFalse);
183 return err;
184 }
185
loadFile2(PDFDoc * newDoc)186 int PDFCore::loadFile2(PDFDoc *newDoc) {
187 int err;
188
189 clearSelection();
190
191 // open the PDF file
192 if (!newDoc->isOk()) {
193 err = newDoc->getErrorCode();
194 delete newDoc;
195 return err;
196 }
197
198 preLoad();
199
200 // replace old document
201 // NB: do not delete doc until after DisplayState::setDoc() returns
202 state->setDoc(newDoc);
203 if (doc) {
204 delete doc;
205 }
206 doc = newDoc;
207 clearPage();
208
209 postLoad();
210
211 return errNone;
212 }
213
clear()214 void PDFCore::clear() {
215 if (!doc) {
216 return;
217 }
218
219 // no document
220 // NB: do not delete doc until after DisplayState::setDoc() returns
221 state->setDoc(NULL);
222 delete doc;
223 doc = NULL;
224 clearPage();
225
226 // redraw
227 state->setScrollPosition(1, 0, 0);
228 invalidateWholeWindow();
229 updateScrollbars();
230 }
231
takeDoc(GBool redraw)232 PDFDoc *PDFCore::takeDoc(GBool redraw) {
233 PDFDoc *docA;
234
235 if (!doc) {
236 return NULL;
237 }
238
239 // no document
240 // NB: do not delete doc until after DisplayState::setDoc() returns
241 state->setDoc(NULL);
242 docA = doc;
243 doc = NULL;
244 clearPage();
245
246 // redraw
247 state->setScrollPosition(1, 0, 0);
248 if (redraw) {
249 invalidateWholeWindow();
250 updateScrollbars();
251 }
252
253 return docA;
254 }
255
displayPage(int page,GBool scrollToTop,GBool scrollToBottom,GBool addToHist)256 void PDFCore::displayPage(int page, GBool scrollToTop,
257 GBool scrollToBottom, GBool addToHist) {
258 int scrollX, scrollY;
259
260 if (page <= 0 || page > doc->getNumPages()) {
261 return;
262 }
263 if (!scrollToTop &&
264 (state->getDisplayMode() == displayContinuous ||
265 state->getDisplayMode() == displaySideBySideContinuous)) {
266 scrollY = tileMap->getPageTopY(page)
267 + (state->getScrollY()
268 - tileMap->getPageTopY(tileMap->getFirstPage()));
269 } else if (scrollToTop ||
270 state->getDisplayMode() == displayContinuous ||
271 state->getDisplayMode() == displaySideBySideContinuous) {
272 scrollY = tileMap->getPageTopY(page);
273 } else if (scrollToBottom) {
274 scrollY = tileMap->getPageBottomY(page);
275 } else {
276 scrollY = state->getScrollY();
277 }
278 if (state->getDisplayMode() == displayHorizontalContinuous) {
279 scrollX = tileMap->getPageLeftX(page);
280 } else {
281 scrollX = state->getScrollX();
282 }
283 startUpdate();
284 state->setScrollPosition(page, scrollX, scrollY);
285 finishUpdate(addToHist, gTrue);
286 }
287
displayDest(LinkDest * dest)288 void PDFCore::displayDest(LinkDest *dest) {
289 Ref pageRef;
290 int page;
291 int dx, dy, scrollX, scrollY;
292
293 if (dest->isPageRef()) {
294 pageRef = dest->getPageRef();
295 page = doc->findPage(pageRef.num, pageRef.gen);
296 } else {
297 page = dest->getPageNum();
298 }
299 if (page <= 0 || page > doc->getNumPages()) {
300 page = 1;
301 }
302
303 switch (dest->getKind()) {
304 case destXYZ:
305 cvtUserToDev(page, dest->getLeft(), dest->getTop(), &dx, &dy);
306 scrollX = tileMap->getPageLeftX(page);
307 if (dest->getChangeLeft()) {
308 scrollX += dx;
309 }
310 scrollY = tileMap->getPageTopY(page);
311 if (dest->getChangeTop()) {
312 scrollY += dy;
313 }
314 startUpdate();
315 state->setScrollPosition(page, scrollX, scrollY);
316 finishUpdate(gTrue, gTrue);
317 break;
318 case destFit:
319 case destFitB:
320 state->setZoom(zoomPage);
321 scrollX = tileMap->getPageLeftX(page);
322 scrollY = tileMap->getPageTopY(page);
323 startUpdate();
324 state->setScrollPosition(page, scrollX, scrollY);
325 finishUpdate(gTrue, gTrue);
326 break;
327 case destFitH:
328 case destFitBH:
329 state->setZoom(zoomWidth);
330 scrollX = tileMap->getPageLeftX(page);
331 scrollY = tileMap->getPageTopY(page);
332 if (dest->getChangeTop()) {
333 cvtUserToDev(page, 0, dest->getTop(), &dx, &dy);
334 scrollY += dy;
335 }
336 startUpdate();
337 state->setScrollPosition(page, scrollX, scrollY);
338 finishUpdate(gTrue, gTrue);
339 break;
340 case destFitV:
341 case destFitBV:
342 state->setZoom(zoomHeight);
343 scrollX = tileMap->getPageLeftX(page);
344 scrollY = tileMap->getPageTopY(page);
345 if (dest->getChangeTop()) {
346 cvtUserToDev(page, dest->getLeft(), 0, &dx, &dy);
347 scrollX += dx;
348 }
349 startUpdate();
350 state->setScrollPosition(page, scrollX, scrollY);
351 finishUpdate(gTrue, gTrue);
352 break;
353 case destFitR:
354 zoomToRect(page, dest->getLeft(), dest->getTop(),
355 dest->getRight(), dest->getBottom());
356 break;
357 }
358 }
359
startUpdate()360 void PDFCore::startUpdate() {
361 }
362
finishUpdate(GBool addToHist,GBool checkForChangedFile)363 void PDFCore::finishUpdate(GBool addToHist, GBool checkForChangedFile) {
364 int scrollPage, scrollX, scrollY, maxScrollX, maxScrollY;
365
366 if (!doc) {
367 invalidateWholeWindow();
368 updateScrollbars();
369 return;
370 }
371
372 // check for changes to the PDF file
373 if (checkForChangedFile &&
374 doc->getFileName() &&
375 checkForNewFile()) {
376 loadFile(doc->getFileName());
377 }
378
379 // zero-page documents are a special case
380 // (check for this *after* checking for changes to the file)
381 if (!doc->getNumPages()) {
382 invalidateWholeWindow();
383 updateScrollbars();
384 return;
385 }
386
387 // check the scroll position
388 scrollPage = state->getScrollPage();
389 if (state->getDisplayMode() == displaySideBySideSingle &&
390 !(scrollPage & 1)) {
391 --scrollPage;
392 }
393 if (state->displayModeIsContinuous()) {
394 scrollPage = 0;
395 } else if (scrollPage <= 0 || scrollPage > doc->getNumPages()) {
396 scrollPage = 1;
397 }
398 scrollX = state->getScrollX();
399 scrollY = state->getScrollY();
400 // we need to set scrollPage before calling getScrollLimits()
401 state->setScrollPosition(scrollPage, scrollX, scrollY);
402 tileMap->getScrollLimits(&maxScrollX, &maxScrollY);
403 maxScrollX -= state->getWinW();
404 maxScrollY -= state->getWinH();
405 if (scrollX > maxScrollX) {
406 scrollX = maxScrollX;
407 }
408 if (scrollX < 0) {
409 scrollX = 0;
410 }
411 if (scrollY > maxScrollY) {
412 scrollY = maxScrollY;
413 }
414 if (scrollY < 0) {
415 scrollY = 0;
416 }
417 if (scrollPage != state->getScrollPage() ||
418 scrollX != state->getScrollX() ||
419 scrollY != state->getScrollY()) {
420 state->setScrollPosition(scrollPage, scrollX, scrollY);
421 }
422
423 // redraw
424 invalidateWholeWindow();
425 updateScrollbars();
426
427 // add to history
428 if (addToHist) {
429 addToHistory();
430 }
431 }
432
addToHistory()433 void PDFCore::addToHistory() {
434 PDFHistory h;
435 PDFHistory *cur;
436
437 cur = &history[historyCur];
438 h.page = tileMap->getMidPage();
439 #ifdef _WIN32
440 if (doc->getFileNameU()) {
441 h.fileName = (wchar_t *)gmallocn(MAX_PATH + 1, sizeof(wchar_t));
442 if (GetFullPathNameW(doc->getFileNameU(), MAX_PATH + 1,
443 h.fileName, NULL) == 0) {
444 h.fileName = NULL;
445 }
446 } else {
447 h.fileName = NULL;
448 }
449 #else
450 if (doc->getFileName()) {
451 h.fileName = doc->getFileName()->copy();
452 } else {
453 h.fileName = NULL;
454 }
455 #endif
456 if (historyBLen > 0 && h.page == cur->page) {
457 if (!h.fileName && !cur->fileName) {
458 return;
459 }
460 #ifdef _WIN32
461 if (h.fileName && cur->fileName && !wcscmp(h.fileName, cur->fileName)) {
462 gfree(h.fileName);
463 return;
464 }
465 #else
466 if (h.fileName && cur->fileName && !h.fileName->cmp(cur->fileName)) {
467 delete h.fileName;
468 return;
469 }
470 #endif
471 }
472 if (++historyCur == pdfHistorySize) {
473 historyCur = 0;
474 }
475 if (history[historyCur].fileName) {
476 #ifdef _WIN32
477 gfree(history[historyCur].fileName);
478 #else
479 delete history[historyCur].fileName;
480 #endif
481 }
482 history[historyCur] = h;
483 if (historyBLen < pdfHistorySize) {
484 ++historyBLen;
485 }
486 historyFLen = 0;
487 }
488
gotoNextPage(int inc,GBool top)489 GBool PDFCore::gotoNextPage(int inc, GBool top) {
490 GBool sideBySide;
491 int pg;
492
493 if (!doc || !doc->getNumPages()) {
494 return gFalse;
495 }
496 pg = tileMap->getFirstPage();
497 sideBySide = state->displayModeIsSideBySide();
498 if (pg + (sideBySide ? 2 : 1) > doc->getNumPages()) {
499 return gFalse;
500 }
501 if (sideBySide && inc < 2) {
502 inc = 2;
503 }
504 if ((pg += inc) > doc->getNumPages()) {
505 pg = doc->getNumPages();
506 }
507 displayPage(pg, top, gFalse);
508 return gTrue;
509 }
510
gotoPrevPage(int dec,GBool top,GBool bottom)511 GBool PDFCore::gotoPrevPage(int dec, GBool top, GBool bottom) {
512 int pg;
513
514 if (!doc || !doc->getNumPages()) {
515 return gFalse;
516 }
517 pg = tileMap->getFirstPage();
518 if (state->getDisplayMode() == displayContinuous &&
519 state->getScrollY() > tileMap->getPageTopY(pg)) {
520 ++pg;
521 } else if (state->getDisplayMode() == displaySideBySideContinuous &&
522 state->getScrollY() > tileMap->getPageTopY(pg)) {
523 pg += 2;
524 } else if (state->getDisplayMode() == displayHorizontalContinuous &&
525 state->getScrollX() > tileMap->getPageLeftX(pg)) {
526 ++pg;
527 }
528 if (pg <= 1) {
529 return gFalse;
530 }
531 if (state->displayModeIsSideBySide() && dec < 2) {
532 dec = 2;
533 }
534 if ((pg -= dec) < 1) {
535 pg = 1;
536 }
537 displayPage(pg, top, bottom);
538 return gTrue;
539 }
540
gotoNamedDestination(GString * dest)541 GBool PDFCore::gotoNamedDestination(GString *dest) {
542 LinkDest *d;
543
544 if (!doc) {
545 return gFalse;
546 }
547 if (!(d = doc->findDest(dest))) {
548 return gFalse;
549 }
550 displayDest(d);
551 delete d;
552 return gTrue;
553 }
554
goForward()555 GBool PDFCore::goForward() {
556 int pg;
557
558 if (historyFLen == 0) {
559 return gFalse;
560 }
561 if (++historyCur == pdfHistorySize) {
562 historyCur = 0;
563 }
564 --historyFLen;
565 ++historyBLen;
566 if (!history[historyCur].fileName) {
567 return gFalse;
568 }
569 #ifdef _WIN32
570 if (!doc ||
571 !doc->getFileNameU() ||
572 wcscmp(history[historyCur].fileName, doc->getFileNameU()) != 0) {
573 if (loadFile(history[historyCur].fileName,
574 (int)wcslen(history[historyCur].fileName)) != errNone) {
575 return gFalse;
576 }
577 }
578 #else
579 if (!doc ||
580 !doc->getFileName() ||
581 history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
582 if (loadFile(history[historyCur].fileName) != errNone) {
583 return gFalse;
584 }
585 }
586 #endif
587 pg = history[historyCur].page;
588 displayPage(pg, gFalse, gFalse, gFalse);
589 return gTrue;
590 }
591
goBackward()592 GBool PDFCore::goBackward() {
593 int pg;
594
595 if (historyBLen <= 1) {
596 return gFalse;
597 }
598 if (--historyCur < 0) {
599 historyCur = pdfHistorySize - 1;
600 }
601 --historyBLen;
602 ++historyFLen;
603 if (!history[historyCur].fileName) {
604 return gFalse;
605 }
606 #ifdef _WIN32
607 if (!doc ||
608 !doc->getFileNameU() ||
609 wcscmp(history[historyCur].fileName, doc->getFileNameU()) != 0) {
610 if (loadFile(history[historyCur].fileName,
611 (int)wcslen(history[historyCur].fileName)) != errNone) {
612 return gFalse;
613 }
614 }
615 #else
616 if (!doc ||
617 !doc->getFileName() ||
618 history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
619 if (loadFile(history[historyCur].fileName) != errNone) {
620 return gFalse;
621 }
622 }
623 #endif
624 pg = history[historyCur].page;
625 displayPage(pg, gFalse, gFalse, gFalse);
626 return gTrue;
627 }
628
scrollLeft(int nCols)629 void PDFCore::scrollLeft(int nCols) {
630 scrollTo(state->getScrollX() - nCols, state->getScrollY());
631 }
632
scrollRight(int nCols)633 void PDFCore::scrollRight(int nCols) {
634 scrollTo(state->getScrollX() + nCols, state->getScrollY());
635 }
636
scrollUp(int nLines,GBool snapToPage)637 void PDFCore::scrollUp(int nLines, GBool snapToPage) {
638 scrollTo(state->getScrollX(), state->getScrollY() - nLines, snapToPage);
639 }
640
scrollUpPrevPage(int nLines)641 void PDFCore::scrollUpPrevPage(int nLines) {
642 if (!state->displayModeIsContinuous() &&
643 state->getScrollY() == 0) {
644 gotoPrevPage(1, gFalse, gTrue);
645 } else {
646 scrollUp(nLines, gTrue);
647 }
648 }
649
scrollDown(int nLines,GBool snapToPage)650 void PDFCore::scrollDown(int nLines, GBool snapToPage) {
651 scrollTo(state->getScrollX(), state->getScrollY() + nLines, snapToPage);
652 }
653
scrollDownNextPage(int nLines)654 void PDFCore::scrollDownNextPage(int nLines) {
655 int horizMax, vertMax;
656
657 if (!state->displayModeIsContinuous()) {
658 tileMap->getScrollLimits(&horizMax, &vertMax);
659 if (state->getScrollY() >= vertMax - state->getWinH()) {
660 gotoNextPage(1, gTrue);
661 } else {
662 scrollDown(nLines);
663 }
664 } else {
665 scrollDown(nLines, gTrue);
666 }
667 }
668
scrollPageUp()669 void PDFCore::scrollPageUp() {
670 scrollUpPrevPage(state->getWinH());
671 }
672
scrollPageDown()673 void PDFCore::scrollPageDown() {
674 scrollDownNextPage(state->getWinH());
675 }
676
scrollTo(int x,int y,GBool snapToPage)677 void PDFCore::scrollTo(int x, int y, GBool snapToPage) {
678 int next, topPage, topPageY, sy, dy;
679
680 startUpdate();
681 state->setScrollPosition(state->getScrollPage(), x, y);
682
683 if (snapToPage) {
684 if (state->getDisplayMode() == displayContinuous ||
685 state->getDisplayMode() == displaySideBySideContinuous) {
686 next = state->getDisplayMode() == displaySideBySideContinuous ? 2 : 1;
687 topPage = tileMap->getFirstPage();
688 // NB: topPage can be out of bounds here, because the scroll
689 // position isn't adjusted until finishUpdate is called, below
690 if (topPage > 0 && topPage <= doc->getNumPages()) {
691 topPageY = tileMap->getPageTopY(topPage);
692 sy = state->getScrollY();
693 dy = sy - topPageY;
694 // note: dy can be negative here if the inter-page gap is at the
695 // top of the window
696 if (-16 < dy && dy < 16) {
697 state->setScrollPosition(state->getScrollPage(), x, topPageY);
698 } else if (topPage + next <= doc->getNumPages()) {
699 topPage += next;
700 topPageY = tileMap->getPageTopY(topPage);
701 dy = sy - topPageY;
702 if (-16 < dy && dy < 0) {
703 state->setScrollPosition(state->getScrollPage(), x, topPageY);
704 }
705 }
706 }
707 }
708 }
709
710 finishUpdate(gTrue, gTrue);
711 }
712
scrollToLeftEdge()713 void PDFCore::scrollToLeftEdge() {
714 scrollTo(0, state->getScrollY());
715 }
716
scrollToRightEdge()717 void PDFCore::scrollToRightEdge() {
718 int horizMax, vertMax;
719
720 tileMap->getScrollLimits(&horizMax, &vertMax);
721 scrollTo(horizMax - state->getWinW(), state->getScrollY());
722 }
723
scrollToTopEdge()724 void PDFCore::scrollToTopEdge() {
725 scrollTo(state->getScrollX(),
726 tileMap->getPageTopY(tileMap->getFirstPage()));
727 }
728
scrollToBottomEdge()729 void PDFCore::scrollToBottomEdge() {
730 scrollTo(state->getScrollX(),
731 tileMap->getPageBottomY(tileMap->getLastPage()));
732 }
733
scrollToTopLeft()734 void PDFCore::scrollToTopLeft() {
735 scrollTo(tileMap->getPageLeftX(tileMap->getFirstPage()),
736 tileMap->getPageTopY(tileMap->getFirstPage()));
737 }
738
scrollToBottomRight()739 void PDFCore::scrollToBottomRight() {
740 scrollTo(tileMap->getPageRightX(tileMap->getLastPage()),
741 tileMap->getPageBottomY(tileMap->getLastPage()));
742 }
743
scrollToCentered(int page,double x,double y)744 void PDFCore::scrollToCentered(int page, double x, double y) {
745 int wx, wy, sx, sy;
746
747 startUpdate();
748
749 // scroll to the requested page
750 state->setScrollPosition(page, tileMap->getPageLeftX(page),
751 tileMap->getPageTopY(page));
752
753 // scroll the requested point to the center of the window
754 cvtUserToWindow(page, x, y, &wx, &wy);
755 sx = state->getScrollX() + wx - state->getWinW() / 2;
756 sy = state->getScrollY() + wy - state->getWinH() / 2;
757 state->setScrollPosition(page, sx, sy);
758
759 finishUpdate(gTrue, gFalse);
760 }
761
setZoom(double zoom)762 void PDFCore::setZoom(double zoom) {
763 int page;
764
765 if (state->getZoom() == zoom) {
766 return;
767 }
768 if (!doc || !doc->getNumPages()) {
769 state->setZoom(zoom);
770 return;
771 }
772 startUpdate();
773 page = tileMap->getFirstPage();
774 state->setZoom(zoom);
775 state->setScrollPosition(page, tileMap->getPageLeftX(page),
776 tileMap->getPageTopY(page));
777 finishUpdate(gTrue, gTrue);
778 }
779
zoomToRect(int page,double ulx,double uly,double lrx,double lry)780 void PDFCore::zoomToRect(int page, double ulx, double uly,
781 double lrx, double lry) {
782 int x0, y0, x1, y1, sx, sy, t;
783 double dpi, rx, ry, zoom;
784
785 startUpdate();
786
787 // set the new zoom level
788 cvtUserToDev(page, ulx, uly, &x0, &y0);
789 cvtUserToDev(page, lrx, lry, &x1, &y1);
790 if (x0 > x1) {
791 t = x0; x0 = x1; x1 = t;
792 }
793 if (y0 > y1) {
794 t = y0; y0 = y1; y1 = t;
795 }
796 rx = (double)state->getWinW() / (double)(x1 - x0);
797 ry = (double)state->getWinH() / (double)(y1 - y0);
798 dpi = tileMap->getDPI(page);
799 if (rx < ry) {
800 zoom = rx * (dpi / (0.01 * 72));
801 } else {
802 zoom = ry * (dpi / (0.01 * 72));
803 }
804 state->setZoom(zoom);
805
806 // scroll to the requested page
807 state->setScrollPosition(page, tileMap->getPageLeftX(page),
808 tileMap->getPageTopY(page));
809
810 // scroll the requested rectangle to the center of the window
811 cvtUserToWindow(page, 0.5 * (ulx + lrx), 0.5 * (uly + lry), &x0, &y0);
812 sx = state->getScrollX() + x0 - state->getWinW() / 2;
813 sy = state->getScrollY() + y0 - state->getWinH() / 2;
814 state->setScrollPosition(page, sx, sy);
815
816 finishUpdate(gTrue, gFalse);
817 }
818
zoomCentered(double zoom)819 void PDFCore::zoomCentered(double zoom) {
820 int page, wx, wy, sx, sy;
821 double cx, cy;
822
823 if (state->getZoom() == zoom) {
824 return;
825 }
826
827 startUpdate();
828
829 // get the center of the window, in user coords
830 cvtWindowToUser(state->getWinW() / 2, state->getWinH() / 2,
831 &page, &cx, &cy);
832
833 // set the new zoom level
834 state->setZoom(zoom);
835
836 // scroll to re-center
837 cvtUserToWindow(page, cx, cy, &wx, &wy);
838 sx = state->getScrollX() + wx - state->getWinW() / 2;
839 sy = state->getScrollY() + wy - state->getWinH() / 2;
840 state->setScrollPosition(page, sx, sy);
841
842 finishUpdate(gTrue, gFalse);
843 }
844
845 // Zoom so that the current page(s) fill the window width. Maintain
846 // the vertical center.
zoomToCurrentWidth()847 void PDFCore::zoomToCurrentWidth() {
848 int page0, page1, page, gap;
849 double w, w1, zoom;
850
851 startUpdate();
852
853 // get first and last pages
854 page0 = tileMap->getFirstPage();
855 page1 = tileMap->getLastPage();
856
857 // compute the desired width (in points)
858 gap = 0;
859 switch (state->getDisplayMode()) {
860 case displaySingle:
861 default:
862 w = tileMap->getPageBoxWidth(page0);
863 break;
864 case displayContinuous:
865 w = 0;
866 for (page = page0; page <= page1; ++page) {
867 w1 = tileMap->getPageBoxWidth(page);
868 if (w1 > w) {
869 w = w1;
870 }
871 }
872 break;
873 case displaySideBySideSingle:
874 w = tileMap->getPageBoxWidth(page0);
875 if (page1 != page0) {
876 w += tileMap->getPageBoxWidth(page1);
877 gap = tileMap->getSideBySidePageSpacing();
878 }
879 break;
880 case displaySideBySideContinuous:
881 w = 0;
882 for (page = page0; w <= page1; w += 2) {
883 w1 = tileMap->getPageBoxWidth(page);
884 if (page + 1 <= doc->getNumPages()) {
885 w1 += tileMap->getPageBoxWidth(page + 1);
886 }
887 if (w1 > w) {
888 w = w1;
889 }
890 }
891 gap = tileMap->getSideBySidePageSpacing();
892 break;
893 case displayHorizontalContinuous:
894 w = 0;
895 gap = 0;
896 for (page = page0; page <= page1; ++page) {
897 w += tileMap->getPageBoxWidth(page);
898 if (page != page0) {
899 gap += tileMap->getHorizContinuousPageSpacing();
900 }
901 }
902 break;
903 }
904
905 // set the new zoom level
906 zoom = 100.0 * (state->getWinW() - gap) / w;
907 state->setZoom(zoom);
908
909 // scroll so that the first page is at the left edge of the window
910 state->setScrollPosition(page0, tileMap->getPageLeftX(page0),
911 tileMap->getPageTopY(page0));
912
913 finishUpdate(gTrue, gFalse);
914 }
915
setRotate(int rotate)916 void PDFCore::setRotate(int rotate) {
917 int page;
918
919 if (state->getRotate() == rotate) {
920 return;
921 }
922 if (!doc || !doc->getNumPages()) {
923 state->setRotate(rotate);
924 return;
925 }
926 startUpdate();
927 page = tileMap->getFirstPage();
928 state->setRotate(rotate);
929 state->setScrollPosition(page, tileMap->getPageLeftX(page),
930 tileMap->getPageTopY(page));
931 finishUpdate(gTrue, gTrue);
932 }
933
setDisplayMode(DisplayMode mode)934 void PDFCore::setDisplayMode(DisplayMode mode) {
935 int page;
936
937 if (state->getDisplayMode() == mode) {
938 return;
939 }
940 if (!doc || !doc->getNumPages()) {
941 state->setDisplayMode(mode);
942 return;
943 }
944 startUpdate();
945 page = tileMap->getFirstPage();
946 state->setDisplayMode(mode);
947 state->setScrollPosition(page, tileMap->getPageLeftX(page),
948 tileMap->getPageTopY(page));
949 finishUpdate(gTrue, gTrue);
950 }
951
setOCGState(OptionalContentGroup * ocg,GBool ocgState)952 void PDFCore::setOCGState(OptionalContentGroup *ocg, GBool ocgState) {
953 if (ocgState != ocg->getState()) {
954 ocg->setState(ocgState);
955 state->optionalContentChanged();
956 invalidateWholeWindow();
957 }
958 }
959
setSelectMode(SelectMode mode)960 void PDFCore::setSelectMode(SelectMode mode) {
961 if (mode != selectMode) {
962 selectMode = mode;
963 clearSelection();
964 }
965 }
966
getSelectionColor()967 SplashColorPtr PDFCore::getSelectionColor() {
968 return state->getSelectColor();
969 }
970
setSelectionColor(SplashColor color)971 void PDFCore::setSelectionColor(SplashColor color) {
972 int wx0, wy0, wx1, wy1;
973
974 state->setSelectColor(color);
975 if (state->hasSelection()) {
976 getSelectionBBox(&wx0, &wy0, &wx1, &wy1);
977 checkInvalidate(wx0, wy0, wx1 - wx0, wy1 - wy0);
978 }
979 }
980
setSelection(int page,int x0,int y0,int x1,int y1)981 void PDFCore::setSelection(int page, int x0, int y0, int x1, int y1) {
982 SelectRect *rect;
983 GBool moveLeft, moveTop, moveRight, moveBottom, needScroll;
984 double selectX0, selectY0, selectX1, selectY1;
985 int oldWx0, oldWy0, oldWx1, oldWy1, ix0, iy0, ix1, iy1;
986 int wx0, wy0, wx1, wy1, sx, sy, t;
987
988
989 // if selection rectangle is empty, clear the selection
990 if (x0 == x1 || y0 == y1) {
991 clearSelection();
992 return;
993 }
994
995 // x0 = left, x1 = right
996 // y0 = top, y1 = bottom
997 if (x0 > x1) {
998 t = x0; x0 = x1; x1 = t;
999 }
1000 if (y0 > y1) {
1001 t = y0; y0 = y1; y1 = t;
1002 }
1003
1004 // convert new selection coords to user space and window space
1005 tileMap->cvtDevToUser(page, x0, y0, &selectX0, &selectY0);
1006 tileMap->cvtDevToUser(page, x1, y1, &selectX1, &selectY1);
1007 cvtUserToWindow(page, selectX0, selectY0, &wx0, &wy0);
1008 cvtUserToWindow(page, selectX1, selectY1, &wx1, &wy1);
1009 if (wx0 > wx1) {
1010 t = wx0; wx0 = wx1; wx1 = t;
1011 }
1012 if (wy0 > wy1) {
1013 t = wy0; wy0 = wy1; wy1 = t;
1014 }
1015
1016 // convert current selection coords to window space;
1017 // check which edges moved
1018 if (state->hasSelection()) {
1019 rect = state->getSelectRect(0);
1020 tileMap->cvtUserToWindow(rect->page, rect->x0, rect->y0, &oldWx0, &oldWy0);
1021 tileMap->cvtUserToWindow(rect->page, rect->x1, rect->y1, &oldWx1, &oldWy1);
1022 if (oldWx0 > oldWx1) {
1023 t = oldWx0; oldWx0 = oldWx1; oldWx1 = t;
1024 }
1025 if (oldWy0 > oldWy1) {
1026 t = oldWy0; oldWy0 = oldWy1; oldWy1 = t;
1027 }
1028 moveLeft = wx0 != oldWx0;
1029 moveTop = wy0 != oldWy0;
1030 moveRight = wx1 != oldWx1;
1031 moveBottom = wy1 != oldWy1;
1032 } else {
1033 oldWx0 = wx0;
1034 oldWy0 = wy0;
1035 oldWx1 = wx1;
1036 oldWy1 = wy1;
1037 moveLeft = moveTop = moveRight = moveBottom = gTrue;
1038 }
1039
1040 // set the new selection
1041 state->setSelection(page, selectX0, selectY0, selectX1, selectY1);
1042
1043 // scroll if necessary
1044 needScroll = gFalse;
1045 sx = state->getScrollX();
1046 sy = state->getScrollY();
1047 if (moveLeft && wx0 < 0) {
1048 sx += wx0;
1049 needScroll = gTrue;
1050 } else if (moveRight && wx1 >= state->getWinW()) {
1051 sx += wx1 - state->getWinW();
1052 needScroll = gTrue;
1053 } else if (moveLeft && wx0 >= state->getWinW()) {
1054 sx += wx0 - state->getWinW();
1055 needScroll = gTrue;
1056 } else if (moveRight && wx1 < 0) {
1057 sx += wx1;
1058 needScroll = gTrue;
1059 }
1060 if (moveTop && wy0 < 0) {
1061 sy += wy0;
1062 needScroll = gTrue;
1063 } else if (moveBottom && wy1 >= state->getWinH()) {
1064 sy += wy1 - state->getWinH();
1065 needScroll = gTrue;
1066 } else if (moveTop && wy0 >= state->getWinH()) {
1067 sy += wy0 - state->getWinH();
1068 needScroll = gTrue;
1069 } else if (moveBottom && wy1 < 0) {
1070 sy += wy1;
1071 needScroll = gTrue;
1072 }
1073 if (needScroll) {
1074 scrollTo(sx, sy);
1075 } else {
1076 ix0 = (wx0 < oldWx0) ? wx0 : oldWx0;
1077 iy0 = (wy0 < oldWy0) ? wy0 : oldWy0;
1078 ix1 = (wx1 > oldWx1) ? wx1 : oldWx1;
1079 iy1 = (wy1 > oldWy1) ? wy1 : oldWy1;
1080 checkInvalidate(ix0, iy0, ix1 - ix0, iy1 - iy0);
1081 }
1082 }
1083
setLinearSelection(int page,TextPosition * pos0,TextPosition * pos1)1084 void PDFCore::setLinearSelection(int page, TextPosition *pos0,
1085 TextPosition *pos1) {
1086 TextPosition begin, end;
1087 GList *rects;
1088 GBool moveLeft, moveTop, moveRight, moveBottom, needScroll;
1089 double x0, y0, x1, y1, x2, y2, x3, y3;
1090 double ux0, uy0, ux1, uy1;
1091 int oldWx0, oldWy0, oldWx1, oldWy1, ix0, iy0, ix1, iy1;
1092 int wx0, wy0, wx1, wy1;
1093 int sx, sy, colIdx;
1094
1095
1096 // if selection rectangle is empty, clear the selection
1097 if (*pos0 == *pos1) {
1098 clearSelection();
1099 return;
1100 }
1101
1102 // swap into correct order
1103 if (*pos0 < *pos1) {
1104 begin = *pos0;
1105 end = *pos1;
1106 } else {
1107 begin = *pos1;
1108 end = *pos0;
1109 }
1110
1111 // build the list of rectangles
1112 //~ this doesn't handle RtL, vertical, or rotated text
1113 loadText(page);
1114 rects = new GList();
1115 if (begin.colIdx == end.colIdx &&
1116 begin.parIdx == end.parIdx &&
1117 begin.lineIdx == end.lineIdx) {
1118 // same line
1119 text->convertPosToPointUpper(&begin, &x0, &y0);
1120 text->convertPosToPointLower(&end, &x1, &y1);
1121 cvtDevToUser(page, (int)(x0 + 0.5), (int)(y0 + 0.5), &ux0, &uy0);
1122 cvtDevToUser(page, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux1, &uy1);
1123 rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
1124 } else if (begin.colIdx == end.colIdx) {
1125 // same column
1126 text->convertPosToPointUpper(&begin, &x0, &y0);
1127 text->convertPosToPointRightEdge(&begin, &x1, &y1);
1128 text->convertPosToPointLeftEdge(&end, &x2, &y2);
1129 text->convertPosToPointLower(&end, &x3, &y3);
1130 cvtDevToUser(page, (int)(x0 + 0.5), (int)(y0 + 0.5), &ux0, &uy0);
1131 cvtDevToUser(page, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux1, &uy1);
1132 rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
1133 cvtDevToUser(page, (int)(x2 + 0.5), (int)(y1 + 0.5), &ux0, &uy0);
1134 cvtDevToUser(page, (int)(x1 + 0.5), (int)(y2 + 0.5), &ux1, &uy1);
1135 rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
1136 cvtDevToUser(page, (int)(x2 + 0.5), (int)(y2 + 0.5), &ux0, &uy0);
1137 cvtDevToUser(page, (int)(x3 + 0.5), (int)(y3 + 0.5), &ux1, &uy1);
1138 rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
1139 } else {
1140 // different columns
1141 text->convertPosToPointUpper(&begin, &x0, &y0);
1142 text->convertPosToPointRightEdge(&begin, &x1, &y1);
1143 text->getColumnLowerLeft(begin.colIdx, &x2, &y2);
1144 cvtDevToUser(page, (int)(x0 + 0.5), (int)(y0 + 0.5), &ux0, &uy0);
1145 cvtDevToUser(page, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux1, &uy1);
1146 rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
1147 cvtDevToUser(page, (int)(x2 + 0.5), (int)(y1 + 0.5), &ux0, &uy0);
1148 cvtDevToUser(page, (int)(x1 + 0.5), (int)(y2 + 0.5), &ux1, &uy1);
1149 rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
1150 for (colIdx = begin.colIdx + 1; colIdx < end.colIdx; ++colIdx) {
1151 text->getColumnLowerLeft(colIdx, &x0, &y0);
1152 text->getColumnUpperRight(colIdx, &x1, &y1);
1153 cvtDevToUser(page, (int)(x0 + 0.5), (int)(y1 + 0.5), &ux0, &uy0);
1154 cvtDevToUser(page, (int)(x1 + 0.5), (int)(y0 + 0.5), &ux1, &uy1);
1155 rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
1156 }
1157 text->getColumnUpperRight(end.colIdx, &x0, &y0);
1158 text->convertPosToPointLeftEdge(&end, &x1, &y1);
1159 text->convertPosToPointLower(&end, &x2, &y2);
1160 cvtDevToUser(page, (int)(x1 + 0.5), (int)(y0 + 0.5), &ux0, &uy0);
1161 cvtDevToUser(page, (int)(x0 + 0.5), (int)(y1 + 0.5), &ux1, &uy1);
1162 rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
1163 cvtDevToUser(page, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux0, &uy0);
1164 cvtDevToUser(page, (int)(x2 + 0.5), (int)(y2 + 0.5), &ux1, &uy1);
1165 rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
1166 }
1167
1168 // get window coord bboxes for old selection and new selection;
1169 // check which edges moved
1170 if (state->hasSelection()) {
1171 getSelectionBBox(&oldWx0, &oldWy0, &oldWx1, &oldWy1);
1172 getSelectRectListBBox(rects, &wx0, &wy0, &wx1, &wy1);
1173 moveLeft = wx0 != oldWx0;
1174 moveTop = wy0 != oldWy0;
1175 moveRight = wx1 != oldWx1;
1176 moveBottom = wy1 != oldWy1;
1177 } else {
1178 getSelectRectListBBox(rects, &wx0, &wy0, &wx1, &wy1);
1179 oldWx0 = wx0;
1180 oldWy0 = wy0;
1181 oldWx1 = wx1;
1182 oldWy1 = wy1;
1183 moveLeft = moveTop = moveRight = moveBottom = gTrue;
1184 }
1185
1186 // set the new selection
1187 state->setSelection(rects);
1188
1189 // scroll if necessary
1190 needScroll = gFalse;
1191 sx = state->getScrollX();
1192 sy = state->getScrollY();
1193 if (moveLeft && wx0 < 0) {
1194 sx += wx0;
1195 needScroll = gTrue;
1196 } else if (moveRight && wx1 >= state->getWinW()) {
1197 sx += wx1 - state->getWinW();
1198 needScroll = gTrue;
1199 } else if (moveLeft && wx0 >= state->getWinW()) {
1200 sx += wx0 - state->getWinW();
1201 needScroll = gTrue;
1202 } else if (moveRight && wx1 < 0) {
1203 sx += wx1;
1204 needScroll = gTrue;
1205 }
1206 if (moveTop && wy0 < 0) {
1207 sy += wy0;
1208 needScroll = gTrue;
1209 } else if (moveBottom && wy1 >= state->getWinH()) {
1210 sy += wy1 - state->getWinH();
1211 needScroll = gTrue;
1212 } else if (moveTop && wy0 >= state->getWinH()) {
1213 sy += wy0 - state->getWinH();
1214 needScroll = gTrue;
1215 } else if (moveBottom && wy1 < 0) {
1216 sy += wy1;
1217 needScroll = gTrue;
1218 }
1219 if (needScroll) {
1220 scrollTo(sx, sy);
1221 } else {
1222 ix0 = (wx0 < oldWx0) ? wx0 : oldWx0;
1223 iy0 = (wy0 < oldWy0) ? wy0 : oldWy0;
1224 ix1 = (wx1 > oldWx1) ? wx1 : oldWx1;
1225 iy1 = (wy1 > oldWy1) ? wy1 : oldWy1;
1226 checkInvalidate(ix0, iy0, ix1 - ix0, iy1 - iy0);
1227 }
1228 }
1229
clearSelection()1230 void PDFCore::clearSelection() {
1231 int wx0, wy0, wx1, wy1;
1232
1233 if (state->hasSelection()) {
1234 getSelectionBBox(&wx0, &wy0, &wx1, &wy1);
1235 state->clearSelection();
1236 checkInvalidate(wx0, wy0, wx1 - wx0, wy1 - wy0);
1237 }
1238 }
1239
startSelectionDrag(int pg,int x,int y)1240 void PDFCore::startSelectionDrag(int pg, int x, int y) {
1241 clearSelection();
1242 if (selectMode == selectModeBlock) {
1243 selectPage = pg;
1244 selectStartX = x;
1245 selectStartY = y;
1246 } else { // selectModeLinear
1247 loadText(pg);
1248 if (text->findPointInside(x, y, &selectStartPos)) {
1249 selectPage = pg;
1250 } else {
1251 selectPage = 0;
1252 }
1253 }
1254 }
1255
moveSelectionDrag(int pg,int x,int y)1256 void PDFCore::moveSelectionDrag(int pg, int x, int y) {
1257 TextPosition pos;
1258
1259 // don't allow selections to span multiple pages
1260 // -- this also handles the case where a linear selection was started
1261 // outside any text column, in which case selectPage = 0
1262 if (pg != selectPage) {
1263 return;
1264 }
1265
1266 if (selectMode == selectModeBlock) {
1267 setSelection(pg, selectStartX, selectStartY, x, y);
1268 } else { // selectModeLinear
1269 loadText(pg);
1270 if (text->findPointNear(x, y, &pos)) {
1271 setLinearSelection(pg, &selectStartPos, &pos);
1272 }
1273 }
1274 }
1275
finishSelectionDrag()1276 void PDFCore::finishSelectionDrag() {
1277 // nothing
1278 }
1279
selectWord(int pg,int x,int y)1280 void PDFCore::selectWord(int pg, int x, int y) {
1281 TextPosition endPos;
1282
1283 loadText(pg);
1284 if (text->findWordPoints(x, y, &selectStartPos, &endPos)) {
1285 selectPage = pg;
1286 setLinearSelection(pg, &selectStartPos, &endPos);
1287 } else {
1288 selectPage = 0;
1289 }
1290 }
1291
selectLine(int pg,int x,int y)1292 void PDFCore::selectLine(int pg, int x, int y) {
1293 TextPosition endPos;
1294
1295 loadText(pg);
1296 if (text->findLinePoints(x, y, &selectStartPos, &endPos)) {
1297 selectPage = pg;
1298 setLinearSelection(pg, &selectStartPos, &endPos);
1299 } else {
1300 selectPage = 0;
1301 }
1302 }
1303
getSelection(int * pg,double * ulx,double * uly,double * lrx,double * lry)1304 GBool PDFCore::getSelection(int *pg, double *ulx, double *uly,
1305 double *lrx, double *lry) {
1306 SelectRect *rect;
1307 double xMin, yMin, xMax, yMax;
1308 int page, i;
1309
1310 if (!state->hasSelection()) {
1311 return gFalse;
1312 }
1313 page = state->getSelectRect(0)->page;
1314 xMin = yMin = xMax = yMax = 0;
1315 for (i = 0; i < state->getNumSelectRects(); ++i) {
1316 rect = state->getSelectRect(i);
1317 if (rect->page != page) {
1318 continue;
1319 }
1320 if (i == 0) {
1321 xMin = xMax = rect->x0;
1322 yMin = yMax = rect->y0;
1323 } else {
1324 if (rect->x0 < xMin) {
1325 xMin = rect->x0;
1326 } else if (rect->x0 > xMax) {
1327 xMax = rect->x0;
1328 }
1329 if (rect->y0 < yMin) {
1330 yMin = rect->y0;
1331 } else if (rect->y0 > yMax) {
1332 yMax = rect->y0;
1333 }
1334 }
1335 if (rect->x1 < xMin) {
1336 xMin = rect->x1;
1337 } else if (rect->x1 > xMax) {
1338 xMax = rect->x1;
1339 }
1340 if (rect->y1 < yMin) {
1341 yMin = rect->y1;
1342 } else if (rect->y1 > yMax) {
1343 yMax = rect->y1;
1344 }
1345 }
1346 *pg = page;
1347 *ulx = xMin;
1348 *uly = yMax;
1349 *lrx = xMax;
1350 *lry = yMin;
1351 return gTrue;
1352 }
1353
hasSelection()1354 GBool PDFCore::hasSelection() {
1355 return state->hasSelection();
1356 }
1357
setTextExtractionMode(TextOutputMode mode)1358 void PDFCore::setTextExtractionMode(TextOutputMode mode) {
1359 if (textOutCtrl.mode != mode) {
1360 textOutCtrl.mode = mode;
1361 if (text) {
1362 delete text;
1363 text = NULL;
1364 }
1365 textPage = 0;
1366 textDPI = 0;
1367 textRotate = 0;
1368 }
1369 }
1370
getDiscardDiagonalText()1371 GBool PDFCore::getDiscardDiagonalText() {
1372 return textOutCtrl.discardDiagonalText;
1373 }
1374
setDiscardDiagonalText(GBool discard)1375 void PDFCore::setDiscardDiagonalText(GBool discard) {
1376 if (textOutCtrl.discardDiagonalText != discard) {
1377 textOutCtrl.discardDiagonalText = discard;
1378 if (text) {
1379 delete text;
1380 text = NULL;
1381 }
1382 textPage = 0;
1383 textDPI = 0;
1384 textRotate = 0;
1385 }
1386 }
1387
extractText(int pg,double xMin,double yMin,double xMax,double yMax)1388 GString *PDFCore::extractText(int pg, double xMin, double yMin,
1389 double xMax, double yMax) {
1390 int x0, y0, x1, y1, t;
1391
1392 loadText(pg);
1393 cvtUserToDev(pg, xMin, yMin, &x0, &y0);
1394 cvtUserToDev(pg, xMax, yMax, &x1, &y1);
1395 if (x0 > x1) {
1396 t = x0; x0 = x1; x1 = t;
1397 }
1398 if (y0 > y1) {
1399 t = y0; y0 = y1; y1 = t;
1400 }
1401 return text->getText(x0, y0, x1, y1);
1402 }
1403
getSelectedText()1404 GString *PDFCore::getSelectedText() {
1405 SelectRect *rect;
1406 GString *ret, *s;
1407 int x0, y0, x1, y1, t, i;
1408
1409 if (!state->hasSelection()) {
1410 return NULL;
1411 }
1412 ret = new GString();
1413 for (i = 0; i < state->getNumSelectRects(); ++i) {
1414 rect = state->getSelectRect(i);
1415 loadText(rect->page);
1416 cvtUserToDev(rect->page, rect->x0, rect->y0, &x0, &y0);
1417 cvtUserToDev(rect->page, rect->x1, rect->y1, &x1, &y1);
1418 if (x0 > x1) {
1419 t = x0; x0 = x1; x1 = t;
1420 }
1421 if (y0 > y1) {
1422 t = y0; y0 = y1; y1 = t;
1423 }
1424 s = text->getText(x0, y0, x1, y1, state->getNumSelectRects() > 1);
1425 ret->append(s);
1426 delete s;
1427 }
1428 return ret;
1429 }
1430
find(char * s,GBool caseSensitive,GBool next,GBool backward,GBool wholeWord,GBool onePageOnly)1431 GBool PDFCore::find(char *s, GBool caseSensitive, GBool next, GBool backward,
1432 GBool wholeWord, GBool onePageOnly) {
1433 Unicode *u;
1434 int len, i;
1435 GBool ret;
1436
1437 // convert to Unicode
1438 len = (int)strlen(s);
1439 u = (Unicode *)gmallocn(len, sizeof(Unicode));
1440 for (i = 0; i < len; ++i) {
1441 u[i] = (Unicode)(s[i] & 0xff);
1442 }
1443
1444 ret = findU(u, len, caseSensitive, next, backward, wholeWord, onePageOnly);
1445
1446 gfree(u);
1447 return ret;
1448 }
1449
findU(Unicode * u,int len,GBool caseSensitive,GBool next,GBool backward,GBool wholeWord,GBool onePageOnly)1450 GBool PDFCore::findU(Unicode *u, int len, GBool caseSensitive,
1451 GBool next, GBool backward, GBool wholeWord,
1452 GBool onePageOnly) {
1453 TextOutputDev *textOut;
1454 SelectRect *rect;
1455 double xMin, yMin, xMax, yMax;
1456 int topPage, pg, x, y, x2, y2;
1457 GBool startAtTop, startAtLast, stopAtLast;
1458
1459 // check for zero-length string
1460 if (len == 0) {
1461 return gFalse;
1462 }
1463
1464 setBusyCursor(gTrue);
1465
1466 // search current page starting at previous result, current
1467 // selection, or top/bottom of page
1468 startAtTop = startAtLast = gFalse;
1469 rect = NULL;
1470 xMin = yMin = xMax = yMax = 0;
1471 topPage = tileMap->getFirstPage();
1472 pg = topPage;
1473 if (next) {
1474 if (textPage >= 1 && textPage <= doc->getNumPages()) {
1475 startAtLast = gTrue;
1476 pg = textPage;
1477 }
1478 } else if (state->hasSelection()) {
1479 rect = state->getSelectRect(0);
1480 pg = rect->page;
1481 cvtUserToDev(pg, rect->x0, rect->y0, &x, &y);
1482 cvtUserToDev(pg, rect->x1, rect->y1, &x2, &y2);
1483 if (x2 < x) {
1484 x = x2;
1485 }
1486 if (y2 < y) {
1487 y = y2;
1488 }
1489 if (backward) {
1490 xMin = x - 1;
1491 yMin = y - 1;
1492 } else {
1493 xMin = x + 1;
1494 yMin = y + 1;
1495 }
1496 } else {
1497 startAtTop = gTrue;
1498 }
1499 loadText(pg);
1500 if (text->findText(u, len, startAtTop, gTrue, startAtLast, gFalse,
1501 caseSensitive, backward, wholeWord,
1502 &xMin, &yMin, &xMax, &yMax)) {
1503 goto found;
1504 }
1505
1506 if (!onePageOnly) {
1507
1508 // search following/previous pages
1509 textOut = new TextOutputDev(NULL, &textOutCtrl, gFalse);
1510 if (!textOut->isOk()) {
1511 delete textOut;
1512 goto notFound;
1513 }
1514 for (pg = backward ? pg - 1 : pg + 1;
1515 backward ? pg >= 1 : pg <= doc->getNumPages();
1516 pg += backward ? -1 : 1) {
1517 doc->displayPage(textOut, pg, 72, 72, 0, gFalse, gTrue, gFalse);
1518 if (textOut->findText(u, len, gTrue, gTrue, gFalse, gFalse,
1519 caseSensitive, backward, wholeWord,
1520 &xMin, &yMin, &xMax, &yMax)) {
1521 delete textOut;
1522 goto foundPage;
1523 }
1524 }
1525
1526 // search previous/following pages
1527 for (pg = backward ? doc->getNumPages() : 1;
1528 backward ? pg > topPage : pg < topPage;
1529 pg += backward ? -1 : 1) {
1530 doc->displayPage(textOut, pg, 72, 72, 0, gFalse, gTrue, gFalse);
1531 if (textOut->findText(u, len, gTrue, gTrue, gFalse, gFalse,
1532 caseSensitive, backward, wholeWord,
1533 &xMin, &yMin, &xMax, &yMax)) {
1534 delete textOut;
1535 goto foundPage;
1536 }
1537 }
1538 delete textOut;
1539
1540 }
1541
1542 // search current page ending at previous result, current selection,
1543 // or bottom/top of page
1544 if (!startAtTop) {
1545 xMin = yMin = xMax = yMax = 0;
1546 if (next) {
1547 stopAtLast = gTrue;
1548 } else {
1549 stopAtLast = gFalse;
1550 cvtUserToDev(pg, rect->x1, rect->y1, &x, &y);
1551 xMax = x;
1552 yMax = y;
1553 }
1554 if (text->findText(u, len, gTrue, gFalse, gFalse, stopAtLast,
1555 caseSensitive, backward, wholeWord,
1556 &xMin, &yMin, &xMax, &yMax)) {
1557 goto found;
1558 }
1559 }
1560
1561 // not found
1562 notFound:
1563 setBusyCursor(gFalse);
1564 return gFalse;
1565
1566 // found on a different page
1567 foundPage:
1568 displayPage(pg, gTrue, gFalse);
1569 loadText(pg);
1570 if (!text->findText(u, len, gTrue, gTrue, gFalse, gFalse,
1571 caseSensitive, backward, wholeWord,
1572 &xMin, &yMin, &xMax, &yMax)) {
1573 // this can happen if coalescing is bad
1574 goto notFound;
1575 }
1576
1577 // found: change the selection
1578 found:
1579 setSelection(pg, (int)floor(xMin), (int)floor(yMin),
1580 (int)ceil(xMax), (int)ceil(yMax));
1581
1582 setBusyCursor(gFalse);
1583 return gTrue;
1584 }
1585
findAll(Unicode * u,int len,GBool caseSensitive,GBool wholeWord,int firstPage,int lastPage)1586 GList *PDFCore::findAll(Unicode *u, int len, GBool caseSensitive,
1587 GBool wholeWord, int firstPage, int lastPage) {
1588 GList *results = new GList();
1589
1590 TextOutputDev *textOut = new TextOutputDev(NULL, &textOutCtrl, gFalse);
1591 if (!textOut->isOk()) {
1592 delete textOut;
1593 return results;
1594 }
1595
1596 for (int pg = firstPage; pg <= lastPage; ++pg) {
1597 doc->displayPage(textOut, pg, 72, 72, 0, gFalse, gTrue, gFalse);
1598 GBool first = gTrue;
1599 while (1) {
1600 double xMin, yMin, xMax, yMax;
1601 if (!textOut->findText(u, len, first, gTrue, !first, gFalse,
1602 caseSensitive, gFalse, wholeWord,
1603 &xMin, &yMin, &xMax, &yMax)) {
1604 break;
1605 }
1606 double uxMin, uyMin, uxMax, uyMax, t;
1607 textOut->cvtDevToUser(xMin, yMin, &uxMin, &uyMin);
1608 textOut->cvtDevToUser(xMax, yMax, &uxMax, &uyMax);
1609 if (uxMin > uxMax) {
1610 t = uxMin; uxMin = uxMax; uxMax = t;
1611 }
1612 if (uyMin > uyMax) {
1613 t = uyMin; uyMin = uyMax; uyMax = t;
1614 }
1615 results->append(new FindResult(pg, uxMin, uyMin, uxMax, uyMax));
1616 first = gFalse;
1617 }
1618 }
1619
1620 delete textOut;
1621
1622 return results;
1623 }
1624
1625
cvtWindowToUser(int xw,int yw,int * pg,double * xu,double * yu)1626 GBool PDFCore::cvtWindowToUser(int xw, int yw,
1627 int *pg, double *xu, double *yu) {
1628 return tileMap->cvtWindowToUser(xw, yw, pg, xu, yu);
1629 }
1630
cvtWindowToDev(int xw,int yw,int * pg,int * xd,int * yd)1631 GBool PDFCore::cvtWindowToDev(int xw, int yw, int *pg, int *xd, int *yd) {
1632 return tileMap->cvtWindowToDev(xw, yw, pg, xd, yd);
1633 }
1634
cvtUserToWindow(int pg,double xu,double yu,int * xw,int * yw)1635 GBool PDFCore::cvtUserToWindow(int pg, double xu, double yu, int *xw, int *yw) {
1636 return tileMap->cvtUserToWindow(pg, xu, yu, xw, yw);
1637 }
1638
cvtUserToDev(int pg,double xu,double yu,int * xd,int * yd)1639 void PDFCore::cvtUserToDev(int pg, double xu, double yu, int *xd, int *yd) {
1640 tileMap->cvtUserToDev(pg, xu, yu, xd, yd);
1641 }
1642
cvtDevToWindow(int pg,int xd,int yd,int * xw,int * yw)1643 GBool PDFCore::cvtDevToWindow(int pg, int xd, int yd, int *xw, int *yw) {
1644 return tileMap->cvtDevToWindow(pg, xd, yd, xw, yw);
1645 }
1646
cvtDevToUser(int pg,int xd,int yd,double * xu,double * yu)1647 void PDFCore::cvtDevToUser(int pg, int xd, int yd, double *xu, double *yu) {
1648 tileMap->cvtDevToUser(pg, xd, yd, xu, yu);
1649 }
1650
getWindowPageRange(int x,int y,int w,int h,int * firstPage,int * lastPage)1651 void PDFCore::getWindowPageRange(int x, int y, int w, int h,
1652 int *firstPage, int *lastPage) {
1653 tileMap->getWindowPageRange(x, y, w, h, firstPage, lastPage);
1654 }
1655
getPageNum()1656 int PDFCore::getPageNum() {
1657 if (!doc || !doc->getNumPages()) {
1658 return 0;
1659 }
1660 return tileMap->getFirstPage();
1661 }
1662
getMidPageNum()1663 int PDFCore::getMidPageNum() {
1664 if (!doc || !doc->getNumPages()) {
1665 return 0;
1666 }
1667 return tileMap->getMidPage();
1668 }
1669
getZoom()1670 double PDFCore::getZoom() {
1671 return state->getZoom();
1672 }
1673
getZoomDPI(int page)1674 double PDFCore::getZoomDPI(int page) {
1675 if (!doc) {
1676 return 0;
1677 }
1678 return tileMap->getDPI(page);
1679 }
1680
getRotate()1681 int PDFCore::getRotate() {
1682 return state->getRotate();
1683 }
1684
getDisplayMode()1685 DisplayMode PDFCore::getDisplayMode() {
1686 return state->getDisplayMode();
1687 }
1688
getScrollX()1689 int PDFCore::getScrollX() {
1690 return state->getScrollX();
1691 }
1692
getScrollY()1693 int PDFCore::getScrollY() {
1694 return state->getScrollY();
1695 }
1696
getWindowWidth()1697 int PDFCore::getWindowWidth() {
1698 return state->getWinW();
1699 }
1700
getWindowHeight()1701 int PDFCore::getWindowHeight() {
1702 return state->getWinH();
1703 }
1704
setPaperColor(SplashColorPtr paperColor)1705 void PDFCore::setPaperColor(SplashColorPtr paperColor) {
1706 state->setPaperColor(paperColor);
1707 invalidateWholeWindow();
1708 }
1709
setMatteColor(SplashColorPtr matteColor)1710 void PDFCore::setMatteColor(SplashColorPtr matteColor) {
1711 state->setMatteColor(matteColor);
1712 invalidateWholeWindow();
1713 }
1714
setReverseVideo(GBool reverseVideo)1715 void PDFCore::setReverseVideo(GBool reverseVideo) {
1716 SplashColorPtr oldPaperColor;
1717 SplashColor newPaperColor;
1718 int i;
1719
1720 if (reverseVideo != state->getReverseVideo()) {
1721 state->setReverseVideo(reverseVideo);
1722 oldPaperColor = state->getPaperColor();
1723 for (i = 0; i < splashColorModeNComps[state->getColorMode()]; ++i) {
1724 newPaperColor[i] = oldPaperColor[i] ^ 0xff;
1725 }
1726 state->setPaperColor(newPaperColor);
1727 invalidateWholeWindow();
1728 }
1729 }
1730
1731
1732
findLink(int pg,double x,double y)1733 LinkAction *PDFCore::findLink(int pg, double x, double y) {
1734 loadLinks(pg);
1735 return links->find(x, y);
1736 }
1737
findAnnot(int pg,double x,double y)1738 Annot *PDFCore::findAnnot(int pg, double x, double y) {
1739 loadAnnots(pg);
1740 return annots->find(x, y);
1741 }
1742
findAnnotIdx(int pg,double x,double y)1743 int PDFCore::findAnnotIdx(int pg, double x, double y) {
1744 loadAnnots(pg);
1745 return annots->findIdx(x, y);
1746 }
1747
getAnnot(int idx)1748 Annot *PDFCore::getAnnot(int idx) {
1749 if (!annots) {
1750 return NULL;
1751 }
1752 if (idx < 0 || idx >= annots->getNumAnnots()) {
1753 return NULL;
1754 }
1755 return annots->getAnnot(idx);
1756 }
1757
findFormField(int pg,double x,double y)1758 AcroFormField *PDFCore::findFormField(int pg, double x, double y) {
1759 if (!doc->getCatalog()->getForm()) {
1760 return NULL;
1761 }
1762 return doc->getCatalog()->getForm()->findField(pg, x, y);
1763 }
1764
findFormFieldIdx(int pg,double x,double y)1765 int PDFCore::findFormFieldIdx(int pg, double x, double y) {
1766 if (!doc->getCatalog()->getForm()) {
1767 return -1;
1768 }
1769 return doc->getCatalog()->getForm()->findFieldIdx(pg, x, y);
1770 }
1771
getFormField(int idx)1772 AcroFormField *PDFCore::getFormField(int idx) {
1773 if (!doc->getCatalog()->getForm()) {
1774 return NULL;
1775 }
1776 if (idx < 0 || idx >= doc->getCatalog()->getForm()->getNumFields()) {
1777 return NULL;
1778 }
1779 return doc->getCatalog()->getForm()->getField(idx);
1780 }
1781
overText(int pg,double x,double y)1782 GBool PDFCore::overText(int pg, double x, double y) {
1783 loadText(pg);
1784 return text->checkPointInside(x, y);
1785 }
1786
forceRedraw()1787 void PDFCore::forceRedraw() {
1788 startUpdate();
1789 state->forceRedraw();
1790 finishUpdate(gFalse, gFalse);
1791 }
1792
setTileDoneCbk(void (* cbk)(void * data),void * data)1793 void PDFCore::setTileDoneCbk(void (*cbk)(void *data), void *data) {
1794 tileCache->setTileDoneCbk(cbk, data);
1795 }
1796
setWindowSize(int winWidth,int winHeight)1797 void PDFCore::setWindowSize(int winWidth, int winHeight) {
1798 GBool doScroll;
1799 int page, wx0, wy0, wx, wy, sx, sy;
1800 double ux, uy;
1801
1802 startUpdate();
1803
1804 wx0 = wy0 = 0; // make gcc happy
1805 doScroll = gFalse;
1806 if (state->getZoom() < 0 && state->displayModeIsContinuous()) {
1807 // save the user coordinates of the appropriate edge of the window
1808 if (state->getDisplayMode() == displayHorizontalContinuous) {
1809 wx0 = 0;
1810 wy0 = state->getWinH() / 2;
1811 } else {
1812 wx0 = state->getWinW() / 2;
1813 wy0 = 0;
1814 }
1815 if (!(doScroll = cvtWindowToUser(wx0, wy0, &page, &ux, &uy))) {
1816 // tweak the save position if it happens to fall in a gutter
1817 if (state->getDisplayMode() == displayContinuous) {
1818 wy0 += tileMap->getContinuousPageSpacing();
1819 } else if (state->getDisplayMode() == displaySideBySideContinuous) {
1820 wx0 += tileMap->getSideBySidePageSpacing();
1821 wy0 += tileMap->getContinuousPageSpacing();
1822 } else { // state->getDisplayMode() == displayHorizontalContinuous
1823 wx0 += tileMap->getHorizContinuousPageSpacing();
1824 }
1825 doScroll = cvtWindowToUser(wx0, wy0, &page, &ux, &uy);
1826 }
1827 }
1828
1829 state->setWindowSize(winWidth, winHeight);
1830
1831 if (doScroll) {
1832 // restore the saved scroll position
1833 cvtUserToWindow(page, ux, uy, &wx, &wy);
1834 sx = state->getScrollX();
1835 sy = state->getScrollY();
1836 if (state->getDisplayMode() == displayHorizontalContinuous) {
1837 sx += wx - wx0;
1838 } else {
1839 sy += wy - wy0;
1840 }
1841 state->setScrollPosition(page, sx, sy);
1842 }
1843
1844 finishUpdate(gTrue, gFalse);
1845 }
1846
getWindowBitmap(GBool wholeWindow)1847 SplashBitmap *PDFCore::getWindowBitmap(GBool wholeWindow) {
1848 GBool dummy;
1849
1850 return tileCompositor->getBitmap(wholeWindow ? &bitmapFinished : &dummy);
1851 }
1852
tick()1853 void PDFCore::tick() {
1854 if (!bitmapFinished) {
1855 invalidateWholeWindow();
1856 }
1857 }
1858
1859 // Clear cached info (links, text) that's tied to a PDFDoc.
clearPage()1860 void PDFCore::clearPage() {
1861 if (links) {
1862 delete links;
1863 }
1864 links = NULL;
1865 linksPage = 0;
1866
1867 if (annots) {
1868 delete annots;
1869 }
1870 annots = NULL;
1871 annotsPage = 0;
1872
1873 if (text) {
1874 delete text;
1875 }
1876 text = NULL;
1877 textPage = 0;
1878 textDPI = 0;
1879 textRotate = 0;
1880 }
1881
1882 // Load the links for <pg>.
loadLinks(int pg)1883 void PDFCore::loadLinks(int pg) {
1884 if (links && linksPage == pg) {
1885 return;
1886 }
1887 if (links) {
1888 delete links;
1889 }
1890 links = doc->getLinks(pg);
1891 linksPage = pg;
1892 }
1893
1894 // Load the annotations for <pg>.
loadAnnots(int pg)1895 void PDFCore::loadAnnots(int pg) {
1896 Object annotsObj;
1897
1898 if (annots && annotsPage == pg) {
1899 return;
1900 }
1901 if (annots) {
1902 delete annots;
1903 }
1904 doc->getCatalog()->getPage(pg)->getAnnots(&annotsObj);
1905 annots = new Annots(doc, &annotsObj);
1906 annotsObj.free();
1907 annotsPage = pg;
1908 }
1909
1910 // Extract text from <pg>.
loadText(int pg)1911 void PDFCore::loadText(int pg) {
1912 TextOutputDev *textOut;
1913 double dpi;
1914 int rotate;
1915
1916 dpi = tileMap->getDPI(pg);
1917 rotate = state->getRotate();
1918 if (text && textPage == pg && textDPI == dpi && textRotate == rotate) {
1919 return;
1920 }
1921 if (text) {
1922 delete text;
1923 }
1924 textOut = new TextOutputDev(NULL, &textOutCtrl, gFalse);
1925 if (!textOut->isOk()) {
1926 text = new TextPage(&textOutCtrl);
1927 } else {
1928 doc->displayPage(textOut, pg, dpi, dpi, rotate, gFalse, gTrue, gFalse);
1929 text = textOut->takeText();
1930 }
1931 delete textOut;
1932 textPage = pg;
1933 textDPI = dpi;
1934 textRotate = rotate;
1935 }
1936
getSelectionBBox(int * wxMin,int * wyMin,int * wxMax,int * wyMax)1937 void PDFCore::getSelectionBBox(int *wxMin, int *wyMin, int *wxMax, int *wyMax) {
1938 *wxMin = *wyMin = *wxMax = *wyMax = 0;
1939 if (!state->hasSelection()) {
1940 return;
1941 }
1942 getSelectRectListBBox(state->getSelectRects(), wxMin, wyMin, wxMax, wyMax);
1943 }
1944
getSelectRectListBBox(GList * rects,int * wxMin,int * wyMin,int * wxMax,int * wyMax)1945 void PDFCore::getSelectRectListBBox(GList *rects, int *wxMin, int *wyMin,
1946 int *wxMax, int *wyMax) {
1947 SelectRect *rect;
1948 int x, y, i;
1949
1950 *wxMin = *wyMin = *wxMax = *wyMax = 0;
1951 for (i = 0; i < rects->getLength(); ++i) {
1952 rect = (SelectRect *)rects->get(i);
1953 tileMap->cvtUserToWindow(rect->page, rect->x0, rect->y0, &x, &y);
1954 if (i == 0) {
1955 *wxMin = *wxMax = x;
1956 *wyMin = *wyMax = y;
1957 } else {
1958 if (x < *wxMin) {
1959 *wxMin = x;
1960 } else if (x > *wxMax) {
1961 *wxMax = x;
1962 }
1963 if (y < *wyMin) {
1964 *wyMin = y;
1965 } else if (y > *wyMax) {
1966 *wyMax = y;
1967 }
1968 }
1969 tileMap->cvtUserToWindow(rect->page, rect->x1, rect->y1, &x, &y);
1970 if (x < *wxMin) {
1971 *wxMin = x;
1972 } else if (x > *wxMax) {
1973 *wxMax = x;
1974 }
1975 if (y < *wyMin) {
1976 *wyMin = y;
1977 } else if (y > *wyMax) {
1978 *wyMax = y;
1979 }
1980 }
1981 }
1982
checkInvalidate(int x,int y,int w,int h)1983 void PDFCore::checkInvalidate(int x, int y, int w, int h) {
1984 if (x < 0) {
1985 w += x;
1986 x = 0;
1987 }
1988 if (x + w > state->getWinW()) {
1989 w = state->getWinW() - x;
1990 }
1991 if (w <= 0) {
1992 return;
1993 }
1994 if (y < 0) {
1995 h += y;
1996 y = 0;
1997 }
1998 if (y + h > state->getWinH()) {
1999 h = state->getWinH() - y;
2000 }
2001 if (h <= 0) {
2002 return;
2003 }
2004 invalidate(x, y, w, h);
2005 }
2006
invalidateWholeWindow()2007 void PDFCore::invalidateWholeWindow() {
2008 invalidate(0, 0, state->getWinW(), state->getWinH());
2009 }
2010