1 //========================================================================
2 //
3 // XPDFCore.cc
4 //
5 // Copyright 2002-2003 Glyph & Cog, LLC
6 //
7 //========================================================================
8 
9 #include <aconf.h>
10 
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
13 #endif
14 
15 #include <X11/keysym.h>
16 #include <X11/cursorfont.h>
17 #include <string.h>
18 #include "gmem.h"
19 #include "GString.h"
20 #include "GList.h"
21 #include "Error.h"
22 #include "GlobalParams.h"
23 #include "PDFDoc.h"
24 #include "Link.h"
25 #include "ErrorCodes.h"
26 #include "GfxState.h"
27 #include "CoreOutputDev.h"
28 #include "PSOutputDev.h"
29 #include "TextOutputDev.h"
30 #include "SplashBitmap.h"
31 #include "SplashPattern.h"
32 #include "XPDFApp.h"
33 #include "XPDFCore.h"
34 
35 // these macro defns conflict with xpdf's Object class
36 #ifdef LESSTIF_VERSION
37 #undef XtDisplay
38 #undef XtScreen
39 #undef XtWindow
40 #undef XtParent
41 #undef XtIsRealized
42 #endif
43 
44 //------------------------------------------------------------------------
45 
46 // Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result.
div255(int x)47 static inline Guchar div255(int x) {
48   return (Guchar)((x + (x >> 8) + 0x80) >> 8);
49 }
50 
51 //------------------------------------------------------------------------
52 
53 GString *XPDFCore::currentSelection = NULL;
54 XPDFCore *XPDFCore::currentSelectionOwner = NULL;
55 Atom XPDFCore::targetsAtom;
56 
57 //------------------------------------------------------------------------
58 // XPDFCoreTile
59 //------------------------------------------------------------------------
60 
61 class XPDFCoreTile: public PDFCoreTile {
62 public:
63   XPDFCoreTile(int xDestA, int yDestA);
64   virtual ~XPDFCoreTile();
65   XImage *image;
66 };
67 
XPDFCoreTile(int xDestA,int yDestA)68 XPDFCoreTile::XPDFCoreTile(int xDestA, int yDestA):
69   PDFCoreTile(xDestA, yDestA)
70 {
71   image = NULL;
72 }
73 
~XPDFCoreTile()74 XPDFCoreTile::~XPDFCoreTile() {
75   if (image) {
76     gfree(image->data);
77     image->data = NULL;
78     XDestroyImage(image);
79   }
80 }
81 
82 //------------------------------------------------------------------------
83 // XPDFCore
84 //------------------------------------------------------------------------
85 
XPDFCore(Widget shellA,Widget parentWidgetA,SplashColorPtr paperColorA,Gulong paperPixelA,Gulong mattePixelA,GBool fullScreenA,GBool reverseVideoA,GBool installCmap,int rgbCubeSizeA)86 XPDFCore::XPDFCore(Widget shellA, Widget parentWidgetA,
87 		   SplashColorPtr paperColorA, Gulong paperPixelA,
88 		   Gulong mattePixelA, GBool fullScreenA, GBool reverseVideoA,
89 		   GBool installCmap, int rgbCubeSizeA):
90   PDFCore(splashModeRGB8, 4, reverseVideoA, paperColorA, !fullScreenA)
91 {
92   GString *initialZoom;
93 
94   shell = shellA;
95   parentWidget = parentWidgetA;
96   display = XtDisplay(parentWidget);
97   screenNum = XScreenNumberOfScreen(XtScreen(parentWidget));
98   targetsAtom = XInternAtom(display, "TARGETS", False);
99 
100   paperPixel = paperPixelA;
101   mattePixel = mattePixelA;
102   fullScreen = fullScreenA;
103 
104   setupX(installCmap, rgbCubeSizeA);
105 
106   scrolledWin = NULL;
107   hScrollBar = NULL;
108   vScrollBar = NULL;
109   drawAreaFrame = NULL;
110   drawArea = NULL;
111 
112   // get the initial zoom value
113   if (fullScreen) {
114     zoom = zoomPage;
115   } else {
116     initialZoom = globalParams->getInitialZoom();
117     if (!initialZoom->cmp("page")) {
118       zoom = zoomPage;
119     } else if (!initialZoom->cmp("width")) {
120       zoom = zoomWidth;
121     } else {
122       zoom = atoi(initialZoom->getCString());
123       if (zoom <= 0) {
124 	zoom = defZoom;
125       }
126     }
127     delete initialZoom;
128   }
129 
130   linkAction = NULL;
131 
132   panning = gFalse;
133 
134   updateCbk = NULL;
135   actionCbk = NULL;
136   keyPressCbk = NULL;
137   mouseCbk = NULL;
138 
139   // optional features default to on
140   hyperlinksEnabled = gTrue;
141   selectEnabled = gTrue;
142 
143   // do X-specific initialization and create the widgets
144   initWindow();
145   initPasswordDialog();
146 }
147 
~XPDFCore()148 XPDFCore::~XPDFCore() {
149   if (currentSelectionOwner == this && currentSelection) {
150     delete currentSelection;
151     currentSelection = NULL;
152     currentSelectionOwner = NULL;
153   }
154   if (drawAreaGC) {
155     XFreeGC(display, drawAreaGC);
156   }
157   if (scrolledWin) {
158     XtDestroyWidget(scrolledWin);
159   }
160   if (busyCursor) {
161     XFreeCursor(display, busyCursor);
162   }
163   if (linkCursor) {
164     XFreeCursor(display, linkCursor);
165   }
166   if (selectCursor) {
167     XFreeCursor(display, selectCursor);
168   }
169 }
170 
171 //------------------------------------------------------------------------
172 // loadFile / displayPage / displayDest
173 //------------------------------------------------------------------------
174 
loadFile(GString * fileName,GString * ownerPassword,GString * userPassword)175 int XPDFCore::loadFile(GString *fileName, GString *ownerPassword,
176 		       GString *userPassword) {
177   int err;
178 
179   err = PDFCore::loadFile(fileName, ownerPassword, userPassword);
180   if (err == errNone) {
181     // save the modification time
182     modTime = getModTime(doc->getFileName()->getCString());
183 
184     // update the parent window
185     if (updateCbk) {
186       (*updateCbk)(updateCbkData, doc->getFileName(), -1,
187 		   doc->getNumPages(), NULL);
188     }
189   }
190   return err;
191 }
192 
loadFile(BaseStream * stream,GString * ownerPassword,GString * userPassword)193 int XPDFCore::loadFile(BaseStream *stream, GString *ownerPassword,
194 		       GString *userPassword) {
195   int err;
196 
197   err = PDFCore::loadFile(stream, ownerPassword, userPassword);
198   if (err == errNone) {
199     // no file
200     modTime = 0;
201 
202     // update the parent window
203     if (updateCbk) {
204       (*updateCbk)(updateCbkData, doc->getFileName(), -1,
205 		   doc->getNumPages(), NULL);
206     }
207   }
208   return err;
209 }
210 
loadDoc(PDFDoc * docA)211 void XPDFCore::loadDoc(PDFDoc *docA) {
212   PDFCore::loadDoc(docA);
213 
214   // save the modification time
215   if (doc->getFileName()) {
216     modTime = getModTime(doc->getFileName()->getCString());
217   }
218 
219   // update the parent window
220   if (updateCbk) {
221     (*updateCbk)(updateCbkData, doc->getFileName(), -1,
222 		 doc->getNumPages(), NULL);
223   }
224 }
225 
resizeToPage(int pg)226 void XPDFCore::resizeToPage(int pg) {
227   Dimension width, height;
228   double width1, height1;
229   Dimension topW, topH, topBorder, daW, daH;
230   Dimension displayW, displayH;
231 
232   displayW = DisplayWidth(display, screenNum);
233   displayH = DisplayHeight(display, screenNum);
234   if (fullScreen) {
235     width = displayW;
236     height = displayH;
237   } else {
238     if (!doc || pg <= 0 || pg > doc->getNumPages()) {
239       width1 = 612;
240       height1 = 792;
241     } else if (doc->getPageRotate(pg) == 90 ||
242 	       doc->getPageRotate(pg) == 270) {
243       width1 = doc->getPageCropHeight(pg);
244       height1 = doc->getPageCropWidth(pg);
245     } else {
246       width1 = doc->getPageCropWidth(pg);
247       height1 = doc->getPageCropHeight(pg);
248     }
249     if (zoom == zoomPage || zoom == zoomWidth) {
250       width = (Dimension)(width1 * 0.01 * defZoom + 0.5);
251       height = (Dimension)(height1 * 0.01 * defZoom + 0.5);
252     } else {
253       width = (Dimension)(width1 * 0.01 * zoom + 0.5);
254       height = (Dimension)(height1 * 0.01 * zoom + 0.5);
255     }
256     if (continuousMode) {
257       height += continuousModePageSpacing;
258     }
259     if (width > displayW - 100) {
260       width = displayW - 100;
261     }
262     if (height > displayH - 100) {
263       height = displayH - 100;
264     }
265   }
266 
267   if (XtIsRealized(shell)) {
268     XtVaGetValues(shell, XmNwidth, &topW, XmNheight, &topH,
269 		  XmNborderWidth, &topBorder, NULL);
270     XtVaGetValues(drawArea, XmNwidth, &daW, XmNheight, &daH, NULL);
271     XtVaSetValues(shell, XmNwidth, width + (topW - daW),
272 		  XmNheight, height + (topH - daH), NULL);
273   } else {
274     XtVaSetValues(drawArea, XmNwidth, width, XmNheight, height, NULL);
275   }
276 }
277 
update(int topPageA,int scrollXA,int scrollYA,double zoomA,int rotateA,GBool force,GBool addToHist,GBool adjustScrollX)278 void XPDFCore::update(int topPageA, int scrollXA, int scrollYA,
279 		      double zoomA, int rotateA, GBool force,
280 		      GBool addToHist, GBool adjustScrollX) {
281   int oldPage;
282 
283   oldPage = topPage;
284   PDFCore::update(topPageA, scrollXA, scrollYA, zoomA, rotateA,
285 		  force, addToHist, adjustScrollX);
286   linkAction = NULL;
287   if (doc && topPage != oldPage) {
288     if (updateCbk) {
289       (*updateCbk)(updateCbkData, NULL, topPage, -1, "");
290     }
291   }
292 }
293 
checkForNewFile()294 GBool XPDFCore::checkForNewFile() {
295   time_t newModTime;
296 
297   if (doc->getFileName()) {
298     newModTime = getModTime(doc->getFileName()->getCString());
299     if (newModTime != modTime) {
300       modTime = newModTime;
301       return gTrue;
302     }
303   }
304   return gFalse;
305 }
306 
307 //------------------------------------------------------------------------
308 // page/position changes
309 //------------------------------------------------------------------------
310 
gotoNextPage(int inc,GBool top)311 GBool XPDFCore::gotoNextPage(int inc, GBool top) {
312   if (!PDFCore::gotoNextPage(inc, top)) {
313     XBell(display, 0);
314     return gFalse;
315   }
316   return gTrue;
317 }
318 
gotoPrevPage(int dec,GBool top,GBool bottom)319 GBool XPDFCore::gotoPrevPage(int dec, GBool top, GBool bottom) {
320   if (!PDFCore::gotoPrevPage(dec, top, bottom)) {
321     XBell(display, 0);
322     return gFalse;
323   }
324   return gTrue;
325 }
326 
goForward()327 GBool XPDFCore::goForward() {
328   if (!PDFCore::goForward()) {
329     XBell(display, 0);
330     return gFalse;
331   }
332   return gTrue;
333 }
334 
goBackward()335 GBool XPDFCore::goBackward() {
336   if (!PDFCore::goBackward()) {
337     XBell(display, 0);
338     return gFalse;
339   }
340   return gTrue;
341 }
342 
startPan(int wx,int wy)343 void XPDFCore::startPan(int wx, int wy) {
344   panning = gTrue;
345   panMX = wx;
346   panMY = wy;
347 }
348 
endPan(int wx,int wy)349 void XPDFCore::endPan(int wx, int wy) {
350   panning = gFalse;
351 }
352 
353 //------------------------------------------------------------------------
354 // selection
355 //------------------------------------------------------------------------
356 
startSelection(int wx,int wy)357 void XPDFCore::startSelection(int wx, int wy) {
358   int pg, x, y;
359 
360   takeFocus();
361   if (doc && doc->getNumPages() > 0) {
362     if (selectEnabled) {
363       if (cvtWindowToDev(wx, wy, &pg, &x, &y)) {
364 	setSelection(pg, x, y, x, y);
365 	setCursor(selectCursor);
366 	dragging = gTrue;
367       }
368     }
369   }
370 }
371 
endSelection(int wx,int wy)372 void XPDFCore::endSelection(int wx, int wy) {
373   int pg, x, y;
374   GBool ok;
375 
376   if (doc && doc->getNumPages() > 0) {
377     ok = cvtWindowToDev(wx, wy, &pg, &x, &y);
378     if (dragging) {
379       dragging = gFalse;
380       setCursor(None);
381       if (ok) {
382 	moveSelection(pg, x, y);
383       }
384 #ifndef NO_TEXT_SELECT
385       if (selectULX != selectLRX &&
386 	  selectULY != selectLRY) {
387 	if (doc->okToCopy()) {
388 	  copySelection();
389 	} else {
390 	  error(errNotAllowed, -1,
391 		"Copying of text from this document is not allowed.");
392 	}
393       }
394 #endif
395     }
396   }
397 }
398 
399 // X's copy-and-paste mechanism is brain damaged.  Xt doesn't help
400 // any, but doesn't make it too much worse, either.  Motif, on the
401 // other hand, adds significant complexity to the mess.  So here we
402 // blow off the Motif junk and stick to plain old Xt.  The next two
403 // functions (copySelection and convertSelectionCbk) implement the
404 // magic needed to deal with Xt's mechanism.  Note that this requires
405 // global variables (currentSelection and currentSelectionOwner).
406 
copySelection()407 void XPDFCore::copySelection() {
408   int pg;
409   double ulx, uly, lrx, lry;
410 
411   if (!doc->okToCopy()) {
412     return;
413   }
414   if (getSelection(&pg, &ulx, &uly, &lrx, &lry)) {
415     //~ for multithreading: need a mutex here
416     if (currentSelection) {
417       delete currentSelection;
418     }
419     currentSelection = extractText(pg, ulx, uly, lrx, lry);
420     currentSelectionOwner = this;
421     XtOwnSelection(drawArea, XA_PRIMARY, XtLastTimestampProcessed(display),
422 		   &convertSelectionCbk, NULL, NULL);
423   }
424 }
425 
convertSelectionCbk(Widget widget,Atom * selection,Atom * target,Atom * type,XtPointer * value,unsigned long * length,int * format)426 Boolean XPDFCore::convertSelectionCbk(Widget widget, Atom *selection,
427 				      Atom *target, Atom *type,
428 				      XtPointer *value, unsigned long *length,
429 				      int *format) {
430   Atom *array;
431 
432   // send back a list of supported conversion targets
433   if (*target == targetsAtom) {
434     if (!(array = (Atom *)XtMalloc(sizeof(Atom)))) {
435       return False;
436     }
437     array[0] = XA_STRING;
438     *value = (XtPointer)array;
439     *type = XA_ATOM;
440     *format = 32;
441     *length = 1;
442     return True;
443 
444   // send the selected text
445   } else if (*target == XA_STRING) {
446     //~ for multithreading: need a mutex here
447     *value = XtNewString(currentSelection->getCString());
448     *length = currentSelection->getLength();
449     *type = XA_STRING;
450     *format = 8; // 8-bit elements
451     return True;
452   }
453 
454   return False;
455 }
456 
457 //------------------------------------------------------------------------
458 // hyperlinks
459 //------------------------------------------------------------------------
460 
doAction(LinkAction * action)461 void XPDFCore::doAction(LinkAction *action) {
462   LinkActionKind kind;
463   LinkDest *dest;
464   GString *namedDest;
465   char *s;
466   GString *fileName, *fileName2;
467   GString *cmd;
468   GString *actionName;
469   Object movieAnnot, obj1, obj2;
470   GString *msg;
471   int i;
472 
473   switch (kind = action->getKind()) {
474 
475   // GoTo / GoToR action
476   case actionGoTo:
477   case actionGoToR:
478     if (kind == actionGoTo) {
479       dest = NULL;
480       namedDest = NULL;
481       if ((dest = ((LinkGoTo *)action)->getDest())) {
482 	dest = dest->copy();
483       } else if ((namedDest = ((LinkGoTo *)action)->getNamedDest())) {
484 	namedDest = namedDest->copy();
485       }
486     } else {
487       dest = NULL;
488       namedDest = NULL;
489       if ((dest = ((LinkGoToR *)action)->getDest())) {
490 	dest = dest->copy();
491       } else if ((namedDest = ((LinkGoToR *)action)->getNamedDest())) {
492 	namedDest = namedDest->copy();
493       }
494       s = ((LinkGoToR *)action)->getFileName()->getCString();
495       //~ translate path name for VMS (deal with '/')
496       if (isAbsolutePath(s) || !doc->getFileName()) {
497 	fileName = new GString(s);
498       } else {
499 	fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);
500       }
501       if (loadFile(fileName) != errNone) {
502 	if (dest) {
503 	  delete dest;
504 	}
505 	if (namedDest) {
506 	  delete namedDest;
507 	}
508 	delete fileName;
509 	return;
510       }
511       delete fileName;
512     }
513     if (namedDest) {
514       dest = doc->findDest(namedDest);
515       delete namedDest;
516     }
517     if (dest) {
518       displayDest(dest, zoom, rotate, gTrue);
519       delete dest;
520     } else {
521       if (kind == actionGoToR) {
522 	displayPage(1, zoom, 0, gFalse, gTrue);
523       }
524     }
525     break;
526 
527   // Launch action
528   case actionLaunch:
529     fileName = ((LinkLaunch *)action)->getFileName();
530     s = fileName->getCString();
531     if (!strcmp(s + fileName->getLength() - 4, ".pdf") ||
532 	!strcmp(s + fileName->getLength() - 4, ".PDF")) {
533       //~ translate path name for VMS (deal with '/')
534       if (isAbsolutePath(s) || !doc->getFileName()) {
535 	fileName = fileName->copy();
536       } else {
537 	fileName = appendToPath(grabPath(doc->getFileName()->getCString()), s);
538       }
539       if (loadFile(fileName) != errNone) {
540 	delete fileName;
541 	return;
542       }
543       delete fileName;
544       displayPage(1, zoom, rotate, gFalse, gTrue);
545     } else {
546       fileName = fileName->copy();
547       if (((LinkLaunch *)action)->getParams()) {
548 	fileName->append(' ');
549 	fileName->append(((LinkLaunch *)action)->getParams());
550       }
551 #ifdef VMS
552       fileName->insert(0, "spawn/nowait ");
553 #elif defined(__EMX__)
554       fileName->insert(0, "start /min /n ");
555 #else
556       fileName->append(" &");
557 #endif
558       if (globalParams->getLaunchCommand()) {
559 	fileName->insert(0, ' ');
560 	fileName->insert(0, globalParams->getLaunchCommand());
561 	system(fileName->getCString());
562       } else {
563 	msg = new GString("About to execute the command:\n");
564 	msg->append(fileName);
565 	if (doQuestionDialog("Launching external application", msg)) {
566 	  system(fileName->getCString());
567 	}
568 	delete msg;
569       }
570       delete fileName;
571     }
572     break;
573 
574   // URI action
575   case actionURI:
576     if (!(cmd = globalParams->getURLCommand())) {
577       error(errConfig, -1, "No urlCommand defined in config file");
578       break;
579     }
580     runCommand(cmd, ((LinkURI *)action)->getURI());
581     break;
582 
583   // Named action
584   case actionNamed:
585     actionName = ((LinkNamed *)action)->getName();
586     if (!actionName->cmp("NextPage")) {
587       gotoNextPage(1, gTrue);
588     } else if (!actionName->cmp("PrevPage")) {
589       gotoPrevPage(1, gTrue, gFalse);
590     } else if (!actionName->cmp("FirstPage")) {
591       if (topPage != 1) {
592 	displayPage(1, zoom, rotate, gTrue, gTrue);
593       }
594     } else if (!actionName->cmp("LastPage")) {
595       if (topPage != doc->getNumPages()) {
596 	displayPage(doc->getNumPages(), zoom, rotate, gTrue, gTrue);
597       }
598     } else if (!actionName->cmp("GoBack")) {
599       goBackward();
600     } else if (!actionName->cmp("GoForward")) {
601       goForward();
602     } else if (!actionName->cmp("Quit")) {
603       if (actionCbk) {
604 	(*actionCbk)(actionCbkData, actionName->getCString());
605       }
606     } else {
607       error(errSyntaxError, -1,
608 	    "Unknown named action: '{0:t}'", actionName);
609     }
610     break;
611 
612   // Movie action
613   case actionMovie:
614     if (!(cmd = globalParams->getMovieCommand())) {
615       error(errConfig, -1, "No movieCommand defined in config file");
616       break;
617     }
618     if (((LinkMovie *)action)->hasAnnotRef()) {
619       doc->getXRef()->fetch(((LinkMovie *)action)->getAnnotRef()->num,
620 			    ((LinkMovie *)action)->getAnnotRef()->gen,
621 			    &movieAnnot);
622     } else {
623       //~ need to use the correct page num here
624       doc->getCatalog()->getPage(topPage)->getAnnots(&obj1);
625       if (obj1.isArray()) {
626 	for (i = 0; i < obj1.arrayGetLength(); ++i) {
627 	  if (obj1.arrayGet(i, &movieAnnot)->isDict()) {
628 	    if (movieAnnot.dictLookup("Subtype", &obj2)->isName("Movie")) {
629 	      obj2.free();
630 	      break;
631 	    }
632 	    obj2.free();
633 	  }
634 	  movieAnnot.free();
635 	}
636 	obj1.free();
637       }
638     }
639     if (movieAnnot.isDict()) {
640       if (movieAnnot.dictLookup("Movie", &obj1)->isDict()) {
641 	if (obj1.dictLookup("F", &obj2)) {
642 	  if ((fileName = LinkAction::getFileSpecName(&obj2))) {
643 	    if (!isAbsolutePath(fileName->getCString()) &&
644 		doc->getFileName()) {
645 	      fileName2 = appendToPath(
646 			      grabPath(doc->getFileName()->getCString()),
647 			      fileName->getCString());
648 	      delete fileName;
649 	      fileName = fileName2;
650 	    }
651 	    runCommand(cmd, fileName);
652 	    delete fileName;
653 	  }
654 	  obj2.free();
655 	}
656 	obj1.free();
657       }
658     }
659     movieAnnot.free();
660     break;
661 
662   // unsupported action types
663   case actionJavaScript:
664   case actionSubmitForm:
665   case actionHide:
666     error(errSyntaxError, -1, "Unsupported link action type");
667     break;
668 
669   // unknown action type
670   case actionUnknown:
671     error(errSyntaxError, -1, "Unknown link action type: '{0:t}'",
672 	  ((LinkUnknown *)action)->getAction());
673     break;
674   }
675 }
676 
677 // Run a command, given a <cmdFmt> string with one '%s' in it, and an
678 // <arg> string to insert in place of the '%s'.
runCommand(GString * cmdFmt,GString * arg)679 void XPDFCore::runCommand(GString *cmdFmt, GString *arg) {
680   GString *cmd;
681   char *s;
682 
683   if ((s = strstr(cmdFmt->getCString(), "%s"))) {
684     cmd = mungeURL(arg);
685     cmd->insert(0, cmdFmt->getCString(),
686 		s - cmdFmt->getCString());
687     cmd->append(s + 2);
688   } else {
689     cmd = cmdFmt->copy();
690   }
691 #ifdef VMS
692   cmd->insert(0, "spawn/nowait ");
693 #elif defined(__EMX__)
694   cmd->insert(0, "start /min /n ");
695 #else
696   cmd->append(" &");
697 #endif
698   system(cmd->getCString());
699   delete cmd;
700 }
701 
702 // Escape any characters in a URL which might cause problems when
703 // calling system().
mungeURL(GString * url)704 GString *XPDFCore::mungeURL(GString *url) {
705   static const char *allowed = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
706                                "abcdefghijklmnopqrstuvwxyz"
707                                "0123456789"
708                                "-_.~/?:@&=+,#%";
709   GString *newURL;
710   char c;
711   char buf[4];
712   int i;
713 
714   newURL = new GString();
715   for (i = 0; i < url->getLength(); ++i) {
716     c = url->getChar(i);
717     if (strchr(allowed, c)) {
718       newURL->append(c);
719     } else {
720       sprintf(buf, "%%%02x", c & 0xff);
721       newURL->append(buf);
722     }
723   }
724   return newURL;
725 }
726 
727 //------------------------------------------------------------------------
728 // find
729 //------------------------------------------------------------------------
730 
find(char * s,GBool caseSensitive,GBool next,GBool backward,GBool wholeWord,GBool onePageOnly)731 GBool XPDFCore::find(char *s, GBool caseSensitive, GBool next,
732 		     GBool backward, GBool wholeWord, GBool onePageOnly) {
733   if (!PDFCore::find(s, caseSensitive, next,
734 		     backward, wholeWord, onePageOnly)) {
735     XBell(display, 0);
736     return gFalse;
737   }
738 #ifndef NO_TEXT_SELECT
739   copySelection();
740 #endif
741   return gTrue;
742 }
743 
findU(Unicode * u,int len,GBool caseSensitive,GBool next,GBool backward,GBool wholeWord,GBool onePageOnly)744 GBool XPDFCore::findU(Unicode *u, int len, GBool caseSensitive,
745 		      GBool next, GBool backward,
746 		      GBool wholeWord, GBool onePageOnly) {
747   if (!PDFCore::findU(u, len, caseSensitive, next,
748 		      backward, wholeWord, onePageOnly)) {
749     XBell(display, 0);
750     return gFalse;
751   }
752 #ifndef NO_TEXT_SELECT
753   copySelection();
754 #endif
755   return gTrue;
756 }
757 
758 //------------------------------------------------------------------------
759 // misc access
760 //------------------------------------------------------------------------
761 
setBusyCursor(GBool busy)762 void XPDFCore::setBusyCursor(GBool busy) {
763   setCursor(busy ? busyCursor : None);
764 }
765 
takeFocus()766 void XPDFCore::takeFocus() {
767   XmProcessTraversal(drawArea, XmTRAVERSE_CURRENT);
768 }
769 
770 //------------------------------------------------------------------------
771 // GUI code
772 //------------------------------------------------------------------------
773 
setupX(GBool installCmap,int rgbCubeSizeA)774 void XPDFCore::setupX(GBool installCmap, int rgbCubeSizeA) {
775   XVisualInfo visualTempl;
776   XVisualInfo *visualList;
777   Gulong mask;
778   int nVisuals;
779   XColor xcolor;
780   XColor *xcolors;
781   int r, g, b, n, m;
782   GBool ok;
783 
784   // for some reason, querying XmNvisual doesn't work (even if done
785   // after the window is mapped)
786   visual = DefaultVisual(display, screenNum);
787   XtVaGetValues(shell, XmNcolormap, &colormap, NULL);
788 
789   // check for TrueColor visual
790   //~ this should scan the list, not just look at the first one
791   visualTempl.visualid = XVisualIDFromVisual(visual);
792   visualList = XGetVisualInfo(display, VisualIDMask,
793                               &visualTempl, &nVisuals);
794   if (nVisuals < 1) {
795     // this shouldn't happen
796     XFree((XPointer)visualList);
797     visualList = XGetVisualInfo(display, VisualNoMask, &visualTempl,
798                                 &nVisuals);
799   }
800   depth = visualList->depth;
801   if (visualList->c_class == TrueColor) {
802     trueColor = gTrue;
803     for (mask = visualList->red_mask, rShift = 0;
804          mask && !(mask & 1);
805          mask >>= 1, ++rShift) ;
806     for (rDiv = 8; mask; mask >>= 1, --rDiv) ;
807     for (mask = visualList->green_mask, gShift = 0;
808          mask && !(mask & 1);
809          mask >>= 1, ++gShift) ;
810     for (gDiv = 8; mask; mask >>= 1, --gDiv) ;
811     for (mask = visualList->blue_mask, bShift = 0;
812          mask && !(mask & 1);
813          mask >>= 1, ++bShift) ;
814     for (bDiv = 8; mask; mask >>= 1, --bDiv) ;
815   } else {
816     trueColor = gFalse;
817   }
818   XFree((XPointer)visualList);
819 
820   // allocate a color cube
821   if (!trueColor) {
822 
823     // set colors in private colormap
824     if (installCmap) {
825       for (rgbCubeSize = xMaxRGBCube; rgbCubeSize >= 2; --rgbCubeSize) {
826         m = rgbCubeSize * rgbCubeSize * rgbCubeSize;
827         if (XAllocColorCells(display, colormap, False, NULL, 0, colors, m)) {
828           break;
829         }
830       }
831       if (rgbCubeSize >= 2) {
832         m = rgbCubeSize * rgbCubeSize * rgbCubeSize;
833         xcolors = (XColor *)gmallocn(m, sizeof(XColor));
834         n = 0;
835         for (r = 0; r < rgbCubeSize; ++r) {
836           for (g = 0; g < rgbCubeSize; ++g) {
837             for (b = 0; b < rgbCubeSize; ++b) {
838               xcolors[n].pixel = colors[n];
839               xcolors[n].red = (r * 65535) / (rgbCubeSize - 1);
840               xcolors[n].green = (g * 65535) / (rgbCubeSize - 1);
841               xcolors[n].blue = (b * 65535) / (rgbCubeSize - 1);
842               xcolors[n].flags = DoRed | DoGreen | DoBlue;
843               ++n;
844             }
845           }
846         }
847         XStoreColors(display, colormap, xcolors, m);
848         gfree(xcolors);
849       } else {
850         rgbCubeSize = 1;
851         colors[0] = BlackPixel(display, screenNum);
852         colors[1] = WhitePixel(display, screenNum);
853       }
854 
855     // allocate colors in shared colormap
856     } else {
857       if (rgbCubeSize > xMaxRGBCube) {
858         rgbCubeSize = xMaxRGBCube;
859       }
860       ok = gFalse;
861       for (rgbCubeSize = rgbCubeSizeA; rgbCubeSize >= 2; --rgbCubeSize) {
862         ok = gTrue;
863         n = 0;
864         for (r = 0; r < rgbCubeSize && ok; ++r) {
865           for (g = 0; g < rgbCubeSize && ok; ++g) {
866             for (b = 0; b < rgbCubeSize && ok; ++b) {
867               if (n == 0) {
868                 colors[n] = BlackPixel(display, screenNum);
869                 ++n;
870               } else {
871                 xcolor.red = (r * 65535) / (rgbCubeSize - 1);
872                 xcolor.green = (g * 65535) / (rgbCubeSize - 1);
873                 xcolor.blue = (b * 65535) / (rgbCubeSize - 1);
874                 if (XAllocColor(display, colormap, &xcolor)) {
875                   colors[n++] = xcolor.pixel;
876                 } else {
877                   ok = gFalse;
878                 }
879               }
880             }
881           }
882         }
883         if (ok) {
884           break;
885         }
886         XFreeColors(display, colormap, &colors[1], n-1, 0);
887       }
888       if (!ok) {
889         rgbCubeSize = 1;
890         colors[0] = BlackPixel(display, screenNum);
891         colors[1] = WhitePixel(display, screenNum);
892       }
893     }
894   }
895 }
896 
initWindow()897 void XPDFCore::initWindow() {
898   Arg args[20];
899   int n;
900 
901   // create the cursors
902   busyCursor = XCreateFontCursor(display, XC_watch);
903   linkCursor = XCreateFontCursor(display, XC_hand2);
904   selectCursor = XCreateFontCursor(display, XC_cross);
905   currentCursor = 0;
906 
907   // create the scrolled window and scrollbars
908   n = 0;
909   XtSetArg(args[n], XmNscrollingPolicy, XmAPPLICATION_DEFINED); ++n;
910   XtSetArg(args[n], XmNvisualPolicy, XmVARIABLE); ++n;
911   scrolledWin = XmCreateScrolledWindow(parentWidget, "scroll", args, n);
912   XtManageChild(scrolledWin);
913   n = 0;
914   XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
915   XtSetArg(args[n], XmNminimum, 0); ++n;
916   XtSetArg(args[n], XmNmaximum, 1); ++n;
917   XtSetArg(args[n], XmNsliderSize, 1); ++n;
918   XtSetArg(args[n], XmNvalue, 0); ++n;
919   XtSetArg(args[n], XmNincrement, 1); ++n;
920   XtSetArg(args[n], XmNpageIncrement, 1); ++n;
921   hScrollBar = XmCreateScrollBar(scrolledWin, "hScrollBar", args, n);
922   if (!fullScreen) {
923     XtManageChild(hScrollBar);
924   }
925   XtAddCallback(hScrollBar, XmNvalueChangedCallback,
926 		&hScrollChangeCbk, (XtPointer)this);
927 #ifndef DISABLE_SMOOTH_SCROLL
928   XtAddCallback(hScrollBar, XmNdragCallback,
929 		&hScrollDragCbk, (XtPointer)this);
930 #endif
931   n = 0;
932   XtSetArg(args[n], XmNorientation, XmVERTICAL); ++n;
933   XtSetArg(args[n], XmNminimum, 0); ++n;
934   XtSetArg(args[n], XmNmaximum, 1); ++n;
935   XtSetArg(args[n], XmNsliderSize, 1); ++n;
936   XtSetArg(args[n], XmNvalue, 0); ++n;
937   XtSetArg(args[n], XmNincrement, 1); ++n;
938   XtSetArg(args[n], XmNpageIncrement, 1); ++n;
939   vScrollBar = XmCreateScrollBar(scrolledWin, "vScrollBar", args, n);
940   if (!fullScreen) {
941     XtManageChild(vScrollBar);
942   }
943   XtAddCallback(vScrollBar, XmNvalueChangedCallback,
944 		&vScrollChangeCbk, (XtPointer)this);
945 #ifndef DISABLE_SMOOTH_SCROLL
946   XtAddCallback(vScrollBar, XmNdragCallback,
947 		&vScrollDragCbk, (XtPointer)this);
948 #endif
949 
950   // create the drawing area
951   n = 0;
952   XtSetArg(args[n], XmNshadowType, XmSHADOW_IN); ++n;
953   XtSetArg(args[n], XmNmarginWidth, 0); ++n;
954   XtSetArg(args[n], XmNmarginHeight, 0); ++n;
955   if (fullScreen) {
956     XtSetArg(args[n], XmNshadowThickness, 0); ++n;
957   }
958   drawAreaFrame = XmCreateFrame(scrolledWin, "drawAreaFrame", args, n);
959   XtManageChild(drawAreaFrame);
960   n = 0;
961   XtSetArg(args[n], XmNresizePolicy, XmRESIZE_ANY); ++n;
962   XtSetArg(args[n], XmNwidth, 700); ++n;
963   XtSetArg(args[n], XmNheight, 500); ++n;
964   drawArea = XmCreateDrawingArea(drawAreaFrame, "drawArea", args, n);
965   XtManageChild(drawArea);
966   XtAddCallback(drawArea, XmNresizeCallback, &resizeCbk, (XtPointer)this);
967   XtAddCallback(drawArea, XmNexposeCallback, &redrawCbk, (XtPointer)this);
968   XtAddCallback(drawArea, XmNinputCallback, &inputCbk, (XtPointer)this);
969   resizeCbk(drawArea, this, NULL);
970 
971   // set up mouse motion translations
972   XtOverrideTranslations(drawArea, XtParseTranslationTable(
973       "<Btn1Down>:DrawingAreaInput()\n"
974       "<Btn1Up>:DrawingAreaInput()\n"
975       "<Btn1Motion>:DrawingAreaInput()\n"
976       "<Motion>:DrawingAreaInput()"));
977 
978   // can't create a GC until the window gets mapped
979   drawAreaGC = NULL;
980 }
981 
hScrollChangeCbk(Widget widget,XtPointer ptr,XtPointer callData)982 void XPDFCore::hScrollChangeCbk(Widget widget, XtPointer ptr,
983 				XtPointer callData) {
984   XPDFCore *core = (XPDFCore *)ptr;
985   XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
986 
987   core->scrollTo(data->value, core->scrollY);
988 }
989 
hScrollDragCbk(Widget widget,XtPointer ptr,XtPointer callData)990 void XPDFCore::hScrollDragCbk(Widget widget, XtPointer ptr,
991 			      XtPointer callData) {
992   XPDFCore *core = (XPDFCore *)ptr;
993   XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
994 
995   core->scrollTo(data->value, core->scrollY);
996 }
997 
vScrollChangeCbk(Widget widget,XtPointer ptr,XtPointer callData)998 void XPDFCore::vScrollChangeCbk(Widget widget, XtPointer ptr,
999 			     XtPointer callData) {
1000   XPDFCore *core = (XPDFCore *)ptr;
1001   XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
1002 
1003   core->scrollTo(core->scrollX, data->value);
1004 }
1005 
vScrollDragCbk(Widget widget,XtPointer ptr,XtPointer callData)1006 void XPDFCore::vScrollDragCbk(Widget widget, XtPointer ptr,
1007 			      XtPointer callData) {
1008   XPDFCore *core = (XPDFCore *)ptr;
1009   XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *)callData;
1010 
1011   core->scrollTo(core->scrollX, data->value);
1012 }
1013 
resizeCbk(Widget widget,XtPointer ptr,XtPointer callData)1014 void XPDFCore::resizeCbk(Widget widget, XtPointer ptr, XtPointer callData) {
1015   XPDFCore *core = (XPDFCore *)ptr;
1016   XEvent event;
1017   Widget top;
1018   Window rootWin;
1019   int x1, y1;
1020   Guint w1, h1, bw1, depth1;
1021   Arg args[2];
1022   int n;
1023   Dimension w, h;
1024   int sx, sy;
1025 
1026   // find the top-most widget which has an associated window, and look
1027   // for a pending ConfigureNotify in the event queue -- if there is
1028   // one, and it specifies a different width or height, that means
1029   // we're still resizing, and we want to skip the current event
1030   for (top = core->parentWidget;
1031        XtParent(top) && XtWindow(XtParent(top));
1032        top = XtParent(top)) ;
1033   if (XCheckTypedWindowEvent(core->display, XtWindow(top),
1034 			     ConfigureNotify, &event)) {
1035     XPutBackEvent(core->display, &event);
1036     XGetGeometry(core->display, event.xconfigure.window,
1037 		 &rootWin, &x1, &y1, &w1, &h1, &bw1, &depth1);
1038     if ((Guint)event.xconfigure.width != w1 ||
1039 	(Guint)event.xconfigure.height != h1) {
1040       return;
1041     }
1042   }
1043 
1044   n = 0;
1045   XtSetArg(args[n], XmNwidth, &w); ++n;
1046   XtSetArg(args[n], XmNheight, &h); ++n;
1047   XtGetValues(core->drawArea, args, n);
1048   core->drawAreaWidth = (int)w;
1049   core->drawAreaHeight = (int)h;
1050   if (core->zoom == zoomPage || core->zoom == zoomWidth) {
1051     sx = sy = -1;
1052   } else {
1053     sx = core->scrollX;
1054     sy = core->scrollY;
1055   }
1056   core->update(core->topPage, sx, sy, core->zoom, core->rotate, gTrue, gFalse,
1057 	       gFalse);
1058 }
1059 
redrawCbk(Widget widget,XtPointer ptr,XtPointer callData)1060 void XPDFCore::redrawCbk(Widget widget, XtPointer ptr, XtPointer callData) {
1061   XPDFCore *core = (XPDFCore *)ptr;
1062   XmDrawingAreaCallbackStruct *data = (XmDrawingAreaCallbackStruct *)callData;
1063   int x, y, w, h;
1064 
1065   if (data->reason == XmCR_EXPOSE) {
1066     x = data->event->xexpose.x;
1067     y = data->event->xexpose.y;
1068     w = data->event->xexpose.width;
1069     h = data->event->xexpose.height;
1070   } else {
1071     x = 0;
1072     y = 0;
1073     w = core->drawAreaWidth;
1074     h = core->drawAreaHeight;
1075   }
1076   core->redrawWindow(x, y, w, h, gFalse);
1077 }
1078 
inputCbk(Widget widget,XtPointer ptr,XtPointer callData)1079 void XPDFCore::inputCbk(Widget widget, XtPointer ptr, XtPointer callData) {
1080   XPDFCore *core = (XPDFCore *)ptr;
1081   XmDrawingAreaCallbackStruct *data = (XmDrawingAreaCallbackStruct *)callData;
1082   LinkAction *action;
1083   int pg, x, y;
1084   double xu, yu;
1085   const char *s;
1086   KeySym key;
1087   GBool ok;
1088 
1089   switch (data->event->type) {
1090   case ButtonPress:
1091     if (*core->mouseCbk) {
1092       (*core->mouseCbk)(core->mouseCbkData, data->event);
1093     }
1094     break;
1095   case ButtonRelease:
1096     if (*core->mouseCbk) {
1097       (*core->mouseCbk)(core->mouseCbkData, data->event);
1098     }
1099     break;
1100   case MotionNotify:
1101     if (core->doc && core->doc->getNumPages() > 0) {
1102       ok = core->cvtWindowToDev(data->event->xmotion.x, data->event->xmotion.y,
1103 				&pg, &x, &y);
1104       if (core->dragging) {
1105 	if (ok) {
1106 	  core->moveSelection(pg, x, y);
1107 	}
1108       } else if (core->hyperlinksEnabled) {
1109 	core->cvtDevToUser(pg, x, y, &xu, &yu);
1110 	if (ok && (action = core->findLink(pg, xu, yu))) {
1111 	  core->setCursor(core->linkCursor);
1112 	  if (action != core->linkAction) {
1113 	    core->linkAction = action;
1114 	    if (core->updateCbk) {
1115 	      s = "";
1116 	      switch (action->getKind()) {
1117 	      case actionGoTo:
1118 		s = "[internal link]";
1119 		break;
1120 	      case actionGoToR:
1121 		s = ((LinkGoToR *)action)->getFileName()->getCString();
1122 		break;
1123 	      case actionLaunch:
1124 		s = ((LinkLaunch *)action)->getFileName()->getCString();
1125 		break;
1126 	      case actionURI:
1127 		s = ((LinkURI *)action)->getURI()->getCString();
1128 		break;
1129 	      case actionNamed:
1130 		s = ((LinkNamed *)action)->getName()->getCString();
1131 		break;
1132 	      case actionMovie:
1133 		s = "[movie]";
1134 		break;
1135 	      case actionJavaScript:
1136 	      case actionSubmitForm:
1137 	      case actionHide:
1138 	      case actionUnknown:
1139 		s = "[unknown link]";
1140 		break;
1141 	      }
1142 	      (*core->updateCbk)(core->updateCbkData, NULL, -1, -1, s);
1143 	    }
1144 	  }
1145 	} else {
1146 	  core->setCursor(None);
1147 	  if (core->linkAction) {
1148 	    core->linkAction = NULL;
1149 	    if (core->updateCbk) {
1150 	      (*core->updateCbk)(core->updateCbkData, NULL, -1, -1, "");
1151 	    }
1152 	  }
1153 	}
1154       }
1155     }
1156     if (core->panning) {
1157       core->scrollTo(core->scrollX - (data->event->xmotion.x - core->panMX),
1158 		     core->scrollY - (data->event->xmotion.y - core->panMY));
1159       core->panMX = data->event->xmotion.x;
1160       core->panMY = data->event->xmotion.y;
1161     }
1162     break;
1163   case KeyPress:
1164     if (core->keyPressCbk) {
1165       key = XLookupKeysym(&data->event->xkey,
1166 			  (data->event->xkey.state & ShiftMask) ? 1 : 0);
1167       (*core->keyPressCbk)(core->keyPressCbkData,
1168 			   key, data->event->xkey.state, data->event);
1169     }
1170     break;
1171   }
1172 }
1173 
newTile(int xDestA,int yDestA)1174 PDFCoreTile *XPDFCore::newTile(int xDestA, int yDestA) {
1175   return new XPDFCoreTile(xDestA, yDestA);
1176 }
1177 
updateTileData(PDFCoreTile * tileA,int xSrc,int ySrc,int width,int height,GBool composited)1178 void XPDFCore::updateTileData(PDFCoreTile *tileA, int xSrc, int ySrc,
1179 			      int width, int height, GBool composited) {
1180   XPDFCoreTile *tile = (XPDFCoreTile *)tileA;
1181   XImage *image;
1182   SplashColorPtr dataPtr, p;
1183   Gulong pixel;
1184   Guchar *ap;
1185   Guchar alpha, alpha1;
1186   int w, h, bw, x, y, r, g, b, gray;
1187   int *errDownR, *errDownG, *errDownB;
1188   int errRightR, errRightG, errRightB;
1189   int errDownRightR, errDownRightG, errDownRightB;
1190   int r0, g0, b0, re, ge, be;
1191 
1192   if (!tile->image) {
1193     w = tile->xMax - tile->xMin;
1194     h = tile->yMax - tile->yMin;
1195     image = XCreateImage(display, visual, depth, ZPixmap, 0, NULL, w, h, 8, 0);
1196     image->data = (char *)gmallocn(h, image->bytes_per_line);
1197     tile->image = image;
1198   } else {
1199     image = (XImage *)tile->image;
1200   }
1201 
1202   //~ optimize for known XImage formats
1203   bw = tile->bitmap->getRowSize();
1204   dataPtr = tile->bitmap->getDataPtr();
1205 
1206   if (trueColor) {
1207     for (y = 0; y < height; ++y) {
1208       p = dataPtr + (ySrc + y) * bw + xSrc * 3;
1209       if (!composited && tile->bitmap->getAlphaPtr()) {
1210 	ap = tile->bitmap->getAlphaPtr() +
1211 	       (ySrc + y) * tile->bitmap->getWidth() + xSrc;
1212       } else {
1213 	ap = NULL;
1214       }
1215       for (x = 0; x < width; ++x) {
1216 	r = splashRGB8R(p);
1217 	g = splashRGB8G(p);
1218 	b = splashRGB8B(p);
1219 	if (ap) {
1220 	  alpha = *ap++;
1221 	  alpha1 = 255 - alpha;
1222 	  r = div255(alpha1 * paperColor[0] + alpha * r);
1223 	  g = div255(alpha1 * paperColor[1] + alpha * g);
1224 	  b = div255(alpha1 * paperColor[2] + alpha * b);
1225 	}
1226 	r >>= rDiv;
1227 	g >>= gDiv;
1228 	b >>= bDiv;
1229 	pixel = ((Gulong)r << rShift) +
1230 	        ((Gulong)g << gShift) +
1231 	        ((Gulong)b << bShift);
1232 	XPutPixel(image, xSrc + x, ySrc + y, pixel);
1233 	p += 3;
1234       }
1235     }
1236   } else if (rgbCubeSize == 1) {
1237     //~ this should really use splashModeMono, with non-clustered dithering
1238     for (y = 0; y < height; ++y) {
1239       p = dataPtr + (ySrc + y) * bw + xSrc * 3;
1240       if (!composited && tile->bitmap->getAlphaPtr()) {
1241 	ap = tile->bitmap->getAlphaPtr() +
1242 	       (ySrc + y) * tile->bitmap->getWidth() + xSrc;
1243       } else {
1244 	ap = NULL;
1245       }
1246       for (x = 0; x < width; ++x) {
1247 	r = splashRGB8R(p);
1248 	g = splashRGB8G(p);
1249 	b = splashRGB8B(p);
1250 	if (ap) {
1251 	  alpha = *ap++;
1252 	  alpha1 = 255 - alpha;
1253 	  r = div255(alpha1 * paperColor[0] + alpha * r);
1254 	  g = div255(alpha1 * paperColor[1] + alpha * g);
1255 	  b = div255(alpha1 * paperColor[2] + alpha * b);
1256 	}
1257 	gray = (int)(0.299 * r + 0.587 * g + 0.114 * b + 0.5);
1258 	if (gray < 128) {
1259 	  pixel = colors[0];
1260 	} else {
1261 	  pixel = colors[1];
1262 	}
1263 	XPutPixel(image, xSrc + x, ySrc + y, pixel);
1264 	p += 3;
1265       }
1266     }
1267   } else {
1268     // do Floyd-Steinberg dithering on the whole bitmap
1269     errDownR = (int *)gmallocn(width + 2, sizeof(int));
1270     errDownG = (int *)gmallocn(width + 2, sizeof(int));
1271     errDownB = (int *)gmallocn(width + 2, sizeof(int));
1272     errRightR = errRightG = errRightB = 0;
1273     errDownRightR = errDownRightG = errDownRightB = 0;
1274     memset(errDownR, 0, (width + 2) * sizeof(int));
1275     memset(errDownG, 0, (width + 2) * sizeof(int));
1276     memset(errDownB, 0, (width + 2) * sizeof(int));
1277     for (y = 0; y < height; ++y) {
1278       p = dataPtr + (ySrc + y) * bw + xSrc * 3;
1279       if (!composited && tile->bitmap->getAlphaPtr()) {
1280 	ap = tile->bitmap->getAlphaPtr() +
1281 	       (ySrc + y) * tile->bitmap->getWidth() + xSrc;
1282       } else {
1283 	ap = NULL;
1284       }
1285       for (x = 0; x < width; ++x) {
1286 	r = splashRGB8R(p);
1287 	g = splashRGB8G(p);
1288 	b = splashRGB8B(p);
1289 	if (ap) {
1290 	  alpha = *ap++;
1291 	  alpha1 = 255 - alpha;
1292 	  r = div255(alpha1 * paperColor[0] + alpha * r);
1293 	  g = div255(alpha1 * paperColor[1] + alpha * g);
1294 	  b = div255(alpha1 * paperColor[2] + alpha * b);
1295 	}
1296 	r0 = r + errRightR + errDownR[x+1];
1297 	g0 = g + errRightG + errDownG[x+1];
1298 	b0 = b + errRightB + errDownB[x+1];
1299 	if (r0 < 0) {
1300 	  r = 0;
1301 	} else if (r0 >= 255) {
1302 	  r = rgbCubeSize - 1;
1303 	} else {
1304 	  r = div255(r0 * (rgbCubeSize - 1));
1305 	}
1306 	if (g0 < 0) {
1307 	  g = 0;
1308 	} else if (g0 >= 255) {
1309 	  g = rgbCubeSize - 1;
1310 	} else {
1311 	  g = div255(g0 * (rgbCubeSize - 1));
1312 	}
1313 	if (b0 < 0) {
1314 	  b = 0;
1315 	} else if (b0 >= 255) {
1316 	  b = rgbCubeSize - 1;
1317 	} else {
1318 	  b = div255(b0 * (rgbCubeSize - 1));
1319 	}
1320 	re = r0 - ((r << 8) - r) / (rgbCubeSize - 1);
1321 	ge = g0 - ((g << 8) - g) / (rgbCubeSize - 1);
1322 	be = b0 - ((b << 8) - b) / (rgbCubeSize - 1);
1323 	errRightR = (re * 7) >> 4;
1324 	errRightG = (ge * 7) >> 4;
1325 	errRightB = (be * 7) >> 4;
1326 	errDownR[x] += (re * 3) >> 4;
1327 	errDownG[x] += (ge * 3) >> 4;
1328 	errDownB[x] += (be * 3) >> 4;
1329 	errDownR[x+1] = ((re * 5) >> 4) + errDownRightR;
1330 	errDownG[x+1] = ((ge * 5) >> 4) + errDownRightG;
1331 	errDownB[x+1] = ((be * 5) >> 4) + errDownRightB;
1332 	errDownRightR = re >> 4;
1333 	errDownRightG = ge >> 4;
1334 	errDownRightB = be >> 4;
1335 	pixel = colors[(r * rgbCubeSize + g) * rgbCubeSize + b];
1336 	XPutPixel(image, xSrc + x, ySrc + y, pixel);
1337 	p += 3;
1338       }
1339     }
1340     gfree(errDownR);
1341     gfree(errDownG);
1342     gfree(errDownB);
1343   }
1344 }
1345 
redrawRect(PDFCoreTile * tileA,int xSrc,int ySrc,int xDest,int yDest,int width,int height,GBool composited)1346 void XPDFCore::redrawRect(PDFCoreTile *tileA, int xSrc, int ySrc,
1347 			  int xDest, int yDest, int width, int height,
1348 			  GBool composited) {
1349   XPDFCoreTile *tile = (XPDFCoreTile *)tileA;
1350   Window drawAreaWin;
1351   XGCValues gcValues;
1352 
1353   // create a GC for the drawing area
1354   drawAreaWin = XtWindow(drawArea);
1355   if (!drawAreaGC) {
1356     gcValues.foreground = mattePixel;
1357     drawAreaGC = XCreateGC(display, drawAreaWin, GCForeground, &gcValues);
1358   }
1359 
1360   // draw the document
1361   if (tile) {
1362     XPutImage(display, drawAreaWin, drawAreaGC, tile->image,
1363 	      xSrc, ySrc, xDest, yDest, width, height);
1364 
1365   // draw the background
1366   } else {
1367     XFillRectangle(display, drawAreaWin, drawAreaGC,
1368 		   xDest, yDest, width, height);
1369   }
1370 
1371   XFlush(display);
1372 }
1373 
updateScrollbars()1374 void XPDFCore::updateScrollbars() {
1375   Arg args[20];
1376   int n;
1377   int maxPos;
1378 
1379   if (pages->getLength() > 0) {
1380     if (continuousMode) {
1381       maxPos = maxPageW;
1382     } else {
1383       maxPos = ((PDFCorePage *)pages->get(0))->w;
1384     }
1385   } else {
1386     maxPos = 1;
1387   }
1388   if (maxPos < drawAreaWidth) {
1389     maxPos = drawAreaWidth;
1390   }
1391   n = 0;
1392   XtSetArg(args[n], XmNvalue, scrollX); ++n;
1393   XtSetArg(args[n], XmNmaximum, maxPos); ++n;
1394   XtSetArg(args[n], XmNsliderSize, drawAreaWidth); ++n;
1395   XtSetArg(args[n], XmNincrement, 16); ++n;
1396   XtSetArg(args[n], XmNpageIncrement, drawAreaWidth); ++n;
1397   XtSetValues(hScrollBar, args, n);
1398 
1399   if (pages->getLength() > 0) {
1400     if (continuousMode) {
1401       maxPos = totalDocH;
1402     } else {
1403       maxPos = ((PDFCorePage *)pages->get(0))->h;
1404     }
1405   } else {
1406     maxPos = 1;
1407   }
1408   if (maxPos < drawAreaHeight) {
1409     maxPos = drawAreaHeight;
1410   }
1411   n = 0;
1412   XtSetArg(args[n], XmNvalue, scrollY); ++n;
1413   XtSetArg(args[n], XmNmaximum, maxPos); ++n;
1414   XtSetArg(args[n], XmNsliderSize, drawAreaHeight); ++n;
1415   XtSetArg(args[n], XmNincrement, 16); ++n;
1416   XtSetArg(args[n], XmNpageIncrement, drawAreaHeight); ++n;
1417   XtSetValues(vScrollBar, args, n);
1418 }
1419 
setCursor(Cursor cursor)1420 void XPDFCore::setCursor(Cursor cursor) {
1421   Window topWin;
1422 
1423   if (cursor == currentCursor) {
1424     return;
1425   }
1426   if (!(topWin = XtWindow(shell))) {
1427     return;
1428   }
1429   if (cursor == None) {
1430     XUndefineCursor(display, topWin);
1431   } else {
1432     XDefineCursor(display, topWin, cursor);
1433   }
1434   XFlush(display);
1435   currentCursor = cursor;
1436 }
1437 
doQuestionDialog(const char * title,GString * msg)1438 GBool XPDFCore::doQuestionDialog(const char *title, GString *msg) {
1439   return doDialog(XmDIALOG_QUESTION, gTrue, title, msg);
1440 }
1441 
doInfoDialog(const char * title,GString * msg)1442 void XPDFCore::doInfoDialog(const char *title, GString *msg) {
1443   doDialog(XmDIALOG_INFORMATION, gFalse, title, msg);
1444 }
1445 
doErrorDialog(const char * title,GString * msg)1446 void XPDFCore::doErrorDialog(const char *title, GString *msg) {
1447   doDialog(XmDIALOG_ERROR, gFalse, title, msg);
1448 }
1449 
doDialog(int type,GBool hasCancel,const char * title,GString * msg)1450 GBool XPDFCore::doDialog(int type, GBool hasCancel,
1451 			 const char *title, GString *msg) {
1452   Widget dialog, scroll, text;
1453   XtAppContext appContext;
1454   Arg args[20];
1455   int n;
1456   XmString s1, s2;
1457   XEvent event;
1458 
1459   n = 0;
1460   XtSetArg(args[n], XmNdialogType, type); ++n;
1461   XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n;
1462   s1 = XmStringCreateLocalized((char *)title);
1463   XtSetArg(args[n], XmNdialogTitle, s1); ++n;
1464   s2 = NULL; // make gcc happy
1465   if (msg->getLength() <= 80) {
1466     s2 = XmStringCreateLocalized(msg->getCString());
1467     XtSetArg(args[n], XmNmessageString, s2); ++n;
1468   }
1469   dialog = XmCreateMessageDialog(drawArea, "questionDialog", args, n);
1470   XmStringFree(s1);
1471   if (msg->getLength() <= 80) {
1472     XmStringFree(s2);
1473   } else {
1474     n = 0;
1475     XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC); ++n;
1476     if (drawAreaWidth > 300) {
1477       XtSetArg(args[n], XmNwidth, drawAreaWidth - 100); ++n;
1478     }
1479     scroll = XmCreateScrolledWindow(dialog, "scroll", args, n);
1480     XtManageChild(scroll);
1481     n = 0;
1482     XtSetArg(args[n], XmNeditable, False); ++n;
1483     XtSetArg(args[n], XmNeditMode, XmMULTI_LINE_EDIT); ++n;
1484     XtSetArg(args[n], XmNvalue, msg->getCString()); ++n;
1485     XtSetArg(args[n], XmNshadowThickness, 0); ++n;
1486     text = XmCreateText(scroll, "text", args, n);
1487     XtManageChild(text);
1488   }
1489   XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
1490   XtAddCallback(dialog, XmNokCallback,
1491 		&dialogOkCbk, (XtPointer)this);
1492   if (hasCancel) {
1493     XtAddCallback(dialog, XmNcancelCallback,
1494 		  &dialogCancelCbk, (XtPointer)this);
1495   } else {
1496     XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON));
1497   }
1498 
1499   XtManageChild(dialog);
1500 
1501   appContext = XtWidgetToApplicationContext(dialog);
1502   dialogDone = 0;
1503   do {
1504     XtAppNextEvent(appContext, &event);
1505     XtDispatchEvent(&event);
1506   } while (!dialogDone);
1507 
1508   XtUnmanageChild(dialog);
1509   XtDestroyWidget(dialog);
1510 
1511   return dialogDone > 0;
1512 }
1513 
dialogOkCbk(Widget widget,XtPointer ptr,XtPointer callData)1514 void XPDFCore::dialogOkCbk(Widget widget, XtPointer ptr,
1515 			   XtPointer callData) {
1516   XPDFCore *core = (XPDFCore *)ptr;
1517 
1518   core->dialogDone = 1;
1519 }
1520 
dialogCancelCbk(Widget widget,XtPointer ptr,XtPointer callData)1521 void XPDFCore::dialogCancelCbk(Widget widget, XtPointer ptr,
1522 			       XtPointer callData) {
1523   XPDFCore *core = (XPDFCore *)ptr;
1524 
1525   core->dialogDone = -1;
1526 }
1527 
1528 //------------------------------------------------------------------------
1529 // password dialog
1530 //------------------------------------------------------------------------
1531 
initPasswordDialog()1532 void XPDFCore::initPasswordDialog() {
1533   Widget row, label, okBtn, cancelBtn;
1534   Arg args[20];
1535   int n;
1536   XmString s;
1537 
1538   //----- dialog
1539   n = 0;
1540   s = XmStringCreateLocalized(xpdfAppName ": Password");
1541   XtSetArg(args[n], XmNdialogTitle, s); ++n;
1542   XtSetArg(args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); ++n;
1543   passwordDialog = XmCreateFormDialog(drawArea, "passwordDialog", args, n);
1544   XmStringFree(s);
1545 
1546   //----- message
1547   n = 0;
1548   XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); ++n;
1549   XtSetArg(args[n], XmNtopOffset, 4); ++n;
1550   XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
1551   XtSetArg(args[n], XmNleftOffset, 4); ++n;
1552   s = XmStringCreateLocalized("This document requires a password.");
1553   XtSetArg(args[n], XmNlabelString, s); ++n;
1554   label = XmCreateLabel(passwordDialog, "msg", args, n);
1555   XmStringFree(s);
1556   XtManageChild(label);
1557 
1558   //----- label and password entry
1559   n = 0;
1560   XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
1561   XtSetArg(args[n], XmNtopWidget, label); ++n;
1562   XtSetArg(args[n], XmNtopOffset, 4); ++n;
1563   XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
1564   XtSetArg(args[n], XmNleftOffset, 4); ++n;
1565   XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
1566   XtSetArg(args[n], XmNleftOffset, 4); ++n;
1567   XtSetArg(args[n], XmNorientation, XmHORIZONTAL); ++n;
1568   XtSetArg(args[n], XmNpacking, XmPACK_TIGHT); ++n;
1569   row = XmCreateRowColumn(passwordDialog, "row", args, n);
1570   XtManageChild(row);
1571   n = 0;
1572   s = XmStringCreateLocalized("Password: ");
1573   XtSetArg(args[n], XmNlabelString, s); ++n;
1574   label = XmCreateLabel(row, "label", args, n);
1575   XmStringFree(s);
1576   XtManageChild(label);
1577   n = 0;
1578   XtSetArg(args[n], XmNcolumns, 16); ++n;
1579   passwordText = XmCreateTextField(row, "text", args, n);
1580   XtManageChild(passwordText);
1581   XtAddCallback(passwordText, XmNmodifyVerifyCallback,
1582 		&passwordTextVerifyCbk, this);
1583 
1584   //----- "Ok" and "Cancel" buttons
1585   n = 0;
1586   XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
1587   XtSetArg(args[n], XmNtopWidget, row); ++n;
1588   XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); ++n;
1589   XtSetArg(args[n], XmNleftOffset, 4); ++n;
1590   XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
1591   XtSetArg(args[n], XmNbottomOffset, 4); ++n;
1592   XtSetArg(args[n], XmNnavigationType, XmEXCLUSIVE_TAB_GROUP); ++n;
1593   okBtn = XmCreatePushButton(passwordDialog, "Ok", args, n);
1594   XtManageChild(okBtn);
1595   XtAddCallback(okBtn, XmNactivateCallback,
1596 		&passwordOkCbk, (XtPointer)this);
1597   n = 0;
1598   XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); ++n;
1599   XtSetArg(args[n], XmNtopWidget, row); ++n;
1600   XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); ++n;
1601   XtSetArg(args[n], XmNrightOffset, 4); ++n;
1602   XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); ++n;
1603   XtSetArg(args[n], XmNbottomOffset, 4); ++n;
1604   XtSetArg(args[n], XmNnavigationType, XmEXCLUSIVE_TAB_GROUP); ++n;
1605   cancelBtn = XmCreatePushButton(passwordDialog, "Cancel", args, n);
1606   XtManageChild(cancelBtn);
1607   XtAddCallback(cancelBtn, XmNactivateCallback,
1608 		&passwordCancelCbk, (XtPointer)this);
1609   n = 0;
1610   XtSetArg(args[n], XmNdefaultButton, okBtn); ++n;
1611   XtSetArg(args[n], XmNcancelButton, cancelBtn); ++n;
1612 #if XmVersion > 1001
1613   XtSetArg(args[n], XmNinitialFocus, passwordText); ++n;
1614 #endif
1615   XtSetValues(passwordDialog, args, n);
1616 }
1617 
passwordTextVerifyCbk(Widget widget,XtPointer ptr,XtPointer callData)1618 void XPDFCore::passwordTextVerifyCbk(Widget widget, XtPointer ptr,
1619 				     XtPointer callData) {
1620   XPDFCore *core = (XPDFCore *)ptr;
1621   XmTextVerifyCallbackStruct *data =
1622       (XmTextVerifyCallbackStruct *)callData;
1623   int i, n;
1624 
1625   i = (int)data->startPos;
1626   n = (int)data->endPos - i;
1627   if (i > core->password->getLength()) {
1628     i = core->password->getLength();
1629   }
1630   if (i + n > core->password->getLength()) {
1631     n = core->password->getLength() - i;
1632   }
1633   core->password->del(i, n);
1634   core->password->insert(i, data->text->ptr, data->text->length);
1635 
1636   for (i = 0; i < data->text->length; ++i) {
1637     data->text->ptr[i] = '*';
1638   }
1639   data->doit = True;
1640 }
1641 
passwordOkCbk(Widget widget,XtPointer ptr,XtPointer callData)1642 void XPDFCore::passwordOkCbk(Widget widget, XtPointer ptr,
1643 			     XtPointer callData) {
1644   XPDFCore *core = (XPDFCore *)ptr;
1645 
1646   core->dialogDone = 1;
1647 }
1648 
passwordCancelCbk(Widget widget,XtPointer ptr,XtPointer callData)1649 void XPDFCore::passwordCancelCbk(Widget widget, XtPointer ptr,
1650 				 XtPointer callData) {
1651   XPDFCore *core = (XPDFCore *)ptr;
1652 
1653   core->dialogDone = -1;
1654 }
1655 
getPassword()1656 GString *XPDFCore::getPassword() {
1657   XtAppContext appContext;
1658   XEvent event;
1659 
1660   // NB: set <password> before calling XmTextFieldSetString, because
1661   // SetString will trigger a call to passwordTextVerifyCbk, which
1662   // expects <password> to be valid
1663   password = new GString();
1664   XmTextFieldSetString(passwordText, "");
1665   XtManageChild(passwordDialog);
1666 
1667   appContext = XtWidgetToApplicationContext(passwordDialog);
1668   dialogDone = 0;
1669   do {
1670     XtAppNextEvent(appContext, &event);
1671     XtDispatchEvent(&event);
1672   } while (!dialogDone);
1673   XtUnmanageChild(passwordDialog);
1674 
1675   if (dialogDone < 0) {
1676     delete password;
1677     return NULL;
1678   }
1679   return password;
1680 }
1681