//
// XPDFCore.cc
//
-// Copyright 2002 Glyph & Cog, LLC
+// Copyright 2002-2003 Glyph & Cog, LLC
//
//========================================================================
#include <X11/keysym.h>
#include <X11/cursorfont.h>
+#include <string.h>
#include "gmem.h"
#include "GString.h"
#include "GList.h"
//------------------------------------------------------------------------
-static int zoomDPI[maxZoom - minZoom + 1] = {
- 29, 35, 42, 50, 60,
- 72,
- 86, 104, 124, 149, 179
-};
-
-//------------------------------------------------------------------------
-
GString *XPDFCore::currentSelection = NULL;
XPDFCore *XPDFCore::currentSelectionOwner = NULL;
+Atom XPDFCore::targetsAtom;
//------------------------------------------------------------------------
// XPDFCore
parentWidget = parentWidgetA;
display = XtDisplay(parentWidget);
screenNum = XScreenNumberOfScreen(XtScreen(parentWidget));
+ targetsAtom = XInternAtom(display, "TARGETS", False);
paperColor = paperColorA;
fullScreen = fullScreenA;
zoom = zoomWidth;
} else {
zoom = atoi(initialZoom->getCString());
- if (zoom < minZoom) {
- zoom = minZoom;
- } else if (zoom > maxZoom) {
- zoom = maxZoom;
+ if (zoom <= 0) {
+ zoom = defZoom;
}
}
delete initialZoom;
if (drawAreaGC) {
XFreeGC(display, drawAreaGC);
}
- if (drawArea) {
- XtDestroyWidget(drawArea);
- }
- if (drawAreaFrame) {
- XtDestroyWidget(drawAreaFrame);
- }
- if (vScrollBar) {
- XtDestroyWidget(vScrollBar);
- }
- if (hScrollBar) {
- XtDestroyWidget(hScrollBar);
- }
if (scrolledWin) {
XtDestroyWidget(scrolledWin);
}
return errNone;
}
+int XPDFCore::loadFile(BaseStream *stream, GString *ownerPassword,
+ GString *userPassword) {
+ PDFDoc *newDoc;
+ GString *password;
+ GBool again;
+ int err;
+
+ // busy cursor
+ setCursor(busyCursor);
+
+ // open the PDF file
+ newDoc = new PDFDoc(stream, ownerPassword, userPassword);
+ if (!newDoc->isOk()) {
+ err = newDoc->getErrorCode();
+ delete newDoc;
+ if (err != errEncrypted || !reqPasswordCbk) {
+ setCursor(None);
+ return err;
+ }
+
+ // try requesting a password
+ again = ownerPassword != NULL || userPassword != NULL;
+ while (1) {
+ if (!(password = (*reqPasswordCbk)(reqPasswordCbkData, again))) {
+ setCursor(None);
+ return errEncrypted;
+ }
+ newDoc = new PDFDoc(stream, password, password);
+ if (newDoc->isOk()) {
+ break;
+ }
+ err = newDoc->getErrorCode();
+ delete newDoc;
+ if (err != errEncrypted) {
+ setCursor(None);
+ return err;
+ }
+ again = gTrue;
+ }
+ }
+
+ // replace old document
+ if (doc) {
+ delete doc;
+ }
+ doc = newDoc;
+ if (out) {
+ out->startDoc(doc->getXRef());
+ }
+
+ // nothing displayed yet
+ page = -99;
+
+ // save the modification time
+ modTime = getModTime(doc->getFileName()->getCString());
+
+ // update the parent window
+ if (updateCbk) {
+ (*updateCbk)(updateCbkData, doc->getFileName(), -1,
+ doc->getNumPages(), NULL);
+ }
+
+ // back to regular cursor
+ setCursor(None);
+
+ return errNone;
+}
+
void XPDFCore::resizeToPage(int pg) {
Dimension width, height;
double width1, height1;
height1 = doc->getPageHeight(pg);
}
if (zoom == zoomPage || zoom == zoomWidth) {
- width = (Dimension)((width1 * zoomDPI[defZoom - minZoom]) / 72 + 0.5);
- height = (Dimension)((height1 * zoomDPI[defZoom - minZoom]) / 72 + 0.5);
+ width = (Dimension)(width1 * 0.01 * defZoom + 0.5);
+ height = (Dimension)(height1 * 0.01 * defZoom + 0.5);
} else {
- width = (Dimension)((width1 * zoomDPI[zoom - minZoom]) / 72 + 0.5);
- height = (Dimension)((height1 * zoomDPI[zoom - minZoom]) / 72 + 0.5);
+ width = (Dimension)(width1 * 0.01 * zoom + 0.5);
+ height = (Dimension)(height1 * 0.01 * zoom + 0.5);
}
if (width > displayW - 100) {
width = displayW - 100;
redrawRectangle(scrollX, scrollY, drawAreaWidth, drawAreaHeight);
}
-void XPDFCore::displayPage(int pageA, int zoomA, int rotateA,
+void XPDFCore::displayPage(int pageA, double zoomA, int rotateA,
GBool scrollToTop, GBool addToHist) {
double hDPI, vDPI;
int rot;
dpi = (drawAreaWidth / doc->getPageWidth(page)) * 72;
}
} else {
- dpi = zoomDPI[zoom - minZoom];
+ dpi = 0.01 * zoom * 72;
}
out->setWindow(XtWindow(drawArea));
- doc->displayPage(out, page, dpi, rotate, gTrue);
+ doc->displayPage(out, page, dpi, dpi, rotate, gTrue);
oldScrollX = scrollX;
oldScrollY = scrollY;
updateScrollBars();
redrawRectangle(scrollX, scrollY, drawAreaWidth, drawAreaHeight);
}
+ // allocate new GCs
+ gcValues.foreground = BlackPixel(display, screenNum) ^
+ WhitePixel(display, screenNum);
+ gcValues.function = GXxor;
+ selectGC = XCreateGC(display, out->getPixmap(),
+ GCForeground | GCFunction, &gcValues);
+ highlightGC = XCreateGC(display, out->getPixmap(),
+ GCForeground | GCFunction, &gcValues);
+
// add to history
if (addToHist) {
if (h->fileName) {
delete h->fileName;
}
- h->fileName = doc->getFileName()->copy();
+ if (doc->getFileName()) {
+ h->fileName = doc->getFileName()->copy();
+ } else {
+ h->fileName = NULL;
+ }
h->page = page;
if (historyBLen < xpdfHistorySize) {
++historyBLen;
(*updateCbk)(updateCbkData, NULL, page, -1, "");
}
- // allocate new GCs
- gcValues.foreground = BlackPixel(display, screenNum) ^
- WhitePixel(display, screenNum);
- gcValues.function = GXxor;
- selectGC = XCreateGC(display, out->getPixmap(),
- GCForeground | GCFunction, &gcValues);
- highlightGC = XCreateGC(display, out->getPixmap(),
- GCForeground | GCFunction, &gcValues);
-
// back to regular cursor
setCursor(None);
}
-void XPDFCore::displayDest(LinkDest *dest, int zoomA, int rotateA,
+void XPDFCore::displayDest(LinkDest *dest, double zoomA, int rotateA,
GBool addToHist) {
Ref pageRef;
int pg;
}
--historyFLen;
++historyBLen;
- if (history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
+ if (!doc || history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
if (loadFile(history[historyCur].fileName) != errNone) {
XBell(display, 0);
return;
}
--historyBLen;
++historyFLen;
- if (history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
+ if (!doc || history[historyCur].fileName->cmp(doc->getFileName()) != 0) {
if (loadFile(history[historyCur].fileName) != errNone) {
XBell(display, 0);
return;
Atom *target, Atom *type,
XtPointer *value, unsigned long *length,
int *format) {
- if (*target != XA_STRING) {
- return False;
- }
- //~ for multithreading: need a mutex here
- *value = XtNewString(currentSelection->getCString());
- *length = currentSelection->getLength();
- *type = XA_STRING;
- *format = 8; // 8-bit elements
- return True;
+ Atom *array;
+
+ // send back a list of supported conversion targets
+ if (*target == targetsAtom) {
+ if (!(array = (Atom *)XtMalloc(sizeof(Atom)))) {
+ return False;
+ }
+ array[0] = XA_STRING;
+ *value = (XtPointer)array;
+ *type = XA_ATOM;
+ *format = 32;
+ *length = 1;
+ return True;
+
+ // send the selected text
+ } else if (*target == XA_STRING) {
+ //~ for multithreading: need a mutex here
+ *value = XtNewString(currentSelection->getCString());
+ *length = currentSelection->getLength();
+ *type = XA_STRING;
+ *format = 8; // 8-bit elements
+ return True;
+ }
+
+ return False;
}
GBool XPDFCore::getSelection(int *xMin, int *yMin, int *xMax, int *yMax) {
if (!doc->okToCopy()) {
return NULL;
}
- textOut = new TextOutputDev(NULL, gFalse, gFalse, gFalse);
+ textOut = new TextOutputDev(NULL, gTrue, gFalse, gFalse);
if (!textOut->isOk()) {
delete textOut;
return NULL;
}
- doc->displayPage(textOut, pageNum, dpi, rotate, gFalse);
+ doc->displayPage(textOut, pageNum, dpi, dpi, rotate, gFalse);
s = textOut->getText(xMin, yMin, xMax, yMax);
delete textOut;
return s;
// hyperlinks
//------------------------------------------------------------------------
-void XPDFCore::doLink(int mx, int my) {
+GBool XPDFCore::doLink(int mx, int my) {
double x, y;
LinkAction *action;
out->cvtDevToUser(mx, my, &x, &y);
if ((action = doc->findLink(x, y))) {
doAction(action);
+ return gTrue;
}
+ return gFalse;
}
void XPDFCore::doAction(LinkAction *action) {
void XPDFCore::runCommand(GString *cmdFmt, GString *arg) {
GString *cmd;
char *s;
- int i;
if ((s = strstr(cmdFmt->getCString(), "%s"))) {
- cmd = arg->copy();
- // filter out any quote marks (' or ") to avoid a potential
- // security hole
- i = 0;
- while (i < cmd->getLength()) {
- if (cmd->getChar(i) == '"') {
- cmd->del(i);
- cmd->insert(i, "%22");
- i += 3;
- } else if (cmd->getChar(i) == '\'') {
- cmd->del(i);
- cmd->insert(i, "%27");
- i += 3;
- } else {
- ++i;
- }
- }
+ cmd = mungeURL(arg);
cmd->insert(0, cmdFmt->getCString(),
s - cmdFmt->getCString());
cmd->append(s + 2);
delete cmd;
}
+// Escape any characters in a URL which might cause problems when
+// calling system().
+GString *XPDFCore::mungeURL(GString *url) {
+ static char *allowed = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789"
+ "-_.~/?:@&=+,#%";
+ GString *newURL;
+ char c;
+ char buf[4];
+ int i;
+
+ newURL = new GString();
+ for (i = 0; i < url->getLength(); ++i) {
+ c = url->getChar(i);
+ if (strchr(allowed, c)) {
+ newURL->append(c);
+ } else {
+ sprintf(buf, "%%%02x", c & 0xff);
+ newURL->append(buf);
+ }
+ }
+ return newURL;
+}
+
//------------------------------------------------------------------------
// find
//------------------------------------------------------------------------
-void XPDFCore::find(char *s) {
+void XPDFCore::find(char *s, GBool next) {
Unicode *u;
TextOutputDev *textOut;
int xMin, yMin, xMax, yMax;
double xMin1, yMin1, xMax1, yMax1;
int pg;
- GBool top;
+ GBool startAtTop;
int len, i;
// check for zero-length string
#endif
// search current page starting at current selection or top of page
- xMin = yMin = xMax = yMax = 0;
- if (selectXMin < selectXMax && selectYMin < selectYMax) {
- xMin = selectXMax;
- yMin = (selectYMin + selectYMax) / 2;
- top = gFalse;
- } else {
- top = gTrue;
- }
- if (out->findText(u, len, top, gTrue, &xMin, &yMin, &xMax, &yMax)) {
+ startAtTop = !next && !(selectXMin < selectXMax && selectYMin < selectYMax);
+ xMin = selectXMin + 1;
+ yMin = selectYMin + 1;
+ xMax = yMax = 0;
+ if (out->findText(u, len, startAtTop, gTrue, next, gFalse,
+ &xMin, &yMin, &xMax, &yMax)) {
goto found;
}
// search following pages
- textOut = new TextOutputDev(NULL, gFalse, gFalse, gFalse);
+ textOut = new TextOutputDev(NULL, gTrue, gFalse, gFalse);
if (!textOut->isOk()) {
delete textOut;
goto done;
}
for (pg = page+1; pg <= doc->getNumPages(); ++pg) {
- doc->displayPage(textOut, pg, 72, 0, gFalse);
- if (textOut->findText(u, len, gTrue, gTrue,
+ doc->displayPage(textOut, pg, 72, 72, 0, gFalse);
+ if (textOut->findText(u, len, gTrue, gTrue, gFalse, gFalse,
&xMin1, &yMin1, &xMax1, &yMax1)) {
goto foundPage;
}
// search previous pages
for (pg = 1; pg < page; ++pg) {
- doc->displayPage(textOut, pg, 72, 0, gFalse);
- if (textOut->findText(u, len, gTrue, gTrue,
+ doc->displayPage(textOut, pg, 72, 72, 0, gFalse);
+ if (textOut->findText(u, len, gTrue, gTrue, gFalse, gFalse,
&xMin1, &yMin1, &xMax1, &yMax1)) {
goto foundPage;
}
delete textOut;
// search current page ending at current selection
- if (selectXMin < selectXMax && selectYMin < selectYMax) {
+ if (!startAtTop) {
+ xMin = yMin = 0;
xMax = selectXMin;
- yMax = (selectYMin + selectYMax) / 2;
- if (out->findText(u, len, gTrue, gFalse, &xMin, &yMin, &xMax, &yMax)) {
+ yMax = selectYMin;
+ if (out->findText(u, len, gTrue, gFalse, gFalse, next,
+ &xMin, &yMin, &xMax, &yMax)) {
goto found;
}
}
foundPage:
delete textOut;
displayPage(pg, zoom, rotate, gTrue, gTrue);
- if (!out->findText(u, len, gTrue, gTrue, &xMin, &yMin, &xMax, &yMax)) {
+ if (!out->findText(u, len, gTrue, gTrue, gFalse, gFalse,
+ &xMin, &yMin, &xMax, &yMax)) {
// this can happen if coalescing is bad
goto done;
}
Arg args[2];
int n;
Dimension w, h;
+ int oldScrollX, oldScrollY;
n = 0;
XtSetArg(args[n], XmNwidth, &w); ++n;
core->displayPage(core->page, core->zoom, core->rotate,
gFalse, gFalse);
} else {
+ oldScrollX = core->scrollX;
+ oldScrollY = core->scrollY;
core->updateScrollBars();
+ if (core->scrollX != oldScrollX || core->scrollY != oldScrollY) {
+ core->redrawRectangle(core->scrollX, core->scrollY,
+ core->drawAreaWidth, core->drawAreaHeight);
+ }
}
}
GBool XPDFCore::doDialog(int type, GBool hasCancel,
char *title, GString *msg) {
- Widget dialog;
+ Widget dialog, scroll, text;
XtAppContext appContext;
Arg args[20];
int n;
XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n;
s1 = XmStringCreateLocalized(title);
XtSetArg(args[n], XmNdialogTitle, s1); ++n;
- s2 = XmStringCreateLocalized(msg->getCString());
- XtSetArg(args[n], XmNmessageString, s2); ++n;
+ s2 = NULL; // make gcc happy
+ if (msg->getLength() <= 80) {
+ s2 = XmStringCreateLocalized(msg->getCString());
+ XtSetArg(args[n], XmNmessageString, s2); ++n;
+ }
dialog = XmCreateMessageDialog(drawArea, "questionDialog", args, n);
XmStringFree(s1);
- XmStringFree(s2);
+ if (msg->getLength() <= 80) {
+ XmStringFree(s2);
+ } else {
+ n = 0;
+ XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC); ++n;
+ if (drawAreaWidth > 300) {
+ XtSetArg(args[n], XmNwidth, drawAreaWidth - 100); ++n;
+ }
+ scroll = XmCreateScrolledWindow(dialog, "scroll", args, n);
+ XtManageChild(scroll);
+ n = 0;
+ XtSetArg(args[n], XmNeditable, False); ++n;
+ XtSetArg(args[n], XmNeditMode, XmMULTI_LINE_EDIT); ++n;
+ XtSetArg(args[n], XmNvalue, msg->getCString()); ++n;
+ XtSetArg(args[n], XmNshadowThickness, 0); ++n;
+ text = XmCreateText(scroll, "text", args, n);
+ XtManageChild(text);
+ }
XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
XtAddCallback(dialog, XmNokCallback,
&dialogOkCbk, (XtPointer)this);