Back to index

tetex-bin  3.0
special.c
Go to the documentation of this file.
00001 /*========================================================================*\
00002 
00003 Copyright (c) 1990-2004  Paul Vojta
00004 
00005 Permission is hereby granted, free of charge, to any person obtaining a copy
00006 of this software and associated documentation files (the "Software"), to
00007 deal in the Software without restriction, including without limitation the
00008 rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
00009 sell copies of the Software, and to permit persons to whom the Software is
00010 furnished to do so, subject to the following conditions:
00011 
00012 The above copyright notice and this permission notice shall be included in
00013 all copies or substantial portions of the Software.
00014 
00015 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00016 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00017 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
00018 PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM,
00019 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
00020 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
00021 OTHER DEALINGS IN THE SOFTWARE.
00022 
00023 NOTE:
00024 This module is based on prior work as noted below.
00025 
00026 \*========================================================================*/
00027 
00028 /*
00029  * Support drawing routines for TeXsun and TeX
00030  *
00031  *      Copyright, (C) 1987, 1988 Tim Morgan, UC Irvine
00032  *     Adapted for xdvi by Jeffrey Lee, U. of Toronto
00033  *
00034  * At the time these routines are called, the values of hh and vv should
00035  * have been updated to the upper left corner of the graph (the position
00036  * the \special appears at in the dvi file).  Then the coordinates in the
00037  * graphics commands are in terms of a virtual page with axes oriented the
00038  * same as the Imagen and the SUN normally have:
00039  *
00040  *                      0,0
00041  *                       +-----------> +x
00042  *                       |
00043  *                       |
00044  *                       |
00045  *                      \ /
00046  *                       +y
00047  *
00048  * Angles are measured in the conventional way, from +x towards +y.
00049  * Unfortunately, that reverses the meaning of "counterclockwise"
00050  * from what it's normally thought of.
00051  *
00052  * A lot of floating point arithmetic has been converted to integer
00053  * arithmetic for speed.  In some places, this is kind-of kludgy, but
00054  * it's worth it.
00055  */
00056 
00057 
00058 #include <setjmp.h>
00059 
00060 #include "xdvi-config.h"
00061 #include "xdvi.h"
00062 
00063 #include <ctype.h>
00064 
00065 #include "kpathsea/c-fopen.h"
00066 #include "kpathsea/c-stat.h"
00067 #include "kpathsea/line.h"
00068 #include "kpathsea/tex-file.h"
00069 
00070 #include "special.h"
00071 #include "hypertex.h"
00072 #include "dvi.h"
00073 #include "message-window.h"
00074 #include "events.h"
00075 #include "dvi-init.h"
00076 #include "dvi-draw.h"
00077 #include "statusline.h"
00078 #include "util.h"
00079 #include "image-magick.h"
00080 #include "pagesel.h"
00081 #include "my-snprintf.h"
00082 #include "string-utils.h"
00083 
00084 #if PS
00085 # ifdef       PS_DPS
00086 # include "psdps.h"
00087 # endif
00088 # ifdef       PS_NEWS
00089 # include "psnews.h"
00090 # endif
00091 # ifdef       PS_GS
00092 # include "psgs.h"
00093 # endif
00094 
00095 /*
00096   Code inside BUG_888087_FIXED improve the appearance of pic specials at magstep 1, but
00097   _decreases_ it at other magnifications (bug #888087; see
00098   http://sourceforge.net/tracker/index.php?func=detail&aid=888087&group_id=23164&atid=377580)
00099   Disabled until this is fixed.
00100 */
00101 #define BUG_888087_FIXED 0
00102 
00103 
00104 /*
00105   Flag whether this page contains raw PS material;
00106   used to warn user about this:
00107 */
00108 Boolean have_raw_postscript = False;
00109 
00110 # if PS_GS && GS_PIXMAP_CLEARING_HACK
00111 /*
00112   Flag whether this page contains PS material;
00113   used to flush the GS buffer
00114 */
00115 Boolean had_ps_specials = False;
00116 # endif /* PS_GS && GS_PIXMAP_CLEARING_HACK */
00117 
00118 #endif
00119 
00120 
00121 #if COLOR
00122 
00123 /*
00124  *     Color stack used when scanning.  These records stay around, to ease
00125  *     the burden on xmalloc().  The first entry is a dummy entry, giving
00126  *     the default foreground color (as modified).
00127  */
00128 
00129 struct colorframe {
00130     struct colorframe *next, *prev;
00131     struct rgb color;
00132 };
00133 
00134 static Boolean m_have_papersize_special = False;
00135 
00136 void reset_papersize_special(void)
00137 {
00138     m_have_papersize_special = False;
00139 }
00140 
00141 Boolean have_papersize_special(void)
00142 {
00143     return m_have_papersize_special;
00144 }
00145 
00146 static struct colorframe scanstack_head;
00147 static int scanstack_len;
00148 static struct colorframe *scanstack_current;
00149 
00150 /*
00151  *     Page stack when displaying.  Again, the records stay around once
00152  *     allocated.  Bottom entries in the stack (inherited from previous
00153  *     pages) are stored in an array instead (see comments in xdvi.h).
00154  */
00155 
00156 static struct colorframe *rcs_head;
00157 
00158 static Boolean parse_color (const char *cp0, const char *cp, struct rgb *rgbp, Boolean generic_ps_flag);
00159 
00160 #endif /* COLOR */
00161 
00162 
00163 #ifndef S_IRUSR
00164 # define S_IRUSR 0400
00165 #endif
00166 #ifndef S_IWUSR
00167 # define S_IWUSR 0200
00168 #endif
00169 
00170 # if HAVE_SYS_WAIT_H
00171 #  include <sys/wait.h>
00172 # endif
00173 # ifndef WEXITSTATUS
00174 #  define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
00175 # endif
00176 # ifndef WIFEXITED
00177 #  define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
00178 # endif
00179 
00180 #define       MAXPOINTS     300    /* Max points in a path */
00181 #define       MAX_PEN_SIZE  7      /* Max pixels of pen width */
00182 #define       TWOPI         (3.14159265359 * 2.0)
00183 
00184 extern double floor(double);
00185 #define       rint(x)       floor((x) + 0.5)
00186 
00187 static int xx[MAXPOINTS], yy[MAXPOINTS];  /* Path in milli-inches */
00188 static int path_len = 0;    /* # points in current path */
00189 static int pen_size = 1;    /* Pixel width of lines drawn */
00190 
00191 static Boolean whiten = False;
00192 static Boolean shade = False;
00193 static Boolean blacken = False;
00194 
00195 Boolean psfig_begun = False;
00196 
00197 #define       CMD(x, y)     ((x) << 8 | (y))
00198 
00199 
00200 /*
00201  *     X drawing routines
00202  */
00203 
00204 #define       toint(x)      ((int) ((x) + 0.5))
00205 #define       xconv(x)      (toint(tpic_conv*(x))/currwin.shrinkfactor + PXL_H)
00206 #define       yconv(y)      (toint(tpic_conv*(y))/currwin.shrinkfactor + PXL_V)
00207 
00208 /*
00209  *     Draw a line from (fx,fy) to (tx,ty).
00210  */
00211 static void
00212 line_btw(int fx, int fy, int tx, int ty)
00213 {
00214     int fcx = xconv(fx);
00215     int tcx = xconv(tx);
00216     int fcy = yconv(fy);
00217     int tcy = yconv(ty);
00218 
00219     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) &&
00220        (fcy < globals.win_expose.max_y || tcy < globals.win_expose.max_y) && (fcy >= globals.win_expose.min_y || tcy >= globals.win_expose.min_y)) {
00221 #if COLOR
00222        if (fg_active != fg_current)
00223            do_color_change();
00224 #endif
00225        XDrawLine(DISP, currwin.win, globals.gc.rule,
00226                 fcx - currwin.base_x, fcy - currwin.base_y,
00227                 tcx - currwin.base_x, tcy - currwin.base_y);
00228     }
00229 }
00230 
00231 /*
00232  *     Draw a dot at (x,y)
00233  */
00234 static void
00235 dot_at(int x, int y)
00236 {
00237     int cx = xconv(x);
00238     int cy = yconv(y);
00239 
00240     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){
00241 #if COLOR
00242        if (fg_active != fg_current)
00243            do_color_change();
00244 #endif
00245        XDrawPoint(DISP, currwin.win, globals.gc.rule,
00246                  cx - currwin.base_x, cy - currwin.base_y);
00247     }
00248 }
00249 
00250 /*
00251  *     Apply the requested attributes to the last path (box) drawn.
00252  *     Attributes are reset.
00253  *     (Not currently implemented.)
00254  */
00255 static void
00256 do_attribute_path(int last_min_x, int last_max_x, int last_min_y, int last_max_y)
00257 {
00258     UNUSED(last_min_x);
00259     UNUSED(last_max_x);
00260     UNUSED(last_min_y);
00261     UNUSED(last_max_y);
00262 }
00263 
00264 /*
00265  *     Set the size of the virtual pen used to draw in milli-inches
00266  */
00267 static void
00268 set_pen_size(char *cp)
00269 {
00270     int ps;
00271 
00272     if (sscanf(cp, " %d ", &ps) != 1) {
00273        XDVI_WARNING((stderr, "invalid .ps command format: %s", cp));
00274        return;
00275     }
00276     pen_size = (2 * ps * resource.pixels_per_inch / currwin.shrinkfactor + 1000) / 2000;
00277     if (pen_size < 1)
00278        pen_size = 1;
00279     else if (pen_size > MAX_PEN_SIZE)
00280        pen_size = MAX_PEN_SIZE;
00281 
00282 #if BUG_888087_FIXED
00283     if (globals.gc.rule) {
00284        XGCValues values;
00285        values.line_width = pen_size;
00286        XChangeGC(DISP, globals.gc.rule, GCLineWidth, &values);
00287     }
00288 #endif
00289 }
00290 
00291 
00292 /*
00293  *     Print the line defined by previous path commands
00294  */
00295 
00296 static void
00297 flush_path(void)
00298 {
00299     int i;
00300     int last_min_x, last_max_x, last_min_y, last_max_y;
00301 
00302     last_min_x = 30000;
00303     last_min_y = 30000;
00304     last_max_x = -30000;
00305     last_max_y = -30000;
00306     for (i = 1; i < path_len; i++) {
00307        if (xx[i] > last_max_x)
00308            last_max_x = xx[i];
00309        if (xx[i] < last_min_x)
00310            last_min_x = xx[i];
00311        if (yy[i] > last_max_y)
00312            last_max_y = yy[i];
00313        if (yy[i] < last_min_y)
00314            last_min_y = yy[i];
00315        line_btw(xx[i], yy[i], xx[i + 1], yy[i + 1]);
00316     }
00317     if (xx[path_len] > last_max_x)
00318        last_max_x = xx[path_len];
00319     if (xx[path_len] < last_min_x)
00320        last_min_x = xx[path_len];
00321     if (yy[path_len] > last_max_y)
00322        last_max_y = yy[path_len];
00323     if (yy[path_len] < last_min_y)
00324        last_min_y = yy[path_len];
00325     path_len = 0;
00326     do_attribute_path(last_min_x, last_max_x, last_min_y, last_max_y);
00327 }
00328 
00329 
00330 /*
00331  *     Print a dashed line along the previously defined path, with
00332  *     the dashes/inch defined.
00333  */
00334 
00335 static void
00336 flush_dashed(char *cp, Boolean dotted)
00337 {
00338     int i;
00339     int numdots;
00340     int lx0, ly0, lx1, ly1;
00341     int cx0, cy0, cx1, cy1;
00342     float inchesperdash;
00343     double d, spacesize, a, b = 0.0, dx, dy, milliperdash;
00344 
00345     if (sscanf(cp, " %f ", &inchesperdash) != 1) {
00346        XDVI_WARNING((stderr, "invalid format for dotted/dashed line: %s", cp));
00347        return;
00348     }
00349     if (path_len <= 1 || inchesperdash <= 0.0) {
00350        XDVI_WARNING((stderr, "invalid conditions for dotted/dashed line"));
00351        return;
00352     }
00353     milliperdash = inchesperdash * 1000.0;
00354     lx0 = xx[1];
00355     ly0 = yy[1];
00356     lx1 = xx[2];
00357     ly1 = yy[2];
00358     dx = lx1 - lx0;
00359     dy = ly1 - ly0;
00360     if (dotted) {
00361        numdots = sqrt(dx * dx + dy * dy) / milliperdash + 0.5;
00362        if (numdots == 0)
00363            numdots = 1;
00364        for (i = 0; i <= numdots; i++) {
00365            a = (float)i / (float)numdots;
00366            cx0 = lx0 + a * dx + 0.5;
00367            cy0 = ly0 + a * dy + 0.5;
00368            dot_at(cx0, cy0);
00369        }
00370     }
00371     else {
00372        d = sqrt(dx * dx + dy * dy);
00373        numdots = d / (2.0 * milliperdash) + 1.0;
00374        if (numdots <= 1)
00375            line_btw(lx0, ly0, lx1, ly1);
00376        else {
00377            spacesize = (d - numdots * milliperdash) / (numdots - 1);
00378            for (i = 0; i < numdots - 1; i++) {
00379               a = i * (milliperdash + spacesize) / d;
00380               b = a + milliperdash / d;
00381               cx0 = lx0 + a * dx + 0.5;
00382               cy0 = ly0 + a * dy + 0.5;
00383               cx1 = lx0 + b * dx + 0.5;
00384               cy1 = ly0 + b * dy + 0.5;
00385               line_btw(cx0, cy0, cx1, cy1);
00386               b += spacesize / d;
00387            }
00388            cx0 = lx0 + b * dx + 0.5;
00389            cy0 = ly0 + b * dy + 0.5;
00390            line_btw(cx0, cy0, lx1, ly1);
00391        }
00392     }
00393     path_len = 0;
00394 }
00395 
00396 
00397 /*
00398  *     Add a point to the current path
00399  */
00400 
00401 static void
00402 add_path(char *cp)
00403 {
00404     int pathx, pathy;
00405 
00406     if (++path_len >= MAXPOINTS)
00407        XDVI_FATAL((stderr, "Too many points"));
00408     if (sscanf(cp, " %d %d ", &pathx, &pathy) != 2)
00409        XDVI_FATAL((stderr, "Malformed path command"));
00410     xx[path_len] = pathx;
00411     yy[path_len] = pathy;
00412 }
00413 
00414 
00415 /*
00416  *     Draw to a floating point position
00417  */
00418 
00419 static void
00420 im_fdraw(double x, double y)
00421 {
00422     if (++path_len >= MAXPOINTS)
00423        XDVI_FATAL((stderr, "Too many arc points"));
00424     xx[path_len] = x + 0.5;
00425     yy[path_len] = y + 0.5;
00426 }
00427 
00428 
00429 /*
00430  *     Draw an ellipse with the indicated center and radices.
00431  */
00432 
00433 static void
00434 draw_ellipse(int xc, int yc, int xr, int yr)
00435 {
00436     double angle, theta;
00437     int n;
00438     int px0, py0, px1, py1;
00439 
00440     angle = (xr + yr) / 2.0;
00441     theta = sqrt(1.0 / angle);
00442     n = TWOPI / theta + 0.5;
00443     if (n < 12)
00444        n = 12;
00445     else if (n > 80)
00446        n = 80;
00447     n /= 2;
00448     theta = TWOPI / n;
00449 
00450     angle = 0.0;
00451     px0 = xc + xr;   /* cos(0) = 1 */
00452     py0 = yc; /* sin(0) = 0 */
00453     while ((angle += theta) <= TWOPI) {
00454        px1 = xc + xr * cos(angle) + 0.5;
00455        py1 = yc + yr * sin(angle) + 0.5;
00456        line_btw(px0, py0, px1, py1);
00457        px0 = px1;
00458        py0 = py1;
00459     }
00460     line_btw(px0, py0, xc + xr, yc);
00461 }
00462 
00463 /*
00464  *     Draw an arc
00465  */
00466 
00467 static void
00468 arc(char *cp, Boolean invis)
00469 {
00470     int xc, yc, xrad, yrad, n;
00471     float start_angle, end_angle, angle, theta, r;
00472     double xradius, yradius, xcenter, ycenter;
00473 
00474     n = sscanf(cp, " %d %d %d %d %f %f ", &xc, &yc, &xrad, &yrad,
00475               &start_angle, &end_angle);
00476 
00477     if (n != 6) {
00478        XDVI_WARNING((stderr, "invalid arc specification: %s", cp));
00479        return;
00480     }
00481 
00482     if (invis)
00483        return;
00484 
00485     /* We have a specialized fast way to draw closed circles/ellipses */
00486     if (start_angle <= 0.0 && end_angle >= 6.282) {
00487        draw_ellipse(xc, yc, xrad, yrad);
00488        return;
00489     }
00490     xcenter = xc;
00491     ycenter = yc;
00492     xradius = xrad;
00493     yradius = yrad;
00494     r = (xradius + yradius) / 2.0;
00495     theta = sqrt(1.0 / r);
00496 #if BUG_888087_FIXED
00497     n = (pen_size * TWOPI) / (theta * currwin.shrinkfactor) + 0.5;
00498 #else
00499     n = 0.3 * TWOPI / theta + 0.5;
00500 #endif
00501     if (n < 12)
00502        n = 12;
00503     else if (n > 80)
00504        n = 80;
00505     n /= 2;
00506     theta = TWOPI / n;
00507     flush_path();
00508     im_fdraw(xcenter + xradius * cos(start_angle),
00509             ycenter + yradius * sin(start_angle));
00510     angle = start_angle + theta;
00511     if (end_angle < start_angle)
00512        end_angle += TWOPI;
00513     while (angle < end_angle) {
00514        im_fdraw(xcenter + xradius * cos(angle),
00515                ycenter + yradius * sin(angle));
00516        angle += theta;
00517     }
00518     im_fdraw(xcenter + xradius * cos(end_angle),
00519             ycenter + yradius * sin(end_angle));
00520     flush_path();
00521 }
00522 
00523 
00524 /*
00525  *     APPROXIMATE integer distance between two points
00526  */
00527 
00528 #define       dist(x0, y0, x1, y1) (abs(x0 - x1) + abs(y0 - y1))
00529 
00530 
00531 /*
00532  *     Draw a spline along the previously defined path
00533  */
00534 
00535 static void
00536 flush_spline(void)
00537 {
00538     int xp, yp;
00539     int N;
00540     int lastx = -1, lasty = -1;
00541     Boolean lastvalid = False;
00542     int t1, t2, t3;
00543     int steps;
00544     int j;
00545     int i, w;
00546 
00547     N = path_len + 1;
00548     xx[0] = xx[1];
00549     yy[0] = yy[1];
00550     xx[N] = xx[N - 1];
00551     yy[N] = yy[N - 1];
00552     for (i = 0; i < N - 1; i++) {  /* interval */
00553        steps = (dist(xx[i], yy[i], xx[i + 1], yy[i + 1]) +
00554                dist(xx[i + 1], yy[i + 1], xx[i + 2], yy[i + 2])) / 80;
00555        for (j = 0; j < steps; j++) {      /* points within */
00556            w = (j * 1000 + 500) / steps;
00557            t1 = w * w / 20;
00558            w -= 500;
00559            t2 = (750000 - w * w) / 10;
00560            w -= 500;
00561            t3 = w * w / 20;
00562            xp =
00563               (t1 * xx[i + 2] + t2 * xx[i + 1] + t3 * xx[i] + 50000) / 100000;
00564            yp =
00565               (t1 * yy[i + 2] + t2 * yy[i + 1] + t3 * yy[i] + 50000) / 100000;
00566            if (lastvalid)
00567               line_btw(lastx, lasty, xp, yp);
00568            lastx = xp;
00569            lasty = yp;
00570            lastvalid = True;
00571        }
00572     }
00573     path_len = 0;
00574 }
00575 
00576 
00577 /*
00578  *     Shade the last box, circle, or ellipse
00579  */
00580 
00581 static void
00582 shade_last(void)
00583 {
00584     blacken = whiten = False;
00585     shade = True;
00586 }
00587 
00588 
00589 /*
00590  *     Make the last box, circle, or ellipse, white inside (shade with white)
00591  */
00592 
00593 static void
00594 whiten_last(void)
00595 {
00596     whiten = True;
00597     blacken = shade = False;
00598 }
00599 
00600 
00601 /*
00602  *     Make last box, etc, black inside
00603  */
00604 
00605 static void
00606 blacken_last(void)
00607 {
00608     blacken = True;
00609     whiten = shade = False;
00610 }
00611 
00612 
00613 /*
00614  *     Code for PostScript<tm> specials begins here.
00615  */
00616 
00617 #if PS
00618 
00619 /*
00620  *     Information on how to search for PS header and figure files.
00621  */
00622 
00623 static void ps_startup(int, int, const char *);
00624 static void ps_startup2(void);
00625 
00626 /* dummy procedures */
00627 static void NullProc(void) { }
00628 static void NullProcInt(int flag) { UNUSED(flag); }
00629 static void NullProcStr(const char *cp) { UNUSED(cp); }
00630 
00631 
00632 struct psprocs psp = {      /* used for lazy startup of the ps machinery */
00633     /* toggle */ NullProcInt,
00634     /* destroy */ NullProc,
00635     /* interrupt */ NullProc,
00636     /* endpage */ NullProc,
00637     /* drawbegin */ ps_startup,
00638     /* drawraw */ NullProcStr,
00639     /* drawfile */ NULL,
00640     /* drawend */ NullProcStr,
00641     /* beginheader */ ps_startup2,
00642     /* endheader */ NullProc,
00643     /* newdoc */ NullProc
00644 };
00645 
00646 struct psprocs no_ps_procs = {     /* used if postscript is unavailable */
00647     /* toggle */ NullProcInt,
00648     /* destroy */ NullProc,
00649     /* interrupt */ NullProc,
00650     /* endpage */ NullProc,
00651     /* drawbegin */ drawbegin_none,
00652     /* drawraw */ NullProcStr,
00653     /* drawfile */ NULL,
00654     /* drawend */ NullProcStr,
00655     /* beginheader */ NullProc,
00656     /* endheader */ NullProc,
00657     /* newdoc */ NullProc
00658 };
00659 
00660 #endif /* PS */
00661 
00662 #ifdef MAGICK
00663 int bbox_angle;
00664 Boolean bbox_valid;
00665 unsigned int bbox_width;
00666 unsigned int bbox_height;
00667 int bbox_voffset;
00668 #else
00669 static int bbox_angle;
00670 static Boolean bbox_valid;
00671 static unsigned int bbox_width;
00672 static unsigned int bbox_height;
00673 static int bbox_voffset;
00674 #endif
00675 
00676 /* info on bboxes on this page */
00677 struct bbox_info {
00678     int x;
00679     int y;
00680     int w;
00681     int h;
00682     int angle;
00683 };
00684     
00685 static struct bbox_info *g_bbox_info = NULL;
00686 static size_t g_bbox_info_size = 0;
00687 static size_t g_bbox_info_max_size = 0;
00688 
00689 /*
00690   Append the current coordinates to g_bbox_info, unless it already
00691   contains these coordinates.
00692  */
00693 
00694 static void
00695 append_bbox_info(int x, int y, int w, int h, int angle)
00696 {
00697     Boolean found = False;
00698     size_t i;
00699     const size_t SIZE_STEP = 16;
00700     
00701     /* is this box already present? */
00702     for (i = 0; i < g_bbox_info_size; i++) {
00703        if (g_bbox_info[i].x == x
00704            && g_bbox_info[i].y == y
00705            && g_bbox_info[i].w == w
00706            && g_bbox_info[i].h == h
00707            && g_bbox_info[i].angle == angle) {
00708            found = True;
00709            break;
00710        }
00711     }
00712 
00713     if (!found) {
00714        g_bbox_info_size++;
00715        
00716        while (g_bbox_info_size >= g_bbox_info_max_size) {
00717            g_bbox_info_max_size += SIZE_STEP;
00718        }
00719        
00720        g_bbox_info = xrealloc(g_bbox_info, g_bbox_info_max_size * sizeof *g_bbox_info);
00721        g_bbox_info[g_bbox_info_size - 1].x = x;
00722        g_bbox_info[g_bbox_info_size - 1].y = y;
00723        g_bbox_info[g_bbox_info_size - 1].w = w;
00724        g_bbox_info[g_bbox_info_size - 1].h = h;
00725        g_bbox_info[g_bbox_info_size - 1].angle = angle;
00726     }
00727 }
00728 
00729 static void
00730 draw_bbox0(int xcorner, int ycorner)
00731 {
00732     if (bbox_valid) {
00733 #if COLOR
00734        if (fg_active != fg_current)
00735            do_color_change();
00736 #endif
00737 
00738        if (bbox_angle == 0) {
00739            ycorner -= bbox_voffset;
00740            XDrawRectangle(DISP, currwin.win, globals.gc.high, xcorner, ycorner, bbox_width, bbox_height);
00741            if (resource.postscript == 0) {
00742               if (htex_inside_href) {
00743                   htex_set_anchorsize(xcorner, ycorner, xcorner + bbox_width, ycorner + bbox_height);
00744                   htex_set_objecttype(HTEX_IMG);
00745               }
00746            }
00747        }
00748        else {
00749            float sin_a = sin(bbox_angle * (TWOPI / 360));
00750            float cos_a = cos(bbox_angle * (TWOPI / 360));
00751            float a, b, c, d;
00752 
00753            a = cos_a * bbox_width;
00754            b = -sin_a * bbox_width;
00755            c = -sin_a * bbox_height;
00756            d = -cos_a * bbox_height;
00757 
00758            XDrawLine(DISP, currwin.win, globals.gc.high,
00759                     xcorner, ycorner,
00760                     xcorner + (int)rint(a), ycorner + (int)rint(b));
00761            XDrawLine(DISP, currwin.win, globals.gc.high,
00762                     xcorner + (int)rint(a), ycorner + (int)rint(b),
00763                     xcorner + (int)rint(a + c), ycorner + (int)rint(b + d));
00764            XDrawLine(DISP, currwin.win, globals.gc.high,
00765                     xcorner + (int)rint(a + c), ycorner + (int)rint(b + d),
00766                     xcorner + (int)rint(c), ycorner + (int)rint(d));
00767            XDrawLine(DISP, currwin.win, globals.gc.high,
00768                     xcorner + (int)rint(c), ycorner + (int)rint(d),
00769                     xcorner, ycorner);
00770        }
00771        bbox_valid = False;
00772     }
00773 }
00774 
00775 /*
00776   Display the items in bbox_info.
00777   Invoked from draw_page() in dvi-draw.c.
00778  */
00779 void
00780 display_bboxes(void)
00781 {
00782     size_t i;
00783     
00784     for (i = 0; i < g_bbox_info_size; i++) {
00785        if (globals.debug & DBG_PS) {
00786            fprintf(stderr, "drawing bbox %lu at %d %d, %d x %d, angle %d\n",
00787                   (unsigned long)i,
00788                   g_bbox_info[i].x,
00789                   g_bbox_info[i].y,
00790                   g_bbox_info[i].w,
00791                   g_bbox_info[i].h,
00792                   g_bbox_info[i].angle);
00793        }
00794 #if 0
00795        XDrawRectangle(DISP, currwin.win, globals.gc.high,
00796                      g_bbox_info[i].x,
00797                      g_bbox_info[i].y,
00798                      g_bbox_info[i].w,
00799                      g_bbox_info[i].h);
00800 #else
00801        bbox_valid = True;
00802        bbox_width = g_bbox_info[i].w;
00803        bbox_height = bbox_voffset = g_bbox_info[i].h;
00804        bbox_angle = g_bbox_info[i].angle;
00805        draw_bbox0(g_bbox_info[i].x, g_bbox_info[i].y + bbox_height);
00806 #endif
00807     }
00808     bbox_angle = 0;
00809     bbox_valid = False;
00810 }
00811 
00812 void
00813 clear_bboxes(void)
00814 {
00815     free(g_bbox_info);
00816     g_bbox_info = NULL;
00817     g_bbox_info_size = 0;
00818     g_bbox_info_max_size = 0;
00819 }
00820 
00821 void
00822 save_bbox(void)
00823 {
00824     int xcorner, ycorner;
00825 
00826     if (bbox_valid) {
00827        xcorner = PXL_H - currwin.base_x;
00828        ycorner = PXL_V - currwin.base_y;
00829        
00830        ycorner -= bbox_voffset;
00831        append_bbox_info(xcorner, ycorner, bbox_width, bbox_height, bbox_angle);
00832 
00833        /* register boundaries of this box as anchor boundaries */
00834        if (htex_inside_href) {
00835            htex_set_anchorsize(xcorner, ycorner, xcorner + bbox_width, ycorner + bbox_height);
00836            htex_set_objecttype(HTEX_IMG);
00837        }
00838     }
00839 }
00840 
00841 
00842 void
00843 draw_bbox(void)
00844 {
00845     draw_bbox0(PXL_H - currwin.base_x, PXL_V - currwin.base_y);
00846 }
00847 
00848 #if PS
00849 
00850 static XIOErrorHandler oldhandler;
00851 
00852 static int XDviIOErrorHandler(Display * disp)
00853 {
00854     ps_destroy();
00855     return oldhandler(disp);
00856 }
00857 
00858 static void
00859 actual_startup(int flag)
00860 {
00861     UNUSED(flag);
00862     oldhandler = XSetIOErrorHandler(XDviIOErrorHandler);
00863 
00864     /*
00865      * Figure out what we want to use to display postscript figures
00866      * and set at most one of the following to True:
00867      * resource.useGS, resource.useDPS, resource.useNeWS
00868      *
00869      * Choose DPS then NEWS then Ghostscript if they are available
00870      */
00871     if (!(
00872 #ifdef PS_DPS
00873          (resource.useDPS && initDPS())
00874 #if    defined(PS_NEWS) || defined(PS_GS)
00875          ||
00876 #endif
00877 #endif /* PS_DPS */
00878 #ifdef PS_NEWS
00879          (resource.useNeWS && initNeWS())
00880 #ifdef PS_GS
00881          ||
00882 #endif
00883 #endif /* PS_NEWS */
00884 #ifdef PS_GS
00885          (resource.useGS && initGS())
00886 #endif
00887          ))
00888        psp = no_ps_procs;
00889 }
00890 
00891 static void
00892 ps_startup(int xul, int yul, const char *cp)
00893 {
00894     if (resource.postscript == 0) {
00895        draw_bbox();
00896        return;
00897     }
00898     actual_startup(0);
00899     psp.drawbegin(xul, yul, cp);
00900 }
00901 
00902 static void
00903 ps_startup2(void)
00904 {
00905     actual_startup(0);
00906     psp.beginheader();
00907 }
00908 
00909 
00910 /*
00911  * dumb parsing of PostScript - search for rotation H. Zeller 1/97
00912  * Returns true if we find a potentially non-supported command that
00913  * we want to warn users about.
00914  */
00915 static Boolean
00916 ps_parseraw(const char *PostScript_cmd)
00917 {
00918     const char *p;
00919 
00920     bbox_angle = 0;
00921     p = strstr(PostScript_cmd, "rotate");
00922     if (p != NULL) {
00923        while (*p != '\0' && !isdigit((int)*p))
00924            --p;
00925        while (*p != '\0' && isdigit((int)*p))
00926            --p;
00927        if (*p != '+' && *p != '-')
00928            ++p;
00929        sscanf(p, "%d neg rotate", &bbox_angle);
00930        return True;
00931     }
00932     if (strstr(PostScript_cmd, " scale ") != NULL)
00933        return True;
00934     return False;
00935 }
00936 
00937 
00938 void
00939 drawbegin_none(int xul, int yul, const char *cp)
00940 {
00941     UNUSED(xul);
00942     UNUSED(yul);
00943     UNUSED(cp);
00944     
00945     draw_bbox();
00946 }
00947 
00948 
00949 struct tickrec {
00950     struct tickrec *next;
00951     int pageno;
00952     char *command;
00953     char *tempname;
00954 };
00955 
00956 static struct tickrec *tickhead = NULL;   /* head of linked list of */
00957 /* cached information */
00958 static int nticks = 0;      /* number of records total */
00959 
00960 #ifndef       TICKCACHESIZE
00961 #define       TICKCACHESIZE 3
00962 #endif
00963 
00964 #ifndef       TICKTMP
00965 #define       TICKTMP              "/tmp"
00966 #endif
00967 
00968 /*
00969  *     cachetick() - returns:
00970  *            NULL          error;
00971  *            fp == NULL    string was not in cache, fd = file
00972  *            fp != NULL    string was in cache, fp = cached file
00973  */
00974 
00975 static struct tickrec *
00976 cachetick(const char *filename, kpse_file_format_type pathinfo, FILE **fp, int *fdp)
00977 {
00978     struct tickrec **linkp;
00979     struct tickrec *tikp;
00980     struct tickrec **freerecp;
00981 
00982     linkp = &tickhead;
00983     freerecp = NULL;
00984     for (;;) {       /* see if we have it already */
00985        tikp = *linkp;
00986        if (tikp == NULL) {  /* if end of list */
00987            int fd;
00988 
00989            if (nticks >= TICKCACHESIZE && freerecp != NULL) {
00990               tikp = *freerecp;
00991               *freerecp = tikp->next;
00992               free(tikp->command);
00993               unlink(tikp->tempname);
00994               /* reuse tikp and tikp->tempname */
00995            }
00996            else {
00997               tikp = xmalloc(sizeof(struct tickrec));
00998               tikp->tempname = NULL;
00999               ++nticks;
01000            }
01001            fd = xdvi_temp_fd(&tikp->tempname);
01002            if (fd == -1) {
01003               perror("Cannot create temporary file");
01004               free(tikp->tempname);
01005               free(tikp);
01006               return NULL;
01007            }
01008            tikp->command = xstrdup(filename);
01009            *fp = NULL;
01010            *fdp = fd;
01011            break;
01012        }
01013        if (strcmp(filename, tikp->command) == 0) {      /* found it */
01014            *linkp = tikp->next;    /* unlink it */
01015            *fp = XFOPEN(tikp->tempname, OPEN_MODE);
01016            if (*fp == NULL) {
01017               perror(tikp->tempname);
01018               free(tikp->tempname);
01019               free(tikp->command);
01020               free(tikp);
01021               return NULL;
01022            }
01023            break;
01024        }
01025        if (tikp->pageno != current_page)
01026            freerecp = linkp;
01027        linkp = &tikp->next;
01028     }
01029     tikp->next = tickhead;  /* link it in */
01030     tickhead = tikp;
01031     tikp->pageno = pathinfo != kpse_tex_ps_header_format ? current_page : -1;
01032     return tikp;
01033 }
01034 
01035 void
01036 ps_clear_cache(void)
01037 {
01038     struct tickrec *tikp;
01039 
01040     while (tickhead != NULL) {
01041        tikp = tickhead;
01042        tickhead = tickhead->next;
01043        free(tikp->command);
01044        unlink(tikp->tempname);
01045        free((char *)tikp->tempname);
01046        free(tikp);
01047     }
01048     nticks = 0;
01049 }
01050 
01051 #ifndef       UNCOMPRESS
01052 #define       UNCOMPRESS    "uncompress"
01053 #endif
01054 
01055 #ifndef       GUNZIP
01056 #define       GUNZIP        "gunzip"
01057 #endif
01058 
01059 #ifndef       BUNZIP2
01060 #define       BUNZIP2              "bunzip2"
01061 #endif
01062 
01063 static void send_ps_file(const char *filename, kpse_file_format_type pathinfo);
01064 
01065 static void
01066 enable_specials_send_ps_file(XtPointer data)
01067 {
01068     const char *filename = (const char *)data;
01069     resource.allow_shell = True;
01070     redraw_page(); /* to erase the bounding box */
01071     statusline_print(STATUS_MEDIUM,
01072                    "Shell specials enabled for this session.",
01073                    filename);
01074     send_ps_file(filename, kpse_pict_format);
01075 }
01076 
01077 static void
01078 try_open_tempname(int status, struct xchild *this)
01079 {
01080     if (WIFEXITED(status)) { /* child exited normally */
01081        if (WEXITSTATUS(status) != 0) {
01082            /* default error procedure */
01083            handle_child_exit(status, this);
01084        }
01085        else {
01086            struct tickrec *tikp = (struct tickrec *)this->data;
01087            FILE *f = XFOPEN(tikp->tempname, OPEN_MODE);
01088            fprintf(stderr, "FILE: %s\n", tikp->tempname);
01089            if (f == NULL) {
01090               perror(tikp->tempname);
01091            }
01092            else {
01093               fprintf(stderr, "sending file: %s, %p\n", tikp->tempname, (void *)f);
01094               /* There's no point in invoking
01095                     psp.drawfile(tikp->tempname, f);
01096                  here, since usually it will be already too late (draw_part() which
01097                  had called us via applicationDoSpecial() will already have terminated).
01098                  So instead, we just close the file and force a redraw of the entire page.
01099               */
01100               fclose(f);
01101               globals.ev.flags |= EV_NEWPAGE;
01102            }
01103        }
01104     }
01105     else if (WIFSIGNALED(status)) {
01106        popup_message(globals.widgets.top_level,
01107                     MSG_WARN,
01108                     NULL,
01109                     "Process `%s' terminated abnormally with signal %d.", this->name, WTERMSIG(status));
01110     }
01111     else if (WIFSTOPPED(status)) {
01112        popup_message(globals.widgets.top_level,
01113                     MSG_WARN,
01114                     NULL,
01115                     "Process `%s' stopped by signal %d.", this->name, WSTOPSIG(status));
01116     }
01117     else {
01118        popup_message(globals.widgets.top_level,
01119                     MSG_WARN,
01120                     NULL,
01121                     "Process `%s' terminated with unknown status.", this->name);
01122     }
01123 }
01124 
01125 static void
01126 send_ps_file(const char *filename, kpse_file_format_type pathinfo)
01127 {
01128     FILE *f;
01129     int fd;
01130     static const char *argv[] = { NULL, "-c", NULL, NULL };
01131     char *bufp = NULL;
01132     struct tickrec *volatile tikp;
01133     size_t len;
01134     char magic1, magic2, magic3;
01135     static Boolean warned_about_shellescape = False;
01136     static size_t ffline_len = 0;
01137     static char *ffline = NULL;
01138     const size_t FFLINE_STEP = 128;
01139 
01140     if (psp.drawfile == NULL || resource.postscript == 0) {
01141        return;
01142     }
01143 
01144     if (filename[0] == '`') {
01145        if (!resource.allow_shell) {
01146            if (!warned_about_shellescape) {
01147               choice_dialog_sized(globals.widgets.top_level,
01148                                 MSG_INFO,
01149                                 SIZE_MEDIUM,
01150                                 /* helptext */
01151                                 "To enable shell specials, use the \"-allowshell\" command "
01152                                 "line option.\n\n"
01153                                 "WARNING: You should NOT use shell specials like\n"
01154                                 "`gunzip -c file.eps.gz\n"
01155                                 "(e.g. via \\DeclareGraphicsRule{...}) "
01156                                 "to uncompress .eps.gz files, even though some obsolete "
01157                                 "LaTeX documentation might suggest it. Current versions "
01158                                 "of xdvi and dvips will handle .eps.gz files just fine without this trick.\n",
01159 #ifndef MOTIF
01160                                 NULL,
01161 #endif
01162                                 NULL, NULL, /* no pre_callbacks */
01163                                 /* Cancel label/callback */
01164                                 "Cancel", NULL, (XtPointer)NULL,
01165                                 /* Enable label/callback */
01166                                 "Enable", enable_specials_send_ps_file, (XtPointer)filename,
01167                                 /* msg */
01168                                 "This page contains a shell special \"%s\", but execution of shell specials "
01169                                 "is disabled.\n\nYou can now click "
01170                                 "\"Enable\" to enable shell specials for this session only, "
01171                                 "\"Cancel\" to leave the specials disabled, or \"Help\" for more help "
01172                                 "on this topic.\n\n"
01173                                 "Please note that shell specials are a security risk, since they "
01174                                 "allow execution of arbitrary shell commands from the TeX file. "
01175                                 "You should enable them only for DVI files that you created yourself.",
01176                                 filename);
01177               warned_about_shellescape = True;
01178            }
01179            else {
01180               statusline_print(STATUS_MEDIUM,
01181                              "Info: Shell special \"%s\" disabled.",
01182                              filename);
01183            }
01184            draw_bbox();
01185            return;
01186        }
01187 
01188        tikp = cachetick(filename, pathinfo, &f, &fd);
01189        if (tikp == NULL)    /* if error */
01190            return;
01191        if (f == NULL) {
01192            char *argv[4];
01193            /* if not cached, need to create.
01194               Fork so that we can collect the process' error messages. */
01195 
01196            /* FIXME: Insecure - this is a /tmp race; shouldn't close(fd), just re-use it */
01197            close(fd);
01198 
01199            len = strlen(filename) + strlen(tikp->tempname) + (4 - 1);
01200            if (len > ffline_len) {
01201               ffline_len += FFLINE_STEP;
01202               ffline = xrealloc(ffline, ffline_len);
01203            }
01204            
01205            sprintf(ffline, "%s > %s", filename + 1, tikp->tempname);
01206            argv[0] = "/bin/sh";
01207            argv[1] = "-c";
01208            argv[2] = ffline;
01209            argv[3] = NULL;
01210            fork_process("/bin/sh", False, globals.dvi_file.dirname, try_open_tempname, tikp, argv);
01211        }
01212        else {
01213            bufp = tikp->tempname;
01214            psp.drawfile(bufp, f);
01215        }
01216     }
01217     else {
01218        char *expanded_filename = NULL;
01219        struct stat statbuf;
01220        expanded_filename = find_file(filename, &statbuf, pathinfo);
01221        if (expanded_filename == NULL &&
01222            (pathinfo == kpse_enc_format || pathinfo == kpse_type1_format)) {
01223            /* in this case, we also kpathsea-search in the `old' place for
01224               backwards compatibility: kpse_tex_ps_header_format (see comment
01225               for load_vector(), dvi-draw.c for details) */
01226            expanded_filename = kpse_find_file(filename, kpse_tex_ps_header_format, True);
01227        }
01228        if (expanded_filename == NULL) { /* still no success, complain */
01229            /* FIXME: this warning may be overwritten by warning about raw PS specials,
01230               so additinally dump to stderr. TODO: make statusline printing respect
01231               more important messages. */
01232            XDVI_WARNING((stderr, "Could not find graphics file \"%s\"", filename));
01233            statusline_print(STATUS_MEDIUM, "Warning: Could not find graphics file \"%s\"",
01234                           filename);
01235            draw_bbox();
01236            return;
01237        }
01238        if (globals.debug & DBG_OPEN)
01239            printf("%s:%d: |%s| expanded to |%s|\n", __FILE__, __LINE__, filename, expanded_filename);
01240 
01241        /* may need to adjust ffline_len, ffline */
01242        while (strlen(expanded_filename) + 1 > ffline_len) {
01243            ffline_len += FFLINE_STEP;
01244            ffline = xrealloc(ffline, ffline_len);
01245        }
01246        strcpy(ffline, expanded_filename);
01247        bufp = ffline;
01248        f = XFOPEN(expanded_filename, OPEN_MODE);
01249        free(expanded_filename);
01250        ASSERT(f != NULL, "Internal error opening file");
01251 
01252        /* check for compressed files */
01253        len = strlen(filename);
01254        magic1 = '\037';
01255        magic3 = '\0';
01256        if ((len > 2 && strcmp(filename + len - 2, ".Z") == 0
01257             && (argv[0] = UNCOMPRESS, magic2 = '\235', True))
01258            || (len > 3 && strcmp(filename + len - 3, ".gz") == 0
01259               && (argv[0] = GUNZIP, magic2 = '\213', True))
01260            || (len > 4 && strcmp(filename + len - 4, ".bz2") == 0
01261               && (argv[0] = BUNZIP2, magic1 = 'B', magic2 = 'Z',
01262                   magic3 = 'h', True))) {
01263            if (getc(f) != magic1 || (char)getc(f) != magic2
01264               || (magic3 != '\0' && getc(f) != magic3)) {
01265               rewind(f);
01266            }
01267            else {
01268               fclose(f);
01269 
01270               tikp = cachetick(filename, pathinfo, &f, &fd);
01271               if (tikp == NULL)    /* if error */
01272                   return;
01273               if (f == NULL) {     /* if not cached, need to create */
01274                   pid_t pid;
01275                   int status;
01276 
01277                   argv[2] = bufp;
01278                   fflush(stderr);  /* avoid double flushing */
01279                   pid = vfork();
01280                   if (pid == 0) {  /* if child */
01281                      (void)dup2(fd, 1);
01282                      (void)execvp(argv[0], (char **)argv);
01283                      XDVI_ERROR((stderr, "Execvp of %s failed: %s", argv[0], strerror(errno)));
01284                      _exit(EXIT_FAILURE);
01285                   }
01286                   (void)close(fd);
01287 
01288                   for (;;) {
01289 #if HAVE_WAITPID
01290                      if (waitpid(pid, &status, 0) != -1)
01291                          break;
01292 #else
01293 # if HAVE_WAIT4
01294                      if (wait4(pid, &status, 0, (struct rusage *)NULL)
01295                          != -1)
01296                          break;
01297 # else
01298                      int retval;
01299 
01300                      retval = wait(&status);
01301                      if (retval == pid)
01302                          break;
01303                      if (retval != -1)
01304                          continue;
01305 # endif       /* HAVE_WAIT4 */
01306 #endif /* HAVE_WAITPID */
01307                      if (errno == EINTR)
01308                          continue;
01309                      perror("[xdvik] waitpid");
01310                      return;
01311                   }
01312                   f = XFOPEN(tikp->tempname, OPEN_MODE);
01313                   if (f == NULL) {
01314                      perror(tikp->tempname);
01315                      return;
01316                   }
01317               }
01318               bufp = tikp->tempname;
01319            }
01320        }
01321        /* Success! */
01322        psp.drawfile(bufp, f); /* this is supposed to close the file */
01323     }
01324 }
01325 
01326 
01327 void
01328 ps_destroy(void)
01329 {
01330     struct tickrec   *tikp;
01331 
01332     /* Note:  old NeXT systems (at least) lack atexit/on_exit.  */
01333     psp.destroy();
01334     for (tikp = tickhead; tikp != NULL; tikp = tikp->next)
01335        if (unlink(tikp->tempname) < 0)
01336            perror(tikp->tempname);
01337 }
01338 
01339 #endif /* PS */
01340 
01341 
01342 void
01343 init_prescan(void)
01344 {
01345 #if PS
01346     struct tickrec   *tikp;
01347 #endif
01348 
01349     scanned_page = scanned_page_reset = resource.prescan ? -1 : total_pages + 1;
01350 #if PS
01351     scanned_page_ps = scanned_page_ps_bak = scanned_page;
01352 #if COLOR
01353     scanned_page_color = scanned_page;
01354 #endif /* COLOR */
01355 #endif /* PS */
01356 
01357     TRACE_FILES((stderr, "init_prescan: scanned_page = %d", scanned_page));
01358 #if PS
01359     if (resource.postscript == 0)
01360        scanned_page_ps = total_pages + 1;
01361 
01362     for (tikp = tickhead; tikp != NULL; tikp = tikp->next)
01363        tikp->pageno = -1;
01364     psp.newdoc();
01365 #endif
01366 
01367 #if COLOR
01368     if (!resource.use_color)
01369        scanned_page_color = total_pages + 1;
01370 #endif
01371 
01372     if (ignore_papersize_specials) {
01373 #if PS && COLOR
01374        scanned_page = scanned_page_ps < scanned_page_color ? scanned_page_ps : scanned_page_color;
01375 #elif PS
01376        scanned_page = scanned_page_ps;
01377 #elif COLOR
01378        scanned_page = scanned_page_color;
01379 #else
01380        scanned_page = total_pages + 1;
01381 #endif
01382     }
01383 }
01384 
01385 
01386 static void
01387 psfig_special(char *cp)
01388 {
01389     char *filename;
01390     int raww, rawh;
01391 
01392     if (strncmp(cp, ":[begin]", 8) == 0) {
01393        cp += 8;
01394        bbox_valid = False;
01395        bbox_angle = 0;
01396        if (sscanf(cp, "%d %d\n", &raww, &rawh) >= 2) {
01397            bbox_valid = True;
01398            bbox_width = pixel_conv(spell_conv(raww));
01399            bbox_height = pixel_conv(spell_conv(rawh));
01400            bbox_voffset = 0;
01401        }
01402        if (INSIDE_MANE_WIN) {
01403 #if    PS
01404            psp.drawbegin(PXL_H - currwin.base_x, PXL_V - currwin.base_y, cp);
01405 #else
01406            draw_bbox();
01407 #endif
01408        }
01409        psfig_begun = True;
01410     }
01411     else if (strncmp(cp, " plotfile ", 10) == 0) {
01412        cp += 10;
01413        while (isspace((int)*cp))
01414            cp++;
01415        /* handle "`zcat file". Borrowed from dvipsk... */
01416        if (*cp == '"') {
01417            cp++;
01418            for (filename = cp; *cp && (*cp != '"'); ++cp);
01419        }
01420        else {
01421            for (filename = cp; *cp && !isspace((int)*cp); ++cp);
01422        }
01423        *cp = '\0';
01424 #if    PS
01425        if (INSIDE_MANE_WIN)
01426            send_ps_file(filename, kpse_pict_format);
01427 #endif
01428     }
01429     else if (strncmp(cp, ":[end]", 6) == 0) {
01430        cp += 6;
01431 #if    PS
01432        if (INSIDE_MANE_WIN) {
01433            psp.drawend(cp);
01434        }
01435 #endif
01436        bbox_valid = False;
01437        psfig_begun = False;
01438     }
01439     else if (*cp == ':') {
01440        /* I am going to send some raw postscript stuff */
01441        ++cp;  /* skip the colon */
01442 #if    PS
01443        if (ps_parseraw(cp))
01444            have_raw_postscript = True;
01445        if (INSIDE_MANE_WIN)
01446            psp.drawraw(cp);
01447 #endif
01448     }
01449     else { /* attempt to parse pstricks color specials */
01450 #if COLOR
01451        struct rgb color;
01452        if (parse_color(cp, cp, &color, True)) {
01453            /* clear stack */
01454            if (rcs_head == NULL) {
01455               rcs_head = xmalloc(sizeof *rcs_head);
01456               rcs_head->prev = rcs_head->next = NULL;
01457            }
01458            rcs_top = rcs_head;
01459            color_bot_size = 0;
01460            
01461            /* Change top of stack */
01462            rcs_top->color = color;
01463            
01464            set_fg_color(&color);
01465        }
01466 #endif     
01467        /* also raw PostScript, but no extra colon to skip */
01468 #if PS
01469        if (INSIDE_MANE_WIN) {
01470            if (ps_parseraw(cp))
01471               have_raw_postscript = True;
01472            
01473            if (psfig_begun)
01474               psp.drawraw(cp);
01475            else {
01476               psp.drawbegin(PXL_H - currwin.base_x,
01477                            PXL_V - currwin.base_y, cp);
01478               psp.drawend("");
01479            }
01480        }
01481 #endif
01482     }
01483 }
01484 
01485 
01486 /*     Keys for epsf specials */
01487 
01488 static const char *keytab[] = { "clip",
01489                               "llx",
01490                               "lly",
01491                               "urx",
01492                               "ury",
01493                               "rwi",
01494                               "rhi",
01495                               "hsize",
01496                               "vsize",
01497                               "hoffset",
01498                               "voffset",
01499                               "hscale",
01500                               "vscale",
01501                               "angle"
01502 };
01503 
01504 #define       KEY_LLX       keyval[0]
01505 #define       KEY_LLY       keyval[1]
01506 #define       KEY_URX       keyval[2]
01507 #define       KEY_URY       keyval[3]
01508 #define       KEY_RWI       keyval[4]
01509 #define       KEY_RHI       keyval[5]
01510 
01511 #define       NKEYS  (sizeof keytab /sizeof *keytab)
01512 #define       N_ARGLESS_KEYS 1
01513 
01514 static void
01515 epsf_special(char *cp)
01516 {
01517     char *filename;
01518     static char *buffer;
01519     static unsigned int buflen = 0;
01520     unsigned int len;
01521     char *q;
01522     int flags = 0;
01523     double keyval[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
01524 
01525     filename = cp;
01526     if (*cp == '\'' || *cp == '"') {
01527        do
01528            ++cp;
01529        while (*cp != '\0' && *cp != *filename);
01530        ++filename;
01531     }
01532     else
01533        while (*cp != '\0' && !isspace((int)*cp))
01534            ++cp;
01535     if (*cp != '\0')
01536        *cp++ = '\0';
01537     while (isspace((int)*cp))
01538        ++cp;
01539     len = strlen(cp) + NKEYS + 30;
01540     if (buflen < len) {
01541        if (buflen != 0)
01542            free(buffer);
01543        buflen = len;
01544        buffer = xmalloc(buflen);
01545     }
01546     strcpy(buffer, "@beginspecial");
01547     q = buffer + strlen(buffer);
01548     while (*cp != '\0') {
01549        char *p1 = cp;
01550        size_t keyno;
01551 
01552        while (*p1 != '=' && !isspace((int)*p1) && *p1 != '\0')
01553            ++p1;
01554        for (keyno = 0;; ++keyno) {
01555            if (keyno >= NKEYS) {
01556               if (globals.warn_spec_now)
01557                   XDVI_WARNING((stderr, "Ignoring unknown keyword (%.*s) in \\special",
01558                               (int)(p1 - cp), cp));
01559               break;
01560            }
01561            if (memcmp(cp, keytab[keyno], p1 - cp) == 0) {
01562               if (keyno >= N_ARGLESS_KEYS) {
01563                   while (isspace((int)*p1))
01564                      ++p1;
01565                   if (*p1 == '=') {
01566                      ++p1;
01567                      while (isspace((int)*p1))
01568                          ++p1;
01569                   }
01570                   if (keyno < N_ARGLESS_KEYS + 6) {
01571                      keyval[keyno - N_ARGLESS_KEYS] = atof(p1);
01572                      flags |= (1 << (keyno - N_ARGLESS_KEYS));
01573                   }
01574                   *q++ = ' ';
01575                   while (!isspace((int)*p1) && *p1 != '\0')
01576                      *q++ = *p1++;
01577               }
01578               *q++ = ' ';
01579               *q++ = '@';
01580               strcpy(q, keytab[keyno]);
01581               q += strlen(q);
01582               break;
01583            }
01584        }
01585        cp = p1;
01586        while (!isspace((int)*cp) && *cp != '\0')
01587            ++cp;
01588        while (isspace((int)*cp))
01589            ++cp;
01590     }
01591     strcpy(q, " @setspecial\n");
01592 
01593     bbox_valid = False;
01594     /* Validate the case where both rwi and rhi are undefined
01595      * (and llx, lly, urx, ury are properly defined) */
01596     if (!(flags & 0x30) && (flags & 0xf) == 0xf) {
01597        KEY_RWI = (KEY_URX - KEY_LLX) * 10;
01598        flags |= 0x10;
01599     }
01600     if ((flags & 0x30) == 0x30 || ((flags & 0x30) && (flags & 0xf) == 0xf)) {
01601        bbox_valid = True;
01602        bbox_width = 0.1 * ((flags & 0x10) ? KEY_RWI
01603                          : KEY_RHI * (KEY_URX - KEY_LLX) / (KEY_URY -
01604                                                         KEY_LLY)) *
01605            dimconv / currwin.shrinkfactor + 0.5;
01606        bbox_voffset = bbox_height =
01607            0.1 * ((flags & 0x20) ? KEY_RHI : KEY_RWI * (KEY_URY - KEY_LLY) /
01608                  (KEY_URX - KEY_LLX))
01609            * dimconv / currwin.shrinkfactor + 0.5;
01610     }
01611 
01612     if (INSIDE_MANE_WIN) {
01613 #if    PS
01614        psp.drawbegin(PXL_H - currwin.base_x, PXL_V - currwin.base_y, buffer);
01615        /* talk directly with the DPSHandler here */
01616        send_ps_file(filename, kpse_pict_format);
01617        psp.drawend(" @endspecial");
01618 #else
01619        draw_bbox();
01620 #endif
01621     }
01622     bbox_valid = False;
01623 }
01624 
01625 
01626 static void
01627 quote_special(char *cp)
01628 {
01629     bbox_valid = False;
01630 
01631 #if    PS
01632     if (currwin.win == mane.win) {
01633        psp.drawbegin(PXL_H - currwin.base_x, PXL_V - currwin.base_y,
01634                     "@beginspecial @setspecial ");
01635        /* talk directly with the DPSHandler here */
01636        psp.drawraw(cp + 1);
01637        psp.drawend(" @endspecial");
01638     }
01639 #endif
01640 
01641     /* nothing else to do--there's no bbox here */
01642 }
01643 
01644 #if    PS
01645 
01646 static void
01647 scan_header(char *cp)
01648 {
01649     char *filename;
01650     /* default for .pro files: */
01651     kpse_file_format_type our_format;
01652     
01653 #if PS_GS
01654     if (gs_postpone_prescan)
01655        return;
01656 #endif
01657     filename = cp;
01658     if (*cp == '\'' || *cp == '"') {
01659        do
01660            ++cp;
01661        while (*cp != '\0' && *cp != *filename);
01662        *cp = '\0';
01663        ++filename;
01664     }
01665 
01666     psp.beginheader();
01667     /* check for suffixes other than `.pro' (default PS headers),
01668        case-insensitive */
01669     if (str_is_suffix(".pfa", filename, False)
01670        || str_is_suffix(".pfb", filename, False))
01671        our_format = kpse_type1_format;
01672     else if (str_is_suffix(".enc", filename, False))
01673        our_format = kpse_enc_format;
01674     else
01675        our_format = kpse_tex_ps_header_format;
01676     
01677     send_ps_file(filename, our_format);
01678 }
01679 
01680 static void
01681 scan_bang(char *cp)
01682 {
01683     psp.beginheader();
01684     psp.drawraw(cp + 1);
01685 }
01686 
01687 #endif /* PS */
01688 
01689 #if COLOR
01690 
01691 /*
01692  *     Table of dvips color names.  Produced by passing the table in
01693  *     dvipsk/color.lpro through the following perl script, and then
01694  *     through sort.
01695  *
01696  *     #! /usr/bin/perl -w
01697  *
01698  *     sub cvpart {
01699  *         return $_[0] < 1.0 ? 1.0 - $_[0] : 0.0;
01700  *     }
01701  *     
01702  *     $pat = "^\\/(\\w+)\\s*\\{\\s*([0-9.]+)\\s*([0-9.]+)\\s*([0-9.]+)"
01703  *       . "\\s*([0-9.]+)\\s*setcmykcolor\\s*\\}\\s*DC\\s*\$";
01704  *     while (<STDIN>) {
01705  *         chop;
01706  *         if (/^%%/) {next;}
01707  *         if (/$pat/o) {
01708  *            printf "\t\tDVIPSCOLORDESC(%2d, \"%s\", %g, %g, %g),\n",
01709  *              length($1), $1,
01710  *              cvpart($2 + $5), cvpart($3 + $5), cvpart($4 + $5);
01711  *         }
01712  *         else {
01713  *            print "Bad line: ", $_, "\n";
01714  *         }
01715  *     }
01716  */
01717 
01718 struct dvipscolor {
01719     const char *name;
01720     struct rgb color;
01721 };
01722 
01723 static Boolean
01724 parse_color(const char *cp0, const char *cp,
01725            struct rgb *rgbp, Boolean generic_ps_flag)
01726 {
01727     double r, g, b;
01728     double k;
01729     double hue, sat, bri;
01730     char dummy[8];
01731 
01732 #define       CVPART(x)     ((int)((x) * 65535 + 0.5))
01733 #define       COLOR_DESC(name, r, g, b) \
01734               {name, {CVPART(r), CVPART(g), CVPART(b)}}
01735 
01736     /* hash table to speed up lookup in following list of color names */
01737     static hashTableT colornames_hash;
01738     /*
01739       Use a prime close to sizof colornames / sizeof colornames[0].
01740       You might want to update this when extending the colornames array,
01741       and to check the filling factor and average chain length by uncommenting the
01742          hash_print(colornames_hash, True);
01743       below. I usually go for filling > 50% and avg. chain len < 2.
01744       
01745       The 68-elem array below results in:
01746       79 buckets, 43 nonempty (54%); 68 entries, average chain 1.6.
01747     */
01748     static const size_t colornames_hash_size = 79;
01749     
01750     static struct dvipscolor colornames[] = {
01751        COLOR_DESC("Red", 1, 0, 0),
01752        COLOR_DESC("Tan", 0.86, 0.58, 0.44),
01753        COLOR_DESC("Blue", 0, 0, 1),
01754        COLOR_DESC("Cyan", 0, 1, 1),
01755        COLOR_DESC("Gray", 0.5, 0.5, 0.5),
01756        COLOR_DESC("Plum", 0.5, 0, 1),
01757        COLOR_DESC("Black", 0, 0, 0),
01758        COLOR_DESC("Brown", 0.4, 0, 0),
01759        COLOR_DESC("Green", 0, 1, 0),
01760        COLOR_DESC("Melon", 1, 0.54, 0.5),
01761        COLOR_DESC("Peach", 1, 0.5, 0.3),
01762        COLOR_DESC("Sepia", 0.3, 0, 0),
01763        COLOR_DESC("White", 1, 1, 1),
01764        COLOR_DESC("Maroon", 0.68, 0, 0),
01765        COLOR_DESC("Orange", 1, 0.39, 0.13),
01766        COLOR_DESC("Orchid", 0.68, 0.36, 1),
01767        COLOR_DESC("Purple", 0.55, 0.14, 1),
01768        COLOR_DESC("Salmon", 1, 0.47, 0.62),
01769        COLOR_DESC("Violet", 0.21, 0.12, 1),
01770        COLOR_DESC("Yellow", 1, 1, 0),
01771        COLOR_DESC("Apricot", 1, 0.68, 0.48),
01772        COLOR_DESC("Emerald", 0, 1, 0.5),
01773        COLOR_DESC("Fuchsia", 0.45, 0.01, 0.92),
01774        COLOR_DESC("Magenta", 1, 0, 1),
01775        COLOR_DESC("SkyBlue", 0.38, 1, 0.88),
01776        COLOR_DESC("Thistle", 0.88, 0.41, 1),
01777        COLOR_DESC("BrickRed", 0.72, 0, 0),
01778        COLOR_DESC("Cerulean", 0.06, 0.89, 1),
01779        COLOR_DESC("Lavender", 1, 0.52, 1),
01780        COLOR_DESC("Mahogany", 0.65, 0, 0),
01781        COLOR_DESC("Mulberry", 0.64, 0.08, 0.98),
01782        COLOR_DESC("NavyBlue", 0.06, 0.46, 1),
01783        COLOR_DESC("SeaGreen", 0.31, 1, 0.5),
01784        COLOR_DESC("TealBlue", 0.12, 0.98, 0.64),
01785        COLOR_DESC("BlueGreen", 0.15, 1, 0.67),
01786        COLOR_DESC("CadetBlue", 0.38, 0.43, 0.77),
01787        COLOR_DESC("Dandelion", 1, 0.71, 0.16),
01788        COLOR_DESC("Goldenrod", 1, 0.9, 0.16),
01789        COLOR_DESC("LimeGreen", 0.5, 1, 0),
01790        COLOR_DESC("OrangeRed", 1, 0, 0.5),
01791        COLOR_DESC("PineGreen", 0, 0.75, 0.16),
01792        COLOR_DESC("RawSienna", 0.55, 0, 0),
01793        COLOR_DESC("RedOrange", 1, 0.23, 0.13),
01794        COLOR_DESC("RedViolet", 0.59, 0, 0.66),
01795        COLOR_DESC("Rhodamine", 1, 0.18, 1),
01796        COLOR_DESC("RoyalBlue", 0, 0.5, 1),
01797        COLOR_DESC("RubineRed", 1, 0, 0.87),
01798        COLOR_DESC("Turquoise", 0.15, 1, 0.8),
01799        COLOR_DESC("VioletRed", 1, 0.19, 1),
01800        COLOR_DESC("Aquamarine", 0.18, 1, 0.7),
01801        COLOR_DESC("BlueViolet", 0.1, 0.05, 0.96),
01802        COLOR_DESC("DarkOrchid", 0.6, 0.2, 0.8),
01803        COLOR_DESC("OliveGreen", 0, 0.6, 0),
01804        COLOR_DESC("Periwinkle", 0.43, 0.45, 1),
01805        COLOR_DESC("Bittersweet", 0.76, 0.01, 0),
01806        COLOR_DESC("BurntOrange", 1, 0.49, 0),
01807        COLOR_DESC("ForestGreen", 0, 0.88, 0),
01808        COLOR_DESC("GreenYellow", 0.85, 1, 0.31),
01809        COLOR_DESC("JungleGreen", 0.01, 1, 0.48),
01810        COLOR_DESC("ProcessBlue", 0.04, 1, 1),
01811        COLOR_DESC("RoyalPurple", 0.25, 0.1, 1),
01812        COLOR_DESC("SpringGreen", 0.74, 1, 0.24),
01813        COLOR_DESC("YellowGreen", 0.56, 1, 0.26),
01814        COLOR_DESC("MidnightBlue", 0, 0.44, 0.57),
01815        COLOR_DESC("YellowOrange", 1, 0.58, 0),
01816        COLOR_DESC("CarnationPink", 1, 0.37, 1),
01817        COLOR_DESC("CornflowerBlue", 0.35, 0.87, 1),
01818        COLOR_DESC("WildStrawberry", 1, 0.04, 0.61),
01819     };
01820 #undef CVPART
01821 #undef COLOR_DESC
01822 
01823     UNUSED(cp0);
01824     
01825     while (*cp == ' ') ++cp;
01826 
01827     if (generic_ps_flag) {
01828         /*
01829          check for pstricks color specials.
01830          The dummy buffer is to ensure that there are no other (general PS)
01831          commands following (we wouldn't know how to deal with these).
01832        */
01833        if (sscanf(cp, "%lf %lf %lf setrgbcolor %7s", &r, &g, &b, dummy) == 3
01834            && r >= 0 && r <= 1 && g >= 0 && g <= 1 && b >= 0 && b <= 1) {
01835            rgbp->r = r * 65535 + 0.5;
01836            rgbp->g = g * 65535 + 0.5;
01837            rgbp->b = b * 65535 + 0.5;
01838            return True;
01839        }
01840        else if (sscanf(cp, "%lf %lf %lf %lf setcmykcolor %7s", &r, &g, &b, &k, dummy) == 4
01841                && r >= 0 && r <= 1 && g >= 0 && g <= 1 && b >= 0 && b <= 1
01842                && k >= 0 && k <= 1) {
01843            r = 1.0 - r - k; /* cyan --> red */
01844            rgbp->r = (r < 0 ? 0 : r * 65535 + 0.5);
01845            g = 1.0 - g - k; /* magenta --> green */
01846            rgbp->g = (g < 0 ? 0 : g * 65535 + 0.5);
01847            b = 1.0 - b - k; /* yellow --> blue */
01848            rgbp->b = (b < 0 ? 0 : b * 65535 + 0.5);
01849            return True;
01850        }
01851        else if (sscanf(cp, "%lf %lf %lf sethsbcolor %7s", &hue, &sat, &bri, dummy) == 3 && hue >= 0
01852                && hue <= 1 && sat >= 0 && sat <= 1 && bri >= 0 && bri <= 1) {
01853            int       h      = (int) (6 * hue);
01854            double    p      = bri * (1 - sat);
01855            double    q      = bri * (1 - sat * (6 * hue - h));
01856            double    t      = p - q + bri;
01857            
01858            switch (h) {
01859            case 0:  r = bri; g = t; b = p; break; /* Red - Yel */
01860            case 1:  r = q; g = bri; b = p; break; /* Yel - Grn */
01861            case 2:  r = p; g = bri; b = t; break; /* Grn - Cyn */
01862            case 3:  r = p; g = q; b = bri; break; /* Cyn - Blu */
01863            case 4:  r = t; g = p; b = bri; break; /* Blu - Mag */
01864            case 5:  r = bri; g = p; b = q; break; /* Mag - Red */
01865            case 6:  r = bri; g = b = p;    break; /* Red */
01866            }
01867            rgbp->r = r * 65535 + 0.5;
01868            rgbp->g = g * 65535 + 0.5;
01869            rgbp->b = b * 65535 + 0.5;
01870            return True;
01871        }
01872        else if (sscanf(cp, "%lf setgray %7s", &r, dummy) == 1 && r >= 0 && r <= 1) {
01873            rgbp->r = rgbp->g = rgbp->b = r * 65535 + 0.5;
01874            return True;
01875        }
01876        else
01877            return False;
01878     }
01879 
01880     /* no generic PS command; must be a dvips color special */
01881     
01882     if (memicmp(cp, "rgb ", 4) == 0) {
01883        if (sscanf(cp + 3, "%lf %lf %lf", &r, &g, &b) == 3
01884            && r >= 0 && r <= 1 && g >= 0 && g <= 1 && b >= 0 && b <= 1) {
01885            rgbp->r = r * 65535 + 0.5;
01886            rgbp->g = g * 65535 + 0.5;
01887            rgbp->b = b * 65535 + 0.5;
01888            return True;
01889        }
01890     }
01891     else if (memicmp(cp, "gray ", 5) == 0) {
01892        if (sscanf(cp + 4, "%lf", &r) == 1 && r >= 0 && r <= 1) {
01893            rgbp->r = rgbp->g = rgbp->b = r * 65535 + 0.5;
01894            return True;
01895        }
01896     }
01897     else if (memicmp(cp, "cmyk ", 5) == 0) {
01898        if (sscanf(cp + 4, "%lf %lf %lf %lf", &r, &g, &b, &k) == 4
01899            && r >= 0 && r <= 1 && g >= 0 && g <= 1 && b >= 0 && b <= 1
01900            && k >= 0 && k <= 1) {
01901            r = 1.0 - r - k; /* cyan --> red */
01902            rgbp->r = (r < 0 ? 0 : r * 65535 + 0.5);
01903            g = 1.0 - g - k; /* magenta --> green */
01904            rgbp->g = (g < 0 ? 0 : g * 65535 + 0.5);
01905            b = 1.0 - b - k; /* yellow --> blue */
01906            rgbp->b = (b < 0 ? 0 : b * 65535 + 0.5);
01907            return True;
01908        }
01909     }
01910     else if (memicmp(cp, "hsb ", 4) == 0) {
01911        if (sscanf(cp + 3, "%lf %lf %lf", &hue, &sat, &bri) == 3 && hue >= 0
01912            && hue <= 1 && sat >= 0 && sat <= 1 && bri >= 0 && bri <= 1) {
01913            int       h      = (int) (6 * hue);
01914            double    p      = bri * (1 - sat);
01915            double    q      = bri * (1 - sat * (6 * hue - h));
01916            double    t      = p - q + bri;
01917 
01918            switch (h) {
01919            case 0:  r = bri; g = t; b = p; break; /* Red - Yel */
01920            case 1:  r = q; g = bri; b = p; break; /* Yel - Grn */
01921            case 2:  r = p; g = bri; b = t; break; /* Grn - Cyn */
01922            case 3:  r = p; g = q; b = bri; break; /* Cyn - Blu */
01923            case 4:  r = t; g = p; b = bri; break; /* Blu - Mag */
01924            case 5:  r = bri; g = p; b = q; break; /* Mag - Red */
01925            case 6:  r = bri; g = b = p;    break; /* Red */
01926            }
01927            rgbp->r = r * 65535 + 0.5;
01928            rgbp->g = g * 65535 + 0.5;
01929            rgbp->b = b * 65535 + 0.5;
01930            return True;
01931        }
01932     }
01933     else { /* is it a dvips color name? */
01934        size_t idx;
01935        size_t len = 0;
01936        static char *testname = NULL;
01937        static size_t testname_len = 0;
01938        size_t testname_step = 16;
01939        
01940        if (colornames_hash.size == 0) { /* initialize hash table */
01941            size_t i;
01942            colornames_hash = hash_create(colornames_hash_size);
01943            /* insert color names and array indexes */
01944            for (i = 0; i < (sizeof colornames / sizeof colornames[0]); i++) {
01945               put_str_int_hash(&colornames_hash, colornames[i].name, i);
01946            }
01947        }
01948        /* uncomment the following to get statistics on hashing: */
01949        /* hash_print(colornames_hash, True); */
01950 
01951        /* need to copy to a null-terminated string for hash lookup ... */
01952        while (isalpha((int)cp[len])) {
01953            ++len;    /* get length of color name */
01954        }
01955        while (len >= testname_len) {
01956            testname_len += testname_step;
01957            testname = xrealloc(testname, testname_len);
01958        }
01959        memcpy(testname, cp, len);
01960        testname[len] = '\0';
01961        
01962        if (find_str_int_hash(&colornames_hash, testname, &idx)) {
01963            *rgbp = colornames[idx].color;
01964            return True;
01965        }
01966     }
01967 
01968     /* not found */
01969     XDVI_WARNING((stderr, "Ignoring invalid color name in special `%s'", cp0 == NULL ? "<NULL>" : cp0));
01970     return False;
01971 }
01972 
01973 void
01974 init_page_colors(struct rgb *foreground, struct rgb *background)
01975 {
01976     int       i;
01977     
01978     page_colors.size = total_pages;
01979     page_colors.stack = xmalloc(page_colors.size * sizeof *page_colors.stack);
01980 
01981     for (i = 0; i <= scanned_page + 1; ++i) {
01982        page_colors.stack[i].bg = *background;
01983        page_colors.stack[i].colorstack = foreground;
01984        page_colors.stack[i].stacksize = 1;
01985     }
01986     for (; i < total_pages; i++) {
01987        page_colors.stack[i].colorstack = NULL;
01988     }
01989 
01990     scanstack_head.color = *foreground;
01991     scanstack_len = 1;                    /* nothing yet */
01992     scanstack_current = &scanstack_head;  /* stack position */
01993 }
01994 
01995 
01996 static void
01997 scan_bg_color(const char *cp)
01998 {
01999     if (!resource.use_color)
02000        return;
02001 
02002     if (page_colors.stack == NULL)
02003        init_page_colors(&fg_initial, &bg_initial);
02004 
02005     ASSERT(scanned_page < (int)page_colors.size, "page_colors.size too small");
02006     (void) parse_color(cp, cp + 11, &(page_colors.stack[scanned_page + 1].bg), False);
02007 }
02008 
02009 void
02010 scan_color(const char *cp)
02011 {
02012     const char       *cp1 = cp + 6;
02013 
02014     if (!resource.use_color)
02015        return;
02016 
02017     while (*cp1 == ' ') ++cp1;
02018 
02019     if (page_colors.stack == NULL)
02020        init_page_colors(&fg_initial, &bg_initial);
02021 
02022     if (memicmp(cp1, "push ", 5) == 0) {
02023        if (scanstack_current->next == NULL) {    /* if at end */
02024            scanstack_current->next = xmalloc(sizeof *scanstack_current);
02025            scanstack_current->next->prev = scanstack_current;
02026            scanstack_current->next->next = NULL;
02027        }
02028        scanstack_current = scanstack_current->next;
02029        ++scanstack_len;
02030        if (!parse_color(cp, cp1 + 5, &scanstack_current->color, False))
02031            scanstack_current->color = scanstack_current->prev->color;
02032     }
02033     else if (memicmp(cp1, "pop", 3) == 0) {
02034        if (scanstack_len <= 1) {
02035            XDVI_WARNING((stderr, "Color pop occurred with empty color stack."));
02036        }
02037        else {
02038            scanstack_current = scanstack_current->prev;
02039            --scanstack_len;
02040        }
02041     }
02042     else {
02043        (void) parse_color(cp, cp1, &scanstack_head.color, False);
02044        if (scanstack_len > 1) {
02045            struct colorframe       *cfp;
02046 
02047            XDVI_WARNING((stderr, "Global color change occurred with non-empty color stack!\n"
02048                        "Trying to recover by setting all stack entries to that color."));
02049            for (cfp = scanstack_head.next;; cfp = cfp->next) {
02050               cfp->color = scanstack_head.color;
02051               if (cfp == scanstack_current) break;
02052            }
02053        }
02054     }
02055 }
02056 
02057 void
02058 scan_color_eop(void)
02059 {
02060     int i;
02061     struct rgb *prev, *p1, *rgbp;
02062     struct colorframe *cf;
02063 
02064     if (page_colors.stack == NULL)
02065        return;
02066 
02067     /* set background color for next page */
02068     if (scanned_page + 1 < total_pages) {
02069        ASSERT(scanned_page + 1 < (int)page_colors.size, "page_colors.size too small");
02070        page_colors.stack[scanned_page + 1].bg = page_colors.stack[scanned_page].bg;
02071     }
02072     ASSERT(scanned_page < (int)page_colors.size, "page_colors.size too small");
02073     /* save the stack contents */
02074     page_colors.stack[scanned_page].stacksize = scanstack_len;
02075 
02076     prev = &fg_initial;
02077     i = 1;
02078     if (scanned_page > 0) {
02079        prev = page_colors.stack[scanned_page - 1].colorstack;
02080        i = page_colors.stack[scanned_page - 1].stacksize;
02081     }
02082     if (scanstack_len <= i) {
02083        /* try to reuse the previous array */
02084        p1 = prev;
02085        cf = &scanstack_head;
02086        for (i = 0;;) {
02087            if (p1->r != cf->color.r || p1->g != cf->color.g || p1->b != cf->color.b)
02088               break;
02089            if (++i >= scanstack_len) {    /* end loop; reuse memory */
02090               page_colors.stack[scanned_page].colorstack = prev;
02091               return;       /* done */
02092            }
02093            ++p1;
02094            cf = cf->next;
02095        }
02096     }
02097     page_colors.stack[scanned_page].colorstack = rgbp = xmalloc(scanstack_len * sizeof *rgbp);
02098     cf = &scanstack_head;
02099     for (i = 0; i < scanstack_len; ++i) {
02100        *rgbp++ = cf->color;
02101        cf = cf->next;
02102     }
02103 }
02104 
02105 
02106 /*
02107  *     We don't actually do any X calls yet to change colors; that can wait
02108  *     until a character or rule needs to be drawn.
02109  */
02110 
02111 void
02112 set_fg_color(const struct rgb *color)
02113 {
02114     struct fgrec     **fgpp;
02115 
02116     if (fg_current != NULL
02117        && color->r == fg_current->color.r
02118        && color->g == fg_current->color.g
02119        && color->b == fg_current->color.b) {
02120        return;
02121     }
02122 
02123     ASSERT(bg_current != NULL, "Background color not initialized");
02124     for (fgpp = &bg_current->fg_head;;) {
02125        fg_current = *fgpp;
02126        if (fg_current == NULL) {   /* if color is not in list */
02127            fg_current = *fgpp = xmalloc(sizeof *fg_current);
02128            fg_current->next = NULL;
02129            fg_current->color = *color;
02130            fg_current->pixel_good = False;
02131 #if GREY
02132            fg_current->palette_good = False;
02133 #endif
02134            break;
02135        }
02136        if (fg_current->color.r == color->r
02137            && fg_current->color.g == color->g
02138            && fg_current->color.b == color->b)
02139            break;
02140        fgpp = &fg_current->next;
02141     }
02142     if (globals.debug & DBG_DVI)
02143        printf("Changing fg color to %5d %5d %5d\n",
02144               color->r, color->g, color->b);
02145 }
02146 
02147 #if 0
02148 static void
02149 show_stack(void) {
02150     struct colorframe *ptr;
02151     int i;
02152     fprintf(stderr, "FG: %d,%d,%d\n", fg_current->color.r, fg_current->color.b, fg_current->color.g);
02153     for (i = 0, ptr = rcs_top; ptr != NULL; ptr = ptr->prev, i++) {
02154        fprintf(stderr, "stack %d: %d,%d,%d\n", i, ptr->color.r, ptr->color.b, ptr->color.g);
02155     }
02156 }
02157 #endif
02158 
02159 void
02160 color_special(const char *cp)
02161 {
02162     while (*cp == ' ') ++cp;
02163 
02164     if (memicmp(cp, "push ", 5) == 0) {
02165        if (rcs_top == NULL) {
02166            if (rcs_head == NULL) {
02167               rcs_head = xmalloc(sizeof *rcs_head);
02168               rcs_head->prev = rcs_head->next = NULL;
02169            }
02170            rcs_top = rcs_head;
02171        }
02172        else {
02173            struct colorframe *rcs_next;
02174 
02175            rcs_next = rcs_top->next;
02176            if (rcs_next == NULL) {
02177               rcs_next = rcs_top->next = xmalloc(sizeof *rcs_next);
02178               rcs_next->prev = rcs_top;
02179               rcs_next->next = NULL;
02180            }
02181            rcs_top = rcs_next;
02182        }
02183        if (!parse_color(NULL, cp + 5, &rcs_top->color, False)) {
02184            if (rcs_top->prev != NULL)
02185               rcs_top->color = rcs_top->prev->color;
02186            else
02187               rcs_top->color = color_bottom[color_bot_size - 1];
02188        }
02189        set_fg_color(&rcs_top->color);
02190     }
02191     else if (memicmp(cp, "pop", 3) == 0) {
02192        /* Pop stack */
02193        if (rcs_top != NULL) {
02194            if (color_bot_size > 0)
02195               rcs_top = rcs_top->prev;
02196            else
02197               return;
02198        }
02199        else if (color_bot_size > 1)
02200            --color_bot_size;
02201        else {
02202            if (scanned_page_reset >= 0) {
02203               /* turn on scanning and redraw the page */
02204               scanned_page = scanned_page_color = scanned_page_reset = -1;
02205 #if PS
02206               scanned_page_ps = scanned_page;
02207 #endif
02208 /*            fprintf(stderr, "forcing redraw!!!\n"); */
02209               globals.ev.flags |= EV_NEWPAGE;           /* force a redraw */
02210               longjmp(globals.ev.canit, 1);
02211            }
02212            return;
02213        }
02214 /*     fprintf(stderr, "bot_size: %d\n", color_bot_size); */
02215        set_fg_color(rcs_top != NULL ? &rcs_top->color
02216                    : &color_bottom[color_bot_size - 1]);
02217     }
02218     else {
02219        struct rgb color;
02220 
02221        if (!parse_color(NULL, cp, &color, False))
02222            return;
02223 
02224        /* clear stack */
02225        if (rcs_head == NULL) {
02226            rcs_head = xmalloc(sizeof *rcs_head);
02227            rcs_head->prev = rcs_head->next = NULL;
02228        }
02229        rcs_top = rcs_head;
02230        color_bot_size = 0;
02231 
02232        /* Change top of stack */
02233        rcs_top->color = color;
02234 
02235        set_fg_color(&color);
02236     }
02237 }
02238 
02239 #endif /* COLOR */
02240 
02241 static unsigned int
02242 myatopix(const char **pp)
02243 {
02244 #define SCR_LEN 16
02245     unsigned int value;
02246     const char *cp = *pp;
02247     char scr[16];
02248     const char *p0, *p1;
02249 
02250     p0 = cp;
02251     while ((*cp >= '0' && *cp <= '9') || *cp == '.')
02252        ++cp;
02253     p1 = cp;
02254     while (isspace((int)*cp))
02255        ++cp;
02256     if (*cp >= 'a' && *cp <= 'z' && cp[1] >= 'a' && cp[1] <= 'z') {
02257        /* if units are present */
02258        if (p1 - p0 <= SCR_LEN - 3) {
02259            sprintf(scr, "%.*s%c%c", (int)(p1 - p0), p0, *cp, cp[1]);
02260            value = atopix(scr, False);
02261        }
02262        else
02263            value = 0;
02264        cp += 2;
02265     }
02266     else
02267        value = atopix(p0, False);
02268 
02269     *pp = cp;
02270     return value;
02271 #undef SCR_LEN
02272 }
02273 
02274 static void
02275 scan_papersize(const char *cp0)
02276 {
02277     const char *cp = cp0;
02278     unsigned int w, h;
02279     double mag = 1.0;
02280 
02281     if (ignore_papersize_specials)
02282        return;
02283 
02284     if (*cp == '*') {
02285        do
02286            ++cp;
02287        while (isspace((int)*cp));
02288        mag = magnification * .001;
02289     }
02290            
02291     w = myatopix(&cp) * mag + 0.5;
02292 
02293     while (isspace((int)*cp))
02294        ++cp;
02295     if (*cp == ',')
02296        do
02297            ++cp;
02298        while (isspace((int)*cp));
02299 
02300     h = myatopix(&cp) * mag + 0.5;
02301 
02302     if (w == 0 || h == 0)
02303        XDVI_WARNING((stderr, "Invalid papersize special `%s'", cp0));
02304     else {
02305        /* we have a paper size special; disable xdvirc_geometry:
02306           fprintf(stderr, "---------- scanned papersize: %dx%d\n", w, h);
02307           resource.xdvirc_geometry = NULL;
02308        */
02309        pageinfo_set_page_width(scanned_page + 1, w);
02310        pageinfo_set_window_width(scanned_page + 1, w);
02311        pageinfo_set_page_height(scanned_page + 1, h);
02312        pageinfo_set_window_height(scanned_page + 1, h);
02313     }
02314 }
02315 
02316 /*
02317  *     The following copyright message applies to the rest of this file.  --PV
02318  */
02319 
02320 /*
02321  *     This program is Copyright (C) 1987 by the Board of Trustees of the
02322  *     University of Illinois, and by the author Dirk Grunwald.
02323  *
02324  *     This program may be freely copied, as long as this copyright
02325  *     message remaines affixed. It may not be sold, although it may
02326  *     be distributed with other software which is sold. If the
02327  *     software is distributed, the source code must be made available.
02328  *
02329  *     No warranty, expressed or implied, is given with this software.
02330  *     It is presented in the hope that it will prove useful.
02331  *
02332  *     Hacked in ignorance and desperation by jonah@db.toronto.edu
02333  */
02334 
02335 /*
02336  *      The code to handle the \specials generated by tpic was modified
02337  *      by Dirk Grunwald using the code Tim Morgan at Univ. of Calif, Irvine
02338  *      wrote for TeXsun.
02339  */
02340 
02341 static char *
02342 endofcommand(char *cp)
02343 {
02344     while (isspace((int)*cp))
02345        ++cp;
02346     if (*cp != '=')
02347        return NULL;
02348     do
02349        ++cp;
02350     while (isspace((int)*cp));
02351     return cp;
02352 }
02353 
02354 #define       CMD(x, y)     ((x) << 8 | (y))
02355 
02356 void
02357 applicationDoSpecial(char *cp, size_t len)
02358 {
02359     char *p;
02360 
02361     if (globals.debug & DBG_DVI)
02362        printf("          `%s'\n", cp);
02363 
02364     while (isspace((int)*cp))
02365        ++cp;
02366 
02367     /* Ignore initial "xdvi:" */
02368     if (memcmp(cp, "xdvi:", 5) == 0) {
02369        cp += 5;
02370        while (isspace((int)*cp))
02371            ++cp;
02372     }
02373        
02374     /* PostScript specials */
02375 #ifdef MAGICK
02376     if (resource.useMAGICK) {
02377        if (Magick_parse_special(cp))
02378            return;
02379     }
02380 #endif
02381 
02382     if (*cp == '"') {
02383        quote_special(cp);
02384        return;
02385     }
02386     if (memicmp(cp, "ps:", 3) == 0) {
02387        cp += 3;
02388        psfig_special(cp);
02389        /* check for hdvips hyperlinks */
02390        if (memicmp(cp, "sdict begin ", strlen("sdict begin ")) == 0) {
02391            static Boolean warned_hypertex_too_old = False;
02392            char *match = NULL;
02393            if (warned_hypertex_too_old) /* don't continue evaluating links in this case */
02394               return;
02395            cp += strlen("sdict begin ");
02396            if (memcmp(cp, "H.S", 3) == 0
02397               || memcmp(cp, "H.R", 3) == 0
02398               || memcmp(cp, "H.B", 3) == 0
02399               /* following 2 conditions could be more restrictive: between `begin' and H.A/H.L,
02400                  there should be a single number (baselineskip in pt) */
02401               || (match = strstr(cp, "H.A")) != NULL
02402               || (match = strstr(cp, "H.L")) != NULL
02403               || (match = strstr(cp, "/Action")) != NULL
02404               || (match = strstr(cp, "/Link")) != NULL
02405               || (match = strstr(cp, "/View")) != NULL) {
02406               if (match != NULL)
02407                   htex_do_special(match, len - 3 - (match - cp) - strlen("sdict begin "));
02408               else
02409                   htex_do_special(cp, len - strlen("sdict begin "));
02410            }
02411            else if (!warned_hypertex_too_old && strstr(cp, "HyperStart") != NULL) {
02412               popup_message(globals.widgets.top_level,
02413                            MSG_WARN, NULL,
02414                            "This DVI was created with a too old version of the `dvips' hyperref driver - "
02415                            "disabling hyperlinks.\n"
02416                            "To fix this, you should either upgrade to a current version of hyperref "
02417                            "(see http://www.tug.org/applications/hyperref/), "
02418                            "or use the `hypertex' package option, like this:\n\\usepackage[hypertex]{hyperref}\n"
02419                            "(Be aware though that this option won't work for PS->PDF conversion!)");
02420               warned_hypertex_too_old = True;
02421            }
02422        }
02423        else {
02424            /* When not ignoring SDict entries, the distiller and pagecolor
02425               code in lshort.dvi from CTAN:info/lshort/russian/lshrtdvi.zip
02426               causes a failed assertion for 'color_bot_size > 0' in dvi-draw.c;
02427               there's something wrong with the parsing order/event handling here
02428               (see bug #856547).
02429               But we also don't want those entries to trigger erasepage_gs(), so
02430               it's correct to ignore them here.
02431            */
02432 #if PS_GS && GS_PIXMAP_CLEARING_HACK
02433            had_ps_specials = True;
02434 #endif
02435        }
02436        return;
02437     }
02438     if (memicmp(cp, "psfile", 6) == 0 && (p = endofcommand(cp + 6)) != NULL) {
02439        epsf_special(p);
02440 #if PS_GS && GS_PIXMAP_CLEARING_HACK
02441        had_ps_specials = True;
02442 #endif
02443        return;
02444     }
02445     if (memicmp(cp, "html:", 5) == 0) {
02446        htex_do_special(cp + 5, len - 5);
02447        return;
02448     }
02449 
02450 #if COLOR
02451     if (memicmp(cp, "color ", 6) == 0) {
02452 /*     fprintf(stderr, "------------- color special\n"); */
02453        if (resource.use_color)
02454            color_special(cp + 6);
02455        return;
02456     }
02457 #endif
02458     
02459     /* these should have been scanned */
02460     if (*cp == '!'
02461        || (memicmp(cp, "header", 6) == 0 && endofcommand(cp + 6) != NULL)) {
02462 #ifdef PS
02463        if (resource.postscript != 0 && scanned_page_reset >= 0) {
02464            /* turn on scanning and redraw the page */
02465            scanned_page = scanned_page_ps = scanned_page_reset = -1;
02466 # if COLOR
02467            scanned_page_color = scanned_page;
02468 # endif
02469            globals.ev.flags |= EV_NEWPAGE;              /* force a redraw */
02470            longjmp(globals.ev.canit, 1);
02471        }
02472 #endif /* PS */
02473        return;
02474     }
02475 
02476     if (memicmp(cp, "background ", 11) == 0) {
02477 #if COLOR
02478        if (resource.use_color && scanned_page_reset >= 0) {
02479            /* turn on scanning and redraw the page */
02480            scanned_page = scanned_page_color = scanned_page_reset = -1;
02481 # if PS
02482            scanned_page_ps = scanned_page;
02483 # endif
02484 /*         fprintf(stderr, "forcing redraw!\n"); */
02485            globals.ev.flags |= EV_NEWPAGE;              /* force a redraw */
02486            longjmp(globals.ev.canit, 1);
02487        }
02488 #endif /* COLOR */
02489        return;
02490     }
02491 
02492     if (memcmp(cp, "papersize", 9) == 0 && endofcommand(cp + 9) != NULL) {
02493        m_have_papersize_special = True;
02494        if (scanned_page_reset >= 0) {
02495            /* turn on scanning and redraw the page */
02496            scanned_page = scanned_page_reset = -1;
02497 #if PS
02498            scanned_page_ps = scanned_page;
02499 #endif
02500 #if COLOR
02501            scanned_page_color = scanned_page;
02502 #endif
02503            globals.ev.flags |= EV_NEWPAGE; /* force a redraw */
02504            longjmp(globals.ev.canit, 1);
02505        }
02506        return;
02507     }
02508 
02509     /* tpic specials */
02510 
02511     if (*cp >= 'a' && *cp <= 'z' && cp[1] >= 'a' && cp[1] <= 'z' &&
02512        (isspace((int)cp[2]) || cp[2] == '\0')) {
02513        switch (CMD(*cp, cp[1])) {
02514        case CMD('p', 'n'):
02515            set_pen_size(cp + 2);
02516            return;
02517        case CMD('f', 'p'):
02518            flush_path();
02519            return;
02520        case CMD('d', 'a'):
02521            flush_dashed(cp + 2, False);
02522            return;
02523        case CMD('d', 't'):
02524            flush_dashed(cp + 2, True);
02525            return;
02526        case CMD('p', 'a'):
02527            add_path(cp + 2);
02528            return;
02529        case CMD('a', 'r'):
02530            arc(cp + 2, False);
02531            return;
02532        case CMD('i', 'a'):
02533            arc(cp + 2, True);
02534            return;
02535        case CMD('s', 'p'):
02536            flush_spline();
02537            return;
02538        case CMD('s', 'h'):
02539            shade_last();
02540            return;
02541        case CMD('w', 'h'):
02542            whiten_last();
02543            return;
02544        case CMD('b', 'k'):
02545            blacken_last();
02546            return;
02547        case CMD('i', 'p'):  /* throw away the path -- jansteen */
02548            path_len = 0;
02549            return;
02550        }
02551     }
02552 
02553     if (memcmp(cp, "src:", 4) == 0) {
02554        have_src_specials = True;
02555     }
02556     else if (globals.warn_spec_now)
02557        XDVI_WARNING((stderr, "Special \"%s\" not implemented.", cp));
02558 }
02559 
02560 #undef CMD
02561 
02562 
02563 Boolean
02564 scan_special(char *cp, int cp_len, void *data)
02565 {
02566     char *p;
02567     Boolean dummy_ret = True; /* currently unused;
02568                              FIXME: use return value to avoid redundant calls (instead of longjmp()) */
02569     UNUSED(cp_len); /* TODO: could probably utilize this in call to htex_prescan_special() */
02570     ASSERT(data != NULL, "Must pass a data pointer when using HTEX");
02571     
02572     if (globals.debug & DBG_PS)
02573        printf("Scanning special `%s'.\n", cp);
02574 
02575     while (isspace((int)*cp))
02576        ++cp;
02577     
02578     /* Ignore initial "xdvi:" */
02579     if (memcmp(cp, "xdvi:", 5) == 0) {
02580        cp += 5;
02581        while (isspace((int)*cp))
02582            ++cp;
02583     }
02584 
02585     if (memicmp(cp, "ps:", 3) == 0) {
02586        Boolean found;
02587        cp += 3;
02588        /* check for hdvips hyperlinks */
02589        if (memicmp(cp, "sdict begin ", strlen("sdict begin ")) == 0) {
02590            static Boolean hypertex_too_old = False;
02591            char *match = NULL;
02592            cp += strlen("sdict begin ");
02593 
02594            if (strstr(cp, "HyperStart") != NULL)
02595               hypertex_too_old = True;
02596            if (hypertex_too_old)
02597               return False;
02598            
02599            if (memcmp(cp, "H.S", 3) == 0
02600               || memcmp(cp, "H.R", 3) == 0
02601               || memcmp(cp, "H.B", 3) == 0
02602               /* following 2 conditions could be more restrictive: between `begin' and H.A/H.L,
02603                  there should be a single number (baselineskip in pt) */
02604               || (match = strstr(cp, "H.A")) != NULL
02605               || (match = strstr(cp, "H.L")) != NULL
02606               || (match = strstr(cp, "/Action")) != NULL
02607               || (match = strstr(cp, "/Link")) != NULL
02608               || (match = strstr(cp, "/View")) != NULL) {
02609               if (match != NULL)
02610                   found = htex_prescan_special(match, cp_len, data);
02611               else
02612                   found = htex_prescan_special(cp, cp_len, data);
02613            }
02614        }
02615            
02616     }
02617     else if (memicmp(cp, "html:", strlen("html:")) == 0) {
02618        Boolean found;
02619        size_t offset = strlen("html:");
02620        found = htex_prescan_special(cp + offset, cp_len - offset, data);
02621        /* if searching for a specific string, return as soon as it's found - not yet implemented */
02622 #if 0
02623        if (my_data != NULL && my_data->scan_type == HTEX_ANCHOR_STRING && found) {
02624            return True;
02625        }
02626 #endif
02627     }
02628     /* do the following only if not searching for an anchor string */
02629     if (((struct htex_prescan_data *)data)->scan_type != HTEX_ANCHOR_STRING) {
02630 #if PS
02631 # if COLOR
02632        if (scanned_page_ps <= scanned_page)
02633 # endif
02634            {
02635               if (*cp == '!') {
02636                   scan_bang(cp);
02637                   return dummy_ret;
02638               }
02639               else if (memicmp(cp, "header", 6) == 0 && (p = endofcommand(cp + 6)) != NULL) {
02640                   scan_header(p);
02641                   return dummy_ret;
02642               }
02643            }
02644 #endif /* PS */
02645        
02646 #if COLOR
02647 # if PS
02648        if (scanned_page_color <= scanned_page)
02649 # endif
02650            {
02651               if (memicmp(cp, "background ", 11) == 0) {
02652                   scan_bg_color(cp);
02653                   return dummy_ret;
02654               }
02655               else if (memicmp(cp, "color ", 6) == 0) {
02656                   scan_color(cp);
02657                   return dummy_ret;
02658               }
02659            }
02660 #endif /* COLOR */
02661        if (memcmp(cp, "papersize", 9) == 0 && (p = endofcommand(cp + 9)) != NULL) {
02662            m_have_papersize_special = True;
02663            scan_papersize(p);
02664            return dummy_ret;
02665        }
02666     }
02667     return dummy_ret;
02668 }
02669 
02670 
02671 /* #define    xspell_conv(n)       spell_conv0(n, current_dimconv) */
02672 #define       xpixel_conv(x)       ((int) ((x) >> 16))
02673 #define       G_PXL_H              xpixel_conv(currinf.data.dvi_h)
02674 
02675 /* cp is not const, because of endofcommand(). */
02676 void
02677 geom_do_special(struct scan_info *info, char *cp, double current_dimconv)
02678 {
02679     const char *p;
02680     struct geom_info *g_info = (struct geom_info *)info->data;
02681     
02682     UNUSED(current_dimconv);
02683     
02684     while (isspace((int)*cp))
02685        ++cp;
02686 
02687     /* Ignore initial "xdvi:" */
02688     if (memcmp(cp, "xdvi:", 5) == 0) {
02689        cp += 5;
02690        while (isspace((int)*cp))
02691            ++cp;
02692     }
02693 
02694     if (memicmp(cp, "psfile", 6) == 0 && (p = endofcommand(cp + 6)) != NULL) {
02695        /* compute epsf bounding box */
02696        char c;
02697        int flags = 0;
02698        double keyval[6];
02699 
02700        c = *p;
02701        if (c == '\'' || c == '"') {
02702            do
02703               ++p;
02704            while (*p != '\0' && *p != c);
02705        }
02706        else
02707            while (*p != '\0' && !isspace((int)*p))
02708               ++p;
02709        while (isspace((int)*p))
02710            ++p;
02711        while (*p != '\0') {
02712            const char *p1 = p;
02713            size_t keyno;
02714 
02715            while (*p1 != '=' && !isspace((int)*p1) && *p1 != '\0')
02716               ++p1;
02717            for (keyno = 0; keyno < NKEYS; ++keyno) {
02718               if (memcmp(p, keytab[keyno], p1 - p) == 0) {
02719                   if (keyno >= N_ARGLESS_KEYS) {
02720                      while (isspace((int)*p1))
02721                          ++p1;
02722                      if (*p1 == '=') {
02723                          ++p1;
02724                          while (isspace((int)*p1))
02725                             ++p1;
02726                      }
02727                      if (keyno < N_ARGLESS_KEYS + 6) {
02728                          keyval[keyno - N_ARGLESS_KEYS] = atof(p1);
02729                          flags |= (1 << (keyno - N_ARGLESS_KEYS));
02730                      }
02731                      while (!isspace((int)*p1) && *p1 != '\0')
02732                          ++p1;
02733                   }
02734                   break;
02735               }
02736            }
02737            p = p1;
02738            while (!isspace((int)*p) && *p != '\0')
02739               ++p;
02740            while (isspace((int)*p))
02741               ++p;
02742        }
02743 
02744        if ((flags & 0x30) == 0x30 || ((flags & 0x30) && (flags & 0xf) == 0xf)) {
02745            long x = G_PXL_H;
02746            long y = PXL_V;
02747            long bbox_w;
02748            long bbox_h;
02749 
02750            bbox_w = 0.1 * ((flags & 0x10) ? KEY_RWI
02751                          : KEY_RHI * (KEY_URX - KEY_LLX) / (KEY_URY -
02752                                                         KEY_LLY)) *
02753               dimconv + 0.5;
02754            bbox_h =
02755               0.1 * ((flags & 0x20) ? KEY_RHI : KEY_RWI *
02756                      (KEY_URY - KEY_LLY) / (KEY_URX - KEY_LLX))
02757               * dimconv + 0.5;
02758 
02759            g_info->geom_box(info, x, y - bbox_h, x + bbox_w, y);
02760        }
02761     }
02762     else if (memicmp(cp, "ps::[begin]", 11) == 0) {
02763        /* compute psfig bounding box */
02764        long bbox_w, bbox_h;
02765 
02766        if (sscanf(cp + 11, "%ld %ld\n", &bbox_w, &bbox_h) >= 2) {
02767            long x = G_PXL_H;
02768            long y = PXL_V;
02769 
02770            bbox_w = xpixel_conv(spell_conv(bbox_w));
02771            bbox_h = xpixel_conv(spell_conv(bbox_h));
02772 
02773            g_info->geom_box(info, x, y, x + bbox_w, y + bbox_h);
02774        }
02775     }
02776 }