aboutsummaryrefslogtreecommitdiff
path: root/xpdf/PDFCore.cc
diff options
context:
space:
mode:
Diffstat (limited to 'xpdf/PDFCore.cc')
-rw-r--r--xpdf/PDFCore.cc2024
1 files changed, 2024 insertions, 0 deletions
diff --git a/xpdf/PDFCore.cc b/xpdf/PDFCore.cc
new file mode 100644
index 0000000..b833d5d
--- /dev/null
+++ b/xpdf/PDFCore.cc
@@ -0,0 +1,2024 @@
+//========================================================================
+//
+// PDFCore.cc
+//
+// Copyright 2004-2014 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <math.h>
+#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
+ // - if the bitmap is available (e.g., we just scrolled), we want to
+ // redraw immediately; if not, postpone the redraw until a
+ // tileDone or tick (incremental update) to avoid "flashing" the
+ // screen (drawing a blank background, followed by the actual
+ // content slightly later)
+ getWindowBitmap(gTrue);
+ if (bitmapFinished) {
+ invalidateWholeWindow();
+ }
+ updateScrollbars();
+
+ // add to history
+ if (addToHist) {
+ addToHistory();
+ }
+}
+
+void PDFCore::addToHistory() {
+ PDFHistory h;
+ PDFHistory *cur;
+
+ cur = &history[historyCur];
+ h.page = tileMap->getMidPage();
+ h.fileName = NULL;
+#ifdef _WIN32
+ if (doc->getFileNameU()) {
+ wchar_t dummy;
+ // NB: if the buffer is too small, GetFullPathNameW returns a
+ // *maximum* required buffer size, which may be larger than the
+ // size actually used by the second call (it looks like it just
+ // adds the size of the current directory and the sizef of the
+ // input path)
+ DWORD nChars = GetFullPathNameW(doc->getFileNameU(), 1, &dummy, NULL);
+ if (nChars > 0) {
+ h.fileName = (wchar_t *)gmallocn(nChars, sizeof(wchar_t));
+ if (GetFullPathNameW(doc->getFileNameU(), nChars,
+ h.fileName, NULL) == 0) {
+ gfree(h.fileName);
+ h.fileName = NULL;
+ }
+ }
+ }
+#else
+ if (doc->getFileName()) {
+ h.fileName = doc->getFileName()->copy();
+ }
+#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 <pg>.
+void PDFCore::loadLinks(int pg) {
+ if (links && linksPage == pg) {
+ return;
+ }
+ if (links) {
+ delete links;
+ }
+ links = doc->getLinks(pg);
+ linksPage = pg;
+}
+
+// Load the annotations for <pg>.
+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 <pg>.
+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());
+}