]> www.fi.muni.cz Git - evince.git/blobdiff - pdf/xpdf/XPDFCore.cc
Fix several bugs with find
[evince.git] / pdf / xpdf / XPDFCore.cc
index 8376ce97d5b5ce85bf352f426cfdea45342fe399..3b9c21e47e819d31b48e84bc41c1023201576fce 100644 (file)
@@ -2,7 +2,7 @@
 //
 // XPDFCore.cc
 //
 //
 // XPDFCore.cc
 //
-// Copyright 2002 Glyph & Cog, LLC
+// Copyright 2002-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
 //
 //========================================================================
 
@@ -14,6 +14,7 @@
 
 #include <X11/keysym.h>
 #include <X11/cursorfont.h>
 
 #include <X11/keysym.h>
 #include <X11/cursorfont.h>
+#include <string.h>
 #include "gmem.h"
 #include "GString.h"
 #include "GList.h"
 #include "gmem.h"
 #include "GString.h"
 #include "GList.h"
@@ -24,7 +25,8 @@
 #include "GfxState.h"
 #include "PSOutputDev.h"
 #include "TextOutputDev.h"
 #include "GfxState.h"
 #include "PSOutputDev.h"
 #include "TextOutputDev.h"
-#include "XPixmapOutputDev.h"
+#include "SplashPattern.h"
+#include "XSplashOutputDev.h"
 #include "XPDFCore.h"
 
 // these macro defns conflict with xpdf's Object class
 #include "XPDFCore.h"
 
 // these macro defns conflict with xpdf's Object class
 
 //------------------------------------------------------------------------
 
 
 //------------------------------------------------------------------------
 
-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;
 GString *XPDFCore::currentSelection = NULL;
 XPDFCore *XPDFCore::currentSelectionOwner = NULL;
+Atom XPDFCore::targetsAtom;
 
 //------------------------------------------------------------------------
 // XPDFCore
 //------------------------------------------------------------------------
 
 XPDFCore::XPDFCore(Widget shellA, Widget parentWidgetA,
 
 //------------------------------------------------------------------------
 // XPDFCore
 //------------------------------------------------------------------------
 
 XPDFCore::XPDFCore(Widget shellA, Widget parentWidgetA,
-                  Gulong paperColorA, GBool fullScreenA, GBool reverseVideo,
-                  GBool installCmap, int rgbCubeSize) {
+                  SplashRGB8 paperColorA, GBool fullScreenA,
+                  GBool reverseVideo, GBool installCmap, int rgbCubeSize) {
   GString *initialZoom;
   GString *initialZoom;
+  SplashColor paperColor2;
   int i;
 
   shell = shellA;
   parentWidget = parentWidgetA;
   display = XtDisplay(parentWidget);
   screenNum = XScreenNumberOfScreen(XtScreen(parentWidget));
   int i;
 
   shell = shellA;
   parentWidget = parentWidgetA;
   display = XtDisplay(parentWidget);
   screenNum = XScreenNumberOfScreen(XtScreen(parentWidget));
+  targetsAtom = XInternAtom(display, "TARGETS", False);
 
   paperColor = paperColorA;
   fullScreen = fullScreenA;
 
   paperColor = paperColorA;
   fullScreen = fullScreenA;
@@ -144,12 +141,11 @@ XPDFCore::XPDFCore(Widget shellA, Widget parentWidgetA,
     zoom = zoomWidth;
   } else {
     zoom = atoi(initialZoom->getCString());
     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;
 
   scrollX = 0;
   scrollY = 0;
 
   scrollX = 0;
   scrollY = 0;
@@ -183,8 +179,9 @@ XPDFCore::XPDFCore(Widget shellA, Widget parentWidgetA,
   initWindow();
 
   // create the OutputDev
   initWindow();
 
   // create the OutputDev
-  out = new XPixmapOutputDev(display, screenNum, visual, colormap,
-                            reverseVideo, paperColor,
+  paperColor2.rgb8 = paperColor;
+  out = new XSplashOutputDev(display, screenNum, visual, colormap,
+                            reverseVideo, paperColor2,
                             installCmap, rgbCubeSize, gTrue,
                             &outputDevRedrawCbk, this);
   out->startDoc(NULL);
                             installCmap, rgbCubeSize, gTrue,
                             &outputDevRedrawCbk, this);
   out->startDoc(NULL);
@@ -209,25 +206,9 @@ XPDFCore::~XPDFCore() {
       delete history[i].fileName;
     }
   }
       delete history[i].fileName;
     }
   }
-  if (selectGC) {
-    XFreeGC(display, selectGC);
-    XFreeGC(display, highlightGC);
-  }
   if (drawAreaGC) {
     XFreeGC(display, drawAreaGC);
   }
   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);
   }
   if (scrolledWin) {
     XtDestroyWidget(scrolledWin);
   }
