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