Back to index

tetex-bin  3.0
image-magick.c
Go to the documentation of this file.
00001 /*
00002   Imagemagick support for xdvik, written by Zhang Linbo <zlb@lsec.cc.ac.cn>
00003   
00004   Permission is hereby granted, free of charge, to any person obtaining a copy
00005   of this software and associated documentation files (the "Software"), to
00006   deal in the Software without restriction, including without limitation the
00007   rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
00008   sell copies of the Software, and to permit persons to whom the Software is
00009   furnished to do so, subject to the following conditions:
00010   
00011   The above copyright notice and this permission notice shall be included in
00012   all copies or substantial portions of the Software.
00013   
00014   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00015   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00016   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
00017   PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM,
00018   DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
00019   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
00020   OTHER DEALINGS IN THE SOFTWARE.
00021 */
00022 
00023 /*
00024   TODO:
00025   
00026   1. Specials other than 'psfile=' and 'em:graph' which include
00027      image files.
00028 
00029      [ NOTE SU: I consider this more or less done now with the backtick
00030        parsing ;-) ]
00031 
00032   2. Use an XtWorkProc or similar to do some background pre-processing
00033      of image files before they are displayed.
00034 
00035   3. Replace popen() by fork and explicit pipes, and make the
00036      use of xdvi_temp_fd safer.
00037      
00038   4. PS figs are always clipped to the bbox defined in the special
00039      string, which is not what we want when clip=false.
00040      The problem can be partly solved by increasing the value of
00041      the PSBW macro, or we may get the correct bbox by reading the
00042      '%%BoundingBox' or '%%HiresBoundingBox' lines in the PS file
00043      and set the image geometry accordingly.
00044   
00045   5. The code only(?) works with true color displays.
00046   
00047   6. There's also work to do to respond to window events in 
00048      load_image() and display_image() functions.
00049 */
00050 #include <ctype.h>
00051 
00052 #include "xdvi-config.h"
00053 #include "xdvi.h"
00054 #include "dvi-init.h"
00055 #include "events.h"
00056 #include "dvi-draw.h"
00057 #include "util.h"
00058 #include "special.h"
00059 #include "image-magick.h"
00060 
00061 #ifdef MAGICK /* entire file */
00062 
00063 #include "magick/api.h"
00064 #if MAGICK_VER_MAJOR > 5 || MAGICK_VER_MINOR >= 4
00065 #  include "magick/xwindow.h"      /* IM >= 5.4 */
00066 #else
00067 #  include "magick/xwindows.h"     /* IM <= 5.3 */
00068 #endif
00069 
00070 #if MAGICK_VER_MAJOR > 5 || MAGICK_VER_MINOR >= 4       /* >= 5.4 */
00071 #  define CATCH_EXCEPTION(e) do { \
00072        if (e.severity != UndefinedException) CatchException(&e); } while (0)
00073 #elif MAGICK_VER_MINOR >= 3 /* 5.3 */
00074 #  define CATCH_EXCEPTION(e) if (image == NULL) \
00075        MagickWarning(e.severity, e.reason, e.description);
00076 #else /* <= 5.2 (no check) */
00077 #  define CATCH_EXCEPTION(e) if (image == NULL) \
00078        MagickWarning(e.severity, e.message, e.qualifier);
00079 #endif
00080 
00081 #if 0
00082 #define CATCH_EXCEPTION(e)  /* do nothing */
00083 #endif
00084 
00085 #ifndef False
00086 #  define False 0
00087 #endif
00088 
00089 #ifndef True
00090 #  define True 1
00091 #endif
00092 
00093 #define TWOPI (2*3.141592653589793)
00094 
00095 static double ROUND_tmp;
00096 #define ROUND(x) ( (ROUND_tmp=(x)) >= 0 ? (long)(ROUND_tmp + 0.5) : \
00097                                    - (long)(- ROUND_tmp + 0.5) )
00098        
00099 static void
00100 showtime(const char *msg)
00101 {
00102     if (globals.debug & DBG_PS) {
00103        static double time0 = -1;
00104        double time;
00105        struct timeval tv;
00106 
00107        gettimeofday(&tv, (struct timezone *) 0);
00108        time = tv.tv_sec + (double) tv.tv_usec * 1e-6;
00109        if (time0 < 0)
00110            time0 = time;
00111        fprintf(stderr, "        %-40s %0.4f\n", msg, time - time0);
00112     }
00113 }
00114 
00115 #define DPI ( resource.pixels_per_inch*0.001*magnification/mane.shrinkfactor )
00116 
00117 typedef struct XDviImageInfo {
00118     Boolean clip;
00119     int rwi, rhi, angle;
00120     double llx, lly, urx, ury;
00121     char filename[MaxTextExtent];
00122 } XDviImageInfo;
00123 
00124 typedef struct {
00125     XDviImageInfo info;
00126     XImage *ximage;  /* ximage */
00127     unsigned long bgpixel;  /* background pixel */
00128     int xoff, yoff;  /* offsets in pixels */
00129 } XDviImage;
00130 
00131 static size_t cache_size = 0;
00132 /* default value for cache_limit (16MB), it can be specified with the
00133  * "-magick_cache size" option */
00134 static size_t cache_limit = 16 * 1024 * 1024;
00135 static int ncached = 0;
00136 static XDviImage *cache = NULL;
00137 
00138 static char *magick_tmp;
00139 static double current_dpi = -1;
00140 
00141 /* Input image types */
00142 enum {
00143     TYPE_UNKNOWN,    /* unknown */
00144     TYPE_CONVERT,    /* `convert ... */
00145 };
00146 static int image_type;      /* type of current input image */
00147 
00148 static void
00149 cleanup(void)
00150 {
00151     if (magick_tmp != NULL)
00152        unlink(magick_tmp);
00153 }
00154 
00155 static void
00156 free_ximage(XImage * ximage)
00157 {
00158     if (ximage == NULL)
00159        return;
00160     if (ximage->data != NULL) {
00161        free(ximage->data);
00162        ximage->data = NULL;
00163     }
00164     XDestroyImage(ximage);
00165 }
00166 
00167 static void
00168 clear_cache()
00169 {
00170     XDviImage *c;
00171     if (!ncached)
00172        return;
00173     for (c = cache; c < cache + ncached; c++)
00174        free_ximage(c->ximage);
00175     free(cache);
00176     ncached = 0;
00177     cache = NULL;
00178     cache_size = 0;
00179 }
00180 
00181 static void
00182 parse_backtick(char *fn)
00183 {
00184     char *p, *q, *r, c;
00185 
00186     if (fn[0] != '`')
00187        return;
00188 
00189     if (globals.debug & DBG_PS)
00190        fprintf(stderr, "Magick: parsing backtick command |%s|\n", fn);
00191 
00192 #ifdef __GNUC__
00193 #warning FIXME: add code to execute the cmd then load the output file, \
00194               if '-allowshell' is effective and the user requests it \
00195               somehow (slow but reliable).
00196 #endif
00197 
00198     fn[0] = '\0';
00199     p = fn + 1;
00200     /* Skip 'env' and 'VAR=value' */
00201     do {
00202        while (isspace((int)*p))
00203            ++p;
00204        q = p;
00205        while (*q != '\0' && !isspace((int)*q))
00206            ++q;
00207        if (*q == '\0' || q <= p)
00208            return;
00209        if (((r = strchr(p, '=')) == NULL || r >= q) &&
00210            (!isspace((int)*q) || strncmp(p, "env", 3)))
00211            break;
00212        p = q;
00213     } while (1);
00214 
00215     /* Now [p ... q-1] is the command name */
00216     if (q - p == 7 && !memcmp(p, "convert", 7)) 
00217        image_type = TYPE_CONVERT;
00218     else
00219        image_type = TYPE_UNKNOWN;
00220     p = q;
00221     do {
00222        while (isspace((int)*p))
00223            ++p;
00224        q = p;
00225        if (*q == '`' || *q == '\'' || *q == '"') {
00226            c = *p;
00227            ++q;
00228            while (*q != '\0' && *q != c)
00229               q++;
00230            if (*q != '\0')
00231               q++;
00232        }
00233        else {
00234            while (*q != '\0' && !isspace((int)*q))
00235               q++;
00236        }
00237 #ifdef __GNUC__
00238 #warning FIXME: check for some cmdline options
00239        break;
00240 #endif
00241     } while (1);
00242     *q = '\0';
00243 
00244     if (globals.debug & DBG_PS)
00245        fprintf(stderr, "Magick: filename=|%s|\n", p);
00246 
00247     /* check for the case `kpsewhich -n ... #1` */
00248     if (*p == '`') {
00249 #if 0
00250        /* WARNING: unsafe! */
00251        FILE *pipe;
00252 
00253        if (globals.debug & DBG_PS)
00254            fprintf(stderr, "Magick: executing %s\n", p);
00255        if (*(--q) == '`')
00256            *q = '\0';
00257        if ((pipe = popen(p + 1, "r")) == NULL)
00258            return;
00259        fscanf(pipe, "%s", fn);
00260        pclose(pipe);
00261        return;
00262 #else
00263        if (memcmp(++p, "kpsewhich", sizeof("kpsewhich") - 1))
00264            return;
00265 #ifdef __GNUC__
00266 #warning FIXME: better make KPSE library call here.
00267 #endif
00268        if (*(--q) == '`')
00269            --q;
00270        while (q >= p && isspace((int)*q))
00271            --q;
00272        if (q < p)
00273            return;
00274        *(q + 1) = '\0';
00275        while (q >= p && !isspace((int)*q))
00276            --q;
00277        p = q + 1;
00278 #endif
00279     }
00280 
00281     strcpy(fn, p);
00282 }
00283 
00284 static Boolean
00285 imagick_eps_to_jpeg(const char *fullpath,
00286                   unsigned long *columns, unsigned long *rows,
00287                   XDviImage *img, PixelPacket *bgcolor,
00288                   double xdpi, double ydpi)
00289 {
00290     size_t size;
00291 #ifdef __GNUC__
00292 #warning FIXME: security (check 'allowshell'?), backticks, pathname canonicalization,
00293     /* note SU: better use fork()/exec() here, popen() is too unsafe. */
00294 #endif
00295     static char *cmdline = NULL;
00296     static size_t cmdline_size = 0;
00297     static char *fmt = "%s -q -dBATCH -dMaxBitmap=300000000 "
00298        "-dNOPAUSE -dNOSAFER -sDEVICE=jpeg "
00299        "-dTextAlphaBits=4 -dGraphicsAlphaBits=4 "
00300        "-g%ldx%ld -r%0.4fx%0.4f -sOutputFile=%s - -c quit";
00301     static const char strsafe[] =
00302        "{ << /PermitFileReading [ (*) ] /PermitFileWriting [ ] /PermitFileControl [ ] "
00303        ">> setuserparams .locksafe "
00304        "} stopped pop\n";
00305     FILE *pipe;
00306 
00307     showtime("PS/EPS -> JPEG:");
00308     /* Add a border to the drawing box (PSBW=border width in bp) */
00309 #define PSBW 1
00310     *columns += ROUND(2 * PSBW / 72.0 * xdpi);
00311     *rows += ROUND(2 * PSBW / 72.0 * ydpi);
00312     img->xoff = ROUND(PSBW / 72.0 * xdpi);
00313     img->yoff = -ROUND(PSBW / 72.0 * ydpi);
00314 
00315     size = strlen(fmt) + 25 * 2 + 10 * 2 + strlen(magick_tmp) + strlen(resource.gs_path) + 1;
00316     if (size > cmdline_size) {
00317        cmdline_size = size;
00318        cmdline = xrealloc(cmdline, cmdline_size);
00319     }
00320     unlink(magick_tmp);
00321     sprintf(cmdline, fmt, resource.gs_path, *columns, *rows, xdpi, ydpi,
00322            magick_tmp);
00323     if ((pipe = popen(cmdline, "w")) == NULL) {
00324        fprintf(stderr, "%s: Cannot execute %s, ignoring image %s.\n",
00325               globals.program_name, resource.gs_path, fullpath);
00326        return False;
00327     }
00328 
00329 #define ColorToPixelPacket(c)      ((c*((1L<<(QuantumDepth))-1L)+32768L)/65535L)
00330 #define UnifiedColorValue(c)       ((c)/(double)((1L<<(QuantumDepth))-1L))
00331 #ifdef __GNUC__
00332 #warning FIXME: how to control roundoff error which may produce a \
00333         different backgroung pixel in the output image?
00334 #endif
00335 #if COLOR
00336     bgcolor->red = ColorToPixelPacket(bg_current->color.r);
00337     bgcolor->green = ColorToPixelPacket(bg_current->color.g);
00338     bgcolor->blue = ColorToPixelPacket(bg_current->color.b);
00339 #else
00340     bgcolor->red = bgcolor->green = bgcolor->blue = ColorToPixelPacket(65535);
00341 #endif
00342     bgcolor->opacity = (1L << QuantumDepth) - 1L;
00343 
00344 #ifdef __GNUC__
00345 #warning FIXME: how to set bg color within gs (instead of filling the bbox) ?
00346 #endif
00347     if (resource.gs_safer) {
00348        fprintf(pipe, strsafe);
00349     }
00350     /* fill PS paper with background color */
00351     fprintf(pipe,
00352            "newpath -5 -5 moveto %f 0 rlineto "
00353            "0 %f rlineto %f 0 rlineto 0 %f rlineto "
00354            "closepath %f %f %f setrgbcolor fill\n",
00355            img->info.urx - img->info.llx + 2 * (PSBW + 5),
00356            img->info.ury - img->info.lly + 2 * (PSBW + 5),
00357            -(img->info.urx - img->info.llx + 2 * (PSBW + 5)),
00358            -(img->info.ury - img->info.lly + 2 * (PSBW + 5)),
00359            UnifiedColorValue(bgcolor->red),
00360            UnifiedColorValue(bgcolor->green),
00361            UnifiedColorValue(bgcolor->blue));
00362 
00363     /* shift PS coordinates */
00364     fprintf(pipe, "%f %f translate\n", -img->info.llx + PSBW, -img->info.lly + PSBW);
00365     if (globals.debug & DBG_PS)
00366        fprintf(stderr, "    clip=%s\n", img->info.clip ? "yes" : "no");
00367     if (img->info.clip) {
00368        fprintf(pipe, "newpath %f %f moveto %f 0 rlineto 0 %f rlineto "
00369               "%f 0 rlineto 0 %f rlineto closepath clip\n",
00370               img->info.llx, img->info.lly, img->info.urx - img->info.llx,
00371               img->info.ury - img->info.lly, -(img->info.urx - img->info.llx),
00372               -(img->info.ury - img->info.lly));
00373     }
00374     fprintf(pipe, "%f %f moveto\n", img->info.llx, img->info.lly);
00375     fprintf(pipe, "(%s) run showpage\n", fullpath);
00376     pclose(pipe);
00377     return True;
00378 }
00379 
00380 
00381 static Image *
00382 rotate_image(XDviImage *img, Image *image, ExceptionInfo exception)
00383 {
00384     Image *tmp_image;
00385     unsigned long w = image->columns, h = image->rows;
00386     double sin_a = sin(img->info.angle * (TWOPI / 360));
00387     double cos_a = cos(img->info.angle * (TWOPI / 360));
00388 
00389     if (globals.debug & DBG_PS)
00390        fprintf(stderr, "\t    rotating image by %d degrees\n",
00391               img->info.angle);
00392 
00393     /* adjust xoff, yoff, assuming (0,0) the origin of rotation */
00394     if (img->xoff || img->yoff) {
00395        double a, b;
00396        a = img->xoff * cos_a + img->yoff * sin_a;
00397        b = -img->xoff * sin_a + img->yoff * cos_a;
00398        img->xoff = ROUND(a);
00399        img->yoff = ROUND(b);
00400     }
00401        
00402     showtime("RotateImage:");
00403     tmp_image = RotateImage(image, -(double)img->info.angle, &exception);
00404     DestroyImage(image);
00405     image = tmp_image;
00406     CATCH_EXCEPTION(exception);
00407     if (image == NULL)
00408        return NULL;
00409     /* 
00410      *  _______________________________________
00411      *   |               *                |
00412      *   |             *   *                     |
00413      *   |           *      *             |
00414      *   |          *                *           |
00415      *  |         *             *         |
00416      *  |       *                 *              |
00417      *  |     *                    *      |
00418      *  |    *                              *    |
00419      *  |  *                           *  |
00420      *  |*                               *|
00421      *  |  *                           *  |
00422      *  |    *                              *    |
00423      *  |     *                    *      |
00424      *  |       *                 *              |
00425      *  |         *             *         |
00426      *   |          *                *           |
00427      *   |           *      *             |
00428      *   |             *   *                     |
00429      *   |               O (origin)              |
00430      *  O--------------------------------------
00431      * (lower-left corner of image box after rotation)
00432      */
00433     if (img->info.angle <= 90) {
00434        img->xoff += ROUND(h * sin_a);
00435     } 
00436     else if (img->info.angle <= 180) {
00437        img->xoff += ROUND(h * sin_a - w * cos_a);
00438        img->yoff += ROUND(h * cos_a);
00439     }
00440     else if (img->info.angle <= 270) {
00441        img->xoff -= ROUND(w * cos_a);
00442        img->yoff += ROUND(w * sin_a + h * cos_a);
00443     }
00444     else {
00445        img->yoff += ROUND(w * sin_a);
00446     }
00447     return image;
00448 }
00449 
00450 static Image *
00451 crop_shift_image(XDviImage *img, Image *image,
00452                unsigned long *columns, unsigned long *rows,
00453                double xscale, double yscale,
00454                ExceptionInfo exception)
00455 {
00456     /* ZLB: the code below works for the case where the BoundingBox
00457      * of the PS figure generated by the shell command is (0 0 w h), 
00458      * where w and h are the pixel width and height of the input image.
00459      * (this is true for the program 'convert' without options).
00460      * The image should then be cropped or/and shifted depending
00461      * on the value of 'clip' */
00462     int w = image->columns, h = image->rows;
00463     
00464     if (globals.debug & DBG_PS)
00465        fprintf(stderr, "Magick: type of input image = CONVERT\n");
00466     
00467     if (!img->info.clip) {
00468        /* We only need to shift the image */
00469        img->xoff = +ROUND(xscale * DPI * img->info.llx / 72.0);
00470        img->yoff = -ROUND(yscale * DPI * img->info.lly / 72.0);
00471        *columns = ROUND(xscale * DPI * w / 72.0);
00472        *rows = ROUND(yscale * DPI * h / 72.0);
00473     }
00474     else {
00475        int llx = ROUND(img->info.llx);
00476        int lly = ROUND(img->info.lly);
00477        int urx = ROUND(img->info.urx);
00478        int ury = ROUND(img->info.ury);
00479        
00480        if (llx < 0) {
00481            img->xoff = +ROUND(xscale * DPI * llx / 72.0);
00482            llx = 0;
00483        }
00484        if (lly < 0) {
00485            img->yoff = -ROUND(yscale * DPI * lly / 72.0);
00486            lly = 0;
00487        }
00488        if (urx > w)
00489            urx = w;
00490        if (ury > h)
00491            ury = h;
00492 
00493        if ((urx - llx) < 1 || (urx - llx) < 1) {
00494            DestroyImage(image);
00495            return NULL;
00496        }
00497 
00498        if (llx != 0 || lly != 0 || urx != w || ury != h) {
00499            /* Crop the image */
00500            Image *tmp_image;
00501            RectangleInfo geo;
00502            geo.width = urx - llx;
00503            geo.height = ury - lly;
00504            geo.x = llx;
00505            geo.y = h - ury;
00506            if (globals.debug & DBG_PS)
00507               fprintf(stderr, "\t    cropping image (%d %d %d %d)\n",
00508                      llx, lly, urx, ury);
00509            showtime("CropImage:");
00510            tmp_image = CropImage(image, &geo, &exception);
00511            DestroyImage(image);
00512            image = tmp_image;
00513            CATCH_EXCEPTION(exception);
00514            if (image == NULL)
00515               return NULL;  /* error cropping image */
00516        }
00517        *columns = ROUND(xscale * DPI * (urx - llx) / 72.0);
00518        *rows = ROUND(yscale * DPI * (ury - lly) / 72.0);
00519     }
00520     return image;
00521 }
00522 
00523 
00524 /* parse and return the `magick_cache' resource size_str */
00525 static long
00526 parse_cache_setting(const char *size_str)
00527 {
00528     char *p;
00529     long size = strtol(size_str, &p, 0);
00530     switch (*p) {
00531     case 'g':
00532     case 'G':
00533        size *= 1024;
00534        /* fall through */
00535     case 'm':
00536     case 'M':
00537        size *= 1024;
00538        /* fall through */
00539     case 'k':
00540     case 'K':
00541        size *= 1024;
00542        break;
00543     default:
00544        if (*p != '\0') {
00545            fprintf(stderr, "Magick: invalid suffix (%c) in "
00546                   "magick_cache option\n", *p);
00547        }
00548     }
00549     return size;
00550 }
00551 
00552 static XDviImage *
00553 load_image(XDviImageInfo *info)
00554 {
00555     static ExceptionInfo exception;
00556     static ImageInfo *image_info;
00557     static XResourceInfo resource_info;
00558     static XVisualInfo visual_info;
00559     static XStandardColormap map_info;
00560     static XPixelInfo pixel_info;
00561     static XWindowInfo window_info;
00562     static Boolean initialized = False;
00563     static Boolean disabled = False;
00564 
00565     static XDviImage img = { {False, 0, 0, 0, 0, 0, 0, 0, ""}, NULL, 0, 0, 0 };
00566 
00567     Image *image, *tmp_image;
00568     double xscale, yscale, xdpi, ydpi;
00569     int n;
00570     unsigned long columns, rows;
00571     char density[40];
00572     size_t size;
00573     char *fn = info->filename;
00574     XDviImage *c;
00575     PixelPacket bgcolor;
00576 
00577     struct stat statbuf;
00578     char *path, *fullpath;
00579     char canonical_path[MAXPATHLEN + 1];
00580     
00581     if (disabled)
00582        return NULL;
00583 
00584     parse_backtick(info->filename);
00585     if (info->filename[0] == '\0') {
00586        if (globals.debug & DBG_PS)
00587            fprintf(stderr, "Magick: skipping malformed backtick command.\n");
00588        return NULL;
00589     }
00590     else {
00591        if (globals.debug & DBG_PS)
00592            fprintf(stderr, "Magick: filename=|%s|\n", info->filename);
00593     }
00594 
00595     img.info = *info;
00596     if (globals.debug & DBG_PS)
00597        fprintf(stderr, "Magick: load_image: fn=%s\n", fn);
00598 
00599     /* check for changes in mag/shrink/resolution */
00600     if (fabs(current_dpi - DPI) > 1e-4) {
00601        current_dpi = DPI;
00602        clear_cache();
00603     }
00604 
00605     /* look it up in the cache */
00606     if (ncached) {
00607        for (c = cache; c < cache + ncached; c++) {
00608            if (memcmp(&c->info, info, sizeof *info))
00609               continue;
00610            if (globals.debug & DBG_PS)
00611               fprintf(stderr, "    Image found in cache.\n");
00612            return c;
00613        }
00614     }
00615 
00616     if (!initialized) {
00617        magick_tmp = NULL;
00618        if ((n = xdvi_temp_fd(&magick_tmp)) == -1) {
00619            fprintf(stderr, "Magick: cannot create tmp filename, disabled.\n");
00620            disabled = True;
00621            return NULL;
00622        }
00623        /* FIXME: mustn't close filedescriptor returned by xdvi_temp_fd(),
00624           else we have a race condition again; see e.g.
00625           http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/avoid-race.html
00626        */
00627        close(n);
00628        unlink(magick_tmp);
00629        atexit(cleanup);     /* remove magick_tmp at exit. */
00630        
00631        showtime("Initializing load_image:");
00632        InitializeMagick("xdvi.bin");
00633        GetExceptionInfo(&exception);
00634        image_info = CloneImageInfo((ImageInfo *) NULL);
00635        
00636        XGetResourceInfo(XrmGetStringDatabase(""), "xdvi", &resource_info);
00637        /* resource_info.debug = 1; */
00638        if (!XMatchVisualInfo(DISP, XScreenNumberOfScreen(SCRN),
00639                            G_depth, G_visual->class, &visual_info)) {
00640            fprintf(stderr, "Magick: can't get visual info, image disabled.\n");
00641            disabled = True;
00642            return NULL;
00643        }
00644        XGetMapInfo(&visual_info, G_colormap, &map_info);
00645        XGetWindowInfo(DISP, &visual_info, &map_info, &pixel_info,
00646                      (XFontStruct *) NULL, &resource_info, &window_info);
00647 
00648        if (resource.magick_cache != NULL) {
00649            cache_limit = parse_cache_setting(resource.magick_cache);
00650            if (globals.debug & DBG_PS)
00651               fprintf(stderr, "    Setting magick_cache to %lu\n",
00652                      (unsigned long)cache_limit);
00653        }
00654        initialized = True;
00655     }
00656 
00657     xscale = info->rwi / (double) ((int) (10 * (info->urx - info->llx) + .5));
00658     yscale = info->rhi / (double) ((int) (10 * (info->ury - info->lly) + .5));
00659     xdpi = xscale * DPI;
00660     ydpi = yscale * DPI;
00661     /* Compute scaled size of the image */
00662     columns = ROUND(DPI * info->rwi * 0.1 / 72);
00663     rows = ROUND(DPI * info->rhi * 0.1 / 72);
00664     if (globals.debug & DBG_PS)
00665        fprintf(stderr, "\tscale=%0.2fx%0.2f, dpi=%0.2fx%0.2f, size=%ldx%ld\n",
00666               xscale, yscale, xdpi, ydpi, columns, rows);
00667 
00668     /* expand and canonicalize path name */
00669     path = find_file(fn, &statbuf, kpse_pict_format);
00670     if (path != NULL) {
00671        fullpath = REALPATH(path, canonical_path);
00672        if (fullpath == NULL) {
00673            XDVI_WARNING((stderr, "Couldn't canonicalize %s to full path - returning unexpanded.",
00674                        path));
00675            fullpath = path;
00676        }
00677        else {
00678            free(path);
00679        }
00680     }
00681     else {
00682        fprintf(stderr, "%s: %s:%d: Can't find image file %s!\n",
00683               globals.program_name, __FILE__, __LINE__, fn);
00684        return NULL;
00685     }
00686     
00687     /* If the file is PS/EPS transform it to JPEG first (faster) */
00688     n = strlen(fullpath);
00689     if ((n >= 3 && !memicmp(fullpath + n - 3, ".ps", 3)) ||
00690        (n >= 4 && !memicmp(fullpath + n - 4, ".eps", 4))) {
00691        if (!imagick_eps_to_jpeg(fullpath, &columns, &rows, &img, &bgcolor, xdpi, ydpi)) {
00692            return NULL;
00693        }
00694        /* Should we append the 'JPEG:' prefix to the filename to help
00695         * ImageMagick to identify the file format? */
00696        fullpath = magick_tmp;
00697     }
00698     else {
00699        img.xoff = img.yoff = 0;
00700     }
00701 
00702     strcpy(image_info->filename, fullpath);
00703     showtime("ReadImage:");
00704     if (fullpath == magick_tmp) {
00705        /* seems 'density' only affects image size of PS/EPS files */
00706        sprintf(density, "%0.2fx%0.2f", xdpi, ydpi);
00707        image_info->density = density;
00708        image_info->units = PixelsPerInchResolution;
00709     }
00710     image = ReadImage(image_info, &exception);
00711     CATCH_EXCEPTION(exception);
00712     unlink(magick_tmp);
00713     if (image == NULL)      /* error loading image */
00714        return NULL;
00715 
00716     /* Set background color for PS/EPS fig */
00717     if (fullpath == magick_tmp)    /* input is PS */
00718        image->background_color = bgcolor;
00719 
00720     if (globals.debug & DBG_PS) {
00721        int i = image->units;
00722        fprintf(stderr, "\t    image size = %ldx%ld\n", 
00723                      image->columns, image->rows);
00724        fprintf(stderr, "\t    image resolution = %0.2fx%0.2f (units=%s)\n",
00725               image->x_resolution, image->y_resolution,
00726               i == UndefinedResolution ? "???" :
00727               i == PixelsPerInchResolution ? "PPI" : "PPCM");
00728     }
00729 
00730     if (fullpath != magick_tmp) {  /* non PS image */
00731        /* Note: (info->llx info->lly info->urx info->ury) should be
00732         * regarded as the viewport */
00733        if (image_type == TYPE_CONVERT) {
00734            image = crop_shift_image(&img, image, &columns, &rows, xscale, yscale, exception);
00735        }
00736        else {
00737            /* Do nothing. The image will be scaled to the size of the bbox.
00738             * ZLB: the output will be identical to dvips iff the bbox of
00739             * the PS figure generated by the shell command is equal to
00740             * (info->llx info->lly info->urx info->ury).
00741             * (this is true if the bbox set via 'bb', 'viewport', etc.,
00742             * is equal to the real bbox of the PS figure) */
00743        }
00744 
00745        if (columns != image->columns || rows != image->rows) {
00746            /* Scale image */
00747            if (globals.debug & DBG_PS) {
00748               fprintf(stderr, "\t    scaling image %ldx%ld -> %ldx%ld\n",
00749                      image->columns, image->rows, columns, rows);
00750            }
00751            showtime("ScaleImage:");
00752            tmp_image = ScaleImage(image, columns, rows, &exception);
00753            DestroyImage(image);
00754            image = tmp_image;
00755            CATCH_EXCEPTION(exception);
00756            if (image == NULL)
00757               return NULL;  /* error resizing image */
00758        }
00759     }
00760 
00761     /* rotate image */
00762     if (info->angle) {
00763        image = rotate_image(&img, image, exception);
00764     }
00765 
00766 #if 0
00767     DescribeImage(image, stdout, 1 /* verbosity */ );
00768     DisplayImages(image_info, image);
00769 #endif
00770 
00771 #define XStandardPixel(map,color,dx)  (unsigned long) (map.base_pixel + \
00772   (((color).red   * map.red_max   + (1L << (dx - 1L))) / ((1L << dx) - 1L)) * map.red_mult + \
00773   (((color).green * map.green_max + (1L << (dx - 1L))) / ((1L << dx) - 1L)) * map.green_mult + \
00774   (((color).blue  * map.blue_max  + (1L << (dx - 1L))) / ((1L << dx) - 1L)) * map.blue_mult)
00775 
00776     img.bgpixel = XStandardPixel(map_info, image->background_color, 16);
00777     if (img.ximage != NULL) {
00778        free_ximage(img.ximage);
00779        img.ximage = NULL;
00780     }
00781 #undef XStandardPixel
00782 
00783     /* Transform to XImage */
00784     window_info.id = mane.win;
00785     window_info.annotate_context = globals.gc.copy;
00786     /*
00787       FIXME: If this isn't set to false, running on a remote display may
00788       lead to `Badaccess (no permission to access private resource)' X
00789       errors (only with ImageMagick >= 5.4 - zlb has emailed the
00790       developers about this).
00791       Setting it to false always might slightly hurt performance - how
00792       to check for remoteness? Parse the DISPLAY variable?
00793     */
00794     window_info.shared_memory = False;
00795     showtime("XMakeImage:");
00796     XGetPixelPacket(DISP, &visual_info, &map_info,
00797                   &resource_info, image, &pixel_info);
00798     n = XMakeImage(DISP, &resource_info, &window_info, image,
00799                  image->columns, image->rows);
00800 
00801     /* Since XDestroyImage doesn't know how to free window_info.ximage->data,
00802      * we simply copy window_info.ximage. The next call of XMakeImage
00803      * will free both window_info.ximage->data and window_info.ximage.
00804      */
00805     DestroyImage(image);
00806     if (!n)
00807        return NULL;
00808     img.ximage = XCreateImage(DISP, G_visual, G_depth, ZPixmap, 0, NULL,
00809                            window_info.ximage->width,
00810                            window_info.ximage->height, BMBITS, 0);
00811     if (img.ximage == NULL)
00812        return NULL;
00813     *img.ximage = *(window_info.ximage);
00814     size = img.ximage->bytes_per_line * img.ximage->height;
00815     img.ximage->data = malloc(size ? size : 1); /* NOTE: no xmalloc! */
00816     if (img.ximage->data == NULL) {
00817        fprintf(stderr, "Magick: cannot allocate memory for ximage data.\n");
00818        XDestroyImage(img.ximage);
00819        img.ximage = NULL;
00820        return NULL;
00821     }
00822     if (size)
00823        memcpy(img.ximage->data, window_info.ximage->data, size);
00824 
00825     size += sizeof(XDviImage) + sizeof(XImage);
00826     if (cache_size + size > cache_limit) {
00827        if (globals.debug & DBG_PS)
00828            fprintf(stderr, "    image not cached (cache limit).\n");
00829        return &img;
00830     }
00831 
00832     c = realloc(cache, sizeof(XDviImage) * (ncached + 1)); /* NOTE: no xrealloc! */
00833     if (c == NULL) {
00834        if (globals.debug & DBG_PS)
00835            fprintf(stderr, "    image not cached (cache alloc).\n");
00836        return &img;
00837     }
00838     cache_size += size;
00839     cache = c;
00840     c = cache + (ncached++);
00841     *c = img;
00842     img.ximage = NULL;      /* so next call won't delete this ximage */
00843 
00844     if (globals.debug & DBG_PS)
00845        fprintf(stderr, "    image cached (cache_size=%uKB, limit=%uKB).\n",
00846               cache_size / 1024, cache_limit / 1024);
00847 #if 0
00848     DestroyImageInfo(image_info);
00849     DestroyExceptionInfo(&exception);
00850     DestroyMagick();
00851 #endif
00852     
00853     return c;
00854 }
00855 
00856 static void
00857 display_image(XDviImage * img, int xul, int yul,
00858              const struct window_expose_info *expose,
00859              const GC gc)
00860 {
00861     if (globals.debug & DBG_PS)
00862        fprintf(stderr, "    display_image: pos=%d, %d\n", xul, yul);
00863 
00864     xul -= img->xoff;
00865     yul -= img->yoff;
00866 
00867 #if 0
00868     XPutImage(DISP, mane.win, globals.gc.copy, img->ximage, 0, 0,
00869              xul, yul - img->ximage.height + 1,
00870              img->ximage.width, img->ximage.height);
00871 #else
00872     /* Try to make background pixels transparent */
00873     {
00874        XImage *scr;
00875        int x0 = xul;
00876        int y0 = yul - img->ximage->height + 1;
00877        int w = img->ximage->width;
00878        int h = img->ximage->height;
00879        int xoff = 0;
00880        int yoff = 0;
00881        int x, y;
00882 
00883        if (x0 + w <= expose->min_x || x0 >= expose->max_x ||
00884            y0 + h <= expose->min_y || y0 >= expose->max_y)
00885            return;
00886        if (x0 < expose->min_x) {
00887            xoff = expose->min_x - x0;
00888            x0 = expose->min_x;
00889            w -= xoff;
00890        }
00891        if (x0 + w > expose->max_x)
00892            w = expose->max_x - x0 + 0;
00893        if (y0 < expose->min_y) {
00894            yoff = expose->min_y - y0;
00895            y0 = expose->min_y;
00896            h -= yoff;
00897        }
00898        if (y0 + h > expose->max_y)
00899            h = expose->max_y - y0 + 0;
00900 
00901        {
00902            /* Need to double check the bounds here, because redraw_page() in
00903             * events.c may set larger bounding box (expose->min_x, expose->min_y, etc.)
00904             * than actual viewport causing 'BadMatch' error in XGetImage().
00905             *
00906             * Here we clip the rectangle w*h+x0+y0 with the root window. */
00907            XWindowAttributes a;
00908            Status status;
00909            Window child;
00910 
00911            status = XGetWindowAttributes(DISP, mane.win, &a);
00912            if (!status || a.map_state != IsViewable)
00913               return;
00914            XTranslateCoordinates(DISP, mane.win, a.root, x0, y0, &x, &y,
00915                               &child);
00916            XGetWindowAttributes(DISP, a.root, &a);
00917            if (x + w <= a.x || x >= a.x + a.width || y + h <= a.y
00918               || y >= a.y + a.height) return;
00919            if (x < a.x) {
00920               x0 += a.x - x;
00921               xoff += a.x - x;
00922               w -= a.x - x;
00923               x = a.x;
00924            }
00925            if (x + w > a.x + a.width)
00926               w = a.x + a.width - x;
00927            if (y < a.y) {
00928               y0 += a.y - y;
00929               yoff += a.y - y;
00930               h -= a.y - y;
00931               y = a.y;
00932            }
00933            if (y + h > a.y + a.height)
00934               h = a.y + a.height - y;
00935 #if 0
00936            XDrawLine(DISP, mane.win, gc, x0, y0, x0 + w - 1, y0);
00937            XDrawLine(DISP, mane.win, gc, x0 + w - 1, y0, x0 + w - 1, y0 + h - 1);
00938            XDrawLine(DISP, mane.win, gc, x0 + w - 1, y0 + h - 1, x0, y0 + h - 1);
00939            XDrawLine(DISP, mane.win, gc, x0, y0 + h - 1, x0, y0);
00940            XDrawLine(DISP, mane.win, gc, x0, y0, x0 + w - 1, y0 + h - 1);
00941            XDrawLine(DISP, mane.win, gc, x0 + w - 1, y0, x0, y0 + h - 1);
00942 
00943            XDrawLine(DISP, mane.win, gc, expose->min_x,
00944                     expose->min_y, expose->max_x, expose->min_y);
00945            XDrawLine(DISP, mane.win, gc, expose->max_x,
00946                     expose->min_y, expose->max_x, expose->max_y);
00947            XDrawLine(DISP, mane.win, gc, expose->max_x,
00948                     expose->max_y, expose->min_x, expose->max_y);
00949            XDrawLine(DISP, mane.win, gc, expose->min_x,
00950                     expose->max_y, expose->min_x, expose->min_y);
00951            XDrawLine(DISP, mane.win, gc, expose->max_x,
00952                     expose->max_y, expose->min_x, expose->min_y);
00953            XDrawLine(DISP, mane.win, gc, expose->min_x,
00954                     expose->max_y, expose->max_x, expose->min_y);
00955 #endif
00956        }
00957 
00958 #ifdef __GNUC__
00959 #warning FIXME: GetImage may still produce 'BadMatch' when dragging \
00960        the window around (toward outside of the desktop).
00961 #endif
00962        scr = XGetImage(DISP, mane.win, x0, y0, w, h, AllPlanes, ZPixmap);
00963 
00964 #warning FIXME: the loops are only a quick hack and are slow.
00965        for (y = 0; y < h; y++)
00966            for (x = 0; x < w; x++) {
00967               unsigned long pixel = 0;
00968               pixel = XGetPixel(img->ximage, x + xoff, y + yoff);
00969               if (pixel == img->bgpixel)
00970                   continue;
00971               XPutPixel(scr, x, y, pixel);
00972            }
00973        XPutImage(DISP, mane.win, globals.gc.copy, scr, 0, 0, x0, y0, w, h);
00974        XDestroyImage(scr);
00975     }
00976 #endif
00977     showtime("    Done.");
00978 }
00979 
00980 static void
00981 render_image_file(XDviImageInfo *info, int x, int y)
00982 {
00983     XDviImage *im = NULL;
00984     int w, h;
00985 
00986     if (globals.debug & DBG_PS)
00987        fprintf(stderr, "Magick: render_image_file: pos=(%d,%d)\n", x, y);
00988 
00989 #ifdef __GNUC__
00990 #warning FIXME: immediate return if image not exposed.
00991 #endif
00992 
00993     if (!INSIDE_MANE_WIN)
00994        return;
00995 
00996     info->angle = bbox_angle >= 0 ? bbox_angle % 360 : 360 - (-bbox_angle) % 360;
00997     if (globals.debug & DBG_PS)
00998        fprintf(stderr, "Magick: render_image_file: angle=%d\n", info->angle);
00999 
01000     if (resource.postscript && (im = load_image(info)) != NULL)
01001        display_image(im, x, y, &globals.win_expose, globals.gc.rule);
01002 
01003     if (im != NULL && resource.postscript == 1)
01004        return;
01005 
01006     /* Draw bbox of the image */
01007     w = ROUND(DPI * info->rwi / 720.0);
01008     h = ROUND(DPI * info->rhi / 720.0);
01009 
01010     if (globals.debug & DBG_PS)
01011        fprintf(stderr, "Magick: render_image_file: box=%dx%d+%d+%d\n",
01012                      w, h, x, y - h + 1);
01013 
01014     if (!info->angle) {
01015        XDrawRectangle(DISP, currwin.win, globals.gc.high, x, y - h + 1, w, h);
01016     }
01017     else
01018     {
01019 #if 1
01020        bbox_valid = True;
01021        bbox_width = w;
01022        bbox_height = bbox_voffset = h;
01023        save_bbox();
01024        bbox_valid = False;
01025 #else
01026        /* code borrowed from special.c:draw_bbox() */
01027        double sin_a = sin(info->angle * (TWOPI / 360));
01028        double cos_a = cos(info->angle * (TWOPI / 360));
01029        double a, b, c, d;
01030 
01031        a = cos_a * (w-1);
01032        b = -sin_a * (w-1);
01033        c = -sin_a * (h-1);
01034        d = -cos_a * (h-1);
01035 
01036        XDrawLine(DISP, currwin.win, globals.gc.high,
01037                     x, y, x + ROUND(a), y + ROUND(b));
01038        XDrawLine(DISP, currwin.win, globals.gc.high,
01039                     x + ROUND(a), y + ROUND(b),
01040                     x + ROUND(a + c), y + ROUND(b + d));
01041        XDrawLine(DISP, currwin.win, globals.gc.high,
01042                     x + ROUND(a + c), y + ROUND(b + d),
01043                     x + ROUND(c), y + ROUND(d));
01044        XDrawLine(DISP, currwin.win, globals.gc.high,
01045                     x + ROUND(c), y + ROUND(d), x, y);
01046 #endif
01047     }
01048 }
01049 
01050 /* Process EPSF specials */
01051 
01052 static Boolean
01053 epsf_special(char *cp)
01054 {
01055     char *filename;
01056     unsigned filename_len;
01057     char *p;
01058     int n;
01059     unsigned long flags = 0, u;
01060     double rwi, rhi;
01061     enum { LLX, LLY,
01062        URX, URY,
01063        RWI, RHI,
01064        HSIZE, VSIZE,
01065        HOFFSET, VOFFSET,
01066        HSCALE, VSCALE,
01067        ANGLE
01068     };
01069     XDviImageInfo info;
01070 
01071     struct {
01072        double val;
01073        const char *key;
01074        long mask;
01075     } key_info[] = {
01076        { 0, "llx",   0x0001 },
01077        { 0, "lly",   0x0002 },
01078        { 0, "urx",   0x0004 },
01079        { 0, "ury",   0x0008 },
01080        { 0, "rwi",   0x0010 },
01081        { 0, "rhi",   0x0020 },
01082        { 0, "hsize", 0x0040 },
01083        { 0, "vsize", 0x0080 },
01084        { 0, "hoffset",      0x0100 },
01085        { 0, "voffset",      0x0200 },
01086        { 0, "hscale",       0x0400 },
01087        { 0, "vscale",       0x0800 },
01088        { 0, "angle", 0x1000 },
01089     };
01090 
01091     memset(&info, 0, sizeof info); /* for validating memcmp(&info, ... */
01092 
01093     filename = cp;
01094     if (*cp == '\'' || *cp == '"') {
01095        do
01096            ++cp;
01097        while (*cp != '\0' && *cp != *filename);
01098        filename_len = cp - filename - 1;
01099        if (*cp == *filename)
01100            ++cp;
01101        ++filename;
01102     }
01103     else {
01104        while (*cp != '\0' && !isspace((int)*cp))
01105            ++cp;
01106        filename_len = cp - filename;
01107     }
01108 
01109     if (filename_len + 1 > MaxTextExtent) {
01110        if (globals.debug & DBG_PS)
01111            fprintf(stderr, "Magick: filename too long, passing to psgs.c\n");
01112        return False;
01113     }
01114 
01115     memcpy(info.filename, filename, filename_len); /* no need to terminate because of memset(...) above */
01116 
01117     if (globals.debug & DBG_PS)
01118        fprintf(stderr, "Magick: epsf_special: filename = '%s'\n",
01119                      info.filename);
01120 
01121     /* Scan for EPSF keywords */
01122     while (*cp != '\0') {
01123        while (isspace((int)*cp))
01124            ++cp;
01125        p = cp;
01126        while (*p != '=' && !isspace((int)*p) && *p != '\0')
01127            p++;
01128        n = p - cp;
01129        if (!n)
01130            break;
01131        if (*p == '=')
01132            ++p;
01133        while (isspace((int)*p))
01134            ++p;
01135 
01136        if (!memcmp(cp, "clip", n)) {
01137            /* this is the only key without value */
01138            info.clip = True;
01139        }
01140        else {
01141            size_t i;
01142            Boolean found = False;
01143            for (i = 0; !found && i < XtNumber(key_info); i++) {
01144               if (memicmp(cp, key_info[i].key, n) == 0) {
01145                   if (sscanf(p, "%lf", &(key_info[i].val)) == 1) {
01146                      flags |= key_info[i].mask;
01147                      while (!isspace((int)*p) && *p != '\0')
01148                          ++p;
01149                      found = True;
01150                   }
01151                   else {
01152                      fprintf(stderr, "Magick: invalid value for %s\n", key_info[i].key);
01153                      return False;
01154                   }
01155               }
01156            }
01157 
01158            if (!found) {
01159               i = *(cp + n);
01160               *(cp + n) = '\0';
01161               fprintf(stderr,"Magick: ignoring unknown EPSF key \"%s\".\n", cp);
01162               *(cp + n) = i;
01163               /* skip '=...' */
01164               while (*p != '\0' && !isspace((int)*p))
01165                   ++p;
01166            }
01167        }
01168        cp = p;
01169     }
01170 
01171     if (globals.debug & DBG_PS)
01172        fprintf(stderr, "Magick: epsf_special: flags = 0x%lx\n", flags);
01173 
01174     /* only accept the special when llx, lly, urx, ury are properly defined */
01175     u = key_info[LLX].mask | key_info[LLY].mask |
01176        key_info[URX].mask | key_info[URY].mask;
01177     if ((flags & u) != u)
01178        return False;
01179 
01180 #ifdef __GNUC__
01181 #warning keywords to handle: [hv]size, [hv]offset, [hv]scale, angle \
01182               (ZLB: not used by the graphicx/graphics packages)
01183 #endif
01184     if (flags & (key_info[HSIZE].mask | key_info[VSIZE].mask |
01185                key_info[HOFFSET].mask | key_info[VOFFSET].mask |
01186                key_info[HSCALE].mask | key_info[VSCALE].mask |
01187                key_info[ANGLE].mask))
01188            fprintf(stderr, "Magick: warning: EPSF keywords '[hv]size', "
01189                   "'[hv]offset', '[hv]scale', and 'angle' are not "
01190                   "implemented.\n");
01191 
01192     info.urx = key_info[URX].val;
01193     info.ury = key_info[URY].val;
01194     info.llx = key_info[LLX].val;
01195     info.lly = key_info[LLY].val;
01196     rwi = key_info[RWI].val;
01197     rhi = key_info[RHI].val;
01198 
01199     if (globals.debug & DBG_PS)
01200        fprintf(stderr, "Magick: epsf_special: llx=%0.2f, lly=%0.2f, "
01201                      "urx=%0.2f, ury=%0.2f\n",
01202               info.llx, info.lly, info.urx, info.ury);
01203 
01204     if (info.urx - info.llx < 1 || info.ury - info.lly < 1)
01205        return False;
01206     if (info.urx - info.llx > 1e+4 || info.ury - info.lly > 1e+4)
01207        return False;
01208     if (fabs(info.llx) > 1e+10 || fabs(info.lly) > 1e+10)
01209        return False;
01210 
01211     if (!(flags & (key_info[RWI].mask | key_info[RHI].mask))) {
01212        /* both rwi and rhi undefined */
01213        rwi = 10 * (info.urx - info.llx);
01214        rhi = 10 * (info.ury - info.lly);
01215     }
01216     else if (!(flags & key_info[RWI].mask)) {
01217        /* rwi undefined, rhi defined */
01218        rwi = rhi * (info.urx - info.llx) / (info.ury - info.lly);
01219     }
01220     else if (!(flags & key_info[RHI].mask)) {
01221        /* rhi undefined, rwi defined */
01222        rhi = rwi * (info.ury - info.lly) / (info.urx - info.llx);
01223     }
01224 
01225     if (globals.debug & DBG_PS)
01226        fprintf(stderr, "Magick: epsf_special: rwi=%f, rhi=%f\n", rwi, rhi);
01227 
01228     if (rwi < 1 || rhi < 1)
01229        return False;
01230 
01231     /* We don't want IM to process large images (>16MB) */
01232     if (DPI * rwi / 720 * DPI * rhi / 720 * G_depth / 8 / 1024 / 1024 > 16) {
01233        p = filename + (n = filename_len);
01234        if ((n >= 3 && !memicmp(p - 3, ".ps", 3)) ||
01235            (n >= 4 && !memicmp(p - 4, ".eps", 4))) {
01236            if (globals.debug & DBG_PS)
01237               fprintf(stderr, "Magick: image too large, passing to psgs.c\n");
01238            return False;
01239        }
01240        else {
01241            /* don't pass non-PS files to psgs.c */
01242            if (globals.debug & DBG_PS)
01243               fprintf(stderr, "Magick: image too large, ignored.\n");
01244            return True;
01245        }
01246     }
01247 
01248     info.rwi = ROUND(rwi);
01249     info.rhi = ROUND(rhi);
01250 
01251     render_image_file(&info, PXL_H - currwin.base_x, PXL_V - currwin.base_y);
01252 
01253     return True;
01254 }
01255 
01256 static char *
01257 emgraph_get_dimen(char *cp, double *v)
01258 {
01259     char *p;
01260     typedef struct {
01261        int name_len;
01262        char *name;
01263        double factor;
01264     } Unit;
01265     Unit *u;
01266     /* table for converting an arbitrary unit to bp */
01267 #define UNIT(u)      sizeof(u)-1, u
01268     static Unit units[] = {
01269        { UNIT("bp"), 1.0 },
01270        { UNIT("in"), 72.0 },
01271        { UNIT("cm"), 72.0 / 2.54 },
01272        { UNIT("mm"), 72.0 / 2.54 * 0.1 },
01273        { UNIT("pt"), 72.0 / 72.27 },
01274        { UNIT("sp"), 72.0 / 72.27 / 65536.0 },
01275        { UNIT("pc"), 12.0 * 72.0 / 72.27 },
01276        { UNIT("dd"), 1238.0 / 1157.0 * 72.0 / 72.27 },
01277        { UNIT("cc"), 12.0 * 1238.0 / 1157.0 * 72.0 / 72.27 }
01278     };
01279 
01280     *v = strtod(cp, &p);
01281     if (p == NULL) {
01282        if (globals.debug & DBG_PS)
01283            fprintf(stderr, "Magick: can't get dimension, image ignored.\n");
01284        return NULL;
01285     }
01286 
01287     while (isspace((int)*p))
01288        ++p;
01289     for (u = units; u < units + XtNumber(units); u++)
01290        if (!memicmp(p, u->name, u->name_len))
01291            break;
01292     if (u >= units + XtNumber(units)) {
01293        if (globals.debug & DBG_PS)
01294            fprintf(stderr, "Magick: unknown unit, image ignored.\n");
01295        return NULL;
01296     }
01297     *v *= u->factor;
01298 
01299     if (globals.debug & DBG_PS)
01300        fprintf(stderr, "Magick: unit=%s, len=%d, factor=%0.4f, value=%0.4f\n",
01301               u->name, u->name_len, u->factor, *v);
01302 
01303     if (*v < 0.01 || *v > 10000) {
01304        if (globals.debug & DBG_PS)
01305            fprintf(stderr, "Magick: invalid dimension, image ignored.\n");
01306        return NULL;
01307     }
01308 
01309     return p + u->name_len;
01310 }
01311 
01312 static Boolean
01313 emgraph_special(char *cp)
01314 {
01315     char *p;
01316     double w, h;
01317     XDviImageInfo info;
01318 
01319     memset(&info, 0, sizeof info);
01320 
01321     /* get filename */
01322     while (isspace((int)*cp))
01323        ++cp;
01324     p = cp;
01325     while (!isspace((int)*p) && *p != '\0' && *p != ',')
01326        ++p;
01327     if ((unsigned)(p - cp) >= sizeof info.filename) {
01328        if (globals.debug & DBG_PS)
01329            fprintf(stderr, "Magick: filename too long, image ignored.\n");
01330        return False;
01331     }
01332     if (p <= cp) {
01333        if (globals.debug & DBG_PS)
01334            fprintf(stderr, "Magick: empty filename, image ignored.\n");
01335        return False;
01336     }
01337     memcpy(info.filename, cp, p - cp); /* no need to terminate because of memset(...) above */
01338 
01339     cp = p;
01340 
01341     /* get width */
01342     if ((cp = strchr(cp, ',')) == NULL) {
01343        if (globals.debug & DBG_PS)
01344            fprintf(stderr, "Magick: width undefined, image ignored.\n");
01345        return False;
01346     }
01347     if ((cp = emgraph_get_dimen(++cp, &w)) == NULL)
01348        return False;
01349 
01350     /* get height */
01351     if ((cp = strchr(cp, ',')) == NULL) {
01352        if (globals.debug & DBG_PS)
01353            fprintf(stderr, "Magick: height undefined, image ignored.\n");
01354        return False;
01355     }
01356     if ((cp = emgraph_get_dimen(++cp, &h)) == NULL)
01357        return False;
01358 
01359     if (globals.debug & DBG_PS)
01360        fprintf(stderr, "Magick: filename=\"%s\", width=%0.2f, height=%0.2f\n",
01361                      info.filename, w, h);
01362 
01363     info.llx = info.lly = 0;
01364     info.urx = w;
01365     info.ury = h;
01366     info.rwi = ROUND(10 * w);
01367     info.rhi = ROUND(10 * h);
01368 
01369     if (info.rwi < 1 || info.rhi < 1)
01370        return False;
01371 
01372     render_image_file(&info, PXL_H - currwin.base_x,
01373                     PXL_V - currwin.base_y + ROUND(h / 72 * DPI) - 1);
01374 
01375     return True;
01376 }
01377 
01378 /* Filter specials. Returns True if the special has been processed
01379  * by ImageMagick, False otherwise. */
01380 Boolean
01381 Magick_parse_special(char *cp)
01382 {
01383     switch (*cp) {
01384     case 'p':
01385     case 'P':
01386        if (!memicmp(cp, "psfile", 6)) {
01387            if (globals.debug & DBG_PS)
01388               fprintf(stderr, "Magick: parsing string |%s|\n", cp);
01389            cp += 6;
01390            while (isspace((int)*cp))
01391               ++cp;
01392            if (*cp != '=')
01393               return False;
01394            do
01395               ++cp;
01396            while (isspace((int)*cp));
01397            return epsf_special(cp);
01398        }
01399        break;
01400     case 'e':
01401     case 'E':
01402        if (!memicmp(cp, "em:", 3)) {
01403            cp += 3;
01404            while (isspace((int)*cp))
01405               ++cp;
01406            if (memicmp(cp, "graph", 5))
01407               return False;
01408            cp += 5;
01409            if (globals.debug & DBG_PS)
01410               fprintf(stderr, "Magick: parsing string |em:graph %s|\n", cp);
01411            return emgraph_special(cp);
01412        }
01413        break;
01414     }
01415 
01416     return False;
01417 }
01418 
01419 #else
01420 /* silence `empty compilation unit' warnings */
01421 static void bar(void); static void foo() { bar(); } static void bar(void) { foo(); }
01422 #endif /* MAGICK */