1 /*========================================================================*\
2
3 Copyright (c) 1990-2004 Paul Vojta
4
5 Permission is hereby granted, free of charge, to any person obtaining a copy
6 of this software and associated documentation files (the "Software"), to
7 deal in the Software without restriction, including without limitation the
8 rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 sell copies of the Software, and to permit persons to whom the Software is
10 furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM,
19 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21 OTHER DEALINGS IN THE SOFTWARE.
22
23 NOTE:
24 This module is based on prior work as noted below.
25
26 \*========================================================================*/
27
28 /*
29 * Support drawing routines for TeXsun and TeX
30 *
31 * Copyright, (C) 1987, 1988 Tim Morgan, UC Irvine
32 * Adapted for xdvi by Jeffrey Lee, U. of Toronto
33 *
34 * At the time these routines are called, the values of hh and vv should
35 * have been updated to the upper left corner of the graph (the position
36 * the \special appears at in the dvi file). Then the coordinates in the
37 * graphics commands are in terms of a virtual page with axes oriented the
38 * same as the Imagen and the SUN normally have:
39 *
40 * 0,0
41 * +-----------> +x
42 * |
43 * |
44 * |
45 * \ /
46 * +y
47 *
48 * Angles are measured in the conventional way, from +x towards +y.
49 * Unfortunately, that reverses the meaning of "counterclockwise"
50 * from what it's normally thought of.
51 *
52 * A lot of floating point arithmetic has been converted to integer
53 * arithmetic for speed. In some places, this is kind-of kludgy, but
54 * it's worth it.
55 */
56
57
58 #include <setjmp.h>
59
60 #include "xdvi-config.h"
61 #include "xdvi.h"
62
63 #include <ctype.h>
64
65 #include "kpathsea/c-fopen.h"
66 #include "kpathsea/c-stat.h"
67 #include "kpathsea/line.h"
68 #include "kpathsea/tex-file.h"
69
70 #include "special.h"
71 #include "hypertex.h"
72 #include "dvi.h"
73 #include "message-window.h"
74 #include "events.h"
75 #include "dvi-init.h"
76 #include "dvi-draw.h"
77 #include "statusline.h"
78 #include "util.h"
79 #include "image-magick.h"
80 #include "pagesel.h"
81 #include "my-snprintf.h"
82 #include "string-utils.h"
83
84 #if PS
85 # ifdef PS_DPS
86 # include "psdps.h"
87 # endif
88 # ifdef PS_NEWS
89 # include "psnews.h"
90 # endif
91 # ifdef PS_GS
92 # include "psgs.h"
93 # endif
94
95 /*
96 Code inside BUG_888087_FIXED improve the appearance of pic specials at magstep 1, but
97 _decreases_ it at other magnifications (bug #888087; see
98 http://sourceforge.net/tracker/index.php?func=detail&aid=888087&group_id=23164&atid=377580)
99 Disabled until this is fixed.
100 */
101 #define BUG_888087_FIXED 0
102
103
104 /*
105 Flag whether this page contains raw PS material;
106 used to warn user about this:
107 */
108 Boolean have_raw_postscript = False;
109
110 # if PS_GS && GS_PIXMAP_CLEARING_HACK
111 /*
112 Flag whether this page contains PS material;
113 used to flush the GS buffer
114 */
115 Boolean had_ps_specials = False;
116 # endif /* PS_GS && GS_PIXMAP_CLEARING_HACK */
117
118 #endif
119
120
121 #if COLOR
122
123 /*
124 * Color stack used when scanning. These records stay around, to ease
125 * the burden on xmalloc(). The first entry is a dummy entry, giving
126 * the default foreground color (as modified).
127 */
128
129 struct colorframe {
130 struct colorframe *next, *prev;
131 struct rgb color;
132 };
133
134 static Boolean m_have_papersize_special = False;
135
reset_papersize_special(void)136 void reset_papersize_special(void)
137 {
138 m_have_papersize_special = False;
139 }
140
have_papersize_special(void)141 Boolean have_papersize_special(void)
142 {
143 return m_have_papersize_special;
144 }
145
146 static struct colorframe scanstack_head;
147 static int scanstack_len;
148 static struct colorframe *scanstack_current;
149
150 /*
151 * Page stack when displaying. Again, the records stay around once
152 * allocated. Bottom entries in the stack (inherited from previous
153 * pages) are stored in an array instead (see comments in xdvi.h).
154 */
155
156 static struct colorframe *rcs_head;
157
158 static Boolean parse_color (const char *cp0, const char *cp, struct rgb *rgbp, Boolean generic_ps_flag);
159
160 #endif /* COLOR */
161
162
163 #ifndef S_IRUSR
164 # define S_IRUSR 0400
165 #endif
166 #ifndef S_IWUSR
167 # define S_IWUSR 0200
168 #endif
169
170 # if HAVE_SYS_WAIT_H
171 # include <sys/wait.h>
172 # endif
173 # ifndef WEXITSTATUS
174 # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
175 # endif
176 # ifndef WIFEXITED
177 # define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
178 # endif
179
180 #define MAXPOINTS 300 /* Max points in a path */
181 #define MAX_PEN_SIZE 7 /* Max pixels of pen width */
182 #define TWOPI (3.14159265359 * 2.0)
183
184 extern double floor(double);
185 #define rint(x) floor((x) + 0.5)
186
187 static int xx[MAXPOINTS], yy[MAXPOINTS]; /* Path in milli-inches */
188 static int path_len = 0; /* # points in current path */
189 static int pen_size = 1; /* Pixel width of lines drawn */
190
191 static Boolean whiten = False;
192 static Boolean shade = False;
193 static Boolean blacken = False;
194
195 Boolean psfig_begun = False;
196
197 #define CMD(x, y) ((x) << 8 | (y))
198
199
200 /*
201 * X drawing routines
202 */
203
204 #define toint(x) ((int) ((x) + 0.5))
205 #define xconv(x) (toint(tpic_conv*(x))/currwin.shrinkfactor + PXL_H)
206 #define yconv(y) (toint(tpic_conv*(y))/currwin.shrinkfactor + PXL_V)
207
208 /*
209 * Draw a line from (fx,fy) to (tx,ty).
210 */
211 static void
line_btw(int fx,int fy,int tx,int ty)212 line_btw(int fx, int fy, int tx, int ty)
213 {
214 int fcx = xconv(fx);
215 int tcx = xconv(tx);
216 int fcy = yconv(fy);
217 int tcy = yconv(ty);
218
219 if ((fcx < globals.win_expose.max_x || tcx < globals.win_expose.max_x) && (fcx >= globals.win_expose.min_x || tcx >= globals.win_expose.min_x) &&
220 (fcy < globals.win_expose.max_y || tcy < globals.win_expose.max_y) && (fcy >= globals.win_expose.min_y || tcy >= globals.win_expose.min_y)) {
221 #if COLOR
222 if (fg_active != fg_current)
223 do_color_change();
224 #endif
225 XDrawLine(DISP, currwin.win, globals.gc.rule,
226 fcx - currwin.base_x, fcy - currwin.base_y,
227 tcx - currwin.base_x, tcy - currwin.base_y);
228 }
229 }
230
231 /*
232 * Draw a dot at (x,y)
233 */
234 static void
dot_at(int x,int y)235 dot_at(int x, int y)
236 {
237 int cx = xconv(x);
238 int cy = yconv(y);
239
240 if (cx < globals.win_expose.max_x && cx >= globals.win_expose.min_x && cy < globals.win_expose.max_y && cy >= globals.win_expose.min_y){
241 #if COLOR
242 if (fg_active != fg_current)
243 do_color_change();
244 #endif
245 XDrawPoint(DISP, currwin.win, globals.gc.rule,
246 cx - currwin.base_x, cy - currwin.base_y);
247 }
248 }
249
250 /*
251 * Apply the requested attributes to the last path (box) drawn.
252 * Attributes are reset.
253 * (Not currently implemented.)
254 */
255 static void
do_attribute_path(int last_min_x,int last_max_x,int last_min_y,int last_max_y)256 do_attribute_path(int last_min_x, int last_max_x, int last_min_y, int last_max_y)
257 {
258 UNUSED(last_min_x);
259 UNUSED(last_max_x);
260 UNUSED(last_min_y);
261 UNUSED(last_max_y);
262 }
263
264 /*
265 * Set the size of the virtual pen used to draw in milli-inches
266 */
267 static void
set_pen_size(char * cp)268 set_pen_size(char *cp)
269 {
270 int ps;
271
272 if (sscanf(cp, " %d ", &ps) != 1) {
273 XDVI_WARNING((stderr, "invalid .ps command format: %s", cp));
274 return;
275 }
276 pen_size = (2 * ps * resource.pixels_per_inch / currwin.shrinkfactor + 1000) / 2000;
277 if (pen_size < 1)
278 pen_size = 1;
279 else if (pen_size > MAX_PEN_SIZE)
280 pen_size = MAX_PEN_SIZE;
281
282 #if BUG_888087_FIXED
283 if (globals.gc.rule) {
284 XGCValues values;
285 values.line_width = pen_size;
286 XChangeGC(DISP, globals.gc.rule, GCLineWidth, &values);
287 }
288 #endif
289 }
290
291
292 /*
293 * Print the line defined by previous path commands
294 */
295
296 static void
flush_path(void)297 flush_path(void)
298 {
299 int i;
300 int last_min_x, last_max_x, last_min_y, last_max_y;
301
302 last_min_x = 30000;
303 last_min_y = 30000;
304 last_max_x = -30000;
305 last_max_y = -30000;
306 for (i = 1; i < path_len; i++) {
307 if (xx[i] > last_max_x)
308 last_max_x = xx[i];
309 if (xx[i] < last_min_x)
310 last_min_x = xx[i];
311 if (yy[i] > last_max_y)
312 last_max_y = yy[i];
313 if (yy[i] < last_min_y)
314 last_min_y = yy[i];
315 line_btw(xx[i], yy[i], xx[i + 1], yy[i + 1]);
316 }
317 if (xx[path_len] > last_max_x)
318 last_max_x = xx[path_len];
319 if (xx[path_len] < last_min_x)
320 last_min_x = xx[path_len];
321 if (yy[path_len] > last_max_y)
322 last_max_y = yy[path_len];
323 if (yy[path_len] < last_min_y)
324 last_min_y = yy[path_len];
325 path_len = 0;
326 do_attribute_path(last_min_x, last_max_x, last_min_y, last_max_y);
327 }
328
329
330 /*
331 * Print a dashed line along the previously defined path, with
332 * the dashes/inch defined.
333 */
334
335 static void
flush_dashed(char * cp,Boolean dotted)336 flush_dashed(char *cp, Boolean dotted)
337 {
338 int i;
339 int numdots;
340 int lx0, ly0, lx1, ly1;
341 int cx0, cy0, cx1, cy1;
342 float inchesperdash;
343 double d, spacesize, a, b = 0.0, dx, dy, milliperdash;
344
345 if (sscanf(cp, " %f ", &inchesperdash) != 1) {
346 XDVI_WARNING((stderr, "invalid format for dotted/dashed line: %s", cp));
347 return;
348 }
349 if (path_len <= 1 || inchesperdash <= 0.0) {
350 XDVI_WARNING((stderr, "invalid conditions for dotted/dashed line"));
351 return;
352 }
353 milliperdash = inchesperdash * 1000.0;
354 lx0 = xx[1];
355 ly0 = yy[1];
356 lx1 = xx[2];
357 ly1 = yy[2];
358 dx = lx1 - lx0;
359 dy = ly1 - ly0;
360 if (dotted) {
361 numdots = sqrt(dx * dx + dy * dy) / milliperdash + 0.5;
362 if (numdots == 0)
363 numdots = 1;
364 for (i = 0; i <= numdots; i++) {
365 a = (float)i / (float)numdots;
366 cx0 = lx0 + a * dx + 0.5;
367 cy0 = ly0 + a * dy + 0.5;
368 dot_at(cx0, cy0);
369 }
370 }
371 else {
372 d = sqrt(dx * dx + dy * dy);
373 numdots = d / (2.0 * milliperdash) + 1.0;
374 if (numdots <= 1)
375 line_btw(lx0, ly0, lx1, ly1);
376 else {
377 spacesize = (d - numdots * milliperdash) / (numdots - 1);
378 for (i = 0; i < numdots - 1; i++) {
379 a = i * (milliperdash + spacesize) / d;
380 b = a + milliperdash / d;
381 cx0 = lx0 + a * dx + 0.5;
382 cy0 = ly0 + a * dy + 0.5;
383 cx1 = lx0 + b * dx + 0.5;
384 cy1 = ly0 + b * dy + 0.5;
385 line_btw(cx0, cy0, cx1, cy1);
386 b += spacesize / d;
387 }
388 cx0 = lx0 + b * dx + 0.5;
389 cy0 = ly0 + b * dy + 0.5;
390 line_btw(cx0, cy0, lx1, ly1);
391 }
392 }
393 path_len = 0;
394 }
395
396
397 /*
398 * Add a point to the current path
399 */
400
401 static void
add_path(char * cp)402 add_path(char *cp)
403 {
404 int pathx, pathy;
405
406 if (++path_len >= MAXPOINTS)
407 XDVI_FATAL((stderr, "Too many points"));
408 if (sscanf(cp, " %d %d ", &pathx, &pathy) != 2)
409 XDVI_FATAL((stderr, "Malformed path command"));
410 xx[path_len] = pathx;
411 yy[path_len] = pathy;
412 }
413
414
415 /*
416 * Draw to a floating point position
417 */
418
419 static void
im_fdraw(double x,double y)420 im_fdraw(double x, double y)
421 {
422 if (++path_len >= MAXPOINTS)
423 XDVI_FATAL((stderr, "Too many arc points"));
424 xx[path_len] = x + 0.5;
425 yy[path_len] = y + 0.5;
426 }
427
428
429 /*
430 * Draw an ellipse with the indicated center and radices.
431 */
432
433 static void
draw_ellipse(int xc,int yc,int xr,int yr)434 draw_ellipse(int xc, int yc, int xr, int yr)
435 {
436 double angle, theta;
437 int n;
438 int px0, py0, px1, py1;
439
440 angle = (xr + yr) / 2.0;
441 theta = sqrt(1.0 / angle);
442 n = TWOPI / theta + 0.5;
443 if (n < 12)
444 n = 12;
445 else if (n > 80)
446 n = 80;
447 n /= 2;
448 theta = TWOPI / n;
449
450 angle = 0.0;
451 px0 = xc + xr; /* cos(0) = 1 */
452 py0 = yc; /* sin(0) = 0 */
453 while ((angle += theta) <= TWOPI) {
454 px1 = xc + xr * cos(angle) + 0.5;
455 py1 = yc + yr * sin(angle) + 0.5;
456 line_btw(px0, py0, px1, py1);
457 px0 = px1;
458 py0 = py1;
459 }
460 line_btw(px0, py0, xc + xr, yc);
461 }
462
463 /*
464 * Draw an arc
465 */
466
467 static void
arc(char * cp,Boolean invis)468 arc(char *cp, Boolean invis)
469 {
470 int xc, yc, xrad, yrad, n;
471 float start_angle, end_angle, angle, theta, r;
472 double xradius, yradius, xcenter, ycenter;
473
474 n = sscanf(cp, " %d %d %d %d %f %f ", &xc, &yc, &xrad, &yrad,
475 &start_angle, &end_angle);
476
477 if (n != 6) {
478 XDVI_WARNING((stderr, "invalid arc specification: %s", cp));
479 return;
480 }
481
482 if (invis)
483 return;
484
485 /* We have a specialized fast way to draw closed circles/ellipses */
486 if (start_angle <= 0.0 && end_angle >= 6.282) {
487 draw_ellipse(xc, yc, xrad, yrad);
488 return;
489 }
490 xcenter = xc;
491 ycenter = yc;
492 xradius = xrad;
493 yradius = yrad;
494 r = (xradius + yradius) / 2.0;
495 theta = sqrt(1.0 / r);
496 #if BUG_888087_FIXED
497 n = (pen_size * TWOPI) / (theta * currwin.shrinkfactor) + 0.5;
498 #else
499 n = 0.3 * TWOPI / theta + 0.5;
500 #endif
501 if (n < 12)
502 n = 12;
503 else if (n > 80)
504 n = 80;
505 n /= 2;
506 theta = TWOPI / n;
507 flush_path();
508 im_fdraw(xcenter + xradius * cos(start_angle),
509 ycenter + yradius * sin(start_angle));
510 angle = start_angle + theta;
511 if (end_angle < start_angle)
512 end_angle += TWOPI;
513 while (angle < end_angle) {
514 im_fdraw(xcenter + xradius * cos(angle),
515 ycenter + yradius * sin(angle));
516 angle += theta;
517 }
518 im_fdraw(xcenter + xradius * cos(end_angle),
519 ycenter + yradius * sin(end_angle));
520 flush_path();
521 }
522
523
524 /*
525 * APPROXIMATE integer distance between two points
526 */
527
528 #define dist(x0, y0, x1, y1) (abs(x0 - x1) + abs(y0 - y1))
529
530
531 /*
532 * Draw a spline along the previously defined path
533 */
534
535 static void
flush_spline(void)536 flush_spline(void)
537 {
538 int xp, yp;
539 int N;
540 int lastx = -1, lasty = -1;
541 Boolean lastvalid = False;
542 int t1, t2, t3;
543 int steps;
544 int j;
545 int i, w;
546
547 N = path_len + 1;
548 xx[0] = xx[1];
549 yy[0] = yy[1];
550 xx[N] = xx[N - 1];
551 yy[N] = yy[N - 1];
552 for (i = 0; i < N - 1; i++) { /* interval */
553 steps = (dist(xx[i], yy[i], xx[i + 1], yy[i + 1]) +
554 dist(xx[i + 1], yy[i + 1], xx[i + 2], yy[i + 2])) / 80;
555 for (j = 0; j < steps; j++) { /* points within */
556 w = (j * 1000 + 500) / steps;
557 t1 = w * w / 20;
558 w -= 500;
559 t2 = (750000 - w * w) / 10;
560 w -= 500;
561 t3 = w * w / 20;
562 xp =
563 (t1 * xx[i + 2] + t2 * xx[i + 1] + t3 * xx[i] + 50000) / 100000;
564 yp =
565 (t1 * yy[i + 2] + t2 * yy[i + 1] + t3 * yy[i] + 50000) / 100000;
566 if (lastvalid)
567 line_btw(lastx, lasty, xp, yp);
568 lastx = xp;
569 lasty = yp;
570 lastvalid = True;
571 }
572 }
573 path_len = 0;
574 }
575
576
577 /*
578 * Shade the last box, circle, or ellipse
579 */
580
581 static void
shade_last(void)582 shade_last(void)
583 {
584 blacken = whiten = False;
585 shade = True;
586 }
587
588
589 /*
590 * Make the last box, circle, or ellipse, white inside (shade with white)
591 */
592
593 static void
whiten_last(void)594 whiten_last(void)
595 {
596 whiten = True;
597 blacken = shade = False;
598 }
599
600
601 /*
602 * Make last box, etc, black inside
603 */
604
605 static void
blacken_last(void)606 blacken_last(void)
607 {
608 blacken = True;
609 whiten = shade = False;
610 }
611
612
613 /*
614 * Code for PostScript<tm> specials begins here.
615 */
616
617 #if PS
618
619 /*
620 * Information on how to search for PS header and figure files.
621 */
622
623 static void ps_startup(int, int, const char *);
624 static void ps_startup2(void);
625
626 /* dummy procedures */
NullProc(void)627 static void NullProc(void) { }
NullProcInt(int flag)628 static void NullProcInt(int flag) { UNUSED(flag); }
NullProcStr(const char * cp)629 static void NullProcStr(const char *cp) { UNUSED(cp); }
630
631
632 struct psprocs psp = { /* used for lazy startup of the ps machinery */
633 /* toggle */ NullProcInt,
634 /* destroy */ NullProc,
635 /* interrupt */ NullProc,
636 /* endpage */ NullProc,
637 /* drawbegin */ ps_startup,
638 /* drawraw */ NullProcStr,
639 /* drawfile */ NULL,
640 /* drawend */ NullProcStr,
641 /* beginheader */ ps_startup2,
642 /* endheader */ NullProc,
643 /* newdoc */ NullProc
644 };
645
646 struct psprocs no_ps_procs = { /* used if postscript is unavailable */
647 /* toggle */ NullProcInt,
648 /* destroy */ NullProc,
649 /* interrupt */ NullProc,
650 /* endpage */ NullProc,
651 /* drawbegin */ drawbegin_none,
652 /* drawraw */ NullProcStr,
653 /* drawfile */ NULL,
654 /* drawend */ NullProcStr,
655 /* beginheader */ NullProc,
656 /* endheader */ NullProc,
657 /* newdoc */ NullProc
658 };
659
660 #endif /* PS */
661
662 #ifdef MAGICK
663 int bbox_angle;
664 Boolean bbox_valid;
665 unsigned int bbox_width;
666 unsigned int bbox_height;
667 int bbox_voffset;
668 #else
669 static int bbox_angle;
670 static Boolean bbox_valid;
671 static unsigned int bbox_width;
672 static unsigned int bbox_height;
673 static int bbox_voffset;
674 #endif
675
676 /* info on bboxes on this page */
677 struct bbox_info {
678 int x;
679 int y;
680 int w;
681 int h;
682 int angle;
683 };
684
685 static struct bbox_info *g_bbox_info = NULL;
686 static size_t g_bbox_info_size = 0;
687 static size_t g_bbox_info_max_size = 0;
688
689 /*
690 Append the current coordinates to g_bbox_info, unless it already
691 contains these coordinates.
692 */
693
694 double bbox_matrix[2][2] = {{1.0, 0.0}, {0.0, 1.0}};
695 Boolean bbox_scaled = False, bbox_rotated = False;
696
697 #define bbox_xscale bbox_matrix[0][0]
698 #define bbox_yscale bbox_matrix[1][1]
699
700 static void
append_bbox_info(int x,int y,int w,int h,int angle)701 append_bbox_info(int x, int y, int w, int h, int angle)
702 {
703 Boolean found = False;
704 size_t i;
705 const size_t SIZE_STEP = 16;
706
707 /* is this box already present? */
708 for (i = 0; i < g_bbox_info_size; i++) {
709 if (g_bbox_info[i].x == x
710 && g_bbox_info[i].y == y
711 && g_bbox_info[i].w == w
712 && g_bbox_info[i].h == h
713 && g_bbox_info[i].angle == angle) {
714 found = True;
715 break;
716 }
717 }
718
719 if (!found) {
720 g_bbox_info_size++;
721
722 while (g_bbox_info_size >= g_bbox_info_max_size) {
723 g_bbox_info_max_size += SIZE_STEP;
724 }
725
726 g_bbox_info = xrealloc(g_bbox_info, g_bbox_info_max_size * sizeof *g_bbox_info);
727 g_bbox_info[g_bbox_info_size - 1].x = x;
728 g_bbox_info[g_bbox_info_size - 1].y = y;
729 g_bbox_info[g_bbox_info_size - 1].w = w;
730 g_bbox_info[g_bbox_info_size - 1].h = h;
731 g_bbox_info[g_bbox_info_size - 1].angle = angle;
732 }
733 }
734
735 static void
draw_bbox0(int xcorner,int ycorner)736 draw_bbox0(int xcorner, int ycorner)
737 {
738 if (bbox_valid) {
739 #if COLOR
740 if (fg_active != fg_current)
741 do_color_change();
742 #endif
743
744 if (! bbox_rotated) {
745 ycorner -= bbox_voffset * bbox_yscale;
746 XDrawRectangle(DISP, currwin.win, globals.gc.high, xcorner, ycorner,
747 bbox_width * bbox_xscale, bbox_height * bbox_yscale);
748 if (resource.postscript == 0) {
749 if (htex_inside_href) {
750 htex_set_anchorsize(xcorner, ycorner, xcorner + bbox_width * bbox_xscale,
751 ycorner + bbox_height * bbox_yscale);
752 htex_set_objecttype(HTEX_IMG);
753 }
754 }
755 }
756 else {
757 float a, b, c, d;
758
759 a = bbox_matrix[0][0] * bbox_width;
760 b = bbox_matrix[1][0] * bbox_width;
761 c = -bbox_matrix[0][1] * bbox_height;
762 d = -bbox_matrix[1][1] * bbox_height;
763
764 XDrawLine(DISP, currwin.win, globals.gc.high,
765 xcorner, ycorner,
766 xcorner + (int)rint(a), ycorner + (int)rint(b));
767 XDrawLine(DISP, currwin.win, globals.gc.high,
768 xcorner + (int)rint(a), ycorner + (int)rint(b),
769 xcorner + (int)rint(a + c), ycorner + (int)rint(b + d));
770 XDrawLine(DISP, currwin.win, globals.gc.high,
771 xcorner + (int)rint(a + c), ycorner + (int)rint(b + d),
772 xcorner + (int)rint(c), ycorner + (int)rint(d));
773 XDrawLine(DISP, currwin.win, globals.gc.high,
774 xcorner + (int)rint(c), ycorner + (int)rint(d),
775 xcorner, ycorner);
776 }
777 bbox_valid = False;
778 }
779 }
780
781 /*
782 Display the items in bbox_info.
783 Invoked from draw_page() in dvi-draw.c.
784 */
785 void
display_bboxes(void)786 display_bboxes(void)
787 {
788 size_t i;
789
790 for (i = 0; i < g_bbox_info_size; i++) {
791 if (globals.debug & DBG_PS) {
792 fprintf(stderr, "drawing bbox %lu at %d %d, %d x %d, angle %d\n",
793 (unsigned long)i,
794 g_bbox_info[i].x,
795 g_bbox_info[i].y,
796 g_bbox_info[i].w,
797 g_bbox_info[i].h,
798 g_bbox_info[i].angle);
799 }
800 #if 0
801 XDrawRectangle(DISP, currwin.win, globals.gc.high,
802 g_bbox_info[i].x,
803 g_bbox_info[i].y,
804 g_bbox_info[i].w,
805 g_bbox_info[i].h);
806 #else
807 bbox_valid = True;
808 bbox_width = g_bbox_info[i].w;
809 bbox_height = bbox_voffset = g_bbox_info[i].h;
810 bbox_angle = g_bbox_info[i].angle;
811 draw_bbox0(g_bbox_info[i].x, g_bbox_info[i].y + bbox_height);
812 #endif
813 }
814 bbox_angle = 0;
815 bbox_valid = False;
816 }
817
818 void
clear_bboxes(void)819 clear_bboxes(void)
820 {
821 free(g_bbox_info);
822 g_bbox_info = NULL;
823 g_bbox_info_size = 0;
824 g_bbox_info_max_size = 0;
825 }
826
827 void
save_bbox(void)828 save_bbox(void)
829 {
830 int xcorner, ycorner;
831
832 if (bbox_valid) {
833 xcorner = PXL_H - currwin.base_x;
834 ycorner = PXL_V - currwin.base_y;
835
836 ycorner -= bbox_voffset;
837 append_bbox_info(xcorner, ycorner, bbox_width, bbox_height, bbox_angle);
838 if (currwin.win == mane.win && resource.postscript != 1) {
839 draw_bbox();
840 }
841
842 /* register boundaries of this box as anchor boundaries */
843 if (htex_inside_href) {
844 htex_set_anchorsize(xcorner, ycorner, xcorner + bbox_width, ycorner + bbox_height);
845 htex_set_objecttype(HTEX_IMG);
846 }
847 }
848 }
849
850
851 void
draw_bbox(void)852 draw_bbox(void)
853 {
854 draw_bbox0(PXL_H - currwin.base_x, PXL_V - currwin.base_y);
855 }
856
857 #if PS
858
859 static XIOErrorHandler oldhandler;
860
XDviIOErrorHandler(Display * disp)861 static int XDviIOErrorHandler(Display * disp)
862 {
863 ps_destroy();
864 return oldhandler(disp);
865 }
866
867 static void
actual_startup(int flag)868 actual_startup(int flag)
869 {
870 UNUSED(flag);
871 oldhandler = XSetIOErrorHandler(XDviIOErrorHandler);
872
873 /*
874 * Figure out what we want to use to display postscript figures
875 * and set at most one of the following to True:
876 * resource.useGS, resource.useDPS, resource.useNeWS
877 *
878 * Choose DPS then NEWS then Ghostscript if they are available
879 */
880 if (!(
881 #ifdef PS_DPS
882 (resource.useDPS && initDPS())
883 #if defined(PS_NEWS) || defined(PS_GS)
884 ||
885 #endif
886 #endif /* PS_DPS */
887 #ifdef PS_NEWS
888 (resource.useNeWS && initNeWS())
889 #ifdef PS_GS
890 ||
891 #endif
892 #endif /* PS_NEWS */
893 #ifdef PS_GS
894 (resource.useGS && initGS())
895 #endif
896 ))
897 psp = no_ps_procs;
898 }
899
900 static void
ps_startup(int xul,int yul,const char * cp)901 ps_startup(int xul, int yul, const char *cp)
902 {
903 if (resource.postscript == 0) {
904 draw_bbox();
905 return;
906 }
907 actual_startup(0);
908 psp.drawbegin(xul, yul, cp);
909 }
910
911 static void
ps_startup2(void)912 ps_startup2(void)
913 {
914 actual_startup(0);
915 psp.beginheader();
916 }
917
918
919 typedef struct {
920 double matrix[2][2];
921 double x, y;
922 } ROTATE_STACK;
923
924 /*
925 * dumb parsing of PostScript - search for rotation H. Zeller 1/97
926 * Returns true if we find a potentially non-supported command that
927 * we want to warn users about.
928 */
929 static Boolean
ps_parseraw(const char * PostScript_cmd)930 ps_parseraw(const char *PostScript_cmd)
931 {
932 const char *p;
933 double xscale, yscale, angle, sin_a, cos_a, a, b, c, d;
934 int n;
935 static int rotate_stack_len = 0;
936 static int rotate_stack_ptr = 0;
937 static ROTATE_STACK *rotate_stack = NULL;
938
939 p = strstr(PostScript_cmd, "currentpoint grestore moveto");
940 if (p != NULL) {
941 if (rotate_stack_ptr > 0) {
942 double r;
943 rotate_stack_ptr--;
944 r = sqrt(((DVI_H - rotate_stack[rotate_stack_ptr].x)
945 * (DVI_H - rotate_stack[rotate_stack_ptr].x)
946 + (DVI_V - rotate_stack[rotate_stack_ptr].y)
947 * (DVI_V - rotate_stack[rotate_stack_ptr].y))
948 / (bbox_matrix[0][0] * bbox_matrix[0][0]
949 + bbox_matrix[1][0] * bbox_matrix[1][0]))
950 * (rotate_stack[rotate_stack_ptr].matrix[0][0]
951 * bbox_matrix[1][0]
952 - rotate_stack[rotate_stack_ptr].matrix[1][0]
953 * bbox_matrix[0][0])
954 / (rotate_stack[rotate_stack_ptr].matrix[0][0]
955 * rotate_stack[rotate_stack_ptr].matrix[0][0]
956 + rotate_stack[rotate_stack_ptr].matrix[1][0]
957 * rotate_stack[rotate_stack_ptr].matrix[1][0]);
958 DVI_H += r * rotate_stack[rotate_stack_ptr].matrix[1][0];
959 DVI_V += r * rotate_stack[rotate_stack_ptr].matrix[0][0];
960 PXL_V = pixel_conv(DVI_V);
961 bcopy(rotate_stack[rotate_stack_ptr].matrix, bbox_matrix,
962 sizeof(bbox_matrix));
963 bbox_rotated
964 = (bbox_matrix[0][1] != 0.0 || bbox_matrix[1][0] != 0.0)
965 ? True : False;
966 bbox_scaled
967 = (bbox_rotated
968 || bbox_matrix[0][0] != 1.0 || bbox_matrix[1][1] != 1.0)
969 ? True : False;
970 }
971 if (globals.debug & DBG_PS)
972 fprintf(stderr, "current angle %f: %s\n",
973 bbox_matrix[0][0] != 0
974 ? -atan(bbox_matrix[1][0] / bbox_matrix[0][0])
975 * (360 / TWOPI)
976 : bbox_matrix[1][0] >= 0 ? (double)90 : (double)-90,
977 PostScript_cmd);
978 }
979
980 bbox_angle = 0;
981 p = strstr(PostScript_cmd, "rotate");
982 if (p != NULL) {
983 while (*p != '\0' && !isdigit((int)*p))
984 --p;
985 while (*p != '\0' && isdigit((int)*p))
986 --p;
987 if (*p == '.') --p;
988 while (*p != '\0' && isdigit(*p)) --p;
989 if (*p != '+' && *p != '-')
990 ++p;
991 n = sscanf(p, "%lf neg rotate", &angle);
992 if (n != 1) {
993 fprintf(stderr, "%s: cannot get angle.\n", globals.program_name);
994 angle = 0.0;
995 }
996 bbox_angle = angle;
997 if (rotate_stack_ptr >= rotate_stack_len) {
998 rotate_stack_len += 0x10;
999 rotate_stack = (ROTATE_STACK *)
1000 xrealloc(rotate_stack,
1001 sizeof(ROTATE_STACK) * rotate_stack_len);
1002 }
1003 bcopy(bbox_matrix, rotate_stack[rotate_stack_ptr].matrix,
1004 sizeof(bbox_matrix));
1005 rotate_stack[rotate_stack_ptr].x = DVI_H;
1006 rotate_stack[rotate_stack_ptr].y = DVI_V;
1007 rotate_stack_ptr++;
1008
1009 sin_a = sin(-angle * (TWOPI / 360));
1010 cos_a = cos(-angle * (TWOPI / 360));
1011 a = bbox_matrix[0][0] * cos_a + bbox_matrix[0][1] * sin_a;
1012 b = - bbox_matrix[0][0] * sin_a + bbox_matrix[0][1] * cos_a;
1013 c = bbox_matrix[1][0] * cos_a + bbox_matrix[1][1] * sin_a;
1014 d = - bbox_matrix[1][0] * sin_a + bbox_matrix[1][1] * cos_a;
1015 bbox_matrix[0][0] = a;
1016 bbox_matrix[0][1] = b;
1017 bbox_matrix[1][0] = c;
1018 bbox_matrix[1][1] = d;
1019 bbox_rotated = (b != 0.0 || c != 0.0) ? True : False;
1020 bbox_scaled
1021 = (bbox_rotated
1022 || bbox_matrix[0][0] != 1.0 || bbox_matrix[1][1] != 1.0)
1023 ? True : False;
1024 if (globals.debug & DBG_PS)
1025 fprintf(stderr, "current angle %f: %s\n",
1026 bbox_matrix[0][0] != 0
1027 ? -atan(bbox_matrix[1][0] / bbox_matrix[0][0])
1028 * (360 / TWOPI)
1029 : bbox_matrix[1][0] >= 0 ? (double)90 : (double)-90,
1030 PostScript_cmd);
1031 }
1032
1033 p = strstr(PostScript_cmd, "scale");
1034 if (p != NULL) {
1035 if (strncmp(p - 4, "div", 3) == 0) {
1036 while (*p != '\0' && !isdigit(*p)) --p;
1037 while (*p != '\0' && isdigit(*p)) --p;
1038 if (*p == '.') --p;
1039 while (*p != '\0' && isdigit(*p)) --p;
1040 while (*p != '\0' && !isdigit(*p)) --p;
1041 while (*p != '\0' && isdigit(*p)) --p;
1042 while (*p != '\0' && !isdigit(*p)) --p;
1043 while (*p != '\0' && isdigit(*p)) --p;
1044 if (*p == '.') --p;
1045 while (*p != '\0' && isdigit(*p)) --p;
1046 while (*p != '\0' && !isdigit(*p)) --p;
1047 while (*p != '\0' && isdigit(*p)) --p;
1048 ++p;
1049 n = sscanf(p, "1 %lf div 1 %lf div scale", &xscale, &yscale);
1050 if (n == 2) {
1051 #ifdef PTEX
1052 if(TATE) {
1053 double temp;
1054 temp = xscale;
1055 xscale = 1.0/yscale;
1056 yscale = 1.0/temp;
1057 }
1058 else {
1059 xscale = 1.0/xscale;
1060 yscale = 1.0/yscale;
1061 }
1062 #else
1063 xscale = 1.0/xscale;
1064 yscale = 1.0/yscale;
1065 #endif
1066 } else {
1067 fprintf(stderr,
1068 "%s: cannot get scaling factors.\n",
1069 globals.program_name);
1070 xscale = yscale = 1.0;
1071 }
1072 } else {
1073 while (*p != '\0' && !isdigit(*p)) --p;
1074 while (*p != '\0' && isdigit(*p)) --p;
1075 if (*p == '.') --p;
1076 while (*p != '\0' && isdigit(*p)) --p;
1077 while (*p != '\0' && !isdigit(*p)) --p;
1078 while (*p != '\0' && isdigit(*p)) --p;
1079 if (*p == '.') --p;
1080 while (*p != '\0' && isdigit(*p)) --p;
1081 if (*p != '+' && *p != '-') ++p;
1082 n = sscanf(p, "%lf %lf scale", &xscale, &yscale);
1083 #ifdef PTEX
1084 if(TATE) {
1085 double temp;
1086 temp = xscale;
1087 xscale = yscale;
1088 yscale = temp;
1089 }
1090 #endif
1091 if (n != 2) {
1092 fprintf(stderr,
1093 "%s: cannot get scaling factors.\n",
1094 globals.program_name);
1095 xscale = yscale = 1.0;
1096 }
1097 }
1098 bbox_matrix[0][0] *= xscale;
1099 bbox_matrix[0][1] *= yscale;
1100 bbox_matrix[1][0] *= xscale;
1101 bbox_matrix[1][1] *= yscale;
1102 bbox_scaled
1103 = (bbox_rotated
1104 || bbox_matrix[0][0] != 1.0 || bbox_matrix[1][1] != 1.0)
1105 ? True : False;
1106 if (globals.debug & DBG_PS)
1107 fprintf(stderr, "current scale %f,%f: %s\n",
1108 bbox_xscale, bbox_yscale, PostScript_cmd);
1109 angle = 0.0;
1110 if (strstr(PostScript_cmd, "neg rotate")) {
1111 n = sscanf(p, "%lf neg rotate", &angle);
1112 if (n != 1) {
1113 fprintf(stderr, "%s: cannot get angle. (%s)\n", globals.program_name,p);
1114 angle = 0.0;
1115 }
1116 }
1117 bbox_angle = angle;
1118 return True;
1119 }
1120 if (strstr(PostScript_cmd, " scale ") != NULL)
1121 return True;
1122 return False;
1123 }
1124
1125
1126 void
drawbegin_none(int xul,int yul,const char * cp)1127 drawbegin_none(int xul, int yul, const char *cp)
1128 {
1129 UNUSED(xul);
1130 UNUSED(yul);
1131 UNUSED(cp);
1132
1133 draw_bbox();
1134 }
1135
1136
1137 struct tickrec {
1138 struct tickrec *next;
1139 int pageno;
1140 char *command;
1141 char *tempname;
1142 };
1143
1144 static struct tickrec *tickhead = NULL; /* head of linked list of */
1145 /* cached information */
1146 static int nticks = 0; /* number of records total */
1147
1148 #ifndef TICKCACHESIZE
1149 #define TICKCACHESIZE 3
1150 #endif
1151
1152 #ifndef TICKTMP
1153 #define TICKTMP "/tmp"
1154 #endif
1155
1156 /*
1157 * cachetick() - returns:
1158 * NULL error;
1159 * fp == NULL string was not in cache, fd = file
1160 * fp != NULL string was in cache, fp = cached file
1161 */
1162
1163 static struct tickrec *
cachetick(const char * filename,kpse_file_format_type pathinfo,FILE ** fp,int * fdp)1164 cachetick(const char *filename, kpse_file_format_type pathinfo, FILE **fp, int *fdp)
1165 {
1166 struct tickrec **linkp;
1167 struct tickrec *tikp;
1168 struct tickrec **freerecp;
1169
1170 linkp = &tickhead;
1171 freerecp = NULL;
1172 for (;;) { /* see if we have it already */
1173 tikp = *linkp;
1174 if (tikp == NULL) { /* if end of list */
1175 int fd;
1176
1177 if (nticks >= TICKCACHESIZE && freerecp != NULL) {
1178 tikp = *freerecp;
1179 *freerecp = tikp->next;
1180 free(tikp->command);
1181 unlink(tikp->tempname);
1182 /* reuse tikp and tikp->tempname */
1183 }
1184 else {
1185 tikp = xmalloc(sizeof(struct tickrec));
1186 tikp->tempname = NULL;
1187 ++nticks;
1188 }
1189 fd = xdvi_temp_fd(&tikp->tempname);
1190 if (fd == -1) {
1191 perror("Cannot create temporary file");
1192 free(tikp->tempname);
1193 free(tikp);
1194 return NULL;
1195 }
1196 tikp->command = xstrdup(filename);
1197 *fp = NULL;
1198 *fdp = fd;
1199 break;
1200 }
1201 if (strcmp(filename, tikp->command) == 0) { /* found it */
1202 *linkp = tikp->next; /* unlink it */
1203 *fp = XFOPEN(tikp->tempname, OPEN_MODE);
1204 if (*fp == NULL) {
1205 perror(tikp->tempname);
1206 free(tikp->tempname);
1207 free(tikp->command);
1208 free(tikp);
1209 return NULL;
1210 }
1211 break;
1212 }
1213 if (tikp->pageno != current_page)
1214 freerecp = linkp;
1215 linkp = &tikp->next;
1216 }
1217 tikp->next = tickhead; /* link it in */
1218 tickhead = tikp;
1219 tikp->pageno = pathinfo != kpse_tex_ps_header_format ? current_page : -1;
1220 return tikp;
1221 }
1222
1223 void
ps_clear_cache(void)1224 ps_clear_cache(void)
1225 {
1226 struct tickrec *tikp;
1227
1228 while (tickhead != NULL) {
1229 tikp = tickhead;
1230 tickhead = tickhead->next;
1231 free(tikp->command);
1232 unlink(tikp->tempname);
1233 free((char *)tikp->tempname);
1234 free(tikp);
1235 }
1236 nticks = 0;
1237 }
1238
1239 #ifndef UNCOMPRESS
1240 #define UNCOMPRESS "uncompress"
1241 #endif
1242
1243 #ifndef GUNZIP
1244 #define GUNZIP "gunzip"
1245 #endif
1246
1247 #ifndef BUNZIP2
1248 #define BUNZIP2 "bunzip2"
1249 #endif
1250
1251 static void send_ps_file(const char *filename, kpse_file_format_type pathinfo);
1252
1253 static void
enable_specials_send_ps_file(XtPointer data)1254 enable_specials_send_ps_file(XtPointer data)
1255 {
1256 const char *filename = (const char *)data;
1257 resource.allow_shell = True;
1258 redraw_page(); /* to erase the bounding box */
1259 statusline_info(STATUS_MEDIUM,
1260 "Shell specials enabled for this session.",
1261 filename);
1262 send_ps_file(filename, kpse_pict_format);
1263 }
1264
1265 static void
try_open_tempname(int status,struct xchild * this)1266 try_open_tempname(int status, struct xchild *this)
1267 {
1268 if (WIFEXITED(status)) { /* child exited normally */
1269 if (WEXITSTATUS(status) != 0) {
1270 /* default error procedure */
1271 handle_child_exit(status, this);
1272 }
1273 else {
1274 struct tickrec *tikp = (struct tickrec *)this->data;
1275 FILE *f = XFOPEN(tikp->tempname, OPEN_MODE);
1276 fprintf(stderr, "FILE: %s\n", tikp->tempname);
1277 if (f == NULL) {
1278 perror(tikp->tempname);
1279 }
1280 else {
1281 fprintf(stderr, "sending file: %s, %p\n", tikp->tempname, (void *)f);
1282 /* There's no point in invoking
1283 psp.drawfile(tikp->tempname, f);
1284 here, since usually it will be already too late (draw_part() which
1285 had called us via applicationDoSpecial() will already have terminated).
1286 So instead, we just close the file and force a redraw of the entire page.
1287 */
1288 fclose(f);
1289 globals.ev.flags |= EV_NEWPAGE;
1290 }
1291 }
1292 }
1293 else if (WIFSIGNALED(status)) {
1294 popup_message(globals.widgets.top_level,
1295 MSG_WARN,
1296 NULL,
1297 "Process `%s' terminated abnormally with signal %d.", this->name, WTERMSIG(status));
1298 }
1299 else if (WIFSTOPPED(status)) {
1300 popup_message(globals.widgets.top_level,
1301 MSG_WARN,
1302 NULL,
1303 "Process `%s' stopped by signal %d.", this->name, WSTOPSIG(status));
1304 }
1305 else {
1306 popup_message(globals.widgets.top_level,
1307 MSG_WARN,
1308 NULL,
1309 "Process `%s' terminated with unknown status.", this->name);
1310 }
1311 }
1312
1313 static void
send_ps_file(const char * filename,kpse_file_format_type pathinfo)1314 send_ps_file(const char *filename, kpse_file_format_type pathinfo)
1315 {
1316 FILE *f;
1317 int fd;
1318 static const char *argv[] = { NULL, "-c", NULL, NULL };
1319 char *bufp = NULL;
1320 struct tickrec *volatile tikp;
1321 size_t len;
1322 char magic1, magic2, magic3;
1323 static Boolean warned_about_shellescape = False;
1324 static size_t ffline_len = 0;
1325 static char *ffline = NULL;
1326 const size_t FFLINE_STEP = 128;
1327
1328 if (psp.drawfile == NULL || resource.postscript == 0) {
1329 return;
1330 }
1331
1332 if (filename[0] == '`') {
1333 if (!resource.allow_shell) {
1334 if (!warned_about_shellescape) {
1335 choice_dialog_sized(globals.widgets.top_level,
1336 MSG_INFO,
1337 SIZE_MEDIUM,
1338 /* helptext */
1339 "To enable shell specials, use the \"-allowshell\" command "
1340 "line option.\n\n"
1341 "WARNING: You should NOT use shell specials like\n"
1342 "`gunzip -c file.eps.gz\n"
1343 "(e.g. via \\DeclareGraphicsRule{...}) "
1344 "to uncompress .eps.gz files, even though some obsolete "
1345 "LaTeX documentation might suggest it. Current versions "
1346 "of xdvi and dvips will handle .eps.gz files just fine without this trick.\n",
1347 #ifndef MOTIF
1348 NULL,
1349 #endif
1350 NULL, NULL, /* no pre_callbacks */
1351 /* Cancel label/callback */
1352 "Cancel", NULL, (XtPointer)NULL,
1353 /* Enable label/callback */
1354 "Enable", enable_specials_send_ps_file, (XtPointer)filename,
1355 /* msg */
1356 "This page contains a shell special \"%s\", but execution of shell specials "
1357 "is disabled.\n\nYou can now click "
1358 "\"Enable\" to enable shell specials for this session only, "
1359 "\"Cancel\" to leave the specials disabled, or \"Help\" for more help "
1360 "on this topic.\n\n"
1361 "Please note that shell specials are a security risk, since they "
1362 "allow execution of arbitrary shell commands from the TeX file. "
1363 "You should enable them only for DVI files that you created yourself.",
1364 filename);
1365 warned_about_shellescape = True;
1366 }
1367 else {
1368 statusline_error(STATUS_MEDIUM,
1369 "Info: Shell special \"%s\" disabled.",
1370 filename);
1371 }
1372 draw_bbox();
1373 return;
1374 }
1375
1376 tikp = cachetick(filename, pathinfo, &f, &fd);
1377 if (tikp == NULL) /* if error */
1378 return;
1379 if (f == NULL) {
1380 char *argv[4];
1381 /* if not cached, need to create.
1382 Fork so that we can collect the process' error messages. */
1383
1384 /* FIXME: Insecure - this is a /tmp race; shouldn't close(fd), just re-use it */
1385 close(fd);
1386
1387 len = strlen(filename) + strlen(tikp->tempname) + (4 - 1);
1388 if (len > ffline_len) {
1389 ffline_len += FFLINE_STEP;
1390 ffline = xrealloc(ffline, ffline_len);
1391 }
1392
1393 sprintf(ffline, "%s > %s", filename + 1, tikp->tempname);
1394 argv[0] = "/bin/sh";
1395 argv[1] = "-c";
1396 argv[2] = ffline;
1397 argv[3] = NULL;
1398 fork_process("/bin/sh", False, globals.dvi_file.dirname, try_open_tempname, tikp, argv);
1399 }
1400 else {
1401 bufp = tikp->tempname;
1402 psp.drawfile(bufp, f);
1403 }
1404 }
1405 else {
1406 char *expanded_filename = NULL;
1407 struct stat statbuf;
1408 expanded_filename = find_file(filename, &statbuf, pathinfo);
1409
1410 if (expanded_filename == NULL &&
1411 (pathinfo == kpse_enc_format || pathinfo == kpse_type1_format)) {
1412 /* in this case, we also kpathsea-search in the `old' place for
1413 backwards compatibility: kpse_tex_ps_header_format (see comment
1414 for load_vector(), dvi-draw.c for details) */
1415 expanded_filename = kpse_find_file(filename, kpse_tex_ps_header_format, True);
1416 }
1417 if (expanded_filename == NULL) {
1418 expanded_filename = kpse_find_file(filename, kpse_program_text_format, True);
1419 }
1420 if (expanded_filename == NULL) { /* still no success, complain */
1421 /* FIXME: this warning may be overwritten by warning about raw PS specials,
1422 so additinally dump to stderr. TODO: make statusline printing respect
1423 more important messages. */
1424 XDVI_WARNING((stderr, "Could not find graphics file \"%s\"", filename));
1425 statusline_info(STATUS_MEDIUM, "Warning: Could not find graphics file \"%s\"",
1426 filename);
1427 draw_bbox();
1428 return;
1429 }
1430 if (globals.debug & DBG_OPEN)
1431 printf("%s:%d: |%s| expanded to |%s|\n", __FILE__, __LINE__, filename, expanded_filename);
1432
1433 /* may need to adjust ffline_len, ffline */
1434 while (strlen(expanded_filename) + 1 > ffline_len) {
1435 ffline_len += FFLINE_STEP;
1436 ffline = xrealloc(ffline, ffline_len);
1437 }
1438 strcpy(ffline, expanded_filename);
1439 bufp = ffline;
1440 f = XFOPEN(expanded_filename, OPEN_MODE);
1441 if (f == NULL) {
1442 XDVI_WARNING((stderr, "Could not open graphics file \"%s\": %s", expanded_filename, strerror(errno)));
1443 statusline_info(STATUS_MEDIUM, "Warning: Could not open graphics file \"%s\": %s",
1444 expanded_filename, strerror(errno));
1445 free(expanded_filename);
1446 draw_bbox();
1447 return;
1448 }
1449 free(expanded_filename);
1450
1451 /* check for compressed files */
1452 len = strlen(filename);
1453 magic1 = '\037';
1454 magic3 = '\0';
1455 if ((len > 2 && strcmp(filename + len - 2, ".Z") == 0
1456 && (argv[0] = UNCOMPRESS, magic2 = '\235', True))
1457 || (len > 3 && strcmp(filename + len - 3, ".gz") == 0
1458 && (argv[0] = GUNZIP, magic2 = '\213', True))
1459 || (len > 4 && strcmp(filename + len - 4, ".bz2") == 0
1460 && (argv[0] = BUNZIP2, magic1 = 'B', magic2 = 'Z',
1461 magic3 = 'h', True))) {
1462 if (getc(f) != magic1 || (char)getc(f) != magic2
1463 || (magic3 != '\0' && getc(f) != magic3)) {
1464 rewind(f);
1465 }
1466 else {
1467 fclose(f);
1468
1469 tikp = cachetick(filename, pathinfo, &f, &fd);
1470 if (tikp == NULL) /* if error */
1471 return;
1472 if (f == NULL) { /* if not cached, need to create */
1473 pid_t pid;
1474 int status;
1475
1476 argv[2] = bufp;
1477 fflush(stderr); /* avoid double flushing */
1478 pid = vfork();
1479 if (pid == 0) { /* if child */
1480 (void)dup2(fd, 1);
1481 (void)execvp(argv[0], (char **)argv);
1482 XDVI_ERROR((stderr, "Execvp of %s failed: %s", argv[0], strerror(errno)));
1483 _exit(EXIT_FAILURE);
1484 }
1485 (void)close(fd);
1486
1487 for (;;) {
1488 #if HAVE_WAITPID
1489 if (waitpid(pid, &status, 0) != -1)
1490 break;
1491 #else
1492 # if HAVE_WAIT4
1493 if (wait4(pid, &status, 0, (struct rusage *)NULL)
1494 != -1)
1495 break;
1496 # else
1497 int retval;
1498
1499 retval = wait(&status);
1500 if (retval == pid)
1501 break;
1502 if (retval != -1)
1503 continue;
1504 # endif /* HAVE_WAIT4 */
1505 #endif /* HAVE_WAITPID */
1506 if (errno == EINTR)
1507 continue;
1508 perror("[xdvik] waitpid");
1509 return;
1510 }
1511 f = XFOPEN(tikp->tempname, OPEN_MODE);
1512 if (f == NULL) {
1513 perror(tikp->tempname);
1514 return;
1515 }
1516 }
1517 bufp = tikp->tempname;
1518 }
1519 }
1520 /* Success! */
1521 psp.drawfile(bufp, f); /* this is supposed to close the file */
1522 }
1523 }
1524
1525
1526 void
ps_destroy(void)1527 ps_destroy(void)
1528 {
1529 struct tickrec *tikp;
1530
1531 /* Note: old NeXT systems (at least) lack atexit/on_exit. */
1532 psp.destroy();
1533 for (tikp = tickhead; tikp != NULL; tikp = tikp->next)
1534 if (unlink(tikp->tempname) < 0)
1535 perror(tikp->tempname);
1536 }
1537
1538 #endif /* PS */
1539
1540
1541 void
init_prescan(void)1542 init_prescan(void)
1543 {
1544 #if PS
1545 struct tickrec *tikp;
1546 #endif
1547
1548 scanned_page = scanned_page_reset = resource.prescan ? -1 : total_pages + 1;
1549 #if PS
1550 scanned_page_ps = scanned_page_ps_bak = scanned_page;
1551 #if COLOR
1552 scanned_page_color = scanned_page;
1553 #endif /* COLOR */
1554 #endif /* PS */
1555
1556 TRACE_FILES((stderr, "init_prescan: scanned_page = %d", scanned_page));
1557 #if PS
1558 if (resource.postscript == 0)
1559 scanned_page_ps = total_pages + 1;
1560
1561 for (tikp = tickhead; tikp != NULL; tikp = tikp->next)
1562 tikp->pageno = -1;
1563 psp.newdoc();
1564 #endif
1565
1566 #if COLOR
1567 if (!resource.use_color)
1568 scanned_page_color = total_pages + 1;
1569 #endif
1570
1571 if (ignore_papersize_specials) {
1572 #if PS && COLOR
1573 scanned_page = scanned_page_ps < scanned_page_color ? scanned_page_ps : scanned_page_color;
1574 #elif PS
1575 scanned_page = scanned_page_ps;
1576 #elif COLOR
1577 scanned_page = scanned_page_color;
1578 #else
1579 scanned_page = total_pages + 1;
1580 #endif
1581 }
1582 }
1583
1584
1585 static void
psfig_special(char * cp)1586 psfig_special(char *cp)
1587 {
1588 char *filename;
1589 int raww, rawh;
1590
1591 if (strncmp(cp, ":[begin]", 8) == 0) {
1592 cp += 8;
1593 bbox_valid = False;
1594 bbox_angle = 0;
1595 if (sscanf(cp, "%d %d\n", &raww, &rawh) >= 2) {
1596 bbox_valid = True;
1597 bbox_width = pixel_conv(spell_conv(raww));
1598 bbox_height = pixel_conv(spell_conv(rawh));
1599 bbox_voffset = 0;
1600 }
1601 if (INSIDE_MANE_WIN) {
1602 #if PS
1603 psp.drawbegin(PXL_H - currwin.base_x, PXL_V - currwin.base_y, cp);
1604 #else
1605 draw_bbox();
1606 #endif
1607 }
1608 psfig_begun = True;
1609 }
1610 else if (strncmp(cp, " plotfile ", 10) == 0) {
1611 cp += 10;
1612 while (isspace((int)*cp))
1613 cp++;
1614 /* handle "`zcat file". Borrowed from dvipsk... */
1615 if (*cp == '"') {
1616 cp++;
1617 for (filename = cp; *cp && (*cp != '"'); ++cp);
1618 }
1619 else {
1620 for (filename = cp; *cp && !isspace((int)*cp); ++cp);
1621 }
1622 *cp = '\0';
1623 #if PS
1624 if (INSIDE_MANE_WIN)
1625 send_ps_file(filename, kpse_pict_format);
1626 #endif
1627 }
1628 else if (strncmp(cp, ":[end]", 6) == 0) {
1629 cp += 6;
1630 #if PS
1631 if (INSIDE_MANE_WIN) {
1632 psp.drawend(cp);
1633 }
1634 #endif
1635 bbox_valid = False;
1636 psfig_begun = False;
1637 }
1638 else if (*cp == ':') {
1639 /* I am going to send some raw postscript stuff */
1640 ++cp; /* skip the colon */
1641 #if PS
1642 if (ps_parseraw(cp))
1643 have_raw_postscript = True;
1644 if (INSIDE_MANE_WIN)
1645 psp.drawraw(cp);
1646 #endif
1647 }
1648 else { /* attempt to parse pstricks color specials */
1649 #if COLOR
1650 struct rgb color;
1651 if (parse_color(cp, cp, &color, True)) {
1652 /* clear stack */
1653 if (rcs_head == NULL) {
1654 rcs_head = xmalloc(sizeof *rcs_head);
1655 rcs_head->prev = rcs_head->next = NULL;
1656 }
1657 rcs_top = rcs_head;
1658 color_bot_size = 0;
1659
1660 /* Change top of stack */
1661 rcs_top->color = color;
1662
1663 set_fg_color(&color);
1664 }
1665 #endif
1666 /* also raw PostScript, but no extra colon to skip */
1667 #if PS
1668 if (ps_parseraw(cp) && INSIDE_MANE_WIN)
1669 have_raw_postscript = True;
1670
1671 if (INSIDE_MANE_WIN) {
1672 if (psfig_begun)
1673 psp.drawraw(cp);
1674 else {
1675 psp.drawbegin(PXL_H - currwin.base_x,
1676 PXL_V - currwin.base_y, cp);
1677 psp.drawend("");
1678 }
1679 }
1680 #endif
1681 }
1682 }
1683
1684
1685 /* Keys for epsf specials */
1686
1687 static const char *keytab[] = { "clip",
1688 "llx",
1689 "lly",
1690 "urx",
1691 "ury",
1692 "rwi",
1693 "rhi",
1694 "hsize",
1695 "vsize",
1696 "hoffset",
1697 "voffset",
1698 "hscale",
1699 "vscale",
1700 "angle"
1701 };
1702
1703 #define KEY_LLX keyval[0]
1704 #define KEY_LLY keyval[1]
1705 #define KEY_URX keyval[2]
1706 #define KEY_URY keyval[3]
1707 #define KEY_RWI keyval[4]
1708 #define KEY_RHI keyval[5]
1709
1710 #define NKEYS (sizeof keytab /sizeof *keytab)
1711 #define N_ARGLESS_KEYS 1
1712
1713 static void
epsf_special(char * cp)1714 epsf_special(char *cp)
1715 {
1716 char *filename;
1717 static char *buffer;
1718 static unsigned int buflen = 0;
1719 unsigned int len;
1720 char *q;
1721 int flags = 0;
1722 double keyval[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
1723
1724 filename = cp;
1725 if (*cp == '\'' || *cp == '"') {
1726 do
1727 ++cp;
1728 while (*cp != '\0' && *cp != *filename);
1729 ++filename;
1730 }
1731 else
1732 while (*cp != '\0' && !isspace((int)*cp))
1733 ++cp;
1734 if (*cp != '\0')
1735 *cp++ = '\0';
1736 while (isspace((int)*cp))
1737 ++cp;
1738 len = strlen(cp) + NKEYS + 30;
1739 if (buflen < len) {
1740 if (buflen != 0)
1741 free(buffer);
1742 buflen = len;
1743 buffer = xmalloc(buflen);
1744 }
1745 strcpy(buffer, "@beginspecial");
1746 q = buffer + strlen(buffer);
1747 while (*cp != '\0') {
1748 char *p1 = cp;
1749 size_t keyno;
1750
1751 while (*p1 != '=' && !isspace((int)*p1) && *p1 != '\0')
1752 ++p1;
1753 for (keyno = 0;; ++keyno) {
1754 if (keyno >= NKEYS) {
1755 if (globals.warn_spec_now)
1756 XDVI_WARNING((stderr, "Ignoring unknown keyword (%.*s) in \\special",
1757 (int)(p1 - cp), cp));
1758 break;
1759 }
1760 if (memcmp(cp, keytab[keyno], p1 - cp) == 0) {
1761 if (keyno >= N_ARGLESS_KEYS) {
1762 while (isspace((int)*p1))
1763 ++p1;
1764 if (*p1 == '=') {
1765 ++p1;
1766 while (isspace((int)*p1))
1767 ++p1;
1768 }
1769 if (keyno < N_ARGLESS_KEYS + 6) {
1770 keyval[keyno - N_ARGLESS_KEYS] = atof(p1);
1771 flags |= (1 << (keyno - N_ARGLESS_KEYS));
1772 }
1773 *q++ = ' ';
1774 while (!isspace((int)*p1) && *p1 != '\0')
1775 *q++ = *p1++;
1776 }
1777 *q++ = ' ';
1778 *q++ = '@';
1779 strcpy(q, keytab[keyno]);
1780 q += strlen(q);
1781 break;
1782 }
1783 }
1784 cp = p1;
1785 while (!isspace((int)*cp) && *cp != '\0')
1786 ++cp;
1787 while (isspace((int)*cp))
1788 ++cp;
1789 }
1790 strcpy(q, " @setspecial\n");
1791
1792 bbox_valid = False;
1793 /* Validate the case where both rwi and rhi are undefined
1794 * (and llx, lly, urx, ury are properly defined) */
1795 if (!(flags & 0x30) && (flags & 0xf) == 0xf) {
1796 KEY_RWI = (KEY_URX - KEY_LLX) * 10;
1797 flags |= 0x10;
1798 }
1799 if ((flags & 0x30) == 0x30 || ((flags & 0x30) && (flags & 0xf) == 0xf)) {
1800 bbox_valid = True;
1801 bbox_width = 0.1 * ((flags & 0x10) ? KEY_RWI
1802 : KEY_RHI * (KEY_URX - KEY_LLX) / (KEY_URY -
1803 KEY_LLY)) *
1804 dimconv / currwin.shrinkfactor + 0.5;
1805 bbox_voffset = bbox_height =
1806 0.1 * ((flags & 0x20) ? KEY_RHI : KEY_RWI * (KEY_URY - KEY_LLY) /
1807 (KEY_URX - KEY_LLX))
1808 * dimconv / currwin.shrinkfactor + 0.5;
1809 }
1810
1811 if (INSIDE_MANE_WIN) {
1812 #if PS
1813 psp.drawbegin(PXL_H - currwin.base_x, PXL_V - currwin.base_y, buffer);
1814 /* talk directly with the DPSHandler here */
1815 send_ps_file(filename, kpse_pict_format);
1816 psp.drawend(" @endspecial");
1817 #else
1818 draw_bbox();
1819 #endif
1820 }
1821 bbox_valid = False;
1822 }
1823
1824
1825 static void
quote_special(char * cp)1826 quote_special(char *cp)
1827 {
1828 bbox_valid = False;
1829
1830 #if PS
1831 if (currwin.win == mane.win) {
1832 psp.drawbegin(PXL_H - currwin.base_x, PXL_V - currwin.base_y,
1833 "@beginspecial @setspecial ");
1834 /* talk directly with the DPSHandler here */
1835 psp.drawraw(cp + 1);
1836 psp.drawend(" @endspecial");
1837 }
1838 #endif
1839
1840 /* nothing else to do--there's no bbox here */
1841 }
1842
1843 #if PS
1844
1845 static void
scan_header(char * cp)1846 scan_header(char *cp)
1847 {
1848 char *filename;
1849 /* default for .pro files: */
1850 kpse_file_format_type our_format;
1851
1852 #if PS_GS
1853 if (gs_postpone_prescan)
1854 return;
1855 #endif
1856 filename = cp;
1857 if (*cp == '\'' || *cp == '"') {
1858 do
1859 ++cp;
1860 while (*cp != '\0' && *cp != *filename);
1861 *cp = '\0';
1862 ++filename;
1863 }
1864
1865 psp.beginheader();
1866 /* check for suffixes other than `.pro' (default PS headers),
1867 case-insensitive */
1868 if (str_is_suffix(".pfa", filename, False)
1869 || str_is_suffix(".pfb", filename, False))
1870 our_format = kpse_type1_format;
1871 else if (str_is_suffix(".enc", filename, False))
1872 our_format = kpse_enc_format;
1873 else
1874 our_format = kpse_tex_ps_header_format;
1875
1876 send_ps_file(filename, our_format);
1877 }
1878
1879 static void
scan_bang(char * cp)1880 scan_bang(char *cp)
1881 {
1882 psp.beginheader();
1883 psp.drawraw(cp + 1);
1884 }
1885
1886 #endif /* PS */
1887
1888 #if COLOR
1889
1890 /*
1891 * Table of dvips color names. Produced by passing the table in
1892 * dvipsk/color.lpro through the following perl script, and then
1893 * through sort.
1894 *
1895 * #! /usr/bin/perl -w
1896 *
1897 * sub cvpart {
1898 * return $_[0] < 1.0 ? 1.0 - $_[0] : 0.0;
1899 * }
1900 *
1901 * $pat = "^\\/(\\w+)\\s*\\{\\s*([0-9.]+)\\s*([0-9.]+)\\s*([0-9.]+)"
1902 * . "\\s*([0-9.]+)\\s*setcmykcolor\\s*\\}\\s*DC\\s*\$";
1903 * while (<STDIN>) {
1904 * chop;
1905 * if (/^%%/) {next;}
1906 * if (/$pat/o) {
1907 * printf "\t\tDVIPSCOLORDESC(%2d, \"%s\", %g, %g, %g),\n",
1908 * length($1), $1,
1909 * cvpart($2 + $5), cvpart($3 + $5), cvpart($4 + $5);
1910 * }
1911 * else {
1912 * print "Bad line: ", $_, "\n";
1913 * }
1914 * }
1915 */
1916
1917 struct dvipscolor {
1918 const char *name;
1919 struct rgb color;
1920 };
1921
1922 static Boolean
parse_color(const char * cp0,const char * cp,struct rgb * rgbp,Boolean generic_ps_flag)1923 parse_color(const char *cp0, const char *cp,
1924 struct rgb *rgbp, Boolean generic_ps_flag)
1925 {
1926 double r, g, b;
1927 double k;
1928 double hue, sat, bri;
1929 char dummy[8];
1930
1931 #define CVPART(x) ((int)((x) * 65535 + 0.5))
1932 #define COLOR_DESC(name, r, g, b) \
1933 {name, {CVPART(r), CVPART(g), CVPART(b)}}
1934
1935 /* hash table to speed up lookup in following list of color names */
1936 static hashTableT colornames_hash;
1937 /*
1938 Use a prime close to sizof colornames / sizeof colornames[0].
1939 You might want to update this when extending the colornames array,
1940 and to check the filling factor and average chain length by uncommenting the
1941 hash_print(colornames_hash, True);
1942 below. I usually go for filling > 50% and avg. chain len < 2.
1943
1944 The 68-elem array below results in:
1945 79 buckets, 43 nonempty (54%); 68 entries, average chain 1.6.
1946 */
1947 static const size_t colornames_hash_size = 79;
1948
1949 static struct dvipscolor colornames[] = {
1950 COLOR_DESC("Red", 1, 0, 0),
1951 COLOR_DESC("Tan", 0.86, 0.58, 0.44),
1952 COLOR_DESC("Blue", 0, 0, 1),
1953 COLOR_DESC("Cyan", 0, 1, 1),
1954 COLOR_DESC("Gray", 0.5, 0.5, 0.5),
1955 COLOR_DESC("Plum", 0.5, 0, 1),
1956 COLOR_DESC("Black", 0, 0, 0),
1957 COLOR_DESC("Brown", 0.4, 0, 0),
1958 COLOR_DESC("Green", 0, 1, 0),
1959 COLOR_DESC("Melon", 1, 0.54, 0.5),
1960 COLOR_DESC("Peach", 1, 0.5, 0.3),
1961 COLOR_DESC("Sepia", 0.3, 0, 0),
1962 COLOR_DESC("White", 1, 1, 1),
1963 COLOR_DESC("Maroon", 0.68, 0, 0),
1964 COLOR_DESC("Orange", 1, 0.39, 0.13),
1965 COLOR_DESC("Orchid", 0.68, 0.36, 1),
1966 COLOR_DESC("Purple", 0.55, 0.14, 1),
1967 COLOR_DESC("Salmon", 1, 0.47, 0.62),
1968 COLOR_DESC("Violet", 0.21, 0.12, 1),
1969 COLOR_DESC("Yellow", 1, 1, 0),
1970 COLOR_DESC("Apricot", 1, 0.68, 0.48),
1971 COLOR_DESC("Emerald", 0, 1, 0.5),
1972 COLOR_DESC("Fuchsia", 0.45, 0.01, 0.92),
1973 COLOR_DESC("Magenta", 1, 0, 1),
1974 COLOR_DESC("SkyBlue", 0.38, 1, 0.88),
1975 COLOR_DESC("Thistle", 0.88, 0.41, 1),
1976 COLOR_DESC("BrickRed", 0.72, 0, 0),
1977 COLOR_DESC("Cerulean", 0.06, 0.89, 1),
1978 COLOR_DESC("Lavender", 1, 0.52, 1),
1979 COLOR_DESC("Mahogany", 0.65, 0, 0),
1980 COLOR_DESC("Mulberry", 0.64, 0.08, 0.98),
1981 COLOR_DESC("NavyBlue", 0.06, 0.46, 1),
1982 COLOR_DESC("SeaGreen", 0.31, 1, 0.5),
1983 COLOR_DESC("TealBlue", 0.12, 0.98, 0.64),
1984 COLOR_DESC("BlueGreen", 0.15, 1, 0.67),
1985 COLOR_DESC("CadetBlue", 0.38, 0.43, 0.77),
1986 COLOR_DESC("Dandelion", 1, 0.71, 0.16),
1987 COLOR_DESC("Goldenrod", 1, 0.9, 0.16),
1988 COLOR_DESC("LimeGreen", 0.5, 1, 0),
1989 COLOR_DESC("OrangeRed", 1, 0, 0.5),
1990 COLOR_DESC("PineGreen", 0, 0.75, 0.16),
1991 COLOR_DESC("RawSienna", 0.55, 0, 0),
1992 COLOR_DESC("RedOrange", 1, 0.23, 0.13),
1993 COLOR_DESC("RedViolet", 0.59, 0, 0.66),
1994 COLOR_DESC("Rhodamine", 1, 0.18, 1),
1995 COLOR_DESC("RoyalBlue", 0, 0.5, 1),
1996 COLOR_DESC("RubineRed", 1, 0, 0.87),
1997 COLOR_DESC("Turquoise", 0.15, 1, 0.8),
1998 COLOR_DESC("VioletRed", 1, 0.19, 1),
1999 COLOR_DESC("Aquamarine", 0.18, 1, 0.7),
2000 COLOR_DESC("BlueViolet", 0.1, 0.05, 0.96),
2001 COLOR_DESC("DarkOrchid", 0.6, 0.2, 0.8),
2002 COLOR_DESC("OliveGreen", 0, 0.6, 0),
2003 COLOR_DESC("Periwinkle", 0.43, 0.45, 1),
2004 COLOR_DESC("Bittersweet", 0.76, 0.01, 0),
2005 COLOR_DESC("BurntOrange", 1, 0.49, 0),
2006 COLOR_DESC("ForestGreen", 0, 0.88, 0),
2007 COLOR_DESC("GreenYellow", 0.85, 1, 0.31),
2008 COLOR_DESC("JungleGreen", 0.01, 1, 0.48),
2009 COLOR_DESC("ProcessBlue", 0.04, 1, 1),
2010 COLOR_DESC("RoyalPurple", 0.25, 0.1, 1),
2011 COLOR_DESC("SpringGreen", 0.74, 1, 0.24),
2012 COLOR_DESC("YellowGreen", 0.56, 1, 0.26),
2013 COLOR_DESC("MidnightBlue", 0, 0.44, 0.57),
2014 COLOR_DESC("YellowOrange", 1, 0.58, 0),
2015 COLOR_DESC("CarnationPink", 1, 0.37, 1),
2016 COLOR_DESC("CornflowerBlue", 0.35, 0.87, 1),
2017 COLOR_DESC("WildStrawberry", 1, 0.04, 0.61),
2018 };
2019 #undef CVPART
2020 #undef COLOR_DESC
2021
2022 UNUSED(cp0);
2023
2024 while (*cp == ' ') ++cp;
2025
2026 if (generic_ps_flag) {
2027 /*
2028 check for pstricks color specials.
2029 The dummy buffer is to ensure that there are no other (general PS)
2030 commands following (we wouldn't know how to deal with these).
2031 */
2032 if (sscanf(cp, "%lf %lf %lf setrgbcolor %7s", &r, &g, &b, dummy) == 3
2033 && r >= 0 && r <= 1 && g >= 0 && g <= 1 && b >= 0 && b <= 1) {
2034 rgbp->r = r * 65535 + 0.5;
2035 rgbp->g = g * 65535 + 0.5;
2036 rgbp->b = b * 65535 + 0.5;
2037 return True;
2038 }
2039 else if (sscanf(cp, "%lf %lf %lf %lf setcmykcolor %7s", &r, &g, &b, &k, dummy) == 4
2040 && r >= 0 && r <= 1 && g >= 0 && g <= 1 && b >= 0 && b <= 1
2041 && k >= 0 && k <= 1) {
2042 r = 1.0 - r - k; /* cyan --> red */
2043 rgbp->r = (r < 0 ? 0 : r * 65535 + 0.5);
2044 g = 1.0 - g - k; /* magenta --> green */
2045 rgbp->g = (g < 0 ? 0 : g * 65535 + 0.5);
2046 b = 1.0 - b - k; /* yellow --> blue */
2047 rgbp->b = (b < 0 ? 0 : b * 65535 + 0.5);
2048 return True;
2049 }
2050 else if (sscanf(cp, "%lf %lf %lf sethsbcolor %7s", &hue, &sat, &bri, dummy) == 3 && hue >= 0
2051 && hue <= 1 && sat >= 0 && sat <= 1 && bri >= 0 && bri <= 1) {
2052 int h = (int) (6 * hue);
2053 double p = bri * (1 - sat);
2054 double q = bri * (1 - sat * (6 * hue - h));
2055 double t = p - q + bri;
2056
2057 switch (h) {
2058 case 0: r = bri; g = t; b = p; break; /* Red - Yel */
2059 case 1: r = q; g = bri; b = p; break; /* Yel - Grn */
2060 case 2: r = p; g = bri; b = t; break; /* Grn - Cyn */
2061 case 3: r = p; g = q; b = bri; break; /* Cyn - Blu */
2062 case 4: r = t; g = p; b = bri; break; /* Blu - Mag */
2063 case 5: r = bri; g = p; b = q; break; /* Mag - Red */
2064 case 6: r = bri; g = b = p; break; /* Red */
2065 }
2066 rgbp->r = r * 65535 + 0.5;
2067 rgbp->g = g * 65535 + 0.5;
2068 rgbp->b = b * 65535 + 0.5;
2069 return True;
2070 }
2071 else if (sscanf(cp, "%lf setgray %7s", &r, dummy) == 1 && r >= 0 && r <= 1) {
2072 rgbp->r = rgbp->g = rgbp->b = r * 65535 + 0.5;
2073 return True;
2074 }
2075 else
2076 return False;
2077 }
2078
2079 /* no generic PS command; must be a dvips color special */
2080
2081 if (memicmp(cp, "rgb ", 4) == 0) {
2082 if (sscanf(cp + 3, "%lf %lf %lf", &r, &g, &b) == 3
2083 && r >= 0 && r <= 1 && g >= 0 && g <= 1 && b >= 0 && b <= 1) {
2084 rgbp->r = r * 65535 + 0.5;
2085 rgbp->g = g * 65535 + 0.5;
2086 rgbp->b = b * 65535 + 0.5;
2087 return True;
2088 }
2089 }
2090 else if (memicmp(cp, "gray ", 5) == 0) {
2091 if (sscanf(cp + 4, "%lf", &r) == 1 && r >= 0 && r <= 1) {
2092 rgbp->r = rgbp->g = rgbp->b = r * 65535 + 0.5;
2093 return True;
2094 }
2095 }
2096 else if (memicmp(cp, "cmyk ", 5) == 0) {
2097 if (sscanf(cp + 4, "%lf %lf %lf %lf", &r, &g, &b, &k) == 4
2098 && r >= 0 && r <= 1 && g >= 0 && g <= 1 && b >= 0 && b <= 1
2099 && k >= 0 && k <= 1) {
2100 r = 1.0 - r - k; /* cyan --> red */
2101 rgbp->r = (r < 0 ? 0 : r * 65535 + 0.5);
2102 g = 1.0 - g - k; /* magenta --> green */
2103 rgbp->g = (g < 0 ? 0 : g * 65535 + 0.5);
2104 b = 1.0 - b - k; /* yellow --> blue */
2105 rgbp->b = (b < 0 ? 0 : b * 65535 + 0.5);
2106 return True;
2107 }
2108 }
2109 else if (memicmp(cp, "hsb ", 4) == 0) {
2110 if (sscanf(cp + 3, "%lf %lf %lf", &hue, &sat, &bri) == 3 && hue >= 0
2111 && hue <= 1 && sat >= 0 && sat <= 1 && bri >= 0 && bri <= 1) {
2112 int h = (int) (6 * hue);
2113 double p = bri * (1 - sat);
2114 double q = bri * (1 - sat * (6 * hue - h));
2115 double t = p - q + bri;
2116
2117 switch (h) {
2118 case 0: r = bri; g = t; b = p; break; /* Red - Yel */
2119 case 1: r = q; g = bri; b = p; break; /* Yel - Grn */
2120 case 2: r = p; g = bri; b = t; break; /* Grn - Cyn */
2121 case 3: r = p; g = q; b = bri; break; /* Cyn - Blu */
2122 case 4: r = t; g = p; b = bri; break; /* Blu - Mag */
2123 case 5: r = bri; g = p; b = q; break; /* Mag - Red */
2124 case 6: r = bri; g = b = p; break; /* Red */
2125 }
2126 rgbp->r = r * 65535 + 0.5;
2127 rgbp->g = g * 65535 + 0.5;
2128 rgbp->b = b * 65535 + 0.5;
2129 return True;
2130 }
2131 }
2132 else { /* is it a dvips color name? */
2133 size_t idx;
2134 size_t len = 0;
2135 static char *testname = NULL;
2136 static size_t testname_len = 0;
2137 size_t testname_step = 16;
2138
2139 if (colornames_hash.size == 0) { /* initialize hash table */
2140 size_t i;
2141 colornames_hash = hash_create(colornames_hash_size);
2142 /* insert color names and array indexes */
2143 for (i = 0; i < (sizeof colornames / sizeof colornames[0]); i++) {
2144 put_str_int_hash(&colornames_hash, colornames[i].name, i);
2145 }
2146 }
2147 /* uncomment the following to get statistics on hashing: */
2148 /* hash_print(colornames_hash, True); */
2149
2150 /* need to copy to a null-terminated string for hash lookup ... */
2151 while (isalpha((int)cp[len])) {
2152 ++len; /* get length of color name */
2153 }
2154 while (len >= testname_len) {
2155 testname_len += testname_step;
2156 testname = xrealloc(testname, testname_len);
2157 }
2158 memcpy(testname, cp, len);
2159 testname[len] = '\0';
2160
2161 if (find_str_int_hash(&colornames_hash, testname, &idx)) {
2162 *rgbp = colornames[idx].color;
2163 return True;
2164 }
2165 }
2166
2167 /* not found */
2168 XDVI_WARNING((stderr, "Ignoring invalid color name in special `%s'", cp0 == NULL ? "<NULL>" : cp0));
2169 return False;
2170 }
2171
2172 void
init_page_colors(struct rgb * foreground,struct rgb * background)2173 init_page_colors(struct rgb *foreground, struct rgb *background)
2174 {
2175 int i;
2176
2177 page_colors.size = total_pages;
2178 page_colors.stack = xmalloc(page_colors.size * sizeof *page_colors.stack);
2179
2180 for (i = 0; i <= scanned_page + 1; ++i) {
2181 page_colors.stack[i].bg = *background;
2182 page_colors.stack[i].colorstack = foreground;
2183 page_colors.stack[i].stacksize = 1;
2184 }
2185 for (; i < total_pages; i++) {
2186 page_colors.stack[i].colorstack = NULL;
2187 }
2188
2189 scanstack_head.color = *foreground;
2190 scanstack_len = 1; /* nothing yet */
2191 scanstack_current = &scanstack_head; /* stack position */
2192 }
2193
2194
2195 static void
scan_bg_color(const char * cp)2196 scan_bg_color(const char *cp)
2197 {
2198 if (!resource.use_color)
2199 return;
2200
2201 if (page_colors.stack == NULL)
2202 init_page_colors(&fg_initial, &bg_initial);
2203
2204 ASSERT(scanned_page < (int)page_colors.size, "page_colors.size too small");
2205 (void) parse_color(cp, cp + 11, &(page_colors.stack[scanned_page + 1].bg), False);
2206 }
2207
2208 void
scan_color(const char * cp)2209 scan_color(const char *cp)
2210 {
2211 const char *cp1 = cp + 6;
2212
2213 if (!resource.use_color)
2214 return;
2215
2216 while (*cp1 == ' ') ++cp1;
2217
2218 if (page_colors.stack == NULL)
2219 init_page_colors(&fg_initial, &bg_initial);
2220
2221 if (memicmp(cp1, "push ", 5) == 0) {
2222 if (scanstack_current->next == NULL) { /* if at end */
2223 scanstack_current->next = xmalloc(sizeof *scanstack_current);
2224 scanstack_current->next->prev = scanstack_current;
2225 scanstack_current->next->next = NULL;
2226 }
2227 scanstack_current = scanstack_current->next;
2228 ++scanstack_len;
2229 if (!parse_color(cp, cp1 + 5, &scanstack_current->color, False))
2230 scanstack_current->color = scanstack_current->prev->color;
2231 }
2232 else if (memicmp(cp1, "pop", 3) == 0) {
2233 if (scanstack_len <= 1) {
2234 XDVI_WARNING((stderr, "Color pop occurred with empty color stack."));
2235 }
2236 else {
2237 scanstack_current = scanstack_current->prev;
2238 --scanstack_len;
2239 }
2240 }
2241 else {
2242 (void) parse_color(cp, cp1, &scanstack_head.color, False);
2243 if (scanstack_len > 1) {
2244 struct colorframe *cfp;
2245
2246 XDVI_WARNING((stderr, "Global color change occurred with non-empty color stack!\n"
2247 "Trying to recover by setting all stack entries to that color."));
2248 for (cfp = scanstack_head.next;; cfp = cfp->next) {
2249 cfp->color = scanstack_head.color;
2250 if (cfp == scanstack_current) break;
2251 }
2252 }
2253 }
2254 }
2255
2256 void
scan_color_eop(void)2257 scan_color_eop(void)
2258 {
2259 int i;
2260 struct rgb *prev, *p1, *rgbp;
2261 struct colorframe *cf;
2262
2263 if (page_colors.stack == NULL)
2264 return;
2265
2266 /* set background color for next page */
2267 if (scanned_page + 1 < total_pages) {
2268 ASSERT(scanned_page + 1 < (int)page_colors.size, "page_colors.size too small");
2269 page_colors.stack[scanned_page + 1].bg = page_colors.stack[scanned_page].bg;
2270 }
2271 ASSERT(scanned_page < (int)page_colors.size, "page_colors.size too small");
2272 /* save the stack contents */
2273 page_colors.stack[scanned_page].stacksize = scanstack_len;
2274
2275 prev = &fg_initial;
2276 i = 1;
2277 if (scanned_page > 0) {
2278 prev = page_colors.stack[scanned_page - 1].colorstack;
2279 i = page_colors.stack[scanned_page - 1].stacksize;
2280 }
2281 if (scanstack_len <= i) {
2282 /* try to reuse the previous array */
2283 p1 = prev;
2284 cf = &scanstack_head;
2285 for (i = 0;;) {
2286 if (p1->r != cf->color.r || p1->g != cf->color.g || p1->b != cf->color.b)
2287 break;
2288 if (++i >= scanstack_len) { /* end loop; reuse memory */
2289 page_colors.stack[scanned_page].colorstack = prev;
2290 return; /* done */
2291 }
2292 ++p1;
2293 cf = cf->next;
2294 }
2295 }
2296 page_colors.stack[scanned_page].colorstack = rgbp = xmalloc(scanstack_len * sizeof *rgbp);
2297 cf = &scanstack_head;
2298 for (i = 0; i < scanstack_len; ++i) {
2299 *rgbp++ = cf->color;
2300 cf = cf->next;
2301 }
2302 }
2303
2304
2305 /*
2306 * We don't actually do any X calls yet to change colors; that can wait
2307 * until a character or rule needs to be drawn.
2308 */
2309
2310 void
set_fg_color(const struct rgb * color)2311 set_fg_color(const struct rgb *color)
2312 {
2313 struct fgrec **fgpp;
2314
2315 if (fg_current != NULL
2316 && color->r == fg_current->color.r
2317 && color->g == fg_current->color.g
2318 && color->b == fg_current->color.b) {
2319 return;
2320 }
2321
2322 ASSERT(bg_current != NULL, "Background color not initialized");
2323 for (fgpp = &bg_current->fg_head;;) {
2324 fg_current = *fgpp;
2325 if (fg_current == NULL) { /* if color is not in list */
2326 fg_current = *fgpp = xmalloc(sizeof *fg_current);
2327 fg_current->next = NULL;
2328 fg_current->color = *color;
2329 fg_current->pixel_good = False;
2330 #if GREY
2331 fg_current->palette_good = False;
2332 #endif
2333 break;
2334 }
2335 if (fg_current->color.r == color->r
2336 && fg_current->color.g == color->g
2337 && fg_current->color.b == color->b)
2338 break;
2339 fgpp = &fg_current->next;
2340 }
2341 if (globals.debug & DBG_DVI)
2342 printf("Changing fg color to %5d %5d %5d\n",
2343 color->r, color->g, color->b);
2344 }
2345
2346 #if 0
2347 static void
2348 show_stack(void) {
2349 struct colorframe *ptr;
2350 int i;
2351 fprintf(stderr, "FG: %d,%d,%d\n", fg_current->color.r, fg_current->color.b, fg_current->color.g);
2352 for (i = 0, ptr = rcs_top; ptr != NULL; ptr = ptr->prev, i++) {
2353 fprintf(stderr, "stack %d: %d,%d,%d\n", i, ptr->color.r, ptr->color.b, ptr->color.g);
2354 }
2355 }
2356 #endif
2357
2358 void
color_special(const char * cp)2359 color_special(const char *cp)
2360 {
2361 while (*cp == ' ') ++cp;
2362
2363 if (memicmp(cp, "push ", 5) == 0) {
2364 if (rcs_top == NULL) {
2365 if (rcs_head == NULL) {
2366 rcs_head = xmalloc(sizeof *rcs_head);
2367 rcs_head->prev = rcs_head->next = NULL;
2368 }
2369 rcs_top = rcs_head;
2370 }
2371 else {
2372 struct colorframe *rcs_next;
2373
2374 rcs_next = rcs_top->next;
2375 if (rcs_next == NULL) {
2376 rcs_next = rcs_top->next = xmalloc(sizeof *rcs_next);
2377 rcs_next->prev = rcs_top;
2378 rcs_next->next = NULL;
2379 }
2380 rcs_top = rcs_next;
2381 }
2382 if (!parse_color(NULL, cp + 5, &rcs_top->color, False)) {
2383 if (rcs_top->prev != NULL)
2384 rcs_top->color = rcs_top->prev->color;
2385 else
2386 rcs_top->color = color_bottom[color_bot_size - 1];
2387 }
2388 set_fg_color(&rcs_top->color);
2389 }
2390 else if (memicmp(cp, "pop", 3) == 0) {
2391 /* Pop stack */
2392 if (rcs_top != NULL) {
2393 if (color_bot_size > 0)
2394 rcs_top = rcs_top->prev;
2395 else
2396 return;
2397 }
2398 else if (color_bot_size > 1)
2399 --color_bot_size;
2400 else {
2401 if (scanned_page_reset >= 0) {
2402 /* turn on scanning and redraw the page */
2403 scanned_page = scanned_page_color = scanned_page_reset = -1;
2404 #if PS
2405 scanned_page_ps = scanned_page;
2406 #endif
2407 /* fprintf(stderr, "forcing redraw!!!\n"); */
2408 globals.ev.flags |= EV_NEWPAGE; /* force a redraw */
2409 longjmp(globals.ev.canit, 1);
2410 }
2411 return;
2412 }
2413 /* fprintf(stderr, "bot_size: %d\n", color_bot_size); */
2414 set_fg_color(rcs_top != NULL ? &rcs_top->color
2415 : &color_bottom[color_bot_size - 1]);
2416 }
2417 else {
2418 struct rgb color;
2419
2420 if (!parse_color(NULL, cp, &color, False))
2421 return;
2422
2423 /* clear stack */
2424 if (rcs_head == NULL) {
2425 rcs_head = xmalloc(sizeof *rcs_head);
2426 rcs_head->prev = rcs_head->next = NULL;
2427 }
2428 rcs_top = rcs_head;
2429 color_bot_size = 0;
2430
2431 /* Change top of stack */
2432 rcs_top->color = color;
2433
2434 set_fg_color(&color);
2435 }
2436 }
2437
2438 #endif /* COLOR */
2439
2440 static unsigned int
myatopix(const char ** pp)2441 myatopix(const char **pp)
2442 {
2443 #define SCR_LEN 16
2444 unsigned int value;
2445 const char *cp = *pp;
2446 char scr[16];
2447 const char *p0, *p1;
2448
2449 p0 = cp;
2450 while ((*cp >= '0' && *cp <= '9') || *cp == '.')
2451 ++cp;
2452 p1 = cp;
2453 while (isspace((int)*cp))
2454 ++cp;
2455 if (*cp >= 'a' && *cp <= 'z' && cp[1] >= 'a' && cp[1] <= 'z') {
2456 /* if units are present */
2457 if (p1 - p0 <= SCR_LEN - 3) {
2458 sprintf(scr, "%.*s%c%c", (int)(p1 - p0), p0, *cp, cp[1]);
2459 value = atopix(scr, False);
2460 }
2461 else
2462 value = 0;
2463 cp += 2;
2464 }
2465 else
2466 value = atopix(p0, False);
2467
2468 *pp = cp;
2469 return value;
2470 #undef SCR_LEN
2471 }
2472
2473 static void
scan_papersize(const char * cp0)2474 scan_papersize(const char *cp0)
2475 {
2476 const char *cp = cp0;
2477 unsigned int w, h;
2478 double mag = 1.0;
2479
2480 if (ignore_papersize_specials)
2481 return;
2482
2483 if (*cp == '*') {
2484 do
2485 ++cp;
2486 while (isspace((int)*cp));
2487 mag = magnification * .001;
2488 }
2489
2490 w = myatopix(&cp) * mag + 0.5;
2491
2492 while (isspace((int)*cp))
2493 ++cp;
2494 if (*cp == ',')
2495 do
2496 ++cp;
2497 while (isspace((int)*cp));
2498
2499 h = myatopix(&cp) * mag + 0.5;
2500
2501 if (w == 0 || h == 0)
2502 XDVI_WARNING((stderr, "Invalid papersize special `%s'", cp0));
2503 else {
2504 /* we have a paper size special; disable xdvirc_geometry:
2505 fprintf(stderr, "---------- scanned papersize: %dx%d\n", w, h);
2506 resource.xdvirc_geometry = NULL;
2507 */
2508 pageinfo_set_page_width(scanned_page + 1, w);
2509 pageinfo_set_window_width(scanned_page + 1, w);
2510 pageinfo_set_page_height(scanned_page + 1, h);
2511 pageinfo_set_window_height(scanned_page + 1, h);
2512 }
2513 }
2514
2515 /*
2516 ** Modified 22/3/95.
2517 */
2518
2519 typedef struct psh_node {
2520 char *this_file;
2521 struct psh_node *next;
2522 } t_pshnode;
2523
2524 static t_pshnode *psh_head = (t_pshnode *) NULL, *psh_tail = (t_pshnode *) NULL;
2525
2526 /*
2527 ** Include one header file.
2528 */
2529
psh_includehfile(cp,beginning)2530 static void psh_includehfile(cp, beginning)
2531 char *cp;
2532 int beginning;
2533
2534 {
2535 static char rawstring[] = " stop\n%%xdvimark\nH userdict begin\0";
2536
2537 if (!beginning)
2538 psp.drawbegin(PXL_H - currwin.base_x, PXL_V - currwin.base_y, "");
2539 psp.drawraw(rawstring);
2540 send_ps_file(cp, kpse_pict_format);
2541 psp.drawend(" end\n");
2542 }
2543
2544 /*
2545 ** Include all header files.
2546 */
2547
psh_includeall()2548 void psh_includeall()
2549
2550 {
2551 t_pshnode *this_node;
2552
2553 this_node = psh_head;
2554 while (this_node != (t_pshnode *) NULL) {
2555 psh_includehfile(this_node->this_file, 1);
2556 this_node = this_node->next;
2557 }
2558 }
2559
2560 /*
2561 ** Deal with the "header=<file>" special.
2562 */
2563
psh_header(cp)2564 static void psh_header(cp)
2565 char *cp;
2566
2567 {
2568 t_pshnode *this_node;
2569 char *psheader;
2570
2571 /*
2572 ** Find the file.
2573 */
2574
2575 psheader = kpse_find_file ( cp+7, kpse_tex_ps_header_format, true );
2576
2577 if (psheader) {
2578
2579 /*
2580 ** Include the file directly in /userdict.
2581 */
2582
2583 /* psh_includehfile(psheader, 0); */
2584 /* deleted by Kazuaki NAKAYAMA (nakayama@ripms.shinshu-u.ac.jp) */
2585
2586 /*
2587 ** Add a new node to the list of header files.
2588 */
2589
2590 this_node = (t_pshnode *) xmalloc(sizeof(t_pshnode));
2591 this_node->this_file = strcpy(xmalloc((strlen(psheader) + 1) * sizeof(char)), psheader);
2592 this_node->next = (t_pshnode *) NULL;
2593 if (psh_head == (t_pshnode *) NULL)
2594 psh_head = psh_tail = this_node;
2595 else {
2596 psh_tail->next = this_node;
2597 psh_tail = this_node;
2598 }
2599
2600 } else
2601 XDVI_WARNING((stderr, "Included PostScript header file %s not found", cp + 7));
2602 }
2603
2604 /*
2605 ** End Modifications
2606 */
2607
2608
2609 /*
2610 * The following copyright message applies to the rest of this file. --PV
2611 */
2612
2613 /*
2614 * This program is Copyright (C) 1987 by the Board of Trustees of the
2615 * University of Illinois, and by the author Dirk Grunwald.
2616 *
2617 * This program may be freely copied, as long as this copyright
2618 * message remaines affixed. It may not be sold, although it may
2619 * be distributed with other software which is sold. If the
2620 * software is distributed, the source code must be made available.
2621 *
2622 * No warranty, expressed or implied, is given with this software.
2623 * It is presented in the hope that it will prove useful.
2624 *
2625 * Hacked in ignorance and desperation by jonah@db.toronto.edu
2626 */
2627
2628 /*
2629 * The code to handle the \specials generated by tpic was modified
2630 * by Dirk Grunwald using the code Tim Morgan at Univ. of Calif, Irvine
2631 * wrote for TeXsun.
2632 */
2633
2634 static char *
endofcommand(char * cp)2635 endofcommand(char *cp)
2636 {
2637 while (isspace((int)*cp))
2638 ++cp;
2639 if (*cp != '=')
2640 return NULL;
2641 do
2642 ++cp;
2643 while (isspace((int)*cp));
2644 return cp;
2645 }
2646
2647 #define CMD(x, y) ((x) << 8 | (y))
2648
2649 void
applicationDoSpecial(char * cp,size_t len)2650 applicationDoSpecial(char *cp, size_t len)
2651 {
2652 char *p;
2653
2654 if (globals.debug & DBG_DVI)
2655 printf(" `%s'\n", cp);
2656
2657 while (isspace((int)*cp))
2658 ++cp;
2659
2660 /* Ignore initial "xdvi:" */
2661 if (memcmp(cp, "xdvi:", 5) == 0) {
2662 cp += 5;
2663 while (isspace((int)*cp))
2664 ++cp;
2665 }
2666
2667 /* PostScript specials */
2668 #ifdef MAGICK
2669 if (resource.useMAGICK) {
2670 if (Magick_parse_special(cp))
2671 return;
2672 }
2673 #endif
2674
2675 if (*cp == '"') {
2676 quote_special(cp);
2677 return;
2678 }
2679 if (memicmp(cp, "ps:", 3) == 0) {
2680 cp += 3;
2681 psfig_special(cp);
2682 /* check for hdvips hyperlinks */
2683 if (memicmp(cp, "sdict begin ", strlen("sdict begin ")) == 0) {
2684 static Boolean warned_hypertex_too_old = False;
2685 char *match = NULL;
2686 if (warned_hypertex_too_old) /* don't continue evaluating links in this case */
2687 return;
2688 cp += strlen("sdict begin ");
2689 if (memcmp(cp, "H.S", 3) == 0
2690 || memcmp(cp, "H.R", 3) == 0
2691 || memcmp(cp, "H.B", 3) == 0
2692 /* following 2 conditions could be more restrictive: between `begin' and H.A/H.L,
2693 there should be a single number (baselineskip in pt) */
2694 || (match = strstr(cp, "H.A")) != NULL
2695 || (match = strstr(cp, "H.L")) != NULL
2696 || (match = strstr(cp, "/Action")) != NULL
2697 || (match = strstr(cp, "/Link")) != NULL
2698 || (match = strstr(cp, "/View")) != NULL) {
2699 if (match != NULL)
2700 htex_do_special(match, len - 3 - (match - cp) - strlen("sdict begin "));
2701 else
2702 htex_do_special(cp, len - strlen("sdict begin "));
2703 }
2704 else if (!warned_hypertex_too_old && strstr(cp, "HyperStart") != NULL) {
2705 popup_message(globals.widgets.top_level,
2706 MSG_WARN, NULL,
2707 "This DVI was created with a too old version of the `dvips' hyperref driver - "
2708 "disabling hyperlinks.\n"
2709 "To fix this, you should either upgrade to a current version of hyperref "
2710 "(see http://www.tug.org/applications/hyperref/), "
2711 "or use the `hypertex' package option, like this:\n\\usepackage[hypertex]{hyperref}\n"
2712 "(Be aware though that this option won't work for PS->PDF conversion!)");
2713 warned_hypertex_too_old = True;
2714 }
2715 }
2716 else {
2717 /* When not ignoring SDict entries, the distiller and pagecolor
2718 code in lshort.dvi from CTAN:info/lshort/russian/lshrtdvi.zip
2719 causes a failed assertion for 'color_bot_size > 0' in dvi-draw.c;
2720 there's something wrong with the parsing order/event handling here
2721 (see bug #856547).
2722 But we also don't want those entries to trigger erasepage_gs(), so
2723 it's correct to ignore them here.
2724 */
2725 #if PS_GS && GS_PIXMAP_CLEARING_HACK
2726 had_ps_specials = True;
2727 #endif
2728 }
2729 return;
2730 }
2731 if (memicmp(cp, "psfile", 6) == 0 && (p = endofcommand(cp + 6)) != NULL) {
2732 epsf_special(p);
2733 #if PS_GS && GS_PIXMAP_CLEARING_HACK
2734 had_ps_specials = True;
2735 #endif
2736 return;
2737 }
2738 if (memicmp(cp, "html:", 5) == 0) {
2739 htex_do_special(cp + 5, len - 5);
2740 return;
2741 }
2742
2743 #if COLOR
2744 if (memicmp(cp, "color ", 6) == 0) {
2745 /* fprintf(stderr, "------------- color special\n"); */
2746 if (resource.use_color)
2747 color_special(cp + 6);
2748 return;
2749 }
2750 #endif
2751
2752 /* these should have been scanned */
2753 if (*cp == '!'
2754 || (memicmp(cp, "header", 6) == 0 && endofcommand(cp + 6) != NULL)) {
2755 #ifdef PS
2756 if (resource.postscript != 0 && scanned_page_reset >= 0) {
2757 /* turn on scanning and redraw the page */
2758 scanned_page = scanned_page_ps = scanned_page_reset = -1;
2759 # if COLOR
2760 scanned_page_color = scanned_page;
2761 # endif
2762 globals.ev.flags |= EV_NEWPAGE; /* force a redraw */
2763 longjmp(globals.ev.canit, 1);
2764 }
2765 #endif /* PS */
2766 return;
2767 }
2768
2769 if (memicmp(cp, "background ", 11) == 0) {
2770 #if COLOR
2771 if (resource.use_color && scanned_page_reset >= 0) {
2772 /* turn on scanning and redraw the page */
2773 scanned_page = scanned_page_color = scanned_page_reset = -1;
2774 # if PS
2775 scanned_page_ps = scanned_page;
2776 # endif
2777 /* fprintf(stderr, "forcing redraw!\n"); */
2778 globals.ev.flags |= EV_NEWPAGE; /* force a redraw */
2779 longjmp(globals.ev.canit, 1);
2780 }
2781 #endif /* COLOR */
2782 return;
2783 }
2784
2785 if (memcmp(cp, "papersize", 9) == 0 && endofcommand(cp + 9) != NULL) {
2786 m_have_papersize_special = True;
2787 if (scanned_page_reset >= 0) {
2788 /* turn on scanning and redraw the page */
2789 scanned_page = scanned_page_reset = -1;
2790 #if PS
2791 scanned_page_ps = scanned_page;
2792 #endif
2793 #if COLOR
2794 scanned_page_color = scanned_page;
2795 #endif
2796 globals.ev.flags |= EV_NEWPAGE; /* force a redraw */
2797 longjmp(globals.ev.canit, 1);
2798 }
2799 return;
2800 }
2801
2802 /* tpic specials */
2803
2804 if (*cp >= 'a' && *cp <= 'z' && cp[1] >= 'a' && cp[1] <= 'z' &&
2805 (isspace((int)cp[2]) || cp[2] == '\0')) {
2806 switch (CMD(*cp, cp[1])) {
2807 case CMD('p', 'n'):
2808 set_pen_size(cp + 2);
2809 return;
2810 case CMD('f', 'p'):
2811 flush_path();
2812 return;
2813 case CMD('d', 'a'):
2814 flush_dashed(cp + 2, False);
2815 return;
2816 case CMD('d', 't'):
2817 flush_dashed(cp + 2, True);
2818 return;
2819 case CMD('p', 'a'):
2820 add_path(cp + 2);
2821 return;
2822 case CMD('a', 'r'):
2823 arc(cp + 2, False);
2824 return;
2825 case CMD('i', 'a'):
2826 arc(cp + 2, True);
2827 return;
2828 case CMD('s', 'p'):
2829 flush_spline();
2830 return;
2831 case CMD('s', 'h'):
2832 shade_last();
2833 return;
2834 case CMD('w', 'h'):
2835 whiten_last();
2836 return;
2837 case CMD('b', 'k'):
2838 blacken_last();
2839 return;
2840 case CMD('h', 'e'):
2841 psh_header(cp); return;
2842 case CMD('i', 'p'): /* throw away the path -- jansteen */
2843 path_len = 0;
2844 return;
2845 }
2846 }
2847
2848 if (memcmp(cp, "src:", 4) == 0) {
2849 have_src_specials = True;
2850 }
2851 else if (globals.warn_spec_now)
2852 XDVI_WARNING((stderr, "Special \"%s\" not implemented.", cp));
2853 }
2854
2855 #undef CMD
2856
2857
2858 Boolean
scan_special(char * cp,int cp_len,void * data)2859 scan_special(char *cp, int cp_len, void *data)
2860 {
2861 char *p;
2862 Boolean dummy_ret = True; /* currently unused;
2863 FIXME: use return value to avoid redundant calls (instead of longjmp()) */
2864 UNUSED(cp_len); /* TODO: could probably utilize this in call to htex_prescan_special() */
2865 ASSERT(data != NULL, "Must pass a data pointer when using HTEX");
2866
2867 if (globals.debug & DBG_PS)
2868 printf("Scanning special `%s'.\n", cp);
2869
2870 while (isspace((int)*cp))
2871 ++cp;
2872
2873 /* Ignore initial "xdvi:" */
2874 if (memcmp(cp, "xdvi:", 5) == 0) {
2875 cp += 5;
2876 while (isspace((int)*cp))
2877 ++cp;
2878 }
2879
2880 if (memicmp(cp, "ps:", 3) == 0) {
2881 Boolean found;
2882 cp += 3;
2883 /* check for hdvips hyperlinks */
2884 if (memicmp(cp, "sdict begin ", strlen("sdict begin ")) == 0) {
2885 static Boolean hypertex_too_old = False;
2886 char *match = NULL;
2887 cp += strlen("sdict begin ");
2888
2889 if (strstr(cp, "HyperStart") != NULL)
2890 hypertex_too_old = True;
2891 if (hypertex_too_old)
2892 return False;
2893
2894 if (memcmp(cp, "H.S", 3) == 0
2895 || memcmp(cp, "H.R", 3) == 0
2896 || memcmp(cp, "H.B", 3) == 0
2897 /* following 2 conditions could be more restrictive: between `begin' and H.A/H.L,
2898 there should be a single number (baselineskip in pt) */
2899 || (match = strstr(cp, "H.A")) != NULL
2900 || (match = strstr(cp, "H.L")) != NULL
2901 || (match = strstr(cp, "/Action")) != NULL
2902 || (match = strstr(cp, "/Link")) != NULL
2903 || (match = strstr(cp, "/View")) != NULL) {
2904 if (match != NULL)
2905 found = htex_prescan_special(match, cp_len, data);
2906 else
2907 found = htex_prescan_special(cp, cp_len, data);
2908 }
2909 }
2910
2911 }
2912 else if (memicmp(cp, "html:", strlen("html:")) == 0) {
2913 Boolean found;
2914 size_t offset = strlen("html:");
2915 found = htex_prescan_special(cp + offset, cp_len - offset, data);
2916 /* if searching for a specific string, return as soon as it's found - not yet implemented */
2917 #if 0
2918 if (my_data != NULL && my_data->scan_type == HTEX_ANCHOR_STRING && found) {
2919 return True;
2920 }
2921 #endif
2922 }
2923 /* do the following only if not searching for an anchor string */
2924 if (((struct htex_prescan_data *)data)->scan_type != HTEX_ANCHOR_STRING) {
2925 #if PS
2926 # if COLOR
2927 if (scanned_page_ps <= scanned_page)
2928 # endif
2929 {
2930 if (*cp == '!') {
2931 scan_bang(cp);
2932 return dummy_ret;
2933 }
2934 else if (memicmp(cp, "header", 6) == 0 && (p = endofcommand(cp + 6)) != NULL) {
2935 scan_header(p);
2936 return dummy_ret;
2937 }
2938 }
2939 #endif /* PS */
2940
2941 #if COLOR
2942 # if PS
2943 if (scanned_page_color <= scanned_page)
2944 # endif
2945 {
2946 if (memicmp(cp, "background ", 11) == 0) {
2947 scan_bg_color(cp);
2948 return dummy_ret;
2949 }
2950 else if (memicmp(cp, "color ", 6) == 0) {
2951 scan_color(cp);
2952 return dummy_ret;
2953 }
2954 }
2955 #endif /* COLOR */
2956 if (memcmp(cp, "papersize", 9) == 0 && (p = endofcommand(cp + 9)) != NULL) {
2957 m_have_papersize_special = True;
2958 scan_papersize(p);
2959 return dummy_ret;
2960 }
2961 }
2962 return dummy_ret;
2963 }
2964
2965
2966 /* #define xspell_conv(n) spell_conv0(n, current_dimconv) */
2967 #define xpixel_conv(x) ((int) ((x) >> 16))
2968 #define G_PXL_H xpixel_conv(currinf.data.dvi_h)
2969
2970 /* cp is not const, because of endofcommand(). */
2971 void
geom_do_special(struct scan_info * info,char * cp,double current_dimconv)2972 geom_do_special(struct scan_info *info, char *cp, double current_dimconv)
2973 {
2974 const char *p;
2975 struct geom_info *g_info = (struct geom_info *)info->data;
2976
2977 UNUSED(current_dimconv);
2978
2979 while (isspace((int)*cp))
2980 ++cp;
2981
2982 /* Ignore initial "xdvi:" */
2983 if (memcmp(cp, "xdvi:", 5) == 0) {
2984 cp += 5;
2985 while (isspace((int)*cp))
2986 ++cp;
2987 }
2988
2989 if (memicmp(cp, "psfile", 6) == 0 && (p = endofcommand(cp + 6)) != NULL) {
2990 /* compute epsf bounding box */
2991 char c;
2992 int flags = 0;
2993 double keyval[6];
2994
2995 c = *p;
2996 if (c == '\'' || c == '"') {
2997 do
2998 ++p;
2999 while (*p != '\0' && *p != c);
3000 }
3001 else
3002 while (*p != '\0' && !isspace((int)*p))
3003 ++p;
3004 while (isspace((int)*p))
3005 ++p;
3006 while (*p != '\0') {
3007 const char *p1 = p;
3008 size_t keyno;
3009
3010 while (*p1 != '=' && !isspace((int)*p1) && *p1 != '\0')
3011 ++p1;
3012 for (keyno = 0; keyno < NKEYS; ++keyno) {
3013 if (memcmp(p, keytab[keyno], p1 - p) == 0) {
3014 if (keyno >= N_ARGLESS_KEYS) {
3015 while (isspace((int)*p1))
3016 ++p1;
3017 if (*p1 == '=') {
3018 ++p1;
3019 while (isspace((int)*p1))
3020 ++p1;
3021 }
3022 if (keyno < N_ARGLESS_KEYS + 6) {
3023 keyval[keyno - N_ARGLESS_KEYS] = atof(p1);
3024 flags |= (1 << (keyno - N_ARGLESS_KEYS));
3025 }
3026 while (!isspace((int)*p1) && *p1 != '\0')
3027 ++p1;
3028 }
3029 break;
3030 }
3031 }
3032 p = p1;
3033 while (!isspace((int)*p) && *p != '\0')
3034 ++p;
3035 while (isspace((int)*p))
3036 ++p;
3037 }
3038
3039 if ((flags & 0x30) == 0x30 || ((flags & 0x30) && (flags & 0xf) == 0xf)) {
3040 long x = G_PXL_H;
3041 long y = PXL_V;
3042 long bbox_w;
3043 long bbox_h;
3044
3045 bbox_w = 0.1 * ((flags & 0x10) ? KEY_RWI
3046 : KEY_RHI * (KEY_URX - KEY_LLX) / (KEY_URY -
3047 KEY_LLY)) *
3048 dimconv + 0.5;
3049 bbox_h =
3050 0.1 * ((flags & 0x20) ? KEY_RHI : KEY_RWI *
3051 (KEY_URY - KEY_LLY) / (KEY_URX - KEY_LLX))
3052 * dimconv + 0.5;
3053
3054 g_info->geom_box(info, x, y - bbox_h, x + bbox_w, y);
3055 }
3056 }
3057 else if (memicmp(cp, "ps::[begin]", 11) == 0) {
3058 /* compute psfig bounding box */
3059 long bbox_w, bbox_h;
3060
3061 if (sscanf(cp + 11, "%ld %ld\n", &bbox_w, &bbox_h) >= 2) {
3062 long x = G_PXL_H;
3063 long y = PXL_V;
3064
3065 bbox_w = xpixel_conv(spell_conv(bbox_w));
3066 bbox_h = xpixel_conv(spell_conv(bbox_h));
3067
3068 g_info->geom_box(info, x, y, x + bbox_w, y + bbox_h);
3069 }
3070 }
3071 }
3072