/* 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 * Alex Woo * Ethan A Merritt * * CONTRIBUTORS * Alfred Reibenschuh or * Ben Laurie * * 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= 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 {}}", " {size ,} {{no}crop}", " {{no}enhanced}", " { ...}", "", " 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", " 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 {}' ", " option. 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 ", " /Face.ttf or /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 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 {}}", " {size ,} {{no}crop}", " {{no}enhanced}", " { ...}", "", " 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 {}' ", " option. 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 ", " /Face.ttf or /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 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 {}}", " {animate {delay