Initial release of Maemo 5 port of gnuplot
[gnuplot] / term / gd.trm
diff --git a/term/gd.trm b/term/gd.trm
new file mode 100644 (file)
index 0000000..1cb7773
--- /dev/null
@@ -0,0 +1,2576 @@
+/* Hello, Emacs, this is -*-C-*-
+ * $Id: gd.trm,v 1.95.2.16 2009/03/03 02:43:52 sfeam Exp $
+ * based on gif.trm,v 1.26.2.1 2000/05/01 00:17:20 joze
+ */
+
+/* GNUPLOT -- gd.trm */
+
+/*[
+ * Copyright 1998, 2001, 2004
+ *
+ * Permission to use, copy, and distribute this software and its
+ * documentation for any purpose with or without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation.
+ *
+ * Permission to modify the software is granted, but not the right to
+ * distribute the complete modified source code.  Modifications are to
+ * be distributed as patches to the released version.  Permission to
+ * distribute binaries produced by compiling modified sources is granted,
+ * provided you
+ *   1. distribute the corresponding source modifications from the
+ *    released version in the form of a patch file along with the binaries,
+ *   2. add special version identification to distinguish your version
+ *    in addition to the base release version number,
+ *   3. provide your name and address as the primary contact for the
+ *    support of your modified version, and
+ *   4. retain our contact information in regard to use of the base
+ *    software.
+ * Permission to distribute the released version of the source code along
+ * with corresponding source modifications in the form of a patch file is
+ * granted with same provisions 2 through 4 for binary distributions.
+ *
+ * This software is provided "as is" without express or implied warranty
+ * to the extent permitted by applicable law.
+]*/
+
+/*
+ * This file is included by ../term.c.
+ *
+ * This terminal driver supports PNG and JPEG output using
+ *  GD library 1.8, 2.0
+ *
+ * To Use:
+ *
+ * set terminal png ?options ...?
+ *
+ * Where an option is:
+ *
+ * transparent - generate transparent PNGs.  The first color will
+ * be the transparent one.
+ *
+ * interlace - generate interlaced PNGs.
+ *
+ * image size (in pixels)
+ *
+ * font size (tiny,small,medium,large,giant)
+ *
+ * font name (TrueType or Adobe Type 1 font name is passed to libgd)
+ *
+ * xrrggbb - sets the next color.  x is the literal character 'x',
+ * rrggbb are the red green and blue components in hex.  For example
+ * x00ff00 is green.  The background color is set first, then the
+ * color borders, then the X & Y axis, then the plotting colors.
+ * (The wierd color spec is in order to get around limitations
+ * in gnuplot's scanner.)
+ *
+ * This driver is modeled after the PBM driver pbm.trm.
+ *
+ * AUTHORS
+ *  Sam Shen <sls@mh1.lbl.gov>
+ *  Alex Woo <woo@playfair.stanford.edu>
+ *  Ethan A Merritt <merritt@u.washington.edu>
+ *
+ * CONTRIBUTORS
+ *  Alfred Reibenschuh <alfred.reibenschuh@it-austria.com> or <fredo@blackbox.at>
+ *  Ben Laurie <ben@algroup.co.uk>
+ *
+ * This version outputs either color or monochrome PNGs.
+ * The default is 640x480 pixels.
+ *
+ ******************************************************************************
+ * PLEASE READ                                                                *
+ * This driver uses the gd library, available from http://www.boutell.com/gd/ *
+ * Earlier versions of the gd library produced GIF images, but starting with  *
+ * version 1.6 the gd library no longer supports creation of GIF images due   *
+ * to licensing issues. Hence this modified driver, which uses the old gd/gif *
+ * code to produce PNG images instead.                                        *
+ *                                                                            *
+ * requires:                                                                  *
+ *      libgd version 1.8 or greater     http://www.boutell.com/gd/           *
+ *      libfreetype version 2            http://www.freetype.org/             *
+ *                                                                            *
+ * This driver allows you to use TrueType fonts.  You can use this driver     *
+ * without having any TrueType fonts installed, but the default fonts are     *
+ * comparatively limited.                                                     *
+ ******************************************************************************
+ *
+ * Petr Mikulik, Jan 1999: terminal entries for PM3D functionality
+ * Ethan Merritt, May 2001: modified gd/gif driver to produce png instead;
+ *                          added support for line width and TrueType fonts
+ */
+
+#include "driver.h"
+
+#ifdef TERM_REGISTER
+register_term(png)
+#endif
+
+#ifdef TERM_PROTO
+TERM_PUBLIC void PNG_options __PROTO((void));
+TERM_PUBLIC void PNG_init __PROTO((void));
+TERM_PUBLIC void PNG_graphics __PROTO((void));
+TERM_PUBLIC void PNG_text __PROTO((void));
+TERM_PUBLIC void PNG_linetype __PROTO((int linetype));
+TERM_PUBLIC void PNG_linewidth __PROTO((double linewidth));
+TERM_PUBLIC void PNG_move __PROTO((unsigned int x, unsigned int y));
+TERM_PUBLIC void PNG_vector __PROTO((unsigned int x, unsigned int y));
+TERM_PUBLIC void PNG_put_text __PROTO((unsigned int x, unsigned int y, const char str[]));
+TERM_PUBLIC int PNG_justify_text __PROTO((enum JUSTIFY mode));
+TERM_PUBLIC void PNG_point __PROTO((unsigned int x, unsigned int y, int number));
+TERM_PUBLIC int PNG_text_angle __PROTO((int ang));
+TERM_PUBLIC void PNG_reset __PROTO((void));
+TERM_PUBLIC int PNG_set_font __PROTO((const char *fontname));
+TERM_PUBLIC void PNG_pointsize __PROTO((double ptsize));
+TERM_PUBLIC void PNG_boxfill(int, unsigned int, unsigned int, unsigned int, unsigned int);
+TERM_PUBLIC int PNG_make_palette (t_sm_palette *);
+/* TERM_PUBLIC void PNG_previous_palette (void); */
+TERM_PUBLIC void PNG_set_color (t_colorspec *);
+TERM_PUBLIC void PNG_filled_polygon (int, gpiPoint *);
+#ifdef WITH_IMAGE
+TERM_PUBLIC void PNG_image __PROTO((unsigned, unsigned, coordval *, gpiPoint *, t_imagecolor));
+#endif
+
+/* To support "set term png enhanced" */
+TERM_PUBLIC void ENHGD_put_text __PROTO((unsigned int x, unsigned int y, const char str[]));
+TERM_PUBLIC void ENHGD_OPEN __PROTO((char * fontname, double fontsize,
+                       double base, TBOOLEAN widthflag, TBOOLEAN showflag,
+                       int overprint));
+TERM_PUBLIC void ENHGD_FLUSH __PROTO((void));
+
+
+#include "gd.h"
+
+#if defined(WIN32) && !defined(NONDLL)
+  /* static font pointers are recommended when using bgd.dll */
+# ifndef GD_NEED_LOCAL_FONT_POINTERS
+#  define GD_NEED_LOCAL_FONT_POINTERS
+# endif
+#endif
+
+#ifdef GD_NEED_LOCAL_FONT_POINTERS
+# include "gdfonts.h"
+# include "gdfontl.h"
+# include "gdfontmb.h"
+# include "gdfontt.h"
+# include "gdfontg.h"
+#endif
+
+/* This is required for the shared library version of libgd on Windows.
+   Newer versions of libgd (>=2.0.24 ?) already define it. */
+#ifndef BGD_EXPORT_DATA_PROT 
+# define BGD_EXPORT_DATA_PROT extern
+#endif
+
+/* These intermediate functions are necessary on Windows since 
+   the shared version of libgd uses a different calling convention
+   and there is no proper macro defined.
+*/
+#if defined(WIN32) && !defined(NONDLL)
+static void gp_gdImagePolygon(gdImagePtr, gdPointPtr, int, int);
+static void gp_gdImageFilledPolygon(gdImagePtr, gdPointPtr, int, int);
+#else
+# define gp_gdImagePolygon gdImagePolygon
+# define gp_gdImageFilledPolygon gdImageFilledPolygon 
+#endif
+
+static void PNG_PointX __PROTO((unsigned int, unsigned int));
+static void PNG_PointPlus __PROTO((unsigned int, unsigned int));
+static void PNG_Triangle(unsigned int x, unsigned int y, int direction,
+       void (*draw_func)(gdImagePtr, gdPointPtr, int, int));
+static void PNG_Diamond(unsigned int x, unsigned int y,
+       void (*draw_func)(gdImagePtr, gdPointPtr, int, int));
+
+#ifndef GD_NEED_LOCAL_FONT_POINTERS
+BGD_EXPORT_DATA_PROT gdFontPtr gdFontSmall;    /* 6x12 */
+BGD_EXPORT_DATA_PROT gdFontPtr gdFontLarge;    /* 8x16 */
+BGD_EXPORT_DATA_PROT gdFontPtr gdFontMediumBold;       /* 7x13 */
+BGD_EXPORT_DATA_PROT gdFontPtr gdFontGiant;  /* 9x15 */
+BGD_EXPORT_DATA_PROT gdFontPtr gdFontTiny;  /* 5x8 */
+#else
+static gdFontPtr gdFontSmall;  /* 6x12 */
+static gdFontPtr gdFontLarge;  /* 8x16 */
+static gdFontPtr gdFontMediumBold;     /* 7x13 */
+static gdFontPtr gdFontGiant;  /* 9x15 */
+static gdFontPtr gdFontTiny;  /* 5x8 */
+#endif
+
+#define GREG_XMAX 640
+#define GREG_YMAX 480
+
+/* This will be the default font */
+# define gdfont gdFontMediumBold
+# define PNG_VCHAR 13
+# define PNG_HCHAR 7
+
+#define PNG_TICSIZE (GREG_YMAX/100)
+
+#define PNG_MAX_COLORS 256
+#define GOT_NEXT_PROTO
+#endif
+
+#ifndef TERM_PROTO_ONLY
+#ifdef TERM_BODY
+
+static TBOOLEAN PNG_initialized = FALSE;       /* Set when terminal first initialized */
+
+static struct {
+    gdImagePtr image;
+    gdFontPtr font;
+    unsigned int x, y;
+    int height;
+    int charh, charw;
+    int color;             /* Magic index returned by libgd */
+    int rgb;               /* Our guess at the corresponding rgb */
+    int n_colors;
+    int color_table[PNG_MAX_COLORS];
+    int rgb_table[PNG_MAX_COLORS];
+    int angle;
+    enum JUSTIFY justify;
+    int flags;
+    int linetype;
+    int linewidth;
+    TBOOLEAN capbutt;  /* use capbutt on lines with GD2, 20051205 MWS*/
+    int ttfsize;
+    char *ttffont;
+    gdFontPtr default_font;
+    char *    default_ttffont;
+    int       default_ttfsize;
+    TBOOLEAN  TrueColor;
+    /* Variables for animated gif support: */
+    TBOOLEAN  animate;         /* Only gif supports animation */
+    int       loop_count;      /* Number of times to repeat sequence */
+    int       frame_count;     /* Number of frames in animation */
+    int       frame_delay;     /* Time between frames in .01 seconds */
+    TBOOLEAN  frame_optimization;
+    gdImagePtr previous_image; /* Needed to encode animation as a series of deltas */
+} png_state;
+
+#define PNG_USE_TRANSPARENT 1
+#define PNG_USE_INTERLACE   2
+#define PNG_USE_CROP        4
+
+enum PNG_id {
+    PNG_TRANSPARENT, PNG_NOTRANSPARENT,
+    PNG_INTERLACE, PNG_NOINTERLACE,
+    PNG_CROP, PNG_NOCROP,
+    /* Font size */
+    PNG_TINY, PNG_SMALL, PNG_MEDIUM, PNG_LARGE, PNG_GIANT,
+    PNG_FONT,
+    PNG_SIZE,
+    PNG_ENHANCED, PNG_NOENHANCED,
+    PNG_TRUECOLOR, PNG_NOTRUECOLOR,
+    PNG_LINEWIDTH, PNG_BUTT, PNG_ROUNDED,
+    GIF_ANIMATE, GIF_DELAY, GIF_LOOP, GIF_NOOPT, GIF_OPT,
+    PNG_OTHER
+};
+
+/* Only needed for dubious backwards compatibility with 'set size'
+ * in pre-4.0 versions that didn't support 'set term size'
+ */
+static TBOOLEAN PNG_explicit_size = FALSE;
+
+
+#ifdef Y
+#   undef Y
+#endif
+#define Y(y) (png_state.height - (y))
+
+static int PNG_XMAX = GREG_XMAX;
+static int PNG_YMAX = GREG_YMAX;
+static const int PNG_POINT_SCALE = 3;
+static int PNG_ps = 3;
+
+static struct gen_table PNG_opts[] =
+{
+    { "trans$parent", PNG_TRANSPARENT },
+    { "notran$sparent", PNG_NOTRANSPARENT },
+    { "inter$lace", PNG_INTERLACE },
+    { "nointer$lace", PNG_NOINTERLACE },
+    { "crop", PNG_CROP },
+    { "nocrop", PNG_NOCROP },
+    { "ti$ny", PNG_TINY },
+    { "s$mall", PNG_SMALL },
+    { "m$edium", PNG_MEDIUM },
+    { "l$arge", PNG_LARGE },
+    { "g$iant", PNG_GIANT },
+    { "fo$nt", PNG_FONT },
+    { "si$ze", PNG_SIZE },
+    { "enh$anced", PNG_ENHANCED },
+    { "noenh$anced", PNG_NOENHANCED },
+    { "true$color", PNG_TRUECOLOR },
+    { "notrue$color", PNG_NOTRUECOLOR },
+    { "linew$idth", PNG_LINEWIDTH },
+    { "anim$ate", GIF_ANIMATE }, /* gif animation options */
+    { "delay", GIF_DELAY },
+    { "loop", GIF_LOOP },
+    { "noopt$imize", GIF_NOOPT },
+    { "opt$imize", GIF_OPT }, /* end of gif animation options */
+    { "lw", PNG_LINEWIDTH },
+    { "butt", PNG_BUTT},
+    { "round$ed", PNG_ROUNDED},
+    { NULL, PNG_OTHER }
+};
+
+#undef MAXLINEWIDTH
+#define MAXLINEWIDTH 12
+static double PNG_linewidth_factor = 1.0;
+
+/* EAM - gdImage structure to hold brushes for linewidth */
+/* We only use brushes 2 through MAXLINEWIDTH            */
+typedef struct {
+    gdImagePtr im;
+    unsigned int last_rgb;
+    int bgnd;
+    int fgnd;
+}   PNG_BRUSH;
+
+static PNG_BRUSH PNG_brush[MAXLINEWIDTH+1];
+
+typedef struct {
+    gdImagePtr im;
+    unsigned int last_rgb;
+    int fillpar;
+}   PNG_FILL_TILE;
+
+static PNG_FILL_TILE PNG_fill_tile = { (gdImagePtr)0, 0, 0 };
+
+/* To be used with libgd 2.0.34 to request Symbol encoding */
+#ifdef gdFTEX_Adobe_Custom
+static gdFTStringExtra PNG_FONT_INFO = {0,0,0,0,0,NULL,NULL};
+#endif
+
+#if defined(WIN32) && !defined(NONDLL)
+static void 
+gp_gdImagePolygon(gdImagePtr im, gdPointPtr p, int n, int c)
+{
+    gdImagePolygon(im, p, n, c);
+}
+
+static void 
+gp_gdImageFilledPolygon(gdImagePtr im, gdPointPtr p, int n, int c)
+{
+    gdImageFilledPolygon(im, p, n, c);
+}
+#endif
+
+
+/* Common code to crop the image around its bounding box, just before writing
+   down the file.
+*/
+static void
+image_do_crop ()
+{
+    if (png_state.flags & PNG_USE_CROP) {
+       int x, y, x1, y1, x2, y2, flag;
+       int bg = png_state.color_table[0]; /* index of the background color */
+       gdImagePtr im_crop;
+       for (flag=0, x1=0; x1 < gdImageSX(png_state.image)-1; x1++) {
+           for (y=0; y < gdImageSY(png_state.image); y++)
+               if (gdImageGetPixel(png_state.image, x1, y) != bg) { flag = 1; break; }
+           if (flag) break;
+       }
+       for (flag=0, x2=gdImageSX(png_state.image)-1; x2 >= x1; x2--) {
+           for (y=0; y < gdImageSY(png_state.image); y++)
+               if (gdImageGetPixel(png_state.image, x2, y) != bg) { flag = 1; break; }
+           if (flag) break;
+       }
+       for (flag=0, y1=0; y1 < gdImageSY(png_state.image)-1; y1++) {
+           for (x=x1; x <= x2; x++)
+               if (gdImageGetPixel(png_state.image, x, y1) != bg) { flag = 1; break; };
+           if (flag) break;
+       }
+       for (flag=0, y2=gdImageSY(png_state.image)-1; y2 >= y1; y2--) {
+           for (x=x1; x <= x2; x++)
+               if (gdImageGetPixel(png_state.image, x, y2) != bg) { flag = 1; break; };
+           if (flag) break;
+       }
+       x = x2 - x1 + 1; /* width */
+       y = y2 - y1 + 1; /* height */
+#if (GD2_VERS >= 2)
+       if (png_state.TrueColor)
+           im_crop = gdImageCreateTrueColor(x,y);
+       else
+           im_crop = gdImageCreate(x,y);
+       if (!im_crop) {
+           int_warn(NO_CARET,"libgd: failed to create cropped image structure");
+           return;
+       }
+       bg = gdImageColorAllocateAlpha(im_crop,255,255,255,127);
+#else
+       im_crop = gdImageCreate(x,y);
+#endif
+
+       gdImagePaletteCopy(im_crop, png_state.image);
+       if (png_state.flags & PNG_USE_TRANSPARENT) {
+           gdImageColorTransparent(im_crop, bg);
+           /* WARNING: This is a work-around for strangeness in libgd,  */
+           /* which doesn't copy transparent pixels in TrueColor images. */
+           if (png_state.TrueColor)
+               gdImageColorTransparent(png_state.image, -1);
+       } else
+           gdImageColorTransparent(im_crop, -1);
+
+       gdImageCopy(im_crop, png_state.image, 0, 0, x1, y1, x, y);
+       gdImageDestroy(png_state.image);
+       png_state.image = im_crop;
+    }
+}
+
+
+static int PNG_FillSolid __PROTO((int fillpar));
+static int PNG_FillPattern __PROTO((int fillpar));
+
+static int
+PNG_FillSolid(int fillpar)
+{
+    int red   = (png_state.rgb >> 16) & 0xff;
+    int green = (png_state.rgb >> 8) & 0xff;
+    int blue  = png_state.rgb & 0xff;
+
+    double fact = (double)(100 - fillpar) * 0.01;
+
+    int color;
+
+    if (fact <= 0 || fact >= 1.0)
+       return png_state.color;
+
+    red   += (0xff - red) * fact;
+    green += (0xff - green) * fact;
+    blue  += (0xff - blue) * fact;
+
+    color = gdImageColorExact(png_state.image, red, green, blue);
+    if (color < 0) {
+       color = gdImageColorAllocate(png_state.image, red, green, blue);
+    }
+    if (color < 0) {
+       color = gdImageColorClosest(png_state.image, red, green, blue);
+    }
+
+    return color;
+}
+
+static int
+PNG_FillPattern(int fillpar)
+{
+    int rgb = png_state.rgb;
+    fillpar %= 8;
+
+    if (!PNG_fill_tile.im || rgb != PNG_fill_tile.last_rgb || PNG_fill_tile.fillpar != fillpar) {
+
+       int foreground, background;
+
+       if (PNG_fill_tile.im) {
+           gdImageDestroy(PNG_fill_tile.im);
+           PNG_fill_tile.im = (gdImagePtr)0;
+       }
+
+       /* save new values */
+       PNG_fill_tile.fillpar = fillpar;
+       PNG_fill_tile.last_rgb = rgb;
+
+       /* create new tile */
+       PNG_fill_tile.im = gdImageCreate(8, 8);
+       if (!PNG_fill_tile.im)
+           int_error(NO_CARET,"libgd: failed to create pattern-fill tile");
+
+       /* background */
+       background = gdImageColorAllocate(PNG_fill_tile.im, 255, 255, 255);
+       /* gdImageColorTransparent(PNG_fill_tile.im, background); */
+       gdImageFilledRectangle(PNG_fill_tile.im, 0, 0, 7, 7, background);
+
+       /* foreground */
+       foreground = gdImageColorAllocate(PNG_fill_tile.im,
+               (rgb >> 16) & 0xff, (rgb >> 8) & 0xff, rgb & 0xff);
+
+       switch (fillpar) {
+           case 0: /* no fill */
+           default:
+               break;
+           case 1: /* cross-hatch */
+               gdImageLine(PNG_fill_tile.im, 0, 0, 7, 7, foreground);
+               gdImageLine(PNG_fill_tile.im, 0, 6, 6, 0, foreground);
+               break;
+           case 2: /* double cross-hatch */
+               gdImageLine(PNG_fill_tile.im, 0, 0, 7, 7, foreground);
+               gdImageLine(PNG_fill_tile.im, 0, 6, 6, 0, foreground);
+               gdImageLine(PNG_fill_tile.im, 0, 2, 2, 0, foreground);
+               gdImageLine(PNG_fill_tile.im, 7, 3, 3, 7, foreground);
+               gdImageLine(PNG_fill_tile.im, 4, 0, 7, 3, foreground);
+               gdImageLine(PNG_fill_tile.im, 0, 4, 3, 7, foreground);
+               break;
+           case 3: /* solid */
+               gdImageFilledRectangle(PNG_fill_tile.im, 0, 0, 7, 7, foreground);
+               break;
+           case 4:
+               gdImageLine(PNG_fill_tile.im, 0, 0, 7, 7, foreground);
+               break;
+           case 5:
+               gdImageLine(PNG_fill_tile.im, 0, 7, 7, 0, foreground);
+               break;
+           case 6:
+               gdImageLine(PNG_fill_tile.im, 0, 0, 3, 7, foreground);
+               gdImageLine(PNG_fill_tile.im, 4, 0, 7, 7, foreground);
+               break;
+           case 7:
+               gdImageLine(PNG_fill_tile.im, 0, 7, 3, 0, foreground);
+               gdImageLine(PNG_fill_tile.im, 4, 7, 7, 0, foreground);
+               break;
+           case 8:
+               gdImageLine(PNG_fill_tile.im, 0, 0, 7, 3, foreground);
+               gdImageLine(PNG_fill_tile.im, 0, 4, 7, 7, foreground);
+               break;
+           case 9:
+               gdImageLine(PNG_fill_tile.im, 0, 3, 7, 0, foreground);
+               gdImageLine(PNG_fill_tile.im, 0, 7, 7, 4, foreground);
+               break;
+       }
+    }
+
+    gdImageSetTile(png_state.image, PNG_fill_tile.im);
+    return (int)gdTiled;
+}
+
+static void
+PNG_PointX(unsigned int x, unsigned int y)
+{
+    gdImageLine(png_state.image, x - PNG_ps, y - PNG_ps,
+           x + PNG_ps, y + PNG_ps, png_state.color);
+    gdImageLine(png_state.image, x + PNG_ps, y - PNG_ps,
+           x - PNG_ps, y + PNG_ps, png_state.color);
+}
+
+static void
+PNG_PointPlus(unsigned int x, unsigned int y)
+{
+    gdImageLine(png_state.image, x - PNG_ps, y,
+           x + PNG_ps, y, png_state.color);
+    gdImageLine(png_state.image, x, y - PNG_ps,
+           x, y + PNG_ps, png_state.color);
+}
+
+static void
+PNG_Triangle(
+    unsigned int x, unsigned int y,
+    int direction,
+    void (*draw_func)(gdImagePtr, gdPointPtr, int, int))
+{
+    int delta  = (int)((1.33 * (double)PNG_ps) + 0.5);
+    int delta_ = (int)((0.67 * (double)PNG_ps) + 0.5);
+
+    gdPoint points[4];
+    points[0].x = x;
+    points[0].y = y - direction * delta;
+    points[1].x = x - delta;
+    points[1].y = y + direction * delta_;
+    points[2].x = x + delta;
+    points[2].y = y + direction * delta_;
+    points[3].x = points[0].x;
+    points[3].y = points[0].y;
+    draw_func(png_state.image, points, 4, png_state.color);
+}
+
+static void
+PNG_Diamond(
+    unsigned int x, unsigned int y,
+    void (*draw_func)(gdImagePtr, gdPointPtr, int, int))
+{
+    gdPoint points[5];
+    points[0].x = x;
+    points[0].y = y - PNG_ps;
+    points[1].x = x + PNG_ps;
+    points[1].y = y;
+    points[2].x = x;
+    points[2].y = y + PNG_ps;
+    points[3].x = x - PNG_ps;
+    points[3].y = y;
+    points[4].x = points[0].x;
+    points[4].y = points[0].y;
+    draw_func(png_state.image, points, 5, png_state.color);
+}
+
+/*
+ * _options()  Called when terminal type is selected.
+ * This procedure should parse options on the command line.  A list of the
+ * currently selected options should be stored in term_options[] in a form
+ * suitable for use with the set term command.  term_options[] is used by
+ * the save command.  Use options_null() if no options are available.
+ */
+TERM_PUBLIC void
+PNG_options()
+{
+    struct value s;
+    int i;
+    char *string;
+    unsigned long color;
+    TBOOLEAN new_colors = FALSE;
+    TBOOLEAN gif_anim_option = FALSE; /* set to TRUE if an animated gif option given */
+
+    if (!PNG_initialized) {
+       PNG_initialized = TRUE;
+       term_options[0] = '\0';
+       term->h_char = PNG_HCHAR; /* Default to medium font */
+       png_state.default_font = gdfont;
+       png_state.n_colors = 0;
+       png_state.flags = 0;
+       png_state.ttffont = NULL;
+       png_state.default_ttffont = NULL;
+       png_state.default_ttfsize = 0;
+       png_state.justify = CENTRE;
+       png_state.TrueColor = FALSE;
+       PNG_linewidth_factor = 1.0;
+       png_state.capbutt = FALSE; /* to preserve previous default behavior */
+#ifdef GD_NEED_LOCAL_FONT_POINTERS
+       gdFontSmall = gdFontGetSmall();
+       gdFontLarge = gdFontGetLarge();
+       gdFontMediumBold = gdFontGetMediumBold();
+       gdFontGiant = gdFontGetGiant();
+       gdFontTiny = gdFontGetTiny();
+#endif
+    } else {
+       /* FIXME EAM - these should never happen! */
+       if (!png_state.default_font) {
+           fprintf(stderr,"gd.trm: caught initialization error\n");
+           png_state.default_font = gdfont;
+       }
+    }
+
+    /* Annoying hack to handle the case of 'set termoption' after */
+    /* we are already in animation mode.                          */
+    if (c_token == 2)
+       FPRINTF((stderr,"gif: Maintaining animation state\n"));
+    else {
+       /* Otherwise reset animation parameters */
+       if (png_state.previous_image)
+           gdImageDestroy(png_state.previous_image);
+       png_state.animate = FALSE;
+       png_state.previous_image = NULL;
+       png_state.frame_optimization = FALSE;
+       png_state.loop_count = 0;
+       /* And default font size */
+       term->h_char = PNG_HCHAR;
+       png_state.default_ttfsize = 0;
+    }
+
+    while (!END_OF_COMMAND) {
+       switch(lookup_table(&PNG_opts[0],c_token)) {
+       case PNG_TRANSPARENT:
+           png_state.flags |= PNG_USE_TRANSPARENT;
+           ++c_token;
+           break;
+       case PNG_NOTRANSPARENT:
+           png_state.flags &= ~PNG_USE_TRANSPARENT;
+           ++c_token;
+           break;
+       case PNG_INTERLACE:
+           png_state.flags |= PNG_USE_INTERLACE;
+           ++c_token;
+           break;
+       case PNG_NOINTERLACE:
+           png_state.flags &= ~PNG_USE_INTERLACE;
+           ++c_token;
+           break;
+       case PNG_CROP:
+           png_state.flags |= PNG_USE_CROP;
+           ++c_token;
+           break;
+       case PNG_NOCROP:
+           png_state.flags &= ~PNG_USE_CROP;
+           ++c_token;
+           break;
+       case PNG_TINY:
+#ifdef HAVE_GD_TTF
+# define UNSET_TTF_FONT \
+           free(png_state.ttffont); \
+           png_state.ttffont = NULL; \
+           png_state.default_ttfsize = 2 * term->h_char - 2;
+#else
+# define UNSET_TTF_FONT \
+           ; /* nothing to do */
+#endif
+           png_state.default_font=gdFontTiny;
+           term->v_char = png_state.default_font->h;
+           term->h_char = png_state.default_font->w;
+           ++c_token;
+           UNSET_TTF_FONT;
+           break;
+       case PNG_SMALL:
+           png_state.default_font = gdFontSmall;
+           term->v_char = png_state.default_font->h;
+           term->h_char = png_state.default_font->w;
+           ++c_token;
+           UNSET_TTF_FONT;
+           break;
+       case PNG_MEDIUM:
+           png_state.default_font = gdFontMediumBold;
+           term->v_char = png_state.default_font->h;
+           term->h_char = png_state.default_font->w;
+           ++c_token;
+           UNSET_TTF_FONT;
+           break;
+       case PNG_LARGE:
+           png_state.default_font = gdFontLarge;
+           term->v_char = png_state.default_font->h;
+           term->h_char = png_state.default_font->w;
+           ++c_token;
+           UNSET_TTF_FONT;
+           break;
+       case PNG_GIANT:
+           png_state.default_font=gdFontGiant;
+           term->v_char = png_state.default_font->h;
+           term->h_char = png_state.default_font->w;
+           ++c_token;
+           UNSET_TTF_FONT;
+           break;
+       case PNG_FONT:
+           c_token++;
+#ifdef HAVE_GD_TTF
+           if (END_OF_COMMAND) {
+               free(png_state.ttffont);
+               png_state.ttffont = NULL;
+               png_state.default_ttfsize = 0;
+           } else {
+               int brect[8];
+               char *err;
+
+               if (isstringvalue(c_token)) {
+                   char *s = try_to_get_string();
+                   char *comma = strrchr(s,',');
+                   double fontsize;
+                   if (comma && (1 == sscanf(comma+1,"%lf",&fontsize))) {
+                       png_state.default_ttfsize = (int)(fontsize+0.5);
+                       png_state.ttfsize = png_state.default_ttfsize;
+                       *comma = '\0';
+                   }
+                   if (*s) {
+                       free(png_state.ttffont);
+                       png_state.ttffont = s;
+                   } else {
+                       continue;
+                   }
+               } else {
+                   free(png_state.ttffont);
+                   png_state.ttffont = gp_alloc(token_len(c_token)+1,"new font");
+                   copy_str(png_state.ttffont, c_token, token_len(c_token)+1);
+                   c_token++;
+               }
+               free(png_state.default_ttffont);
+               png_state.default_ttffont = gp_strdup(png_state.ttffont);
+               err = gdImageStringFT(NULL, &brect[0], 0,
+                       png_state.ttffont, (double)png_state.ttfsize,
+                       0.0, 0, 0, "test");
+               if (err) {
+                       fprintf(stderr,
+                               "%s when opening font %s, trying default\n",
+                               err, png_state.ttffont);
+                       free(png_state.ttffont);
+                       free(png_state.default_ttffont);
+                       png_state.ttffont = NULL;
+                       png_state.default_ttffont = NULL;
+               }
+           }
+#else
+           c_token++;
+           fprintf(stderr,"No TTF font support, using internal non-scalable font\n");
+#endif
+           break;
+       case PNG_SIZE:
+           c_token++;
+           if (END_OF_COMMAND) {
+               PNG_XMAX = GREG_XMAX;
+               PNG_YMAX = GREG_YMAX;
+               PNG_explicit_size = FALSE;
+           } else {
+               PNG_XMAX = real(const_express(&s));
+               if (equals(c_token, ",")) {
+                   c_token++;
+                   PNG_YMAX = real(const_express(&s));
+               }
+               if (PNG_XMAX < 0)
+                   PNG_XMAX = GREG_XMAX;
+               if (PNG_YMAX < 0)
+                   PNG_YMAX = GREG_YMAX;
+               PNG_explicit_size = TRUE;
+           }
+           term->ymax = PNG_YMAX;
+           term->xmax = PNG_XMAX;
+           /* EAM Apr 2003 - same tic size on both x and y axes */
+           term->v_tic = (PNG_XMAX < PNG_YMAX) ? PNG_XMAX/100 : PNG_YMAX/100;
+           if (term->v_tic < 1)
+               term->v_tic = 1;
+           term->h_tic = term->v_tic;
+           break;
+       case PNG_ENHANCED:
+           term->flags |= TERM_ENHANCED_TEXT;
+           term->put_text = ENHGD_put_text;
+           ++c_token;
+           break;
+       case PNG_NOENHANCED:
+           term->flags &= ~TERM_ENHANCED_TEXT;
+           term->put_text = PNG_put_text;
+           ++c_token;
+           break;
+       case PNG_TRUECOLOR:
+           png_state.TrueColor = TRUE;
+           c_token++;
+           break;
+       case PNG_NOTRUECOLOR:
+           png_state.TrueColor = FALSE;
+           c_token++;
+           break;
+       case PNG_LINEWIDTH:
+           c_token++;
+           PNG_linewidth_factor = real(const_express(&s));
+           if (PNG_linewidth_factor < 0)
+               PNG_linewidth_factor = 1.0;
+           break;
+
+       /* parse gif animation options */
+       case GIF_ANIMATE:
+           if (strncmp("gif",term->name,3))
+               int_error(c_token,"Only the gif terminal supports animation");
+           c_token++;
+           png_state.animate = TRUE;
+           png_state.frame_count = 0;
+           png_state.frame_delay = 10;
+           png_state.frame_optimization = FALSE;
+           gif_anim_option = 1;
+           break;
+       case GIF_DELAY:
+           if (strncmp("gif",term->name,3))
+               int_error(c_token,"Only the gif terminal supports animation");
+           c_token++;
+           png_state.frame_delay = (int)real(const_express(&s));
+           if (png_state.frame_delay <= 0)
+               png_state.frame_delay = 10;
+           gif_anim_option = 1;
+           break;
+       case GIF_LOOP:
+           if (strncmp("gif",term->name,3))
+               int_error(c_token,"Only the gif terminal supports animation");
+           c_token++;
+           png_state.loop_count = (int)real(const_express(&s));
+           gif_anim_option = 1;
+           break;
+       case GIF_NOOPT:
+           if (strncmp("gif",term->name,3))
+               int_error(c_token,"Only the gif terminal supports animation");
+           c_token++;
+           png_state.frame_optimization = FALSE;
+           gif_anim_option = 1;
+           break;
+       case GIF_OPT:
+           if (strncmp("gif",term->name,3))
+               int_error(c_token,"Only the gif terminal supports animation");
+           c_token++;
+           png_state.frame_optimization = TRUE;
+           gif_anim_option = 1;
+           break;
+
+       case PNG_BUTT:
+           png_state.capbutt = TRUE;
+           c_token++;
+           break;
+
+       case PNG_ROUNDED:
+           png_state.capbutt = FALSE;
+           c_token++;
+           break;
+
+       case PNG_OTHER:
+       default:
+           /* not "size" */
+           string = gp_input_line + token[c_token].start_index;
+
+#ifdef HAVE_GD_TTF
+           /* Check for explicit TTF font size */
+           if (sscanf(string, "%d", &i) == 1) {
+               if (i > 0 && i < 999)
+                   png_state.default_ttfsize = i;
+               else
+                   int_warn(c_token,"illegal font size");
+               ++c_token;
+               break;
+           }
+#endif
+
+           if (sscanf(string, "x%lx", &color) != 1) {
+               int_error(c_token, "invalid color spec, must be xRRGGBB");
+           } else if (png_state.n_colors == PNG_MAX_COLORS && new_colors) {
+               int_warn(c_token, "too many colors, ignoring");
+               ++c_token;
+           } else {
+               if (!new_colors) {
+                   new_colors = TRUE;
+                   png_state.n_colors = 0;
+               }
+               png_state.rgb_table[png_state.n_colors++] = color;
+               ++c_token;
+           }
+           break;
+       }
+    }
+
+#ifndef GIF_ANIMATION /* animated gifs not supported by the current GD library */
+    if (gif_anim_option) {
+       png_state.animate = FALSE;
+       int_warn(NO_CARET, "gif animation options ignored (not compiled into this binary)");
+    }
+#endif
+
+#ifdef HAVE_GD_TTF
+    /* If no font has been chosen but there is a default, use it */
+    if (!png_state.ttffont) {
+       char *external_default = getenv("GNUPLOT_DEFAULT_GDFONT");
+       int brect[8];
+       char *err;
+
+       if (external_default)
+           png_state.ttffont = gp_strdup(external_default);
+       else    /* Might as well try some plausible font; it's no worse than failing immediately */
+           png_state.ttffont = gp_strdup("arial");
+
+       free(png_state.default_ttffont);
+       png_state.default_ttffont = gp_strdup(png_state.ttffont);
+       png_state.default_ttfsize = 2 * term->h_char - 2;
+       err = gdImageStringFT(NULL, &brect[0], 0,
+               png_state.ttffont, (double)png_state.default_ttfsize,
+               0.0, 0, 0, "test");
+       if (err) {
+               fprintf(stderr,"%s when opening font \"%s\", using internal non-scalable font\n",
+                       err, png_state.ttffont);
+               free(png_state.ttffont);
+               free(png_state.default_ttffont);
+               png_state.ttffont = NULL;
+               png_state.default_ttffont = NULL;
+       }
+    }
+
+    /* If no explicit TTF font size found, generate default */
+    if (png_state.default_ttfsize == 0)
+       png_state.default_ttfsize = 2 * term->h_char - 2;
+    png_state.ttfsize = png_state.default_ttfsize;
+
+    /* Find approximate character width of selected TTF font   */
+    /* This is needed in order to set appropriate border width */
+    if (png_state.default_ttffont) {
+       int brect[8];
+       char *err;
+       err = gdImageStringFT(NULL, &brect[0], 0,
+               png_state.default_ttffont, (double)png_state.default_ttfsize,
+               0.0, 0, 0, "f00000000g");
+       if (!err) {
+           term->h_char = .11 * (float)(brect[2] - brect[0]) + 0.5;
+           term->v_char = 1.1 * (float)(brect[1] - brect[7]) + 0.5;
+       }
+    }
+#endif
+
+    /* This code is shared by png, gif, and jpeg terminal types */
+    if (!strcmp(term->name,"jpeg"))
+       png_state.flags &= ~PNG_USE_TRANSPARENT;
+
+    /* now generate options string */
+
+    if (png_state.flags & PNG_USE_TRANSPARENT) {
+       strcat(term_options, "transparent ");
+    }
+    if (png_state.flags & PNG_USE_INTERLACE) {
+       strcat(term_options, "interlace ");
+    }
+    /* JPEG files are always 24-bit color */
+    if (strcmp(term->name, "jpeg") == 0)
+       png_state.TrueColor = TRUE;
+    else if (png_state.TrueColor) {
+       strcat(term_options, "truecolor ");
+    }
+    if (!(png_state.flags & PNG_USE_CROP)) {
+       strcat(term_options, "no");
+    }
+    strcat(term_options, "crop ");
+
+    if (term->flags & TERM_ENHANCED_TEXT) {
+       strcat(term_options, "enhanced ");
+    }
+
+    if (png_state.ttffont) {
+       sprintf(term_options + strlen(term_options),
+               "font %s %d ", png_state.ttffont, png_state.ttfsize);
+    } else switch (term->h_char) {
+    case 5:
+       strcat(term_options,"tiny ");
+       break;
+    case 6:
+       strcat(term_options, "small ");
+       break;
+    case 7:
+    default:
+       strcat(term_options, "medium ");
+       break;
+    case 8:
+       strcat(term_options, "large ");
+       break;
+    case 9:
+       strcat(term_options,"giant ");
+       break;
+    }
+
+    if (PNG_linewidth_factor != 1.0)
+       sprintf(term_options + strlen(term_options),
+           "linewidth %3.1f ", PNG_linewidth_factor);
+
+    if (png_state.capbutt) {
+       sprintf(term_options + strlen(term_options),
+           "butt ");
+    }
+
+    if (png_state.animate) {
+       sprintf(term_options + strlen(term_options),
+           "animate delay %d loop %d %soptimize ", 
+           png_state.frame_delay, png_state.loop_count,
+           png_state.frame_optimization ? "" : "no");
+    }
+
+    if (PNG_explicit_size)
+       sprintf(term_options + strlen(term_options),
+           "size %d,%d ", PNG_XMAX, PNG_YMAX);
+
+    for (i = 0; strlen(term_options) + 9 < MAX_LINE_LEN &&
+           i < png_state.n_colors; i++) {
+       sprintf(term_options + strlen(term_options),
+               "x%06x ", png_state.rgb_table[i]);
+    }
+}
+
+
+/*
+ * _init()  Called once, when the device is first selected.  This procedure
+ * should set up things that only need to be set once, like handshaking and
+ * character sets etc...
+ */
+TERM_PUBLIC void
+PNG_init()
+{
+int i;
+
+    png_state.linetype = 0;
+    png_state.linewidth = 1;
+    /* EAM - pre-define brushes to implement linewidth */
+    for (i=2; i<=MAXLINEWIDTH; i++) {
+       if (!((PNG_brush[i].im = gdImageCreate(i,i))))
+           int_error(NO_CARET,"libgd: failed to create brush structure");
+       PNG_brush[i].bgnd = gdImageColorAllocate( PNG_brush[i].im, 255, 255, 255 );
+       gdImageFill( PNG_brush[i].im, 0, 0, PNG_brush[i].bgnd );
+       gdImageColorTransparent( PNG_brush[i].im, PNG_brush[i].bgnd );
+       PNG_brush[i].fgnd = gdImageColorAllocate( PNG_brush[i].im, 0, 0, 0 );
+       PNG_brush[i].last_rgb = -99;  /* invalid index will force update on first use */
+    }
+    /* EAM - quick and dirty is to fill the entire brush (square nib).    */
+    /*       might be better to approximate a circular nib by selectively */
+    /*       coloring the individual pixels of the brush image.           */
+    for (i=2; i<=MAXLINEWIDTH; i++) {
+       gdImageFilledRectangle( PNG_brush[i].im, 0, 0, i-1, i-1, PNG_brush[i].fgnd );
+    }
+
+}
+
+/*
+ * _reset()  Called when gnuplot is exited, the output device changed or
+ * the terminal type changed.  This procedure should reset the device,
+ * possibly flushing a buffer somewhere or generating a form feed.
+ */
+TERM_PUBLIC void
+PNG_reset()
+{
+    int i;
+    /* EAM - Clean up the brushes used for linewidth */
+    for (i=2; i<=MAXLINEWIDTH; i++) {
+       if (PNG_brush[i].im)
+           gdImageDestroy(PNG_brush[i].im);
+       PNG_brush[i].im = 0;
+    }
+    if (PNG_fill_tile.im) {
+       gdImageDestroy(PNG_fill_tile.im);
+       PNG_fill_tile.im = (gdImagePtr)0;
+    }
+#ifdef GIF_ANIMATION
+    if (png_state.animate) {
+       gdImageGifAnimEnd(gpoutfile);
+       png_state.frame_count = 0;
+       png_state.animate = FALSE;
+       fprintf(stderr,"End of animation sequence\n");
+    }
+#endif
+}
+
+#if 0
+/* use  #if 1  that's just for debugging */
+void
+PNG_show_current_palette()
+{
+    int i;
+
+    fprintf(stderr, "*****\n SHOW THE PALETTE! total=%i\n",
+           gdImageColorsTotal(png_state.image));
+    for (i=0; i < gdImageColorsTotal(png_state.image); i++) {
+       /* Use access macros to learn colors. */
+       fprintf(stderr, "%i\tr=%d\t g=%d\tb=%d\n",
+               i,
+               gdImageRed(png_state.image,i),
+               gdImageGreen(png_state.image,i),
+               gdImageBlue(png_state.image,i));
+    }
+}
+#endif
+
+/*
+How this works: Gray interval [0;1] will be mapped to interval
+[0;sm_palette.colors-1] those r,g,b components are mapped by the array
+below palette.offset equals 0 since png_smooth_color[0..colors] are
+from ColorAllocate
+*/
+static int png_smooth_color[gdMaxColors];
+
+#if (0)
+/*
+ * This is only needed in order to maintain png_state_rgb for pattern-fill,
+ * and only for palette-based coloring. It doesn't seem worth the space or effort.
+ * EAM November 2004
+ */
+static int png_smooth_rgb[gdMaxColors];
+#endif
+
+  /* TODO: how to recover from a multiplot with two colour pm3d maps?
+     They must use the same palette! Or palette size must be
+     restricted to certain number of colours---a new user's option
+  */
+
+TERM_PUBLIC int PNG_make_palette (t_sm_palette *palette)
+{
+    int i;
+    if (palette == NULL) {
+       /* If the output format is TrueColor there in no color limit */
+       if (png_state.TrueColor)
+           return(0);
+
+       /* return maximal number of colours in a PNG palette */
+       i = gdMaxColors /*256*/ - gdImageColorsTotal(png_state.image);
+       /* the latter is the number of currently allocated colours. We want
+          to allocate the rest */
+       /*BACK PLEASE  fprintf(stderr,"colors in PNG palette=%i\n",(int)gdMaxColors); */
+       if (i == 0) {
+           i = (sm_palette.colors <= 0) ? -1 : sm_palette.colors;
+               /* (no more colorus) : (previous palette (obviously multiplot mode)) */
+#if 0
+           if (i > 0) fprintf(stderr,"reusing it again\n");
+#endif
+       }
+       return i;
+    }
+    if (0 == gdMaxColors /*256*/ - gdImageColorsTotal(png_state.image))
+       return 0; /* reuse previous palette (without warning) */
+    for (i = 0; i < sm_palette.colors; i++) {
+       png_smooth_color[i] = gdImageColorAllocate(png_state.image,
+           (int)( palette->color[i].r * 255 + 0.5 ), /* r,g,b values for png */
+           (int)( palette->color[i].g * 255 + 0.5 ), /* terminal are [0;255] */
+           (int)( palette->color[i].b * 255 + 0.5 ) );
+#if (0)
+       png_smooth_rgb[i] = (((int)(palette->color[i].r * 255.) & 0xff) << 16)
+                         + (((int)(palette->color[i].g * 255.) & 0xff) << 8)
+                         +  ((int)(palette->color[i].b * 255.) & 0xff);
+#endif
+       if (png_smooth_color[i] < 0) { /* this should never happen! take away? */
+           FPRINTF((stderr,"png_smooth_color[i]<0 cannot happen"));
+           exit(1);
+       }
+#if 0
+       fprintf(stderr,"ALLOCATED: i=%i\t=> pal_index=%i\tr=%g g=%g b=%g\n",
+           i, png_smooth_color[i],
+           palette->color[i].r, palette->color[i].g, palette->color[i].b );
+#endif
+    }
+    return 0;
+}
+
+
+TERM_PUBLIC
+void PNG_set_color (t_colorspec *colorspec)
+{
+    double gray = colorspec->value;
+
+    if (colorspec->type == TC_LT) {
+       int savetype = png_state.linetype;
+       PNG_linetype(colorspec->lt);
+       /* Harmless now; will be needed if we ever support dot/dash */
+       png_state.linetype = savetype;
+    }
+
+    if (colorspec->type == TC_RGB) {
+       png_state.rgb = colorspec->lt;
+       png_state.color = gdImageColorResolve(png_state.image,
+           colorspec->lt >> 16, (colorspec->lt >> 8) & 0xff, colorspec->lt & 0xff);
+    }
+
+    if (colorspec->type != TC_FRAC)
+       return;
+
+    if (png_state.TrueColor) {
+       rgb255_color color;
+       rgb255maxcolors_from_gray(gray, &color);
+       png_state.color = gdImageColorResolve(png_state.image,
+           (int)color.r, (int)color.g, (int)color.b);
+       png_state.rgb = (color.r << 16) + (color.g << 8) +color.b;
+       return;
+    } else {
+       int png_color = (gray <= 0) ? 0 : (int)(gray * sm_palette.colors);
+       if (png_color >= sm_palette.colors)
+           png_color = sm_palette.colors - 1;
+       /* map [0;1] to interval [0;png_smooth_colors-1] */
+       png_state.color = png_smooth_color[ png_color ];
+#if (0)
+       png_state.rgb = png_smooth_rgb[ png_color ];
+#endif
+    }
+}
+
+TERM_PUBLIC
+void PNG_filled_polygon(int points, gpiPoint *corners)
+{
+    int i;
+    int fillpar = corners->style >> 4;
+    int color = png_state.color;
+    
+    /* since gpiPoint carries more than just x and y if
+     * we have EXTENDED_COLOR_SPECS defined, we need to
+     * copy it to the gdPointPtr struct; make it static
+     * so that is faster (joze) */
+    static gdPointPtr gd_corners = (gdPointPtr) 0;
+    static unsigned int size = 0;
+    if (points > size) {
+       size = points;
+       gd_corners = gp_realloc(gd_corners, sizeof(gdPoint) * size,
+           "PNG_filled_polygon->gd_corners");
+    }
+    for (i = 0; i < points; i++) {
+       gd_corners[i].x = corners[i].x;
+       gd_corners[i].y = Y(corners[i].y);
+    }
+
+    switch (corners->style & 0xf) {
+       case FS_EMPTY: /* fill with background color */
+           color = png_state.color_table[0];
+           break;
+       case FS_SOLID: /* solid fill */
+           color = PNG_FillSolid(fillpar);
+           break;
+       case FS_PATTERN: /* pattern fill */
+           color = PNG_FillPattern(fillpar);
+           break;
+       default:
+           color = png_state.color;
+           break;
+    }
+
+    gdImageFilledPolygon(png_state.image, gd_corners, points, color);
+
+    /* easy, since gdPointPtr is the same as (gdiPoint*) */
+    /* if someone someday needs this routine to be NON-DESTRUCTIVE, then change
+       the following line to #if 1 */
+#if 0
+    for (i = 0; i < points; i++)
+       corners[i].y = Y(corners[i].y);
+#endif
+}
+
+/*
+ * This function is used for filledboxes
+ * style parameter is some garbled hash combining fillstyle and filldensity
+ */
+TERM_PUBLIC void
+PNG_boxfill(
+    int style,
+    unsigned int x, unsigned int y,
+    unsigned int width, unsigned int height)
+{
+    unsigned int x1, y1, x2, y2;
+    int          color;
+
+    /* fillpar:
+     * - solid   : 0 - 100
+     * - pattern : 0 - 100
+     */
+    int fillpar = style >> 4;
+
+    style &= 0xf;
+
+    switch (style) {
+       case FS_EMPTY: /* fill with background color */
+           color = png_state.color_table[0];
+           break;
+       case FS_SOLID: /* solid fill */
+           color = PNG_FillSolid(fillpar);
+           break;
+       case FS_PATTERN: /* pattern fill */
+           color = PNG_FillPattern(fillpar);
+           break;
+       default:
+           /* should never happen */
+           color = png_state.color;
+           break;
+    }
+
+    x1 = x;
+    x2 = x + width - 1;
+    y2 = Y(y);
+    y1 = y2 - height + 1;
+    gdImageFilledRectangle(png_state.image, x1, y1, x2, y2, color);
+}
+
+/*
+ * _graphics()  Called just before a plot is going to be displayed.  This
+ * procedure should set the device into graphics mode.  Devices which can't
+ * be used as terminals (like plotters) will probably be in graphics mode
+ * always and therefore won't need this.
+ */
+TERM_PUBLIC void
+PNG_graphics()
+{
+    int i;
+    unsigned int rgb;
+    double xscale = 1.;
+    double yscale = 1.;
+
+#ifdef BACKWARDS_COMPATIBLE
+    /* We are deprecating the use of 'set size' to change the actual   */
+    /* canvas size, in this case the size of the output file in pixels.        */
+    if (!PNG_explicit_size) {
+       xscale *= xsize;
+       yscale *= ysize;
+    }
+#endif
+
+#if (GD2_VERS >= 2)
+    /* TrueColor images default to a black background; load white instead */
+    if (png_state.TrueColor) {
+       png_state.image = gdImageCreateTrueColor(
+                       (int) (xscale * PNG_XMAX), (int) (yscale * PNG_YMAX));
+       if (!png_state.image)
+           int_error(NO_CARET,"libgd: failed to create output image structure");
+       rgb = gdImageColorAllocate(png_state.image, 255, 255, 255);
+       gdImageFill(png_state.image, 1, 1, rgb);
+    } else
+#endif
+       png_state.image = gdImageCreate(
+                       (int) (xscale * PNG_XMAX), (int) (yscale * PNG_YMAX));
+       if (!png_state.image)
+           int_error(NO_CARET,"libgd: failed to create output image structure");
+
+    png_state.height = (yscale * PNG_YMAX) - 1;
+    png_state.charw = term->h_char;    /* png_state.font->w; */
+    png_state.charh = term->v_char;    /* png_state.font->h; */
+    png_state.font = png_state.default_font;
+    png_state.color = 0;
+    for (i = png_state.n_colors; i < WEB_N_COLORS; i++)
+       png_state.rgb_table[i] =
+           (web_color_rgbs[i].r << 16) |
+           (web_color_rgbs[i].g << 8) |
+           web_color_rgbs[i].b;
+    if (png_state.n_colors < WEB_N_COLORS)
+       png_state.n_colors = WEB_N_COLORS;
+    for (i = 0; i < png_state.n_colors; i++) {
+       rgb = png_state.rgb_table[i];
+       png_state.color_table[i] =
+           gdImageColorAllocate(png_state.image, (rgb >> 16) & 0xff,
+                                (rgb >> 8) & 0xff, rgb & 0xff);
+    }
+    if (png_state.flags & PNG_USE_TRANSPARENT)
+       gdImageColorTransparent(png_state.image,
+                               png_state.color_table[0]);
+    else
+       gdImageColorTransparent(png_state.image, -1);
+
+}
+
+/*
+ * _text()  Called immediately after a plot is displayed.  This procedure
+ * should set the device back into text mode if it is also a terminal, so
+ * that commands can be seen as they're typed.  Again, this will probably
+ * do nothing if the device can't be used as a terminal.
+ */
+TERM_PUBLIC void
+PNG_text()
+{
+    image_do_crop();
+    if (png_state.flags & PNG_USE_INTERLACE)
+       gdImageInterlace(png_state.image, 1);
+    gdImagePng(png_state.image, gpoutfile);
+    gdImageDestroy(png_state.image);
+}
+
+/* _move(x,y)  Called at the start of a line.  The cursor should move to the
+ * (x,y) position without drawing.
+ */
+TERM_PUBLIC void
+PNG_move(unsigned int x, unsigned int y)
+{
+    png_state.x = x;
+    png_state.y = y;
+}
+
+/* _vector(x,y)  Called when a line is to be drawn.  This should display a line
+ * from the last (x,y) position given by _move() or _vector() to this new (x,y)
+ * position.
+ */
+TERM_PUBLIC void
+PNG_vector(unsigned int x, unsigned int y)
+{
+    int lw;
+
+    if (png_state.linetype == -1) {
+       int png_linetype_dotted[5];
+       png_linetype_dotted[0] = png_state.color;
+       png_linetype_dotted[1] = png_state.color;
+       png_linetype_dotted[2] = gdTransparent;
+       png_linetype_dotted[3] = gdTransparent;
+       png_linetype_dotted[4] = gdTransparent;
+
+       gdImageSetStyle(png_state.image, png_linetype_dotted, 5);
+       gdImageLine(png_state.image, png_state.x, Y(png_state.y),
+                   x, Y(y), gdStyled);
+    } else {
+       if (png_state.linewidth == 1) {
+#if (GD2_VERS >= 2) && defined(gdAntiAliased) 
+           gdImageSetThickness(png_state.image,1);
+           gdImageSetAntiAliased(png_state.image, png_state.color);
+           gdImageLine(png_state.image, png_state.x, Y(png_state.y),
+                       x, Y(y), gdAntiAliased);
+#else
+           gdImageLine(png_state.image, png_state.x, Y(png_state.y),
+                       x, Y(y), png_state.color);
+#endif
+#if (GD2_VERS >= 2)
+       } else if (png_state.capbutt){
+
+           gdImageSetThickness(png_state.image,png_state.linewidth);
+           gdImageLine(png_state.image, png_state.x, Y(png_state.y),
+                       x, Y(y), png_state.color);
+#endif
+       }else{
+
+           /* EAM - Implement linewidth by selecting a pre-defined brush */
+           /* The tricky bit is re-coloring the brush to the current color */
+           lw   = png_state.linewidth;
+           if (png_state.color != PNG_brush[lw].last_rgb) {
+               PNG_brush[lw].fgnd = gdImageColorResolve(PNG_brush[lw].im,
+                   gdImageRed(png_state.image,png_state.color),
+                   gdImageGreen(png_state.image,png_state.color),
+                   gdImageBlue(png_state.image,png_state.color) );
+               PNG_brush[lw].last_rgb = png_state.color;
+           }
+           gdImageFilledRectangle( PNG_brush[lw].im, 0, 0, lw-1, lw-1, PNG_brush[lw].fgnd );
+           gdImageSetBrush(png_state.image, PNG_brush[lw].im);
+           gdImageLine(png_state.image, png_state.x, Y(png_state.y),
+                       x, Y(y), gdBrushed );
+       }
+    }
+    png_state.x = x;
+    png_state.y = y;
+}
+
+/* _linetype(lt)  Called to set the line type before text is displayed or
+ * line(s) plotted.
+ * Negative linetypes are defined in gadgets.h
+ * lt 0 and upwards are used for plots 0 and upwards.
+ * If _linetype() is called with lt greater than the available line types,
+ * it should map it to one of the available line types.
+ */
+TERM_PUBLIC void
+PNG_linetype(int type)
+{
+    if (type >= (png_state.n_colors - 3))
+       type %= (png_state.n_colors - 3);
+    if (type <= LT_BACKGROUND) /* LT_NODRAW, LT_BACKGROUND, LT_UNDEFINED */
+       type = -3;      /* Draw in background color */
+
+    png_state.color = png_state.color_table[type + 3];
+    png_state.rgb = png_state.rgb_table[type + 3];
+    png_state.linetype = type;
+}
+
+/* Use the "brush" tools in the gd library to control line width.
+ * Pre-define brushes for linewidths 2, 3, 4, 5, 6 (1 doesn't need a brush!).
+ * Here we just remember the state.
+ */
+TERM_PUBLIC void
+PNG_linewidth(double linewidth)
+{
+    png_state.linewidth = (int)(PNG_linewidth_factor * linewidth+0.49);
+    if (png_state.linewidth > MAXLINEWIDTH) png_state.linewidth = MAXLINEWIDTH;
+    if (png_state.linewidth < 1) png_state.linewidth = 1;
+}
+
+/* _put_text(x,y,str)  Called to display text at the (x,y) position,
+ * while in graphics mode.   The text should be vertically (with respect
+ * to the text) justified about (x,y).  The text is rotated according
+ * to _text_angle and then horizontally (with respect to the text)
+ * justified according to _justify_text.
+ */
+#ifdef HAVE_GD_TTF
+TERM_PUBLIC void
+PNG_put_text(unsigned int x, unsigned int y, const char *string)
+{
+    if (png_state.ttffont) {
+       int brect[8]; char *err;
+       /* Draw once with a NULL image to get the bounding rectangle */
+       /* then draw it again, centered.                             */
+       err = gdImageStringFT(NULL, brect, png_state.color,
+                       png_state.ttffont, (double)png_state.ttfsize,
+                       (double)png_state.angle * M_PI_2 / 90. ,
+                       x, Y(y), (char *)string);
+       if (err) {
+           fprintf(stderr,"gdImageStringFT: %s while printing string %s with font %s\n",
+               err,string,png_state.ttffont);
+       } else {
+           x += sin((double)png_state.angle * M_PI_2/90.) * (double)png_state.charh/4.;
+           y -= cos((double)png_state.angle * M_PI_2/90.) * (double)png_state.charh/4.;
+           switch (png_state.justify) {
+               case RIGHT:
+                               x -= (brect[2]-brect[0]);
+                               y += (brect[3]-brect[1]);
+                               break;
+               case CENTRE:
+                               x -= (brect[2]-brect[0]) / 2.;
+                               y += (brect[3]-brect[1]) / 2.;
+                               break;
+               case LEFT:
+               default:        break;
+           }
+           err = gdImageStringFT(png_state.image, brect, png_state.color,
+                       png_state.ttffont, (double)png_state.ttfsize,
+                       (double)png_state.angle * M_PI_2 / 90.,
+                       x, Y(y), (char *)string);
+           if (err)
+               fprintf(stderr,"gdImageStringFT: %s while printing string %s with font %s\n",
+                   err,string,png_state.ttffont);
+       }
+    } else if (png_state.angle != 0) {
+       x -= png_state.charh / 2;
+       switch (png_state.justify) {
+           case RIGHT: y -= png_state.charw * strlen(string);
+                       break;
+           case CENTRE:y -= png_state.charw * strlen(string) / 2;
+                       break;
+           case LEFT:
+           default:    break;
+       }
+       gdImageStringUp(png_state.image, png_state.font,
+                       x, Y(y),
+                       (unsigned char *)string, png_state.color);
+    } else {
+       y += png_state.charh / 2;
+       switch (png_state.justify) {
+           case RIGHT: x -= png_state.charw * strlen(string);
+                       break;
+           case CENTRE:x -= png_state.charw * strlen(string) / 2;
+                       break;
+           case LEFT:
+           default:    break;
+       }
+       gdImageString(png_state.image, png_state.font,
+                     x, Y(y),
+                     (unsigned char *)string, png_state.color);
+    }
+}
+
+#else  /* not HAVE_GD_TTF */
+
+TERM_PUBLIC void
+PNG_put_text(unsigned int x, unsigned int y, const char *string)
+{
+    if (png_state.angle == 0) {
+       y += png_state.charh / 2;
+       gdImageString(png_state.image, png_state.font,
+                     x, Y(y),
+                     (unsigned char *)string, png_state.color);
+    } else {
+       x -= png_state.charh / 2;
+       gdImageStringUp(png_state.image, png_state.font,
+                       x, Y(y),
+                       (unsigned char *)string, png_state.color);
+    }
+}
+
+#endif /* HAVE_GD_TTF */
+
+
+TERM_PUBLIC int
+PNG_text_angle(int ang)
+{
+    while (ang < -180) ang += 360;     /* Should not be needed, but reported to */
+    while (ang > 180) ang -= 360;      /* avoid a bug in some libgd versions    */
+    png_state.angle = ang;
+    return TRUE;
+}
+
+TERM_PUBLIC int
+PNG_justify_text(enum JUSTIFY mode)
+{
+#ifdef HAVE_GD_TTF
+    png_state.justify = mode;
+    return TRUE;
+#else
+    return null_justify_text(mode);
+#endif
+}
+
+TERM_PUBLIC void
+PNG_point(unsigned int x, unsigned int y, int number)
+{
+    int save_color = png_state.color;
+
+    if (number < 0) {
+       gdImageSetPixel(png_state.image, x, Y(y), png_state.color);
+       return;
+    }
+    /* Use current linewidth to draw the point symbol */
+    if (png_state.linewidth > 1) {
+       /* EAM - Implement linewidth by selecting a pre-defined brush */
+       /* The tricky bit is re-coloring the brush to the current color */
+       int lw   = png_state.linewidth;
+       if (png_state.color != PNG_brush[lw].last_rgb) {
+           PNG_brush[lw].fgnd = gdImageColorResolve(PNG_brush[lw].im,
+               gdImageRed(png_state.image,png_state.color),
+               gdImageGreen(png_state.image,png_state.color),
+               gdImageBlue(png_state.image,png_state.color) );
+           PNG_brush[lw].last_rgb = png_state.color;
+       }
+       gdImageFilledRectangle( PNG_brush[lw].im, 0, 0, lw-1, lw-1, PNG_brush[lw].fgnd );
+       gdImageSetBrush(png_state.image, PNG_brush[lw].im);
+       png_state.color = gdBrushed;
+    }
+
+    y = Y(y);
+
+    switch (number % 13) {
+    case 0: /* plus */
+    default:
+       PNG_PointPlus(x, y);
+       break;
+    case 1: /* X */
+       PNG_PointX(x, y);
+       break;
+    case 2: /* star */
+       PNG_PointPlus(x, y);
+       PNG_PointX(x, y);
+       break;
+    case 3: /* box */
+       gdImageRectangle(png_state.image, x - PNG_ps, y - PNG_ps,
+                        x + PNG_ps, y + PNG_ps, png_state.color);
+       break;
+    case 4: /* box                   filled */
+       gdImageFilledRectangle(png_state.image, x - PNG_ps, y - PNG_ps,
+                              x + PNG_ps, y + PNG_ps, png_state.color);
+       break;
+    case 5: /* circle */
+       gdImageArc(png_state.image, x, y, 2 * PNG_ps, 2 * PNG_ps,
+                  0, 360, png_state.color);
+       break;
+    case 6: /* circle (disk)         filled */
+#if (GD2_VERS >= 2)
+       gdImageFilledArc(png_state.image, x, y, 2 * PNG_ps, 2 * PNG_ps,
+                  0, 360, png_state.color, gdArc);
+#else
+       gdImageArc(png_state.image, x, y, 2 * PNG_ps, 2 * PNG_ps,
+                  0, 360, png_state.color);
+       gdImageFillToBorder(png_state.image, x, y,
+                           png_state.color, png_state.color);
+#endif
+       break;
+    case 7: /* triangle */
+       PNG_Triangle(x, y, 1, gp_gdImagePolygon);
+       break;
+    case 8: /* triangle              filled */
+       PNG_Triangle(x, y, 1, gp_gdImageFilledPolygon);
+       break;
+    case 9: /* upside down triangle */
+       PNG_Triangle(x, y, -1, gp_gdImagePolygon);
+       break;
+    case 10: /* upside down triangle  filled */
+       PNG_Triangle(x, y, -1, gp_gdImageFilledPolygon);
+       break;
+    case 11: /* diamond */
+       PNG_Diamond(x, y, gp_gdImagePolygon);
+       break;
+    case 12: /* diamond               filled */
+       PNG_Diamond(x, y, gp_gdImageFilledPolygon);
+       break;
+    }
+
+    png_state.color = save_color;
+}
+
+TERM_PUBLIC int
+PNG_set_font(const char *fontname)
+{
+    int  sep;
+    int  size;
+    gdFontPtr font = png_state.default_font;
+    char *name = gp_strdup(fontname);
+
+    sep = strcspn(fontname,",");
+    strncpy(name,fontname,sep);
+    name[sep] = '\0';
+    size = png_state.default_ttfsize;
+    sscanf (&(fontname[sep+1]),"%d",&size);
+
+    if (!strcmp(name,"small"))
+       font = gdFontSmall;
+    else if (!strcmp(name,"medium"))
+       font = gdFontMediumBold;
+    else if (!strcmp(name,"large"))
+       font = gdFontLarge;
+    else if (!strcmp(name,"giant"))
+       font = gdFontGiant;
+    else if (!strcmp(name,"tiny"))
+       font = gdFontTiny;
+    else if (*name) {
+       /* New ttf font */
+       free(png_state.ttffont);
+       png_state.ttffont = gp_strdup(name);
+       png_state.ttfsize = size;
+    } else {
+       /* Restore initial default font */
+       free(png_state.ttffont);
+       png_state.ttffont = gp_strdup(png_state.default_ttffont);
+       png_state.ttfsize = png_state.default_ttfsize;
+    }
+    free(name);
+
+    png_state.font  = font;
+    png_state.charw = font->w;
+    png_state.charh = font->h;
+
+/* EAM 9-Feb-2003 Make new font size visible to higher level routines like write_multiline */
+    term->h_char = font->w;
+    term->v_char = font->h;
+#ifdef HAVE_GD_TTF
+    /* Find approximate character width and height of selected TTF font */
+    if (png_state.ttffont) {
+       int brect[8];
+       char *err;
+       err = gdImageStringFT(NULL, &brect[0], 0,
+               png_state.ttffont, (double)png_state.ttfsize,
+               0.0, 0, 0, "f00000000g");
+       if (!err) {
+           term->h_char = .11 * (float)(brect[2] - brect[0]) + 0.5;
+           term->v_char = 1.1 * (float)(brect[1] - brect[7]) + 0.5;
+       }
+    }
+#endif
+
+    return TRUE;
+}
+
+TERM_PUBLIC void
+PNG_pointsize(double ptsize)
+{
+    if (ptsize < 0)
+       ptsize = 1;
+    PNG_ps = (int)(((double)PNG_POINT_SCALE * ptsize) + 0.5);
+}
+
+/*
+ * Ethan A Merritt November 2003
+ *     - Support for enhanced text mode
+ * BUGS:
+ *     - placement of overprinted characters is not correct;
+ *       the overprinted text (pass 2) should be centered, not left-justified
+ * PROBLEMS:
+ *     - the Symbol font encoding didn't work in libgd until 2.0.21
+ *     - Placement of superscripts and subscripts relies on information
+ *       in the font description that is not always reliable
+ *     - the TTF character encoding for non-keyboard characters does
+ *       not always match the PostScript standard.
+ *     - Spacing of rotated text is incorrect; I believe this is a due
+ *       to a problem in the text rotation code (aspect ratio??).
+ */
+
+static TBOOLEAN ENHgd_opened_string;
+
+/* used in determining height of processed text */
+static float ENHgd_base;
+
+/* use these so that we don't over-write the current font settings in png_state */
+static double  ENHgd_fontsize;
+static char   *ENHgd_font;
+
+static TBOOLEAN ENHgd_show = TRUE;
+static TBOOLEAN ENHgd_sizeonly = FALSE;
+static int ENHgd_overprint = 0;
+static TBOOLEAN ENHgd_widthflag = TRUE;
+static unsigned int ENHgd_xsave, ENHgd_ysave;
+
+TERM_PUBLIC void
+ENHGD_OPEN(
+    char *fontname,
+    double fontsize, double base,
+    TBOOLEAN widthflag,
+    TBOOLEAN showflag,
+    int overprint)
+{
+    /* If the overprint code requests a save or restore, that's all we do */
+    if (overprint == 3) {
+       ENHgd_xsave = png_state.x;
+       ENHgd_ysave = png_state.y;
+       return;
+    } else if (overprint == 4) {
+       PNG_move(ENHgd_xsave, ENHgd_ysave);
+       return;
+    }
+
+    if (!ENHgd_opened_string) {
+       ENHgd_opened_string = TRUE;
+       enhanced_cur_text = &enhanced_text[0];
+       ENHgd_font = fontname;
+       ENHgd_fontsize = fontsize;
+       ENHgd_base = base;
+       ENHgd_show = showflag;
+       ENHgd_overprint = overprint;
+       ENHgd_widthflag = widthflag;
+    }
+}
+
+/* Write a string fragment and update the current position */
+TERM_PUBLIC void
+ENHGD_FLUSH()
+{
+    int brect[8]; char *err;
+    unsigned int x, y;
+
+       if (ENHgd_opened_string) {
+           ENHgd_opened_string = FALSE;
+           *enhanced_cur_text = '\0';
+           x = png_state.x;
+           y = png_state.y;
+           x -= sin((double)png_state.angle * M_PI_2/90.) * ENHgd_base;
+           y += cos((double)png_state.angle * M_PI_2/90.) * ENHgd_base;
+           x += sin((double)png_state.angle * M_PI_2/90.) * (double)png_state.charh/4.;
+           y -= cos((double)png_state.angle * M_PI_2/90.) * (double)png_state.charh/4.;
+
+#ifdef gdFTEX_Adobe_Custom
+           /* libgd defaults to UTF-8 encodings. We have limited options for   */
+           /* over-riding this, but we can try                                 */
+           if (ENHgd_font && !strcmp(ENHgd_font,"Symbol")) {
+               PNG_FONT_INFO.flags |= gdFTEX_CHARMAP;
+               PNG_FONT_INFO.charmap = gdFTEX_Adobe_Custom;
+           } else {
+               PNG_FONT_INFO.flags &= ~gdFTEX_CHARMAP;
+               PNG_FONT_INFO.charmap = 0;   /* gdFTEX_Adobe_Custom */
+           }
+           err = gdImageStringFTEx(
+                       (ENHgd_show && !ENHgd_sizeonly) ? png_state.image : NULL,
+                       brect, png_state.color,
+                       ENHgd_font, ENHgd_fontsize,
+                       (double)png_state.angle * M_PI_2/90.,
+                       x, Y(y), enhanced_text, &PNG_FONT_INFO);
+#else
+           err = gdImageStringFT(
+                       (ENHgd_show && !ENHgd_sizeonly) ? png_state.image : NULL,
+                       brect, png_state.color,
+                       ENHgd_font, ENHgd_fontsize,
+                       (double)png_state.angle * M_PI_2/90.,
+                       x, Y(y), enhanced_text);
+#endif
+           if (err) 
+               fprintf(stderr,"gdImageStringFT: %s while printing string %s with font %s\n",
+                   err,enhanced_text,ENHgd_font);
+
+           FPRINTF((stderr,"outputstring: %s boundingbox: %d %d %d %d\n",
+                       enhanced_text, brect[6], brect[7], brect[2], brect[3]));
+           if (ENHgd_overprint == 1) {
+               png_state.x += ((brect[2] - brect[0]))/2;
+               png_state.y -= (brect[3] - brect[1]);
+           } else if (ENHgd_widthflag) {
+               png_state.x += (brect[2] - brect[0]);
+               png_state.y -= (brect[3] - brect[1]);
+           }
+       }
+}
+
+TERM_PUBLIC void
+ENHGD_put_text(unsigned int x, unsigned int y, const char *str)
+{
+    char *original_string = (char *)str;
+
+    if (ignore_enhanced_text || !png_state.ttffont) {
+       PNG_put_text(x,y,str);
+       return;
+    }
+
+    if (!strlen(str))
+       return;
+
+    /* if there are no magic characters, we should just be able
+     * punt the string to PNG_put_text()
+     */
+    if (!strpbrk(str, "{}^_@&~")) {
+       /* FIXME: do something to ensure default font is selected */
+       PNG_put_text(x,y,str);
+       return;
+    }
+
+    PNG_move(x,y);
+
+    /* set up the global variables needed by enhanced_recursion() */
+    enhanced_max_height = -1000;
+    enhanced_min_height = 1000;
+    enhanced_fontscale = 1.0;
+    strncpy(enhanced_escape_format,"&#x%2.2x;",sizeof(enhanced_escape_format));
+
+    ENHgd_opened_string = FALSE;
+    ENHgd_show = TRUE;
+    ENHgd_overprint = 0;
+
+    /* EAM - post.trm wasn't doing this, but how else do they get initialized? */
+       ENHgd_font = png_state.ttffont;
+       ENHgd_fontsize = png_state.ttfsize;
+
+    /* EAM - Software text justification requires two passes */
+    if (png_state.justify == RIGHT || png_state.justify == CENTRE)
+       ENHgd_sizeonly = TRUE;
+
+    /* Set the recursion going. We say to keep going until a
+     * closing brace, but we don't really expect to find one.
+     * If the return value is not the nul-terminator of the
+     * string, that can only mean that we did find an unmatched
+     * closing brace in the string. We increment past it (else
+     * we get stuck in an infinite loop) and try again.
+     */
+    while (*(str = enhanced_recursion((char *)str, TRUE,
+                       ENHgd_font, ENHgd_fontsize,
+                       0.0, TRUE, TRUE, 0))) {
+       (term->enhanced_flush)();
+
+       /* I think we can only get here if *str == '}' */
+           enh_err_check(str);
+
+       if (!*++str)
+           break; /* end of string */
+
+       /* else carry on and process the rest of the string */
+    }
+
+    enhanced_max_height += enhanced_min_height;
+
+    /* We can do text justification by running the entire top level string */
+    /* through 2 times, with the ENHgd_sizeonly flag set the first time.   */
+    /* After seeing where the final position is, we then offset the start  */
+    /* point accordingly and run it again without the flag set.            */
+    if (png_state.justify == RIGHT || png_state.justify == CENTRE) {
+       int justification = png_state.justify;
+       int x_offset = png_state.x - x;
+       int y_offset = 0;
+
+       if (png_state.angle != 0)
+           y_offset = png_state.y - y;
+       png_state.justify = LEFT;
+       ENHgd_sizeonly = FALSE;
+
+       if (justification == RIGHT) {
+           ENHGD_put_text(x - x_offset, y - y_offset, original_string);
+       } else if (justification == CENTRE) {
+           ENHGD_put_text(x - x_offset/2, y - y_offset/2, original_string);
+       }
+       png_state.justify = justification;
+    }
+
+}
+
+#undef gdfont
+
+#ifdef WITH_IMAGE
+
+TERM_PUBLIC void
+PNG_image (unsigned M, unsigned N, coordval * image, gpiPoint * corner, t_imagecolor color_mode)
+{
+    int m, n, mout, nout;
+    int x1,y1,x2,y2;
+    int xclip1, xclip2, yclip1, yclip2;
+    int pixel;
+    gdImagePtr im;
+
+#if (GD2_VERS >= 2)
+    if (png_state.TrueColor) {
+       im = gdImageCreateTrueColor(M, N);
+       if (!im)
+           int_error(NO_CARET,"libgd: failed to create image structure");
+    } else
+#endif
+       {
+       im = gdImageCreate(M, N);
+       if (!im)
+           int_error(NO_CARET,"libgd: failed to create image structure");
+       gdImagePaletteCopy(im, png_state.image);
+    }
+
+    /* TrueColor 24-bit color mode */
+    if (color_mode == IC_RGB) {
+       for (n=0; n<N; n++) {
+       for (m=0; m<M; m++) {
+           rgb_color rgb1;
+           rgb255_color rgb255;
+           rgb1.r = *image++;
+           rgb1.g = *image++;
+           rgb1.b = *image++;
+           rgb255_from_rgb1( rgb1, &rgb255 );
+           pixel = gdImageColorResolve( im, 
+               (int)rgb255.r, (int)rgb255.g, (int)rgb255.b );
+           gdImageSetPixel( im, m, n, pixel );
+       }
+       }
+    /* Palette color lookup from gray value */
+    } else {
+       for (n=0; n<N; n++) {
+       for (m=0; m<M; m++) {
+           rgb255_color rgb;
+           rgb255maxcolors_from_gray( *image++, &rgb );
+           pixel = gdImageColorResolve( im,
+               (int)rgb.r, (int)rgb.g, (int)rgb.b );
+           gdImageSetPixel( im, m, n, pixel );
+       }
+       }
+    }
+
+#if (GD2_VERS >= 2)
+    /* Set clipping bound for area into which we will copy */
+    xclip1 = GPMIN(corner[2].x, corner[3].x);
+    xclip2 = GPMAX(corner[2].x, corner[3].x);
+    yclip1 = GPMIN(Y(corner[2].y), Y(corner[3].y));
+    yclip2 = GPMAX(Y(corner[2].y), Y(corner[3].y));
+    gdImageGetClip(png_state.image, &x1, &y1, &x2, &y2);
+    gdImageSetClip(png_state.image, xclip1, yclip1, xclip2, yclip2);
+#endif
+
+    /* Copy and resize onto requested region of plot */
+    /* FIXME - WOULD gdImageCopyResampled() do a nicer job ??? */
+    mout = abs( (int)corner[1].x - (int)corner[0].x );
+    nout = abs( (int)corner[1].y - (int)corner[0].y );
+    gdImageCopyResized(png_state.image, im,
+       (corner[0].x), Y(corner[0].y),  /* Destination X, Y */
+       0, 0,                           /* Source X, Y */
+       mout, nout,                     /* Destination Width, Height */
+       M, N                            /* Source Width, Height */
+       );
+    gdImageDestroy(im);
+
+#if (GD2_VERS >= 2)
+    /* Restore previous clipping, if any */
+    gdImageSetClip(png_state.image, x1, y1, x2, y2);
+#endif
+
+}
+
+#endif
+#undef MAXLINEWIDTH
+#undef Y
+
+#endif /* TERM_BODY */
+#ifdef TERM_TABLE
+
+TERM_TABLE_START(png_driver)
+    "png", "PNG images using libgd and TrueType fonts",
+    GREG_XMAX, GREG_YMAX, PNG_VCHAR, PNG_HCHAR,
+    PNG_TICSIZE, PNG_TICSIZE, PNG_options, PNG_init, PNG_reset,
+    PNG_text, null_scale, PNG_graphics, PNG_move, PNG_vector,
+    PNG_linetype, PNG_put_text, PNG_text_angle,
+    PNG_justify_text, PNG_point, do_arrow, PNG_set_font,
+    PNG_pointsize,
+    TERM_BINARY,
+    0 /*suspend*/, 0 /*resume*/,
+    PNG_boxfill /*EAM - fillbox*/,
+    PNG_linewidth /*EAM - linewidth*/
+#ifdef USE_MOUSE
+    , 0, 0, 0, 0, 0 /* no mouse support */
+#endif
+    , PNG_make_palette,
+    0, /* previous_palette() ... no, single array of 256 colours for PNG */
+    PNG_set_color,
+    PNG_filled_polygon
+#ifdef WITH_IMAGE
+    , PNG_image
+#endif
+    , ENHGD_OPEN, ENHGD_FLUSH, do_enh_writec
+TERM_TABLE_END(png_driver)
+
+#undef LAST_TERM
+#define LAST_TERM png_driver
+
+#endif /* TERM_TABLE */
+#endif /* TERM_PROTO_ONLY */
+
+#ifndef JPEG_HELP_ONLY
+#ifdef TERM_HELP
+START_HELP(png)
+"1 png",
+"?commands set terminal png",
+"?set terminal png",
+"?set term png",
+"?terminal png",
+"?term png",
+"?png",
+" Syntax:",
+"       set terminal png ",
+"              {{no}transparent} {{no}interlace}",
+"              {{no}truecolor} {rounded|butt}",
+"              {tiny | small | medium | large | giant}",
+"              {font <face> {<pointsize>}}",
+"              {size <x>,<y>} {{no}crop}",
+"              {{no}enhanced}",
+"              {<color0> <color1> <color2> ...}",
+"",
+" PNG images are created using libgd, with optional support for TrueType",
+" and Adobe Type 1 fonts via libfreetype. Version 1.8 or greater of libgd",
+" is required.",
+"",
+" `transparent` instructs the driver to generate transparent PNGs.  The first",
+" color will be the transparent one. Default is `notransparent`.",
+"",
+" `interlace` instructs the driver to generate interlaced PNGs.",
+" Default is `nointerlace`.",
+"",
+" `butt` instructs the driver to use a line drawing method that does",
+" not overshoot the desired end point of a line.  This setting is only",
+" applicable for line widths greater than 1.  This setting is most useful when",
+" drawing horizontal or vertical lines.  Default is `rounded`.",
+" Version 2.0 or greater of libgd is required.",
+"",
+" PNG plots may be conveniently viewed by piping the output to the",
+" 'display' program from the ImageMagick package as follows:",
+"                set term png",
+"                set output '| display png:-'",
+"",
+" View the output from successive plot commands interactively by hitting",
+" <space> in the display window.  To save a particular one to disk, left",
+" click in the display window and choose `save`.",
+"",
+" Five basic fonts are supported directly by the gd library. These are",
+" `tiny` (5x8 pixels), `small` (6x12 pixels), `medium`, (7x13 Bold), ",
+" `large` (8x16) or `giant` (9x15 pixels). These fonts cannot be scaled",
+" or rotated (pure horizontal or vertical text only).",
+"",
+"=fonts",
+" If gnuplot was built with support for TrueType (*.ttf) or Adobe Type 1 ",
+" (*.pfa) fonts, they may be selected using the 'font <face> {<pointsize>}' ",
+" option. <face> is either the full pathname to the font file, or a font ",
+" face name that is assumed to be the first part of a filename in one of the ",
+" directories listed in the GDFONTPATH environmental variable. That is, ",
+" 'set term png font \"Face\"' will look for a font file named either ",
+" <somedirectory>/Face.ttf or <somedirectory>/Face.pfa. Both TrueType and ",
+" Adobe Type 1 fonts are fully scalable and may be rotated through any angle.",
+" If no font is specified, gnuplot checks the environmental variable ",
+" GNUPLOT_DEFAULT_GDFONT to see if there is a preferred default font. ",
+"",
+" `enhanced` enables the enhanced text processing features, (subscripts, ",
+" superscripts and mixed fonts). See `enhanced` for more information. ",
+" The full enhanced mode syntax is supported by the PNG/JPEG driver itself,",
+" but some of these features are dependent on which version of the ",
+" underlying libgd library is present, and which fonts are available.",
+"",
+" The size <x,y> is given in pixels---it defaults to 640x480.  The number of",
+" pixels can be also modified by scaling with the `set size` command.",
+" `crop` trims blank space from the edges of the completed plot, resulting",
+" in a smaller final image size. Default is `nocrop`.",
+"",
+" Each color must be of the form 'xrrggbb', where x is the literal character",
+" 'x' and 'rrggbb' are the red, green and blue components in hex.  For example,",
+" 'x00ff00' is green.  The background color is set first, then the border",
+" colors, then the X & Y axis colors, then the plotting colors.  The maximum",
+" number of colors that can be set is 256.",
+"",
+" Examples:",
+"       set terminal png medium size 640,480 \\",
+"                        xffffff x000000 x404040 \\",
+"                        xff0000 xffa500 x66cdaa xcdb5cd \\",
+"                        xadd8e6 x0000ff xdda0dd x9500d3    # defaults",
+"",
+" which uses white for the non-transparent background, black for borders, gray",
+" for the axes, and red, orange, medium aquamarine, thistle 3, light blue, blue,",
+" plum and dark violet for eight plotting colors.",
+"",
+"       set terminal png font arial 14 size 800,600",
+"",
+" which searches for a TrueType font with face name 'arial' in the directory",
+" specified by the environment variable GDFONTPATH and 14pt font size.",
+"",
+"       set terminal png transparent xffffff \\",
+"                        x000000 x202020 x404040 x606060 \\",
+"                        x808080 xA0A0A0 xC0C0C0 xE0E0E0",
+"",
+" which uses white for the transparent background, black for borders, dark",
+" gray for axes, and a gray-scale for the six plotting colors.",
+""
+END_HELP(png)
+#endif /* TERM_HELP */
+#endif /* JPEG_HELP_ONLY */
+
+/*
+ * JPEG support comes almost for free.
+ * We just piggy-back on the PNG routines, since they both go via libgd
+ */
+#ifdef HAVE_GD_JPEG
+
+#ifdef TERM_REGISTER
+register_term(jpeg)
+#endif
+
+#ifdef TERM_PROTO
+TERM_PUBLIC void JPEG_text __PROTO((void));
+#define GOT_NEXT_PROTO
+#endif
+
+#ifndef TERM_PROTO_ONLY
+
+#ifdef TERM_BODY
+
+#include "gd.h"
+/*
+ * All functions except the final write to file
+ * are actually performed by the PNG driver code
+ */
+TERM_PUBLIC void
+JPEG_text()
+{
+int quality = 90;
+
+    image_do_crop();
+    if (png_state.flags & PNG_USE_INTERLACE)
+       gdImageInterlace(png_state.image, 1);
+    gdImageJpeg(png_state.image, gpoutfile, quality);
+    gdImageDestroy(png_state.image);
+}
+
+#endif /* TERM_BODY */
+
+#ifdef TERM_TABLE
+
+TERM_TABLE_START(jpeg_driver)
+    "jpeg", "JPEG images using libgd and TrueType fonts",
+    GREG_XMAX, GREG_YMAX, PNG_VCHAR, PNG_HCHAR,
+    PNG_TICSIZE, PNG_TICSIZE, PNG_options, PNG_init, PNG_reset,
+    JPEG_text, null_scale, PNG_graphics, PNG_move, PNG_vector,
+    PNG_linetype, PNG_put_text, PNG_text_angle,
+    PNG_justify_text, PNG_point, do_arrow, PNG_set_font,
+    PNG_pointsize,
+    TERM_CAN_MULTIPLOT | TERM_BINARY,
+    0 /*suspend*/, 0 /*resume*/,
+    PNG_boxfill /*EAM - fillbox*/,
+    PNG_linewidth /*EAM - linewidth*/
+#ifdef USE_MOUSE
+    , 0, 0, 0, 0, 0 /* no mouse support */
+#endif
+    , PNG_make_palette,
+    0, /* previous_palette() ... no, single array of 256 colours for PNG */
+    PNG_set_color,
+    PNG_filled_polygon
+#ifdef WITH_IMAGE
+    , PNG_image
+#endif
+    , ENHGD_OPEN, ENHGD_FLUSH, do_enh_writec
+TERM_TABLE_END(jpeg_driver)
+
+#undef LAST_TERM
+#define LAST_TERM jpeg_driver
+
+#endif /* TERM_TABLE */
+#endif /* TERM_PROTO_ONLY */
+
+
+#ifdef TERM_HELP
+START_HELP(jpeg)
+"1 jpeg",
+"?commands set terminal jpeg",
+"?set terminal jpeg",
+"?set term jpeg",
+"?terminal jpeg",
+"?term jpeg",
+"?jpeg",
+"",
+" Syntax:",
+"       set terminal jpeg ",
+"                        {{no}interlace}",
+"                        {tiny | small | medium | large | giant}",
+"                        {font <face> {<pointsize>}}",
+"                        {size <x>,<y>} {{no}crop}",
+"                        {{no}enhanced}",
+"                        {<color0> <color1> <color2> ...}",
+"",
+" JPEG images are created using libgd, with optional support for TrueType",
+" fonts via libfreetype.",
+"",
+" The `interlace` option creates a progressive JPEG image.",
+" Default is `nointerlace`.",
+"",
+" Five basic fonts are supported directly by the gd library. These are",
+" `tiny` (5x8 pixels), `small` (6x12 pixels), `medium`, (7x13 Bold), ",
+" `large` (8x16) or `giant` (9x15 pixels). These fonts cannot be scaled",
+" or rotated (pure horizontal or vertical text only).",
+"",
+"=fonts",
+" If gnuplot was built with support for TrueType (*.ttf) or Adobe Type 1 ",
+" (*.pfa) fonts, they may be selected using the 'font <face> {<pointsize>}' ",
+" option. <face> is either the full pathname to the font file, or a font ",
+" face name that is assumed to be the first part of a filename in one of the ",
+" directories listed in the GDFONTPATH environmental variable. That is, ",
+" 'set term jpeg font \"Face\"' will look for a font file named either ",
+" <somedirectory>/Face.ttf or <somedirectory>/Face.pfa. Both TrueType and ",
+" Adobe Type 1 fonts are fully scalable and may be rotated through any angle.",
+" If no font is specified, gnuplot checks the environmental variable ",
+" GNUPLOT_DEFAULT_GDFONT to see if there is a preferred default font. ",
+"",
+" `enhanced` enables the enhanced text processing features, (subscripts, ",
+" superscripts and mixed fonts). See `enhanced` for more information. ",
+" The full enhanced mode syntax is supported by the PNG/JPEG driver itself,",
+" but some of these features are dependent on which version of the ",
+" underlying libgd library is present, and which fonts are available.",
+"",
+" The size <x,y> is given in pixels---it defaults to 640x480.  The number of",
+" pixels can be also modified by scaling with the `set size` command.",
+" `crop` trims blank space from the edges of the completed plot, resulting",
+" in a smaller final image size. Default is `nocrop`.",
+"",
+" Each color must be of the form 'xrrggbb', where x is the literal character",
+" 'x' and 'rrggbb' are the red, green and blue components in hex.  For example,",
+" 'x00ff00' is green.  The background color is set first, then the border",
+" colors, then the X & Y axis colors, then the plotting colors.  The maximum",
+" number of colors that can be set is 256.",
+"",
+" Examples:",
+"       set terminal jpeg medium size 640,480 \\",
+"                        xffffff x000000 x404040 \\",
+"                        xff0000 xffa500 x66cdaa xcdb5cd \\",
+"                        xadd8e6 x0000ff xdda0dd x9500d3    # defaults",
+"",
+" which uses white for the non-transparent background, black for borders, gray",
+" for the axes, and red, orange, medium aquamarine, thistle 3, light blue, blue,",
+" plum and dark violet for eight plotting colors.",
+"",
+"       set terminal jpeg large font arial size 800,600",
+"",
+" which searches for a TrueType font with face name 'arial' in the directory",
+" specified by the environment variable GDFONTPATH and large (14pt) font size.",
+""
+END_HELP(jpeg)
+#endif /* TERM_HELP */
+#endif /* HAVE_GD_JPEG */
+
+#ifdef HAVE_GD_GIF
+/*
+ * GIF support comes almost for free.
+ * We just piggy-back on the PNG routines, since they both go via libgd.
+ * Required libgd version is 2.0.28 or newer.
+ */
+#ifdef HAVE_GD_GIF
+
+#ifdef TERM_REGISTER
+register_term(gif)
+#endif
+
+#ifdef TERM_PROTO
+TERM_PUBLIC void GIF_text __PROTO((void));
+#define GOT_NEXT_PROTO
+#endif
+
+#ifndef TERM_PROTO_ONLY
+
+#ifdef TERM_BODY
+
+#include "gd.h"
+/*
+ * All functions except the final write to file
+ * are actually performed by the PNG driver code
+ */
+TERM_PUBLIC void
+GIF_text()
+{
+    image_do_crop();
+
+#ifdef GIF_ANIMATION
+    if (png_state.animate) {
+       /* Note - using a global colormap saves space, but it breaks    */
+       /* if later frames add new colors to the palette.               */
+       if (png_state.frame_count == 0) {
+           gdImageGifAnimBegin(png_state.image, gpoutfile, 
+               1, /* Load Global Colormap even if it isn't used */ 
+               png_state.loop_count );
+       }
+       gdImageGifAnimAdd(png_state.image, gpoutfile,
+           png_state.frame_optimization ? 0  /* use global map  */
+                                        : 1, /* use private map */
+           0, 0 /* No offset */,
+           png_state.frame_delay,
+           (png_state.flags & PNG_USE_TRANSPARENT)
+                       ? gdDisposalRestorePrevious 
+                       : gdDisposalNone, 
+           (png_state.frame_optimization && !(png_state.flags & PNG_USE_TRANSPARENT))
+           ? png_state.previous_image : NULL);
+       png_state.frame_count++;
+       if (png_state.previous_image)
+           gdImageDestroy(png_state.previous_image);
+       png_state.previous_image = png_state.image;
+       return;
+    }
+#endif
+
+    gdImageGif(png_state.image, gpoutfile);
+    gdImageDestroy(png_state.image);
+}
+
+#endif /* TERM_BODY */
+
+#ifdef TERM_TABLE
+
+TERM_TABLE_START(gif_driver)
+    "gif", "GIF images using libgd and TrueType fonts",
+    GREG_XMAX, GREG_YMAX, PNG_VCHAR, PNG_HCHAR,
+    PNG_TICSIZE, PNG_TICSIZE, PNG_options, PNG_init, PNG_reset,
+    GIF_text, null_scale, PNG_graphics, PNG_move, PNG_vector,
+    PNG_linetype, PNG_put_text, PNG_text_angle,
+    PNG_justify_text, PNG_point, do_arrow, PNG_set_font,
+    PNG_pointsize,
+    TERM_CAN_MULTIPLOT | TERM_BINARY,
+    0 /*suspend*/, 0 /*resume*/,
+    PNG_boxfill /*EAM - fillbox*/,
+    PNG_linewidth /*EAM - linewidth*/
+#ifdef USE_MOUSE
+    , 0, 0, 0, 0, 0 /* no mouse support */
+#endif
+    , PNG_make_palette,
+    0, /* previous_palette() ... no, single array of 256 colours for PNG */
+    PNG_set_color,
+    PNG_filled_polygon
+#ifdef WITH_IMAGE
+    , PNG_image
+#endif
+    , ENHGD_OPEN, ENHGD_FLUSH, do_enh_writec
+TERM_TABLE_END(gif_driver)
+
+#undef LAST_TERM
+#define LAST_TERM gif_driver
+
+#endif /* TERM_TABLE */
+#endif /* TERM_PROTO_ONLY */
+
+
+#ifdef TERM_HELP
+START_HELP(gif)
+"1 gif",
+"?commands set terminal gif",
+"?set terminal gif",
+"?set term gif",
+"?terminal gif",
+"?term gif",
+"?gif",
+"",
+" Syntax:",
+"       set terminal gif ",
+"                        {tiny | small | medium | large | giant}",
+"                        {{no}transparent} {{no}enhanced}",
+"                        {font <face> {<pointsize>}}",
+"                        {animate {delay <time>} {{no}optimize}}",
+"                        {size <x>,<y>} {{no}crop}",
+"                        {<color0> <color1> <color2> ...}",
+"",
+" GIF images are created using libgd, with optional support for TrueType",
+" fonts via libfreetype.",
+"",
+" GIF plots may be conveniently viewed by piping the output to the",
+" 'display' program from the ImageMagick package as follows:",
+"                set term gif",
+"                set output '| display gif:-'",
+"",
+" View the output from successive plot commands interactively by hitting",
+" <space> in the display window.  To save a particular one to disk, left",
+" click in the display window and choose `save`.",
+"",
+" Five basic fonts are supported directly by the gd library. These are",
+" `tiny` (5x8 pixels), `small` (6x12 pixels), `medium`, (7x13 Bold), ",
+" `large` (8x16) or `giant` (9x15 pixels). These fonts cannot be scaled",
+" or rotated (pure horizontal or vertical text only).",
+"",
+" `transparent` instructs the driver to generate transparent GIFs.  The first",
+" color will be the transparent one. Default is `notransparent`.",
+"",
+" `enhanced` enables the enhanced text processing features, (subscripts, ",
+" superscripts and mixed fonts). See `enhanced` for more information. ",
+" The full enhanced mode syntax is supported by the PNG/GIF driver itself,",
+" but some of these features are dependent on which version of the ",
+" underlying libgd library is present, and which fonts are available.",
+"",
+"=fonts",
+" If your local gd library was built with support for TrueType  and Adobe",
+" Type 1 fonts, they may be selected using the 'font <face> {<pointsize>}' ",
+" option. <face> is either the full pathname to the font file, or a font ",
+" face name that is assumed to be the first part of a filename in one of the ",
+" directories listed in the GDFONTPATH environmental variable. That is, ",
+" 'set term gif font \"Face\"' will look for a font file named either ",
+" <somedirectory>/Face.ttf or <somedirectory>/Face.pfa. Both TrueType and ",
+" Adobe Type 1 fonts are fully scalable and may be rotated through any angle.",
+" If no font is specified, gnuplot checks the environmental variable ",
+" GNUPLOT_DEFAULT_GDFONT to see if there is a preferred default font. ",
+"",
+" The `animate` option is available only if your local gd library supports",
+" the creation of animated gifs. The default delay between display of",
+" successive images may be specified in units of 1/100 second (default 5).",
+" The actual delay may vary depending on the program used as a viewer.",
+" An animation sequence is terminated by the next `set output` or `set term`",
+" command.  The `optimize` option has two effects on the animation.",
+"",
+" 1) A single color map is used for the entire animation. This requires",
+" that all colors used in any frame of the animation are already",
+" defined in the first frame.",
+"",
+" 2) If possible, only the portions of a frame that differ from the",
+" previous frame are stored in the animation file.  This space saving",
+" may not be possible if the animation uses transparency.",
+"",
+" Both of these optimizations are intended to produce a smaller output file,",
+" but the decrease in size is probably only significant for long animations",
+" or very small frame sizes.",
+" The `nooptimize` option turns off both of the effects just described.",
+" Each frame is stored in its entirety along with a private color map.",
+" Note that it is possible to post-process a non-optimized animation",
+" using external utilities, and this post-processing can yield a smaller",
+" file than gnuplot's internal optimization mode.",
+" The default is `nooptimize`.",
+"",
+" The size <x,y> is given in pixels---it defaults to 640x480.  The number of",
+" pixels can be also modified by scaling with the `set size` command.",
+" `crop` trims blank space from the edges of the completed plot, resulting",
+" in a smaller final image size. Default is `nocrop`.",
+"",
+" Each color must be of the form 'xrrggbb', where x is the literal character",
+" 'x' and 'rrggbb' are the red, green and blue components in hex.  For example,",
+" 'x00ff00' is green.  The background color is set first, then the border",
+" colors, then the X & Y axis colors, then the plotting colors.  The maximum",
+" number of colors that can be set is 256.",
+"",
+" Examples:",
+"       set terminal gif medium size 640,480 \\",
+"                        xffffff x000000 x404040 \\",
+"                        xff0000 xffa500 x66cdaa xcdb5cd \\",
+"                        xadd8e6 x0000ff xdda0dd x9500d3    # defaults",
+"",
+" which uses white for the non-transparent background, black for borders, gray",
+" for the axes, and red, orange, medium aquamarine, thistle 3, light blue, blue,",
+" plum and dark violet for eight plotting colors.",
+"",
+"       set terminal gif font 'arial' 14 size 800,600",
+"",
+" which searches for a TrueType font with face name 'arial' in the directory",
+" specified by the environment variable GDFONTPATH and 14pt font size.",
+""
+END_HELP(gif)
+#endif /* TERM_HELP */
+#endif /* HAVE_GD_GIF */
+#endif