summaryrefslogtreecommitdiff
path: root/Build/source/texk/xdvik/image-magick.c
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2006-01-17 21:41:51 +0000
committerKarl Berry <karl@freefriends.org>2006-01-17 21:41:51 +0000
commit487ca4806cc046076293cf6cc5fbba0db282bac7 (patch)
tree847b412ab5158dd7bdd7ed7e5a4cc3fbca94be32 /Build/source/texk/xdvik/image-magick.c
parenta3d3111bfe26b8e5f5bc6049dfb2a4ca2edc7881 (diff)
texk 1
git-svn-id: svn://tug.org/texlive/trunk@1485 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/xdvik/image-magick.c')
-rw-r--r--Build/source/texk/xdvik/image-magick.c1422
1 files changed, 1422 insertions, 0 deletions
diff --git a/Build/source/texk/xdvik/image-magick.c b/Build/source/texk/xdvik/image-magick.c
new file mode 100644
index 00000000000..50bf201956a
--- /dev/null
+++ b/Build/source/texk/xdvik/image-magick.c
@@ -0,0 +1,1422 @@
+/*
+ Imagemagick support for xdvik, written by Zhang Linbo <zlb@lsec.cc.ac.cn>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM,
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+/*
+ TODO:
+
+ 1. Specials other than 'psfile=' and 'em:graph' which include
+ image files.
+
+ [ NOTE SU: I consider this more or less done now with the backtick
+ parsing ;-) ]
+
+ 2. Use an XtWorkProc or similar to do some background pre-processing
+ of image files before they are displayed.
+
+ 3. Replace popen() by fork and explicit pipes, and make the
+ use of xdvi_temp_fd safer.
+
+ 4. PS figs are always clipped to the bbox defined in the special
+ string, which is not what we want when clip=false.
+ The problem can be partly solved by increasing the value of
+ the PSBW macro, or we may get the correct bbox by reading the
+ '%%BoundingBox' or '%%HiresBoundingBox' lines in the PS file
+ and set the image geometry accordingly.
+
+ 5. The code only(?) works with true color displays.
+
+ 6. There's also work to do to respond to window events in
+ load_image() and display_image() functions.
+*/
+#include <ctype.h>
+
+#include "xdvi-config.h"
+#include "xdvi.h"
+#include "dvi-init.h"
+#include "events.h"
+#include "dvi-draw.h"
+#include "util.h"
+#include "special.h"
+#include "image-magick.h"
+
+#ifdef MAGICK /* entire file */
+
+#include "magick/api.h"
+#if MAGICK_VER_MAJOR > 5 || MAGICK_VER_MINOR >= 4
+# include "magick/xwindow.h" /* IM >= 5.4 */
+#else
+# include "magick/xwindows.h" /* IM <= 5.3 */
+#endif
+
+#if MAGICK_VER_MAJOR > 5 || MAGICK_VER_MINOR >= 4 /* >= 5.4 */
+# define CATCH_EXCEPTION(e) do { \
+ if (e.severity != UndefinedException) CatchException(&e); } while (0)
+#elif MAGICK_VER_MINOR >= 3 /* 5.3 */
+# define CATCH_EXCEPTION(e) if (image == NULL) \
+ MagickWarning(e.severity, e.reason, e.description);
+#else /* <= 5.2 (no check) */
+# define CATCH_EXCEPTION(e) if (image == NULL) \
+ MagickWarning(e.severity, e.message, e.qualifier);
+#endif
+
+#if 0
+#define CATCH_EXCEPTION(e) /* do nothing */
+#endif
+
+#ifndef False
+# define False 0
+#endif
+
+#ifndef True
+# define True 1
+#endif
+
+#define TWOPI (2*3.141592653589793)
+
+static double ROUND_tmp;
+#define ROUND(x) ( (ROUND_tmp=(x)) >= 0 ? (long)(ROUND_tmp + 0.5) : \
+ - (long)(- ROUND_tmp + 0.5) )
+
+static void
+showtime(const char *msg)
+{
+ if (globals.debug & DBG_PS) {
+ static double time0 = -1;
+ double time;
+ struct timeval tv;
+
+ gettimeofday(&tv, (struct timezone *) 0);
+ time = tv.tv_sec + (double) tv.tv_usec * 1e-6;
+ if (time0 < 0)
+ time0 = time;
+ fprintf(stderr, " %-40s %0.4f\n", msg, time - time0);
+ }
+}
+
+#define DPI ( resource.pixels_per_inch*0.001*magnification/mane.shrinkfactor )
+
+typedef struct XDviImageInfo {
+ Boolean clip;
+ int rwi, rhi, angle;
+ double llx, lly, urx, ury;
+ char filename[MaxTextExtent];
+} XDviImageInfo;
+
+typedef struct {
+ XDviImageInfo info;
+ XImage *ximage; /* ximage */
+ unsigned long bgpixel; /* background pixel */
+ int xoff, yoff; /* offsets in pixels */
+} XDviImage;
+
+static size_t cache_size = 0;
+/* default value for cache_limit (16MB), it can be specified with the
+ * "-magick_cache size" option */
+static size_t cache_limit = 16 * 1024 * 1024;
+static int ncached = 0;
+static XDviImage *cache = NULL;
+
+static char *magick_tmp;
+static double current_dpi = -1;
+
+/* Input image types */
+enum {
+ TYPE_UNKNOWN, /* unknown */
+ TYPE_CONVERT, /* `convert ... */
+};
+static int image_type; /* type of current input image */
+
+static void
+cleanup(void)
+{
+ if (magick_tmp != NULL)
+ unlink(magick_tmp);
+}
+
+static void
+free_ximage(XImage * ximage)
+{
+ if (ximage == NULL)
+ return;
+ if (ximage->data != NULL) {
+ free(ximage->data);
+ ximage->data = NULL;
+ }
+ XDestroyImage(ximage);
+}
+
+static void
+clear_cache()
+{
+ XDviImage *c;
+ if (!ncached)
+ return;
+ for (c = cache; c < cache + ncached; c++)
+ free_ximage(c->ximage);
+ free(cache);
+ ncached = 0;
+ cache = NULL;
+ cache_size = 0;
+}
+
+static void
+parse_backtick(char *fn)
+{
+ char *p, *q, *r, c;
+
+ if (fn[0] != '`')
+ return;
+
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: parsing backtick command |%s|\n", fn);
+
+#ifdef __GNUC__
+#warning FIXME: add code to execute the cmd then load the output file, \
+ if '-allowshell' is effective and the user requests it \
+ somehow (slow but reliable).
+#endif
+
+ fn[0] = '\0';
+ p = fn + 1;
+ /* Skip 'env' and 'VAR=value' */
+ do {
+ while (isspace((int)*p))
+ ++p;
+ q = p;
+ while (*q != '\0' && !isspace((int)*q))
+ ++q;
+ if (*q == '\0' || q <= p)
+ return;
+ if (((r = strchr(p, '=')) == NULL || r >= q) &&
+ (!isspace((int)*q) || strncmp(p, "env", 3)))
+ break;
+ p = q;
+ } while (1);
+
+ /* Now [p ... q-1] is the command name */
+ if (q - p == 7 && !memcmp(p, "convert", 7))
+ image_type = TYPE_CONVERT;
+ else
+ image_type = TYPE_UNKNOWN;
+ p = q;
+ do {
+ while (isspace((int)*p))
+ ++p;
+ q = p;
+ if (*q == '`' || *q == '\'' || *q == '"') {
+ c = *p;
+ ++q;
+ while (*q != '\0' && *q != c)
+ q++;
+ if (*q != '\0')
+ q++;
+ }
+ else {
+ while (*q != '\0' && !isspace((int)*q))
+ q++;
+ }
+#ifdef __GNUC__
+#warning FIXME: check for some cmdline options
+ break;
+#endif
+ } while (1);
+ *q = '\0';
+
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: filename=|%s|\n", p);
+
+ /* check for the case `kpsewhich -n ... #1` */
+ if (*p == '`') {
+#if 0
+ /* WARNING: unsafe! */
+ FILE *pipe;
+
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: executing %s\n", p);
+ if (*(--q) == '`')
+ *q = '\0';
+ if ((pipe = popen(p + 1, "r")) == NULL)
+ return;
+ fscanf(pipe, "%s", fn);
+ pclose(pipe);
+ return;
+#else
+ if (memcmp(++p, "kpsewhich", sizeof("kpsewhich") - 1))
+ return;
+#ifdef __GNUC__
+#warning FIXME: better make KPSE library call here.
+#endif
+ if (*(--q) == '`')
+ --q;
+ while (q >= p && isspace((int)*q))
+ --q;
+ if (q < p)
+ return;
+ *(q + 1) = '\0';
+ while (q >= p && !isspace((int)*q))
+ --q;
+ p = q + 1;
+#endif
+ }
+
+ strcpy(fn, p);
+}
+
+static Boolean
+imagick_eps_to_jpeg(const char *fullpath,
+ unsigned long *columns, unsigned long *rows,
+ XDviImage *img, PixelPacket *bgcolor,
+ double xdpi, double ydpi)
+{
+ size_t size;
+#ifdef __GNUC__
+#warning FIXME: security (check 'allowshell'?), backticks, pathname canonicalization,
+ /* note SU: better use fork()/exec() here, popen() is too unsafe. */
+#endif
+ static char *cmdline = NULL;
+ static size_t cmdline_size = 0;
+ static char *fmt = "%s -q -dBATCH -dMaxBitmap=300000000 "
+ "-dNOPAUSE -dNOSAFER -sDEVICE=jpeg "
+ "-dTextAlphaBits=4 -dGraphicsAlphaBits=4 "
+ "-g%ldx%ld -r%0.4fx%0.4f -sOutputFile=%s - -c quit";
+ static const char strsafe[] =
+ "{ << /PermitFileReading [ (*) ] /PermitFileWriting [ ] /PermitFileControl [ ] "
+ ">> setuserparams .locksafe "
+ "} stopped pop\n";
+ FILE *pipe;
+
+ showtime("PS/EPS -> JPEG:");
+ /* Add a border to the drawing box (PSBW=border width in bp) */
+#define PSBW 1
+ *columns += ROUND(2 * PSBW / 72.0 * xdpi);
+ *rows += ROUND(2 * PSBW / 72.0 * ydpi);
+ img->xoff = ROUND(PSBW / 72.0 * xdpi);
+ img->yoff = -ROUND(PSBW / 72.0 * ydpi);
+
+ size = strlen(fmt) + 25 * 2 + 10 * 2 + strlen(magick_tmp) + strlen(resource.gs_path) + 1;
+ if (size > cmdline_size) {
+ cmdline_size = size;
+ cmdline = xrealloc(cmdline, cmdline_size);
+ }
+ unlink(magick_tmp);
+ sprintf(cmdline, fmt, resource.gs_path, *columns, *rows, xdpi, ydpi,
+ magick_tmp);
+ if ((pipe = popen(cmdline, "w")) == NULL) {
+ fprintf(stderr, "%s: Cannot execute %s, ignoring image %s.\n",
+ globals.program_name, resource.gs_path, fullpath);
+ return False;
+ }
+
+#define ColorToPixelPacket(c) ((c*((1L<<(QuantumDepth))-1L)+32768L)/65535L)
+#define UnifiedColorValue(c) ((c)/(double)((1L<<(QuantumDepth))-1L))
+#ifdef __GNUC__
+#warning FIXME: how to control roundoff error which may produce a \
+ different backgroung pixel in the output image?
+#endif
+#if COLOR
+ bgcolor->red = ColorToPixelPacket(bg_current->color.r);
+ bgcolor->green = ColorToPixelPacket(bg_current->color.g);
+ bgcolor->blue = ColorToPixelPacket(bg_current->color.b);
+#else
+ bgcolor->red = bgcolor->green = bgcolor->blue = ColorToPixelPacket(65535);
+#endif
+ bgcolor->opacity = (1L << QuantumDepth) - 1L;
+
+#ifdef __GNUC__
+#warning FIXME: how to set bg color within gs (instead of filling the bbox) ?
+#endif
+ if (resource.gs_safer) {
+ fprintf(pipe, strsafe);
+ }
+ /* fill PS paper with background color */
+ fprintf(pipe,
+ "newpath -5 -5 moveto %f 0 rlineto "
+ "0 %f rlineto %f 0 rlineto 0 %f rlineto "
+ "closepath %f %f %f setrgbcolor fill\n",
+ img->info.urx - img->info.llx + 2 * (PSBW + 5),
+ img->info.ury - img->info.lly + 2 * (PSBW + 5),
+ -(img->info.urx - img->info.llx + 2 * (PSBW + 5)),
+ -(img->info.ury - img->info.lly + 2 * (PSBW + 5)),
+ UnifiedColorValue(bgcolor->red),
+ UnifiedColorValue(bgcolor->green),
+ UnifiedColorValue(bgcolor->blue));
+
+ /* shift PS coordinates */
+ fprintf(pipe, "%f %f translate\n", -img->info.llx + PSBW, -img->info.lly + PSBW);
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, " clip=%s\n", img->info.clip ? "yes" : "no");
+ if (img->info.clip) {
+ fprintf(pipe, "newpath %f %f moveto %f 0 rlineto 0 %f rlineto "
+ "%f 0 rlineto 0 %f rlineto closepath clip\n",
+ img->info.llx, img->info.lly, img->info.urx - img->info.llx,
+ img->info.ury - img->info.lly, -(img->info.urx - img->info.llx),
+ -(img->info.ury - img->info.lly));
+ }
+ fprintf(pipe, "%f %f moveto\n", img->info.llx, img->info.lly);
+ fprintf(pipe, "(%s) run showpage\n", fullpath);
+ pclose(pipe);
+ return True;
+}
+
+
+static Image *
+rotate_image(XDviImage *img, Image *image, ExceptionInfo exception)
+{
+ Image *tmp_image;
+ unsigned long w = image->columns, h = image->rows;
+ double sin_a = sin(img->info.angle * (TWOPI / 360));
+ double cos_a = cos(img->info.angle * (TWOPI / 360));
+
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "\t rotating image by %d degrees\n",
+ img->info.angle);
+
+ /* adjust xoff, yoff, assuming (0,0) the origin of rotation */
+ if (img->xoff || img->yoff) {
+ double a, b;
+ a = img->xoff * cos_a + img->yoff * sin_a;
+ b = -img->xoff * sin_a + img->yoff * cos_a;
+ img->xoff = ROUND(a);
+ img->yoff = ROUND(b);
+ }
+
+ showtime("RotateImage:");
+ tmp_image = RotateImage(image, -(double)img->info.angle, &exception);
+ DestroyImage(image);
+ image = tmp_image;
+ CATCH_EXCEPTION(exception);
+ if (image == NULL)
+ return NULL;
+ /*
+ * _______________________________________
+ * | * |
+ * | * * |
+ * | * * |
+ * | * * |
+ * | * * |
+ * | * * |
+ * | * * |
+ * | * * |
+ * | * * |
+ * |* *|
+ * | * * |
+ * | * * |
+ * | * * |
+ * | * * |
+ * | * * |
+ * | * * |
+ * | * * |
+ * | * * |
+ * | O (origin) |
+ * O--------------------------------------
+ * (lower-left corner of image box after rotation)
+ */
+ if (img->info.angle <= 90) {
+ img->xoff += ROUND(h * sin_a);
+ }
+ else if (img->info.angle <= 180) {
+ img->xoff += ROUND(h * sin_a - w * cos_a);
+ img->yoff += ROUND(h * cos_a);
+ }
+ else if (img->info.angle <= 270) {
+ img->xoff -= ROUND(w * cos_a);
+ img->yoff += ROUND(w * sin_a + h * cos_a);
+ }
+ else {
+ img->yoff += ROUND(w * sin_a);
+ }
+ return image;
+}
+
+static Image *
+crop_shift_image(XDviImage *img, Image *image,
+ unsigned long *columns, unsigned long *rows,
+ double xscale, double yscale,
+ ExceptionInfo exception)
+{
+ /* ZLB: the code below works for the case where the BoundingBox
+ * of the PS figure generated by the shell command is (0 0 w h),
+ * where w and h are the pixel width and height of the input image.
+ * (this is true for the program 'convert' without options).
+ * The image should then be cropped or/and shifted depending
+ * on the value of 'clip' */
+ int w = image->columns, h = image->rows;
+
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: type of input image = CONVERT\n");
+
+ if (!img->info.clip) {
+ /* We only need to shift the image */
+ img->xoff = +ROUND(xscale * DPI * img->info.llx / 72.0);
+ img->yoff = -ROUND(yscale * DPI * img->info.lly / 72.0);
+ *columns = ROUND(xscale * DPI * w / 72.0);
+ *rows = ROUND(yscale * DPI * h / 72.0);
+ }
+ else {
+ int llx = ROUND(img->info.llx);
+ int lly = ROUND(img->info.lly);
+ int urx = ROUND(img->info.urx);
+ int ury = ROUND(img->info.ury);
+
+ if (llx < 0) {
+ img->xoff = +ROUND(xscale * DPI * llx / 72.0);
+ llx = 0;
+ }
+ if (lly < 0) {
+ img->yoff = -ROUND(yscale * DPI * lly / 72.0);
+ lly = 0;
+ }
+ if (urx > w)
+ urx = w;
+ if (ury > h)
+ ury = h;
+
+ if ((urx - llx) < 1 || (urx - llx) < 1) {
+ DestroyImage(image);
+ return NULL;
+ }
+
+ if (llx != 0 || lly != 0 || urx != w || ury != h) {
+ /* Crop the image */
+ Image *tmp_image;
+ RectangleInfo geo;
+ geo.width = urx - llx;
+ geo.height = ury - lly;
+ geo.x = llx;
+ geo.y = h - ury;
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "\t cropping image (%d %d %d %d)\n",
+ llx, lly, urx, ury);
+ showtime("CropImage:");
+ tmp_image = CropImage(image, &geo, &exception);
+ DestroyImage(image);
+ image = tmp_image;
+ CATCH_EXCEPTION(exception);
+ if (image == NULL)
+ return NULL; /* error cropping image */
+ }
+ *columns = ROUND(xscale * DPI * (urx - llx) / 72.0);
+ *rows = ROUND(yscale * DPI * (ury - lly) / 72.0);
+ }
+ return image;
+}
+
+
+/* parse and return the `magick_cache' resource size_str */
+static long
+parse_cache_setting(const char *size_str)
+{
+ char *p;
+ long size = strtol(size_str, &p, 0);
+ switch (*p) {
+ case 'g':
+ case 'G':
+ size *= 1024;
+ /* fall through */
+ case 'm':
+ case 'M':
+ size *= 1024;
+ /* fall through */
+ case 'k':
+ case 'K':
+ size *= 1024;
+ break;
+ default:
+ if (*p != '\0') {
+ fprintf(stderr, "Magick: invalid suffix (%c) in "
+ "magick_cache option\n", *p);
+ }
+ }
+ return size;
+}
+
+static XDviImage *
+load_image(XDviImageInfo *info)
+{
+ static ExceptionInfo exception;
+ static ImageInfo *image_info;
+ static XResourceInfo resource_info;
+ static XVisualInfo visual_info;
+ static XStandardColormap map_info;
+ static XPixelInfo pixel_info;
+ static XWindowInfo window_info;
+ static Boolean initialized = False;
+ static Boolean disabled = False;
+
+ static XDviImage img = { {False, 0, 0, 0, 0, 0, 0, 0, ""}, NULL, 0, 0, 0 };
+
+ Image *image, *tmp_image;
+ double xscale, yscale, xdpi, ydpi;
+ int n;
+ unsigned long columns, rows;
+ char density[40];
+ size_t size;
+ char *fn = info->filename;
+ XDviImage *c;
+ PixelPacket bgcolor;
+
+ struct stat statbuf;
+ char *path, *fullpath;
+ char canonical_path[MAXPATHLEN + 1];
+
+ if (disabled)
+ return NULL;
+
+ parse_backtick(info->filename);
+ if (info->filename[0] == '\0') {
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: skipping malformed backtick command.\n");
+ return NULL;
+ }
+ else {
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: filename=|%s|\n", info->filename);
+ }
+
+ img.info = *info;
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: load_image: fn=%s\n", fn);
+
+ /* check for changes in mag/shrink/resolution */
+ if (fabs(current_dpi - DPI) > 1e-4) {
+ current_dpi = DPI;
+ clear_cache();
+ }
+
+ /* look it up in the cache */
+ if (ncached) {
+ for (c = cache; c < cache + ncached; c++) {
+ if (memcmp(&c->info, info, sizeof *info))
+ continue;
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, " Image found in cache.\n");
+ return c;
+ }
+ }
+
+ if (!initialized) {
+ magick_tmp = NULL;
+ if ((n = xdvi_temp_fd(&magick_tmp)) == -1) {
+ fprintf(stderr, "Magick: cannot create tmp filename, disabled.\n");
+ disabled = True;
+ return NULL;
+ }
+ /* FIXME: mustn't close filedescriptor returned by xdvi_temp_fd(),
+ else we have a race condition again; see e.g.
+ http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/avoid-race.html
+ */
+ close(n);
+ unlink(magick_tmp);
+ atexit(cleanup); /* remove magick_tmp at exit. */
+
+ showtime("Initializing load_image:");
+ InitializeMagick("xdvi.bin");
+ GetExceptionInfo(&exception);
+ image_info = CloneImageInfo((ImageInfo *) NULL);
+
+ XGetResourceInfo(XrmGetStringDatabase(""), "xdvi", &resource_info);
+ /* resource_info.debug = 1; */
+ if (!XMatchVisualInfo(DISP, XScreenNumberOfScreen(SCRN),
+ G_depth, G_visual->class, &visual_info)) {
+ fprintf(stderr, "Magick: can't get visual info, image disabled.\n");
+ disabled = True;
+ return NULL;
+ }
+ XGetMapInfo(&visual_info, G_colormap, &map_info);
+ XGetWindowInfo(DISP, &visual_info, &map_info, &pixel_info,
+ (XFontStruct *) NULL, &resource_info, &window_info);
+
+ if (resource.magick_cache != NULL) {
+ cache_limit = parse_cache_setting(resource.magick_cache);
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, " Setting magick_cache to %lu\n",
+ (unsigned long)cache_limit);
+ }
+ initialized = True;
+ }
+
+ xscale = info->rwi / (double) ((int) (10 * (info->urx - info->llx) + .5));
+ yscale = info->rhi / (double) ((int) (10 * (info->ury - info->lly) + .5));
+ xdpi = xscale * DPI;
+ ydpi = yscale * DPI;
+ /* Compute scaled size of the image */
+ columns = ROUND(DPI * info->rwi * 0.1 / 72);
+ rows = ROUND(DPI * info->rhi * 0.1 / 72);
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "\tscale=%0.2fx%0.2f, dpi=%0.2fx%0.2f, size=%ldx%ld\n",
+ xscale, yscale, xdpi, ydpi, columns, rows);
+
+ /* expand and canonicalize path name */
+ path = find_file(fn, &statbuf, kpse_pict_format);
+ if (path != NULL) {
+ fullpath = REALPATH(path, canonical_path);
+ if (fullpath == NULL) {
+ XDVI_WARNING((stderr, "Couldn't canonicalize %s to full path - returning unexpanded.",
+ path));
+ fullpath = path;
+ }
+ else {
+ free(path);
+ }
+ }
+ else {
+ fprintf(stderr, "%s: %s:%d: Can't find image file %s!\n",
+ globals.program_name, __FILE__, __LINE__, fn);
+ return NULL;
+ }
+
+ /* If the file is PS/EPS transform it to JPEG first (faster) */
+ n = strlen(fullpath);
+ if ((n >= 3 && !memicmp(fullpath + n - 3, ".ps", 3)) ||
+ (n >= 4 && !memicmp(fullpath + n - 4, ".eps", 4))) {
+ if (!imagick_eps_to_jpeg(fullpath, &columns, &rows, &img, &bgcolor, xdpi, ydpi)) {
+ return NULL;
+ }
+ /* Should we append the 'JPEG:' prefix to the filename to help
+ * ImageMagick to identify the file format? */
+ fullpath = magick_tmp;
+ }
+ else {
+ img.xoff = img.yoff = 0;
+ }
+
+ strcpy(image_info->filename, fullpath);
+ showtime("ReadImage:");
+ if (fullpath == magick_tmp) {
+ /* seems 'density' only affects image size of PS/EPS files */
+ sprintf(density, "%0.2fx%0.2f", xdpi, ydpi);
+ image_info->density = density;
+ image_info->units = PixelsPerInchResolution;
+ }
+ image = ReadImage(image_info, &exception);
+ CATCH_EXCEPTION(exception);
+ unlink(magick_tmp);
+ if (image == NULL) /* error loading image */
+ return NULL;
+
+ /* Set background color for PS/EPS fig */
+ if (fullpath == magick_tmp) /* input is PS */
+ image->background_color = bgcolor;
+
+ if (globals.debug & DBG_PS) {
+ int i = image->units;
+ fprintf(stderr, "\t image size = %ldx%ld\n",
+ image->columns, image->rows);
+ fprintf(stderr, "\t image resolution = %0.2fx%0.2f (units=%s)\n",
+ image->x_resolution, image->y_resolution,
+ i == UndefinedResolution ? "???" :
+ i == PixelsPerInchResolution ? "PPI" : "PPCM");
+ }
+
+ if (fullpath != magick_tmp) { /* non PS image */
+ /* Note: (info->llx info->lly info->urx info->ury) should be
+ * regarded as the viewport */
+ if (image_type == TYPE_CONVERT) {
+ image = crop_shift_image(&img, image, &columns, &rows, xscale, yscale, exception);
+ }
+ else {
+ /* Do nothing. The image will be scaled to the size of the bbox.
+ * ZLB: the output will be identical to dvips iff the bbox of
+ * the PS figure generated by the shell command is equal to
+ * (info->llx info->lly info->urx info->ury).
+ * (this is true if the bbox set via 'bb', 'viewport', etc.,
+ * is equal to the real bbox of the PS figure) */
+ }
+
+ if (columns != image->columns || rows != image->rows) {
+ /* Scale image */
+ if (globals.debug & DBG_PS) {
+ fprintf(stderr, "\t scaling image %ldx%ld -> %ldx%ld\n",
+ image->columns, image->rows, columns, rows);
+ }
+ showtime("ScaleImage:");
+ tmp_image = ScaleImage(image, columns, rows, &exception);
+ DestroyImage(image);
+ image = tmp_image;
+ CATCH_EXCEPTION(exception);
+ if (image == NULL)
+ return NULL; /* error resizing image */
+ }
+ }
+
+ /* rotate image */
+ if (info->angle) {
+ image = rotate_image(&img, image, exception);
+ }
+
+#if 0
+ DescribeImage(image, stdout, 1 /* verbosity */ );
+ DisplayImages(image_info, image);
+#endif
+
+#define XStandardPixel(map,color,dx) (unsigned long) (map.base_pixel + \
+ (((color).red * map.red_max + (1L << (dx - 1L))) / ((1L << dx) - 1L)) * map.red_mult + \
+ (((color).green * map.green_max + (1L << (dx - 1L))) / ((1L << dx) - 1L)) * map.green_mult + \
+ (((color).blue * map.blue_max + (1L << (dx - 1L))) / ((1L << dx) - 1L)) * map.blue_mult)
+
+ img.bgpixel = XStandardPixel(map_info, image->background_color, 16);
+ if (img.ximage != NULL) {
+ free_ximage(img.ximage);
+ img.ximage = NULL;
+ }
+#undef XStandardPixel
+
+ /* Transform to XImage */
+ window_info.id = mane.win;
+ window_info.annotate_context = globals.gc.copy;
+ /*
+ FIXME: If this isn't set to false, running on a remote display may
+ lead to `Badaccess (no permission to access private resource)' X
+ errors (only with ImageMagick >= 5.4 - zlb has emailed the
+ developers about this).
+ Setting it to false always might slightly hurt performance - how
+ to check for remoteness? Parse the DISPLAY variable?
+ */
+ window_info.shared_memory = False;
+ showtime("XMakeImage:");
+ XGetPixelPacket(DISP, &visual_info, &map_info,
+ &resource_info, image, &pixel_info);
+ n = XMakeImage(DISP, &resource_info, &window_info, image,
+ image->columns, image->rows);
+
+ /* Since XDestroyImage doesn't know how to free window_info.ximage->data,
+ * we simply copy window_info.ximage. The next call of XMakeImage
+ * will free both window_info.ximage->data and window_info.ximage.
+ */
+ DestroyImage(image);
+ if (!n)
+ return NULL;
+ img.ximage = XCreateImage(DISP, G_visual, G_depth, ZPixmap, 0, NULL,
+ window_info.ximage->width,
+ window_info.ximage->height, BMBITS, 0);
+ if (img.ximage == NULL)
+ return NULL;
+ *img.ximage = *(window_info.ximage);
+ size = img.ximage->bytes_per_line * img.ximage->height;
+ img.ximage->data = malloc(size ? size : 1); /* NOTE: no xmalloc! */
+ if (img.ximage->data == NULL) {
+ fprintf(stderr, "Magick: cannot allocate memory for ximage data.\n");
+ XDestroyImage(img.ximage);
+ img.ximage = NULL;
+ return NULL;
+ }
+ if (size)
+ memcpy(img.ximage->data, window_info.ximage->data, size);
+
+ size += sizeof(XDviImage) + sizeof(XImage);
+ if (cache_size + size > cache_limit) {
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, " image not cached (cache limit).\n");
+ return &img;
+ }
+
+ c = realloc(cache, sizeof(XDviImage) * (ncached + 1)); /* NOTE: no xrealloc! */
+ if (c == NULL) {
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, " image not cached (cache alloc).\n");
+ return &img;
+ }
+ cache_size += size;
+ cache = c;
+ c = cache + (ncached++);
+ *c = img;
+ img.ximage = NULL; /* so next call won't delete this ximage */
+
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, " image cached (cache_size=%uKB, limit=%uKB).\n",
+ cache_size / 1024, cache_limit / 1024);
+#if 0
+ DestroyImageInfo(image_info);
+ DestroyExceptionInfo(&exception);
+ DestroyMagick();
+#endif
+
+ return c;
+}
+
+static void
+display_image(XDviImage * img, int xul, int yul,
+ const struct window_expose_info *expose,
+ const GC gc)
+{
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, " display_image: pos=%d, %d\n", xul, yul);
+
+ xul -= img->xoff;
+ yul -= img->yoff;
+
+#if 0
+ XPutImage(DISP, mane.win, globals.gc.copy, img->ximage, 0, 0,
+ xul, yul - img->ximage.height + 1,
+ img->ximage.width, img->ximage.height);
+#else
+ /* Try to make background pixels transparent */
+ {
+ XImage *scr;
+ int x0 = xul;
+ int y0 = yul - img->ximage->height + 1;
+ int w = img->ximage->width;
+ int h = img->ximage->height;
+ int xoff = 0;
+ int yoff = 0;
+ int x, y;
+
+ if (x0 + w <= expose->min_x || x0 >= expose->max_x ||
+ y0 + h <= expose->min_y || y0 >= expose->max_y)
+ return;
+ if (x0 < expose->min_x) {
+ xoff = expose->min_x - x0;
+ x0 = expose->min_x;
+ w -= xoff;
+ }
+ if (x0 + w > expose->max_x)
+ w = expose->max_x - x0 + 0;
+ if (y0 < expose->min_y) {
+ yoff = expose->min_y - y0;
+ y0 = expose->min_y;
+ h -= yoff;
+ }
+ if (y0 + h > expose->max_y)
+ h = expose->max_y - y0 + 0;
+
+ {
+ /* Need to double check the bounds here, because redraw_page() in
+ * events.c may set larger bounding box (expose->min_x, expose->min_y, etc.)
+ * than actual viewport causing 'BadMatch' error in XGetImage().
+ *
+ * Here we clip the rectangle w*h+x0+y0 with the root window. */
+ XWindowAttributes a;
+ Status status;
+ Window child;
+
+ status = XGetWindowAttributes(DISP, mane.win, &a);
+ if (!status || a.map_state != IsViewable)
+ return;
+ XTranslateCoordinates(DISP, mane.win, a.root, x0, y0, &x, &y,
+ &child);
+ XGetWindowAttributes(DISP, a.root, &a);
+ if (x + w <= a.x || x >= a.x + a.width || y + h <= a.y
+ || y >= a.y + a.height) return;
+ if (x < a.x) {
+ x0 += a.x - x;
+ xoff += a.x - x;
+ w -= a.x - x;
+ x = a.x;
+ }
+ if (x + w > a.x + a.width)
+ w = a.x + a.width - x;
+ if (y < a.y) {
+ y0 += a.y - y;
+ yoff += a.y - y;
+ h -= a.y - y;
+ y = a.y;
+ }
+ if (y + h > a.y + a.height)
+ h = a.y + a.height - y;
+#if 0
+ XDrawLine(DISP, mane.win, gc, x0, y0, x0 + w - 1, y0);
+ XDrawLine(DISP, mane.win, gc, x0 + w - 1, y0, x0 + w - 1, y0 + h - 1);
+ XDrawLine(DISP, mane.win, gc, x0 + w - 1, y0 + h - 1, x0, y0 + h - 1);
+ XDrawLine(DISP, mane.win, gc, x0, y0 + h - 1, x0, y0);
+ XDrawLine(DISP, mane.win, gc, x0, y0, x0 + w - 1, y0 + h - 1);
+ XDrawLine(DISP, mane.win, gc, x0 + w - 1, y0, x0, y0 + h - 1);
+
+ XDrawLine(DISP, mane.win, gc, expose->min_x,
+ expose->min_y, expose->max_x, expose->min_y);
+ XDrawLine(DISP, mane.win, gc, expose->max_x,
+ expose->min_y, expose->max_x, expose->max_y);
+ XDrawLine(DISP, mane.win, gc, expose->max_x,
+ expose->max_y, expose->min_x, expose->max_y);
+ XDrawLine(DISP, mane.win, gc, expose->min_x,
+ expose->max_y, expose->min_x, expose->min_y);
+ XDrawLine(DISP, mane.win, gc, expose->max_x,
+ expose->max_y, expose->min_x, expose->min_y);
+ XDrawLine(DISP, mane.win, gc, expose->min_x,
+ expose->max_y, expose->max_x, expose->min_y);
+#endif
+ }
+
+#ifdef __GNUC__
+#warning FIXME: GetImage may still produce 'BadMatch' when dragging \
+ the window around (toward outside of the desktop).
+#endif
+ scr = XGetImage(DISP, mane.win, x0, y0, w, h, AllPlanes, ZPixmap);
+
+#warning FIXME: the loops are only a quick hack and are slow.
+ for (y = 0; y < h; y++)
+ for (x = 0; x < w; x++) {
+ unsigned long pixel = 0;
+ pixel = XGetPixel(img->ximage, x + xoff, y + yoff);
+ if (pixel == img->bgpixel)
+ continue;
+ XPutPixel(scr, x, y, pixel);
+ }
+ XPutImage(DISP, mane.win, globals.gc.copy, scr, 0, 0, x0, y0, w, h);
+ XDestroyImage(scr);
+ }
+#endif
+ showtime(" Done.");
+}
+
+static void
+render_image_file(XDviImageInfo *info, int x, int y)
+{
+ XDviImage *im = NULL;
+ int w, h;
+
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: render_image_file: pos=(%d,%d)\n", x, y);
+
+#ifdef __GNUC__
+#warning FIXME: immediate return if image not exposed.
+#endif
+
+ if (!INSIDE_MANE_WIN)
+ return;
+
+ info->angle = bbox_angle >= 0 ? bbox_angle % 360 : 360 - (-bbox_angle) % 360;
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: render_image_file: angle=%d\n", info->angle);
+
+ if (resource.postscript && (im = load_image(info)) != NULL)
+ display_image(im, x, y, &globals.win_expose, globals.gc.rule);
+
+ if (im != NULL && resource.postscript == 1)
+ return;
+
+ /* Draw bbox of the image */
+ w = ROUND(DPI * info->rwi / 720.0);
+ h = ROUND(DPI * info->rhi / 720.0);
+
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: render_image_file: box=%dx%d+%d+%d\n",
+ w, h, x, y - h + 1);
+
+ if (!info->angle) {
+ XDrawRectangle(DISP, currwin.win, globals.gc.high, x, y - h + 1, w, h);
+ }
+ else
+ {
+#if 1
+ bbox_valid = True;
+ bbox_width = w;
+ bbox_height = bbox_voffset = h;
+ save_bbox();
+ bbox_valid = False;
+#else
+ /* code borrowed from special.c:draw_bbox() */
+ double sin_a = sin(info->angle * (TWOPI / 360));
+ double cos_a = cos(info->angle * (TWOPI / 360));
+ double a, b, c, d;
+
+ a = cos_a * (w-1);
+ b = -sin_a * (w-1);
+ c = -sin_a * (h-1);
+ d = -cos_a * (h-1);
+
+ XDrawLine(DISP, currwin.win, globals.gc.high,
+ x, y, x + ROUND(a), y + ROUND(b));
+ XDrawLine(DISP, currwin.win, globals.gc.high,
+ x + ROUND(a), y + ROUND(b),
+ x + ROUND(a + c), y + ROUND(b + d));
+ XDrawLine(DISP, currwin.win, globals.gc.high,
+ x + ROUND(a + c), y + ROUND(b + d),
+ x + ROUND(c), y + ROUND(d));
+ XDrawLine(DISP, currwin.win, globals.gc.high,
+ x + ROUND(c), y + ROUND(d), x, y);
+#endif
+ }
+}
+
+/* Process EPSF specials */
+
+static Boolean
+epsf_special(char *cp)
+{
+ char *filename;
+ unsigned filename_len;
+ char *p;
+ int n;
+ unsigned long flags = 0, u;
+ double rwi, rhi;
+ enum { LLX, LLY,
+ URX, URY,
+ RWI, RHI,
+ HSIZE, VSIZE,
+ HOFFSET, VOFFSET,
+ HSCALE, VSCALE,
+ ANGLE
+ };
+ XDviImageInfo info;
+
+ struct {
+ double val;
+ const char *key;
+ long mask;
+ } key_info[] = {
+ { 0, "llx", 0x0001 },
+ { 0, "lly", 0x0002 },
+ { 0, "urx", 0x0004 },
+ { 0, "ury", 0x0008 },
+ { 0, "rwi", 0x0010 },
+ { 0, "rhi", 0x0020 },
+ { 0, "hsize", 0x0040 },
+ { 0, "vsize", 0x0080 },
+ { 0, "hoffset", 0x0100 },
+ { 0, "voffset", 0x0200 },
+ { 0, "hscale", 0x0400 },
+ { 0, "vscale", 0x0800 },
+ { 0, "angle", 0x1000 },
+ };
+
+ memset(&info, 0, sizeof info); /* for validating memcmp(&info, ... */
+
+ filename = cp;
+ if (*cp == '\'' || *cp == '"') {
+ do
+ ++cp;
+ while (*cp != '\0' && *cp != *filename);
+ filename_len = cp - filename - 1;
+ if (*cp == *filename)
+ ++cp;
+ ++filename;
+ }
+ else {
+ while (*cp != '\0' && !isspace((int)*cp))
+ ++cp;
+ filename_len = cp - filename;
+ }
+
+ if (filename_len + 1 > MaxTextExtent) {
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: filename too long, passing to psgs.c\n");
+ return False;
+ }
+
+ memcpy(info.filename, filename, filename_len); /* no need to terminate because of memset(...) above */
+
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: epsf_special: filename = '%s'\n",
+ info.filename);
+
+ /* Scan for EPSF keywords */
+ while (*cp != '\0') {
+ while (isspace((int)*cp))
+ ++cp;
+ p = cp;
+ while (*p != '=' && !isspace((int)*p) && *p != '\0')
+ p++;
+ n = p - cp;
+ if (!n)
+ break;
+ if (*p == '=')
+ ++p;
+ while (isspace((int)*p))
+ ++p;
+
+ if (!memcmp(cp, "clip", n)) {
+ /* this is the only key without value */
+ info.clip = True;
+ }
+ else {
+ size_t i;
+ Boolean found = False;
+ for (i = 0; !found && i < XtNumber(key_info); i++) {
+ if (memicmp(cp, key_info[i].key, n) == 0) {
+ if (sscanf(p, "%lf", &(key_info[i].val)) == 1) {
+ flags |= key_info[i].mask;
+ while (!isspace((int)*p) && *p != '\0')
+ ++p;
+ found = True;
+ }
+ else {
+ fprintf(stderr, "Magick: invalid value for %s\n", key_info[i].key);
+ return False;
+ }
+ }
+ }
+
+ if (!found) {
+ i = *(cp + n);
+ *(cp + n) = '\0';
+ fprintf(stderr,"Magick: ignoring unknown EPSF key \"%s\".\n", cp);
+ *(cp + n) = i;
+ /* skip '=...' */
+ while (*p != '\0' && !isspace((int)*p))
+ ++p;
+ }
+ }
+ cp = p;
+ }
+
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: epsf_special: flags = 0x%lx\n", flags);
+
+ /* only accept the special when llx, lly, urx, ury are properly defined */
+ u = key_info[LLX].mask | key_info[LLY].mask |
+ key_info[URX].mask | key_info[URY].mask;
+ if ((flags & u) != u)
+ return False;
+
+#ifdef __GNUC__
+#warning keywords to handle: [hv]size, [hv]offset, [hv]scale, angle \
+ (ZLB: not used by the graphicx/graphics packages)
+#endif
+ if (flags & (key_info[HSIZE].mask | key_info[VSIZE].mask |
+ key_info[HOFFSET].mask | key_info[VOFFSET].mask |
+ key_info[HSCALE].mask | key_info[VSCALE].mask |
+ key_info[ANGLE].mask))
+ fprintf(stderr, "Magick: warning: EPSF keywords '[hv]size', "
+ "'[hv]offset', '[hv]scale', and 'angle' are not "
+ "implemented.\n");
+
+ info.urx = key_info[URX].val;
+ info.ury = key_info[URY].val;
+ info.llx = key_info[LLX].val;
+ info.lly = key_info[LLY].val;
+ rwi = key_info[RWI].val;
+ rhi = key_info[RHI].val;
+
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: epsf_special: llx=%0.2f, lly=%0.2f, "
+ "urx=%0.2f, ury=%0.2f\n",
+ info.llx, info.lly, info.urx, info.ury);
+
+ if (info.urx - info.llx < 1 || info.ury - info.lly < 1)
+ return False;
+ if (info.urx - info.llx > 1e+4 || info.ury - info.lly > 1e+4)
+ return False;
+ if (fabs(info.llx) > 1e+10 || fabs(info.lly) > 1e+10)
+ return False;
+
+ if (!(flags & (key_info[RWI].mask | key_info[RHI].mask))) {
+ /* both rwi and rhi undefined */
+ rwi = 10 * (info.urx - info.llx);
+ rhi = 10 * (info.ury - info.lly);
+ }
+ else if (!(flags & key_info[RWI].mask)) {
+ /* rwi undefined, rhi defined */
+ rwi = rhi * (info.urx - info.llx) / (info.ury - info.lly);
+ }
+ else if (!(flags & key_info[RHI].mask)) {
+ /* rhi undefined, rwi defined */
+ rhi = rwi * (info.ury - info.lly) / (info.urx - info.llx);
+ }
+
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: epsf_special: rwi=%f, rhi=%f\n", rwi, rhi);
+
+ if (rwi < 1 || rhi < 1)
+ return False;
+
+ /* We don't want IM to process large images (>16MB) */
+ if (DPI * rwi / 720 * DPI * rhi / 720 * G_depth / 8 / 1024 / 1024 > 16) {
+ p = filename + (n = filename_len);
+ if ((n >= 3 && !memicmp(p - 3, ".ps", 3)) ||
+ (n >= 4 && !memicmp(p - 4, ".eps", 4))) {
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: image too large, passing to psgs.c\n");
+ return False;
+ }
+ else {
+ /* don't pass non-PS files to psgs.c */
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: image too large, ignored.\n");
+ return True;
+ }
+ }
+
+ info.rwi = ROUND(rwi);
+ info.rhi = ROUND(rhi);
+
+ render_image_file(&info, PXL_H - currwin.base_x, PXL_V - currwin.base_y);
+
+ return True;
+}
+
+static char *
+emgraph_get_dimen(char *cp, double *v)
+{
+ char *p;
+ typedef struct {
+ int name_len;
+ char *name;
+ double factor;
+ } Unit;
+ Unit *u;
+ /* table for converting an arbitrary unit to bp */
+#define UNIT(u) sizeof(u)-1, u
+ static Unit units[] = {
+ { UNIT("bp"), 1.0 },
+ { UNIT("in"), 72.0 },
+ { UNIT("cm"), 72.0 / 2.54 },
+ { UNIT("mm"), 72.0 / 2.54 * 0.1 },
+ { UNIT("pt"), 72.0 / 72.27 },
+ { UNIT("sp"), 72.0 / 72.27 / 65536.0 },
+ { UNIT("pc"), 12.0 * 72.0 / 72.27 },
+ { UNIT("dd"), 1238.0 / 1157.0 * 72.0 / 72.27 },
+ { UNIT("cc"), 12.0 * 1238.0 / 1157.0 * 72.0 / 72.27 }
+ };
+
+ *v = strtod(cp, &p);
+ if (p == NULL) {
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: can't get dimension, image ignored.\n");
+ return NULL;
+ }
+
+ while (isspace((int)*p))
+ ++p;
+ for (u = units; u < units + XtNumber(units); u++)
+ if (!memicmp(p, u->name, u->name_len))
+ break;
+ if (u >= units + XtNumber(units)) {
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: unknown unit, image ignored.\n");
+ return NULL;
+ }
+ *v *= u->factor;
+
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: unit=%s, len=%d, factor=%0.4f, value=%0.4f\n",
+ u->name, u->name_len, u->factor, *v);
+
+ if (*v < 0.01 || *v > 10000) {
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: invalid dimension, image ignored.\n");
+ return NULL;
+ }
+
+ return p + u->name_len;
+}
+
+static Boolean
+emgraph_special(char *cp)
+{
+ char *p;
+ double w, h;
+ XDviImageInfo info;
+
+ memset(&info, 0, sizeof info);
+
+ /* get filename */
+ while (isspace((int)*cp))
+ ++cp;
+ p = cp;
+ while (!isspace((int)*p) && *p != '\0' && *p != ',')
+ ++p;
+ if ((unsigned)(p - cp) >= sizeof info.filename) {
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: filename too long, image ignored.\n");
+ return False;
+ }
+ if (p <= cp) {
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: empty filename, image ignored.\n");
+ return False;
+ }
+ memcpy(info.filename, cp, p - cp); /* no need to terminate because of memset(...) above */
+
+ cp = p;
+
+ /* get width */
+ if ((cp = strchr(cp, ',')) == NULL) {
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: width undefined, image ignored.\n");
+ return False;
+ }
+ if ((cp = emgraph_get_dimen(++cp, &w)) == NULL)
+ return False;
+
+ /* get height */
+ if ((cp = strchr(cp, ',')) == NULL) {
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: height undefined, image ignored.\n");
+ return False;
+ }
+ if ((cp = emgraph_get_dimen(++cp, &h)) == NULL)
+ return False;
+
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: filename=\"%s\", width=%0.2f, height=%0.2f\n",
+ info.filename, w, h);
+
+ info.llx = info.lly = 0;
+ info.urx = w;
+ info.ury = h;
+ info.rwi = ROUND(10 * w);
+ info.rhi = ROUND(10 * h);
+
+ if (info.rwi < 1 || info.rhi < 1)
+ return False;
+
+ render_image_file(&info, PXL_H - currwin.base_x,
+ PXL_V - currwin.base_y + ROUND(h / 72 * DPI) - 1);
+
+ return True;
+}
+
+/* Filter specials. Returns True if the special has been processed
+ * by ImageMagick, False otherwise. */
+Boolean
+Magick_parse_special(char *cp)
+{
+ switch (*cp) {
+ case 'p':
+ case 'P':
+ if (!memicmp(cp, "psfile", 6)) {
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: parsing string |%s|\n", cp);
+ cp += 6;
+ while (isspace((int)*cp))
+ ++cp;
+ if (*cp != '=')
+ return False;
+ do
+ ++cp;
+ while (isspace((int)*cp));
+ return epsf_special(cp);
+ }
+ break;
+ case 'e':
+ case 'E':
+ if (!memicmp(cp, "em:", 3)) {
+ cp += 3;
+ while (isspace((int)*cp))
+ ++cp;
+ if (memicmp(cp, "graph", 5))
+ return False;
+ cp += 5;
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "Magick: parsing string |em:graph %s|\n", cp);
+ return emgraph_special(cp);
+ }
+ break;
+ }
+
+ return False;
+}
+
+#else
+/* silence `empty compilation unit' warnings */
+static void bar(void); static void foo() { bar(); } static void bar(void) { foo(); }
+#endif /* MAGICK */