diff options
author | Calvin Morrison <calvin@pobox.com> | 2023-04-05 14:13:39 -0400 |
---|---|---|
committer | Calvin Morrison <calvin@pobox.com> | 2023-04-05 14:13:39 -0400 |
commit | 835e373b3eeaabcd0621ed6798ab500f37982fae (patch) | |
tree | dfa16b0e2e1b4956b38f693220eac4e607802133 /xpdf/PDFCore.cc |
Diffstat (limited to 'xpdf/PDFCore.cc')
-rw-r--r-- | xpdf/PDFCore.cc | 2024 |
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()); +} |