"Fossies" - the Fresh Open Source Software Archive 
Member "xpdf-4.04/xpdf/PDFCore.cc" (18 Apr 2022, 51111 Bytes) of package /linux/misc/xpdf-4.04.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
1 //========================================================================
2 //
3 // 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
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
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
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
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
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
166 void PDFCore::loadDoc(PDFDoc *docA) {
167 setBusyCursor(gTrue);
168 loadFile2(docA);
169 setBusyCursor(gFalse);
170 }
171
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
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
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
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
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
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
360 void PDFCore::startUpdate() {
361 }
362
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 // - if the bitmap is available (e.g., we just scrolled), we want to
425 // redraw immediately; if not, postpone the redraw until a
426 // tileDone or tick (incremental update) to avoid "flashing" the
427 // screen (drawing a blank background, followed by the actual
428 // content slightly later)
429 getWindowBitmap(gTrue);
430 if (bitmapFinished) {
431 invalidateWholeWindow();
432 }
433 updateScrollbars();
434
435 // add to history
436 if (addToHist) {
437 addToHistory();
438 }
439 }
440
441 void PDFCore::addToHistory() {
442 PDFHistory h;
443 PDFHistory *cur;
444
445 cur = &history[historyCur];
446 h.page = tileMap->getMidPage();
447 h.fileName = NULL;
448 #ifdef _WIN32
449 if (doc->getFileNameU()) {
450 wchar_t dummy;
451 // NB: if the buffer is too small, GetFullPathNameW returns a
452 // *maximum* required buffer size, which may be larger than the
453 // size actually used by the second call (it looks like it just
454 // adds the size of the current directory and the sizef of the
455 // input path)
456 DWORD nChars = GetFullPathNameW(doc->getFileNameU(), 1, &dummy, NULL);
457 if (nChars > 0) {
458 h.fileName = (wchar_t *)gmallocn(nChars, sizeof(wchar_t));
459 if (GetFullPathNameW(doc->getFileNameU(), nChars,
460 h.fileName, NULL) == 0) {
461 gfree(h.fileName);
462 h.fileName = NULL;
463 }
464 }
465 }
466 #else
467 if (doc->getFileName()) {
468 h.fileName = doc->getFileName()->copy();
469 }
470 #endif
471 if (historyBLen > 0 && h.page == cur->page) {
472 if (!h.fileName && !cur->fileName) {
473 return;
474 }
475 #ifdef _WIN32
476 if (h.fileName && cur->fileName && !wcscmp(h.fileName, cur->fileName)) {
477 gfree(h.fileName);
478 return;
479 }
480 #else
481 if (h.fileName && cur->fileName && !h.fileName->cmp(cur->fileName)) {
482 delete h.fileName;
483 return;
484 }
485 #endif
486 }
487 if (++historyCur == pdfHistorySize) {
488 historyCur = 0;
489 }
490 if (history[historyCur].fileName) {
491 #ifdef _WIN32
492 gfree(history[historyCur].fileName);
493 #else
494 delete history[historyCur].fileName;
495 #endif
496 }
497 history[historyCur] = h;
498 if (historyBLen < pdfHistorySize) {
499 ++historyBLen;
500 }
501 historyFLen = 0;
502 }
503
504 GBool PDFCore::gotoNextPage(int inc, GBool top) {
505 GBool sideBySide;
506 int pg;
507
508 if (!doc || !doc->getNumPages()) {
509 return gFalse;
510 }
511 pg = tileMap->getFirstPage();
512 sideBySide = state->displayModeIsSideBySide();
513 if (pg + (sideBySide ? 2 : 1) > doc->getNumPages()) {
514 return gFalse;
515 }
516 if (sideBySide && inc < 2) {
517 inc = 2;
518 }
519 if ((pg += inc) > doc->getNumPages()) {
520 pg = doc->getNumPages();
521 }
522 displayPage(pg, top, gFalse);
523 return gTrue;
524 }
525
526 GBool PDFCore::gotoPrevPage(int dec, GBool top, GBool bottom) {
527 int pg;
528
529 if (!doc || !doc->getNumPages()) {
530 return gFalse;
531 }
532 pg = tileMap->getFirstPage();
533 if (state->getDisplayMode() == displayContinuous &&
534 state->getScrollY() > tileMap->getPageTopY(pg)) {
535 ++pg;
536 } else if (state->getDisplayMode() == displaySideBySideContinuous &&
537 state->getScrollY() > tileMap->getPageTopY(pg)) {
538 pg += 2;
539 } else if (state->getDisplayMode() == displayHorizontalContinuous &&
540 state->getScrollX() > tileMap->getPageLeftX(pg)) {
541 ++pg;
542 }
543 if (pg <= 1) {
544 return gFalse;
545 }
546 if (state->displayModeIsSideBySide() && dec < 2) {
547 dec = 2;
548 }
549 if ((pg -= dec) < 1) {
550 pg = 1;
551 }
552 displayPage(pg, top, bottom);
553 return gTrue;
554 }
555
556 GBool PDFCore::gotoNamedDestination(GString *dest) {
557 LinkDest *d;
558
559 if (!doc) {
560 return gFalse;
561 }
562 if (!(d = doc->findDest(dest))) {
563 return gFalse;
564 }
565 displayDest(d);
566 delete d;
567 return gTrue;
568 }
569
570 GBool PDFCore::goForward() {
571 int pg;
572
573 if (historyFLen == 0) {
574 return gFalse;
575 }
576 if (++historyCur == pdfHistorySize) {
577 historyCur = 0;
578 }
579 --historyFLen;
580 ++historyBLen;
581 if (!history[historyCur].fileName) {
582 return gFalse;
583 }
584 #ifdef _WIN32
585 if (!doc ||
586 !doc->getFileNameU() ||
587 wcscmp(history[historyCur].fileName, doc->getFileNameU()) != 0) {
588 if (loadFile(history[historyCur].fileName,
589 (int)wcslen(history[historyCur].fileName)) != errNone) {
590 return gFalse;
591 }
592 }
593 #else
594 if (!doc ||
595 !doc->getFileName() ||
596 history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
597 if (loadFile(history[historyCur].fileName) != errNone) {
598 return gFalse;
599 }
600 }
601 #endif
602 pg = history[historyCur].page;
603 displayPage(pg, gFalse, gFalse, gFalse);
604 return gTrue;
605 }
606
607 GBool PDFCore::goBackward() {
608 int pg;
609
610 if (historyBLen <= 1) {
611 return gFalse;
612 }
613 if (--historyCur < 0) {
614 historyCur = pdfHistorySize - 1;
615 }
616 --historyBLen;
617 ++historyFLen;
618 if (!history[historyCur].fileName) {
619 return gFalse;
620 }
621 #ifdef _WIN32
622 if (!doc ||
623 !doc->getFileNameU() ||
624 wcscmp(history[historyCur].fileName, doc->getFileNameU()) != 0) {
625 if (loadFile(history[historyCur].fileName,
626 (int)wcslen(history[historyCur].fileName)) != errNone) {
627 return gFalse;
628 }
629 }
630 #else
631 if (!doc ||
632 !doc->getFileName() ||
633 history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
634 if (loadFile(history[historyCur].fileName) != errNone) {
635 return gFalse;
636 }
637 }
638 #endif
639 pg = history[historyCur].page;
640 displayPage(pg, gFalse, gFalse, gFalse);
641 return gTrue;
642 }
643
644 void PDFCore::scrollLeft(int nCols) {
645 scrollTo(state->getScrollX() - nCols, state->getScrollY());
646 }
647
648 void PDFCore::scrollRight(int nCols) {
649 scrollTo(state->getScrollX() + nCols, state->getScrollY());
650 }
651
652 void PDFCore::scrollUp(int nLines, GBool snapToPage) {
653 scrollTo(state->getScrollX(), state->getScrollY() - nLines, snapToPage);
654 }
655
656 void PDFCore::scrollUpPrevPage(int nLines) {
657 if (!state->displayModeIsContinuous() &&
658 state->getScrollY() == 0) {
659 gotoPrevPage(1, gFalse, gTrue);
660 } else {
661 scrollUp(nLines, gTrue);
662 }
663 }
664
665 void PDFCore::scrollDown(int nLines, GBool snapToPage) {
666 scrollTo(state->getScrollX(), state->getScrollY() + nLines, snapToPage);
667 }
668
669 void PDFCore::scrollDownNextPage(int nLines) {
670 int horizMax, vertMax;
671
672 if (!state->displayModeIsContinuous()) {
673 tileMap->getScrollLimits(&horizMax, &vertMax);
674 if (state->getScrollY() >= vertMax - state->getWinH()) {
675 gotoNextPage(1, gTrue);
676 } else {
677 scrollDown(nLines);
678 }
679 } else {
680 scrollDown(nLines, gTrue);
681 }
682 }
683
684 void PDFCore::scrollPageUp() {
685 scrollUpPrevPage(state->getWinH());
686 }
687
688 void PDFCore::scrollPageDown() {
689 scrollDownNextPage(state->getWinH());
690 }
691
692 void PDFCore::scrollTo(int x, int y, GBool snapToPage) {
693 int next, topPage, topPageY, sy, dy;
694
695 startUpdate();
696 state->setScrollPosition(state->getScrollPage(), x, y);
697
698 if (snapToPage) {
699 if (state->getDisplayMode() == displayContinuous ||
700 state->getDisplayMode() == displaySideBySideContinuous) {
701 next = state->getDisplayMode() == displaySideBySideContinuous ? 2 : 1;
702 topPage = tileMap->getFirstPage();
703 // NB: topPage can be out of bounds here, because the scroll
704 // position isn't adjusted until finishUpdate is called, below
705 if (topPage > 0 && topPage <= doc->getNumPages()) {
706 topPageY = tileMap->getPageTopY(topPage);
707 sy = state->getScrollY();
708 dy = sy - topPageY;
709 // note: dy can be negative here if the inter-page gap is at the
710 // top of the window
711 if (-16 < dy && dy < 16) {
712 state->setScrollPosition(state->getScrollPage(), x, topPageY);
713 } else if (topPage + next <= doc->getNumPages()) {
714 topPage += next;
715 topPageY = tileMap->getPageTopY(topPage);
716 dy = sy - topPageY;
717 if (-16 < dy && dy < 0) {
718 state->setScrollPosition(state->getScrollPage(), x, topPageY);
719 }
720 }
721 }
722 }
723 }
724
725 finishUpdate(gTrue, gTrue);
726 }
727
728 void PDFCore::scrollToLeftEdge() {
729 scrollTo(0, state->getScrollY());
730 }
731
732 void PDFCore::scrollToRightEdge() {
733 int horizMax, vertMax;
734
735 tileMap->getScrollLimits(&horizMax, &vertMax);
736 scrollTo(horizMax - state->getWinW(), state->getScrollY());
737 }
738
739 void PDFCore::scrollToTopEdge() {
740 scrollTo(state->getScrollX(),
741 tileMap->getPageTopY(tileMap->getFirstPage()));
742 }
743
744 void PDFCore::scrollToBottomEdge() {
745 scrollTo(state->getScrollX(),
746 tileMap->getPageBottomY(tileMap->getLastPage()));
747 }
748
749 void PDFCore::scrollToTopLeft() {
750 scrollTo(tileMap->getPageLeftX(tileMap->getFirstPage()),
751 tileMap->getPageTopY(tileMap->getFirstPage()));
752 }
753
754 void PDFCore::scrollToBottomRight() {
755 scrollTo(tileMap->getPageRightX(tileMap->getLastPage()),
756 tileMap->getPageBottomY(tileMap->getLastPage()));
757 }
758
759 void PDFCore::scrollToCentered(int page, double x, double y) {
760 int wx, wy, sx, sy;
761
762 startUpdate();
763
764 // scroll to the requested page
765 state->setScrollPosition(page, tileMap->getPageLeftX(page),
766 tileMap->getPageTopY(page));
767
768 // scroll the requested point to the center of the window
769 cvtUserToWindow(page, x, y, &wx, &wy);
770 sx = state->getScrollX() + wx - state->getWinW() / 2;
771 sy = state->getScrollY() + wy - state->getWinH() / 2;
772 state->setScrollPosition(page, sx, sy);
773
774 finishUpdate(gTrue, gFalse);
775 }
776
777 void PDFCore::setZoom(double zoom) {
778 int page;
779
780 if (state->getZoom() == zoom) {
781 return;
782 }
783 if (!doc || !doc->getNumPages()) {
784 state->setZoom(zoom);
785 return;
786 }
787 startUpdate();
788 page = tileMap->getFirstPage();
789 state->setZoom(zoom);
790 state->setScrollPosition(page, tileMap->getPageLeftX(page),
791 tileMap->getPageTopY(page));
792 finishUpdate(gTrue, gTrue);
793 }
794
795 void PDFCore::zoomToRect(int page, double ulx, double uly,
796 double lrx, double lry) {
797 int x0, y0, x1, y1, sx, sy, t;
798 double dpi, rx, ry, zoom;
799
800 startUpdate();
801
802 // set the new zoom level
803 cvtUserToDev(page, ulx, uly, &x0, &y0);
804 cvtUserToDev(page, lrx, lry, &x1, &y1);
805 if (x0 > x1) {
806 t = x0; x0 = x1; x1 = t;
807 }
808 if (y0 > y1) {
809 t = y0; y0 = y1; y1 = t;
810 }
811 rx = (double)state->getWinW() / (double)(x1 - x0);
812 ry = (double)state->getWinH() / (double)(y1 - y0);
813 dpi = tileMap->getDPI(page);
814 if (rx < ry) {
815 zoom = rx * (dpi / (0.01 * 72));
816 } else {
817 zoom = ry * (dpi / (0.01 * 72));
818 }
819 state->setZoom(zoom);
820
821 // scroll to the requested page
822 state->setScrollPosition(page, tileMap->getPageLeftX(page),
823 tileMap->getPageTopY(page));
824
825 // scroll the requested rectangle to the center of the window
826 cvtUserToWindow(page, 0.5 * (ulx + lrx), 0.5 * (uly + lry), &x0, &y0);
827 sx = state->getScrollX() + x0 - state->getWinW() / 2;
828 sy = state->getScrollY() + y0 - state->getWinH() / 2;
829 state->setScrollPosition(page, sx, sy);
830
831 finishUpdate(gTrue, gFalse);
832 }
833
834 void PDFCore::zoomCentered(double zoom) {
835 int page, wx, wy, sx, sy;
836 double cx, cy;
837
838 if (state->getZoom() == zoom) {
839 return;
840 }
841
842 startUpdate();
843
844 // get the center of the window, in user coords
845 cvtWindowToUser(state->getWinW() / 2, state->getWinH() / 2,
846 &page, &cx, &cy);
847
848 // set the new zoom level
849 state->setZoom(zoom);
850
851 // scroll to re-center
852 cvtUserToWindow(page, cx, cy, &wx, &wy);
853 sx = state->getScrollX() + wx - state->getWinW() / 2;
854 sy = state->getScrollY() + wy - state->getWinH() / 2;
855 state->setScrollPosition(page, sx, sy);
856
857 finishUpdate(gTrue, gFalse);
858 }
859
860 // Zoom so that the current page(s) fill the window width. Maintain
861 // the vertical center.
862 void PDFCore::zoomToCurrentWidth() {
863 int page0, page1, page, gap;
864 double w, w1, zoom;
865
866 startUpdate();
867
868 // get first and last pages
869 page0 = tileMap->getFirstPage();
870 page1 = tileMap->getLastPage();
871
872 // compute the desired width (in points)
873 gap = 0;
874 switch (state->getDisplayMode()) {
875 case displaySingle:
876 default:
877 w = tileMap->getPageBoxWidth(page0);
878 break;
879 case displayContinuous:
880 w = 0;
881 for (page = page0; page <= page1; ++page) {
882 w1 = tileMap->getPageBoxWidth(page);
883 if (w1 > w) {
884 w = w1;
885 }
886 }
887 break;
888 case displaySideBySideSingle:
889 w = tileMap->getPageBoxWidth(page0);
890 if (page1 != page0) {
891 w += tileMap->getPageBoxWidth(page1);
892 gap = tileMap->getSideBySidePageSpacing();
893 }
894 break;
895 case displaySideBySideContinuous:
896 w = 0;
897 for (page = page0; w <= page1; w += 2) {
898 w1 = tileMap->getPageBoxWidth(page);
899 if (page + 1 <= doc->getNumPages()) {
900 w1 += tileMap->getPageBoxWidth(page + 1);
901 }
902 if (w1 > w) {
903 w = w1;
904 }
905 }
906 gap = tileMap->getSideBySidePageSpacing();
907 break;
908 case displayHorizontalContinuous:
909 w = 0;
910 gap = 0;
911 for (page = page0; page <= page1; ++page) {
912 w += tileMap->getPageBoxWidth(page);
913 if (page != page0) {
914 gap += tileMap->getHorizContinuousPageSpacing();
915 }
916 }
917 break;
918 }
919
920 // set the new zoom level
921 zoom = 100.0 * (state->getWinW() - gap) / w;
922 state->setZoom(zoom);
923
924 // scroll so that the first page is at the left edge of the window
925 state->setScrollPosition(page0, tileMap->getPageLeftX(page0),
926 tileMap->getPageTopY(page0));
927
928 finishUpdate(gTrue, gFalse);
929 }
930
931 void PDFCore::setRotate(int rotate) {
932 int page;
933
934 if (state->getRotate() == rotate) {
935 return;
936 }
937 if (!doc || !doc->getNumPages()) {
938 state->setRotate(rotate);
939 return;
940 }
941 startUpdate();
942 page = tileMap->getFirstPage();
943 state->setRotate(rotate);
944 state->setScrollPosition(page, tileMap->getPageLeftX(page),
945 tileMap->getPageTopY(page));
946 finishUpdate(gTrue, gTrue);
947 }
948
949 void PDFCore::setDisplayMode(DisplayMode mode) {
950 int page;
951
952 if (state->getDisplayMode() == mode) {
953 return;
954 }
955 if (!doc || !doc->getNumPages()) {
956 state->setDisplayMode(mode);
957 return;
958 }
959 startUpdate();
960 page = tileMap->getFirstPage();
961 state->setDisplayMode(mode);
962 state->setScrollPosition(page, tileMap->getPageLeftX(page),
963 tileMap->getPageTopY(page));
964 finishUpdate(gTrue, gTrue);
965 }
966
967 void PDFCore::setOCGState(OptionalContentGroup *ocg, GBool ocgState) {
968 if (ocgState != ocg->getState()) {
969 ocg->setState(ocgState);
970 state->optionalContentChanged();
971 invalidateWholeWindow();
972 }
973 }
974
975 void PDFCore::setSelectMode(SelectMode mode) {
976 if (mode != selectMode) {
977 selectMode = mode;
978 clearSelection();
979 }
980 }
981
982 SplashColorPtr PDFCore::getSelectionColor() {
983 return state->getSelectColor();
984 }
985
986 void PDFCore::setSelectionColor(SplashColor color) {
987 int wx0, wy0, wx1, wy1;
988
989 state->setSelectColor(color);
990 if (state->hasSelection()) {
991 getSelectionBBox(&wx0, &wy0, &wx1, &wy1);
992 checkInvalidate(wx0, wy0, wx1 - wx0, wy1 - wy0);
993 }
994 }
995
996 void PDFCore::setSelection(int page, int x0, int y0, int x1, int y1) {
997 SelectRect *rect;
998 GBool moveLeft, moveTop, moveRight, moveBottom, needScroll;
999 double selectX0, selectY0, selectX1, selectY1;
1000 int oldWx0, oldWy0, oldWx1, oldWy1, ix0, iy0, ix1, iy1;
1001 int wx0, wy0, wx1, wy1, sx, sy, t;
1002
1003
1004 // if selection rectangle is empty, clear the selection
1005 if (x0 == x1 || y0 == y1) {
1006 clearSelection();
1007 return;
1008 }
1009
1010 // x0 = left, x1 = right
1011 // y0 = top, y1 = bottom
1012 if (x0 > x1) {
1013 t = x0; x0 = x1; x1 = t;
1014 }
1015 if (y0 > y1) {
1016 t = y0; y0 = y1; y1 = t;
1017 }
1018
1019 // convert new selection coords to user space and window space
1020 tileMap->cvtDevToUser(page, x0, y0, &selectX0, &selectY0);
1021 tileMap->cvtDevToUser(page, x1, y1, &selectX1, &selectY1);
1022 cvtUserToWindow(page, selectX0, selectY0, &wx0, &wy0);
1023 cvtUserToWindow(page, selectX1, selectY1, &wx1, &wy1);
1024 if (wx0 > wx1) {
1025 t = wx0; wx0 = wx1; wx1 = t;
1026 }
1027 if (wy0 > wy1) {
1028 t = wy0; wy0 = wy1; wy1 = t;
1029 }
1030
1031 // convert current selection coords to window space;
1032 // check which edges moved
1033 if (state->hasSelection()) {
1034 rect = state->getSelectRect(0);
1035 tileMap->cvtUserToWindow(rect->page, rect->x0, rect->y0, &oldWx0, &oldWy0);
1036 tileMap->cvtUserToWindow(rect->page, rect->x1, rect->y1, &oldWx1, &oldWy1);
1037 if (oldWx0 > oldWx1) {
1038 t = oldWx0; oldWx0 = oldWx1; oldWx1 = t;
1039 }
1040 if (oldWy0 > oldWy1) {
1041 t = oldWy0; oldWy0 = oldWy1; oldWy1 = t;
1042 }
1043 moveLeft = wx0 != oldWx0;
1044 moveTop = wy0 != oldWy0;
1045 moveRight = wx1 != oldWx1;
1046 moveBottom = wy1 != oldWy1;
1047 } else {
1048 oldWx0 = wx0;
1049 oldWy0 = wy0;
1050 oldWx1 = wx1;
1051 oldWy1 = wy1;
1052 moveLeft = moveTop = moveRight = moveBottom = gTrue;
1053 }
1054
1055 // set the new selection
1056 state->setSelection(page, selectX0, selectY0, selectX1, selectY1);
1057
1058 // scroll if necessary
1059 needScroll = gFalse;
1060 sx = state->getScrollX();
1061 sy = state->getScrollY();
1062 if (moveLeft && wx0 < 0) {
1063 sx += wx0;
1064 needScroll = gTrue;
1065 } else if (moveRight && wx1 >= state->getWinW()) {
1066 sx += wx1 - state->getWinW();
1067 needScroll = gTrue;
1068 } else if (moveLeft && wx0 >= state->getWinW()) {
1069 sx += wx0 - state->getWinW();
1070 needScroll = gTrue;
1071 } else if (moveRight && wx1 < 0) {
1072 sx += wx1;
1073 needScroll = gTrue;
1074 }
1075 if (moveTop && wy0 < 0) {
1076 sy += wy0;
1077 needScroll = gTrue;
1078 } else if (moveBottom && wy1 >= state->getWinH()) {
1079 sy += wy1 - state->getWinH();
1080 needScroll = gTrue;
1081 } else if (moveTop && wy0 >= state->getWinH()) {
1082 sy += wy0 - state->getWinH();
1083 needScroll = gTrue;
1084 } else if (moveBottom && wy1 < 0) {
1085 sy += wy1;
1086 needScroll = gTrue;
1087 }
1088 if (needScroll) {
1089 scrollTo(sx, sy);
1090 } else {
1091 ix0 = (wx0 < oldWx0) ? wx0 : oldWx0;
1092 iy0 = (wy0 < oldWy0) ? wy0 : oldWy0;
1093 ix1 = (wx1 > oldWx1) ? wx1 : oldWx1;
1094 iy1 = (wy1 > oldWy1) ? wy1 : oldWy1;
1095 checkInvalidate(ix0, iy0, ix1 - ix0, iy1 - iy0);
1096 }
1097 }
1098
1099 void PDFCore::setLinearSelection(int page, TextPosition *pos0,
1100 TextPosition *pos1) {
1101 TextPosition begin, end;
1102 GList *rects;
1103 GBool moveLeft, moveTop, moveRight, moveBottom, needScroll;
1104 double x0, y0, x1, y1, x2, y2, x3, y3;
1105 double ux0, uy0, ux1, uy1;
1106 int oldWx0, oldWy0, oldWx1, oldWy1, ix0, iy0, ix1, iy1;
1107 int wx0, wy0, wx1, wy1;
1108 int sx, sy, colIdx;
1109
1110
1111 // if selection rectangle is empty, clear the selection
1112 if (*pos0 == *pos1) {
1113 clearSelection();
1114 return;
1115 }
1116
1117 // swap into correct order
1118 if (*pos0 < *pos1) {
1119 begin = *pos0;
1120 end = *pos1;
1121 } else {
1122 begin = *pos1;
1123 end = *pos0;
1124 }
1125
1126 // build the list of rectangles
1127 //~ this doesn't handle RtL, vertical, or rotated text
1128 loadText(page);
1129 rects = new GList();
1130 if (begin.colIdx == end.colIdx &&
1131 begin.parIdx == end.parIdx &&
1132 begin.lineIdx == end.lineIdx) {
1133 // same line
1134 text->convertPosToPointUpper(&begin, &x0, &y0);
1135 text->convertPosToPointLower(&end, &x1, &y1);
1136 cvtDevToUser(page, (int)(x0 + 0.5), (int)(y0 + 0.5), &ux0, &uy0);
1137 cvtDevToUser(page, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux1, &uy1);
1138 rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
1139 } else if (begin.colIdx == end.colIdx) {
1140 // same column
1141 text->convertPosToPointUpper(&begin, &x0, &y0);
1142 text->convertPosToPointRightEdge(&begin, &x1, &y1);
1143 text->convertPosToPointLeftEdge(&end, &x2, &y2);
1144 text->convertPosToPointLower(&end, &x3, &y3);
1145 cvtDevToUser(page, (int)(x0 + 0.5), (int)(y0 + 0.5), &ux0, &uy0);
1146 cvtDevToUser(page, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux1, &uy1);
1147 rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
1148 cvtDevToUser(page, (int)(x2 + 0.5), (int)(y1 + 0.5), &ux0, &uy0);
1149 cvtDevToUser(page, (int)(x1 + 0.5), (int)(y2 + 0.5), &ux1, &uy1);
1150 rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
1151 cvtDevToUser(page, (int)(x2 + 0.5), (int)(y2 + 0.5), &ux0, &uy0);
1152 cvtDevToUser(page, (int)(x3 + 0.5), (int)(y3 + 0.5), &ux1, &uy1);
1153 rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
1154 } else {
1155 // different columns
1156 text->convertPosToPointUpper(&begin, &x0, &y0);
1157 text->convertPosToPointRightEdge(&begin, &x1, &y1);
1158 text->getColumnLowerLeft(begin.colIdx, &x2, &y2);
1159 cvtDevToUser(page, (int)(x0 + 0.5), (int)(y0 + 0.5), &ux0, &uy0);
1160 cvtDevToUser(page, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux1, &uy1);
1161 rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
1162 cvtDevToUser(page, (int)(x2 + 0.5), (int)(y1 + 0.5), &ux0, &uy0);
1163 cvtDevToUser(page, (int)(x1 + 0.5), (int)(y2 + 0.5), &ux1, &uy1);
1164 rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
1165 for (colIdx = begin.colIdx + 1; colIdx < end.colIdx; ++colIdx) {
1166 text->getColumnLowerLeft(colIdx, &x0, &y0);
1167 text->getColumnUpperRight(colIdx, &x1, &y1);
1168 cvtDevToUser(page, (int)(x0 + 0.5), (int)(y1 + 0.5), &ux0, &uy0);
1169 cvtDevToUser(page, (int)(x1 + 0.5), (int)(y0 + 0.5), &ux1, &uy1);
1170 rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
1171 }
1172 text->getColumnUpperRight(end.colIdx, &x0, &y0);
1173 text->convertPosToPointLeftEdge(&end, &x1, &y1);
1174 text->convertPosToPointLower(&end, &x2, &y2);
1175 cvtDevToUser(page, (int)(x1 + 0.5), (int)(y0 + 0.5), &ux0, &uy0);
1176 cvtDevToUser(page, (int)(x0 + 0.5), (int)(y1 + 0.5), &ux1, &uy1);
1177 rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
1178 cvtDevToUser(page, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux0, &uy0);
1179 cvtDevToUser(page, (int)(x2 + 0.5), (int)(y2 + 0.5), &ux1, &uy1);
1180 rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
1181 }
1182
1183 // get window coord bboxes for old selection and new selection;
1184 // check which edges moved
1185 if (state->hasSelection()) {
1186 getSelectionBBox(&oldWx0, &oldWy0, &oldWx1, &oldWy1);
1187 getSelectRectListBBox(rects, &wx0, &wy0, &wx1, &wy1);
1188 moveLeft = wx0 != oldWx0;
1189 moveTop = wy0 != oldWy0;
1190 moveRight = wx1 != oldWx1;
1191 moveBottom = wy1 != oldWy1;
1192 } else {
1193 getSelectRectListBBox(rects, &wx0, &wy0, &wx1, &wy1);
1194 oldWx0 = wx0;
1195 oldWy0 = wy0;
1196 oldWx1 = wx1;
1197 oldWy1 = wy1;
1198 moveLeft = moveTop = moveRight = moveBottom = gTrue;
1199 }
1200
1201 // set the new selection
1202 state->setSelection(rects);
1203
1204 // scroll if necessary
1205 needScroll = gFalse;
1206 sx = state->getScrollX();
1207 sy = state->getScrollY();
1208 if (moveLeft && wx0 < 0) {
1209 sx += wx0;
1210 needScroll = gTrue;
1211 } else if (moveRight && wx1 >= state->getWinW()) {
1212 sx += wx1 - state->getWinW();
1213 needScroll = gTrue;
1214 } else if (moveLeft && wx0 >= state->getWinW()) {
1215 sx += wx0 - state->getWinW();
1216 needScroll = gTrue;
1217 } else if (moveRight && wx1 < 0) {
1218 sx += wx1;
1219 needScroll = gTrue;
1220 }
1221 if (moveTop && wy0 < 0) {
1222 sy += wy0;
1223 needScroll = gTrue;
1224 } else if (moveBottom && wy1 >= state->getWinH()) {
1225 sy += wy1 - state->getWinH();
1226 needScroll = gTrue;
1227 } else if (moveTop && wy0 >= state->getWinH()) {
1228 sy += wy0 - state->getWinH();
1229 needScroll = gTrue;
1230 } else if (moveBottom && wy1 < 0) {
1231 sy += wy1;
1232 needScroll = gTrue;
1233 }
1234 if (needScroll) {
1235 scrollTo(sx, sy);
1236 } else {
1237 ix0 = (wx0 < oldWx0) ? wx0 : oldWx0;
1238 iy0 = (wy0 < oldWy0) ? wy0 : oldWy0;
1239 ix1 = (wx1 > oldWx1) ? wx1 : oldWx1;
1240 iy1 = (wy1 > oldWy1) ? wy1 : oldWy1;
1241 checkInvalidate(ix0, iy0, ix1 - ix0, iy1 - iy0);
1242 }
1243 }
1244
1245 void PDFCore::clearSelection() {
1246 int wx0, wy0, wx1, wy1;
1247
1248 if (state->hasSelection()) {
1249 getSelectionBBox(&wx0, &wy0, &wx1, &wy1);
1250 state->clearSelection();
1251 checkInvalidate(wx0, wy0, wx1 - wx0, wy1 - wy0);
1252 }
1253 }
1254
1255 void PDFCore::startSelectionDrag(int pg, int x, int y) {
1256 clearSelection();
1257 if (selectMode == selectModeBlock) {
1258 selectPage = pg;
1259 selectStartX = x;
1260 selectStartY = y;
1261 } else { // selectModeLinear
1262 loadText(pg);
1263 if (text->findPointInside(x, y, &selectStartPos)) {
1264 selectPage = pg;
1265 } else {
1266 selectPage = 0;
1267 }
1268 }
1269 }
1270
1271 void PDFCore::moveSelectionDrag(int pg, int x, int y) {
1272 TextPosition pos;
1273
1274 // don't allow selections to span multiple pages
1275 // -- this also handles the case where a linear selection was started
1276 // outside any text column, in which case selectPage = 0
1277 if (pg != selectPage) {
1278 return;
1279 }
1280
1281 if (selectMode == selectModeBlock) {
1282 setSelection(pg, selectStartX, selectStartY, x, y);
1283 } else { // selectModeLinear
1284 loadText(pg);
1285 if (text->findPointNear(x, y, &pos)) {
1286 setLinearSelection(pg, &selectStartPos, &pos);
1287 }
1288 }
1289 }
1290
1291 void PDFCore::finishSelectionDrag() {
1292 // nothing
1293 }
1294
1295 void PDFCore::selectWord(int pg, int x, int y) {
1296 TextPosition endPos;
1297
1298 loadText(pg);
1299 if (text->findWordPoints(x, y, &selectStartPos, &endPos)) {
1300 selectPage = pg;
1301 setLinearSelection(pg, &selectStartPos, &endPos);
1302 } else {
1303 selectPage = 0;
1304 }
1305 }
1306
1307 void PDFCore::selectLine(int pg, int x, int y) {
1308 TextPosition endPos;
1309
1310 loadText(pg);
1311 if (text->findLinePoints(x, y, &selectStartPos, &endPos)) {
1312 selectPage = pg;
1313 setLinearSelection(pg, &selectStartPos, &endPos);
1314 } else {
1315 selectPage = 0;
1316 }
1317 }
1318
1319 GBool PDFCore::getSelection(int *pg, double *ulx, double *uly,
1320 double *lrx, double *lry) {
1321 SelectRect *rect;
1322 double xMin, yMin, xMax, yMax;
1323 int page, i;
1324
1325 if (!state->hasSelection()) {
1326 return gFalse;
1327 }
1328 page = state->getSelectRect(0)->page;
1329 xMin = yMin = xMax = yMax = 0;
1330 for (i = 0; i < state->getNumSelectRects(); ++i) {
1331 rect = state->getSelectRect(i);
1332 if (rect->page != page) {
1333 continue;
1334 }
1335 if (i == 0) {
1336 xMin = xMax = rect->x0;
1337 yMin = yMax = rect->y0;
1338 } else {
1339 if (rect->x0 < xMin) {
1340 xMin = rect->x0;
1341 } else if (rect->x0 > xMax) {
1342 xMax = rect->x0;
1343 }
1344 if (rect->y0 < yMin) {
1345 yMin = rect->y0;
1346 } else if (rect->y0 > yMax) {
1347 yMax = rect->y0;
1348 }
1349 }
1350 if (rect->x1 < xMin) {
1351 xMin = rect->x1;
1352 } else if (rect->x1 > xMax) {
1353 xMax = rect->x1;
1354 }
1355 if (rect->y1 < yMin) {
1356 yMin = rect->y1;
1357 } else if (rect->y1 > yMax) {
1358 yMax = rect->y1;
1359 }
1360 }
1361 *pg = page;
1362 *ulx = xMin;
1363 *uly = yMax;
1364 *lrx = xMax;
1365 *lry = yMin;
1366 return gTrue;
1367 }
1368
1369 GBool PDFCore::hasSelection() {
1370 return state->hasSelection();
1371 }
1372
1373 void PDFCore::setTextExtractionMode(TextOutputMode mode) {
1374 if (textOutCtrl.mode != mode) {
1375 textOutCtrl.mode = mode;
1376 if (text) {
1377 delete text;
1378 text = NULL;
1379 }
1380 textPage = 0;
1381 textDPI = 0;
1382 textRotate = 0;
1383 }
1384 }
1385
1386 GBool PDFCore::getDiscardDiagonalText() {
1387 return textOutCtrl.discardDiagonalText;
1388 }
1389
1390 void PDFCore::setDiscardDiagonalText(GBool discard) {
1391 if (textOutCtrl.discardDiagonalText != discard) {
1392 textOutCtrl.discardDiagonalText = discard;
1393 if (text) {
1394 delete text;
1395 text = NULL;
1396 }
1397 textPage = 0;
1398 textDPI = 0;
1399 textRotate = 0;
1400 }
1401 }
1402
1403 GString *PDFCore::extractText(int pg, double xMin, double yMin,
1404 double xMax, double yMax) {
1405 int x0, y0, x1, y1, t;
1406
1407 loadText(pg);
1408 cvtUserToDev(pg, xMin, yMin, &x0, &y0);
1409 cvtUserToDev(pg, xMax, yMax, &x1, &y1);
1410 if (x0 > x1) {
1411 t = x0; x0 = x1; x1 = t;
1412 }
1413 if (y0 > y1) {
1414 t = y0; y0 = y1; y1 = t;
1415 }
1416 return text->getText(x0, y0, x1, y1);
1417 }
1418
1419 GString *PDFCore::getSelectedText() {
1420 SelectRect *rect;
1421 GString *ret, *s;
1422 int x0, y0, x1, y1, t, i;
1423
1424 if (!state->hasSelection()) {
1425 return NULL;
1426 }
1427 ret = new GString();
1428 for (i = 0; i < state->getNumSelectRects(); ++i) {
1429 rect = state->getSelectRect(i);
1430 loadText(rect->page);
1431 cvtUserToDev(rect->page, rect->x0, rect->y0, &x0, &y0);
1432 cvtUserToDev(rect->page, rect->x1, rect->y1, &x1, &y1);
1433 if (x0 > x1) {
1434 t = x0; x0 = x1; x1 = t;
1435 }
1436 if (y0 > y1) {
1437 t = y0; y0 = y1; y1 = t;
1438 }
1439 s = text->getText(x0, y0, x1, y1, state->getNumSelectRects() > 1);
1440 ret->append(s);
1441 delete s;
1442 }
1443 return ret;
1444 }
1445
1446 GBool PDFCore::find(char *s, GBool caseSensitive, GBool next, GBool backward,
1447 GBool wholeWord, GBool onePageOnly) {
1448 Unicode *u;
1449 int len, i;
1450 GBool ret;
1451
1452 // convert to Unicode
1453 len = (int)strlen(s);
1454 u = (Unicode *)gmallocn(len, sizeof(Unicode));
1455 for (i = 0; i < len; ++i) {
1456 u[i] = (Unicode)(s[i] & 0xff);
1457 }
1458
1459 ret = findU(u, len, caseSensitive, next, backward, wholeWord, onePageOnly);
1460
1461 gfree(u);
1462 return ret;
1463 }
1464
1465 GBool PDFCore::findU(Unicode *u, int len, GBool caseSensitive,
1466 GBool next, GBool backward, GBool wholeWord,
1467 GBool onePageOnly) {
1468 TextOutputDev *textOut;
1469 SelectRect *rect;
1470 double xMin, yMin, xMax, yMax;
1471 int topPage, pg, x, y, x2, y2;
1472 GBool startAtTop, startAtLast, stopAtLast;
1473
1474 // check for zero-length string
1475 if (len == 0) {
1476 return gFalse;
1477 }
1478
1479 setBusyCursor(gTrue);
1480
1481 // search current page starting at previous result, current
1482 // selection, or top/bottom of page
1483 startAtTop = startAtLast = gFalse;
1484 rect = NULL;
1485 xMin = yMin = xMax = yMax = 0;
1486 topPage = tileMap->getFirstPage();
1487 pg = topPage;
1488 if (next) {
1489 if (textPage >= 1 && textPage <= doc->getNumPages()) {
1490 startAtLast = gTrue;
1491 pg = textPage;
1492 }
1493 } else if (state->hasSelection()) {
1494 rect = state->getSelectRect(0);
1495 pg = rect->page;
1496 cvtUserToDev(pg, rect->x0, rect->y0, &x, &y);
1497 cvtUserToDev(pg, rect->x1, rect->y1, &x2, &y2);
1498 if (x2 < x) {
1499 x = x2;
1500 }
1501 if (y2 < y) {
1502 y = y2;
1503 }
1504 if (backward) {
1505 xMin = x - 1;
1506 yMin = y - 1;
1507 } else {
1508 xMin = x + 1;
1509 yMin = y + 1;
1510 }
1511 } else {
1512 startAtTop = gTrue;
1513 }
1514 loadText(pg);
1515 if (text->findText(u, len, startAtTop, gTrue, startAtLast, gFalse,
1516 caseSensitive, backward, wholeWord,
1517 &xMin, &yMin, &xMax, &yMax)) {
1518 goto found;
1519 }
1520
1521 if (!onePageOnly) {
1522
1523 // search following/previous pages
1524 textOut = new TextOutputDev(NULL, &textOutCtrl, gFalse);
1525 if (!textOut->isOk()) {
1526 delete textOut;
1527 goto notFound;
1528 }
1529 for (pg = backward ? pg - 1 : pg + 1;
1530 backward ? pg >= 1 : pg <= doc->getNumPages();
1531 pg += backward ? -1 : 1) {
1532 doc->displayPage(textOut, pg, 72, 72, 0, gFalse, gTrue, gFalse);
1533 if (textOut->findText(u, len, gTrue, gTrue, gFalse, gFalse,
1534 caseSensitive, backward, wholeWord,
1535 &xMin, &yMin, &xMax, &yMax)) {
1536 delete textOut;
1537 goto foundPage;
1538 }
1539 }
1540
1541 // search previous/following pages
1542 for (pg = backward ? doc->getNumPages() : 1;
1543 backward ? pg > topPage : pg < topPage;
1544 pg += backward ? -1 : 1) {
1545 doc->displayPage(textOut, pg, 72, 72, 0, gFalse, gTrue, gFalse);
1546 if (textOut->findText(u, len, gTrue, gTrue, gFalse, gFalse,
1547 caseSensitive, backward, wholeWord,
1548 &xMin, &yMin, &xMax, &yMax)) {
1549 delete textOut;
1550 goto foundPage;
1551 }
1552 }
1553 delete textOut;
1554
1555 }
1556
1557 // search current page ending at previous result, current selection,
1558 // or bottom/top of page
1559 if (!startAtTop) {
1560 xMin = yMin = xMax = yMax = 0;
1561 if (next) {
1562 stopAtLast = gTrue;
1563 } else {
1564 stopAtLast = gFalse;
1565 cvtUserToDev(pg, rect->x1, rect->y1, &x, &y);
1566 xMax = x;
1567 yMax = y;
1568 }
1569 if (text->findText(u, len, gTrue, gFalse, gFalse, stopAtLast,
1570 caseSensitive, backward, wholeWord,
1571 &xMin, &yMin, &xMax, &yMax)) {
1572 goto found;
1573 }
1574 }
1575
1576 // not found
1577 notFound:
1578 setBusyCursor(gFalse);
1579 return gFalse;
1580
1581 // found on a different page
1582 foundPage:
1583 displayPage(pg, gTrue, gFalse);
1584 loadText(pg);
1585 if (!text->findText(u, len, gTrue, gTrue, gFalse, gFalse,
1586 caseSensitive, backward, wholeWord,
1587 &xMin, &yMin, &xMax, &yMax)) {
1588 // this can happen if coalescing is bad
1589 goto notFound;
1590 }
1591
1592 // found: change the selection
1593 found:
1594 setSelection(pg, (int)floor(xMin), (int)floor(yMin),
1595 (int)ceil(xMax), (int)ceil(yMax));
1596
1597 setBusyCursor(gFalse);
1598 return gTrue;
1599 }
1600
1601 GList *PDFCore::findAll(Unicode *u, int len, GBool caseSensitive,
1602 GBool wholeWord, int firstPage, int lastPage) {
1603 GList *results = new GList();
1604
1605 TextOutputDev *textOut = new TextOutputDev(NULL, &textOutCtrl, gFalse);
1606 if (!textOut->isOk()) {
1607 delete textOut;
1608 return results;
1609 }
1610
1611 for (int pg = firstPage; pg <= lastPage; ++pg) {
1612 doc->displayPage(textOut, pg, 72, 72, 0, gFalse, gTrue, gFalse);
1613 GBool first = gTrue;
1614 while (1) {
1615 double xMin, yMin, xMax, yMax;
1616 if (!textOut->findText(u, len, first, gTrue, !first, gFalse,
1617 caseSensitive, gFalse, wholeWord,
1618 &xMin, &yMin, &xMax, &yMax)) {
1619 break;
1620 }
1621 double uxMin, uyMin, uxMax, uyMax, t;
1622 textOut->cvtDevToUser(xMin, yMin, &uxMin, &uyMin);
1623 textOut->cvtDevToUser(xMax, yMax, &uxMax, &uyMax);
1624 if (uxMin > uxMax) {
1625 t = uxMin; uxMin = uxMax; uxMax = t;
1626 }
1627 if (uyMin > uyMax) {
1628 t = uyMin; uyMin = uyMax; uyMax = t;
1629 }
1630 results->append(new FindResult(pg, uxMin, uyMin, uxMax, uyMax));
1631 first = gFalse;
1632 }
1633 }
1634
1635 delete textOut;
1636
1637 return results;
1638 }
1639
1640
1641 GBool PDFCore::cvtWindowToUser(int xw, int yw,
1642 int *pg, double *xu, double *yu) {
1643 return tileMap->cvtWindowToUser(xw, yw, pg, xu, yu);
1644 }
1645
1646 GBool PDFCore::cvtWindowToDev(int xw, int yw, int *pg, int *xd, int *yd) {
1647 return tileMap->cvtWindowToDev(xw, yw, pg, xd, yd);
1648 }
1649
1650 GBool PDFCore::cvtUserToWindow(int pg, double xu, double yu, int *xw, int *yw) {
1651 return tileMap->cvtUserToWindow(pg, xu, yu, xw, yw);
1652 }
1653
1654 void PDFCore::cvtUserToDev(int pg, double xu, double yu, int *xd, int *yd) {
1655 tileMap->cvtUserToDev(pg, xu, yu, xd, yd);
1656 }
1657
1658 GBool PDFCore::cvtDevToWindow(int pg, int xd, int yd, int *xw, int *yw) {
1659 return tileMap->cvtDevToWindow(pg, xd, yd, xw, yw);
1660 }
1661
1662 void PDFCore::cvtDevToUser(int pg, int xd, int yd, double *xu, double *yu) {
1663 tileMap->cvtDevToUser(pg, xd, yd, xu, yu);
1664 }
1665
1666 void PDFCore::getWindowPageRange(int x, int y, int w, int h,
1667 int *firstPage, int *lastPage) {
1668 tileMap->getWindowPageRange(x, y, w, h, firstPage, lastPage);
1669 }
1670
1671 int PDFCore::getPageNum() {
1672 if (!doc || !doc->getNumPages()) {
1673 return 0;
1674 }
1675 return tileMap->getFirstPage();
1676 }
1677
1678 int PDFCore::getMidPageNum() {
1679 if (!doc || !doc->getNumPages()) {
1680 return 0;
1681 }
1682 return tileMap->getMidPage();
1683 }
1684
1685 double PDFCore::getZoom() {
1686 return state->getZoom();
1687 }
1688
1689 double PDFCore::getZoomDPI(int page) {
1690 if (!doc) {
1691 return 0;
1692 }
1693 return tileMap->getDPI(page);
1694 }
1695
1696 int PDFCore::getRotate() {
1697 return state->getRotate();
1698 }
1699
1700 DisplayMode PDFCore::getDisplayMode() {
1701 return state->getDisplayMode();
1702 }
1703
1704 int PDFCore::getScrollX() {
1705 return state->getScrollX();
1706 }
1707
1708 int PDFCore::getScrollY() {
1709 return state->getScrollY();
1710 }
1711
1712 int PDFCore::getWindowWidth() {
1713 return state->getWinW();
1714 }
1715
1716 int PDFCore::getWindowHeight() {
1717 return state->getWinH();
1718 }
1719
1720 void PDFCore::setPaperColor(SplashColorPtr paperColor) {
1721 state->setPaperColor(paperColor);
1722 invalidateWholeWindow();
1723 }
1724
1725 void PDFCore::setMatteColor(SplashColorPtr matteColor) {
1726 state->setMatteColor(matteColor);
1727 invalidateWholeWindow();
1728 }
1729
1730 void PDFCore::setReverseVideo(GBool reverseVideo) {
1731 SplashColorPtr oldPaperColor;
1732 SplashColor newPaperColor;
1733 int i;
1734
1735 if (reverseVideo != state->getReverseVideo()) {
1736 state->setReverseVideo(reverseVideo);
1737 oldPaperColor = state->getPaperColor();
1738 for (i = 0; i < splashColorModeNComps[state->getColorMode()]; ++i) {
1739 newPaperColor[i] = oldPaperColor[i] ^ 0xff;
1740 }
1741 state->setPaperColor(newPaperColor);
1742 invalidateWholeWindow();
1743 }
1744 }
1745
1746
1747
1748 LinkAction *PDFCore::findLink(int pg, double x, double y) {
1749 loadLinks(pg);
1750 return links->find(x, y);
1751 }
1752
1753 Annot *PDFCore::findAnnot(int pg, double x, double y) {
1754 loadAnnots(pg);
1755 return annots->find(x, y);
1756 }
1757
1758 int PDFCore::findAnnotIdx(int pg, double x, double y) {
1759 loadAnnots(pg);
1760 return annots->findIdx(x, y);
1761 }
1762
1763 Annot *PDFCore::getAnnot(int idx) {
1764 if (!annots) {
1765 return NULL;
1766 }
1767 if (idx < 0 || idx >= annots->getNumAnnots()) {
1768 return NULL;
1769 }
1770 return annots->getAnnot(idx);
1771 }
1772
1773 AcroFormField *PDFCore::findFormField(int pg, double x, double y) {
1774 if (!doc->getCatalog()->getForm()) {
1775 return NULL;
1776 }
1777 return doc->getCatalog()->getForm()->findField(pg, x, y);
1778 }
1779
1780 int PDFCore::findFormFieldIdx(int pg, double x, double y) {
1781 if (!doc->getCatalog()->getForm()) {
1782 return -1;
1783 }
1784 return doc->getCatalog()->getForm()->findFieldIdx(pg, x, y);
1785 }
1786
1787 AcroFormField *PDFCore::getFormField(int idx) {
1788 if (!doc->getCatalog()->getForm()) {
1789 return NULL;
1790 }
1791 if (idx < 0 || idx >= doc->getCatalog()->getForm()->getNumFields()) {
1792 return NULL;
1793 }
1794 return doc->getCatalog()->getForm()->getField(idx);
1795 }
1796
1797 GBool PDFCore::overText(int pg, double x, double y) {
1798 loadText(pg);
1799 return text->checkPointInside(x, y);
1800 }
1801
1802 void PDFCore::forceRedraw() {
1803 startUpdate();
1804 state->forceRedraw();
1805 finishUpdate(gFalse, gFalse);
1806 }
1807
1808 void PDFCore::setTileDoneCbk(void (*cbk)(void *data), void *data) {
1809 tileCache->setTileDoneCbk(cbk, data);
1810 }
1811
1812 void PDFCore::setWindowSize(int winWidth, int winHeight) {
1813 GBool doScroll;
1814 int page, wx0, wy0, wx, wy, sx, sy;
1815 double ux, uy;
1816
1817 startUpdate();
1818
1819 wx0 = wy0 = 0; // make gcc happy
1820 doScroll = gFalse;
1821 if (state->getZoom() < 0 && state->displayModeIsContinuous()) {
1822 // save the user coordinates of the appropriate edge of the window
1823 if (state->getDisplayMode() == displayHorizontalContinuous) {
1824 wx0 = 0;
1825 wy0 = state->getWinH() / 2;
1826 } else {
1827 wx0 = state->getWinW() / 2;
1828 wy0 = 0;
1829 }
1830 if (!(doScroll = cvtWindowToUser(wx0, wy0, &page, &ux, &uy))) {
1831 // tweak the save position if it happens to fall in a gutter
1832 if (state->getDisplayMode() == displayContinuous) {
1833 wy0 += tileMap->getContinuousPageSpacing();
1834 } else if (state->getDisplayMode() == displaySideBySideContinuous) {
1835 wx0 += tileMap->getSideBySidePageSpacing();
1836 wy0 += tileMap->getContinuousPageSpacing();
1837 } else { // state->getDisplayMode() == displayHorizontalContinuous
1838 wx0 += tileMap->getHorizContinuousPageSpacing();
1839 }
1840 doScroll = cvtWindowToUser(wx0, wy0, &page, &ux, &uy);
1841 }
1842 }
1843
1844 state->setWindowSize(winWidth, winHeight);
1845
1846 if (doScroll) {
1847 // restore the saved scroll position
1848 cvtUserToWindow(page, ux, uy, &wx, &wy);
1849 sx = state->getScrollX();
1850 sy = state->getScrollY();
1851 if (state->getDisplayMode() == displayHorizontalContinuous) {
1852 sx += wx - wx0;
1853 } else {
1854 sy += wy - wy0;
1855 }
1856 state->setScrollPosition(page, sx, sy);
1857 }
1858
1859 finishUpdate(gTrue, gFalse);
1860 }
1861
1862 SplashBitmap *PDFCore::getWindowBitmap(GBool wholeWindow) {
1863 GBool dummy;
1864
1865 return tileCompositor->getBitmap(wholeWindow ? &bitmapFinished : &dummy);
1866 }
1867
1868 void PDFCore::tick() {
1869 if (!bitmapFinished) {
1870 invalidateWholeWindow();
1871 }
1872 }
1873
1874 // Clear cached info (links, text) that's tied to a PDFDoc.
1875 void PDFCore::clearPage() {
1876 if (links) {
1877 delete links;
1878 }
1879 links = NULL;
1880 linksPage = 0;
1881
1882 if (annots) {
1883 delete annots;
1884 }
1885 annots = NULL;
1886 annotsPage = 0;
1887
1888 if (text) {
1889 delete text;
1890 }
1891 text = NULL;
1892 textPage = 0;
1893 textDPI = 0;
1894 textRotate = 0;
1895 }
1896
1897 // Load the links for <pg>.
1898 void PDFCore::loadLinks(int pg) {
1899 if (links && linksPage == pg) {
1900 return;
1901 }
1902 if (links) {
1903 delete links;
1904 }
1905 links = doc->getLinks(pg);
1906 linksPage = pg;
1907 }
1908
1909 // Load the annotations for <pg>.
1910 void PDFCore::loadAnnots(int pg) {
1911 Object annotsObj;
1912
1913 if (annots && annotsPage == pg) {
1914 return;
1915 }
1916 if (annots) {
1917 delete annots;
1918 }
1919 doc->getCatalog()->getPage(pg)->getAnnots(&annotsObj);
1920 annots = new Annots(doc, &annotsObj);
1921 annotsObj.free();
1922 annotsPage = pg;
1923 }
1924
1925 // Extract text from <pg>.
1926 void PDFCore::loadText(int pg) {
1927 TextOutputDev *textOut;
1928 double dpi;
1929 int rotate;
1930
1931 dpi = tileMap->getDPI(pg);
1932 rotate = state->getRotate();
1933 if (text && textPage == pg && textDPI == dpi && textRotate == rotate) {
1934 return;
1935 }
1936 if (text) {
1937 delete text;
1938 }
1939 textOut = new TextOutputDev(NULL, &textOutCtrl, gFalse);
1940 if (!textOut->isOk()) {
1941 text = new TextPage(&textOutCtrl);
1942 } else {
1943 doc->displayPage(textOut, pg, dpi, dpi, rotate, gFalse, gTrue, gFalse);
1944 text = textOut->takeText();
1945 }
1946 delete textOut;
1947 textPage = pg;
1948 textDPI = dpi;
1949 textRotate = rotate;
1950 }
1951
1952 void PDFCore::getSelectionBBox(int *wxMin, int *wyMin, int *wxMax, int *wyMax) {
1953 *wxMin = *wyMin = *wxMax = *wyMax = 0;
1954 if (!state->hasSelection()) {
1955 return;
1956 }
1957 getSelectRectListBBox(state->getSelectRects(), wxMin, wyMin, wxMax, wyMax);
1958 }
1959
1960 void PDFCore::getSelectRectListBBox(GList *rects, int *wxMin, int *wyMin,
1961 int *wxMax, int *wyMax) {
1962 SelectRect *rect;
1963 int x, y, i;
1964
1965 *wxMin = *wyMin = *wxMax = *wyMax = 0;
1966 for (i = 0; i < rects->getLength(); ++i) {
1967 rect = (SelectRect *)rects->get(i);
1968 tileMap->cvtUserToWindow(rect->page, rect->x0, rect->y0, &x, &y);
1969 if (i == 0) {
1970 *wxMin = *wxMax = x;
1971 *wyMin = *wyMax = y;
1972 } else {
1973 if (x < *wxMin) {
1974 *wxMin = x;
1975 } else if (x > *wxMax) {
1976 *wxMax = x;
1977 }
1978 if (y < *wyMin) {
1979 *wyMin = y;
1980 } else if (y > *wyMax) {
1981 *wyMax = y;
1982 }
1983 }
1984 tileMap->cvtUserToWindow(rect->page, rect->x1, rect->y1, &x, &y);
1985 if (x < *wxMin) {
1986 *wxMin = x;
1987 } else if (x > *wxMax) {
1988 *wxMax = x;
1989 }
1990 if (y < *wyMin) {
1991 *wyMin = y;
1992 } else if (y > *wyMax) {
1993 *wyMax = y;
1994 }
1995 }
1996 }
1997
1998 void PDFCore::checkInvalidate(int x, int y, int w, int h) {
1999 if (x < 0) {
2000 w += x;
2001 x = 0;
2002 }
2003 if (x + w > state->getWinW()) {
2004 w = state->getWinW() - x;
2005 }
2006 if (w <= 0) {
2007 return;
2008 }
2009 if (y < 0) {
2010 h += y;
2011 y = 0;
2012 }
2013 if (y + h > state->getWinH()) {
2014 h = state->getWinH() - y;
2015 }
2016 if (h <= 0) {
2017 return;
2018 }
2019 invalidate(x, y, w, h);
2020 }
2021
2022 void PDFCore::invalidateWholeWindow() {
2023 invalidate(0, 0, state->getWinW(), state->getWinH());
2024 }