@@ -314,6 +295,74 @@ int XPDFCore::loadFile(GString *fileName, GString *ownerPassword,
   return errNone;
 }
 
   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;
 void XPDFCore::resizeToPage(int pg) {
   Dimension width, height;
   double width1, height1;
@@ -338,11 +387,11 @@ void XPDFCore::resizeToPage(int pg) {
       height1 = doc->getPageHeight(pg);
     }
     if (zoom == zoomPage || zoom == zoomWidth) {
       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 {
     } 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;
     }
     if (width > displayW - 100) {
       width = displayW - 100;
@@ -382,13 +431,12 @@ void XPDFCore::clear() {
   redrawRectangle(scrollX, scrollY, drawAreaWidth, drawAreaHeight);
 }
 
   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;
   XPDFHistory *h;
   GBool newZoom;
                           GBool scrollToTop, GBool addToHist) {
   double hDPI, vDPI;
   int rot;
   XPDFHistory *h;
   GBool newZoom;
-  XGCValues gcValues;
   time_t newModTime;
   int oldScrollX, oldScrollY;
 
   time_t newModTime;
   int oldScrollX, oldScrollY;
 
@@ -417,12 +465,6 @@ void XPDFCore::displayPage(int pageA, int zoomA, int rotateA,
     modTime = newModTime;
   }
 
     modTime = newModTime;
   }
 
-  // free the old GCs
-  if (selectGC) {
-    XFreeGC(display, selectGC);
-    XFreeGC(display, highlightGC);
-  }
-
   // new page number
   page = pageA;
 
   // new page number
   page = pageA;
 
@@ -466,10 +508,9 @@ void XPDFCore::displayPage(int pageA, int zoomA, int rotateA,
       dpi = (drawAreaWidth / doc->getPageWidth(page)) * 72;
     }
   } else {
       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, gTrue);
   oldScrollX = scrollX;
   oldScrollY = scrollY;
   updateScrollBars();
   oldScrollX = scrollX;
   oldScrollY = scrollY;
   updateScrollBars();
