Files
Yajbir Singh f1b860b25c
Some checks failed
check / markdownlint (push) Has been cancelled
check / spellchecker (push) Has been cancelled
updated
2025-12-11 19:03:17 +05:30

2010 lines
49 KiB
C++

//========================================================================
//
// 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
invalidateWholeWindow();
updateScrollbars();
// add to history
if (addToHist) {
addToHistory();
}
}
void PDFCore::addToHistory() {
PDFHistory h;
PDFHistory *cur;
cur = &history[historyCur];
h.page = tileMap->getMidPage();
#ifdef _WIN32
if (doc->getFileNameU()) {
h.fileName = (wchar_t *)gmallocn(MAX_PATH + 1, sizeof(wchar_t));
if (GetFullPathNameW(doc->getFileNameU(), MAX_PATH + 1,
h.fileName, NULL) == 0) {
h.fileName = NULL;
}
} else {
h.fileName = NULL;
}
#else
if (doc->getFileName()) {
h.fileName = doc->getFileName()->copy();
} else {
h.fileName = NULL;
}
#endif
if (historyBLen > 0 && h.page == cur->page) {
if (!h.fileName && !cur->fileName) {
return;
}
#ifdef _WIN32
if (h.fileName && cur->fileName && !wcscmp(h.fileName, cur->fileName)) {
gfree(h.fileName);
return;
}
#else
if (h.fileName && cur->fileName && !h.fileName->cmp(cur->fileName)) {
delete h.fileName;
return;
}
#endif
}
if (++historyCur == pdfHistorySize) {
historyCur = 0;
}
if (history[historyCur].fileName) {
#ifdef _WIN32
gfree(history[historyCur].fileName);
#else
delete history[historyCur].fileName;
#endif
}
history[historyCur] = h;
if (historyBLen < pdfHistorySize) {
++historyBLen;
}
historyFLen = 0;
}
GBool PDFCore::gotoNextPage(int inc, GBool top) {
GBool sideBySide;
int pg;
if (!doc || !doc->getNumPages()) {
return gFalse;
}
pg = tileMap->getFirstPage();
sideBySide = state->displayModeIsSideBySide();
if (pg + (sideBySide ? 2 : 1) > doc->getNumPages()) {
return gFalse;
}
if (sideBySide && inc < 2) {
inc = 2;
}
if ((pg += inc) > doc->getNumPages()) {
pg = doc->getNumPages();
}
displayPage(pg, top, gFalse);
return gTrue;
}
GBool PDFCore::gotoPrevPage(int dec, GBool top, GBool bottom) {
int pg;
if (!doc || !doc->getNumPages()) {
return gFalse;
}
pg = tileMap->getFirstPage();
if (state->getDisplayMode() == displayContinuous &&
state->getScrollY() > tileMap->getPageTopY(pg)) {
++pg;
} else if (state->getDisplayMode() == displaySideBySideContinuous &&
state->getScrollY() > tileMap->getPageTopY(pg)) {
pg += 2;
} else if (state->getDisplayMode() == displayHorizontalContinuous &&
state->getScrollX() > tileMap->getPageLeftX(pg)) {
++pg;
}
if (pg <= 1) {
return gFalse;
}
if (state->displayModeIsSideBySide() && dec < 2) {
dec = 2;
}
if ((pg -= dec) < 1) {
pg = 1;
}
displayPage(pg, top, bottom);
return gTrue;
}
GBool PDFCore::gotoNamedDestination(GString *dest) {
LinkDest *d;
if (!doc) {
return gFalse;
}
if (!(d = doc->findDest(dest))) {
return gFalse;
}
displayDest(d);
delete d;
return gTrue;
}
GBool PDFCore::goForward() {
int pg;
if (historyFLen == 0) {
return gFalse;
}
if (++historyCur == pdfHistorySize) {
historyCur = 0;
}
--historyFLen;
++historyBLen;
if (!history[historyCur].fileName) {
return gFalse;
}
#ifdef _WIN32
if (!doc ||
!doc->getFileNameU() ||
wcscmp(history[historyCur].fileName, doc->getFileNameU()) != 0) {
if (loadFile(history[historyCur].fileName,
(int)wcslen(history[historyCur].fileName)) != errNone) {
return gFalse;
}
}
#else
if (!doc ||
!doc->getFileName() ||
history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
if (loadFile(history[historyCur].fileName) != errNone) {
return gFalse;
}
}
#endif
pg = history[historyCur].page;
displayPage(pg, gFalse, gFalse, gFalse);
return gTrue;
}
GBool PDFCore::goBackward() {
int pg;
if (historyBLen <= 1) {
return gFalse;
}
if (--historyCur < 0) {
historyCur = pdfHistorySize - 1;
}
--historyBLen;
++historyFLen;
if (!history[historyCur].fileName) {
return gFalse;
}
#ifdef _WIN32
if (!doc ||
!doc->getFileNameU() ||
wcscmp(history[historyCur].fileName, doc->getFileNameU()) != 0) {
if (loadFile(history[historyCur].fileName,
(int)wcslen(history[historyCur].fileName)) != errNone) {
return gFalse;
}
}
#else
if (!doc ||
!doc->getFileName() ||
history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
if (loadFile(history[historyCur].fileName) != errNone) {
return gFalse;
}
}
#endif
pg = history[historyCur].page;
displayPage(pg, gFalse, gFalse, gFalse);
return gTrue;
}
void PDFCore::scrollLeft(int nCols) {
scrollTo(state->getScrollX() - nCols, state->getScrollY());
}
void PDFCore::scrollRight(int nCols) {
scrollTo(state->getScrollX() + nCols, state->getScrollY());
}
void PDFCore::scrollUp(int nLines, GBool snapToPage) {
scrollTo(state->getScrollX(), state->getScrollY() - nLines, snapToPage);
}
void PDFCore::scrollUpPrevPage(int nLines) {
if (!state->displayModeIsContinuous() &&
state->getScrollY() == 0) {
gotoPrevPage(1, gFalse, gTrue);
} else {
scrollUp(nLines, gTrue);
}
}
void PDFCore::scrollDown(int nLines, GBool snapToPage) {
scrollTo(state->getScrollX(), state->getScrollY() + nLines, snapToPage);
}
void PDFCore::scrollDownNextPage(int nLines) {
int horizMax, vertMax;
if (!state->displayModeIsContinuous()) {
tileMap->getScrollLimits(&horizMax, &vertMax);
if (state->getScrollY() >= vertMax - state->getWinH()) {
gotoNextPage(1, gTrue);
} else {
scrollDown(nLines);
}
} else {
scrollDown(nLines, gTrue);
}
}
void PDFCore::scrollPageUp() {
scrollUpPrevPage(state->getWinH());
}
void PDFCore::scrollPageDown() {
scrollDownNextPage(state->getWinH());
}
void PDFCore::scrollTo(int x, int y, GBool snapToPage) {
int next, topPage, topPageY, sy, dy;
startUpdate();
state->setScrollPosition(state->getScrollPage(), x, y);
if (snapToPage) {
if (state->getDisplayMode() == displayContinuous ||
state->getDisplayMode() == displaySideBySideContinuous) {
next = state->getDisplayMode() == displaySideBySideContinuous ? 2 : 1;
topPage = tileMap->getFirstPage();
// NB: topPage can be out of bounds here, because the scroll
// position isn't adjusted until finishUpdate is called, below
if (topPage > 0 && topPage <= doc->getNumPages()) {
topPageY = tileMap->getPageTopY(topPage);
sy = state->getScrollY();
dy = sy - topPageY;
// note: dy can be negative here if the inter-page gap is at the
// top of the window
if (-16 < dy && dy < 16) {
state->setScrollPosition(state->getScrollPage(), x, topPageY);
} else if (topPage + next <= doc->getNumPages()) {
topPage += next;
topPageY = tileMap->getPageTopY(topPage);
dy = sy - topPageY;
if (-16 < dy && dy < 0) {
state->setScrollPosition(state->getScrollPage(), x, topPageY);
}
}
}
}
}
finishUpdate(gTrue, gTrue);
}
void PDFCore::scrollToLeftEdge() {
scrollTo(0, state->getScrollY());
}
void PDFCore::scrollToRightEdge() {
int horizMax, vertMax;
tileMap->getScrollLimits(&horizMax, &vertMax);
scrollTo(horizMax - state->getWinW(), state->getScrollY());
}
void PDFCore::scrollToTopEdge() {
scrollTo(state->getScrollX(),
tileMap->getPageTopY(tileMap->getFirstPage()));
}
void PDFCore::scrollToBottomEdge() {
scrollTo(state->getScrollX(),
tileMap->getPageBottomY(tileMap->getLastPage()));
}
void PDFCore::scrollToTopLeft() {
scrollTo(tileMap->getPageLeftX(tileMap->getFirstPage()),
tileMap->getPageTopY(tileMap->getFirstPage()));
}
void PDFCore::scrollToBottomRight() {
scrollTo(tileMap->getPageRightX(tileMap->getLastPage()),
tileMap->getPageBottomY(tileMap->getLastPage()));
}
void PDFCore::scrollToCentered(int page, double x, double y) {
int wx, wy, sx, sy;
startUpdate();
// scroll to the requested page
state->setScrollPosition(page, tileMap->getPageLeftX(page),
tileMap->getPageTopY(page));
// scroll the requested point to the center of the window
cvtUserToWindow(page, x, y, &wx, &wy);
sx = state->getScrollX() + wx - state->getWinW() / 2;
sy = state->getScrollY() + wy - state->getWinH() / 2;
state->setScrollPosition(page, sx, sy);
finishUpdate(gTrue, gFalse);
}
void PDFCore::setZoom(double zoom) {
int page;
if (state->getZoom() == zoom) {
return;
}
if (!doc || !doc->getNumPages()) {
state->setZoom(zoom);
return;
}
startUpdate();
page = tileMap->getFirstPage();
state->setZoom(zoom);
state->setScrollPosition(page, tileMap->getPageLeftX(page),
tileMap->getPageTopY(page));
finishUpdate(gTrue, gTrue);
}
void PDFCore::zoomToRect(int page, double ulx, double uly,
double lrx, double lry) {
int x0, y0, x1, y1, sx, sy, t;
double dpi, rx, ry, zoom;
startUpdate();
// set the new zoom level
cvtUserToDev(page, ulx, uly, &x0, &y0);
cvtUserToDev(page, lrx, lry, &x1, &y1);
if (x0 > x1) {
t = x0; x0 = x1; x1 = t;
}
if (y0 > y1) {
t = y0; y0 = y1; y1 = t;
}
rx = (double)state->getWinW() / (double)(x1 - x0);
ry = (double)state->getWinH() / (double)(y1 - y0);
dpi = tileMap->getDPI(page);
if (rx < ry) {
zoom = rx * (dpi / (0.01 * 72));
} else {
zoom = ry * (dpi / (0.01 * 72));
}
state->setZoom(zoom);
// scroll to the requested page
state->setScrollPosition(page, tileMap->getPageLeftX(page),
tileMap->getPageTopY(page));
// scroll the requested rectangle to the center of the window
cvtUserToWindow(page, 0.5 * (ulx + lrx), 0.5 * (uly + lry), &x0, &y0);
sx = state->getScrollX() + x0 - state->getWinW() / 2;
sy = state->getScrollY() + y0 - state->getWinH() / 2;
state->setScrollPosition(page, sx, sy);
finishUpdate(gTrue, gFalse);
}
void PDFCore::zoomCentered(double zoom) {
int page, wx, wy, sx, sy;
double cx, cy;
if (state->getZoom() == zoom) {
return;
}
startUpdate();
// get the center of the window, in user coords
cvtWindowToUser(state->getWinW() / 2, state->getWinH() / 2,
&page, &cx, &cy);
// set the new zoom level
state->setZoom(zoom);
// scroll to re-center
cvtUserToWindow(page, cx, cy, &wx, &wy);
sx = state->getScrollX() + wx - state->getWinW() / 2;
sy = state->getScrollY() + wy - state->getWinH() / 2;
state->setScrollPosition(page, sx, sy);
finishUpdate(gTrue, gFalse);
}
// Zoom so that the current page(s) fill the window width. Maintain
// the vertical center.
void PDFCore::zoomToCurrentWidth() {
int page0, page1, page, gap;
double w, w1, zoom;
startUpdate();
// get first and last pages
page0 = tileMap->getFirstPage();
page1 = tileMap->getLastPage();
// compute the desired width (in points)
gap = 0;
switch (state->getDisplayMode()) {
case displaySingle:
default:
w = tileMap->getPageBoxWidth(page0);
break;
case displayContinuous:
w = 0;
for (page = page0; page <= page1; ++page) {
w1 = tileMap->getPageBoxWidth(page);
if (w1 > w) {
w = w1;
}
}
break;
case displaySideBySideSingle:
w = tileMap->getPageBoxWidth(page0);
if (page1 != page0) {
w += tileMap->getPageBoxWidth(page1);
gap = tileMap->getSideBySidePageSpacing();
}
break;
case displaySideBySideContinuous:
w = 0;
for (page = page0; w <= page1; w += 2) {
w1 = tileMap->getPageBoxWidth(page);
if (page + 1 <= doc->getNumPages()) {
w1 += tileMap->getPageBoxWidth(page + 1);
}
if (w1 > w) {
w = w1;
}
}
gap = tileMap->getSideBySidePageSpacing();
break;
case displayHorizontalContinuous:
w = 0;
gap = 0;
for (page = page0; page <= page1; ++page) {
w += tileMap->getPageBoxWidth(page);
if (page != page0) {
gap += tileMap->getHorizContinuousPageSpacing();
}
}
break;
}
// set the new zoom level
zoom = 100.0 * (state->getWinW() - gap) / w;
state->setZoom(zoom);
// scroll so that the first page is at the left edge of the window
state->setScrollPosition(page0, tileMap->getPageLeftX(page0),
tileMap->getPageTopY(page0));
finishUpdate(gTrue, gFalse);
}
void PDFCore::setRotate(int rotate) {
int page;
if (state->getRotate() == rotate) {
return;
}
if (!doc || !doc->getNumPages()) {
state->setRotate(rotate);
return;
}
startUpdate();
page = tileMap->getFirstPage();
state->setRotate(rotate);
state->setScrollPosition(page, tileMap->getPageLeftX(page),
tileMap->getPageTopY(page));
finishUpdate(gTrue, gTrue);
}
void PDFCore::setDisplayMode(DisplayMode mode) {
int page;
if (state->getDisplayMode() == mode) {
return;
}
if (!doc || !doc->getNumPages()) {
state->setDisplayMode(mode);
return;
}
startUpdate();
page = tileMap->getFirstPage();
state->setDisplayMode(mode);
state->setScrollPosition(page, tileMap->getPageLeftX(page),
tileMap->getPageTopY(page));
finishUpdate(gTrue, gTrue);
}
void PDFCore::setOCGState(OptionalContentGroup *ocg, GBool ocgState) {
if (ocgState != ocg->getState()) {
ocg->setState(ocgState);
state->optionalContentChanged();
invalidateWholeWindow();
}
}
void PDFCore::setSelectMode(SelectMode mode) {
if (mode != selectMode) {
selectMode = mode;
clearSelection();
}
}
SplashColorPtr PDFCore::getSelectionColor() {
return state->getSelectColor();
}
void PDFCore::setSelectionColor(SplashColor color) {
int wx0, wy0, wx1, wy1;
state->setSelectColor(color);
if (state->hasSelection()) {
getSelectionBBox(&wx0, &wy0, &wx1, &wy1);
checkInvalidate(wx0, wy0, wx1 - wx0, wy1 - wy0);
}
}
void PDFCore::setSelection(int page, int x0, int y0, int x1, int y1) {
SelectRect *rect;
GBool moveLeft, moveTop, moveRight, moveBottom, needScroll;
double selectX0, selectY0, selectX1, selectY1;
int oldWx0, oldWy0, oldWx1, oldWy1, ix0, iy0, ix1, iy1;
int wx0, wy0, wx1, wy1, sx, sy, t;
// if selection rectangle is empty, clear the selection
if (x0 == x1 || y0 == y1) {
clearSelection();
return;
}
// x0 = left, x1 = right
// y0 = top, y1 = bottom
if (x0 > x1) {
t = x0; x0 = x1; x1 = t;
}
if (y0 > y1) {
t = y0; y0 = y1; y1 = t;
}
// convert new selection coords to user space and window space
tileMap->cvtDevToUser(page, x0, y0, &selectX0, &selectY0);
tileMap->cvtDevToUser(page, x1, y1, &selectX1, &selectY1);
cvtUserToWindow(page, selectX0, selectY0, &wx0, &wy0);
cvtUserToWindow(page, selectX1, selectY1, &wx1, &wy1);
if (wx0 > wx1) {
t = wx0; wx0 = wx1; wx1 = t;
}
if (wy0 > wy1) {
t = wy0; wy0 = wy1; wy1 = t;
}
// convert current selection coords to window space;
// check which edges moved
if (state->hasSelection()) {
rect = state->getSelectRect(0);
tileMap->cvtUserToWindow(rect->page, rect->x0, rect->y0, &oldWx0, &oldWy0);
tileMap->cvtUserToWindow(rect->page, rect->x1, rect->y1, &oldWx1, &oldWy1);
if (oldWx0 > oldWx1) {
t = oldWx0; oldWx0 = oldWx1; oldWx1 = t;
}
if (oldWy0 > oldWy1) {
t = oldWy0; oldWy0 = oldWy1; oldWy1 = t;
}
moveLeft = wx0 != oldWx0;
moveTop = wy0 != oldWy0;
moveRight = wx1 != oldWx1;
moveBottom = wy1 != oldWy1;
} else {
oldWx0 = wx0;
oldWy0 = wy0;
oldWx1 = wx1;
oldWy1 = wy1;
moveLeft = moveTop = moveRight = moveBottom = gTrue;
}
// set the new selection
state->setSelection(page, selectX0, selectY0, selectX1, selectY1);
// scroll if necessary
needScroll = gFalse;
sx = state->getScrollX();
sy = state->getScrollY();
if (moveLeft && wx0 < 0) {
sx += wx0;
needScroll = gTrue;
} else if (moveRight && wx1 >= state->getWinW()) {
sx += wx1 - state->getWinW();
needScroll = gTrue;
} else if (moveLeft && wx0 >= state->getWinW()) {
sx += wx0 - state->getWinW();
needScroll = gTrue;
} else if (moveRight && wx1 < 0) {
sx += wx1;
needScroll = gTrue;
}
if (moveTop && wy0 < 0) {
sy += wy0;
needScroll = gTrue;
} else if (moveBottom && wy1 >= state->getWinH()) {
sy += wy1 - state->getWinH();
needScroll = gTrue;
} else if (moveTop && wy0 >= state->getWinH()) {
sy += wy0 - state->getWinH();
needScroll = gTrue;
} else if (moveBottom && wy1 < 0) {
sy += wy1;
needScroll = gTrue;
}
if (needScroll) {
scrollTo(sx, sy);
} else {
ix0 = (wx0 < oldWx0) ? wx0 : oldWx0;
iy0 = (wy0 < oldWy0) ? wy0 : oldWy0;
ix1 = (wx1 > oldWx1) ? wx1 : oldWx1;
iy1 = (wy1 > oldWy1) ? wy1 : oldWy1;
checkInvalidate(ix0, iy0, ix1 - ix0, iy1 - iy0);
}
}
void PDFCore::setLinearSelection(int page, TextPosition *pos0,
TextPosition *pos1) {
TextPosition begin, end;
GList *rects;
GBool moveLeft, moveTop, moveRight, moveBottom, needScroll;
double x0, y0, x1, y1, x2, y2, x3, y3;
double ux0, uy0, ux1, uy1;
int oldWx0, oldWy0, oldWx1, oldWy1, ix0, iy0, ix1, iy1;
int wx0, wy0, wx1, wy1;
int sx, sy, colIdx;
// if selection rectangle is empty, clear the selection
if (*pos0 == *pos1) {
clearSelection();
return;
}
// swap into correct order
if (*pos0 < *pos1) {
begin = *pos0;
end = *pos1;
} else {
begin = *pos1;
end = *pos0;
}
// build the list of rectangles
//~ this doesn't handle RtL, vertical, or rotated text
loadText(page);
rects = new GList();
if (begin.colIdx == end.colIdx &&
begin.parIdx == end.parIdx &&
begin.lineIdx == end.lineIdx) {
// same line
text->convertPosToPointUpper(&begin, &x0, &y0);
text->convertPosToPointLower(&end, &x1, &y1);
cvtDevToUser(page, (int)(x0 + 0.5), (int)(y0 + 0.5), &ux0, &uy0);
cvtDevToUser(page, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux1, &uy1);
rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
} else if (begin.colIdx == end.colIdx) {
// same column
text->convertPosToPointUpper(&begin, &x0, &y0);
text->convertPosToPointRightEdge(&begin, &x1, &y1);
text->convertPosToPointLeftEdge(&end, &x2, &y2);
text->convertPosToPointLower(&end, &x3, &y3);
cvtDevToUser(page, (int)(x0 + 0.5), (int)(y0 + 0.5), &ux0, &uy0);
cvtDevToUser(page, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux1, &uy1);
rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
cvtDevToUser(page, (int)(x2 + 0.5), (int)(y1 + 0.5), &ux0, &uy0);
cvtDevToUser(page, (int)(x1 + 0.5), (int)(y2 + 0.5), &ux1, &uy1);
rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
cvtDevToUser(page, (int)(x2 + 0.5), (int)(y2 + 0.5), &ux0, &uy0);
cvtDevToUser(page, (int)(x3 + 0.5), (int)(y3 + 0.5), &ux1, &uy1);
rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
} else {
// different columns
text->convertPosToPointUpper(&begin, &x0, &y0);
text->convertPosToPointRightEdge(&begin, &x1, &y1);
text->getColumnLowerLeft(begin.colIdx, &x2, &y2);
cvtDevToUser(page, (int)(x0 + 0.5), (int)(y0 + 0.5), &ux0, &uy0);
cvtDevToUser(page, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux1, &uy1);
rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
cvtDevToUser(page, (int)(x2 + 0.5), (int)(y1 + 0.5), &ux0, &uy0);
cvtDevToUser(page, (int)(x1 + 0.5), (int)(y2 + 0.5), &ux1, &uy1);
rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
for (colIdx = begin.colIdx + 1; colIdx < end.colIdx; ++colIdx) {
text->getColumnLowerLeft(colIdx, &x0, &y0);
text->getColumnUpperRight(colIdx, &x1, &y1);
cvtDevToUser(page, (int)(x0 + 0.5), (int)(y1 + 0.5), &ux0, &uy0);
cvtDevToUser(page, (int)(x1 + 0.5), (int)(y0 + 0.5), &ux1, &uy1);
rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
}
text->getColumnUpperRight(end.colIdx, &x0, &y0);
text->convertPosToPointLeftEdge(&end, &x1, &y1);
text->convertPosToPointLower(&end, &x2, &y2);
cvtDevToUser(page, (int)(x1 + 0.5), (int)(y0 + 0.5), &ux0, &uy0);
cvtDevToUser(page, (int)(x0 + 0.5), (int)(y1 + 0.5), &ux1, &uy1);
rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
cvtDevToUser(page, (int)(x1 + 0.5), (int)(y1 + 0.5), &ux0, &uy0);
cvtDevToUser(page, (int)(x2 + 0.5), (int)(y2 + 0.5), &ux1, &uy1);
rects->append(new SelectRect(page, ux0, uy0, ux1, uy1));
}
// get window coord bboxes for old selection and new selection;
// check which edges moved
if (state->hasSelection()) {
getSelectionBBox(&oldWx0, &oldWy0, &oldWx1, &oldWy1);
getSelectRectListBBox(rects, &wx0, &wy0, &wx1, &wy1);
moveLeft = wx0 != oldWx0;
moveTop = wy0 != oldWy0;
moveRight = wx1 != oldWx1;
moveBottom = wy1 != oldWy1;
} else {
getSelectRectListBBox(rects, &wx0, &wy0, &wx1, &wy1);
oldWx0 = wx0;
oldWy0 = wy0;
oldWx1 = wx1;
oldWy1 = wy1;
moveLeft = moveTop = moveRight = moveBottom = gTrue;
}
// set the new selection
state->setSelection(rects);
// scroll if necessary
needScroll = gFalse;
sx = state->getScrollX();
sy = state->getScrollY();
if (moveLeft && wx0 < 0) {
sx += wx0;
needScroll = gTrue;
} else if (moveRight && wx1 >= state->getWinW()) {
sx += wx1 - state->getWinW();
needScroll = gTrue;
} else if (moveLeft && wx0 >= state->getWinW()) {
sx += wx0 - state->getWinW();
needScroll = gTrue;
} else if (moveRight && wx1 < 0) {
sx += wx1;
needScroll = gTrue;
}
if (moveTop && wy0 < 0) {
sy += wy0;
needScroll = gTrue;
} else if (moveBottom && wy1 >= state->getWinH()) {
sy += wy1 - state->getWinH();
needScroll = gTrue;
} else if (moveTop && wy0 >= state->getWinH()) {
sy += wy0 - state->getWinH();
needScroll = gTrue;
} else if (moveBottom && wy1 < 0) {
sy += wy1;
needScroll = gTrue;
}
if (needScroll) {
scrollTo(sx, sy);
} else {
ix0 = (wx0 < oldWx0) ? wx0 : oldWx0;
iy0 = (wy0 < oldWy0) ? wy0 : oldWy0;
ix1 = (wx1 > oldWx1) ? wx1 : oldWx1;
iy1 = (wy1 > oldWy1) ? wy1 : oldWy1;
checkInvalidate(ix0, iy0, ix1 - ix0, iy1 - iy0);
}
}
void PDFCore::clearSelection() {
int wx0, wy0, wx1, wy1;
if (state->hasSelection()) {
getSelectionBBox(&wx0, &wy0, &wx1, &wy1);
state->clearSelection();
checkInvalidate(wx0, wy0, wx1 - wx0, wy1 - wy0);
}
}
void PDFCore::startSelectionDrag(int pg, int x, int y) {
clearSelection();
if (selectMode == selectModeBlock) {
selectPage = pg;
selectStartX = x;
selectStartY = y;
} else { // selectModeLinear
loadText(pg);
if (text->findPointInside(x, y, &selectStartPos)) {
selectPage = pg;
} else {
selectPage = 0;
}
}
}
void PDFCore::moveSelectionDrag(int pg, int x, int y) {
TextPosition pos;
// don't allow selections to span multiple pages
// -- this also handles the case where a linear selection was started
// outside any text column, in which case selectPage = 0
if (pg != selectPage) {
return;
}
if (selectMode == selectModeBlock) {
setSelection(pg, selectStartX, selectStartY, x, y);
} else { // selectModeLinear
loadText(pg);
if (text->findPointNear(x, y, &pos)) {
setLinearSelection(pg, &selectStartPos, &pos);
}
}
}
void PDFCore::finishSelectionDrag() {
// nothing
}
void PDFCore::selectWord(int pg, int x, int y) {
TextPosition endPos;
loadText(pg);
if (text->findWordPoints(x, y, &selectStartPos, &endPos)) {
selectPage = pg;
setLinearSelection(pg, &selectStartPos, &endPos);
} else {
selectPage = 0;
}
}
void PDFCore::selectLine(int pg, int x, int y) {
TextPosition endPos;
loadText(pg);
if (text->findLinePoints(x, y, &selectStartPos, &endPos)) {
selectPage = pg;
setLinearSelection(pg, &selectStartPos, &endPos);
} else {
selectPage = 0;
}
}
GBool PDFCore::getSelection(int *pg, double *ulx, double *uly,
double *lrx, double *lry) {
SelectRect *rect;
double xMin, yMin, xMax, yMax;
int page, i;
if (!state->hasSelection()) {
return gFalse;
}
page = state->getSelectRect(0)->page;
xMin = yMin = xMax = yMax = 0;
for (i = 0; i < state->getNumSelectRects(); ++i) {
rect = state->getSelectRect(i);
if (rect->page != page) {
continue;
}
if (i == 0) {
xMin = xMax = rect->x0;
yMin = yMax = rect->y0;
} else {
if (rect->x0 < xMin) {
xMin = rect->x0;
} else if (rect->x0 > xMax) {
xMax = rect->x0;
}
if (rect->y0 < yMin) {
yMin = rect->y0;
} else if (rect->y0 > yMax) {
yMax = rect->y0;
}
}
if (rect->x1 < xMin) {
xMin = rect->x1;
} else if (rect->x1 > xMax) {
xMax = rect->x1;
}
if (rect->y1 < yMin) {
yMin = rect->y1;
} else if (rect->y1 > yMax) {
yMax = rect->y1;
}
}
*pg = page;
*ulx = xMin;
*uly = yMax;
*lrx = xMax;
*lry = yMin;
return gTrue;
}
GBool PDFCore::hasSelection() {
return state->hasSelection();
}
void PDFCore::setTextExtractionMode(TextOutputMode mode) {
if (textOutCtrl.mode != mode) {
textOutCtrl.mode = mode;
if (text) {
delete text;
text = NULL;
}
textPage = 0;
textDPI = 0;
textRotate = 0;
}
}
GBool PDFCore::getDiscardDiagonalText() {
return textOutCtrl.discardDiagonalText;
}
void PDFCore::setDiscardDiagonalText(GBool discard) {
if (textOutCtrl.discardDiagonalText != discard) {
textOutCtrl.discardDiagonalText = discard;
if (text) {
delete text;
text = NULL;
}
textPage = 0;
textDPI = 0;
textRotate = 0;
}
}
GString *PDFCore::extractText(int pg, double xMin, double yMin,
double xMax, double yMax) {
int x0, y0, x1, y1, t;
loadText(pg);
cvtUserToDev(pg, xMin, yMin, &x0, &y0);
cvtUserToDev(pg, xMax, yMax, &x1, &y1);
if (x0 > x1) {
t = x0; x0 = x1; x1 = t;
}
if (y0 > y1) {
t = y0; y0 = y1; y1 = t;
}
return text->getText(x0, y0, x1, y1);
}
GString *PDFCore::getSelectedText() {
SelectRect *rect;
GString *ret, *s;
int x0, y0, x1, y1, t, i;
if (!state->hasSelection()) {
return NULL;
}
ret = new GString();
for (i = 0; i < state->getNumSelectRects(); ++i) {
rect = state->getSelectRect(i);
loadText(rect->page);
cvtUserToDev(rect->page, rect->x0, rect->y0, &x0, &y0);
cvtUserToDev(rect->page, rect->x1, rect->y1, &x1, &y1);
if (x0 > x1) {
t = x0; x0 = x1; x1 = t;
}
if (y0 > y1) {
t = y0; y0 = y1; y1 = t;
}
s = text->getText(x0, y0, x1, y1, state->getNumSelectRects() > 1);
ret->append(s);
delete s;
}
return ret;
}
GBool PDFCore::find(char *s, GBool caseSensitive, GBool next, GBool backward,
GBool wholeWord, GBool onePageOnly) {
Unicode *u;
int len, i;
GBool ret;
// convert to Unicode
len = (int)strlen(s);
u = (Unicode *)gmallocn(len, sizeof(Unicode));
for (i = 0; i < len; ++i) {
u[i] = (Unicode)(s[i] & 0xff);
}
ret = findU(u, len, caseSensitive, next, backward, wholeWord, onePageOnly);
gfree(u);
return ret;
}
GBool PDFCore::findU(Unicode *u, int len, GBool caseSensitive,
GBool next, GBool backward, GBool wholeWord,
GBool onePageOnly) {
TextOutputDev *textOut;
SelectRect *rect;
double xMin, yMin, xMax, yMax;
int topPage, pg, x, y, x2, y2;
GBool startAtTop, startAtLast, stopAtLast;
// check for zero-length string
if (len == 0) {
return gFalse;
}
setBusyCursor(gTrue);
// search current page starting at previous result, current
// selection, or top/bottom of page
startAtTop = startAtLast = gFalse;
rect = NULL;
xMin = yMin = xMax = yMax = 0;
topPage = tileMap->getFirstPage();
pg = topPage;
if (next) {
if (textPage >= 1 && textPage <= doc->getNumPages()) {
startAtLast = gTrue;
pg = textPage;
}
} else if (state->hasSelection()) {
rect = state->getSelectRect(0);
pg = rect->page;
cvtUserToDev(pg, rect->x0, rect->y0, &x, &y);
cvtUserToDev(pg, rect->x1, rect->y1, &x2, &y2);
if (x2 < x) {
x = x2;
}
if (y2 < y) {
y = y2;
}
if (backward) {
xMin = x - 1;
yMin = y - 1;
} else {
xMin = x + 1;
yMin = y + 1;
}
} else {
startAtTop = gTrue;
}
loadText(pg);
if (text->findText(u, len, startAtTop, gTrue, startAtLast, gFalse,
caseSensitive, backward, wholeWord,
&xMin, &yMin, &xMax, &yMax)) {
goto found;
}
if (!onePageOnly) {
// search following/previous pages
textOut = new TextOutputDev(NULL, &textOutCtrl, gFalse);
if (!textOut->isOk()) {
delete textOut;
goto notFound;
}
for (pg = backward ? pg - 1 : pg + 1;
backward ? pg >= 1 : pg <= doc->getNumPages();
pg += backward ? -1 : 1) {
doc->displayPage(textOut, pg, 72, 72, 0, gFalse, gTrue, gFalse);
if (textOut->findText(u, len, gTrue, gTrue, gFalse, gFalse,
caseSensitive, backward, wholeWord,
&xMin, &yMin, &xMax, &yMax)) {
delete textOut;
goto foundPage;
}
}
// search previous/following pages
for (pg = backward ? doc->getNumPages() : 1;
backward ? pg > topPage : pg < topPage;
pg += backward ? -1 : 1) {
doc->displayPage(textOut, pg, 72, 72, 0, gFalse, gTrue, gFalse);
if (textOut->findText(u, len, gTrue, gTrue, gFalse, gFalse,
caseSensitive, backward, wholeWord,
&xMin, &yMin, &xMax, &yMax)) {
delete textOut;
goto foundPage;
}
}
delete textOut;
}
// search current page ending at previous result, current selection,
// or bottom/top of page
if (!startAtTop) {
xMin = yMin = xMax = yMax = 0;
if (next) {
stopAtLast = gTrue;
} else {
stopAtLast = gFalse;
cvtUserToDev(pg, rect->x1, rect->y1, &x, &y);
xMax = x;
yMax = y;
}
if (text->findText(u, len, gTrue, gFalse, gFalse, stopAtLast,
caseSensitive, backward, wholeWord,
&xMin, &yMin, &xMax, &yMax)) {
goto found;
}
}
// not found
notFound:
setBusyCursor(gFalse);
return gFalse;
// found on a different page
foundPage:
displayPage(pg, gTrue, gFalse);
loadText(pg);
if (!text->findText(u, len, gTrue, gTrue, gFalse, gFalse,
caseSensitive, backward, wholeWord,
&xMin, &yMin, &xMax, &yMax)) {
// this can happen if coalescing is bad
goto notFound;
}
// found: change the selection
found:
setSelection(pg, (int)floor(xMin), (int)floor(yMin),
(int)ceil(xMax), (int)ceil(yMax));
setBusyCursor(gFalse);
return gTrue;
}
GList *PDFCore::findAll(Unicode *u, int len, GBool caseSensitive,
GBool wholeWord, int firstPage, int lastPage) {
GList *results = new GList();
TextOutputDev *textOut = new TextOutputDev(NULL, &textOutCtrl, gFalse);
if (!textOut->isOk()) {
delete textOut;
return results;
}
for (int pg = firstPage; pg <= lastPage; ++pg) {
doc->displayPage(textOut, pg, 72, 72, 0, gFalse, gTrue, gFalse);
GBool first = gTrue;
while (1) {
double xMin, yMin, xMax, yMax;
if (!textOut->findText(u, len, first, gTrue, !first, gFalse,
caseSensitive, gFalse, wholeWord,
&xMin, &yMin, &xMax, &yMax)) {
break;
}
double uxMin, uyMin, uxMax, uyMax, t;
textOut->cvtDevToUser(xMin, yMin, &uxMin, &uyMin);
textOut->cvtDevToUser(xMax, yMax, &uxMax, &uyMax);
if (uxMin > uxMax) {
t = uxMin; uxMin = uxMax; uxMax = t;
}
if (uyMin > uyMax) {
t = uyMin; uyMin = uyMax; uyMax = t;
}
results->append(new FindResult(pg, uxMin, uyMin, uxMax, uyMax));
first = gFalse;
}
}
delete textOut;
return results;
}
GBool PDFCore::cvtWindowToUser(int xw, int yw,
int *pg, double *xu, double *yu) {
return tileMap->cvtWindowToUser(xw, yw, pg, xu, yu);
}
GBool PDFCore::cvtWindowToDev(int xw, int yw, int *pg, int *xd, int *yd) {
return tileMap->cvtWindowToDev(xw, yw, pg, xd, yd);
}
GBool PDFCore::cvtUserToWindow(int pg, double xu, double yu, int *xw, int *yw) {
return tileMap->cvtUserToWindow(pg, xu, yu, xw, yw);
}
void PDFCore::cvtUserToDev(int pg, double xu, double yu, int *xd, int *yd) {
tileMap->cvtUserToDev(pg, xu, yu, xd, yd);
}
GBool PDFCore::cvtDevToWindow(int pg, int xd, int yd, int *xw, int *yw) {
return tileMap->cvtDevToWindow(pg, xd, yd, xw, yw);
}
void PDFCore::cvtDevToUser(int pg, int xd, int yd, double *xu, double *yu) {
tileMap->cvtDevToUser(pg, xd, yd, xu, yu);
}
void PDFCore::getWindowPageRange(int x, int y, int w, int h,
int *firstPage, int *lastPage) {
tileMap->getWindowPageRange(x, y, w, h, firstPage, lastPage);
}
int PDFCore::getPageNum() {
if (!doc || !doc->getNumPages()) {
return 0;
}
return tileMap->getFirstPage();
}
int PDFCore::getMidPageNum() {
if (!doc || !doc->getNumPages()) {
return 0;
}
return tileMap->getMidPage();
}
double PDFCore::getZoom() {
return state->getZoom();
}
double PDFCore::getZoomDPI(int page) {
if (!doc) {
return 0;
}
return tileMap->getDPI(page);
}
int PDFCore::getRotate() {
return state->getRotate();
}
DisplayMode PDFCore::getDisplayMode() {
return state->getDisplayMode();
}
int PDFCore::getScrollX() {
return state->getScrollX();
}
int PDFCore::getScrollY() {
return state->getScrollY();
}
int PDFCore::getWindowWidth() {
return state->getWinW();
}
int PDFCore::getWindowHeight() {
return state->getWinH();
}
void PDFCore::setPaperColor(SplashColorPtr paperColor) {
state->setPaperColor(paperColor);
invalidateWholeWindow();
}
void PDFCore::setMatteColor(SplashColorPtr matteColor) {
state->setMatteColor(matteColor);
invalidateWholeWindow();
}
void PDFCore::setReverseVideo(GBool reverseVideo) {
SplashColorPtr oldPaperColor;
SplashColor newPaperColor;
int i;
if (reverseVideo != state->getReverseVideo()) {
state->setReverseVideo(reverseVideo);
oldPaperColor = state->getPaperColor();
for (i = 0; i < splashColorModeNComps[state->getColorMode()]; ++i) {
newPaperColor[i] = oldPaperColor[i] ^ 0xff;
}
state->setPaperColor(newPaperColor);
invalidateWholeWindow();
}
}
LinkAction *PDFCore::findLink(int pg, double x, double y) {
loadLinks(pg);
return links->find(x, y);
}
Annot *PDFCore::findAnnot(int pg, double x, double y) {
loadAnnots(pg);
return annots->find(x, y);
}
int PDFCore::findAnnotIdx(int pg, double x, double y) {
loadAnnots(pg);
return annots->findIdx(x, y);
}
Annot *PDFCore::getAnnot(int idx) {
if (!annots) {
return NULL;
}
if (idx < 0 || idx >= annots->getNumAnnots()) {
return NULL;
}
return annots->getAnnot(idx);
}
AcroFormField *PDFCore::findFormField(int pg, double x, double y) {
if (!doc->getCatalog()->getForm()) {
return NULL;
}
return doc->getCatalog()->getForm()->findField(pg, x, y);
}
int PDFCore::findFormFieldIdx(int pg, double x, double y) {
if (!doc->getCatalog()->getForm()) {
return -1;
}
return doc->getCatalog()->getForm()->findFieldIdx(pg, x, y);
}
AcroFormField *PDFCore::getFormField(int idx) {
if (!doc->getCatalog()->getForm()) {
return NULL;
}
if (idx < 0 || idx >= doc->getCatalog()->getForm()->getNumFields()) {
return NULL;
}
return doc->getCatalog()->getForm()->getField(idx);
}
GBool PDFCore::overText(int pg, double x, double y) {
loadText(pg);
return text->checkPointInside(x, y);
}
void PDFCore::forceRedraw() {
startUpdate();
state->forceRedraw();
finishUpdate(gFalse, gFalse);
}
void PDFCore::setTileDoneCbk(void (*cbk)(void *data), void *data) {
tileCache->setTileDoneCbk(cbk, data);
}
void PDFCore::setWindowSize(int winWidth, int winHeight) {
GBool doScroll;
int page, wx0, wy0, wx, wy, sx, sy;
double ux, uy;
startUpdate();
wx0 = wy0 = 0; // make gcc happy
doScroll = gFalse;
if (state->getZoom() < 0 && state->displayModeIsContinuous()) {
// save the user coordinates of the appropriate edge of the window
if (state->getDisplayMode() == displayHorizontalContinuous) {
wx0 = 0;
wy0 = state->getWinH() / 2;
} else {
wx0 = state->getWinW() / 2;
wy0 = 0;
}
if (!(doScroll = cvtWindowToUser(wx0, wy0, &page, &ux, &uy))) {
// tweak the save position if it happens to fall in a gutter
if (state->getDisplayMode() == displayContinuous) {
wy0 += tileMap->getContinuousPageSpacing();
} else if (state->getDisplayMode() == displaySideBySideContinuous) {
wx0 += tileMap->getSideBySidePageSpacing();
wy0 += tileMap->getContinuousPageSpacing();
} else { // state->getDisplayMode() == displayHorizontalContinuous
wx0 += tileMap->getHorizContinuousPageSpacing();
}
doScroll = cvtWindowToUser(wx0, wy0, &page, &ux, &uy);
}
}
state->setWindowSize(winWidth, winHeight);
if (doScroll) {
// restore the saved scroll position
cvtUserToWindow(page, ux, uy, &wx, &wy);
sx = state->getScrollX();
sy = state->getScrollY();
if (state->getDisplayMode() == displayHorizontalContinuous) {
sx += wx - wx0;
} else {
sy += wy - wy0;
}
state->setScrollPosition(page, sx, sy);
}
finishUpdate(gTrue, gFalse);
}
SplashBitmap *PDFCore::getWindowBitmap(GBool wholeWindow) {
GBool dummy;
return tileCompositor->getBitmap(wholeWindow ? &bitmapFinished : &dummy);
}
void PDFCore::tick() {
if (!bitmapFinished) {
invalidateWholeWindow();
}
}
// Clear cached info (links, text) that's tied to a PDFDoc.
void PDFCore::clearPage() {
if (links) {
delete links;
}
links = NULL;
linksPage = 0;
if (annots) {
delete annots;
}
annots = NULL;
annotsPage = 0;
if (text) {
delete text;
}
text = NULL;
textPage = 0;
textDPI = 0;
textRotate = 0;
}
// Load the links for <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());
}