"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 }