@@ -487,7 +528,11 @@ void XPDFCore::displayPage(int pageA, int zoomA, int rotateA,
     if (h->fileName) {
       delete h->fileName;
     }
     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;
     h->page = page;
     if (historyBLen < xpdfHistorySize) {
       ++historyBLen;
@@ -500,20 +545,11 @@ void XPDFCore::displayPage(int pageA, int zoomA, int rotateA,
     (*updateCbk)(updateCbkData, NULL, page, -1, "");
   }
 
     (*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);
 }
 
   // 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;
                           GBool addToHist) {
   Ref pageRef;
   int pg;
@@ -597,7 +633,7 @@ void XPDFCore::gotoPrevPage(int dec, GBool top, GBool bottom) {
   }
   if (page > 1) {
     if (!fullScreen && bottom) {
   }
   if (page > 1) {
     if (!fullScreen && bottom) {
-      scrollY = out->getPixmapHeight() - drawAreaHeight;
+      scrollY = out->getBitmapHeight() - drawAreaHeight;
       if (scrollY < 0) {
        scrollY = 0;
       }
       if (scrollY < 0) {
        scrollY = 0;
       }
@@ -622,7 +658,7 @@ void XPDFCore::goForward() {
   }
   --historyFLen;
   ++historyBLen;
   }
   --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;
     if (loadFile(history[historyCur].fileName) != errNone) {
       XBell(display, 0);
       return;
@@ -641,7 +677,7 @@ void XPDFCore::goBackward() {
   }
   --historyBLen;
   ++historyFLen;
   }
   --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;
     if (loadFile(history[historyCur].fileName) != errNone) {
       XBell(display, 0);
       return;
@@ -675,7 +711,7 @@ void XPDFCore::scrollPageUp() {
 }
 
 void XPDFCore::scrollPageDown() {
 }
 
 void XPDFCore::scrollPageDown() {
-  if (scrollY >= out->getPixmapHeight() - drawAreaHeight) {
+  if (scrollY >= out->getBitmapHeight() - drawAreaHeight) {
     gotoNextPage(1, gTrue);
   } else {
     scrollTo(scrollX, scrollY + drawAreaHeight);
     gotoNextPage(1, gTrue);
   } else {
     scrollTo(scrollX, scrollY + drawAreaHeight);
@@ -688,7 +724,7 @@ void XPDFCore::scrollTo(int x, int y) {
 
   needRedraw = gFalse;
 
 
   needRedraw = gFalse;
 
-  maxPos = out ? out->getPixmapWidth() : 1;
+  maxPos = out ? out->getBitmapWidth() : 1;
   if (maxPos < drawAreaWidth) {
     maxPos = drawAreaWidth;
   }
   if (maxPos < drawAreaWidth) {
     maxPos = drawAreaWidth;
   }
@@ -706,7 +742,7 @@ void XPDFCore::scrollTo(int x, int y) {
     needRedraw = gTrue;
   }
 
     needRedraw = gTrue;
   }
 
-  maxPos = out ? out->getPixmapHeight() : 1;
+  maxPos = out ? out->getBitmapHeight() : 1;
   if (maxPos < drawAreaHeight) {
     maxPos = drawAreaHeight;
   }
   if (maxPos < drawAreaHeight) {
     maxPos = drawAreaHeight;
   }
@@ -735,28 +771,26 @@ void XPDFCore::scrollTo(int x, int y) {
 
 void XPDFCore::setSelection(int newXMin, int newYMin,
                            int newXMax, int newYMax) {
 
 void XPDFCore::setSelection(int newXMin, int newYMin,
                            int newXMax, int newYMax) {
-  Pixmap pixmap;
   int x, y;
   GBool needRedraw, needScroll;
   GBool moveLeft, moveRight, moveTop, moveBottom;
   int x, y;
   GBool needRedraw, needScroll;
   GBool moveLeft, moveRight, moveTop, moveBottom;
-
-  pixmap = out->getPixmap();
+  SplashColor xorColor;
 
 
   // erase old selection on off-screen bitmap
   needRedraw = gFalse;
   if (selectXMin < selectXMax && selectYMin < selectYMax) {
 
 
   // erase old selection on off-screen bitmap
   needRedraw = gFalse;
   if (selectXMin < selectXMax && selectYMin < selectYMax) {
-    XFillRectangle(display, pixmap,
-                  selectGC, selectXMin, selectYMin,
-                  selectXMax - selectXMin, selectYMax - selectYMin);
+    xorColor.rgb8 = splashMakeRGB8(0xff, 0xff, 0xff);
+    out->xorRectangle(selectXMin, selectYMin, selectXMax, selectYMax,
+                     new SplashSolidColor(xorColor));
     needRedraw = gTrue;
   }
 
   // draw new selection on off-screen bitmap
   if (newXMin < newXMax && newYMin < newYMax) {
     needRedraw = gTrue;
   }
 
   // draw new selection on off-screen bitmap
   if (newXMin < newXMax && newYMin < newYMax) {
-    XFillRectangle(display, pixmap,
-                  selectGC, newXMin, newYMin,
-                  newXMax - newXMin, newYMax - newYMin);
+    xorColor.rgb8 = splashMakeRGB8(0xff, 0xff, 0xff);
+    out->xorRectangle(newXMin, newYMin, newXMax, newYMax,
+                     new SplashSolidColor(xorColor));
     needRedraw = gTrue;
   }
 
     needRedraw = gTrue;
   }
 
@@ -844,13 +878,13 @@ void XPDFCore::moveSelection(int mx, int my) {
   // clip mouse coords
   if (mx < 0) {
     mx = 0;
   // clip mouse coords
   if (mx < 0) {
     mx = 0;
-  } else if (mx >= out->getPixmapWidth()) {
-    mx = out->getPixmapWidth() - 1;
+  } else if (mx >= out->getBitmapWidth()) {
+    mx = out->getBitmapWidth() - 1;
   }
   if (my < 0) {
     my = 0;
   }
   if (my < 0) {
     my = 0;
-  } else if (my >= out->getPixmapHeight()) {
-    my = out->getPixmapHeight() - 1;
+  } else if (my >= out->getBitmapHeight()) {
+    my = out->getBitmapHeight() - 1;
   }
 
   // move appropriate edges of selection
   }
 
   // move appropriate edges of selection
@@ -924,15 +958,31 @@ Boolean XPDFCore::convertSelectionCbk(Widget widget, Atom *selection,
                                      Atom *target, Atom *type,
                                      XtPointer *value, unsigned long *length,
                                      int *format) {
                                      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) {
 }
 
 GBool XPDFCore::getSelection(int *xMin, int *yMin, int *xMax, int *yMax) {
@@ -961,12 +1011,12 @@ GString *XPDFCore::extractText(int pageNum,
   if (!doc->okToCopy()) {
     return NULL;
   }
   if (!doc->okToCopy()) {
     return NULL;
   }
-  textOut = new TextOutputDev(NULL, gFalse, gFalse);
+  textOut = new TextOutputDev(NULL, gTrue, gFalse, gFalse);
   if (!textOut->isOk()) {
     delete textOut;
     return NULL;
   }
   if (!textOut->isOk()) {
     delete textOut;
     return NULL;
   }
-  doc->displayPage(textOut, pageNum, dpi, rotate, gFalse);
+  doc->displayPage(textOut, pageNum, dpi, dpi, rotate, gTrue, gFalse);
   s = textOut->getText(xMin, yMin, xMax, yMax);
   delete textOut;
   return s;
   s = textOut->getText(xMin, yMin, xMax, yMax);
   delete textOut;
   return s;
@@ -976,7 +1026,7 @@ GString *XPDFCore::extractText(int pageNum,
 // hyperlinks
 //------------------------------------------------------------------------
 
 // hyperlinks
 //------------------------------------------------------------------------
 
-void XPDFCore::doLink(int mx, int my) {
+GBool XPDFCore::doLink(int mx, int my) {
   double x, y;
   LinkAction *action;
 
   double x, y;
   LinkAction *action;
 
@@ -984,7 +1034,9 @@ void XPDFCore::doLink(int mx, int my) {
   out->cvtDevToUser(mx, my, &x, &y);
   if ((action = doc->findLink(x, y))) {
     doAction(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::doAction(LinkAction *action) {
@@ -1192,26 +1244,9 @@ void XPDFCore::doAction(LinkAction *action) {
 void XPDFCore::runCommand(GString *cmdFmt, GString *arg) {
   GString *cmd;
   char *s;
 void XPDFCore::runCommand(GString *cmdFmt, GString *arg) {
   GString *cmd;
   char *s;
-  int i;
 
   if ((s = strstr(cmdFmt->getCString(), "%s"))) {
 
   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);
     cmd->insert(0, cmdFmt->getCString(),
                s - cmdFmt->getCString());
     cmd->append(s + 2);
@@ -1229,18 +1264,43 @@ void XPDFCore::runCommand(GString *cmdFmt, GString *arg) {
   delete cmd;
 }
 
   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
 //------------------------------------------------------------------------
 
 
 //------------------------------------------------------------------------
 // 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;
   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
   int len, i;
 
   // check for zero-length string
@@ -1262,27 +1322,24 @@ void XPDFCore::find(char *s) {
 #endif
 
   // search current page starting at current selection or top of page
 #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
     goto found;
   }
 
   // search following pages
-  textOut = new TextOutputDev(NULL, gFalse, gFalse);
+  textOut = new TextOutputDev(NULL, gTrue, gFalse, gFalse);
   if (!textOut->isOk()) {
     delete textOut;
     goto done;
   }
   for (pg = page+1; pg <= doc->getNumPages(); ++pg) {
   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, gTrue, gFalse);
+    if (textOut->findText(u, len, gTrue, gTrue, gFalse, gFalse,
                          &xMin1, &yMin1, &xMax1, &yMax1)) {
       goto foundPage;
     }
                          &xMin1, &yMin1, &xMax1, &yMax1)) {
       goto foundPage;
     }
@@ -1290,8 +1347,8 @@ void XPDFCore::find(char *s) {
 
   // search previous pages
   for (pg = 1; pg < page; ++pg) {
 
   // 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, gTrue, gFalse);
+    if (textOut->findText(u, len, gTrue, gTrue, gFalse, gFalse,
                          &xMin1, &yMin1, &xMax1, &yMax1)) {
       goto foundPage;
     }
                          &xMin1, &yMin1, &xMax1, &yMax1)) {
       goto foundPage;
     }
@@ -1299,10 +1356,12 @@ void XPDFCore::find(char *s) {
   delete textOut;
 
   // search current page ending at current selection
   delete textOut;
 
   // search current page ending at current selection
-  if (selectXMin < selectXMax && selectYMin < selectYMax) {
+  if (!startAtTop) {
+    xMin = yMin = 0;
     xMax = selectXMin;
     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;
     }
   }
       goto found;
     }
   }
@@ -1315,7 +1374,8 @@ void XPDFCore::find(char *s) {
  foundPage:
   delete textOut;
   displayPage(pg, zoom, rotate, gTrue, gTrue);
  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;
   }
     // this can happen if coalescing is bad
     goto done;
   }
@@ -1411,7 +1471,6 @@ void XPDFCore::initWindow() {
   XtManageChild(drawAreaFrame);
   n = 0;
   XtSetArg(args[n], XmNresizePolicy, XmRESIZE_ANY); ++n;
   XtManageChild(drawAreaFrame);
   n = 0;
   XtSetArg(args[n], XmNresizePolicy, XmRESIZE_ANY); ++n;
-  XtSetArg(args[n], XmNbackground, paperColor); ++n;
   XtSetArg(args[n], XmNwidth, 700); ++n;
   XtSetArg(args[n], XmNheight, 500); ++n;
   drawArea = XmCreateDrawingArea(drawAreaFrame, "drawArea", args, n);
   XtSetArg(args[n], XmNwidth, 700); ++n;
   XtSetArg(args[n], XmNheight, 500); ++n;
   drawArea = XmCreateDrawingArea(drawAreaFrame, "drawArea", args, n);
@@ -1430,8 +1489,6 @@ void XPDFCore::initWindow() {
 
   // can't create a GC until the window gets mapped
   drawAreaGC = NULL;
 
   // can't create a GC until the window gets mapped
   drawAreaGC = NULL;
-  selectGC = NULL;
-  highlightGC = NULL;
 }
 
 void XPDFCore::hScrollChangeCbk(Widget widget, XtPointer ptr,
 }
 
 void XPDFCore::hScrollChangeCbk(Widget widget, XtPointer ptr,
@@ -1471,6 +1528,7 @@ void XPDFCore::resizeCbk(Widget widget, XtPointer ptr, XtPointer callData) {
   Arg args[2];
   int n;
   Dimension w, h;
   Arg args[2];
   int n;
   Dimension w, h;
+  int oldScrollX, oldScrollY;
 
   n = 0;
   XtSetArg(args[n], XmNwidth, &w); ++n;
 
   n = 0;
   XtSetArg(args[n], XmNwidth, &w); ++n;
@@ -1483,7 +1541,13 @@ void XPDFCore::resizeCbk(Widget widget, XtPointer ptr, XtPointer callData) {
     core->displayPage(core->page, core->zoom, core->rotate,
                      gFalse, gFalse);
   } else {
     core->displayPage(core->page, core->zoom, core->rotate,
                      gFalse, gFalse);
   } else {
+    oldScrollX = core->scrollX;
+    oldScrollY = core->scrollY;
     core->updateScrollBars();
     core->updateScrollBars();
+    if (core->scrollX != oldScrollX || core->scrollY != oldScrollY) {
+      core->redrawRectangle(core->scrollX, core->scrollY,
+                           core->drawAreaWidth, core->drawAreaHeight);
+    }
   }
 }
 
   }
 }
 
@@ -1554,18 +1618,18 @@ void XPDFCore::inputCbk(Widget widget, XtPointer ptr, XtPointer callData) {
     } else if (data->event->xbutton.button == 5) { // mouse wheel down
       if (core->fullScreen ||
          core->scrollY >=
     } else if (data->event->xbutton.button == 5) { // mouse wheel down
       if (core->fullScreen ||
          core->scrollY >=
-           core->out->getPixmapHeight() - core->drawAreaHeight) {
+           core->out->getBitmapHeight() - core->drawAreaHeight) {
        core->gotoNextPage(1, gTrue);
       } else {
        core->scrollDown(1);
       }
        core->gotoNextPage(1, gTrue);
       } else {
        core->scrollDown(1);
       }
-    } else if (data->event->xbutton.button == 6) { // second mouse wheel right
+    } else if (data->event->xbutton.button == 6) { // second mouse wheel left
       if (!core->fullScreen) {
       if (!core->fullScreen) {
-       core->scrollRight(1);
+       core->scrollLeft(1);
       }
       }
-    } else if (data->event->xbutton.button == 7) { // second mouse wheel left
+    } else if (data->event->xbutton.button == 7) { // second mouse wheel right
       if (!core->fullScreen) {
       if (!core->fullScreen) {
-       core->scrollLeft(1);
+       core->scrollRight(1);
       }
     } else {
       if (*core->mouseCbk) {
       }
     } else {
       if (*core->mouseCbk) {
@@ -1689,8 +1753,8 @@ void XPDFCore::keyPress(char *s, KeySym key, Guint modifiers) {
     if (modifiers & ControlMask) {
       displayPage(doc->getNumPages(), zoom, rotate, gTrue, gTrue);
     } else if (!fullScreen) {
     if (modifiers & ControlMask) {
       displayPage(doc->getNumPages(), zoom, rotate, gTrue, gTrue);
     } else if (!fullScreen) {
-      scrollTo(out->getPixmapWidth() - drawAreaWidth,
-              out->getPixmapHeight() - drawAreaHeight);
+      scrollTo(out->getBitmapWidth() - drawAreaWidth,
+              out->getBitmapHeight() - drawAreaHeight);
     }
     return;
   case XK_Page_Up:
     }
     return;
   case XK_Page_Up:
@@ -1768,23 +1832,22 @@ void XPDFCore::redrawRectangle(int x, int y, int w, int h) {
   }
 
   // draw white background past the edges of the document
   }
 
   // draw white background past the edges of the document
-  if (x + w > out->getPixmapWidth()) {
+  if (x + w > out->getBitmapWidth()) {
     XFillRectangle(display, drawAreaWin, drawAreaGC,
     XFillRectangle(display, drawAreaWin, drawAreaGC,
-                  out->getPixmapWidth() - scrollX, y - scrollY,
-                  x + w - out->getPixmapWidth(), h);
-    w = out->getPixmapWidth() - x;
+                  out->getBitmapWidth() - scrollX, y - scrollY,
+                  x + w - out->getBitmapWidth(), h);
+    w = out->getBitmapWidth() - x;
   }
   }
-  if (y + h > out->getPixmapHeight()) {
+  if (y + h > out->getBitmapHeight()) {
     XFillRectangle(display, drawAreaWin, drawAreaGC,
     XFillRectangle(display, drawAreaWin, drawAreaGC,
-                  x - scrollX, out->getPixmapHeight() - scrollY,
-                  w, y + h - out->getPixmapHeight());
-    h = out->getPixmapHeight() - y;
+                  x - scrollX, out->getBitmapHeight() - scrollY,
+                  w, y + h - out->getBitmapHeight());
+    h = out->getBitmapHeight() - y;
   }
 
   }
 
-  // redraw (checking to see if pixmap has been allocated yet)
-  if (out->getPixmapWidth() > 0) {
-    XCopyArea(display, out->getPixmap(), drawAreaWin, drawAreaGC,
-             x, y, w, h, x - scrollX, y - scrollY);
+  // redraw
+  if (w >= 0 && h >= 0) {
+    out->redraw(x, y, drawAreaWin, drawAreaGC, x - scrollX, y - scrollY, w, h);
   }
 }
 
   }
 }
 
@@ -1793,7 +1856,7 @@ void XPDFCore::updateScrollBars() {
   int n;
   int maxPos;
 
   int n;
   int maxPos;
 
-  maxPos = out ? out->getPixmapWidth() : 1;
+  maxPos = out ? out->getBitmapWidth() : 1;
   if (maxPos < drawAreaWidth) {
     maxPos = drawAreaWidth;
   }
   if (maxPos < drawAreaWidth) {
     maxPos = drawAreaWidth;
   }
@@ -1808,7 +1871,7 @@ void XPDFCore::updateScrollBars() {
   XtSetArg(args[n], XmNpageIncrement, drawAreaWidth); ++n;
   XtSetValues(hScrollBar, args, n);
 
   XtSetArg(args[n], XmNpageIncrement, drawAreaWidth); ++n;
   XtSetValues(hScrollBar, args, n);
 
-  maxPos = out ? out->getPixmapHeight() : 1;
+  maxPos = out ? out->getBitmapHeight() : 1;
   if (maxPos < drawAreaHeight) {
     maxPos = drawAreaHeight;
   }
   if (maxPos < drawAreaHeight) {
     maxPos = drawAreaHeight;
   }
@@ -1856,7 +1919,7 @@ void XPDFCore::doErrorDialog(char *title, GString *msg) {
 
 GBool XPDFCore::doDialog(int type, GBool hasCancel,
                         char *title, GString *msg) {
 
 GBool XPDFCore::doDialog(int type, GBool hasCancel,
                         char *title, GString *msg) {
-  Widget dialog;
+  Widget dialog, scroll, text;
   XtAppContext appContext;
   Arg args[20];
   int n;
   XtAppContext appContext;
   Arg args[20];
   int n;
@@ -1868,11 +1931,31 @@ GBool XPDFCore::doDialog(int type, GBool hasCancel,
   XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n;
   s1 = XmStringCreateLocalized(title);
   XtSetArg(args[n], XmNdialogTitle, s1); ++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);
   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);
   XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
   XtAddCallback(dialog, XmNokCallback,
                &dialogOkCbk, (XtPointer)this);