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