summaryrefslogtreecommitdiff
path: root/Build/source/texk/xdvik/special.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/xdvik/special.c')
-rw-r--r--Build/source/texk/xdvik/special.c2776
1 files changed, 2776 insertions, 0 deletions
diff --git a/Build/source/texk/xdvik/special.c b/Build/source/texk/xdvik/special.c
new file mode 100644
index 00000000000..ec770c89983
--- /dev/null
+++ b/Build/source/texk/xdvik/special.c
@@ -0,0 +1,2776 @@
+/*========================================================================*\
+
+Copyright (c) 1990-2004 Paul Vojta
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+NOTE:
+This module is based on prior work as noted below.
+
+\*========================================================================*/
+
+/*
+ * Support drawing routines for TeXsun and TeX
+ *
+ * Copyright, (C) 1987, 1988 Tim Morgan, UC Irvine
+ * Adapted for xdvi by Jeffrey Lee, U. of Toronto
+ *
+ * At the time these routines are called, the values of hh and vv should
+ * have been updated to the upper left corner of the graph (the position
+ * the \special appears at in the dvi file). Then the coordinates in the
+ * graphics commands are in terms of a virtual page with axes oriented the
+ * same as the Imagen and the SUN normally have:
+ *
+ * 0,0
+ * +-----------> +x
+ * |
+ * |
+ * |
+ * \ /
+ * +y
+ *
+ * Angles are measured in the conventional way, from +x towards +y.
+ * Unfortunately, that reverses the meaning of "counterclockwise"
+ * from what it's normally thought of.
+ *
+ * A lot of floating point arithmetic has been converted to integer
+ * arithmetic for speed. In some places, this is kind-of kludgy, but
+ * it's worth it.
+ */
+
+
+#include <setjmp.h>
+
+#include "xdvi-config.h"
+#include "xdvi.h"
+
+#include <ctype.h>
+
+#include "kpathsea/c-fopen.h"
+#include "kpathsea/c-stat.h"
+#include "kpathsea/line.h"
+#include "kpathsea/tex-file.h"
+
+#include "special.h"
+#include "hypertex.h"
+#include "dvi.h"
+#include "message-window.h"
+#include "events.h"
+#include "dvi-init.h"
+#include "dvi-draw.h"
+#include "statusline.h"
+#include "util.h"
+#include "image-magick.h"
+#include "pagesel.h"
+#include "my-snprintf.h"
+#include "string-utils.h"
+
+#if PS
+# ifdef PS_DPS
+# include "psdps.h"
+# endif
+# ifdef PS_NEWS
+# include "psnews.h"
+# endif
+# ifdef PS_GS
+# include "psgs.h"
+# endif
+
+/*
+ Code inside BUG_888087_FIXED improve the appearance of pic specials at magstep 1, but
+ _decreases_ it at other magnifications (bug #888087; see
+ http://sourceforge.net/tracker/index.php?func=detail&aid=888087&group_id=23164&atid=377580)
+ Disabled until this is fixed.
+*/
+#define BUG_888087_FIXED 0
+
+
+/*
+ Flag whether this page contains raw PS material;
+ used to warn user about this:
+*/
+Boolean have_raw_postscript = False;
+
+# if PS_GS && GS_PIXMAP_CLEARING_HACK
+/*
+ Flag whether this page contains PS material;
+ used to flush the GS buffer
+*/
+Boolean had_ps_specials = False;
+# endif /* PS_GS && GS_PIXMAP_CLEARING_HACK */
+
+#endif
+
+
+#if COLOR
+
+/*
+ * Color stack used when scanning. These records stay around, to ease
+ * the burden on xmalloc(). The first entry is a dummy entry, giving
+ * the default foreground color (as modified).
+ */
+
+struct colorframe {
+ struct colorframe *next, *prev;
+ struct rgb color;
+};
+
+static Boolean m_have_papersize_special = False;
+
+void reset_papersize_special(void)
+{
+ m_have_papersize_special = False;
+}
+
+Boolean have_papersize_special(void)
+{
+ return m_have_papersize_special;
+}
+
+static struct colorframe scanstack_head;
+static int scanstack_len;
+static struct colorframe *scanstack_current;
+
+/*
+ * Page stack when displaying. Again, the records stay around once
+ * allocated. Bottom entries in the stack (inherited from previous
+ * pages) are stored in an array instead (see comments in xdvi.h).
+ */
+
+static struct colorframe *rcs_head;
+
+static Boolean parse_color (const char *cp0, const char *cp, struct rgb *rgbp, Boolean generic_ps_flag);
+
+#endif /* COLOR */
+
+
+#ifndef S_IRUSR
+# define S_IRUSR 0400
+#endif
+#ifndef S_IWUSR
+# define S_IWUSR 0200
+#endif
+
+# if HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+# endif
+# ifndef WEXITSTATUS
+# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+# endif
+# ifndef WIFEXITED
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+# endif
+
+#define MAXPOINTS 300 /* Max points in a path */
+#define MAX_PEN_SIZE 7 /* Max pixels of pen width */
+#define TWOPI (3.14159265359 * 2.0)
+
+extern double floor(double);
+#define rint(x) floor((x) + 0.5)
+
+static int xx[MAXPOINTS], yy[MAXPOINTS]; /* Path in milli-inches */
+static int path_len = 0; /* # points in current path */
+static int pen_size = 1; /* Pixel width of lines drawn */
+
+static Boolean whiten = False;
+static Boolean shade = False;
+static Boolean blacken = False;
+
+Boolean psfig_begun = False;
+
+#define CMD(x, y) ((x) << 8 | (y))
+
+
+/*
+ * X drawing routines
+ */
+
+#define toint(x) ((int) ((x) + 0.5))
+#define xconv(x) (toint(tpic_conv*(x))/currwin.shrinkfactor + PXL_H)
+#define yconv(y) (toint(tpic_conv*(y))/currwin.shrinkfactor + PXL_V)
+
+/*
+ * Draw a line from (fx,fy) to (tx,ty).
+ */
+static void
+line_btw(int fx, int fy, int tx, int ty)
+{
+ int fcx = xconv(fx);
+ int tcx = xconv(tx);
+ int fcy = yconv(fy);
+ int tcy = yconv(ty);
+
+ 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) &&
+ (fcy < globals.win_expose.max_y || tcy < globals.win_expose.max_y) && (fcy >= globals.win_expose.min_y || tcy >= globals.win_expose.min_y)) {
+#if COLOR
+ if (fg_active != fg_current)
+ do_color_change();
+#endif
+ XDrawLine(DISP, currwin.win, globals.gc.rule,
+ fcx - currwin.base_x, fcy - currwin.base_y,
+ tcx - currwin.base_x, tcy - currwin.base_y);
+ }
+}
+
+/*
+ * Draw a dot at (x,y)
+ */
+static void
+dot_at(int x, int y)
+{
+ int cx = xconv(x);
+ int cy = yconv(y);
+
+ 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){
+#if COLOR
+ if (fg_active != fg_current)
+ do_color_change();
+#endif
+ XDrawPoint(DISP, currwin.win, globals.gc.rule,
+ cx - currwin.base_x, cy - currwin.base_y);
+ }
+}
+
+/*
+ * Apply the requested attributes to the last path (box) drawn.
+ * Attributes are reset.
+ * (Not currently implemented.)
+ */
+static void
+do_attribute_path(int last_min_x, int last_max_x, int last_min_y, int last_max_y)
+{
+ UNUSED(last_min_x);
+ UNUSED(last_max_x);
+ UNUSED(last_min_y);
+ UNUSED(last_max_y);
+}
+
+/*
+ * Set the size of the virtual pen used to draw in milli-inches
+ */
+static void
+set_pen_size(char *cp)
+{
+ int ps;
+
+ if (sscanf(cp, " %d ", &ps) != 1) {
+ XDVI_WARNING((stderr, "invalid .ps command format: %s", cp));
+ return;
+ }
+ pen_size = (2 * ps * resource.pixels_per_inch / currwin.shrinkfactor + 1000) / 2000;
+ if (pen_size < 1)
+ pen_size = 1;
+ else if (pen_size > MAX_PEN_SIZE)
+ pen_size = MAX_PEN_SIZE;
+
+#if BUG_888087_FIXED
+ if (globals.gc.rule) {
+ XGCValues values;
+ values.line_width = pen_size;
+ XChangeGC(DISP, globals.gc.rule, GCLineWidth, &values);
+ }
+#endif
+}
+
+
+/*
+ * Print the line defined by previous path commands
+ */
+
+static void
+flush_path(void)
+{
+ int i;
+ int last_min_x, last_max_x, last_min_y, last_max_y;
+
+ last_min_x = 30000;
+ last_min_y = 30000;
+ last_max_x = -30000;
+ last_max_y = -30000;
+ for (i = 1; i < path_len; i++) {
+ if (xx[i] > last_max_x)
+ last_max_x = xx[i];
+ if (xx[i] < last_min_x)
+ last_min_x = xx[i];
+ if (yy[i] > last_max_y)
+ last_max_y = yy[i];
+ if (yy[i] < last_min_y)
+ last_min_y = yy[i];
+ line_btw(xx[i], yy[i], xx[i + 1], yy[i + 1]);
+ }
+ if (xx[path_len] > last_max_x)
+ last_max_x = xx[path_len];
+ if (xx[path_len] < last_min_x)
+ last_min_x = xx[path_len];
+ if (yy[path_len] > last_max_y)
+ last_max_y = yy[path_len];
+ if (yy[path_len] < last_min_y)
+ last_min_y = yy[path_len];
+ path_len = 0;
+ do_attribute_path(last_min_x, last_max_x, last_min_y, last_max_y);
+}
+
+
+/*
+ * Print a dashed line along the previously defined path, with
+ * the dashes/inch defined.
+ */
+
+static void
+flush_dashed(char *cp, Boolean dotted)
+{
+ int i;
+ int numdots;
+ int lx0, ly0, lx1, ly1;
+ int cx0, cy0, cx1, cy1;
+ float inchesperdash;
+ double d, spacesize, a, b = 0.0, dx, dy, milliperdash;
+
+ if (sscanf(cp, " %f ", &inchesperdash) != 1) {
+ XDVI_WARNING((stderr, "invalid format for dotted/dashed line: %s", cp));
+ return;
+ }
+ if (path_len <= 1 || inchesperdash <= 0.0) {
+ XDVI_WARNING((stderr, "invalid conditions for dotted/dashed line"));
+ return;
+ }
+ milliperdash = inchesperdash * 1000.0;
+ lx0 = xx[1];
+ ly0 = yy[1];
+ lx1 = xx[2];
+ ly1 = yy[2];
+ dx = lx1 - lx0;
+ dy = ly1 - ly0;
+ if (dotted) {
+ numdots = sqrt(dx * dx + dy * dy) / milliperdash + 0.5;
+ if (numdots == 0)
+ numdots = 1;
+ for (i = 0; i <= numdots; i++) {
+ a = (float)i / (float)numdots;
+ cx0 = lx0 + a * dx + 0.5;
+ cy0 = ly0 + a * dy + 0.5;
+ dot_at(cx0, cy0);
+ }
+ }
+ else {
+ d = sqrt(dx * dx + dy * dy);
+ numdots = d / (2.0 * milliperdash) + 1.0;
+ if (numdots <= 1)
+ line_btw(lx0, ly0, lx1, ly1);
+ else {
+ spacesize = (d - numdots * milliperdash) / (numdots - 1);
+ for (i = 0; i < numdots - 1; i++) {
+ a = i * (milliperdash + spacesize) / d;
+ b = a + milliperdash / d;
+ cx0 = lx0 + a * dx + 0.5;
+ cy0 = ly0 + a * dy + 0.5;
+ cx1 = lx0 + b * dx + 0.5;
+ cy1 = ly0 + b * dy + 0.5;
+ line_btw(cx0, cy0, cx1, cy1);
+ b += spacesize / d;
+ }
+ cx0 = lx0 + b * dx + 0.5;
+ cy0 = ly0 + b * dy + 0.5;
+ line_btw(cx0, cy0, lx1, ly1);
+ }
+ }
+ path_len = 0;
+}
+
+
+/*
+ * Add a point to the current path
+ */
+
+static void
+add_path(char *cp)
+{
+ int pathx, pathy;
+
+ if (++path_len >= MAXPOINTS)
+ XDVI_FATAL((stderr, "Too many points"));
+ if (sscanf(cp, " %d %d ", &pathx, &pathy) != 2)
+ XDVI_FATAL((stderr, "Malformed path command"));
+ xx[path_len] = pathx;
+ yy[path_len] = pathy;
+}
+
+
+/*
+ * Draw to a floating point position
+ */
+
+static void
+im_fdraw(double x, double y)
+{
+ if (++path_len >= MAXPOINTS)
+ XDVI_FATAL((stderr, "Too many arc points"));
+ xx[path_len] = x + 0.5;
+ yy[path_len] = y + 0.5;
+}
+
+
+/*
+ * Draw an ellipse with the indicated center and radices.
+ */
+
+static void
+draw_ellipse(int xc, int yc, int xr, int yr)
+{
+ double angle, theta;
+ int n;
+ int px0, py0, px1, py1;
+
+ angle = (xr + yr) / 2.0;
+ theta = sqrt(1.0 / angle);
+ n = TWOPI / theta + 0.5;
+ if (n < 12)
+ n = 12;
+ else if (n > 80)
+ n = 80;
+ n /= 2;
+ theta = TWOPI / n;
+
+ angle = 0.0;
+ px0 = xc + xr; /* cos(0) = 1 */
+ py0 = yc; /* sin(0) = 0 */
+ while ((angle += theta) <= TWOPI) {
+ px1 = xc + xr * cos(angle) + 0.5;
+ py1 = yc + yr * sin(angle) + 0.5;
+ line_btw(px0, py0, px1, py1);
+ px0 = px1;
+ py0 = py1;
+ }
+ line_btw(px0, py0, xc + xr, yc);
+}
+
+/*
+ * Draw an arc
+ */
+
+static void
+arc(char *cp, Boolean invis)
+{
+ int xc, yc, xrad, yrad, n;
+ float start_angle, end_angle, angle, theta, r;
+ double xradius, yradius, xcenter, ycenter;
+
+ n = sscanf(cp, " %d %d %d %d %f %f ", &xc, &yc, &xrad, &yrad,
+ &start_angle, &end_angle);
+
+ if (n != 6) {
+ XDVI_WARNING((stderr, "invalid arc specification: %s", cp));
+ return;
+ }
+
+ if (invis)
+ return;
+
+ /* We have a specialized fast way to draw closed circles/ellipses */
+ if (start_angle <= 0.0 && end_angle >= 6.282) {
+ draw_ellipse(xc, yc, xrad, yrad);
+ return;
+ }
+ xcenter = xc;
+ ycenter = yc;
+ xradius = xrad;
+ yradius = yrad;
+ r = (xradius + yradius) / 2.0;
+ theta = sqrt(1.0 / r);
+#if BUG_888087_FIXED
+ n = (pen_size * TWOPI) / (theta * currwin.shrinkfactor) + 0.5;
+#else
+ n = 0.3 * TWOPI / theta + 0.5;
+#endif
+ if (n < 12)
+ n = 12;
+ else if (n > 80)
+ n = 80;
+ n /= 2;
+ theta = TWOPI / n;
+ flush_path();
+ im_fdraw(xcenter + xradius * cos(start_angle),
+ ycenter + yradius * sin(start_angle));
+ angle = start_angle + theta;
+ if (end_angle < start_angle)
+ end_angle += TWOPI;
+ while (angle < end_angle) {
+ im_fdraw(xcenter + xradius * cos(angle),
+ ycenter + yradius * sin(angle));
+ angle += theta;
+ }
+ im_fdraw(xcenter + xradius * cos(end_angle),
+ ycenter + yradius * sin(end_angle));
+ flush_path();
+}
+
+
+/*
+ * APPROXIMATE integer distance between two points
+ */
+
+#define dist(x0, y0, x1, y1) (abs(x0 - x1) + abs(y0 - y1))
+
+
+/*
+ * Draw a spline along the previously defined path
+ */
+
+static void
+flush_spline(void)
+{
+ int xp, yp;
+ int N;
+ int lastx = -1, lasty = -1;
+ Boolean lastvalid = False;
+ int t1, t2, t3;
+ int steps;
+ int j;
+ int i, w;
+
+ N = path_len + 1;
+ xx[0] = xx[1];
+ yy[0] = yy[1];
+ xx[N] = xx[N - 1];
+ yy[N] = yy[N - 1];
+ for (i = 0; i < N - 1; i++) { /* interval */
+ steps = (dist(xx[i], yy[i], xx[i + 1], yy[i + 1]) +
+ dist(xx[i + 1], yy[i + 1], xx[i + 2], yy[i + 2])) / 80;
+ for (j = 0; j < steps; j++) { /* points within */
+ w = (j * 1000 + 500) / steps;
+ t1 = w * w / 20;
+ w -= 500;
+ t2 = (750000 - w * w) / 10;
+ w -= 500;
+ t3 = w * w / 20;
+ xp =
+ (t1 * xx[i + 2] + t2 * xx[i + 1] + t3 * xx[i] + 50000) / 100000;
+ yp =
+ (t1 * yy[i + 2] + t2 * yy[i + 1] + t3 * yy[i] + 50000) / 100000;
+ if (lastvalid)
+ line_btw(lastx, lasty, xp, yp);
+ lastx = xp;
+ lasty = yp;
+ lastvalid = True;
+ }
+ }
+ path_len = 0;
+}
+
+
+/*
+ * Shade the last box, circle, or ellipse
+ */
+
+static void
+shade_last(void)
+{
+ blacken = whiten = False;
+ shade = True;
+}
+
+
+/*
+ * Make the last box, circle, or ellipse, white inside (shade with white)
+ */
+
+static void
+whiten_last(void)
+{
+ whiten = True;
+ blacken = shade = False;
+}
+
+
+/*
+ * Make last box, etc, black inside
+ */
+
+static void
+blacken_last(void)
+{
+ blacken = True;
+ whiten = shade = False;
+}
+
+
+/*
+ * Code for PostScript<tm> specials begins here.
+ */
+
+#if PS
+
+/*
+ * Information on how to search for PS header and figure files.
+ */
+
+static void ps_startup(int, int, const char *);
+static void ps_startup2(void);
+
+/* dummy procedures */
+static void NullProc(void) { }
+static void NullProcInt(int flag) { UNUSED(flag); }
+static void NullProcStr(const char *cp) { UNUSED(cp); }
+
+
+struct psprocs psp = { /* used for lazy startup of the ps machinery */
+ /* toggle */ NullProcInt,
+ /* destroy */ NullProc,
+ /* interrupt */ NullProc,
+ /* endpage */ NullProc,
+ /* drawbegin */ ps_startup,
+ /* drawraw */ NullProcStr,
+ /* drawfile */ NULL,
+ /* drawend */ NullProcStr,
+ /* beginheader */ ps_startup2,
+ /* endheader */ NullProc,
+ /* newdoc */ NullProc
+};
+
+struct psprocs no_ps_procs = { /* used if postscript is unavailable */
+ /* toggle */ NullProcInt,
+ /* destroy */ NullProc,
+ /* interrupt */ NullProc,
+ /* endpage */ NullProc,
+ /* drawbegin */ drawbegin_none,
+ /* drawraw */ NullProcStr,
+ /* drawfile */ NULL,
+ /* drawend */ NullProcStr,
+ /* beginheader */ NullProc,
+ /* endheader */ NullProc,
+ /* newdoc */ NullProc
+};
+
+#endif /* PS */
+
+#ifdef MAGICK
+int bbox_angle;
+Boolean bbox_valid;
+unsigned int bbox_width;
+unsigned int bbox_height;
+int bbox_voffset;
+#else
+static int bbox_angle;
+static Boolean bbox_valid;
+static unsigned int bbox_width;
+static unsigned int bbox_height;
+static int bbox_voffset;
+#endif
+
+/* info on bboxes on this page */
+struct bbox_info {
+ int x;
+ int y;
+ int w;
+ int h;
+ int angle;
+};
+
+static struct bbox_info *g_bbox_info = NULL;
+static size_t g_bbox_info_size = 0;
+static size_t g_bbox_info_max_size = 0;
+
+/*
+ Append the current coordinates to g_bbox_info, unless it already
+ contains these coordinates.
+ */
+
+static void
+append_bbox_info(int x, int y, int w, int h, int angle)
+{
+ Boolean found = False;
+ size_t i;
+ const size_t SIZE_STEP = 16;
+
+ /* is this box already present? */
+ for (i = 0; i < g_bbox_info_size; i++) {
+ if (g_bbox_info[i].x == x
+ && g_bbox_info[i].y == y
+ && g_bbox_info[i].w == w
+ && g_bbox_info[i].h == h
+ && g_bbox_info[i].angle == angle) {
+ found = True;
+ break;
+ }
+ }
+
+ if (!found) {
+ g_bbox_info_size++;
+
+ while (g_bbox_info_size >= g_bbox_info_max_size) {
+ g_bbox_info_max_size += SIZE_STEP;
+ }
+
+ g_bbox_info = xrealloc(g_bbox_info, g_bbox_info_max_size * sizeof *g_bbox_info);
+ g_bbox_info[g_bbox_info_size - 1].x = x;
+ g_bbox_info[g_bbox_info_size - 1].y = y;
+ g_bbox_info[g_bbox_info_size - 1].w = w;
+ g_bbox_info[g_bbox_info_size - 1].h = h;
+ g_bbox_info[g_bbox_info_size - 1].angle = angle;
+ }
+}
+
+static void
+draw_bbox0(int xcorner, int ycorner)
+{
+ if (bbox_valid) {
+#if COLOR
+ if (fg_active != fg_current)
+ do_color_change();
+#endif
+
+ if (bbox_angle == 0) {
+ ycorner -= bbox_voffset;
+ XDrawRectangle(DISP, currwin.win, globals.gc.high, xcorner, ycorner, bbox_width, bbox_height);
+ if (resource.postscript == 0) {
+ if (htex_inside_href) {
+ htex_set_anchorsize(xcorner, ycorner, xcorner + bbox_width, ycorner + bbox_height);
+ htex_set_objecttype(HTEX_IMG);
+ }
+ }
+ }
+ else {
+ float sin_a = sin(bbox_angle * (TWOPI / 360));
+ float cos_a = cos(bbox_angle * (TWOPI / 360));
+ float a, b, c, d;
+
+ a = cos_a * bbox_width;
+ b = -sin_a * bbox_width;
+ c = -sin_a * bbox_height;
+ d = -cos_a * bbox_height;
+
+ XDrawLine(DISP, currwin.win, globals.gc.high,
+ xcorner, ycorner,
+ xcorner + (int)rint(a), ycorner + (int)rint(b));
+ XDrawLine(DISP, currwin.win, globals.gc.high,
+ xcorner + (int)rint(a), ycorner + (int)rint(b),
+ xcorner + (int)rint(a + c), ycorner + (int)rint(b + d));
+ XDrawLine(DISP, currwin.win, globals.gc.high,
+ xcorner + (int)rint(a + c), ycorner + (int)rint(b + d),
+ xcorner + (int)rint(c), ycorner + (int)rint(d));
+ XDrawLine(DISP, currwin.win, globals.gc.high,
+ xcorner + (int)rint(c), ycorner + (int)rint(d),
+ xcorner, ycorner);
+ }
+ bbox_valid = False;
+ }
+}
+
+/*
+ Display the items in bbox_info.
+ Invoked from draw_page() in dvi-draw.c.
+ */
+void
+display_bboxes(void)
+{
+ size_t i;
+
+ for (i = 0; i < g_bbox_info_size; i++) {
+ if (globals.debug & DBG_PS) {
+ fprintf(stderr, "drawing bbox %lu at %d %d, %d x %d, angle %d\n",
+ (unsigned long)i,
+ g_bbox_info[i].x,
+ g_bbox_info[i].y,
+ g_bbox_info[i].w,
+ g_bbox_info[i].h,
+ g_bbox_info[i].angle);
+ }
+#if 0
+ XDrawRectangle(DISP, currwin.win, globals.gc.high,
+ g_bbox_info[i].x,
+ g_bbox_info[i].y,
+ g_bbox_info[i].w,
+ g_bbox_info[i].h);
+#else
+ bbox_valid = True;
+ bbox_width = g_bbox_info[i].w;
+ bbox_height = bbox_voffset = g_bbox_info[i].h;
+ bbox_angle = g_bbox_info[i].angle;
+ draw_bbox0(g_bbox_info[i].x, g_bbox_info[i].y + bbox_height);
+#endif
+ }
+ bbox_angle = 0;
+ bbox_valid = False;
+}
+
+void
+clear_bboxes(void)
+{
+ free(g_bbox_info);
+ g_bbox_info = NULL;
+ g_bbox_info_size = 0;
+ g_bbox_info_max_size = 0;
+}
+
+void
+save_bbox(void)
+{
+ int xcorner, ycorner;
+
+ if (bbox_valid) {
+ xcorner = PXL_H - currwin.base_x;
+ ycorner = PXL_V - currwin.base_y;
+
+ ycorner -= bbox_voffset;
+ append_bbox_info(xcorner, ycorner, bbox_width, bbox_height, bbox_angle);
+
+ /* register boundaries of this box as anchor boundaries */
+ if (htex_inside_href) {
+ htex_set_anchorsize(xcorner, ycorner, xcorner + bbox_width, ycorner + bbox_height);
+ htex_set_objecttype(HTEX_IMG);
+ }
+ }
+}
+
+
+void
+draw_bbox(void)
+{
+ draw_bbox0(PXL_H - currwin.base_x, PXL_V - currwin.base_y);
+}
+
+#if PS
+
+static XIOErrorHandler oldhandler;
+
+static int XDviIOErrorHandler(Display * disp)
+{
+ ps_destroy();
+ return oldhandler(disp);
+}
+
+static void
+actual_startup(int flag)
+{
+ UNUSED(flag);
+ oldhandler = XSetIOErrorHandler(XDviIOErrorHandler);
+
+ /*
+ * Figure out what we want to use to display postscript figures
+ * and set at most one of the following to True:
+ * resource.useGS, resource.useDPS, resource.useNeWS
+ *
+ * Choose DPS then NEWS then Ghostscript if they are available
+ */
+ if (!(
+#ifdef PS_DPS
+ (resource.useDPS && initDPS())
+#if defined(PS_NEWS) || defined(PS_GS)
+ ||
+#endif
+#endif /* PS_DPS */
+#ifdef PS_NEWS
+ (resource.useNeWS && initNeWS())
+#ifdef PS_GS
+ ||
+#endif
+#endif /* PS_NEWS */
+#ifdef PS_GS
+ (resource.useGS && initGS())
+#endif
+ ))
+ psp = no_ps_procs;
+}
+
+static void
+ps_startup(int xul, int yul, const char *cp)
+{
+ if (resource.postscript == 0) {
+ draw_bbox();
+ return;
+ }
+ actual_startup(0);
+ psp.drawbegin(xul, yul, cp);
+}
+
+static void
+ps_startup2(void)
+{
+ actual_startup(0);
+ psp.beginheader();
+}
+
+
+/*
+ * dumb parsing of PostScript - search for rotation H. Zeller 1/97
+ * Returns true if we find a potentially non-supported command that
+ * we want to warn users about.
+ */
+static Boolean
+ps_parseraw(const char *PostScript_cmd)
+{
+ const char *p;
+
+ bbox_angle = 0;
+ p = strstr(PostScript_cmd, "rotate");
+ if (p != NULL) {
+ while (*p != '\0' && !isdigit((int)*p))
+ --p;
+ while (*p != '\0' && isdigit((int)*p))
+ --p;
+ if (*p != '+' && *p != '-')
+ ++p;
+ sscanf(p, "%d neg rotate", &bbox_angle);
+ return True;
+ }
+ if (strstr(PostScript_cmd, " scale ") != NULL)
+ return True;
+ return False;
+}
+
+
+void
+drawbegin_none(int xul, int yul, const char *cp)
+{
+ UNUSED(xul);
+ UNUSED(yul);
+ UNUSED(cp);
+
+ draw_bbox();
+}
+
+
+struct tickrec {
+ struct tickrec *next;
+ int pageno;
+ char *command;
+ char *tempname;
+};
+
+static struct tickrec *tickhead = NULL; /* head of linked list of */
+/* cached information */
+static int nticks = 0; /* number of records total */
+
+#ifndef TICKCACHESIZE
+#define TICKCACHESIZE 3
+#endif
+
+#ifndef TICKTMP
+#define TICKTMP "/tmp"
+#endif
+
+/*
+ * cachetick() - returns:
+ * NULL error;
+ * fp == NULL string was not in cache, fd = file
+ * fp != NULL string was in cache, fp = cached file
+ */
+
+static struct tickrec *
+cachetick(const char *filename, kpse_file_format_type pathinfo, FILE **fp, int *fdp)
+{
+ struct tickrec **linkp;
+ struct tickrec *tikp;
+ struct tickrec **freerecp;
+
+ linkp = &tickhead;
+ freerecp = NULL;
+ for (;;) { /* see if we have it already */
+ tikp = *linkp;
+ if (tikp == NULL) { /* if end of list */
+ int fd;
+
+ if (nticks >= TICKCACHESIZE && freerecp != NULL) {
+ tikp = *freerecp;
+ *freerecp = tikp->next;
+ free(tikp->command);
+ unlink(tikp->tempname);
+ /* reuse tikp and tikp->tempname */
+ }
+ else {
+ tikp = xmalloc(sizeof(struct tickrec));
+ tikp->tempname = NULL;
+ ++nticks;
+ }
+ fd = xdvi_temp_fd(&tikp->tempname);
+ if (fd == -1) {
+ perror("Cannot create temporary file");
+ free(tikp->tempname);
+ free(tikp);
+ return NULL;
+ }
+ tikp->command = xstrdup(filename);
+ *fp = NULL;
+ *fdp = fd;
+ break;
+ }
+ if (strcmp(filename, tikp->command) == 0) { /* found it */
+ *linkp = tikp->next; /* unlink it */
+ *fp = XFOPEN(tikp->tempname, OPEN_MODE);
+ if (*fp == NULL) {
+ perror(tikp->tempname);
+ free(tikp->tempname);
+ free(tikp->command);
+ free(tikp);
+ return NULL;
+ }
+ break;
+ }
+ if (tikp->pageno != current_page)
+ freerecp = linkp;
+ linkp = &tikp->next;
+ }
+ tikp->next = tickhead; /* link it in */
+ tickhead = tikp;
+ tikp->pageno = pathinfo != kpse_tex_ps_header_format ? current_page : -1;
+ return tikp;
+}
+
+void
+ps_clear_cache(void)
+{
+ struct tickrec *tikp;
+
+ while (tickhead != NULL) {
+ tikp = tickhead;
+ tickhead = tickhead->next;
+ free(tikp->command);
+ unlink(tikp->tempname);
+ free((char *)tikp->tempname);
+ free(tikp);
+ }
+ nticks = 0;
+}
+
+#ifndef UNCOMPRESS
+#define UNCOMPRESS "uncompress"
+#endif
+
+#ifndef GUNZIP
+#define GUNZIP "gunzip"
+#endif
+
+#ifndef BUNZIP2
+#define BUNZIP2 "bunzip2"
+#endif
+
+static void send_ps_file(const char *filename, kpse_file_format_type pathinfo);
+
+static void
+enable_specials_send_ps_file(XtPointer data)
+{
+ const char *filename = (const char *)data;
+ resource.allow_shell = True;
+ redraw_page(); /* to erase the bounding box */
+ statusline_print(STATUS_MEDIUM,
+ "Shell specials enabled for this session.",
+ filename);
+ send_ps_file(filename, kpse_pict_format);
+}
+
+static void
+try_open_tempname(int status, struct xchild *this)
+{
+ if (WIFEXITED(status)) { /* child exited normally */
+ if (WEXITSTATUS(status) != 0) {
+ /* default error procedure */
+ handle_child_exit(status, this);
+ }
+ else {
+ struct tickrec *tikp = (struct tickrec *)this->data;
+ FILE *f = XFOPEN(tikp->tempname, OPEN_MODE);
+ fprintf(stderr, "FILE: %s\n", tikp->tempname);
+ if (f == NULL) {
+ perror(tikp->tempname);
+ }
+ else {
+ fprintf(stderr, "sending file: %s, %p\n", tikp->tempname, (void *)f);
+ /* There's no point in invoking
+ psp.drawfile(tikp->tempname, f);
+ here, since usually it will be already too late (draw_part() which
+ had called us via applicationDoSpecial() will already have terminated).
+ So instead, we just close the file and force a redraw of the entire page.
+ */
+ fclose(f);
+ globals.ev.flags |= EV_NEWPAGE;
+ }
+ }
+ }
+ else if (WIFSIGNALED(status)) {
+ popup_message(globals.widgets.top_level,
+ MSG_WARN,
+ NULL,
+ "Process `%s' terminated abnormally with signal %d.", this->name, WTERMSIG(status));
+ }
+ else if (WIFSTOPPED(status)) {
+ popup_message(globals.widgets.top_level,
+ MSG_WARN,
+ NULL,
+ "Process `%s' stopped by signal %d.", this->name, WSTOPSIG(status));
+ }
+ else {
+ popup_message(globals.widgets.top_level,
+ MSG_WARN,
+ NULL,
+ "Process `%s' terminated with unknown status.", this->name);
+ }
+}
+
+static void
+send_ps_file(const char *filename, kpse_file_format_type pathinfo)
+{
+ FILE *f;
+ int fd;
+ static const char *argv[] = { NULL, "-c", NULL, NULL };
+ char *bufp = NULL;
+ struct tickrec *volatile tikp;
+ size_t len;
+ char magic1, magic2, magic3;
+ static Boolean warned_about_shellescape = False;
+ static size_t ffline_len = 0;
+ static char *ffline = NULL;
+ const size_t FFLINE_STEP = 128;
+
+ if (psp.drawfile == NULL || resource.postscript == 0) {
+ return;
+ }
+
+ if (filename[0] == '`') {
+ if (!resource.allow_shell) {
+ if (!warned_about_shellescape) {
+ choice_dialog_sized(globals.widgets.top_level,
+ MSG_INFO,
+ SIZE_MEDIUM,
+ /* helptext */
+ "To enable shell specials, use the \"-allowshell\" command "
+ "line option.\n\n"
+ "WARNING: You should NOT use shell specials like\n"
+ "`gunzip -c file.eps.gz\n"
+ "(e.g. via \\DeclareGraphicsRule{...}) "
+ "to uncompress .eps.gz files, even though some obsolete "
+ "LaTeX documentation might suggest it. Current versions "
+ "of xdvi and dvips will handle .eps.gz files just fine without this trick.\n",
+#ifndef MOTIF
+ NULL,
+#endif
+ NULL, NULL, /* no pre_callbacks */
+ /* Cancel label/callback */
+ "Cancel", NULL, (XtPointer)NULL,
+ /* Enable label/callback */
+ "Enable", enable_specials_send_ps_file, (XtPointer)filename,
+ /* msg */
+ "This page contains a shell special \"%s\", but execution of shell specials "
+ "is disabled.\n\nYou can now click "
+ "\"Enable\" to enable shell specials for this session only, "
+ "\"Cancel\" to leave the specials disabled, or \"Help\" for more help "
+ "on this topic.\n\n"
+ "Please note that shell specials are a security risk, since they "
+ "allow execution of arbitrary shell commands from the TeX file. "
+ "You should enable them only for DVI files that you created yourself.",
+ filename);
+ warned_about_shellescape = True;
+ }
+ else {
+ statusline_print(STATUS_MEDIUM,
+ "Info: Shell special \"%s\" disabled.",
+ filename);
+ }
+ draw_bbox();
+ return;
+ }
+
+ tikp = cachetick(filename, pathinfo, &f, &fd);
+ if (tikp == NULL) /* if error */
+ return;
+ if (f == NULL) {
+ char *argv[4];
+ /* if not cached, need to create.
+ Fork so that we can collect the process' error messages. */
+
+ /* FIXME: Insecure - this is a /tmp race; shouldn't close(fd), just re-use it */
+ close(fd);
+
+ len = strlen(filename) + strlen(tikp->tempname) + (4 - 1);
+ if (len > ffline_len) {
+ ffline_len += FFLINE_STEP;
+ ffline = xrealloc(ffline, ffline_len);
+ }
+
+ sprintf(ffline, "%s > %s", filename + 1, tikp->tempname);
+ argv[0] = "/bin/sh";
+ argv[1] = "-c";
+ argv[2] = ffline;
+ argv[3] = NULL;
+ fork_process("/bin/sh", False, globals.dvi_file.dirname, try_open_tempname, tikp, argv);
+ }
+ else {
+ bufp = tikp->tempname;
+ psp.drawfile(bufp, f);
+ }
+ }
+ else {
+ char *expanded_filename = NULL;
+ struct stat statbuf;
+ expanded_filename = find_file(filename, &statbuf, pathinfo);
+ if (expanded_filename == NULL &&
+ (pathinfo == kpse_enc_format || pathinfo == kpse_type1_format)) {
+ /* in this case, we also kpathsea-search in the `old' place for
+ backwards compatibility: kpse_tex_ps_header_format (see comment
+ for load_vector(), dvi-draw.c for details) */
+ expanded_filename = kpse_find_file(filename, kpse_tex_ps_header_format, True);
+ }
+ if (expanded_filename == NULL) { /* still no success, complain */
+ /* FIXME: this warning may be overwritten by warning about raw PS specials,
+ so additinally dump to stderr. TODO: make statusline printing respect
+ more important messages. */
+ XDVI_WARNING((stderr, "Could not find graphics file \"%s\"", filename));
+ statusline_print(STATUS_MEDIUM, "Warning: Could not find graphics file \"%s\"",
+ filename);
+ draw_bbox();
+ return;
+ }
+ if (globals.debug & DBG_OPEN)
+ printf("%s:%d: |%s| expanded to |%s|\n", __FILE__, __LINE__, filename, expanded_filename);
+
+ /* may need to adjust ffline_len, ffline */
+ while (strlen(expanded_filename) + 1 > ffline_len) {
+ ffline_len += FFLINE_STEP;
+ ffline = xrealloc(ffline, ffline_len);
+ }
+ strcpy(ffline, expanded_filename);
+ bufp = ffline;
+ f = XFOPEN(expanded_filename, OPEN_MODE);
+ free(expanded_filename);
+ ASSERT(f != NULL, "Internal error opening file");
+
+ /* check for compressed files */
+ len = strlen(filename);
+ magic1 = '\037';
+ magic3 = '\0';
+ if ((len > 2 && strcmp(filename + len - 2, ".Z") == 0
+ && (argv[0] = UNCOMPRESS, magic2 = '\235', True))
+ || (len > 3 && strcmp(filename + len - 3, ".gz") == 0
+ && (argv[0] = GUNZIP, magic2 = '\213', True))
+ || (len > 4 && strcmp(filename + len - 4, ".bz2") == 0
+ && (argv[0] = BUNZIP2, magic1 = 'B', magic2 = 'Z',
+ magic3 = 'h', True))) {
+ if (getc(f) != magic1 || (char)getc(f) != magic2
+ || (magic3 != '\0' && getc(f) != magic3)) {
+ rewind(f);
+ }
+ else {
+ fclose(f);
+
+ tikp = cachetick(filename, pathinfo, &f, &fd);
+ if (tikp == NULL) /* if error */
+ return;
+ if (f == NULL) { /* if not cached, need to create */
+ pid_t pid;
+ int status;
+
+ argv[2] = bufp;
+ fflush(stderr); /* avoid double flushing */
+ pid = vfork();
+ if (pid == 0) { /* if child */
+ (void)dup2(fd, 1);
+ (void)execvp(argv[0], (char **)argv);
+ XDVI_ERROR((stderr, "Execvp of %s failed: %s", argv[0], strerror(errno)));
+ _exit(EXIT_FAILURE);
+ }
+ (void)close(fd);
+
+ for (;;) {
+#if HAVE_WAITPID
+ if (waitpid(pid, &status, 0) != -1)
+ break;
+#else
+# if HAVE_WAIT4
+ if (wait4(pid, &status, 0, (struct rusage *)NULL)
+ != -1)
+ break;
+# else
+ int retval;
+
+ retval = wait(&status);
+ if (retval == pid)
+ break;
+ if (retval != -1)
+ continue;
+# endif /* HAVE_WAIT4 */
+#endif /* HAVE_WAITPID */
+ if (errno == EINTR)
+ continue;
+ perror("[xdvik] waitpid");
+ return;
+ }
+ f = XFOPEN(tikp->tempname, OPEN_MODE);
+ if (f == NULL) {
+ perror(tikp->tempname);
+ return;
+ }
+ }
+ bufp = tikp->tempname;
+ }
+ }
+ /* Success! */
+ psp.drawfile(bufp, f); /* this is supposed to close the file */
+ }
+}
+
+
+void
+ps_destroy(void)
+{
+ struct tickrec *tikp;
+
+ /* Note: old NeXT systems (at least) lack atexit/on_exit. */
+ psp.destroy();
+ for (tikp = tickhead; tikp != NULL; tikp = tikp->next)
+ if (unlink(tikp->tempname) < 0)
+ perror(tikp->tempname);
+}
+
+#endif /* PS */
+
+
+void
+init_prescan(void)
+{
+#if PS
+ struct tickrec *tikp;
+#endif
+
+ scanned_page = scanned_page_reset = resource.prescan ? -1 : total_pages + 1;
+#if PS
+ scanned_page_ps = scanned_page_ps_bak = scanned_page;
+#if COLOR
+ scanned_page_color = scanned_page;
+#endif /* COLOR */
+#endif /* PS */
+
+ TRACE_FILES((stderr, "init_prescan: scanned_page = %d", scanned_page));
+#if PS
+ if (resource.postscript == 0)
+ scanned_page_ps = total_pages + 1;
+
+ for (tikp = tickhead; tikp != NULL; tikp = tikp->next)
+ tikp->pageno = -1;
+ psp.newdoc();
+#endif
+
+#if COLOR
+ if (!resource.use_color)
+ scanned_page_color = total_pages + 1;
+#endif
+
+ if (ignore_papersize_specials) {
+#if PS && COLOR
+ scanned_page = scanned_page_ps < scanned_page_color ? scanned_page_ps : scanned_page_color;
+#elif PS
+ scanned_page = scanned_page_ps;
+#elif COLOR
+ scanned_page = scanned_page_color;
+#else
+ scanned_page = total_pages + 1;
+#endif
+ }
+}
+
+
+static void
+psfig_special(char *cp)
+{
+ char *filename;
+ int raww, rawh;
+
+ if (strncmp(cp, ":[begin]", 8) == 0) {
+ cp += 8;
+ bbox_valid = False;
+ bbox_angle = 0;
+ if (sscanf(cp, "%d %d\n", &raww, &rawh) >= 2) {
+ bbox_valid = True;
+ bbox_width = pixel_conv(spell_conv(raww));
+ bbox_height = pixel_conv(spell_conv(rawh));
+ bbox_voffset = 0;
+ }
+ if (INSIDE_MANE_WIN) {
+#if PS
+ psp.drawbegin(PXL_H - currwin.base_x, PXL_V - currwin.base_y, cp);
+#else
+ draw_bbox();
+#endif
+ }
+ psfig_begun = True;
+ }
+ else if (strncmp(cp, " plotfile ", 10) == 0) {
+ cp += 10;
+ while (isspace((int)*cp))
+ cp++;
+ /* handle "`zcat file". Borrowed from dvipsk... */
+ if (*cp == '"') {
+ cp++;
+ for (filename = cp; *cp && (*cp != '"'); ++cp);
+ }
+ else {
+ for (filename = cp; *cp && !isspace((int)*cp); ++cp);
+ }
+ *cp = '\0';
+#if PS
+ if (INSIDE_MANE_WIN)
+ send_ps_file(filename, kpse_pict_format);
+#endif
+ }
+ else if (strncmp(cp, ":[end]", 6) == 0) {
+ cp += 6;
+#if PS
+ if (INSIDE_MANE_WIN) {
+ psp.drawend(cp);
+ }
+#endif
+ bbox_valid = False;
+ psfig_begun = False;
+ }
+ else if (*cp == ':') {
+ /* I am going to send some raw postscript stuff */
+ ++cp; /* skip the colon */
+#if PS
+ if (ps_parseraw(cp))
+ have_raw_postscript = True;
+ if (INSIDE_MANE_WIN)
+ psp.drawraw(cp);
+#endif
+ }
+ else { /* attempt to parse pstricks color specials */
+#if COLOR
+ struct rgb color;
+ if (parse_color(cp, cp, &color, True)) {
+ /* clear stack */
+ if (rcs_head == NULL) {
+ rcs_head = xmalloc(sizeof *rcs_head);
+ rcs_head->prev = rcs_head->next = NULL;
+ }
+ rcs_top = rcs_head;
+ color_bot_size = 0;
+
+ /* Change top of stack */
+ rcs_top->color = color;
+
+ set_fg_color(&color);
+ }
+#endif
+ /* also raw PostScript, but no extra colon to skip */
+#if PS
+ if (INSIDE_MANE_WIN) {
+ if (ps_parseraw(cp))
+ have_raw_postscript = True;
+
+ if (psfig_begun)
+ psp.drawraw(cp);
+ else {
+ psp.drawbegin(PXL_H - currwin.base_x,
+ PXL_V - currwin.base_y, cp);
+ psp.drawend("");
+ }
+ }
+#endif
+ }
+}
+
+
+/* Keys for epsf specials */
+
+static const char *keytab[] = { "clip",
+ "llx",
+ "lly",
+ "urx",
+ "ury",
+ "rwi",
+ "rhi",
+ "hsize",
+ "vsize",
+ "hoffset",
+ "voffset",
+ "hscale",
+ "vscale",
+ "angle"
+};
+
+#define KEY_LLX keyval[0]
+#define KEY_LLY keyval[1]
+#define KEY_URX keyval[2]
+#define KEY_URY keyval[3]
+#define KEY_RWI keyval[4]
+#define KEY_RHI keyval[5]
+
+#define NKEYS (sizeof keytab /sizeof *keytab)
+#define N_ARGLESS_KEYS 1
+
+static void
+epsf_special(char *cp)
+{
+ char *filename;
+ static char *buffer;
+ static unsigned int buflen = 0;
+ unsigned int len;
+ char *q;
+ int flags = 0;
+ double keyval[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
+
+ filename = cp;
+ if (*cp == '\'' || *cp == '"') {
+ do
+ ++cp;
+ while (*cp != '\0' && *cp != *filename);
+ ++filename;
+ }
+ else
+ while (*cp != '\0' && !isspace((int)*cp))
+ ++cp;
+ if (*cp != '\0')
+ *cp++ = '\0';
+ while (isspace((int)*cp))
+ ++cp;
+ len = strlen(cp) + NKEYS + 30;
+ if (buflen < len) {
+ if (buflen != 0)
+ free(buffer);
+ buflen = len;
+ buffer = xmalloc(buflen);
+ }
+ strcpy(buffer, "@beginspecial");
+ q = buffer + strlen(buffer);
+ while (*cp != '\0') {
+ char *p1 = cp;
+ size_t keyno;
+
+ while (*p1 != '=' && !isspace((int)*p1) && *p1 != '\0')
+ ++p1;
+ for (keyno = 0;; ++keyno) {
+ if (keyno >= NKEYS) {
+ if (globals.warn_spec_now)
+ XDVI_WARNING((stderr, "Ignoring unknown keyword (%.*s) in \\special",
+ (int)(p1 - cp), cp));
+ break;
+ }
+ if (memcmp(cp, keytab[keyno], p1 - cp) == 0) {
+ if (keyno >= N_ARGLESS_KEYS) {
+ while (isspace((int)*p1))
+ ++p1;
+ if (*p1 == '=') {
+ ++p1;
+ while (isspace((int)*p1))
+ ++p1;
+ }
+ if (keyno < N_ARGLESS_KEYS + 6) {
+ keyval[keyno - N_ARGLESS_KEYS] = atof(p1);
+ flags |= (1 << (keyno - N_ARGLESS_KEYS));
+ }
+ *q++ = ' ';
+ while (!isspace((int)*p1) && *p1 != '\0')
+ *q++ = *p1++;
+ }
+ *q++ = ' ';
+ *q++ = '@';
+ strcpy(q, keytab[keyno]);
+ q += strlen(q);
+ break;
+ }
+ }
+ cp = p1;
+ while (!isspace((int)*cp) && *cp != '\0')
+ ++cp;
+ while (isspace((int)*cp))
+ ++cp;
+ }
+ strcpy(q, " @setspecial\n");
+
+ bbox_valid = False;
+ /* Validate the case where both rwi and rhi are undefined
+ * (and llx, lly, urx, ury are properly defined) */
+ if (!(flags & 0x30) && (flags & 0xf) == 0xf) {
+ KEY_RWI = (KEY_URX - KEY_LLX) * 10;
+ flags |= 0x10;
+ }
+ if ((flags & 0x30) == 0x30 || ((flags & 0x30) && (flags & 0xf) == 0xf)) {
+ bbox_valid = True;
+ bbox_width = 0.1 * ((flags & 0x10) ? KEY_RWI
+ : KEY_RHI * (KEY_URX - KEY_LLX) / (KEY_URY -
+ KEY_LLY)) *
+ dimconv / currwin.shrinkfactor + 0.5;
+ bbox_voffset = bbox_height =
+ 0.1 * ((flags & 0x20) ? KEY_RHI : KEY_RWI * (KEY_URY - KEY_LLY) /
+ (KEY_URX - KEY_LLX))
+ * dimconv / currwin.shrinkfactor + 0.5;
+ }
+
+ if (INSIDE_MANE_WIN) {
+#if PS
+ psp.drawbegin(PXL_H - currwin.base_x, PXL_V - currwin.base_y, buffer);
+ /* talk directly with the DPSHandler here */
+ send_ps_file(filename, kpse_pict_format);
+ psp.drawend(" @endspecial");
+#else
+ draw_bbox();
+#endif
+ }
+ bbox_valid = False;
+}
+
+
+static void
+quote_special(char *cp)
+{
+ bbox_valid = False;
+
+#if PS
+ if (currwin.win == mane.win) {
+ psp.drawbegin(PXL_H - currwin.base_x, PXL_V - currwin.base_y,
+ "@beginspecial @setspecial ");
+ /* talk directly with the DPSHandler here */
+ psp.drawraw(cp + 1);
+ psp.drawend(" @endspecial");
+ }
+#endif
+
+ /* nothing else to do--there's no bbox here */
+}
+
+#if PS
+
+static void
+scan_header(char *cp)
+{
+ char *filename;
+ /* default for .pro files: */
+ kpse_file_format_type our_format;
+
+#if PS_GS
+ if (gs_postpone_prescan)
+ return;
+#endif
+ filename = cp;
+ if (*cp == '\'' || *cp == '"') {
+ do
+ ++cp;
+ while (*cp != '\0' && *cp != *filename);
+ *cp = '\0';
+ ++filename;
+ }
+
+ psp.beginheader();
+ /* check for suffixes other than `.pro' (default PS headers),
+ case-insensitive */
+ if (str_is_suffix(".pfa", filename, False)
+ || str_is_suffix(".pfb", filename, False))
+ our_format = kpse_type1_format;
+ else if (str_is_suffix(".enc", filename, False))
+ our_format = kpse_enc_format;
+ else
+ our_format = kpse_tex_ps_header_format;
+
+ send_ps_file(filename, our_format);
+}
+
+static void
+scan_bang(char *cp)
+{
+ psp.beginheader();
+ psp.drawraw(cp + 1);
+}
+
+#endif /* PS */
+
+#if COLOR
+
+/*
+ * Table of dvips color names. Produced by passing the table in
+ * dvipsk/color.lpro through the following perl script, and then
+ * through sort.
+ *
+ * #! /usr/bin/perl -w
+ *
+ * sub cvpart {
+ * return $_[0] < 1.0 ? 1.0 - $_[0] : 0.0;
+ * }
+ *
+ * $pat = "^\\/(\\w+)\\s*\\{\\s*([0-9.]+)\\s*([0-9.]+)\\s*([0-9.]+)"
+ * . "\\s*([0-9.]+)\\s*setcmykcolor\\s*\\}\\s*DC\\s*\$";
+ * while (<STDIN>) {
+ * chop;
+ * if (/^%%/) {next;}
+ * if (/$pat/o) {
+ * printf "\t\tDVIPSCOLORDESC(%2d, \"%s\", %g, %g, %g),\n",
+ * length($1), $1,
+ * cvpart($2 + $5), cvpart($3 + $5), cvpart($4 + $5);
+ * }
+ * else {
+ * print "Bad line: ", $_, "\n";
+ * }
+ * }
+ */
+
+struct dvipscolor {
+ const char *name;
+ struct rgb color;
+};
+
+static Boolean
+parse_color(const char *cp0, const char *cp,
+ struct rgb *rgbp, Boolean generic_ps_flag)
+{
+ double r, g, b;
+ double k;
+ double hue, sat, bri;
+ char dummy[8];
+
+#define CVPART(x) ((int)((x) * 65535 + 0.5))
+#define COLOR_DESC(name, r, g, b) \
+ {name, {CVPART(r), CVPART(g), CVPART(b)}}
+
+ /* hash table to speed up lookup in following list of color names */
+ static hashTableT colornames_hash;
+ /*
+ Use a prime close to sizof colornames / sizeof colornames[0].
+ You might want to update this when extending the colornames array,
+ and to check the filling factor and average chain length by uncommenting the
+ hash_print(colornames_hash, True);
+ below. I usually go for filling > 50% and avg. chain len < 2.
+
+ The 68-elem array below results in:
+ 79 buckets, 43 nonempty (54%); 68 entries, average chain 1.6.
+ */
+ static const size_t colornames_hash_size = 79;
+
+ static struct dvipscolor colornames[] = {
+ COLOR_DESC("Red", 1, 0, 0),
+ COLOR_DESC("Tan", 0.86, 0.58, 0.44),
+ COLOR_DESC("Blue", 0, 0, 1),
+ COLOR_DESC("Cyan", 0, 1, 1),
+ COLOR_DESC("Gray", 0.5, 0.5, 0.5),
+ COLOR_DESC("Plum", 0.5, 0, 1),
+ COLOR_DESC("Black", 0, 0, 0),
+ COLOR_DESC("Brown", 0.4, 0, 0),
+ COLOR_DESC("Green", 0, 1, 0),
+ COLOR_DESC("Melon", 1, 0.54, 0.5),
+ COLOR_DESC("Peach", 1, 0.5, 0.3),
+ COLOR_DESC("Sepia", 0.3, 0, 0),
+ COLOR_DESC("White", 1, 1, 1),
+ COLOR_DESC("Maroon", 0.68, 0, 0),
+ COLOR_DESC("Orange", 1, 0.39, 0.13),
+ COLOR_DESC("Orchid", 0.68, 0.36, 1),
+ COLOR_DESC("Purple", 0.55, 0.14, 1),
+ COLOR_DESC("Salmon", 1, 0.47, 0.62),
+ COLOR_DESC("Violet", 0.21, 0.12, 1),
+ COLOR_DESC("Yellow", 1, 1, 0),
+ COLOR_DESC("Apricot", 1, 0.68, 0.48),
+ COLOR_DESC("Emerald", 0, 1, 0.5),
+ COLOR_DESC("Fuchsia", 0.45, 0.01, 0.92),
+ COLOR_DESC("Magenta", 1, 0, 1),
+ COLOR_DESC("SkyBlue", 0.38, 1, 0.88),
+ COLOR_DESC("Thistle", 0.88, 0.41, 1),
+ COLOR_DESC("BrickRed", 0.72, 0, 0),
+ COLOR_DESC("Cerulean", 0.06, 0.89, 1),
+ COLOR_DESC("Lavender", 1, 0.52, 1),
+ COLOR_DESC("Mahogany", 0.65, 0, 0),
+ COLOR_DESC("Mulberry", 0.64, 0.08, 0.98),
+ COLOR_DESC("NavyBlue", 0.06, 0.46, 1),
+ COLOR_DESC("SeaGreen", 0.31, 1, 0.5),
+ COLOR_DESC("TealBlue", 0.12, 0.98, 0.64),
+ COLOR_DESC("BlueGreen", 0.15, 1, 0.67),
+ COLOR_DESC("CadetBlue", 0.38, 0.43, 0.77),
+ COLOR_DESC("Dandelion", 1, 0.71, 0.16),
+ COLOR_DESC("Goldenrod", 1, 0.9, 0.16),
+ COLOR_DESC("LimeGreen", 0.5, 1, 0),
+ COLOR_DESC("OrangeRed", 1, 0, 0.5),
+ COLOR_DESC("PineGreen", 0, 0.75, 0.16),
+ COLOR_DESC("RawSienna", 0.55, 0, 0),
+ COLOR_DESC("RedOrange", 1, 0.23, 0.13),
+ COLOR_DESC("RedViolet", 0.59, 0, 0.66),
+ COLOR_DESC("Rhodamine", 1, 0.18, 1),
+ COLOR_DESC("RoyalBlue", 0, 0.5, 1),
+ COLOR_DESC("RubineRed", 1, 0, 0.87),
+ COLOR_DESC("Turquoise", 0.15, 1, 0.8),
+ COLOR_DESC("VioletRed", 1, 0.19, 1),
+ COLOR_DESC("Aquamarine", 0.18, 1, 0.7),
+ COLOR_DESC("BlueViolet", 0.1, 0.05, 0.96),
+ COLOR_DESC("DarkOrchid", 0.6, 0.2, 0.8),
+ COLOR_DESC("OliveGreen", 0, 0.6, 0),
+ COLOR_DESC("Periwinkle", 0.43, 0.45, 1),
+ COLOR_DESC("Bittersweet", 0.76, 0.01, 0),
+ COLOR_DESC("BurntOrange", 1, 0.49, 0),
+ COLOR_DESC("ForestGreen", 0, 0.88, 0),
+ COLOR_DESC("GreenYellow", 0.85, 1, 0.31),
+ COLOR_DESC("JungleGreen", 0.01, 1, 0.48),
+ COLOR_DESC("ProcessBlue", 0.04, 1, 1),
+ COLOR_DESC("RoyalPurple", 0.25, 0.1, 1),
+ COLOR_DESC("SpringGreen", 0.74, 1, 0.24),
+ COLOR_DESC("YellowGreen", 0.56, 1, 0.26),
+ COLOR_DESC("MidnightBlue", 0, 0.44, 0.57),
+ COLOR_DESC("YellowOrange", 1, 0.58, 0),
+ COLOR_DESC("CarnationPink", 1, 0.37, 1),
+ COLOR_DESC("CornflowerBlue", 0.35, 0.87, 1),
+ COLOR_DESC("WildStrawberry", 1, 0.04, 0.61),
+ };
+#undef CVPART
+#undef COLOR_DESC
+
+ UNUSED(cp0);
+
+ while (*cp == ' ') ++cp;
+
+ if (generic_ps_flag) {
+ /*
+ check for pstricks color specials.
+ The dummy buffer is to ensure that there are no other (general PS)
+ commands following (we wouldn't know how to deal with these).
+ */
+ if (sscanf(cp, "%lf %lf %lf setrgbcolor %7s", &r, &g, &b, dummy) == 3
+ && r >= 0 && r <= 1 && g >= 0 && g <= 1 && b >= 0 && b <= 1) {
+ rgbp->r = r * 65535 + 0.5;
+ rgbp->g = g * 65535 + 0.5;
+ rgbp->b = b * 65535 + 0.5;
+ return True;
+ }
+ else if (sscanf(cp, "%lf %lf %lf %lf setcmykcolor %7s", &r, &g, &b, &k, dummy) == 4
+ && r >= 0 && r <= 1 && g >= 0 && g <= 1 && b >= 0 && b <= 1
+ && k >= 0 && k <= 1) {
+ r = 1.0 - r - k; /* cyan --> red */
+ rgbp->r = (r < 0 ? 0 : r * 65535 + 0.5);
+ g = 1.0 - g - k; /* magenta --> green */
+ rgbp->g = (g < 0 ? 0 : g * 65535 + 0.5);
+ b = 1.0 - b - k; /* yellow --> blue */
+ rgbp->b = (b < 0 ? 0 : b * 65535 + 0.5);
+ return True;
+ }
+ else if (sscanf(cp, "%lf %lf %lf sethsbcolor %7s", &hue, &sat, &bri, dummy) == 3 && hue >= 0
+ && hue <= 1 && sat >= 0 && sat <= 1 && bri >= 0 && bri <= 1) {
+ int h = (int) (6 * hue);
+ double p = bri * (1 - sat);
+ double q = bri * (1 - sat * (6 * hue - h));
+ double t = p - q + bri;
+
+ switch (h) {
+ case 0: r = bri; g = t; b = p; break; /* Red - Yel */
+ case 1: r = q; g = bri; b = p; break; /* Yel - Grn */
+ case 2: r = p; g = bri; b = t; break; /* Grn - Cyn */
+ case 3: r = p; g = q; b = bri; break; /* Cyn - Blu */
+ case 4: r = t; g = p; b = bri; break; /* Blu - Mag */
+ case 5: r = bri; g = p; b = q; break; /* Mag - Red */
+ case 6: r = bri; g = b = p; break; /* Red */
+ }
+ rgbp->r = r * 65535 + 0.5;
+ rgbp->g = g * 65535 + 0.5;
+ rgbp->b = b * 65535 + 0.5;
+ return True;
+ }
+ else if (sscanf(cp, "%lf setgray %7s", &r, dummy) == 1 && r >= 0 && r <= 1) {
+ rgbp->r = rgbp->g = rgbp->b = r * 65535 + 0.5;
+ return True;
+ }
+ else
+ return False;
+ }
+
+ /* no generic PS command; must be a dvips color special */
+
+ if (memicmp(cp, "rgb ", 4) == 0) {
+ if (sscanf(cp + 3, "%lf %lf %lf", &r, &g, &b) == 3
+ && r >= 0 && r <= 1 && g >= 0 && g <= 1 && b >= 0 && b <= 1) {
+ rgbp->r = r * 65535 + 0.5;
+ rgbp->g = g * 65535 + 0.5;
+ rgbp->b = b * 65535 + 0.5;
+ return True;
+ }
+ }
+ else if (memicmp(cp, "gray ", 5) == 0) {
+ if (sscanf(cp + 4, "%lf", &r) == 1 && r >= 0 && r <= 1) {
+ rgbp->r = rgbp->g = rgbp->b = r * 65535 + 0.5;
+ return True;
+ }
+ }
+ else if (memicmp(cp, "cmyk ", 5) == 0) {
+ if (sscanf(cp + 4, "%lf %lf %lf %lf", &r, &g, &b, &k) == 4
+ && r >= 0 && r <= 1 && g >= 0 && g <= 1 && b >= 0 && b <= 1
+ && k >= 0 && k <= 1) {
+ r = 1.0 - r - k; /* cyan --> red */
+ rgbp->r = (r < 0 ? 0 : r * 65535 + 0.5);
+ g = 1.0 - g - k; /* magenta --> green */
+ rgbp->g = (g < 0 ? 0 : g * 65535 + 0.5);
+ b = 1.0 - b - k; /* yellow --> blue */
+ rgbp->b = (b < 0 ? 0 : b * 65535 + 0.5);
+ return True;
+ }
+ }
+ else if (memicmp(cp, "hsb ", 4) == 0) {
+ if (sscanf(cp + 3, "%lf %lf %lf", &hue, &sat, &bri) == 3 && hue >= 0
+ && hue <= 1 && sat >= 0 && sat <= 1 && bri >= 0 && bri <= 1) {
+ int h = (int) (6 * hue);
+ double p = bri * (1 - sat);
+ double q = bri * (1 - sat * (6 * hue - h));
+ double t = p - q + bri;
+
+ switch (h) {
+ case 0: r = bri; g = t; b = p; break; /* Red - Yel */
+ case 1: r = q; g = bri; b = p; break; /* Yel - Grn */
+ case 2: r = p; g = bri; b = t; break; /* Grn - Cyn */
+ case 3: r = p; g = q; b = bri; break; /* Cyn - Blu */
+ case 4: r = t; g = p; b = bri; break; /* Blu - Mag */
+ case 5: r = bri; g = p; b = q; break; /* Mag - Red */
+ case 6: r = bri; g = b = p; break; /* Red */
+ }
+ rgbp->r = r * 65535 + 0.5;
+ rgbp->g = g * 65535 + 0.5;
+ rgbp->b = b * 65535 + 0.5;
+ return True;
+ }
+ }
+ else { /* is it a dvips color name? */
+ size_t idx;
+ size_t len = 0;
+ static char *testname = NULL;
+ static size_t testname_len = 0;
+ size_t testname_step = 16;
+
+ if (colornames_hash.size == 0) { /* initialize hash table */
+ size_t i;
+ colornames_hash = hash_create(colornames_hash_size);
+ /* insert color names and array indexes */
+ for (i = 0; i < (sizeof colornames / sizeof colornames[0]); i++) {
+ put_str_int_hash(&colornames_hash, colornames[i].name, i);
+ }
+ }
+ /* uncomment the following to get statistics on hashing: */
+ /* hash_print(colornames_hash, True); */
+
+ /* need to copy to a null-terminated string for hash lookup ... */
+ while (isalpha((int)cp[len])) {
+ ++len; /* get length of color name */
+ }
+ while (len >= testname_len) {
+ testname_len += testname_step;
+ testname = xrealloc(testname, testname_len);
+ }
+ memcpy(testname, cp, len);
+ testname[len] = '\0';
+
+ if (find_str_int_hash(&colornames_hash, testname, &idx)) {
+ *rgbp = colornames[idx].color;
+ return True;
+ }
+ }
+
+ /* not found */
+ XDVI_WARNING((stderr, "Ignoring invalid color name in special `%s'", cp0 == NULL ? "<NULL>" : cp0));
+ return False;
+}
+
+void
+init_page_colors(struct rgb *foreground, struct rgb *background)
+{
+ int i;
+
+ page_colors.size = total_pages;
+ page_colors.stack = xmalloc(page_colors.size * sizeof *page_colors.stack);
+
+ for (i = 0; i <= scanned_page + 1; ++i) {
+ page_colors.stack[i].bg = *background;
+ page_colors.stack[i].colorstack = foreground;
+ page_colors.stack[i].stacksize = 1;
+ }
+ for (; i < total_pages; i++) {
+ page_colors.stack[i].colorstack = NULL;
+ }
+
+ scanstack_head.color = *foreground;
+ scanstack_len = 1; /* nothing yet */
+ scanstack_current = &scanstack_head; /* stack position */
+}
+
+
+static void
+scan_bg_color(const char *cp)
+{
+ if (!resource.use_color)
+ return;
+
+ if (page_colors.stack == NULL)
+ init_page_colors(&fg_initial, &bg_initial);
+
+ ASSERT(scanned_page < (int)page_colors.size, "page_colors.size too small");
+ (void) parse_color(cp, cp + 11, &(page_colors.stack[scanned_page + 1].bg), False);
+}
+
+void
+scan_color(const char *cp)
+{
+ const char *cp1 = cp + 6;
+
+ if (!resource.use_color)
+ return;
+
+ while (*cp1 == ' ') ++cp1;
+
+ if (page_colors.stack == NULL)
+ init_page_colors(&fg_initial, &bg_initial);
+
+ if (memicmp(cp1, "push ", 5) == 0) {
+ if (scanstack_current->next == NULL) { /* if at end */
+ scanstack_current->next = xmalloc(sizeof *scanstack_current);
+ scanstack_current->next->prev = scanstack_current;
+ scanstack_current->next->next = NULL;
+ }
+ scanstack_current = scanstack_current->next;
+ ++scanstack_len;
+ if (!parse_color(cp, cp1 + 5, &scanstack_current->color, False))
+ scanstack_current->color = scanstack_current->prev->color;
+ }
+ else if (memicmp(cp1, "pop", 3) == 0) {
+ if (scanstack_len <= 1) {
+ XDVI_WARNING((stderr, "Color pop occurred with empty color stack."));
+ }
+ else {
+ scanstack_current = scanstack_current->prev;
+ --scanstack_len;
+ }
+ }
+ else {
+ (void) parse_color(cp, cp1, &scanstack_head.color, False);
+ if (scanstack_len > 1) {
+ struct colorframe *cfp;
+
+ XDVI_WARNING((stderr, "Global color change occurred with non-empty color stack!\n"
+ "Trying to recover by setting all stack entries to that color."));
+ for (cfp = scanstack_head.next;; cfp = cfp->next) {
+ cfp->color = scanstack_head.color;
+ if (cfp == scanstack_current) break;
+ }
+ }
+ }
+}
+
+void
+scan_color_eop(void)
+{
+ int i;
+ struct rgb *prev, *p1, *rgbp;
+ struct colorframe *cf;
+
+ if (page_colors.stack == NULL)
+ return;
+
+ /* set background color for next page */
+ if (scanned_page + 1 < total_pages) {
+ ASSERT(scanned_page + 1 < (int)page_colors.size, "page_colors.size too small");
+ page_colors.stack[scanned_page + 1].bg = page_colors.stack[scanned_page].bg;
+ }
+ ASSERT(scanned_page < (int)page_colors.size, "page_colors.size too small");
+ /* save the stack contents */
+ page_colors.stack[scanned_page].stacksize = scanstack_len;
+
+ prev = &fg_initial;
+ i = 1;
+ if (scanned_page > 0) {
+ prev = page_colors.stack[scanned_page - 1].colorstack;
+ i = page_colors.stack[scanned_page - 1].stacksize;
+ }
+ if (scanstack_len <= i) {
+ /* try to reuse the previous array */
+ p1 = prev;
+ cf = &scanstack_head;
+ for (i = 0;;) {
+ if (p1->r != cf->color.r || p1->g != cf->color.g || p1->b != cf->color.b)
+ break;
+ if (++i >= scanstack_len) { /* end loop; reuse memory */
+ page_colors.stack[scanned_page].colorstack = prev;
+ return; /* done */
+ }
+ ++p1;
+ cf = cf->next;
+ }
+ }
+ page_colors.stack[scanned_page].colorstack = rgbp = xmalloc(scanstack_len * sizeof *rgbp);
+ cf = &scanstack_head;
+ for (i = 0; i < scanstack_len; ++i) {
+ *rgbp++ = cf->color;
+ cf = cf->next;
+ }
+}
+
+
+/*
+ * We don't actually do any X calls yet to change colors; that can wait
+ * until a character or rule needs to be drawn.
+ */
+
+void
+set_fg_color(const struct rgb *color)
+{
+ struct fgrec **fgpp;
+
+ if (fg_current != NULL
+ && color->r == fg_current->color.r
+ && color->g == fg_current->color.g
+ && color->b == fg_current->color.b) {
+ return;
+ }
+
+ ASSERT(bg_current != NULL, "Background color not initialized");
+ for (fgpp = &bg_current->fg_head;;) {
+ fg_current = *fgpp;
+ if (fg_current == NULL) { /* if color is not in list */
+ fg_current = *fgpp = xmalloc(sizeof *fg_current);
+ fg_current->next = NULL;
+ fg_current->color = *color;
+ fg_current->pixel_good = False;
+#if GREY
+ fg_current->palette_good = False;
+#endif
+ break;
+ }
+ if (fg_current->color.r == color->r
+ && fg_current->color.g == color->g
+ && fg_current->color.b == color->b)
+ break;
+ fgpp = &fg_current->next;
+ }
+ if (globals.debug & DBG_DVI)
+ printf("Changing fg color to %5d %5d %5d\n",
+ color->r, color->g, color->b);
+}
+
+#if 0
+static void
+show_stack(void) {
+ struct colorframe *ptr;
+ int i;
+ fprintf(stderr, "FG: %d,%d,%d\n", fg_current->color.r, fg_current->color.b, fg_current->color.g);
+ for (i = 0, ptr = rcs_top; ptr != NULL; ptr = ptr->prev, i++) {
+ fprintf(stderr, "stack %d: %d,%d,%d\n", i, ptr->color.r, ptr->color.b, ptr->color.g);
+ }
+}
+#endif
+
+void
+color_special(const char *cp)
+{
+ while (*cp == ' ') ++cp;
+
+ if (memicmp(cp, "push ", 5) == 0) {
+ if (rcs_top == NULL) {
+ if (rcs_head == NULL) {
+ rcs_head = xmalloc(sizeof *rcs_head);
+ rcs_head->prev = rcs_head->next = NULL;
+ }
+ rcs_top = rcs_head;
+ }
+ else {
+ struct colorframe *rcs_next;
+
+ rcs_next = rcs_top->next;
+ if (rcs_next == NULL) {
+ rcs_next = rcs_top->next = xmalloc(sizeof *rcs_next);
+ rcs_next->prev = rcs_top;
+ rcs_next->next = NULL;
+ }
+ rcs_top = rcs_next;
+ }
+ if (!parse_color(NULL, cp + 5, &rcs_top->color, False)) {
+ if (rcs_top->prev != NULL)
+ rcs_top->color = rcs_top->prev->color;
+ else
+ rcs_top->color = color_bottom[color_bot_size - 1];
+ }
+ set_fg_color(&rcs_top->color);
+ }
+ else if (memicmp(cp, "pop", 3) == 0) {
+ /* Pop stack */
+ if (rcs_top != NULL) {
+ if (color_bot_size > 0)
+ rcs_top = rcs_top->prev;
+ else
+ return;
+ }
+ else if (color_bot_size > 1)
+ --color_bot_size;
+ else {
+ if (scanned_page_reset >= 0) {
+ /* turn on scanning and redraw the page */
+ scanned_page = scanned_page_color = scanned_page_reset = -1;
+#if PS
+ scanned_page_ps = scanned_page;
+#endif
+/* fprintf(stderr, "forcing redraw!!!\n"); */
+ globals.ev.flags |= EV_NEWPAGE; /* force a redraw */
+ longjmp(globals.ev.canit, 1);
+ }
+ return;
+ }
+/* fprintf(stderr, "bot_size: %d\n", color_bot_size); */
+ set_fg_color(rcs_top != NULL ? &rcs_top->color
+ : &color_bottom[color_bot_size - 1]);
+ }
+ else {
+ struct rgb color;
+
+ if (!parse_color(NULL, cp, &color, False))
+ return;
+
+ /* clear stack */
+ if (rcs_head == NULL) {
+ rcs_head = xmalloc(sizeof *rcs_head);
+ rcs_head->prev = rcs_head->next = NULL;
+ }
+ rcs_top = rcs_head;
+ color_bot_size = 0;
+
+ /* Change top of stack */
+ rcs_top->color = color;
+
+ set_fg_color(&color);
+ }
+}
+
+#endif /* COLOR */
+
+static unsigned int
+myatopix(const char **pp)
+{
+#define SCR_LEN 16
+ unsigned int value;
+ const char *cp = *pp;
+ char scr[16];
+ const char *p0, *p1;
+
+ p0 = cp;
+ while ((*cp >= '0' && *cp <= '9') || *cp == '.')
+ ++cp;
+ p1 = cp;
+ while (isspace((int)*cp))
+ ++cp;
+ if (*cp >= 'a' && *cp <= 'z' && cp[1] >= 'a' && cp[1] <= 'z') {
+ /* if units are present */
+ if (p1 - p0 <= SCR_LEN - 3) {
+ sprintf(scr, "%.*s%c%c", (int)(p1 - p0), p0, *cp, cp[1]);
+ value = atopix(scr, False);
+ }
+ else
+ value = 0;
+ cp += 2;
+ }
+ else
+ value = atopix(p0, False);
+
+ *pp = cp;
+ return value;
+#undef SCR_LEN
+}
+
+static void
+scan_papersize(const char *cp0)
+{
+ const char *cp = cp0;
+ unsigned int w, h;
+ double mag = 1.0;
+
+ if (ignore_papersize_specials)
+ return;
+
+ if (*cp == '*') {
+ do
+ ++cp;
+ while (isspace((int)*cp));
+ mag = magnification * .001;
+ }
+
+ w = myatopix(&cp) * mag + 0.5;
+
+ while (isspace((int)*cp))
+ ++cp;
+ if (*cp == ',')
+ do
+ ++cp;
+ while (isspace((int)*cp));
+
+ h = myatopix(&cp) * mag + 0.5;
+
+ if (w == 0 || h == 0)
+ XDVI_WARNING((stderr, "Invalid papersize special `%s'", cp0));
+ else {
+ /* we have a paper size special; disable xdvirc_geometry:
+ fprintf(stderr, "---------- scanned papersize: %dx%d\n", w, h);
+ resource.xdvirc_geometry = NULL;
+ */
+ pageinfo_set_page_width(scanned_page + 1, w);
+ pageinfo_set_window_width(scanned_page + 1, w);
+ pageinfo_set_page_height(scanned_page + 1, h);
+ pageinfo_set_window_height(scanned_page + 1, h);
+ }
+}
+
+/*
+ * The following copyright message applies to the rest of this file. --PV
+ */
+
+/*
+ * This program is Copyright (C) 1987 by the Board of Trustees of the
+ * University of Illinois, and by the author Dirk Grunwald.
+ *
+ * This program may be freely copied, as long as this copyright
+ * message remaines affixed. It may not be sold, although it may
+ * be distributed with other software which is sold. If the
+ * software is distributed, the source code must be made available.
+ *
+ * No warranty, expressed or implied, is given with this software.
+ * It is presented in the hope that it will prove useful.
+ *
+ * Hacked in ignorance and desperation by jonah@db.toronto.edu
+ */
+
+/*
+ * The code to handle the \specials generated by tpic was modified
+ * by Dirk Grunwald using the code Tim Morgan at Univ. of Calif, Irvine
+ * wrote for TeXsun.
+ */
+
+static char *
+endofcommand(char *cp)
+{
+ while (isspace((int)*cp))
+ ++cp;
+ if (*cp != '=')
+ return NULL;
+ do
+ ++cp;
+ while (isspace((int)*cp));
+ return cp;
+}
+
+#define CMD(x, y) ((x) << 8 | (y))
+
+void
+applicationDoSpecial(char *cp, size_t len)
+{
+ char *p;
+
+ if (globals.debug & DBG_DVI)
+ printf(" `%s'\n", cp);
+
+ while (isspace((int)*cp))
+ ++cp;
+
+ /* Ignore initial "xdvi:" */
+ if (memcmp(cp, "xdvi:", 5) == 0) {
+ cp += 5;
+ while (isspace((int)*cp))
+ ++cp;
+ }
+
+ /* PostScript specials */
+#ifdef MAGICK
+ if (resource.useMAGICK) {
+ if (Magick_parse_special(cp))
+ return;
+ }
+#endif
+
+ if (*cp == '"') {
+ quote_special(cp);
+ return;
+ }
+ if (memicmp(cp, "ps:", 3) == 0) {
+ cp += 3;
+ psfig_special(cp);
+ /* check for hdvips hyperlinks */
+ if (memicmp(cp, "sdict begin ", strlen("sdict begin ")) == 0) {
+ static Boolean warned_hypertex_too_old = False;
+ char *match = NULL;
+ if (warned_hypertex_too_old) /* don't continue evaluating links in this case */
+ return;
+ cp += strlen("sdict begin ");
+ if (memcmp(cp, "H.S", 3) == 0
+ || memcmp(cp, "H.R", 3) == 0
+ || memcmp(cp, "H.B", 3) == 0
+ /* following 2 conditions could be more restrictive: between `begin' and H.A/H.L,
+ there should be a single number (baselineskip in pt) */
+ || (match = strstr(cp, "H.A")) != NULL
+ || (match = strstr(cp, "H.L")) != NULL
+ || (match = strstr(cp, "/Action")) != NULL
+ || (match = strstr(cp, "/Link")) != NULL
+ || (match = strstr(cp, "/View")) != NULL) {
+ if (match != NULL)
+ htex_do_special(match, len - 3 - (match - cp) - strlen("sdict begin "));
+ else
+ htex_do_special(cp, len - strlen("sdict begin "));
+ }
+ else if (!warned_hypertex_too_old && strstr(cp, "HyperStart") != NULL) {
+ popup_message(globals.widgets.top_level,
+ MSG_WARN, NULL,
+ "This DVI was created with a too old version of the `dvips' hyperref driver - "
+ "disabling hyperlinks.\n"
+ "To fix this, you should either upgrade to a current version of hyperref "
+ "(see http://www.tug.org/applications/hyperref/), "
+ "or use the `hypertex' package option, like this:\n\\usepackage[hypertex]{hyperref}\n"
+ "(Be aware though that this option won't work for PS->PDF conversion!)");
+ warned_hypertex_too_old = True;
+ }
+ }
+ else {
+ /* When not ignoring SDict entries, the distiller and pagecolor
+ code in lshort.dvi from CTAN:info/lshort/russian/lshrtdvi.zip
+ causes a failed assertion for 'color_bot_size > 0' in dvi-draw.c;
+ there's something wrong with the parsing order/event handling here
+ (see bug #856547).
+ But we also don't want those entries to trigger erasepage_gs(), so
+ it's correct to ignore them here.
+ */
+#if PS_GS && GS_PIXMAP_CLEARING_HACK
+ had_ps_specials = True;
+#endif
+ }
+ return;
+ }
+ if (memicmp(cp, "psfile", 6) == 0 && (p = endofcommand(cp + 6)) != NULL) {
+ epsf_special(p);
+#if PS_GS && GS_PIXMAP_CLEARING_HACK
+ had_ps_specials = True;
+#endif
+ return;
+ }
+ if (memicmp(cp, "html:", 5) == 0) {
+ htex_do_special(cp + 5, len - 5);
+ return;
+ }
+
+#if COLOR
+ if (memicmp(cp, "color ", 6) == 0) {
+/* fprintf(stderr, "------------- color special\n"); */
+ if (resource.use_color)
+ color_special(cp + 6);
+ return;
+ }
+#endif
+
+ /* these should have been scanned */
+ if (*cp == '!'
+ || (memicmp(cp, "header", 6) == 0 && endofcommand(cp + 6) != NULL)) {
+#ifdef PS
+ if (resource.postscript != 0 && scanned_page_reset >= 0) {
+ /* turn on scanning and redraw the page */
+ scanned_page = scanned_page_ps = scanned_page_reset = -1;
+# if COLOR
+ scanned_page_color = scanned_page;
+# endif
+ globals.ev.flags |= EV_NEWPAGE; /* force a redraw */
+ longjmp(globals.ev.canit, 1);
+ }
+#endif /* PS */
+ return;
+ }
+
+ if (memicmp(cp, "background ", 11) == 0) {
+#if COLOR
+ if (resource.use_color && scanned_page_reset >= 0) {
+ /* turn on scanning and redraw the page */
+ scanned_page = scanned_page_color = scanned_page_reset = -1;
+# if PS
+ scanned_page_ps = scanned_page;
+# endif
+/* fprintf(stderr, "forcing redraw!\n"); */
+ globals.ev.flags |= EV_NEWPAGE; /* force a redraw */
+ longjmp(globals.ev.canit, 1);
+ }
+#endif /* COLOR */
+ return;
+ }
+
+ if (memcmp(cp, "papersize", 9) == 0 && endofcommand(cp + 9) != NULL) {
+ m_have_papersize_special = True;
+ if (scanned_page_reset >= 0) {
+ /* turn on scanning and redraw the page */
+ scanned_page = scanned_page_reset = -1;
+#if PS
+ scanned_page_ps = scanned_page;
+#endif
+#if COLOR
+ scanned_page_color = scanned_page;
+#endif
+ globals.ev.flags |= EV_NEWPAGE; /* force a redraw */
+ longjmp(globals.ev.canit, 1);
+ }
+ return;
+ }
+
+ /* tpic specials */
+
+ if (*cp >= 'a' && *cp <= 'z' && cp[1] >= 'a' && cp[1] <= 'z' &&
+ (isspace((int)cp[2]) || cp[2] == '\0')) {
+ switch (CMD(*cp, cp[1])) {
+ case CMD('p', 'n'):
+ set_pen_size(cp + 2);
+ return;
+ case CMD('f', 'p'):
+ flush_path();
+ return;
+ case CMD('d', 'a'):
+ flush_dashed(cp + 2, False);
+ return;
+ case CMD('d', 't'):
+ flush_dashed(cp + 2, True);
+ return;
+ case CMD('p', 'a'):
+ add_path(cp + 2);
+ return;
+ case CMD('a', 'r'):
+ arc(cp + 2, False);
+ return;
+ case CMD('i', 'a'):
+ arc(cp + 2, True);
+ return;
+ case CMD('s', 'p'):
+ flush_spline();
+ return;
+ case CMD('s', 'h'):
+ shade_last();
+ return;
+ case CMD('w', 'h'):
+ whiten_last();
+ return;
+ case CMD('b', 'k'):
+ blacken_last();
+ return;
+ case CMD('i', 'p'): /* throw away the path -- jansteen */
+ path_len = 0;
+ return;
+ }
+ }
+
+ if (memcmp(cp, "src:", 4) == 0) {
+ have_src_specials = True;
+ }
+ else if (globals.warn_spec_now)
+ XDVI_WARNING((stderr, "Special \"%s\" not implemented.", cp));
+}
+
+#undef CMD
+
+
+Boolean
+scan_special(char *cp, int cp_len, void *data)
+{
+ char *p;
+ Boolean dummy_ret = True; /* currently unused;
+ FIXME: use return value to avoid redundant calls (instead of longjmp()) */
+ UNUSED(cp_len); /* TODO: could probably utilize this in call to htex_prescan_special() */
+ ASSERT(data != NULL, "Must pass a data pointer when using HTEX");
+
+ if (globals.debug & DBG_PS)
+ printf("Scanning special `%s'.\n", cp);
+
+ while (isspace((int)*cp))
+ ++cp;
+
+ /* Ignore initial "xdvi:" */
+ if (memcmp(cp, "xdvi:", 5) == 0) {
+ cp += 5;
+ while (isspace((int)*cp))
+ ++cp;
+ }
+
+ if (memicmp(cp, "ps:", 3) == 0) {
+ Boolean found;
+ cp += 3;
+ /* check for hdvips hyperlinks */
+ if (memicmp(cp, "sdict begin ", strlen("sdict begin ")) == 0) {
+ static Boolean hypertex_too_old = False;
+ char *match = NULL;
+ cp += strlen("sdict begin ");
+
+ if (strstr(cp, "HyperStart") != NULL)
+ hypertex_too_old = True;
+ if (hypertex_too_old)
+ return False;
+
+ if (memcmp(cp, "H.S", 3) == 0
+ || memcmp(cp, "H.R", 3) == 0
+ || memcmp(cp, "H.B", 3) == 0
+ /* following 2 conditions could be more restrictive: between `begin' and H.A/H.L,
+ there should be a single number (baselineskip in pt) */
+ || (match = strstr(cp, "H.A")) != NULL
+ || (match = strstr(cp, "H.L")) != NULL
+ || (match = strstr(cp, "/Action")) != NULL
+ || (match = strstr(cp, "/Link")) != NULL
+ || (match = strstr(cp, "/View")) != NULL) {
+ if (match != NULL)
+ found = htex_prescan_special(match, cp_len, data);
+ else
+ found = htex_prescan_special(cp, cp_len, data);
+ }
+ }
+
+ }
+ else if (memicmp(cp, "html:", strlen("html:")) == 0) {
+ Boolean found;
+ size_t offset = strlen("html:");
+ found = htex_prescan_special(cp + offset, cp_len - offset, data);
+ /* if searching for a specific string, return as soon as it's found - not yet implemented */
+#if 0
+ if (my_data != NULL && my_data->scan_type == HTEX_ANCHOR_STRING && found) {
+ return True;
+ }
+#endif
+ }
+ /* do the following only if not searching for an anchor string */
+ if (((struct htex_prescan_data *)data)->scan_type != HTEX_ANCHOR_STRING) {
+#if PS
+# if COLOR
+ if (scanned_page_ps <= scanned_page)
+# endif
+ {
+ if (*cp == '!') {
+ scan_bang(cp);
+ return dummy_ret;
+ }
+ else if (memicmp(cp, "header", 6) == 0 && (p = endofcommand(cp + 6)) != NULL) {
+ scan_header(p);
+ return dummy_ret;
+ }
+ }
+#endif /* PS */
+
+#if COLOR
+# if PS
+ if (scanned_page_color <= scanned_page)
+# endif
+ {
+ if (memicmp(cp, "background ", 11) == 0) {
+ scan_bg_color(cp);
+ return dummy_ret;
+ }
+ else if (memicmp(cp, "color ", 6) == 0) {
+ scan_color(cp);
+ return dummy_ret;
+ }
+ }
+#endif /* COLOR */
+ if (memcmp(cp, "papersize", 9) == 0 && (p = endofcommand(cp + 9)) != NULL) {
+ m_have_papersize_special = True;
+ scan_papersize(p);
+ return dummy_ret;
+ }
+ }
+ return dummy_ret;
+}
+
+
+/* #define xspell_conv(n) spell_conv0(n, current_dimconv) */
+#define xpixel_conv(x) ((int) ((x) >> 16))
+#define G_PXL_H xpixel_conv(currinf.data.dvi_h)
+
+/* cp is not const, because of endofcommand(). */
+void
+geom_do_special(struct scan_info *info, char *cp, double current_dimconv)
+{
+ const char *p;
+ struct geom_info *g_info = (struct geom_info *)info->data;
+
+ UNUSED(current_dimconv);
+
+ while (isspace((int)*cp))
+ ++cp;
+
+ /* Ignore initial "xdvi:" */
+ if (memcmp(cp, "xdvi:", 5) == 0) {
+ cp += 5;
+ while (isspace((int)*cp))
+ ++cp;
+ }
+
+ if (memicmp(cp, "psfile", 6) == 0 && (p = endofcommand(cp + 6)) != NULL) {
+ /* compute epsf bounding box */
+ char c;
+ int flags = 0;
+ double keyval[6];
+
+ c = *p;
+ if (c == '\'' || c == '"') {
+ do
+ ++p;
+ while (*p != '\0' && *p != c);
+ }
+ else
+ while (*p != '\0' && !isspace((int)*p))
+ ++p;
+ while (isspace((int)*p))
+ ++p;
+ while (*p != '\0') {
+ const char *p1 = p;
+ size_t keyno;
+
+ while (*p1 != '=' && !isspace((int)*p1) && *p1 != '\0')
+ ++p1;
+ for (keyno = 0; keyno < NKEYS; ++keyno) {
+ if (memcmp(p, keytab[keyno], p1 - p) == 0) {
+ if (keyno >= N_ARGLESS_KEYS) {
+ while (isspace((int)*p1))
+ ++p1;
+ if (*p1 == '=') {
+ ++p1;
+ while (isspace((int)*p1))
+ ++p1;
+ }
+ if (keyno < N_ARGLESS_KEYS + 6) {
+ keyval[keyno - N_ARGLESS_KEYS] = atof(p1);
+ flags |= (1 << (keyno - N_ARGLESS_KEYS));
+ }
+ while (!isspace((int)*p1) && *p1 != '\0')
+ ++p1;
+ }
+ break;
+ }
+ }
+ p = p1;
+ while (!isspace((int)*p) && *p != '\0')
+ ++p;
+ while (isspace((int)*p))
+ ++p;
+ }
+
+ if ((flags & 0x30) == 0x30 || ((flags & 0x30) && (flags & 0xf) == 0xf)) {
+ long x = G_PXL_H;
+ long y = PXL_V;
+ long bbox_w;
+ long bbox_h;
+
+ bbox_w = 0.1 * ((flags & 0x10) ? KEY_RWI
+ : KEY_RHI * (KEY_URX - KEY_LLX) / (KEY_URY -
+ KEY_LLY)) *
+ dimconv + 0.5;
+ bbox_h =
+ 0.1 * ((flags & 0x20) ? KEY_RHI : KEY_RWI *
+ (KEY_URY - KEY_LLY) / (KEY_URX - KEY_LLX))
+ * dimconv + 0.5;
+
+ g_info->geom_box(info, x, y - bbox_h, x + bbox_w, y);
+ }
+ }
+ else if (memicmp(cp, "ps::[begin]", 11) == 0) {
+ /* compute psfig bounding box */
+ long bbox_w, bbox_h;
+
+ if (sscanf(cp + 11, "%ld %ld\n", &bbox_w, &bbox_h) >= 2) {
+ long x = G_PXL_H;
+ long y = PXL_V;
+
+ bbox_w = xpixel_conv(spell_conv(bbox_w));
+ bbox_h = xpixel_conv(spell_conv(bbox_h));
+
+ g_info->geom_box(info, x, y, x + bbox_w, y + bbox_h);
+ }
+ }
+}