"Fossies" - the Fresh Open Source Software Archive

Member "xpdf-4.04/xpdf/PDFCore.cc" (18 Apr 2022, 51111 Bytes) of package /linux/misc/xpdf-4.04.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

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