//======================================================================== // // PDFCore.cc // // Copyright 2004-2014 Glyph & Cog, LLC // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include "gmempp.h" #include "GString.h" #include "GList.h" #include "GlobalParams.h" #include "Splash.h" #include "SplashBitmap.h" #include "SplashPattern.h" #include "SplashPath.h" #include "Error.h" #include "ErrorCodes.h" #include "PDFDoc.h" #include "Link.h" #include "Annot.h" #include "AcroForm.h" #include "OptionalContent.h" #include "TileMap.h" #include "TileCache.h" #include "TileCompositor.h" #include "PDFCore.h" //------------------------------------------------------------------------ // PDFCore //------------------------------------------------------------------------ PDFCore::PDFCore(SplashColorMode colorMode, int bitmapRowPad, GBool reverseVideo, SplashColorPtr paperColor) { GString *initialZoom, *initialDisplayMode; int z, i; doc = NULL; linksPage = 0; links = NULL; annotsPage = 0; annots = NULL; textPage = 0; textDPI = 0; textRotate = 0; textOutCtrl.mode = textOutPhysLayout; text = NULL; state = new DisplayState(globalParams->getMaxTileWidth(), globalParams->getMaxTileHeight(), globalParams->getTileCacheSize(), globalParams->getWorkerThreads(), colorMode, bitmapRowPad); tileMap = new TileMap(state); tileCache = new TileCache(state); tileCompositor = new TileCompositor(state, tileMap, tileCache); bitmapFinished = gTrue; state->setReverseVideo(reverseVideo); state->setPaperColor(paperColor); initialZoom = globalParams->getInitialZoom(); if (!initialZoom->cmp("page")) { state->setZoom(zoomPage); } else if (!initialZoom->cmp("width")) { state->setZoom(zoomWidth); } else { z = atoi(initialZoom->getCString()); if (z <= 0) { z = zoomWidth; } state->setZoom(z); } delete initialZoom; initialDisplayMode = globalParams->getInitialDisplayMode(); if (!initialDisplayMode->cmp("single")) { state->setDisplayMode(displaySingle); } else if (!initialDisplayMode->cmp("sideBySideSingle")) { state->setDisplayMode(displaySideBySideSingle); } else if (!initialDisplayMode->cmp("sideBySideContinuous")) { state->setDisplayMode(displaySideBySideContinuous); } else if (!initialDisplayMode->cmp("horizontalContinuous")) { state->setDisplayMode(displayHorizontalContinuous); } else { state->setDisplayMode(displayContinuous); } delete initialDisplayMode; selectMode = selectModeBlock; selectPage = 0; selectStartX = selectStartY = 0; historyCur = pdfHistorySize - 1; historyBLen = historyFLen = 0; for (i = 0; i < pdfHistorySize; ++i) { history[i].fileName = NULL; history[i].page = 0; } } PDFCore::~PDFCore() { int i; delete tileCompositor; delete tileCache; delete tileMap; delete state; clearPage(); if (doc) { delete doc; } for (i = 0; i < pdfHistorySize; ++i) { if (history[i].fileName) { #ifdef _WIN32 delete[] history[i].fileName; #else delete history[i].fileName; #endif } } } int PDFCore::loadFile(GString *fileName, GString *ownerPassword, GString *userPassword) { int err; setBusyCursor(gTrue); err = loadFile2(new PDFDoc(fileName->copy(), ownerPassword, userPassword, this)); setBusyCursor(gFalse); return err; } #ifdef _WIN32 int PDFCore::loadFile(wchar_t *fileName, int fileNameLen, GString *ownerPassword, GString *userPassword) { int err; setBusyCursor(gTrue); err = loadFile2(new PDFDoc(fileName, fileNameLen, ownerPassword, userPassword, this)); setBusyCursor(gFalse); return err; } #endif int PDFCore::loadFile(BaseStream *stream, GString *ownerPassword, GString *userPassword) { int err; setBusyCursor(gTrue); err = loadFile2(new PDFDoc(stream, ownerPassword, userPassword, this)); setBusyCursor(gFalse); return err; } void PDFCore::loadDoc(PDFDoc *docA) { setBusyCursor(gTrue); loadFile2(docA); setBusyCursor(gFalse); } int PDFCore::reload() { int err; if (!doc->getFileName()) { return errOpenFile; } setBusyCursor(gTrue); err = loadFile2(new PDFDoc(doc->getFileName()->copy(), NULL, NULL, this)); setBusyCursor(gFalse); startUpdate(); finishUpdate(gTrue, gFalse); return err; } int PDFCore::loadFile2(PDFDoc *newDoc) { int err; clearSelection(); // open the PDF file if (!newDoc->isOk()) { err = newDoc->getErrorCode(); delete newDoc; return err; } preLoad(); // replace old document // NB: do not delete doc until after DisplayState::setDoc() returns state->setDoc(newDoc); if (doc) { delete doc; } doc = newDoc; clearPage(); postLoad(); return errNone; } void PDFCore::clear() { if (!doc) { return; } // no document // NB: do not delete doc until after DisplayState::setDoc() returns state->setDoc(NULL); delete doc; doc = NULL; clearPage(); // redraw state->setScrollPosition(1, 0, 0); invalidateWholeWindow(); updateScrollbars(); } PDFDoc *PDFCore::takeDoc(GBool redraw) { PDFDoc *docA; if (!doc) { return NULL; } // no document // NB: do not delete doc until after DisplayState::setDoc() returns state->setDoc(NULL); docA = doc; doc = NULL; clearPage(); // redraw state->setScrollPosition(1, 0, 0); if (redraw) { invalidateWholeWindow(); updateScrollbars(); } return docA; } void PDFCore::displayPage(int page, GBool scrollToTop, GBool scrollToBottom, GBool addToHist) { int scrollX, scrollY; if (page <= 0 || page > doc->getNumPages()) { return; } if (!scrollToTop && (state->getDisplayMode() == displayContinuous || state->getDisplayMode() == displaySideBySideContinuous)) { scrollY = tileMap->getPageTopY(page) + (state->getScrollY() - tileMap->getPageTopY(tileMap->getFirstPage())); } else if (scrollToTop || state->getDisplayMode() == displayContinuous || state->getDisplayMode() == displaySideBySideContinuous) { scrollY = tileMap->getPageTopY(page); } else if (scrollToBottom) { scrollY = tileMap->getPageBottomY(page); } else { scrollY = state->getScrollY(); } if (state->getDisplayMode() == displayHorizontalContinuous) { scrollX = tileMap->getPageLeftX(page); } else { scrollX = state->getScrollX(); } startUpdate(); state->setScrollPosition(page, scrollX, scrollY); finishUpdate(addToHist, gTrue); } void PDFCore::displayDest(LinkDest *dest) { Ref pageRef; int page; int dx, dy, scrollX, scrollY; if (dest->isPageRef()) { pageRef = dest->getPageRef(); page = doc->findPage(pageRef.num, pageRef.gen); } else { page = dest->getPageNum(); } if (page <= 0 || page > doc->getNumPages()) { page = 1; } switch (dest->getKind()) { case destXYZ: cvtUserToDev(page, dest->getLeft(), dest->getTop(), &dx, &dy); scrollX = tileMap->getPageLeftX(page); if (dest->getChangeLeft()) { scrollX += dx; } scrollY = tileMap->getPageTopY(page); if (dest->getChangeTop()) { scrollY += dy; } startUpdate(); state->setScrollPosition(page, scrollX, scrollY); finishUpdate(gTrue, gTrue); break; case destFit: case destFitB: state->setZoom(zoomPage); scrollX = tileMap->getPageLeftX(page); scrollY = tileMap->getPageTopY(page); startUpdate(); state->setScrollPosition(page, scrollX, scrollY); finishUpdate(gTrue, gTrue); break; case destFitH: case destFitBH: state->setZoom(zoomWidth); scrollX = tileMap->getPageLeftX(page); scrollY = tileMap->getPageTopY(page); if (dest->getChangeTop()) { cvtUserToDev(page, 0, dest->getTop(), &dx, &dy); scrollY += dy; } startUpdate(); state->setScrollPosition(page, scrollX, scrollY); finishUpdate(gTrue, gTrue); break; case destFitV: case destFitBV: state->setZoom(zoomHeight); scrollX = tileMap->getPageLeftX(page); scrollY = tileMap->getPageTopY(page); if (dest->getChangeTop()) { cvtUserToDev(page, dest->getLeft(), 0, &dx, &dy); scrollX += dx; } startUpdate(); state->setScrollPosition(page, scrollX, scrollY); finishUpdate(gTrue, gTrue); break; case destFitR: zoomToRect(page, dest->getLeft(), dest->getTop(), dest->getRight(), dest->getBottom()); break; } } void PDFCore::startUpdate() { } void PDFCore::finishUpdate(GBool addToHist, GBool checkForChangedFile) { int scrollPage, scrollX, scrollY, maxScrollX, maxScrollY; if (!doc) { invalidateWholeWindow(); updateScrollbars(); return; } // check for changes to the PDF file if (checkForChangedFile && doc->getFileName() && checkForNewFile()) { loadFile(doc->getFileName()); } // zero-page documents are a special case // (check for this *after* checking for changes to the file) if (!doc->getNumPages()) { invalidateWholeWindow(); updateScrollbars(); return; } // check the scroll position scrollPage = state->getScrollPage(); if (state->getDisplayMode() == displaySideBySideSingle && !(scrollPage & 1)) { --scrollPage; } if (state->displayModeIsContinuous()) { scrollPage = 0; } else if (scrollPage <= 0 || scrollPage > doc->getNumPages()) { scrollPage = 1; } scrollX = state->getScrollX(); scrollY = state->getScrollY(); // we need to set scrollPage before calling getScrollLimits() state->setScrollPosition(scrollPage, scrollX, scrollY); tileMap->getScrollLimits(&maxScrollX, &maxScrollY); maxScrollX -= state->getWinW(); maxScrollY -= state->getWinH(); if (scrollX > maxScrollX) { scrollX = maxScrollX; } if (scrollX < 0) { scrollX = 0; } if (scrollY > maxScrollY) { scrollY = maxScrollY; } if (scrollY < 0) { scrollY = 0; } if (scrollPage != state->getScrollPage() || scrollX != state->getScrollX() || scrollY != state->getScrollY()) { state->setScrollPosition(scrollPage, scrollX, scrollY); } // redraw invalidateWholeWindow(); updateScrollbars(); // add to history if (addToHist) { addToHistory(); } } void PDFCore::addToHistory() { PDFHistory h; PDFHistory *cur; cur = &history[historyCur]; h.page = tileMap->getMidPage(); #ifdef _WIN32 if (doc->getFileNameU()) { h.fileName = (wchar_t *)gmallocn(MAX_PATH + 1, sizeof(wchar_t)); if (GetFullPathNameW(doc->getFileNameU(), MAX_PATH + 1, h.fileName, NULL) == 0) { h.fileName = NULL; } } else { h.fileName = NULL; } #else if (doc->getFileName()) { h.fileName = doc->getFileName()->copy(); } else { h.fileName = NULL; } #endif if (historyBLen > 0 && h.page == cur->page) { if (!h.fileName && !cur->fileName) { return; } #ifdef _WIN32 if (h.fileName && cur->fileName && !wcscmp(h.fileName, cur->fileName)) { gfree(h.fileName); return; } #else if (h.fileName && cur->fileName && !h.fileName->cmp(cur->fileName)) { delete h.fileName; return; } #endif } if (++historyCur == pdfHistorySize) { historyCur = 0; } if (history[historyCur].fileName) { #ifdef _WIN32 gfree(history[historyCur].fileName); #else delete history[historyCur].fileName; #endif } history[historyCur] = h; if (historyBLen < pdfHistorySize) { ++historyBLen; } historyFLen = 0; } GBool PDFCore::gotoNextPage(int inc, GBool top) { GBool sideBySide; int pg; if (!doc || !doc->getNumPages()) { return gFalse; } pg = tileMap->getFirstPage(); sideBySide = state->displayModeIsSideBySide(); if (pg + (sideBySide ? 2 : 1) > doc->getNumPages()) { return gFalse; } if (sideBySide && inc < 2) { inc = 2; } if ((pg += inc) > doc->getNumPages()) { pg = doc->getNumPages(); } displayPage(pg, top, gFalse); return gTrue; } GBool PDFCore::gotoPrevPage(int dec, GBool top, GBool bottom) { int pg; if (!doc || !doc->getNumPages()) { return gFalse; } pg = tileMap->getFirstPage(); if (state->getDisplayMode() == displayContinuous && state->getScrollY() > tileMap->getPageTopY(pg)) { ++pg; } else if (state->getDisplayMode() == displaySideBySideContinuous && state->getScrollY() > tileMap->getPageTopY(pg)) { pg += 2; } else if (state->getDisplayMode() == displayHorizontalContinuous && state->getScrollX() > tileMap->getPageLeftX(pg)) { ++pg; } if (pg <= 1) { return gFalse; } if (state->displayModeIsSideBySide() && dec < 2) { dec = 2; } if ((pg -= dec) < 1) { pg = 1; } displayPage(pg, top, bottom); return gTrue; } GBool PDFCore::gotoNamedDestination(GString *dest) { LinkDest *d; if (!doc) { return gFalse; } if (!(d = doc->findDest(dest))) { return gFalse; } displayDest(d); delete d; return gTrue; } GBool PDFCore::goForward() { int pg; if (historyFLen == 0) { return gFalse; } if (++historyCur == pdfHistorySize) { historyCur = 0; } --historyFLen; ++historyBLen; if (!history[historyCur].fileName) { return gFalse; } #ifdef _WIN32 if (!doc || !doc->getFileNameU() || wcscmp(history[historyCur].fileName, doc->getFileNameU()) != 0) { if (loadFile(history[historyCur].fileName, (int)wcslen(history[historyCur].fileName)) != errNone) { return gFalse; } } #else if (!doc || !doc->getFileName() || history[historyCur].fileName->cmp(doc->getFileName()) != 0) { if (loadFile(history[historyCur].fileName) != errNone) { return gFalse; } } #endif pg = history[historyCur].page; displayPage(pg, gFalse, gFalse, gFalse); return gTrue; } GBool PDFCore::goBackward() { int pg; if (historyBLen <= 1) { return gFalse; } if (--historyCur < 0) { historyCur = pdfHistorySize - 1; } --historyBLen; ++historyFLen; if (!history[historyCur].fileName) { return gFalse; } #ifdef _WIN32 if (!doc || !doc->getFileNameU() || wcscmp(history[historyCur].fileName, doc->getFileNameU()) != 0) { if (loadFile(history[historyCur].fileName, (int)wcslen(history[historyCur].fileName)) != errNone) { return gFalse; } } #else if (!doc || !doc->getFileName() || history[historyCur].fileName->cmp(doc->getFileName()) != 0) { if (loadFile(history[historyCur].fileName) != errNone) { return gFalse; } } #endif pg = history[historyCur].page; displayPage(pg, gFalse, gFalse, gFalse); return gTrue; } void PDFCore::scrollLeft(int nCols) { scrollTo(state->getScrollX() - nCols, state->getScrollY()); } void PDFCore::scrollRight(int nCols) { scrollTo(state->getScrollX() + nCols, state->getScrollY()); } void PDFCore::scrollUp(int nLines, GBool snapToPage) { scrollTo(state->getScrollX(), state->getScrollY() - nLines, snapToPage); } void PDFCore::scrollUpPrevPage(int nLines) { if (!state->displayModeIsContinuous() && state->getScrollY() == 0) { gotoPrevPage(1, gFalse, gTrue); } else { scrollUp(nLines, gTrue); } } void PDFCore::scrollDown(int nLines, GBool snapToPage) { scrollTo(state->getScrollX(), state->getScrollY() + nLines, snapToPage); } void PDFCore::scrollDownNextPage(int nLines) { int horizMax, vertMax; if (!state->displayModeIsContinuous()) { tileMap->getScrollLimits(&horizMax, &vertMax); if (state->getScrollY() >= vertMax - state->getWinH()) { gotoNextPage(1, gTrue); } else { scrollDown(nLines); } } else { scrollDown(nLines, gTrue); } } void PDFCore::scrollPageUp() { scrollUpPrevPage(state->getWinH()); } void PDFCore::scrollPageDown() { scrollDownNextPage(state->getWinH()); } void PDFCore::scrollTo(int x, int y, GBool snapToPage) { int next, topPage, topPageY, sy, dy; startUpdate(); state->setScrollPosition(state->getScrollPage(), x, y); if (snapToPage) { if (state->getDisplayMode() == displayContinuous || state->getDisplayMode() == displaySideBySideContinuous) { next = state->getDisplayMode() == displaySideBySideContinuous ? 2 : 1; topPage = tileMap->getFirstPage(); // NB: topPage can be out of bounds here, because the scroll // position isn't adjusted until finishUpdate is called, below if (topPage > 0 && topPage <= doc->getNumPages()) { topPageY = tileMap->getPageTopY(topPage); sy = state->getScrollY(); dy = sy - topPageY; // note: dy can be negative here if the inter-page gap is at the // top of the window if (-16 < dy && dy < 16) { state->setScrollPosition(state->getScrollPage(), x, topPageY); } else if (topPage + next <= doc->getNumPages()) { topPage += next; topPageY = tileMap->getPageTopY(topPage); dy = sy - topPageY; if (-16 < dy && dy < 0) { state->setScrollPosition(state->getScrollPage(), x, topPageY); } } } } } finishUpdate(gTrue, gTrue); } void PDFCore::scrollToLeftEdge() { scrollTo(0, state->getScrollY()); } void PDFCore::scrollToRightEdge() { int horizMax, vertMax; tileMap->getScrollLimits(&horizMax, &vertMax); scrollTo(horizMax - state->getWinW(), state->getScrollY()); } void PDFCore::scrollToTopEdge() { scrollTo(state->getScrollX(), tileMap->getPageTopY(tileMap->getFirstPage())); } void PDFCore::scrollToBottomEdge() { scrollTo(state->getScrollX(), tileMap->getPageBottomY(tileMap->getLastPage())); } void PDFCore::scrollToTopLeft() { scrollTo(tileMap->getPageLeftX(tileMap->getFirstPage()), tileMap->getPageTopY(tileMap->getFirstPage())); } void PDFCore::scrollToBottomRight() { scrollTo(tileMap->getPageRightX(tileMap->getLastPage()), tileMap->getPageBottomY(tileMap->getLastPage())); } void PDFCore::scrollToCentered(int page, double x, double y) { int wx, wy, sx, sy; startUpdate(); // scroll to the requested page state->setScrollPosition(page, tileMap->getPageLeftX(page), tileMap->getPageTopY(page)); // scroll the requested point to the center of the window cvtUserToWindow(page, x, y, &wx, &wy); sx = state->getScrollX() + wx - state->getWinW() / 2; sy = state->getScrollY() + wy - state->getWinH() / 2; state->setScrollPosition(page, sx, sy); finishUpdate(gTrue, gFalse); } void PDFCore::setZoom(double zoom) { int page; if (state->getZoom() == zoom) { return; } if (!doc || !doc->getNumPages()) { state->setZoom(zoom); return; } startUpdate(); page = tileMap->getFirstPage(); state->setZoom(zoom); state->setScrollPosition(page, tileMap->getPageLeftX(page), tileMap->getPageTopY(page)); finishUpdate(gTrue, gTrue); } void PDFCore::zoomToRect(int page, double ulx, double uly, double lrx, double lry) { int x0, y0, x1, y1, sx, sy, t; double dpi, rx, ry, zoom; startUpdate(); // set the new zoom level cvtUserToDev(page, ulx, uly, &x0, &y0); cvtUserToDev(page, lrx, lry, &x1, &y1); if (x0 > x1) { t = x0; x0 = x1; x1 = t; } if (y0 > y1) { t = y0; y0 = y1; y1 = t; } rx = (double)state->getWinW() / (double)(x1 - x0); ry = (double)state->getWinH() / (double)(y1 - y0); dpi = tileMap->getDPI(page); if (rx < ry) { zoom = rx * (dpi / (0.01 * 72)); } else { zoom = ry * (dpi / (0.01 * 72)); } state->setZoom(zoom); // scroll to the requested page state->setScrollPosition(page, tileMap->getPageLeftX(page), tileMap->getPageTopY(page)); // scroll the requested rectangle to the center of the window cvtUserToWindow(page, 0.5 * (ulx + lrx), 0.5 * (uly + lry), &x0, &y0); sx = state->getScrollX() + x0 - state->getWinW() / 2; sy = state->getScrollY() + y0 - state->getWinH() / 2; state->setScrollPosition(page, sx, sy); finishUpdate(gTrue, gFalse); } void PDFCore::zoomCentered(double zoom) { int page, wx, wy, sx, sy; double cx, cy; if (state->getZoom() == zoom) { return; } startUpdate(); // get the center of the window, in user coords cvtWindowToUser(state->getWinW() / 2, state->getWinH() / 2, &page, &cx, &cy); // set the new zoom level state->setZoom(zoom); // scroll to re-center cvtUserToWindow(page, cx, cy, &wx, &wy); sx = state->getScrollX() + wx - state->getWinW() / 2; sy = state->getScrollY() + wy - state->getWinH() / 2; state->setScrollPosition(page, sx, sy); finishUpdate(gTrue, gFalse); } // Zoom so that the current page(s) fill the window width. Maintain // the vertical center. void PDFCore::zoomToCurrentWidth() { int page0, page1, page, gap; double w, w1, zoom; startUpdate(); // get first and last pages page0 = tileMap->getFirstPage(); page1 = tileMap->getLastPage(); // compute the desired width (in points) gap = 0; switch (state->getDisplayMode()) { case displaySingle: default: w = tileMap->getPageBoxWidth(page0); break; case displayContinuous: w = 0; for (page = page0; page <= page1; ++page) { w1 = tileMap->getPageBoxWidth(page); if (w1 > w) { w = w1; } } break; case displaySideBySideSingle: w = tileMap->getPageBoxWidth(page0); if (page1 != page0) { w += tileMap->getPageBoxWidth(page1); gap = tileMap->getSideBySidePageSpacing(); } break; case displaySideBySideContinuous: w = 0; for (page = page0; w <= page1; w += 2) { w1 = tileMap->getPageBoxWidth(page); if (page + 1 <= doc->getNumPages()) { w1 += tileMap->getPageBoxWidth(page + 1); } if (w1 > w) { w = w1; } } gap = tileMap->getSideBySidePageSpacing(); break; case displayHorizontalContinuous: w = 0; gap = 0; for (page = page0; page <= page1; ++page) { w += tileMap->getPageBoxWidth(page); if (page != page0) { gap += tileMap->getHorizContinuousPageSpacing(); } } break; } // set the new zoom level zoom = 100.0 * (state->getWinW() - gap) / w; state->setZoom(zoom); // scroll so that the first page is at the left edge of the window state->setScrollPosition(page0, tileMap->getPageLeftX(page0), tileMap->getPageTopY(page0)); finishUpdate(gTrue, gFalse); } void PDFCore::setRotate(int rotate) { int page; if (state->getRotate() == rotate) { return; } if (!doc || !doc->getNumPages()) { state->setRotate(rotate); return; } startUpdate(); page = tileMap->getFirstPage(); state->setRotate(rotate); state->setScrollPosition(page, tileMap->getPageLeftX(page), tileMap->getPageTopY(page)); finishUpdate(gTrue, gTrue); } void PDFCore::setDisplayMode(DisplayMode mode) { int page; if (state->getDisplayMode() == mode) { return; } if (!doc || !doc->getNumPages()) { state->setDisplayMode(mode); return; } startUpdate(); page = tileMap->getFirstPage(); state->setDisplayMode(mode); state->setScrollPosition(page, tileMap->getPageLeftX(page), tileMap->getPageTopY(page)); finishUpdate(gTrue, gTrue); } void PDFCore::setOCGState(OptionalContentGroup *ocg, GBool ocgState) { if (ocgState != ocg->getState()) { ocg->setState(ocgState); state->optionalContentChanged(); invalidateWholeWindow(); } } void PDFCore::setSelectMode(SelectMode mode) { if (mode != selectMode) { selectMode = mode; clearSelection(); } } SplashColorPtr PDFCore::getSelectionColor() { return state->getSelectColor(); } void PDFCore::setSelectionColor(SplashColor color) { int wx0, wy0, wx1, wy1; state->setSelectColor(color); if (state->hasSelection()) { getSelectionBBox(&wx0, &wy0, &wx1, &wy1); checkInvalidate(wx0, wy0, wx1 - wx0, wy1 - wy0); } } void PDFCore::setSelection(int page, int x0, int y0, int x1, int y1) { SelectRect *rect; GBool moveLeft, moveTop, moveRight, moveBottom, needScroll; double selectX0, selectY0, selectX1, selectY1; int oldWx0, oldWy0, oldWx1, oldWy1, ix0, iy0, ix1, iy1; int wx0, wy0, wx1, wy1, sx, sy, t; // if selection rectangle is empty, clear the selection if (x0 == x1 || y0 == y1) { clearSelection(); return; } // x0 = left, x1 = right // y0 = top, y1 = bottom if (x0 > x1) { t = x0; x0 = x1; x1 = t; } if (y0 > y1) { t = y0; y0 = y1; y1 = t; } // convert new selection coords to user space and window space tileMap->cvtDevToUser(page, x0, y0, &selectX0, &selectY0); tileMap->cvtDevToUser(page, x1, y1, &selectX1, &selectY1); cvtUserToWindow(page, selectX0, selectY0, &wx0, &wy0); cvtUserToWindow(page, selectX1, selectY1, &wx1, &wy1); if (wx0 > wx1) { t = wx0; wx0 = wx1; wx1 = t; } if (wy0 > wy1) { t = wy0; wy0 = wy1; wy1 = t; } // convert current selection coords to window space; // check which edges moved if (state->hasSelection()) { rect = state->getSelectRect(0); tileMap->cvtUserToWindow(rect->page, rect->x0, rect->y0, &oldWx0, &oldWy0); tileMap->cvtUserToWindow(rect->page, rect->x1, rect->y1, &oldWx1, &oldWy1); if (oldWx0 > oldWx1) { t = oldWx0; oldWx0 = oldWx1; oldWx1 = t; } if (oldWy0 > oldWy1) { t = oldWy0; oldWy0 = oldWy1; oldWy1 = t; } moveLeft = wx0 != oldWx0; moveTop = wy0 != oldWy0; moveRight = wx1 != oldWx1; moveBottom = wy1 != oldWy1; } else { oldWx0 = wx0; oldWy0 = wy0; oldWx1 = wx1; oldWy1 = wy1; moveLeft = moveTop = moveRight = moveBottom = gTrue; } // set the new selection state->setSelection(page, selectX0, selectY0, selectX1, selectY1); // scroll if necessary needScroll = gFalse; sx = state->getScrollX(); sy = state->getScrollY(); if (moveLeft && wx0 < 0) { sx += wx0; needScroll = gTrue; } else if (moveRight && wx1 >= state->getWinW()) { sx += wx1 - state->getWinW(); needScroll = gTrue; } else if (moveLeft && wx0 >= state->getWinW()) { sx += wx0 - state->getWinW(); needScroll = gTrue; } else if (moveRight && wx1 < 0) { sx += wx1; needScroll = gTrue; } if (moveTop && wy0 < 0) { sy += wy0; needScroll = gTrue; } else if (moveBottom && wy1 >= state->getWinH()) { sy += wy1 - state->getWinH(); needScroll = gTrue; } else if (moveTop && wy0 >= state->getWinH()) { sy += wy0 - state->getWinH(); needScroll = gTrue; } else if (moveBottom && wy1 < 0) { sy += wy1; needScroll = gTrue; } if (needScroll) { scrollTo(sx, sy); } else { ix0 = (wx0 < oldWx0) ? wx0 : oldWx0; iy0 = (wy0 < oldWy0) ? wy0 : oldWy0; ix1 = (wx1 > oldWx1) ? wx1 : oldWx1; iy1 = (wy1 > oldWy1) ? wy1 : oldWy1; checkInvalidate(ix0, iy0, ix1 - ix0, iy1 - iy0); } } void PDFCore::setLinearSelection(int page, TextPosition *pos0, TextPosition *pos1) { TextPosition begin, end; GList *rects; GBool moveLeft, moveTop, moveRight, moveBottom, needScroll; double x0, y0, x1, y1, x2, y2, x3, y3; double ux0, uy0, ux1, uy1; int oldWx0, oldWy0, oldWx1, oldWy1, ix0, iy0, ix1, iy1; int wx0, wy0, wx1, wy1; int sx, sy, colIdx; // if selection rectangle is empty, clear the selection if (*pos0 == *pos1) { clearSelection(); return; } // swap into correct order if (*pos0 < *pos1) { begin = *pos0; end = *pos1; } else { begin = *pos1; end = *pos0; } // build the list of rectangles //~ this doesn't handle RtL, vertical, or rotated text loadText(page); rects = new GList(); if (begin.colIdx == end.colIdx && begin.parIdx == end.parIdx && begin.lineIdx == end.lineIdx) { // same line text->convertPosToPointUpper(&begin, &x0, &y0); text->convertPosToPointLower(&end, &x1, &y1); cvtDevToUser(page, (int)(x0 + 0.5), (int)(y0 + 0.5), &ux0, &uy0); cvtDevToUser(page, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux1, &uy1); rects->append(new SelectRect(page, ux0, uy0, ux1, uy1)); } else if (begin.colIdx == end.colIdx) { // same column text->convertPosToPointUpper(&begin, &x0, &y0); text->convertPosToPointRightEdge(&begin, &x1, &y1); text->convertPosToPointLeftEdge(&end, &x2, &y2); text->convertPosToPointLower(&end, &x3, &y3); cvtDevToUser(page, (int)(x0 + 0.5), (int)(y0 + 0.5), &ux0, &uy0); cvtDevToUser(page, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux1, &uy1); rects->append(new SelectRect(page, ux0, uy0, ux1, uy1)); cvtDevToUser(page, (int)(x2 + 0.5), (int)(y1 + 0.5), &ux0, &uy0); cvtDevToUser(page, (int)(x1 + 0.5), (int)(y2 + 0.5), &ux1, &uy1); rects->append(new SelectRect(page, ux0, uy0, ux1, uy1)); cvtDevToUser(page, (int)(x2 + 0.5), (int)(y2 + 0.5), &ux0, &uy0); cvtDevToUser(page, (int)(x3 + 0.5), (int)(y3 + 0.5), &ux1, &uy1); rects->append(new SelectRect(page, ux0, uy0, ux1, uy1)); } else { // different columns text->convertPosToPointUpper(&begin, &x0, &y0); text->convertPosToPointRightEdge(&begin, &x1, &y1); text->getColumnLowerLeft(begin.colIdx, &x2, &y2); cvtDevToUser(page, (int)(x0 + 0.5), (int)(y0 + 0.5), &ux0, &uy0); cvtDevToUser(page, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux1, &uy1); rects->append(new SelectRect(page, ux0, uy0, ux1, uy1)); cvtDevToUser(page, (int)(x2 + 0.5), (int)(y1 + 0.5), &ux0, &uy0); cvtDevToUser(page, (int)(x1 + 0.5), (int)(y2 + 0.5), &ux1, &uy1); rects->append(new SelectRect(page, ux0, uy0, ux1, uy1)); for (colIdx = begin.colIdx + 1; colIdx < end.colIdx; ++colIdx) { text->getColumnLowerLeft(colIdx, &x0, &y0); text->getColumnUpperRight(colIdx, &x1, &y1); cvtDevToUser(page, (int)(x0 + 0.5), (int)(y1 + 0.5), &ux0, &uy0); cvtDevToUser(page, (int)(x1 + 0.5), (int)(y0 + 0.5), &ux1, &uy1); rects->append(new SelectRect(page, ux0, uy0, ux1, uy1)); } text->getColumnUpperRight(end.colIdx, &x0, &y0); text->convertPosToPointLeftEdge(&end, &x1, &y1); text->convertPosToPointLower(&end, &x2, &y2); cvtDevToUser(page, (int)(x1 + 0.5), (int)(y0 + 0.5), &ux0, &uy0); cvtDevToUser(page, (int)(x0 + 0.5), (int)(y1 + 0.5), &ux1, &uy1); rects->append(new SelectRect(page, ux0, uy0, ux1, uy1)); cvtDevToUser(page, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux0, &uy0); cvtDevToUser(page, (int)(x2 + 0.5), (int)(y2 + 0.5), &ux1, &uy1); rects->append(new SelectRect(page, ux0, uy0, ux1, uy1)); } // get window coord bboxes for old selection and new selection; // check which edges moved if (state->hasSelection()) { getSelectionBBox(&oldWx0, &oldWy0, &oldWx1, &oldWy1); getSelectRectListBBox(rects, &wx0, &wy0, &wx1, &wy1); moveLeft = wx0 != oldWx0; moveTop = wy0 != oldWy0; moveRight = wx1 != oldWx1; moveBottom = wy1 != oldWy1; } else { getSelectRectListBBox(rects, &wx0, &wy0, &wx1, &wy1); oldWx0 = wx0; oldWy0 = wy0; oldWx1 = wx1; oldWy1 = wy1; moveLeft = moveTop = moveRight = moveBottom = gTrue; } // set the new selection state->setSelection(rects); // scroll if necessary needScroll = gFalse; sx = state->getScrollX(); sy = state->getScrollY(); if (moveLeft && wx0 < 0) { sx += wx0; needScroll = gTrue; } else if (moveRight && wx1 >= state->getWinW()) { sx += wx1 - state->getWinW(); needScroll = gTrue; } else if (moveLeft && wx0 >= state->getWinW()) { sx += wx0 - state->getWinW(); needScroll = gTrue; } else if (moveRight && wx1 < 0) { sx += wx1; needScroll = gTrue; } if (moveTop && wy0 < 0) { sy += wy0; needScroll = gTrue; } else if (moveBottom && wy1 >= state->getWinH()) { sy += wy1 - state->getWinH(); needScroll = gTrue; } else if (moveTop && wy0 >= state->getWinH()) { sy += wy0 - state->getWinH(); needScroll = gTrue; } else if (moveBottom && wy1 < 0) { sy += wy1; needScroll = gTrue; } if (needScroll) { scrollTo(sx, sy); } else { ix0 = (wx0 < oldWx0) ? wx0 : oldWx0; iy0 = (wy0 < oldWy0) ? wy0 : oldWy0; ix1 = (wx1 > oldWx1) ? wx1 : oldWx1; iy1 = (wy1 > oldWy1) ? wy1 : oldWy1; checkInvalidate(ix0, iy0, ix1 - ix0, iy1 - iy0); } } void PDFCore::clearSelection() { int wx0, wy0, wx1, wy1; if (state->hasSelection()) { getSelectionBBox(&wx0, &wy0, &wx1, &wy1); state->clearSelection(); checkInvalidate(wx0, wy0, wx1 - wx0, wy1 - wy0); } } void PDFCore::startSelectionDrag(int pg, int x, int y) { clearSelection(); if (selectMode == selectModeBlock) { selectPage = pg; selectStartX = x; selectStartY = y; } else { // selectModeLinear loadText(pg); if (text->findPointInside(x, y, &selectStartPos)) { selectPage = pg; } else { selectPage = 0; } } } void PDFCore::moveSelectionDrag(int pg, int x, int y) { TextPosition pos; // don't allow selections to span multiple pages // -- this also handles the case where a linear selection was started // outside any text column, in which case selectPage = 0 if (pg != selectPage) { return; } if (selectMode == selectModeBlock) { setSelection(pg, selectStartX, selectStartY, x, y); } else { // selectModeLinear loadText(pg); if (text->findPointNear(x, y, &pos)) { setLinearSelection(pg, &selectStartPos, &pos); } } } void PDFCore::finishSelectionDrag() { // nothing } void PDFCore::selectWord(int pg, int x, int y) { TextPosition endPos; loadText(pg); if (text->findWordPoints(x, y, &selectStartPos, &endPos)) { selectPage = pg; setLinearSelection(pg, &selectStartPos, &endPos); } else { selectPage = 0; } } void PDFCore::selectLine(int pg, int x, int y) { TextPosition endPos; loadText(pg); if (text->findLinePoints(x, y, &selectStartPos, &endPos)) { selectPage = pg; setLinearSelection(pg, &selectStartPos, &endPos); } else { selectPage = 0; } } GBool PDFCore::getSelection(int *pg, double *ulx, double *uly, double *lrx, double *lry) { SelectRect *rect; double xMin, yMin, xMax, yMax; int page, i; if (!state->hasSelection()) { return gFalse; } page = state->getSelectRect(0)->page; xMin = yMin = xMax = yMax = 0; for (i = 0; i < state->getNumSelectRects(); ++i) { rect = state->getSelectRect(i); if (rect->page != page) { continue; } if (i == 0) { xMin = xMax = rect->x0; yMin = yMax = rect->y0; } else { if (rect->x0 < xMin) { xMin = rect->x0; } else if (rect->x0 > xMax) { xMax = rect->x0; } if (rect->y0 < yMin) { yMin = rect->y0; } else if (rect->y0 > yMax) { yMax = rect->y0; } } if (rect->x1 < xMin) { xMin = rect->x1; } else if (rect->x1 > xMax) { xMax = rect->x1; } if (rect->y1 < yMin) { yMin = rect->y1; } else if (rect->y1 > yMax) { yMax = rect->y1; } } *pg = page; *ulx = xMin; *uly = yMax; *lrx = xMax; *lry = yMin; return gTrue; } GBool PDFCore::hasSelection() { return state->hasSelection(); } void PDFCore::setTextExtractionMode(TextOutputMode mode) { if (textOutCtrl.mode != mode) { textOutCtrl.mode = mode; if (text) { delete text; text = NULL; } textPage = 0; textDPI = 0; textRotate = 0; } } GBool PDFCore::getDiscardDiagonalText() { return textOutCtrl.discardDiagonalText; } void PDFCore::setDiscardDiagonalText(GBool discard) { if (textOutCtrl.discardDiagonalText != discard) { textOutCtrl.discardDiagonalText = discard; if (text) { delete text; text = NULL; } textPage = 0; textDPI = 0; textRotate = 0; } } GString *PDFCore::extractText(int pg, double xMin, double yMin, double xMax, double yMax) { int x0, y0, x1, y1, t; loadText(pg); cvtUserToDev(pg, xMin, yMin, &x0, &y0); cvtUserToDev(pg, xMax, yMax, &x1, &y1); if (x0 > x1) { t = x0; x0 = x1; x1 = t; } if (y0 > y1) { t = y0; y0 = y1; y1 = t; } return text->getText(x0, y0, x1, y1); } GString *PDFCore::getSelectedText() { SelectRect *rect; GString *ret, *s; int x0, y0, x1, y1, t, i; if (!state->hasSelection()) { return NULL; } ret = new GString(); for (i = 0; i < state->getNumSelectRects(); ++i) { rect = state->getSelectRect(i); loadText(rect->page); cvtUserToDev(rect->page, rect->x0, rect->y0, &x0, &y0); cvtUserToDev(rect->page, rect->x1, rect->y1, &x1, &y1); if (x0 > x1) { t = x0; x0 = x1; x1 = t; } if (y0 > y1) { t = y0; y0 = y1; y1 = t; } s = text->getText(x0, y0, x1, y1, state->getNumSelectRects() > 1); ret->append(s); delete s; } return ret; } GBool PDFCore::find(char *s, GBool caseSensitive, GBool next, GBool backward, GBool wholeWord, GBool onePageOnly) { Unicode *u; int len, i; GBool ret; // convert to Unicode len = (int)strlen(s); u = (Unicode *)gmallocn(len, sizeof(Unicode)); for (i = 0; i < len; ++i) { u[i] = (Unicode)(s[i] & 0xff); } ret = findU(u, len, caseSensitive, next, backward, wholeWord, onePageOnly); gfree(u); return ret; } GBool PDFCore::findU(Unicode *u, int len, GBool caseSensitive, GBool next, GBool backward, GBool wholeWord, GBool onePageOnly) { TextOutputDev *textOut; SelectRect *rect; double xMin, yMin, xMax, yMax; int topPage, pg, x, y, x2, y2; GBool startAtTop, startAtLast, stopAtLast; // check for zero-length string if (len == 0) { return gFalse; } setBusyCursor(gTrue); // search current page starting at previous result, current // selection, or top/bottom of page startAtTop = startAtLast = gFalse; rect = NULL; xMin = yMin = xMax = yMax = 0; topPage = tileMap->getFirstPage(); pg = topPage; if (next) { if (textPage >= 1 && textPage <= doc->getNumPages()) { startAtLast = gTrue; pg = textPage; } } else if (state->hasSelection()) { rect = state->getSelectRect(0); pg = rect->page; cvtUserToDev(pg, rect->x0, rect->y0, &x, &y); cvtUserToDev(pg, rect->x1, rect->y1, &x2, &y2); if (x2 < x) { x = x2; } if (y2 < y) { y = y2; } if (backward) { xMin = x - 1; yMin = y - 1; } else { xMin = x + 1; yMin = y + 1; } } else { startAtTop = gTrue; } loadText(pg); if (text->findText(u, len, startAtTop, gTrue, startAtLast, gFalse, caseSensitive, backward, wholeWord, &xMin, &yMin, &xMax, &yMax)) { goto found; } if (!onePageOnly) { // search following/previous pages textOut = new TextOutputDev(NULL, &textOutCtrl, gFalse); if (!textOut->isOk()) { delete textOut; goto notFound; } for (pg = backward ? pg - 1 : pg + 1; backward ? pg >= 1 : pg <= doc->getNumPages(); pg += backward ? -1 : 1) { doc->displayPage(textOut, pg, 72, 72, 0, gFalse, gTrue, gFalse); if (textOut->findText(u, len, gTrue, gTrue, gFalse, gFalse, caseSensitive, backward, wholeWord, &xMin, &yMin, &xMax, &yMax)) { delete textOut; goto foundPage; } } // search previous/following pages for (pg = backward ? doc->getNumPages() : 1; backward ? pg > topPage : pg < topPage; pg += backward ? -1 : 1) { doc->displayPage(textOut, pg, 72, 72, 0, gFalse, gTrue, gFalse); if (textOut->findText(u, len, gTrue, gTrue, gFalse, gFalse, caseSensitive, backward, wholeWord, &xMin, &yMin, &xMax, &yMax)) { delete textOut; goto foundPage; } } delete textOut; } // search current page ending at previous result, current selection, // or bottom/top of page if (!startAtTop) { xMin = yMin = xMax = yMax = 0; if (next) { stopAtLast = gTrue; } else { stopAtLast = gFalse; cvtUserToDev(pg, rect->x1, rect->y1, &x, &y); xMax = x; yMax = y; } if (text->findText(u, len, gTrue, gFalse, gFalse, stopAtLast, caseSensitive, backward, wholeWord, &xMin, &yMin, &xMax, &yMax)) { goto found; } } // not found notFound: setBusyCursor(gFalse); return gFalse; // found on a different page foundPage: displayPage(pg, gTrue, gFalse); loadText(pg); if (!text->findText(u, len, gTrue, gTrue, gFalse, gFalse, caseSensitive, backward, wholeWord, &xMin, &yMin, &xMax, &yMax)) { // this can happen if coalescing is bad goto notFound; } // found: change the selection found: setSelection(pg, (int)floor(xMin), (int)floor(yMin), (int)ceil(xMax), (int)ceil(yMax)); setBusyCursor(gFalse); return gTrue; } GList *PDFCore::findAll(Unicode *u, int len, GBool caseSensitive, GBool wholeWord, int firstPage, int lastPage) { GList *results = new GList(); TextOutputDev *textOut = new TextOutputDev(NULL, &textOutCtrl, gFalse); if (!textOut->isOk()) { delete textOut; return results; } for (int pg = firstPage; pg <= lastPage; ++pg) { doc->displayPage(textOut, pg, 72, 72, 0, gFalse, gTrue, gFalse); GBool first = gTrue; while (1) { double xMin, yMin, xMax, yMax; if (!textOut->findText(u, len, first, gTrue, !first, gFalse, caseSensitive, gFalse, wholeWord, &xMin, &yMin, &xMax, &yMax)) { break; } double uxMin, uyMin, uxMax, uyMax, t; textOut->cvtDevToUser(xMin, yMin, &uxMin, &uyMin); textOut->cvtDevToUser(xMax, yMax, &uxMax, &uyMax); if (uxMin > uxMax) { t = uxMin; uxMin = uxMax; uxMax = t; } if (uyMin > uyMax) { t = uyMin; uyMin = uyMax; uyMax = t; } results->append(new FindResult(pg, uxMin, uyMin, uxMax, uyMax)); first = gFalse; } } delete textOut; return results; } GBool PDFCore::cvtWindowToUser(int xw, int yw, int *pg, double *xu, double *yu) { return tileMap->cvtWindowToUser(xw, yw, pg, xu, yu); } GBool PDFCore::cvtWindowToDev(int xw, int yw, int *pg, int *xd, int *yd) { return tileMap->cvtWindowToDev(xw, yw, pg, xd, yd); } GBool PDFCore::cvtUserToWindow(int pg, double xu, double yu, int *xw, int *yw) { return tileMap->cvtUserToWindow(pg, xu, yu, xw, yw); } void PDFCore::cvtUserToDev(int pg, double xu, double yu, int *xd, int *yd) { tileMap->cvtUserToDev(pg, xu, yu, xd, yd); } GBool PDFCore::cvtDevToWindow(int pg, int xd, int yd, int *xw, int *yw) { return tileMap->cvtDevToWindow(pg, xd, yd, xw, yw); } void PDFCore::cvtDevToUser(int pg, int xd, int yd, double *xu, double *yu) { tileMap->cvtDevToUser(pg, xd, yd, xu, yu); } void PDFCore::getWindowPageRange(int x, int y, int w, int h, int *firstPage, int *lastPage) { tileMap->getWindowPageRange(x, y, w, h, firstPage, lastPage); } int PDFCore::getPageNum() { if (!doc || !doc->getNumPages()) { return 0; } return tileMap->getFirstPage(); } int PDFCore::getMidPageNum() { if (!doc || !doc->getNumPages()) { return 0; } return tileMap->getMidPage(); } double PDFCore::getZoom() { return state->getZoom(); } double PDFCore::getZoomDPI(int page) { if (!doc) { return 0; } return tileMap->getDPI(page); } int PDFCore::getRotate() { return state->getRotate(); } DisplayMode PDFCore::getDisplayMode() { return state->getDisplayMode(); } int PDFCore::getScrollX() { return state->getScrollX(); } int PDFCore::getScrollY() { return state->getScrollY(); } int PDFCore::getWindowWidth() { return state->getWinW(); } int PDFCore::getWindowHeight() { return state->getWinH(); } void PDFCore::setPaperColor(SplashColorPtr paperColor) { state->setPaperColor(paperColor); invalidateWholeWindow(); } void PDFCore::setMatteColor(SplashColorPtr matteColor) { state->setMatteColor(matteColor); invalidateWholeWindow(); } void PDFCore::setReverseVideo(GBool reverseVideo) { SplashColorPtr oldPaperColor; SplashColor newPaperColor; int i; if (reverseVideo != state->getReverseVideo()) { state->setReverseVideo(reverseVideo); oldPaperColor = state->getPaperColor(); for (i = 0; i < splashColorModeNComps[state->getColorMode()]; ++i) { newPaperColor[i] = oldPaperColor[i] ^ 0xff; } state->setPaperColor(newPaperColor); invalidateWholeWindow(); } } LinkAction *PDFCore::findLink(int pg, double x, double y) { loadLinks(pg); return links->find(x, y); } Annot *PDFCore::findAnnot(int pg, double x, double y) { loadAnnots(pg); return annots->find(x, y); } int PDFCore::findAnnotIdx(int pg, double x, double y) { loadAnnots(pg); return annots->findIdx(x, y); } Annot *PDFCore::getAnnot(int idx) { if (!annots) { return NULL; } if (idx < 0 || idx >= annots->getNumAnnots()) { return NULL; } return annots->getAnnot(idx); } AcroFormField *PDFCore::findFormField(int pg, double x, double y) { if (!doc->getCatalog()->getForm()) { return NULL; } return doc->getCatalog()->getForm()->findField(pg, x, y); } int PDFCore::findFormFieldIdx(int pg, double x, double y) { if (!doc->getCatalog()->getForm()) { return -1; } return doc->getCatalog()->getForm()->findFieldIdx(pg, x, y); } AcroFormField *PDFCore::getFormField(int idx) { if (!doc->getCatalog()->getForm()) { return NULL; } if (idx < 0 || idx >= doc->getCatalog()->getForm()->getNumFields()) { return NULL; } return doc->getCatalog()->getForm()->getField(idx); } GBool PDFCore::overText(int pg, double x, double y) { loadText(pg); return text->checkPointInside(x, y); } void PDFCore::forceRedraw() { startUpdate(); state->forceRedraw(); finishUpdate(gFalse, gFalse); } void PDFCore::setTileDoneCbk(void (*cbk)(void *data), void *data) { tileCache->setTileDoneCbk(cbk, data); } void PDFCore::setWindowSize(int winWidth, int winHeight) { GBool doScroll; int page, wx0, wy0, wx, wy, sx, sy; double ux, uy; startUpdate(); wx0 = wy0 = 0; // make gcc happy doScroll = gFalse; if (state->getZoom() < 0 && state->displayModeIsContinuous()) { // save the user coordinates of the appropriate edge of the window if (state->getDisplayMode() == displayHorizontalContinuous) { wx0 = 0; wy0 = state->getWinH() / 2; } else { wx0 = state->getWinW() / 2; wy0 = 0; } if (!(doScroll = cvtWindowToUser(wx0, wy0, &page, &ux, &uy))) { // tweak the save position if it happens to fall in a gutter if (state->getDisplayMode() == displayContinuous) { wy0 += tileMap->getContinuousPageSpacing(); } else if (state->getDisplayMode() == displaySideBySideContinuous) { wx0 += tileMap->getSideBySidePageSpacing(); wy0 += tileMap->getContinuousPageSpacing(); } else { // state->getDisplayMode() == displayHorizontalContinuous wx0 += tileMap->getHorizContinuousPageSpacing(); } doScroll = cvtWindowToUser(wx0, wy0, &page, &ux, &uy); } } state->setWindowSize(winWidth, winHeight); if (doScroll) { // restore the saved scroll position cvtUserToWindow(page, ux, uy, &wx, &wy); sx = state->getScrollX(); sy = state->getScrollY(); if (state->getDisplayMode() == displayHorizontalContinuous) { sx += wx - wx0; } else { sy += wy - wy0; } state->setScrollPosition(page, sx, sy); } finishUpdate(gTrue, gFalse); } SplashBitmap *PDFCore::getWindowBitmap(GBool wholeWindow) { GBool dummy; return tileCompositor->getBitmap(wholeWindow ? &bitmapFinished : &dummy); } void PDFCore::tick() { if (!bitmapFinished) { invalidateWholeWindow(); } } // Clear cached info (links, text) that's tied to a PDFDoc. void PDFCore::clearPage() { if (links) { delete links; } links = NULL; linksPage = 0; if (annots) { delete annots; } annots = NULL; annotsPage = 0; if (text) { delete text; } text = NULL; textPage = 0; textDPI = 0; textRotate = 0; } // Load the links for . void PDFCore::loadLinks(int pg) { if (links && linksPage == pg) { return; } if (links) { delete links; } links = doc->getLinks(pg); linksPage = pg; } // Load the annotations for . void PDFCore::loadAnnots(int pg) { Object annotsObj; if (annots && annotsPage == pg) { return; } if (annots) { delete annots; } doc->getCatalog()->getPage(pg)->getAnnots(&annotsObj); annots = new Annots(doc, &annotsObj); annotsObj.free(); annotsPage = pg; } // Extract text from . void PDFCore::loadText(int pg) { TextOutputDev *textOut; double dpi; int rotate; dpi = tileMap->getDPI(pg); rotate = state->getRotate(); if (text && textPage == pg && textDPI == dpi && textRotate == rotate) { return; } if (text) { delete text; } textOut = new TextOutputDev(NULL, &textOutCtrl, gFalse); if (!textOut->isOk()) { text = new TextPage(&textOutCtrl); } else { doc->displayPage(textOut, pg, dpi, dpi, rotate, gFalse, gTrue, gFalse); text = textOut->takeText(); } delete textOut; textPage = pg; textDPI = dpi; textRotate = rotate; } void PDFCore::getSelectionBBox(int *wxMin, int *wyMin, int *wxMax, int *wyMax) { *wxMin = *wyMin = *wxMax = *wyMax = 0; if (!state->hasSelection()) { return; } getSelectRectListBBox(state->getSelectRects(), wxMin, wyMin, wxMax, wyMax); } void PDFCore::getSelectRectListBBox(GList *rects, int *wxMin, int *wyMin, int *wxMax, int *wyMax) { SelectRect *rect; int x, y, i; *wxMin = *wyMin = *wxMax = *wyMax = 0; for (i = 0; i < rects->getLength(); ++i) { rect = (SelectRect *)rects->get(i); tileMap->cvtUserToWindow(rect->page, rect->x0, rect->y0, &x, &y); if (i == 0) { *wxMin = *wxMax = x; *wyMin = *wyMax = y; } else { if (x < *wxMin) { *wxMin = x; } else if (x > *wxMax) { *wxMax = x; } if (y < *wyMin) { *wyMin = y; } else if (y > *wyMax) { *wyMax = y; } } tileMap->cvtUserToWindow(rect->page, rect->x1, rect->y1, &x, &y); if (x < *wxMin) { *wxMin = x; } else if (x > *wxMax) { *wxMax = x; } if (y < *wyMin) { *wyMin = y; } else if (y > *wyMax) { *wyMax = y; } } } void PDFCore::checkInvalidate(int x, int y, int w, int h) { if (x < 0) { w += x; x = 0; } if (x + w > state->getWinW()) { w = state->getWinW() - x; } if (w <= 0) { return; } if (y < 0) { h += y; y = 0; } if (y + h > state->getWinH()) { h = state->getWinH() - y; } if (h <= 0) { return; } invalidate(x, y, w, h); } void PDFCore::invalidateWholeWindow() { invalidate(0, 0, state->getWinW(), state->getWinH()); }