/* Hello, Emacs, this is -*-C-*- * $Id: canvas.trm,v 1.81 2017/05/02 19:41:14 sfeam Exp $ * */ /* GNUPLOT - canvas.trm */ /* * This file is included by ../term.c. * * This terminal driver supports: * W3C HTML tag * * AUTHOR * Bruce Lueckenhoff, Aug 2008 * Bruce_Lueckenhoff@yahoo.com * * Additions * Ethan A Merritt, Jan 2009 * CANVAS_set_color(), CANVAS_make_palette(), CANVAS_fillbox(), fillstyles, * CANVAS_point(), CANVAS_pointsize() * "name " option to create only a callable javascript file foo.js * "fsize " option to select font size (default remains 10.0) * Ethan A Merritt, Jan 2009 * Prototype mousing code in separate file gnuplot_mouse.js * Ethan A Merritt, Feb 2009 * Enhanced text support. Note: character placement could be done more * precisely by moving the enhanced text code into the javascript routines, * where exact character widths are known, and save/restore can be used. * Ethan A Merritt, Mar 2009 * Oversampling and client-side zoom/unzoom, hotkeys * Ethan A Merritt, May 2009 * Give each plot its own namespace for mousing (allows multiple mouseable * plots in a single HTML document). * Ethan A Merritt, Nov 2010 * Dashed line support, butt/rounded line properties * revised javascript with all methods and variables in container gnuplot() * Ethan A Merritt, Feb/Mar/Apr 2011 * Optional explicit background * Wrap each plot in a test for gnuplot.hide_plot_N * Handle image data by storing it in a parallel PNG file * Ethan A Merritt, Mar 2012 * Hypertext * * send your comments or suggestions to (gnuplot-info@lists.sourceforge.net). * */ #include "driver.h" #ifdef TERM_REGISTER register_term(canvas_driver) #endif #ifdef TERM_PROTO TERM_PUBLIC void CANVAS_options __PROTO((void)); TERM_PUBLIC void CANVAS_init __PROTO((void)); TERM_PUBLIC void CANVAS_graphics __PROTO((void)); TERM_PUBLIC int CANVAS_justify_text __PROTO((enum JUSTIFY mode)); TERM_PUBLIC void CANVAS_text __PROTO((void)); TERM_PUBLIC void CANVAS_reset __PROTO((void)); TERM_PUBLIC void CANVAS_linetype __PROTO((int linetype)); TERM_PUBLIC void CANVAS_dashtype __PROTO((int type, t_dashtype *custom_dash_type)); TERM_PUBLIC void CANVAS_fillbox __PROTO((int style, unsigned int x1, unsigned int y1, unsigned int width, unsigned int height)); TERM_PUBLIC void CANVAS_linewidth __PROTO((double linewidth)); TERM_PUBLIC void CANVAS_move __PROTO((unsigned int x, unsigned int y)); TERM_PUBLIC void CANVAS_vector __PROTO((unsigned int x, unsigned int y)); TERM_PUBLIC void CANVAS_point __PROTO((unsigned int x, unsigned int y, int number)); TERM_PUBLIC void CANVAS_pointsize __PROTO((double size)); TERM_PUBLIC void CANVAS_put_text __PROTO((unsigned int x, unsigned int y, const char *str)); TERM_PUBLIC int CANVAS_text_angle __PROTO((int ang)); TERM_PUBLIC void CANVAS_filled_polygon __PROTO((int, gpiPoint *)); TERM_PUBLIC void CANVAS_set_color __PROTO((t_colorspec *colorspec)); TERM_PUBLIC int CANVAS_make_palette __PROTO((t_sm_palette *palette)); TERM_PUBLIC void CANVAS_layer __PROTO((t_termlayer)); TERM_PUBLIC void CANVAS_path __PROTO((int)); TERM_PUBLIC void CANVAS_hypertext __PROTO((int, const char *)); TERM_PUBLIC int CANVAS_set_font __PROTO((const char *)); TERM_PUBLIC void ENHCANVAS_OPEN __PROTO((char *, double, double, TBOOLEAN, TBOOLEAN, int)); TERM_PUBLIC void ENHCANVAS_FLUSH __PROTO((void)); TERM_PUBLIC void ENHCANVAS_put_text __PROTO((unsigned int, unsigned int, const char *)); #define CANVAS_OVERSAMPLE 10. #define CANVAS_XMAX (600 * CANVAS_OVERSAMPLE) #define CANVAS_YMAX (400 * CANVAS_OVERSAMPLE) #define CANVASVTIC (10 * CANVAS_OVERSAMPLE) #define CANVASHTIC (10 * CANVAS_OVERSAMPLE) #define CANVASVCHAR (10 * CANVAS_OVERSAMPLE) #define CANVASHCHAR (8 * CANVAS_OVERSAMPLE) #endif /* TERM_PROTO */ #ifdef TERM_BODY #define CANVAS_AXIS_CONST '\1' #define CANVAS_BORDER_CONST '\2' static int canvas_x = -1; /* current X position */ static int canvas_y = -1; /* current Y position */ static int canvas_xmax = CANVAS_XMAX; static int canvas_ymax = CANVAS_YMAX; static int canvas_line_type = LT_UNDEFINED; static int canvas_dash_type = DASHTYPE_SOLID; static double canvas_linewidth = 1.0; static double canvas_dashlength_factor = 1.0; static double CANVAS_ps = 1; /* pointsize multiplier */ static double CANVAS_default_fsize = 10; static double canvas_font_size = 10; static double canvas_fontscale = 1.0; static char * canvas_font_name = NULL; static char *canvas_justify = ""; static int canvas_text_angle = 0; static int canvas_in_a_path = FALSE; static int already_closed = FALSE; static TBOOLEAN canvas_dashed = TRUE; /* Version 5: dashes always enabled */ static t_linecap canvas_linecap = ROUNDED; static TBOOLEAN CANVAS_mouseable = FALSE; static TBOOLEAN CANVAS_standalone = TRUE; static char CANVAS_background[18] = {'\0'}; static char *CANVAS_name = NULL; static char *CANVAS_scriptdir = NULL; static char *CANVAS_title = NULL; static char *CANVAS_hypertext_text = NULL; /* * Stuff for tracking images stored in separate files * to be referenced by canvas.drawImage(); */ static int CANVAS_imageno = 0; typedef struct canvas_imagefile { int imageno; /* Used to generate the internal name */ char *filename; /* The parallel file that it maps to */ struct canvas_imagefile *next; } canvas_imagefile; static canvas_imagefile *imagelist = NULL; static struct { int previous_linewidth; double alpha; /* alpha channel */ char color[18]; /* rgb(rrr,ggg,bbb) */ char previous_color[18]; /* rgb(rrr,ggg,bbb) */ char previous_fill[24]; /* rgba(rrr,ggg,bbb,aaaa) */ int plotno; /* Which plot are we in? */ } canvas_state; enum CANVAS_case { CANVAS_SIZE, CANVAS_FONT, CANVAS_FSIZE, CANVAS_NAME, CANVAS_STANDALONE, CANVAS_TITLE, CANVAS_LINEWIDTH, CANVAS_MOUSING, CANVAS_JSDIR, CANVAS_ENH, CANVAS_NOENH, CANVAS_FONTSCALE, CANVAS_SOLID, CANVAS_DASHED, CANVAS_DASHLENGTH, CANVAS_ROUNDED, CANVAS_BUTT, CANVAS_SQUARE, CANVAS_BACKGROUND, CANVAS_OTHER }; static struct gen_table CANVAS_opts[] = { { "font", CANVAS_FONT }, { "fsize", CANVAS_FSIZE }, { "name", CANVAS_NAME }, { "size", CANVAS_SIZE }, { "standalone", CANVAS_STANDALONE }, { "mous$ing", CANVAS_MOUSING }, { "mouse", CANVAS_MOUSING }, { "js$dir", CANVAS_JSDIR }, { "enh$anced", CANVAS_ENH }, { "noenh$anced", CANVAS_NOENH }, { "lw", CANVAS_LINEWIDTH }, { "linew$idth", CANVAS_LINEWIDTH }, { "title", CANVAS_TITLE }, { "fontscale", CANVAS_FONTSCALE }, { "solid", CANVAS_SOLID }, { "dash$ed", CANVAS_DASHED }, { "dashl$ength", CANVAS_DASHLENGTH }, { "dl", CANVAS_DASHLENGTH }, { "round$ed", CANVAS_ROUNDED }, { "butt", CANVAS_BUTT }, { "square", CANVAS_SQUARE }, { "backg$round", CANVAS_BACKGROUND }, { NULL, CANVAS_OTHER } }; /* Fill patterns */ #define PATTERN1 "tile.moveTo(0,0); tile.lineTo(32,32); tile.moveTo(0,16); tile.lineTo(16,32); tile.moveTo(16,0); tile.lineTo(32,16);" #define PATTERN2 "tile.moveTo(0,32); tile.lineTo(32,0); tile.moveTo(0,16); tile.lineTo(16,0); tile.moveTo(16,32); tile.lineTo(32,16);" #define PATTERN3 "tile.moveTo(8,0); tile.lineTo(32,24); tile.moveTo(0,8); tile.lineTo(24,32); tile.moveTo(24,0); tile.lineTo(32,8); tile.moveTo(0,24); tile.lineTo(8,32); tile.moveTo(8,32); tile.lineTo(32,8); tile.moveTo(0,24); tile.lineTo(24,0); tile.moveTo(24,32); tile.lineTo(32,24); tile.moveTo(0,8); tile.lineTo(8,0);" static void CANVAS_start (void) { if (canvas_in_a_path) return; fprintf(gpoutfile, "ctx.beginPath();\n"); canvas_in_a_path = TRUE; already_closed = FALSE; } static void CANVAS_finish (void) { if (!canvas_in_a_path) return; fprintf(gpoutfile, "ctx.stroke();\n"); if (!already_closed) fprintf(gpoutfile, "ctx.closePath();\n"); canvas_in_a_path = FALSE; already_closed = TRUE; } TERM_PUBLIC void CANVAS_options() { int canvas_background = 0; if (!almost_equals(c_token-1, "termopt$ion")) { /* Re-initialize a few things */ canvas_font_size = CANVAS_default_fsize = 10; canvas_fontscale = 1.0; CANVAS_standalone = TRUE; CANVAS_mouseable = FALSE; free(CANVAS_name); CANVAS_name = NULL; free(CANVAS_title); CANVAS_title = NULL; free(CANVAS_scriptdir); CANVAS_scriptdir = NULL; canvas_linewidth = 1.0; canvas_dashed = TRUE; canvas_dashlength_factor = 1.0; CANVAS_background[0] = '\0'; /* Default to enhanced text mode */ term->put_text = ENHCANVAS_put_text; term->flags |= TERM_ENHANCED_TEXT; } while (!END_OF_COMMAND) { switch(lookup_table(&CANVAS_opts[0],c_token++)) { case CANVAS_SIZE: if (END_OF_COMMAND) { canvas_xmax = CANVAS_XMAX; canvas_ymax = CANVAS_YMAX; } else { canvas_xmax = int_expression() * CANVAS_OVERSAMPLE; if (equals(c_token,",")) { c_token++; canvas_ymax = int_expression() * CANVAS_OVERSAMPLE; } } if (canvas_xmax <= 0) canvas_xmax = CANVAS_XMAX; if (canvas_ymax <= 0) canvas_ymax = CANVAS_YMAX; term->xmax = canvas_xmax; term->ymax = canvas_ymax; break; case CANVAS_TITLE: CANVAS_title = try_to_get_string(); if (!CANVAS_title) int_error(c_token,"expecting an HTML title string"); break; case CANVAS_NAME: CANVAS_name = try_to_get_string(); if (!CANVAS_name) int_error(c_token,"expecting a javascript function name"); if (CANVAS_name[strspn(CANVAS_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_1234567890")]) int_error(c_token-1,"illegal javascript function name"); CANVAS_standalone = FALSE; break; case CANVAS_STANDALONE: CANVAS_standalone = TRUE; break; case CANVAS_FONT: /* FIXME: See note at CANVAS_set_font() */ free(canvas_font_name); if (!(canvas_font_name = try_to_get_string())) int_error(c_token,"font: expecting string"); CANVAS_set_font(canvas_font_name); break; case CANVAS_FSIZE: CANVAS_default_fsize = real_expression(); if (CANVAS_default_fsize <= 0) CANVAS_default_fsize = 10; canvas_font_size = CANVAS_default_fsize; break; case CANVAS_MOUSING: CANVAS_mouseable = TRUE; break; case CANVAS_JSDIR: CANVAS_scriptdir = try_to_get_string(); break; case CANVAS_ENH: term->put_text = ENHCANVAS_put_text; term->flags |= TERM_ENHANCED_TEXT; break; case CANVAS_NOENH: term->put_text = CANVAS_put_text; term->flags &= ~TERM_ENHANCED_TEXT; break; case CANVAS_LINEWIDTH: canvas_linewidth = real_expression(); if (canvas_linewidth <= 0) canvas_linewidth = 1.0; break; case CANVAS_FONTSCALE: canvas_fontscale = real_expression(); if (canvas_fontscale <= 0) canvas_fontscale = 1.0; break; case CANVAS_SOLID: case CANVAS_DASHED: /* Version 5 always allows dashes */ canvas_dashed = TRUE; break; case CANVAS_DASHLENGTH: canvas_dashlength_factor = real_expression(); if (canvas_dashlength_factor <= 0.2) canvas_dashlength_factor = 1.0; break; case CANVAS_ROUNDED: canvas_linecap = ROUNDED; break; case CANVAS_BUTT: canvas_linecap = BUTT; break; case CANVAS_SQUARE: canvas_linecap = SQUARE; break; case CANVAS_BACKGROUND: canvas_background = parse_color_name(); sprintf(CANVAS_background,"rgb(%03d,%03d,%03d)", (canvas_background >> 16) & 0xff, (canvas_background >> 8) & 0xff, canvas_background & 0xff); break; default: int_warn(c_token-1,"unrecognized terminal option"); break; } } term->v_char = canvas_font_size * canvas_fontscale * CANVAS_OVERSAMPLE; term->h_char = canvas_font_size * canvas_fontscale * 0.8 * CANVAS_OVERSAMPLE; if (canvas_dashlength_factor != 1.0) sprintf(term_options + strlen(term_options), " dashlength %3.1f", canvas_dashlength_factor); sprintf(term_options + strlen(term_options), canvas_linecap == ROUNDED ? " rounded" : canvas_linecap == SQUARE ? " square" : " butt"); sprintf(term_options + strlen(term_options), " size %d,%d", (int)(term->xmax/CANVAS_OVERSAMPLE), (int)(term->ymax/CANVAS_OVERSAMPLE)); sprintf(term_options + strlen(term_options), "%s fsize %g lw %g", term->put_text == ENHCANVAS_put_text ? " enhanced" : "", canvas_font_size, canvas_linewidth); sprintf(term_options + strlen(term_options), " fontscale %g", canvas_fontscale); if (*CANVAS_background) sprintf(term_options + strlen(term_options), " background \"#%06x\"", canvas_background); if (CANVAS_name) sprintf(term_options + strlen(term_options), " name \"%s\"", CANVAS_name); else { sprintf(term_options + strlen(term_options), " standalone"); if (CANVAS_mouseable) sprintf(term_options + strlen(term_options), " mousing"); if (CANVAS_title) sprintf(term_options + strlen(term_options), " title \"%s\"", CANVAS_title); } if (CANVAS_scriptdir) sprintf(term_options + strlen(term_options), " jsdir \"%s\"", CANVAS_scriptdir); } TERM_PUBLIC void CANVAS_init() { } TERM_PUBLIC void CANVAS_graphics() { #if !defined(VMS) int len; #endif /* Force initialization at the beginning of each plot */ canvas_line_type = LT_UNDEFINED; canvas_text_angle = 0; canvas_in_a_path = FALSE; canvas_state.previous_linewidth = -1; canvas_state.previous_color[0] = '\0'; canvas_state.previous_fill[0] = '\0'; strcpy(canvas_state.color,"rgb(000,000,000)"); canvas_state.plotno = 0; /* * FIXME: This code could be shared with svg.trm * Figure out where to find javascript support routines * when the page is viewed. */ if (CANVAS_scriptdir == NULL) { #ifdef GNUPLOT_JS_DIR # if defined(_WIN32) CANVAS_scriptdir = RelativePathToGnuplot(GNUPLOT_JS_DIR); # else /* !_WIN32 */ /* use hardcoded _absolute_ path */ CANVAS_scriptdir = strdup(GNUPLOT_JS_DIR); # endif #else CANVAS_scriptdir = strdup(""); #endif /* GNUPLOT_JS_DIR */ } #if !defined(VMS) len = strlen(CANVAS_scriptdir); # if defined(_WIN32) if (*CANVAS_scriptdir && CANVAS_scriptdir[len-1] != '\\' && CANVAS_scriptdir[len-1] != '/') { CANVAS_scriptdir = gp_realloc(CANVAS_scriptdir, len+2, "jsdir"); if (CANVAS_scriptdir[len-1] == '\\') /* use backslash if used in jsdir, otherwise slash */ strcat(CANVAS_scriptdir,"\\"); else strcat(CANVAS_scriptdir,"/"); } # else if (*CANVAS_scriptdir && CANVAS_scriptdir[len-1] != '/') { CANVAS_scriptdir = gp_realloc(CANVAS_scriptdir, len+2, "jsdir"); strcat(CANVAS_scriptdir,"/"); } # endif #endif if (CANVAS_standalone) { fprintf(gpoutfile, "\n" "\n" "\n" "%s\n", CANVAS_title ? CANVAS_title : "Gnuplot Canvas Graph" ); if (encoding == S_ENC_UTF8 || encoding == S_ENC_DEFAULT) fprintf(gpoutfile, "\n" ); fprintf(gpoutfile, "\n" "\n" "\n" , CANVAS_scriptdir , (encoding == S_ENC_UTF8) ? "canvasmath" : "canvastext" , CANVAS_scriptdir); if (canvas_dashed) fprintf(gpoutfile, "\n" , CANVAS_scriptdir); if (CANVAS_mouseable) { fprintf(gpoutfile, "\n" , CANVAS_scriptdir); fprintf(gpoutfile, "\n" , CANVAS_scriptdir); } else { fprintf(gpoutfile, "\n"); } fprintf(gpoutfile, "\n" "\n" "\n" "\n\n" "
\n", CANVAS_scriptdir ? CANVAS_scriptdir : "" ); /* Pattern-fill requires having a canvas element to hold a template * pattern tile. Define it here but try to hide it (may not work in IE?) */ fprintf(gpoutfile, "\n"); /* The format of the plot box and in particular the mouse tracking box * are determined by the CSS specs in customizable file gnuplot_mouse.css * We could make this even more customizable by providing an external HTML * template, but in that case the user might as well just create a *.js * file and provide his own wrapping HTML document. */ if (CANVAS_mouseable) { int i; fprintf(gpoutfile, "
\n" "\n" " \n" "
\n" " \n" " \n" " \n" "
\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n", CANVAS_scriptdir ? CANVAS_scriptdir : "", CANVAS_scriptdir ? CANVAS_scriptdir : "", CANVAS_scriptdir ? CANVAS_scriptdir : "", CANVAS_scriptdir ? CANVAS_scriptdir : "", CANVAS_scriptdir ? CANVAS_scriptdir : "" ); /* Table row of plot toggle icons goes here */ for (i=1; i<=6*((canvas_state.plotno+5)/6); i++) { if ((i%6) == 1) fprintf(gpoutfile, " \n"); if (i<=canvas_state.plotno) fprintf(gpoutfile, " \n",i,i); else fprintf(gpoutfile, " \n"); if ((i%6) == 0) fprintf(gpoutfile, " \n"); } fprintf(gpoutfile, "
\"#\"\"unzoom\"\"rezoom\"\"zoom\"?\"
%d
\n" "
\n" ); fprintf(gpoutfile, "\n" "\n" "\n" ); if ((axis_array[SECOND_X_AXIS].ticmode & TICS_MASK) != NO_TICS) fprintf(gpoutfile, "\n"); if ((axis_array[SECOND_Y_AXIS].ticmode & TICS_MASK) != NO_TICS) fprintf(gpoutfile, "\n"); fprintf(gpoutfile, "
 
 
x2   
y2   
\n" ); fprintf(gpoutfile, "
\n" ); } /* End if (CANVAS_mouseable) */ fprintf(gpoutfile, "\n" "\n" "
\n" " \n" " Sorry, your browser seems not to support the HTML 5 canvas element\n" " \n" "
\n", (int)(term->xmax/CANVAS_OVERSAMPLE), (int)(term->ymax/CANVAS_OVERSAMPLE) ); if (CANVAS_mouseable) { fprintf(gpoutfile, "
\n" ); } fprintf(gpoutfile, "
\n\n" "\n" "\n" ); } fflush(gpoutfile); } TERM_PUBLIC void CANVAS_reset() { ; } TERM_PUBLIC void CANVAS_linetype(int linetype) { /* NB: These values are manipulated as numbers; */ /* it does not work to give only the color name */ static const char * pen_type[17] = { "rgb(255,255,255)", /* should be background */ "rgb(000,000,000)", /* black */ "rgb(160,160,160)", /* grey */ "rgb(255,000,000)", /* red */ "rgb(000,171,000)", /* green */ "rgb(000,000,225)", /* blue */ "rgb(190,000,190)", /* purple */ "rgb(000,255,255)", /* cyan */ "rgb(021,117,069)", /* pine green*/ "rgb(000,000,148)", /* navy */ "rgb(255,153,000)", /* orange */ "rgb(000,153,161)", /* green blue*/ "rgb(214,214,069)", /* olive*/ "rgb(163,145,255)", /* cornflower*/ "rgb(255,204,000)", /* gold*/ "rgb(214,000,120)", /* mulberry*/ "rgb(171,214,000)", /* green yellow*/ }; // FIXME // if (linetype == canvas_line_type) return; canvas_line_type = linetype; CANVAS_finish(); if (linetype >= 14) linetype %= 14; if (linetype <= LT_NODRAW) /* LT_NODRAW, LT_BACKGROUND, LT_UNDEFINED */ linetype = -3; if (linetype == -3 && *CANVAS_background) strcpy(canvas_state.color,CANVAS_background); else strcpy(canvas_state.color,pen_type[linetype + 3]); if (strcmp(canvas_state.color, canvas_state.previous_color)) { fprintf(gpoutfile, "ctx.strokeStyle = \"%s\";\n", canvas_state.color); strcpy(canvas_state.previous_color, canvas_state.color); } } TERM_PUBLIC void CANVAS_dashtype(int type, t_dashtype *custom_dash_type) { if (canvas_line_type == LT_AXIS) type = DASHTYPE_AXIS; switch (type) { case DASHTYPE_AXIS: fprintf(gpoutfile, "DT(gnuplot.dashpattern3);\n"); break; case DASHTYPE_SOLID: if (canvas_dash_type != DASHTYPE_SOLID) fprintf(gpoutfile, "DT(gnuplot.solid);\n"); break; default: type = type % 5; if (canvas_dash_type != type) fprintf(gpoutfile, "DT(gnuplot.dashpattern%1d);\n", type + 1); break; case DASHTYPE_CUSTOM: if (custom_dash_type) { double length = 0; double cumulative = 0; int i; for (i = 0; i < DASHPATTERN_LENGTH && custom_dash_type->pattern[i] > 0; i++) length += custom_dash_type->pattern[i]; fprintf(gpoutfile, "DT(["); for (i = 0; i < DASHPATTERN_LENGTH && custom_dash_type->pattern[i] > 0; i++) { cumulative += custom_dash_type->pattern[i]; fprintf(gpoutfile, " %4.2f,", cumulative / length); } fprintf(gpoutfile, " 0]);\n"); } break; } canvas_dash_type = type; } TERM_PUBLIC void CANVAS_move(unsigned int arg_x, unsigned int arg_y) { if (canvas_in_a_path && (canvas_x == arg_x) && (canvas_y == arg_y)) { return; } CANVAS_start(); fprintf(gpoutfile, "M(%u,%u);\n", arg_x, canvas_ymax - arg_y); canvas_x = arg_x; canvas_y = arg_y; } TERM_PUBLIC void CANVAS_vector(unsigned int arg_x, unsigned int arg_y) { if ((canvas_x == arg_x) && (canvas_y == arg_y)) return; if (!canvas_in_a_path) { /* Force a new path */ CANVAS_move(canvas_x, canvas_y); } fprintf(gpoutfile, "L(%u,%u);\n", arg_x, canvas_ymax - arg_y ); canvas_x = arg_x; canvas_y = arg_y; } TERM_PUBLIC int CANVAS_justify_text(enum JUSTIFY mode) { switch (mode) { case (CENTRE): canvas_justify = "Center"; break; case (RIGHT): canvas_justify = "Right"; break; default: case (LEFT): canvas_justify = ""; break; } return (TRUE); } TERM_PUBLIC void CANVAS_point(unsigned int x, unsigned int y, int number) { double width = CANVAS_ps * 0.6 * CANVASHTIC; int pt = number % 9; /* Skip size 0 points; the alternative would be to draw a Dot instead */ if (width <= 0 && pt >= 0) return; CANVAS_finish(); switch (pt) { default: fprintf(gpoutfile, "Dot(%d,%d);\n", x, (canvas_ymax-y)); break; case 4: case 6: case 8: if (strcmp(canvas_state.previous_fill, canvas_state.color)) { fprintf(gpoutfile, "ctx.fillStyle = \"%s\";\n", canvas_state.color); strcpy(canvas_state.previous_fill, canvas_state.color); } /* Fall through */ case 0: case 1: case 2: case 3: case 5: case 7: fprintf(gpoutfile, "Pt(%d,%d,%d,%.1f);\n", pt, x, (canvas_ymax-y), width); break; } /* Hypertext support */ if (CANVAS_hypertext_text) { /* EAM DEBUG: embedded \n produce illegal javascript output */ while (strchr(CANVAS_hypertext_text,'\n')) *strchr(CANVAS_hypertext_text,'\n') = '\v'; fprintf(gpoutfile, "Hypertext(%d,%d,%.1f,\"%s\");\n", x, (canvas_ymax-y), width, CANVAS_hypertext_text); free(CANVAS_hypertext_text); CANVAS_hypertext_text = NULL; } } TERM_PUBLIC void CANVAS_pointsize(double ptsize) { if (ptsize < 0) CANVAS_ps = 1; else CANVAS_ps = ptsize; } TERM_PUBLIC int CANVAS_text_angle(int ang) { canvas_text_angle = -1 * ang; return TRUE; } TERM_PUBLIC void CANVAS_put_text(unsigned int x, unsigned int y, const char *str) { if (!str || !(*str)) return; CANVAS_finish(); /* ctx.fillText uses fillStyle rather than strokeStyle */ if (strcmp(canvas_state.previous_fill, canvas_state.color)) { fprintf(gpoutfile, "ctx.fillStyle = \"%s\";\n", canvas_state.color); strcpy(canvas_state.previous_fill, canvas_state.color); } if (0 != canvas_text_angle) { fprintf(gpoutfile, "TR(%d,%d,%d,%.1f,\"%s\",\"", x, (int)(canvas_ymax + 5*CANVAS_OVERSAMPLE - y), canvas_text_angle, canvas_font_size * canvas_fontscale, canvas_justify); } else { fprintf(gpoutfile, "T(%d,%d,%.1f,\"%s\",\"", x, (int)(canvas_ymax + 5*CANVAS_OVERSAMPLE - y), canvas_font_size * canvas_fontscale, canvas_justify); } /* Sanitize string by escaping quote characters */ do { if (*str == '"' || *str == '\\') fputc('\\', gpoutfile); fputc(*str++, gpoutfile); } while (*str); fprintf(gpoutfile, "\");\n"); } TERM_PUBLIC void CANVAS_linewidth(double linewidth) { CANVAS_finish(); if (canvas_state.previous_linewidth != linewidth) { fprintf(gpoutfile, "ctx.lineWidth = %g;\n", linewidth * canvas_linewidth); canvas_state.previous_linewidth = linewidth; } } TERM_PUBLIC void CANVAS_set_color(t_colorspec *colorspec) { rgb255_color rgb255; canvas_state.alpha = 0.0; if (colorspec->type == TC_LT) { CANVAS_linetype(colorspec->lt); return; } else if (colorspec->type == TC_RGB) { rgb255.r = (colorspec->lt >> 16) & 0xff; rgb255.g = (colorspec->lt >> 8) & 0xff; rgb255.b = colorspec->lt & 0xff; canvas_state.alpha = (double)((colorspec->lt >> 24) & 0xff)/255.; } else if (colorspec->type == TC_FRAC) { rgb255maxcolors_from_gray(colorspec->value, &rgb255); } else /* Other color types not yet supported */ return; CANVAS_finish(); sprintf(canvas_state.color,"rgb(%03d,%03d,%03d)", rgb255.r, rgb255.g, rgb255.b); if (strcmp(canvas_state.color, canvas_state.previous_color)) { if (canvas_state.alpha > 0) fprintf(gpoutfile, "ctx.strokeStyle = \"rgba(%11.11s,%4.2f)\";\n", &canvas_state.color[4], 1.0 - canvas_state.alpha); else fprintf(gpoutfile, "ctx.strokeStyle = \"%s\";\n", canvas_state.color); strcpy(canvas_state.previous_color, canvas_state.color); } canvas_line_type = LT_UNDEFINED; } TERM_PUBLIC int CANVAS_make_palette(t_sm_palette *palette) { /* We can do full RGB color */ return 0; } static char * CANVAS_fillstyle(int style) { float density = (float)(style >> 4) / 100.; int pattern = style >> 4; TBOOLEAN transparent_pattern = TRUE; static char fillcolor[24]; switch (style & 0xf) { case FS_TRANSPARENT_SOLID: sprintf(fillcolor,"rgba(%11.11s,%4.2f)%c", &canvas_state.color[4], density, '\0'); break; case FS_EMPTY: strcpy(fillcolor,"rgba(255,255,255,0.00)"); break; case FS_PATTERN: transparent_pattern = FALSE; case FS_TRANSPARENT_PATTERN: if (pattern%6 == 3) { strcpy(fillcolor,canvas_state.color); strcpy(canvas_state.previous_fill, ""); break; } /* Write one copy of the pattern into the Tile element. */ fprintf(gpoutfile, "var template = document.getElementById('Tile');\n" "var tile = template.getContext('2d');\n" "tile.clearRect(0,0,32,32);\n"); if (!transparent_pattern) fprintf(gpoutfile, "tile.fillStyle = \"%s\"; tile.fillRect(0,0,32,32);\n", (*CANVAS_background) ? CANVAS_background : "white"); fprintf(gpoutfile, "tile.beginPath();\n"); switch(pattern%6) { case 1: fprintf(gpoutfile,"%s %s\n",PATTERN1,PATTERN2); break; case 2: fprintf(gpoutfile,"%s %s %s\n",PATTERN1,PATTERN2,PATTERN3); break; case 4: fprintf(gpoutfile,"%s\n",PATTERN1); break; case 5: fprintf(gpoutfile,"%s\n",PATTERN2); break; default: break; } fprintf(gpoutfile, "tile.strokeStyle=\"%s\"; tile.lineWidth=\"2\"; tile.stroke();\n", canvas_state.color); /* Then load it as a fill style */ fprintf(gpoutfile, "ctx.fillStyle = ctx.createPattern(template,\"repeat\");\n"); strcpy(fillcolor,"pattern"); break; case FS_SOLID: if (canvas_state.alpha > 0) { sprintf(fillcolor,"rgba(%11.11s,%4.2f)%c", &canvas_state.color[4], 1.0 - canvas_state.alpha, '\0'); } else if (density == 1) { strcpy(fillcolor,canvas_state.color); } else { int r = atoi(&canvas_state.color[4]); int g = atoi(&canvas_state.color[8]); int b = atoi(&canvas_state.color[12]); r = (float)r*density + 255.*(1.-density); g = (float)g*density + 255.*(1.-density); b = (float)b*density + 255.*(1.-density); sprintf(fillcolor,"rgb(%3d,%3d,%3d)%c", r, g, b, '\0'); } break; default: /* Use current color, wherever it came from */ sprintf(fillcolor,"%s%c",canvas_state.color,'\0'); } return fillcolor; } TERM_PUBLIC void CANVAS_filled_polygon(int points, gpiPoint *corners) { int i; CANVAS_finish(); /* FIXME: I do not understand why this is necessary, but without it */ /* a dashed line followed by a filled area fails to fill. */ if (canvas_dashed) { fprintf(gpoutfile, "DT(gnuplot.solid);\n"); canvas_line_type = LT_UNDEFINED; } if (corners->style != FS_OPAQUE && corners->style != FS_DEFAULT) { char *fillcolor = CANVAS_fillstyle(corners->style); if (strcmp(fillcolor,"pattern")) if (strcmp(canvas_state.previous_fill, fillcolor)) { fprintf(gpoutfile, "ctx.fillStyle = \"%s\";\n", fillcolor); strcpy(canvas_state.previous_fill, fillcolor); } } fprintf(gpoutfile, "bp(%d, %d);\n" , corners[0].x, canvas_ymax - corners[0].y); for (i = 1; i < points; i++) { fprintf(gpoutfile, "L(%d, %d);\n" , corners[i].x, canvas_ymax - corners[i].y); } if (corners->style != FS_OPAQUE && corners->style != FS_DEFAULT) /* Fill with separate fillStyle color */ fprintf(gpoutfile, "cfp();\n"); else /* Fill with stroke color */ fprintf(gpoutfile, "cfsp();\n"); } TERM_PUBLIC void CANVAS_fillbox(int style, unsigned int x1, unsigned int y1, unsigned int width, unsigned int height) { char *fillcolor = CANVAS_fillstyle(style); /* FIXME: I do not understand why this is necessary, but without it */ /* a dashed line followed by a filled area fails to fill. */ if (canvas_dashed) { fprintf(gpoutfile, "DT(gnuplot.solid);\n"); canvas_line_type = LT_UNDEFINED; } /* Since filled-rectangle is a primitive operation for the canvas element */ /* it's worth making this a special case rather than using filled_polygon */ if (strcmp(fillcolor,"pattern")) if (strcmp(canvas_state.previous_fill, fillcolor)) { fprintf(gpoutfile, "ctx.fillStyle = \"%s\";\n", fillcolor); strcpy(canvas_state.previous_fill, fillcolor); } fprintf(gpoutfile, "R(%d,%d,%d,%d);\n", x1, canvas_ymax - (y1+height), width, height); } TERM_PUBLIC void CANVAS_layer(t_termlayer layer) { char *basename = (CANVAS_name) ? CANVAS_name : "gp"; switch (layer) { case TERM_LAYER_BEFORE_PLOT: canvas_state.plotno++; CANVAS_finish(); fprintf(gpoutfile, "if (typeof(gnuplot.hide_%s_plot_%d) == \"undefined\"" "|| !gnuplot.hide_%s_plot_%d) {\n", basename, canvas_state.plotno, basename, canvas_state.plotno); break; case TERM_LAYER_AFTER_PLOT: CANVAS_finish(); fprintf(gpoutfile,"} // End %s_plot_%d \n", basename, canvas_state.plotno); canvas_line_type = LT_UNDEFINED; canvas_dash_type = LT_UNDEFINED; canvas_state.previous_linewidth = -1; canvas_state.previous_color[0] = '\0'; canvas_state.previous_fill[0] = '\0'; break; case TERM_LAYER_BEGIN_GRID: fprintf(gpoutfile, "if (gnuplot.grid_lines) {\n" "var saveWidth = ctx.lineWidth;\n" "ctx.lineWidth = ctx.lineWidth * 0.5;\n"); break; case TERM_LAYER_END_GRID: fprintf(gpoutfile, "ctx.lineWidth = saveWidth;\n" "} // grid_lines\n"); break; case TERM_LAYER_RESET: case TERM_LAYER_RESET_PLOTNO: canvas_state.plotno = 0; break; default: break; } } TERM_PUBLIC void CANVAS_path(int p) { switch (p) { case 1: /* Close path */ fprintf(gpoutfile, "ctx.closePath();\n"); already_closed = TRUE; break; case 0: break; } } TERM_PUBLIC void CANVAS_hypertext(int type, const char *hypertext) { if (type != TERM_HYPERTEXT_TOOLTIP) return; free(CANVAS_hypertext_text); if (hypertext) CANVAS_hypertext_text = gp_strdup(hypertext); else CANVAS_hypertext_text = NULL; } /* FIXME: The HTML5 canvas element did not originally provide any */ /* support for text output, let alone fonts.There are now text routines */ /* in the HTML5 draft spec, but they are not yet widely supported. */ /* So for now we will accept a font request, but ignore it except for */ /* the size. */ /* Eventually the text calls will look something like */ /* ctx.font = "12pt Times"; */ /* ctx.textAlign = "center"; */ /* ctx.fillText( "Oh goody, text support.", x, y ); */ /* */ TERM_PUBLIC int CANVAS_set_font(const char *newfont) { int sep; if (!newfont || !(*newfont)) canvas_font_size = CANVAS_default_fsize; else { sep = strcspn(newfont,","); if (newfont[sep] == ',') { sscanf(&(newfont[sep+1]), "%lf", &canvas_font_size); if (canvas_font_size <= 0) canvas_font_size = CANVAS_default_fsize; } } term->v_char = canvas_font_size * canvas_fontscale * CANVAS_OVERSAMPLE; term->h_char = canvas_font_size * canvas_fontscale * 0.8 * CANVAS_OVERSAMPLE; return 1; } /* Enhanced text mode support starts here */ static double ENHCANVAS_base = 0.0; static double ENHCANVAS_fontsize = 0; static TBOOLEAN ENHCANVAS_opened_string = FALSE; static TBOOLEAN ENHCANVAS_show = TRUE; static TBOOLEAN ENHCANVAS_sizeonly = FALSE; static TBOOLEAN ENHCANVAS_widthflag = TRUE; static TBOOLEAN ENHCANVAS_overprint = FALSE; TERM_PUBLIC void ENHCANVAS_OPEN( char *fontname, double fontsize, double base, TBOOLEAN widthflag, TBOOLEAN showflag, int overprint) { static int save_x, save_y; /* overprint = 1 means print the base text (leave position in center) * overprint = 2 means print the overlying text * overprint = 3 means save current position * overprint = 4 means restore saved position */ if (overprint == 3) { save_x = canvas_x; save_y = canvas_y; return; } if (overprint == 4) { canvas_x = save_x; canvas_y = save_y; return; } if (!ENHCANVAS_opened_string) { ENHCANVAS_opened_string = TRUE; enhanced_cur_text = &enhanced_text[0]; ENHCANVAS_fontsize = fontsize; ENHCANVAS_base = base * CANVAS_OVERSAMPLE; ENHCANVAS_show = showflag; ENHCANVAS_widthflag = widthflag; ENHCANVAS_overprint = overprint; } } /* * Since we only have the one font, and the character widths are known, * we can go to the trouble of actually counting character widths. * As it happens, the averages width of ascii characters is 20. */ static int canvas_strwidth(char *s) { int width = 0; char *end = s + strlen(s); while (*s) { if ((*s & 0x80) == 0) { if (strchr("iIl|", *s)) width += 8; else if (strchr("j`',;:!.", *s)) width += 10; else if (strchr("ftr", *s)) width += 12; else if (strchr("()[]{}\\", *s)) width += 14; else if (strchr(" JTv^_\"*ykLsxz", *s)) width += 16; else if (strchr("AceFV?abdEghnopqu", *s)) width += 18; else if (strchr("M~<>%W=&@", *s)) width += 24; else if (strchr("m", *s)) width += 30; else width += 20; s++; continue; } else if (encoding != S_ENC_UTF8) s++; else if ((*s & 0xE0) == 0xC0) s += 2; else if ((*s & 0xF0) == 0xE0) s += 3; else s += 4; width += 18; /* Assumed average width for UTF8 characters */ if (s > end) break; } return (width); } TERM_PUBLIC void ENHCANVAS_FLUSH() { double save_fontsize; int x,y; double w; if (ENHCANVAS_opened_string) { ENHCANVAS_opened_string = FALSE; *enhanced_cur_text = '\0'; save_fontsize = canvas_font_size; x = canvas_x; y = canvas_y; w = canvas_strwidth(enhanced_text) * CANVAS_OVERSAMPLE * ENHCANVAS_fontsize/25.; canvas_font_size = ENHCANVAS_fontsize; x += sin((double)canvas_text_angle * M_PI_2/90.) * ENHCANVAS_base; y += cos((double)canvas_text_angle * M_PI_2/90.) * ENHCANVAS_base; if (ENHCANVAS_show && !ENHCANVAS_sizeonly) CANVAS_put_text(x, y, enhanced_text); if (ENHCANVAS_overprint == 1) { canvas_x += w * cos((double)canvas_text_angle * M_PI_2/90.)/2; canvas_y -= w * sin((double)canvas_text_angle * M_PI_2/90.)/2; } else if (ENHCANVAS_widthflag) { canvas_x += w * cos((double)canvas_text_angle * M_PI_2/90.); canvas_y -= w * sin((double)canvas_text_angle * M_PI_2/90.); } canvas_font_size = save_fontsize; } } TERM_PUBLIC void ENHCANVAS_put_text(unsigned int x, unsigned int y, const char *str) { char *original_string = (char *)str; /* Save starting font properties */ double fontsize = canvas_font_size; char *fontname = ""; if (!strlen(str)) return; if (ignore_enhanced_text || !strpbrk(str, "{}^_@&~")) { CANVAS_put_text(x, y, str); return; } /* ctx.fillText uses fillStyle rather than strokeStyle */ if (strcmp(canvas_state.previous_fill, canvas_state.color)) { fprintf(gpoutfile, "ctx.fillStyle = \"%s\";\n", canvas_state.color); strcpy(canvas_state.previous_fill, canvas_state.color); } CANVAS_move(x,y); /* Set up global variables needed by enhanced_recursion() */ enhanced_fontscale = 1.0; strncpy(enhanced_escape_format,"%c",sizeof(enhanced_escape_format)); ENHCANVAS_opened_string = FALSE; ENHCANVAS_fontsize = canvas_font_size; if (!strcmp(canvas_justify,"Right") || !strcmp(canvas_justify,"Center")) ENHCANVAS_sizeonly = TRUE; while (*(str = enhanced_recursion((char *)str, TRUE, fontname, fontsize, 0.0, TRUE, TRUE, 0))) { (term->enhanced_flush)(); enh_err_check(str); if (!*++str) break; /* end of string */ } /* 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 (!strcmp(canvas_justify,"Right") || !strcmp(canvas_justify,"Center")) { char *justification = canvas_justify; int x_offset = canvas_x - x; int y_offset = (canvas_text_angle == 0) ? 0 : canvas_y - y; canvas_justify = ""; ENHCANVAS_sizeonly = FALSE; if (!strcmp(justification,"Right")) { ENHCANVAS_put_text(x - x_offset, y - y_offset, original_string); } else if (!strcmp(justification,"Center")) { ENHCANVAS_put_text(x - x_offset/2, y - y_offset/2, original_string); } canvas_justify = justification; } /* Make sure we leave with the same font properties as on entry */ canvas_font_size = fontsize; ENHCANVAS_base = 0; return; } #ifdef WRITE_PNG_IMAGE TERM_PUBLIC void CANVAS_image (unsigned m, unsigned n, coordval *image, gpiPoint *corner, t_imagecolor color_mode) { char *image_file; char *base_name = CANVAS_name ? CANVAS_name : "gp"; canvas_imagefile *thisimage = NULL; /* Write the image to a png file */ image_file = gp_alloc(strlen(base_name)+16, "CANVAS_image"); sprintf(image_file, "%s_image_%02d.png", base_name, ++CANVAS_imageno); write_png_image (m, n, image, color_mode, image_file); /* Map it onto the terminals coordinate system. */ fprintf(gpoutfile, "gnuplot.ZI(%s_image_%02d, %d, %d, %d, %d, %d, %d);\n", base_name, CANVAS_imageno, m, n, corner[0].x, canvas_ymax - corner[0].y, corner[1].x, canvas_ymax - corner[1].y); /* Maintain a linked list of imageno|filename pairs */ /* so that we can load them at the end of the plot. */ thisimage = gp_alloc(sizeof(canvas_imagefile), "canvas imagefile"); thisimage->imageno = CANVAS_imageno; thisimage->filename = image_file; thisimage->next = imagelist; imagelist = thisimage; } #endif #endif /* TERM_BODY */ #ifdef TERM_TABLE TERM_TABLE_START(canvas_driver) "canvas", "HTML Canvas object", CANVAS_XMAX, CANVAS_YMAX, CANVASVCHAR, CANVASHCHAR, CANVASVTIC, CANVASHTIC, CANVAS_options, CANVAS_init, CANVAS_reset, CANVAS_text, null_scale, CANVAS_graphics, CANVAS_move, CANVAS_vector, CANVAS_linetype, CANVAS_put_text, CANVAS_text_angle, CANVAS_justify_text, CANVAS_point, do_arrow, CANVAS_set_font, CANVAS_pointsize, TERM_CAN_MULTIPLOT|TERM_ALPHA_CHANNEL|TERM_LINEWIDTH|TERM_CAN_DASH, NULL, NULL, CANVAS_fillbox, CANVAS_linewidth #ifdef USE_MOUSE , NULL, NULL, NULL, NULL, NULL #endif , CANVAS_make_palette, NULL, CANVAS_set_color , CANVAS_filled_polygon #ifdef WRITE_PNG_IMAGE , CANVAS_image #else , NULL #endif , ENHCANVAS_OPEN, ENHCANVAS_FLUSH, do_enh_writec , CANVAS_layer , CANVAS_path /* path */ , 1.0 /* Should this be CANVAS_OVERSAMPLE? */ , CANVAS_hypertext #ifdef EAM_BOXED_TEXT , NULL /* boxed text */ #endif , NULL /* modify_plots */ , CANVAS_dashtype TERM_TABLE_END(canvas_driver) #undef LAST_TERM #define LAST_TERM canvas_driver #endif /* TERM_TABLE */ #ifdef TERM_HELP START_HELP(canvas) "1 canvas", "?commands set terminal canvas", "?set terminal canvas", "?set term canvas", "?terminal canvas", "?term canvas", "", " The `canvas` terminal creates a set of javascript commands that draw onto the", " HTML5 canvas element.", " Syntax:", " set terminal canvas {size , } {background }", " {font {}{,}} | {fsize }", " {{no}enhanced} {linewidth }", " {rounded | butt | square}", " {dashlength
}", " {standalone {mousing} | name ''}", " {jsdir 'URL/for/javascripts'}", " {title ''}", "", " where and set the size of the plot area in pixels.", " The default size in standalone mode is 600 by 400 pixels.", " The default font size is 10.", "", " NB: Only one font is available, the ascii portion of Hershey simplex Roman", " provided in the file canvastext.js. You can replace this with the file", " canvasmath.js, which contains also UTF-8 encoded Hershey simplex Greek and", " math symbols. For consistency with other terminals, it is also possible to", " use `font \"name,size\"`. Currently the font `name` is ignored, but browser", " support for named fonts is likely to arrive eventually.", "", " The default `standalone` mode creates an html page containing javascript", " code that renders the plot using the HTML 5 canvas element. The html page", " links to two required javascript files 'canvastext.js' and 'gnuplot_common.js'.", " An additional file 'gnuplot_dashedlines.js' is needed to support dashed lines.", " By default these point to local files, on unix-like systems usually in", " directory /usr/local/share/gnuplot//js. See installation notes for", " other platforms. You can change this by using the `jsdir` option to specify", " either a different local directory or a general URL. The latter is usually", " appropriate if the plot is exported for viewing on remote client machines.", "", " All plots produced by the canvas terminal are mouseable. The additional", " keyword `mousing` causes the `standalone` mode to add a mouse-tracking box", " underneath the plot. It also adds a link to a javascript file", " 'gnuplot_mouse.js' and to a stylesheet for the mouse box 'gnuplot_mouse.css'", " in the same local or URL directory as 'canvastext.js'.", "", " The `name` option creates a file containing only javascript. Both the", " javascript function it contains and the id of the canvas element that it", " draws onto are taken from the following string parameter. The commands", " set term canvas name 'fishplot'", " set output 'fishplot.js'", " will create a file containing a javascript function fishplot() that will", " draw onto a canvas with id=fishplot. An html page that invokes this", " javascript function must also load the canvastext.js function as described", " above. A minimal html file to wrap the fishplot created above might be:", "", " ", " ", " ", " ", " ", " ", " ", " ", "
No support for HTML 5 canvas element
", "
", " ", " ", "", " The individual plots drawn on this canvas will have names fishplot_plot_1,", " fishplot_plot_2, and so on. These can be referenced by external javascript", " routines, for example gnuplot.toggle_visibility(\"fishplot_plot_2\").", "" END_HELP(canvas) #endif /* TERM_HELP */