/* Hello, Emacs: this is -*-C-*- ! */
/* GNUPLOT - cairo.trm */
/*[
* Copyright 2007 Timothee Lecomte
*
* 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.
]*/
/*
* Modifications:
* Peter Danenberg, Ethan Merritt - handle png output as well as pdf
* Peter Danenberg - crop for PNGs
* Ren Haber - added epscairo terminal
* Bastian Maerkisch - cairolatex terminal
* Henri Menke - add png option for graphics output
*/
#ifdef TERM_REGISTER
register_term (pdfcairo)
#endif
#ifdef TERM_PROTO
TERM_PUBLIC void cairotrm_options(void);
TERM_PUBLIC void cairotrm_init(void);
TERM_PUBLIC void cairotrm_graphics(void);
TERM_PUBLIC void cairotrm_text(void);
TERM_PUBLIC void cairotrm_linetype(int linetype);
TERM_PUBLIC void cairotrm_move(unsigned int x, unsigned int y);
TERM_PUBLIC void cairotrm_vector(unsigned int x, unsigned int y);
TERM_PUBLIC void cairotrm_put_text(unsigned int x, unsigned int y, const char *str);
TERM_PUBLIC void cairotrm_enhanced_flush(void);
TERM_PUBLIC void cairotrm_enhanced_writec(int c);
TERM_PUBLIC void cairotrm_enhanced_open(char* fontname, double fontsize, double base, TBOOLEAN widthflag, TBOOLEAN showflag, int overprint);
TERM_PUBLIC void cairotrm_reset(void);
TERM_PUBLIC int cairotrm_justify_text(enum JUSTIFY mode);
TERM_PUBLIC void cairotrm_point(unsigned int x, unsigned int y, int pointstyle);
TERM_PUBLIC void cairotrm_linewidth(double linewidth);
TERM_PUBLIC int cairotrm_text_angle(float ang);
TERM_PUBLIC void cairotrm_fillbox(int style, unsigned int x1, unsigned int y1, unsigned int width, unsigned int height);
TERM_PUBLIC int cairotrm_set_font(const char *font);
TERM_PUBLIC void cairotrm_pointsize(double ptsize);
TERM_PUBLIC void cairotrm_image(unsigned int M, unsigned int N, coordval * image, gpiPoint * corner, t_imagecolor color_mode);
TERM_PUBLIC int cairotrm_make_palette(t_sm_palette *palette);
TERM_PUBLIC void cairotrm_set_color(t_colorspec *colorspec);
TERM_PUBLIC void cairotrm_filled_polygon(int n, gpiPoint * corners);
TERM_PUBLIC void cairotrm_boxed_text(unsigned int x, unsigned int y, int option);
TERM_PUBLIC void cairotrm_dashtype(int type, t_dashtype *custom_dash_pattern);
#ifdef HAVE_WEBP
TERM_PUBLIC void WEBP_options(int); /* We call it from cairotrm_options */
#endif
#endif /* TERM_PROTO */
#ifndef TERM_PROTO_ONLY
#ifdef TERM_BODY
#include "cairo-pdf.h"
#ifdef HAVE_CAIROEPS
#include "cairo-ps.h"
#endif
#include "wxterminal/gp_cairo.h"
#include "wxterminal/gp_cairo_helpers.h"
#include "glib.h" /* For guint32 */
#define CAIROTRM_DEFAULT_FONTNAME "Sans"
static cairo_status_t cairostream_write(void *closure, unsigned char *data, unsigned int length);
static int cairostream_error[1];
/* Terminal id */
enum CAIROTRM_id {
CAIROTRM_FONT,
CAIROTRM_FONTSCALE,
CAIROTRM_ENHANCED,
CAIROTRM_NOENHANCED,
CAIROTRM_SIZE,
CAIROTRM_ROUNDED,
CAIROTRM_BUTT,
CAIROTRM_SQUARE,
CAIROTRM_LINEWIDTH,
CAIROTRM_DASHED,
CAIROTRM_SOLID,
CAIROTRM_MONO,
CAIROTRM_COLOR,
CAIROTRM_DASHLENGTH,
CAIROTRM_TRANSPARENT,
CAIROTRM_NOTRANSPARENT,
CAIROTRM_CROP,
CAIROTRM_NOCROP,
CAIROTRM_BACKGROUND,
CAIROTRM_POINTSCALE,
CAIROLATEX_STANDALONE,
CAIROLATEX_INPUT,
CAIROLATEX_HEADER,
CAIROLATEX_NOHEADER,
CAIROLATEX_BLACKTEXT,
CAIROLATEX_COLORTEXT,
CAIROLATEX_EPS,
CAIROLATEX_PDF,
CAIROLATEX_PNG,
CAIROLATEX_PNG_RESOLUTION,
CAIROTRM_OTHER
};
/* Terminal type of postscript dialect */
enum CAIRO_TERMINALTYPE {
CAIROTERM_EPS, CAIROTERM_PDF, CAIROTERM_PNG, CAIROTERM_LATEX
};
/* One struct that takes all terminal parameters
* by Harald Harders */
typedef struct cairo_params_t {
enum CAIRO_TERMINALTYPE terminal;
size_units explicit_units;
TBOOLEAN enhanced;
TBOOLEAN dashed;
float dash_length;
rgb_color background; /* struct with r/g/b [0.0:1.0] */
unsigned int rgb_bkgnd; /* same thing packe into 24bits */
TBOOLEAN mono;
t_linecap linecap; /* butt/rounded/square linecaps and linejoins */
TBOOLEAN transparent;
TBOOLEAN crop;
char *fontname; /* name of font */
float fontsize; /* size of font in pts */
float fontscale; /* modified by this */
float width;
float height;
float base_linewidth;
float lw;
float ps;
enum CAIROTRM_id output; /* format of the graphics produced by cairolatex */
int resolution; /* in dpi; CAIROLATEX_PNG only */
TBOOLEAN anchor; /* TRUE = anchor kitty output to top left */
} cairo_params_t;
#define CAIROEPS_PARAMS_DEFAULT { \
CAIROTERM_EPS, INCHES, FALSE, FALSE, 1.0, {1.,1.,1.}, 0xffffff, \
FALSE, BUTT, TRUE, FALSE, NULL, \
12, 0.5, 5*72., 3*72., 0.25, 1.0, 1.0, FALSE \
}
static cairo_params_t cairoeps_params = CAIROEPS_PARAMS_DEFAULT;
static const cairo_params_t cairoeps_params_default = CAIROEPS_PARAMS_DEFAULT;
#ifdef HAVE_CAIROEPS
#define CAIROLATEX_PARAMS_DEFAULT { \
CAIROTERM_LATEX, INCHES, FALSE, FALSE, 1.0, {1.,1.,1.}, 0xffffff, \
FALSE, BUTT, TRUE, FALSE, NULL, \
11, 0.6, 5*72., 3*72., 0.25, 1.0, 1.0, FALSE, 300 \
}
#else
#define CAIROLATEX_PARAMS_DEFAULT { \
CAIROTERM_LATEX, INCHES, FALSE, FALSE, 1.0, {1.,1.,1.}, 0xffffff, \
FALSE, BUTT, TRUE, FALSE, NULL, \
11, 0.6, 5*72., 3*72., 0.25, 1.0, 1.0, TRUE, 300 \
}
#endif
static cairo_params_t cairolatex_params = CAIROLATEX_PARAMS_DEFAULT;
static const cairo_params_t cairolatex_params_default = CAIROLATEX_PARAMS_DEFAULT;
#define CAIROPDF_PARAMS_DEFAULT { \
CAIROTERM_PDF, INCHES, FALSE, FALSE, 1.0, {1.,1.,1.}, 0xffffff, \
FALSE, BUTT, TRUE, FALSE, NULL, \
12, 0.5, 5*72., 3*72., 0.25, 1.0, 1.0, FALSE \
}
static cairo_params_t cairopdf_params = CAIROPDF_PARAMS_DEFAULT;
static const cairo_params_t cairopdf_params_default = CAIROPDF_PARAMS_DEFAULT;
#define CAIROPNG_PARAMS_DEFAULT { \
CAIROTERM_PNG, PIXELS, FALSE, FALSE, 1.0, {1.,1.,1.}, 0xffffff, \
FALSE, BUTT, FALSE, FALSE, NULL, \
12, 1.0, 640., 480., 1.0, 1.0, 1.0, FALSE, 0, TRUE\
}
static cairo_params_t cairopng_params = CAIROPNG_PARAMS_DEFAULT;
static const cairo_params_t cairopng_params_default = CAIROPNG_PARAMS_DEFAULT;
static cairo_params_t *cairo_params = &cairopdf_params;
static const cairo_params_t *cairo_params_default = &cairopdf_params;
#ifdef PSLATEX_DRIVER
#define ISCAIROLATEX (cairo_params->terminal == CAIROTERM_LATEX)
static ps_params_t cairo_epslatex_params = EPSLATEX_PARAMS_DEFAULT;
#else
#define ISCAIROLATEX (FALSE)
#endif
char *cairo_enhanced_fontname = NULL;
plot_struct plot;
static struct gen_table cairotrm_opts[] = {
{"fontscale", CAIROTRM_FONTSCALE},
{"f$ont", CAIROTRM_FONT},
{"enh$anced", CAIROTRM_ENHANCED},
{"noenh$anced", CAIROTRM_NOENHANCED},
{"si$ze", CAIROTRM_SIZE},
{"round$ed", CAIROTRM_ROUNDED},
{"butt", CAIROTRM_BUTT},
{"square", CAIROTRM_SQUARE},
{"lw", CAIROTRM_LINEWIDTH},
{"linew$idth", CAIROTRM_LINEWIDTH},
{"pointscale", CAIROTRM_POINTSCALE},
{"ps", CAIROTRM_POINTSCALE},
{"dash$ed", CAIROTRM_DASHED},
{"solid", CAIROTRM_SOLID},
{"mono$chrome", CAIROTRM_MONO},
{"color", CAIROTRM_COLOR},
{"col$our", CAIROTRM_COLOR},
{"dl", CAIROTRM_DASHLENGTH},
{"dashl$ength", CAIROTRM_DASHLENGTH},
{"transp$arent", CAIROTRM_TRANSPARENT},
{"notransp$arent", CAIROTRM_NOTRANSPARENT},
{"crop", CAIROTRM_CROP},
{"nocrop", CAIROTRM_NOCROP},
{"backg$round", CAIROTRM_BACKGROUND},
{"nobackg$round", CAIROTRM_TRANSPARENT},
{"stand$alone", CAIROLATEX_STANDALONE},
{"inp$ut", CAIROLATEX_INPUT},
{"header", CAIROLATEX_HEADER},
{"noheader", CAIROLATEX_NOHEADER},
{"b$lacktext", CAIROLATEX_BLACKTEXT},
{"colort$ext", CAIROLATEX_COLORTEXT},
{"colourt$ext", CAIROLATEX_COLORTEXT},
{"eps", CAIROLATEX_EPS},
{"pdf", CAIROLATEX_PDF},
{"png", CAIROLATEX_PNG},
{"resolution", CAIROLATEX_PNG_RESOLUTION},
{NULL, CAIROTRM_OTHER}
};
/* "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 cairotrm_options()
{
char *s = NULL;
char *font_setting = NULL;
TBOOLEAN duplication = FALSE;
TBOOLEAN set_font = FALSE, set_size = FALSE;
TBOOLEAN set_capjoin = FALSE;
TBOOLEAN set_standalone = FALSE, set_header = FALSE;
char tmp_term_options[MAX_LINE_LEN+1] = "";
/* Initialize terminal-dependent values */
if ((strcmp(term->name, "pngcairo") == 0) || (strcmp(term->name, "kittycairo") == 0)) {
cairo_params = &cairopng_params;
cairo_params_default = &cairopng_params_default;
} else if (strcmp(term->name, "epscairo") == 0) {
cairo_params = &cairoeps_params;
cairo_params_default = &cairoeps_params_default;
}
#ifdef HAVE_WEBP
else if (strcmp(term->name, "webp") == 0) {
cairo_params = &cairopng_params;
cairo_params_default = &cairopng_params_default;
}
#endif
#ifdef PSLATEX_DRIVER
else if (strcmp(term->name, "cairolatex") == 0) {
cairo_params = &cairolatex_params;
cairo_params_default = &cairolatex_params_default;
ps_params = &cairo_epslatex_params;
}
#endif
else {
cairo_params = &cairopdf_params;
cairo_params_default = &cairopdf_params_default;
}
/* Default to enhanced text mode */
if (!almost_equals(c_token-1, "termopt$ion") && !ISCAIROLATEX) {
cairo_params->enhanced = TRUE;
term->flags |= TERM_ENHANCED_TEXT;
}
while (!END_OF_COMMAND) {
FPRINTF((stderr, "processing token\n"));
switch (lookup_table(&cairotrm_opts[0], c_token)) {
case CAIROTRM_FONT:
c_token++;
if (!(s = try_to_get_string()))
int_error(c_token,"font: expecting string");
font_setting = gp_strdup(s);
if (*s) {
char *sep = strchr(s,',');
if (sep) {
sscanf(sep+1, "%f", &cairo_params->fontsize);
*sep = '\0';
}
free(cairo_params->fontname);
cairo_params->fontname = gp_strdup(s);
}
free(s);
if (set_font) duplication=TRUE;
set_font = TRUE;
break;
case CAIROTRM_ENHANCED:
if (ISCAIROLATEX)
int_error(c_token,
"extraneous argument in set terminal %s", term->name);
c_token++;
cairo_params->enhanced = TRUE;
term->flags |= TERM_ENHANCED_TEXT;
break;
case CAIROTRM_NOENHANCED:
if (ISCAIROLATEX)
int_error(c_token,
"extraneous argument in set terminal %s", term->name);
c_token++;
cairo_params->enhanced = FALSE;
term->flags &= ~TERM_ENHANCED_TEXT;
break;
case CAIROTRM_SIZE:
c_token++;
if (!strcmp(term->name,"pngcairo") || !strcmp(term->name, "kittycairo"))
cairo_params->explicit_units = parse_term_size(&cairo_params->width, &cairo_params->height, PIXELS);
#ifdef HAVE_WEBP
else if (!strcmp(term->name,"webp"))
cairo_params->explicit_units = parse_term_size(&cairo_params->width, &cairo_params->height, PIXELS);
#endif
else
cairo_params->explicit_units = parse_term_size(&cairo_params->width, &cairo_params->height, INCHES);
if (set_size) duplication=TRUE;
set_size = TRUE;
break;
case CAIROTRM_ROUNDED:
c_token++;
if (set_capjoin) duplication=TRUE;
cairo_params->linecap = ROUNDED;
set_capjoin = TRUE;
break;
case CAIROTRM_BUTT:
c_token++;
if (set_capjoin) duplication=TRUE;
cairo_params->linecap = BUTT;
set_capjoin = TRUE;
break;
case CAIROTRM_SQUARE:
c_token++;
if (set_capjoin) duplication=TRUE;
cairo_params->linecap = SQUARE;
set_capjoin = TRUE;
break;
case CAIROTRM_LINEWIDTH:
c_token++;
cairo_params->lw = real_expression();
if (cairo_params->lw < 0.0)
cairo_params->lw = cairo_params_default->lw;
break;
case CAIROTRM_POINTSCALE:
c_token++;
cairo_params->ps = real_expression();
if (cairo_params->ps < 0.0)
cairo_params->ps = 1.0;
break;
case CAIROTRM_DASHED:
case CAIROTRM_SOLID:
/* dashes always enabled in version 5 */
c_token++;
cairo_params->dashed = TRUE;
break;
case CAIROTRM_MONO:
c_token++;
cairo_params->mono = TRUE;
term->flags |= TERM_MONOCHROME;
break;
case CAIROTRM_COLOR:
c_token++;
cairo_params->mono = FALSE;
term->flags &= ~TERM_MONOCHROME;
break;
case CAIROTRM_DASHLENGTH:
c_token++;
cairo_params->dash_length = real_expression();
if (cairo_params->dash_length < 0.0)
cairo_params->dash_length = cairo_params_default->dash_length;
break;
case CAIROTRM_FONTSCALE:
c_token++;
cairo_params->fontscale = END_OF_COMMAND ? -1 : real_expression();
if (cairo_params->fontscale fontscale = cairo_params_default->fontscale;
/* set_fontscale = TRUE; */
break;
case CAIROTRM_TRANSPARENT:
c_token++;
cairo_params->transparent = TRUE;
break;
case CAIROTRM_NOTRANSPARENT:
c_token++;
cairo_params->transparent = FALSE;
break;
case CAIROTRM_CROP:
c_token++;
if ((cairo_params->terminal == CAIROTERM_PNG) ||
(cairo_params->terminal == CAIROTERM_EPS))
cairo_params->crop = TRUE;
break;
case CAIROTRM_NOCROP:
c_token++;
if ((cairo_params->terminal == CAIROTERM_PNG) ||
(cairo_params->terminal == CAIROTERM_EPS))
cairo_params->crop = FALSE;
break;
case CAIROTRM_BACKGROUND:
{
int wxt_background;
c_token++;
wxt_background = parse_color_name();
cairo_params->background.r = (double)((wxt_background >> 16) & 0xff) / 255.;
cairo_params->background.g = (double)((wxt_background >> 8) & 0xff) / 255.;
cairo_params->background.b = (double)( wxt_background & 0xff) / 255.;
cairo_params->transparent = FALSE;
cairo_params->rgb_bkgnd = wxt_background & 0xffffff;
break;
}
#ifdef PSLATEX_DRIVER
case CAIROLATEX_STANDALONE:
if (!ISCAIROLATEX)
int_error(c_token,
"extraneous argument in set terminal %s", term->name);
ps_params->epslatex_standalone = TRUE;
++c_token;
if (set_standalone) duplication = TRUE;
set_standalone = TRUE;
break;
case CAIROLATEX_INPUT:
if (!ISCAIROLATEX)
int_error(c_token,
"extraneous argument in set terminal %s", term->name);
ps_params->epslatex_standalone = FALSE;
++c_token;
if (set_standalone) duplication = TRUE;
set_standalone = TRUE;
break;
case CAIROLATEX_HEADER:
if (!ISCAIROLATEX)
int_error(c_token,
"extraneous argument in set terminal %s", term->name);
++c_token;
free(epslatex_header);
/* Protect against int_error() bail from try_to_get_string() */
epslatex_header = NULL;
epslatex_header = try_to_get_string();
if (!epslatex_header)
int_error(c_token,"String containing header information expected");
if (set_header) duplication = TRUE;
set_header = TRUE;
break;
case CAIROLATEX_NOHEADER:
if (!ISCAIROLATEX)
int_error(c_token,
"extraneous argument in set terminal %s", term->name);
free(epslatex_header);
epslatex_header = NULL;
if (set_header) duplication = TRUE;
set_header = TRUE;
++c_token;
break;
case CAIROLATEX_BLACKTEXT:
if (!ISCAIROLATEX)
int_error(c_token,
"extraneous argument in set terminal %s",term->name);
ps_params->blacktext = TRUE;
c_token++;
break;
case CAIROLATEX_COLORTEXT:
if (!ISCAIROLATEX)
int_error(c_token,
"extraneous argument in set terminal %s",term->name);
ps_params->blacktext = FALSE;
c_token++;
break;
case CAIROLATEX_EPS:
if (!ISCAIROLATEX)
int_error(c_token,
"extraneous argument in set terminal %s",term->name);
#ifdef HAVE_CAIROEPS
cairo_params->output = CAIROLATEX_EPS;
#else
int_error(c_token, "eps output not supported");
#endif
c_token++;
break;
case CAIROLATEX_PDF:
if (!ISCAIROLATEX)
int_error(c_token,
"extraneous argument in set terminal %s",term->name);
cairo_params->output = CAIROLATEX_PDF;
c_token++;
break;
case CAIROLATEX_PNG:
if (!ISCAIROLATEX)
int_error(c_token,
"extraneous argument in set terminal %s",term->name);
cairo_params->output = CAIROLATEX_PNG;
++c_token;
break;
case CAIROLATEX_PNG_RESOLUTION:
if (cairo_params->output != CAIROLATEX_PNG)
int_error(c_token,
"extraneous argument in set terminal %s",term->name);
++c_token;
cairo_params->resolution = int_expression();
if (cairo_params->resolution resolution = 300;
break;
#endif /* PSLATEX_DRIVER */
case CAIROTRM_OTHER:
/* Ignore irrelevant keywords used by other png/pdf drivers */
if (almost_equals(c_token,"true$color")
|| almost_equals(c_token,"inter$lace")
|| almost_equals(c_token,"nointer$lace")) {
c_token++;
break;
}
#ifdef HAVE_WEBP
if (strcmp(term->name, "webp") == 0) {
int save_token = c_token;
WEBP_options(0);
if (save_token != c_token)
break;
}
#endif
#ifdef HAVE_KITTY_TERM
if (strncmp(term->name, "kitty", 5) == 0) {
if (equals(c_token, "animate") || equals(c_token, "anchor")) {
c_token++;
cairo_params->anchor = TRUE;
break;
}
if (equals(c_token, "scroll")) {
c_token++;
cairo_params->anchor = FALSE;
break;
}
}
#endif
default:
int_error(c_token, "unrecognized terminal option");
break;
}
if (duplication)
int_error(c_token-1, "Duplicated or contradicting arguments in cairo terminal options.");
}
#if 0
/* Copy terminal-dependent values to the generic equivalent */
cairo_params->width = cairo_params_default->width;
cairo_params->height = cairo_params_default->height;
cairo_params->base_linewidth = cairo_params_default->base_linewidth;
#endif
#ifdef PSLATEX_DRIVER
if (ISCAIROLATEX) {
const char * output = "";
switch (cairo_params->output) {
default:
case CAIROLATEX_PDF:
output = "pdf";
break;
case CAIROLATEX_EPS:
output = "eps";
break;
case CAIROLATEX_PNG:
output = "png";
break;
}
sprintf(tmp_term_options, " %s %s", output,
ps_params->epslatex_standalone ? "standalone" : "input");
strcat(term_options, tmp_term_options);
if (epslatex_header)
sprintf(tmp_term_options, " header \"%s\"", epslatex_header);
else
sprintf(tmp_term_options, " noheader");
strcat(term_options, tmp_term_options);
sprintf(tmp_term_options, " %s", ps_params->blacktext ? "blacktext" : "colortext");
strcat(term_options, tmp_term_options);
}
#endif
/* Save options back into options string in normalized format */
if (cairo_params->transparent)
strncat(term_options, ISCAIROLATEX ? " nobackground" : " transparent",
sizeof(term_options) - strlen(term_options)-1);
else {
sprintf(tmp_term_options, " background \"#%02x%02x%02x\"",
(int)(255 * cairo_params->background.r),
(int)(255 * cairo_params->background.g),
(int)(255 * cairo_params->background.b));
strcat(term_options, tmp_term_options);
}
if (cairo_params->crop)
strncat(term_options, " crop", sizeof(term_options)-strlen(term_options)-1);
if (strncmp(term->name,"kitty",5) == 0)
strncat(term_options, cairo_params->anchor ? " anchor" : " scroll",
sizeof(term_options)-strlen(term_options)-1);
strncat(term_options, cairo_params->enhanced ? " enhanced" : " noenhanced",
sizeof(term_options)-strlen(term_options)-1);
if (set_font) {
snprintf(tmp_term_options, sizeof(tmp_term_options), " font \"%s\"", font_setting);
free(font_setting);
strncat(term_options, tmp_term_options, sizeof(term_options)-strlen(term_options)-1);
}
/* if (set_fontscale) */
{
snprintf(tmp_term_options, sizeof(tmp_term_options), " fontscale %.1f", cairo_params->fontscale);
strncat(term_options, tmp_term_options, sizeof(term_options)-strlen(term_options)-1);
}
if (cairo_params->mono)
strncat(term_options, " monochrome", sizeof(term_options)-strlen(term_options)-1);
if (1 || set_size) {
if (cairo_params->explicit_units == CM)
snprintf(tmp_term_options,
sizeof(tmp_term_options),
" size %.2fcm, %.2fcm ",
2.54*cairo_params->width/72., 2.54*cairo_params->height/72.);
else if (cairo_params->explicit_units == PIXELS)
snprintf(tmp_term_options,
sizeof(tmp_term_options),
" size %d, %d ",
(int)cairo_params->width, (int)cairo_params->height);
else
snprintf(tmp_term_options,
sizeof(tmp_term_options),
" size %.2fin, %.2fin ",
cairo_params->width/72., cairo_params->height/72.);
strncat(term_options, tmp_term_options, sizeof(term_options)-strlen(term_options)-1);
}
if (set_capjoin) {
strncat(term_options,
cairo_params->linecap == ROUNDED ? " rounded"
: cairo_params->linecap == BUTT ? " butt"
: " square",
sizeof(term_options)-strlen(term_options)-1);
}
if (cairo_params->lw != cairo_params_default->lw) {
snprintf(tmp_term_options,sizeof(tmp_term_options), " linewidth %g", cairo_params->lw);
strncat(term_options, tmp_term_options, sizeof(term_options)-strlen(term_options)-1);
}
if (cairo_params->ps != 1.0) {
snprintf(tmp_term_options,sizeof(tmp_term_options), " pointscale %g", cairo_params->ps);
strncat(term_options, tmp_term_options, sizeof(term_options)-strlen(term_options)-1);
}
if (cairo_params->dash_length != cairo_params_default->dash_length) {
snprintf(tmp_term_options,sizeof(tmp_term_options), " dashlength %g", cairo_params->dash_length);
strncat(term_options, tmp_term_options, sizeof(term_options)-strlen(term_options)-1);
}
/* sync settings with ps_params for latex terminal */
#ifdef PSLATEX_DRIVER
if (ISCAIROLATEX) {
ps_params->color = !cairo_params->mono;
ps_params->font[sizeof(ps_params->font) - 1] = '\0';
if (!cairo_params->fontname)
ps_params->font[0] = '\0';
else
strncpy(ps_params->font, cairo_params->fontname, sizeof(ps_params->font) - 1);
ps_params->fontsize = cairo_params->fontsize;
ps_params->fontscale = cairo_params->fontscale;
if (cairo_params->transparent) {
ps_params->background.r = -1;
ps_params->background.g = -1;
ps_params->background.b = -1;
} else {
ps_params->background.r = cairo_params->background.r;
ps_params->background.g = cairo_params->background.g;
ps_params->background.b = cairo_params->background.b;
}
}
#endif
#ifdef HAVE_WEBP
/* Add webp-specific options to the terminal option string */
if (strcmp(term->name, "webp") == 0)
WEBP_options(1);
#endif
}
#ifdef HAVE_CAIROEPS
/*
* Helper routine for eps output
*/
static void add_BoundingBox(cairo_surface_t *surface)
{
/* Early cairo versions did not allow an explicit bounding box */
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,11,1)
char bb[100];
sprintf(bb, "%%%%BoundingBox: 0 0 %i %i", plot.device_xmax, plot.device_ymax);
cairo_ps_surface_dsc_comment(surface, bb);
cairo_ps_surface_dsc_begin_page_setup(surface);
sprintf(bb, "%%%%PageBoundingBox: 0 0 %i %i", plot.device_xmax, plot.device_ymax);
cairo_ps_surface_dsc_comment(surface, bb);
#endif /* CAIRO_VERSION >= 1.11.1 */
}
#endif /* HAVE_CAIROEPS */
/* "Called once, when the device is first selected."
* Is the 'main' function of the terminal. */
void cairotrm_init()
{
cairo_surface_t *surface = NULL;
FPRINTF((stderr,"Init\n"));
/* do a sanity check for once */
if (strcmp(term->name, "epscairo") && strcmp(term->name, "cairolatex") &&
strcmp(term->name, "pdfcairo") && strcmp(term->name, "pngcairo") &&
strcmp(term->name, "kittycairo"))
int_error(c_token,"Unrecognized cairo terminal");
/* cairolatex requires a file */
if (ISCAIROLATEX && !outstr)
os_error(c_token, "cairolatex terminal cannot write to standard output");
/* We no longer rely on this having been done in cairotrm_reset() */
if (plot.cr)
cairo_destroy(plot.cr);
/* initialisations */
gp_cairo_initialize_plot(&plot);
plot.device_xmax = (double) cairo_params->width;
plot.device_ymax = (double) cairo_params->height;
plot.dashlength = cairo_params->dash_length;
if (!strcmp(term->name,"pdfcairo")) {
#ifdef _WIN32
/* Redirection to a printer on Windows requires the use of gpoutfile
* via the cairostream_write callback.
*/
surface = cairo_pdf_surface_create_for_stream(
(cairo_write_func_t)cairostream_write, cairostream_error,
plot.device_xmax /*double width_in_points*/,
plot.device_ymax /*double height_in_points*/);
#else
/* Output can either be a file or stdout */
if (outstr) {
surface = cairo_pdf_surface_create(outstr,
plot.device_xmax, plot.device_ymax);
} else {
surface = cairo_pdf_surface_create_for_stream(
(cairo_write_func_t)cairostream_write, cairostream_error,
plot.device_xmax, plot.device_ymax);
}
#endif /* _WIN32 */
/* it is up to the pdf viewer to do the hinting */
plot.hinting = 0;
/* Cairo at least through version 16 fails to handle OPERATOR_SATURATE
* when applied to pdf surfaces; it degrades the rendering of all text.
* Unfortunately, without it there may be visible artifacts at the
* boundary between filled polygons (viewer-dependent).
*/
plot.polygons_saturate = FALSE;
/* Empirical correction to make pdf output look more like wxt and png */
plot.dashlength /= 2;
} else if (!strcmp(term->name,"pngcairo") || !strcmp(term->name, "kittycairo")) {
surface = cairo_image_surface_create( CAIRO_FORMAT_ARGB32,
plot.device_xmax /*double width_in_points*/,
plot.device_ymax /*double height_in_points*/);
/* png is bitmapped, let's do the full hinting */
plot.hinting = 100;
plot.polygons_saturate = TRUE;
}
#ifdef HAVE_CAIROEPS
else if (!strcmp(term->name,"epscairo")) {
/* Output can either be a file or stdout */
if (!outstr) {
surface = cairo_ps_surface_create_for_stream(
(cairo_write_func_t)cairostream_write, cairostream_error,
plot.device_xmax /*double width_in_points*/,
plot.device_ymax /*double height_in_points*/);
} else {
surface = cairo_ps_surface_create(outstr,
plot.device_xmax /*double width_in_points*/,
plot.device_ymax /*double height_in_points*/);
}
cairo_ps_surface_set_eps(surface, TRUE);
if (!cairo_params->crop)
add_BoundingBox(surface);
/* OPERATOR_SATURATE is OK for epscairo because
* text is handled by PostScript rather than cairo/pango.
*/
plot.polygons_saturate = TRUE;
plot.hinting = 0;
/* Empirical correction to make eps output look more like wxt and png */
plot.dashlength /= 2;
}
#endif /* HAVE_CAIROEPS */
#ifdef PSLATEX_DRIVER
else if (ISCAIROLATEX) {
/* Output can only go to a stream */
switch (cairo_params->output) {
default:
case CAIROLATEX_PDF:
EPSLATEX_reopen_output("pdf");
surface = cairo_pdf_surface_create_for_stream(
(cairo_write_func_t)cairostream_write, cairostream_error,
plot.device_xmax /*double width_in_points*/,
plot.device_ymax /*double height_in_points*/);
break;
case CAIROLATEX_PNG:
EPSLATEX_reopen_output("png");
/* We manually scale up to 300 dpi instead of the default 72 dpi */
plot.upsampling_rate = cairo_params->resolution / 72.;
surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
(int)floor(plot.device_xmax * plot.upsampling_rate),
(int)floor(plot.device_ymax * plot.upsampling_rate));
break;
#ifdef HAVE_CAIROEPS
case CAIROLATEX_EPS:
EPSLATEX_reopen_output("eps");
surface = cairo_ps_surface_create_for_stream(
(cairo_write_func_t)cairostream_write, cairostream_error,
plot.device_xmax /*double width_in_points*/,
plot.device_ymax /*double height_in_points*/);
cairo_ps_surface_set_eps(surface, TRUE);
add_BoundingBox(surface);
break;
#endif /* HAVE_CAIROEPS */
}
switch (cairo_params->output) {
case CAIROLATEX_PNG:
/* png is bitmapped, let's do the full hinting */
plot.hinting = 100;
plot.polygons_saturate = TRUE;
break;
default:
/* it is up to the pdf viewer to do the hinting */
plot.hinting = 0;
/* OPERATOR_SATURATE is OK for cairolatex because
* text is generated by LaTeX rather than cairo/pango.
*/
plot.polygons_saturate = TRUE;
/* Empirical correction to make pdf output look more like wxt and png */
plot.dashlength /= 2;
break;
}
}
#endif /* PSLATEX_DRIVER */
plot.cr = cairo_create(surface);
cairo_surface_destroy( surface );
FPRINTF((stderr,"status = %s\n",cairo_status_to_string(cairo_status(plot.cr))));
FPRINTF((stderr,"Init finished \n"));
}
/* "Called just before a plot is going to be displayed."
* Should clear the terminal. */
void cairotrm_graphics()
{
/* Initialize background */
plot.background.r = cairo_params->background.r;
plot.background.g = cairo_params->background.g;
plot.background.b = cairo_params->background.b;
gp_cairo_set_background(cairo_params->background);
if (ISCAIROLATEX || cairo_params->transparent)
gp_cairo_clear_background(&plot);
else
gp_cairo_solid_background(&plot);
/* update the window scale factor first, cairo needs it */
plot.xscale = 1.0;
plot.yscale = 1.0;
/* We manually scale up the PNG for higher pixel density */
if (ISCAIROLATEX && cairo_params->output == CAIROLATEX_PNG) {
plot.xscale = plot.upsampling_rate;
plot.yscale = plot.upsampling_rate;
}
/* update graphics state properties */
plot.linecap = cairo_params->linecap;
FPRINTF((stderr,"Graphics1\n"));
/* set the transformation matrix of the context, and other details */
/* depends on plot.xscale and plot.yscale */
gp_cairo_initialize_context(&plot);
/* set or refresh terminal size according to the window size */
/* oversampling_scale is updated in gp_cairo_initialize_context */
plot.xmax = (unsigned int) plot.device_xmax*plot.oversampling_scale;
plot.ymax = (unsigned int) plot.device_ymax*plot.oversampling_scale;
/* initialize encoding */
plot.encoding = encoding;
/* Adjust for the mismatch of floating point coordinate range 0->max */
/* and integer terminal pixel coordinates [0:max-1]. */
term->xmax = (plot.device_xmax - 1) * plot.oversampling_scale;
term->ymax = (plot.device_ymax - 1) * plot.oversampling_scale;
term->tscale = plot.oversampling_scale;
#ifdef _WIN32
/* On Windows, always explicitly set the resolution to 96dpi to avoid
applying the "text scaling" factor. */
gp_cairo_set_resolution(96);
#endif
/* set font details (h_char, v_char) according to settings */
cairotrm_set_font("");
term->v_tic = (unsigned int) (term->v_char/2.5);
term->h_tic = (unsigned int) (term->v_char/2.5);
#ifdef PSLATEX_DRIVER
/* Init latex output, requires terminal size */
if (ISCAIROLATEX)
EPSLATEX_common_init();
#endif
#if CAIRO_VERSION < CAIRO_VERSION_ENCODE(1,11,1)
/* Put "invisible" points in two corners to enforce bounding box. */
if ((ISCAIROLATEX) || ((cairo_params->terminal == CAIROTERM_EPS) && !cairo_params->crop)) {
cairo_set_line_width(plot.cr, 1);
cairo_set_source_rgb(plot.cr, cairo_params->background.r,
cairo_params->background.g, cairo_params->background.b);
cairo_move_to(plot.cr, 0, 0);
cairo_line_to(plot.cr, 1, 0);
cairo_move_to(plot.cr, plot.xmax - 1, plot.ymax);
cairo_line_to(plot.cr, plot.xmax, plot.ymax);
cairo_stroke(plot.cr);
}
#endif
FPRINTF((stderr,"Graphics xmax %d ymax %d v_char %d h_char %d\n",
term->xmax, term->ymax, term->v_char, term->h_char));
}
/* cairo mechanism to allow writing to an output stream */
cairo_status_t
cairostream_write(void *closure, unsigned char *data, unsigned int length)
{
/* in case of a cairolatex terminal, output is redirected to a secondary file */
if (length != fwrite(data, 1, length, (ISCAIROLATEX) ? gppsfile : gpoutfile))
return CAIRO_STATUS_WRITE_ERROR;
return CAIRO_STATUS_SUCCESS;
}
void
cairopng_write_cropped_image(cairo_surface_t *surface)
{
unsigned char *data = cairo_image_surface_get_data(surface);
int width = cairo_image_surface_get_width(surface);
int height = cairo_image_surface_get_height(surface);
int stride = cairo_image_surface_get_stride(surface);
int i, j, x1 = 0, y1 = 0, x2 = width, y2 = height;
guint32 *row;
guint32 BG = (cairo_params->transparent)
? 0x0
: cairo_params->rgb_bkgnd | 0xff000000;
/* Row-wise, top-down iteration */
for (i = 0; i < height; i++) {
row = (guint32 *) (data + i * stride);
for (j = 0; j < width; j++) {
if (row[j] != BG) {
y1 = i;
goto found_y1;
}
}
}
found_y1:
/* Row-wise, bottom-up iteration */
for (i = height - 1; i >= y1; i--) {
row = (guint32 *)(data + i * stride);
for (j = 0; j < width; j++) {
if (row[j] != BG) {
y2 = i;
goto found_y2;
}
}
}
found_y2:
/* Column-wise, left-to-right iteration */
for (j = 0; j < width; j++) {
for (i = y1; i = x1; j--) {
for (i = y1; i name, "pngcairo") == 0 ||
(strcmp(term->name, "cairolatex") == 0 &&
cairo_params->output == CAIROLATEX_PNG)) {
cairo_surface_t *surface = cairo_get_target(plot.cr);
if (cairo_params->crop) {
cairopng_write_cropped_image(surface);
} else {
cairo_surface_write_to_png_stream(surface,
(cairo_write_func_t)cairostream_write, cairostream_error);
}
#ifdef HAVE_KITTY_TERM
} else if (strcmp(term->name, "kittycairo") == 0) {
base64s *b64;
cairo_surface_t *surface = cairo_get_target(plot.cr);
/* TODO: crop */
/* Draw starting at top left of screen */
if (cairo_params->anchor)
fprintf(gpoutfile, "\033[H");
kitty_init(KITTY_FORMAT_PNG);
b64 = gp_alloc(sizeof(base64s), "base64s");
init_base64_state_data(b64, kitty_writechar, NULL);
cairo_surface_write_to_png_stream(surface, cairo_write_base64_callback, b64);
piecemeal_write_base64_data_finish(b64);
free(b64);
kitty_flush(FALSE);
fputc('\n', gpoutfile);
#endif
}
FPRINTF((stderr,"status = %s\n",cairo_status_to_string(cairo_status(plot.cr))));
FPRINTF((stderr,"Text finished\n"));
}
/* sent when gnuplot exits and when the terminal or the output change.*/
void cairotrm_reset()
{
if (plot.cr)
cairo_destroy(plot.cr);
plot.cr = NULL;
#ifdef PSLATEX_DRIVER
/* finish latex output */
if (ISCAIROLATEX)
PSLATEX_reset();
#endif
FPRINTF((stderr,"cairotrm_reset\n"));
}
void cairotrm_move(unsigned int x, unsigned int y)
{
gp_cairo_move(&plot, x, term->ymax - y);
}
void cairotrm_vector(unsigned int x, unsigned int y)
{
gp_cairo_vector(&plot, x, term->ymax - y);
}
void cairotrm_put_text(unsigned int x, unsigned int y, const char * string)
{
if (!string || !strlen(string))
return;
/* if ignore_enhanced_text is set, draw with the normal routine.
* This is meant to avoid enhanced syntax when the enhanced mode is on */
if (ignore_enhanced_text || !cairo_params->enhanced) {
gp_cairo_draw_text(&plot, x, term->ymax - y, string, NULL, NULL);
return;
}
/* If there are no mark-up characters we can use the normal routine */
if (!strpbrk(string, "{}^_@&~") && !contains_unicode(string)) {
gp_cairo_draw_text(&plot, x, term->ymax - y, string, NULL, NULL);
return;
}
/* Uses enhanced_recursion() to analyse the string to print.
* enhanced_recursion() calls _enhanced_open() to initialize the text drawing,
* then it calls _enhanced_writec() which buffers the characters to draw,
* and finally _enhanced_flush() to draw the buffer with the correct justification.
*/
gp_cairo_enhanced_init(&plot, strlen(string));
/* set up the global variables needed by enhanced_recursion() */
enhanced_fontscale = cairo_params->fontscale;
strncpy(enhanced_escape_format, "%c", sizeof(enhanced_escape_format));
/* 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 (*(string = enhanced_recursion((char*)string, TRUE,
cairo_enhanced_fontname,
plot.fontsize,
0.0, TRUE, TRUE, 0))) {
cairotrm_enhanced_flush();
/* we can only get here if *str == '}' */
enh_err_check(string);
if (!*++string)
break; /* end of string */
/* else carry on and process the rest of the string */
}
/* I still don't understand why this is needed */
gp_cairo_clear_bold_font(&plot);
/* finish */
gp_cairo_enhanced_finish(&plot, x, term->ymax - y);
return;
}
void cairotrm_enhanced_flush()
{
gp_cairo_enhanced_flush(&plot);
}
void cairotrm_enhanced_writec(int c)
{
gp_cairo_enhanced_writec(&plot, c);
}
void cairotrm_enhanced_open(char* fontname, double fontsize, double base, TBOOLEAN widthflag, TBOOLEAN showflag, int overprint)
{
gp_cairo_enhanced_open(&plot, fontname, fontsize,
base, widthflag, showflag, overprint);
}
void cairotrm_linetype(int lt)
{
gp_cairo_set_linetype(&plot, lt);
/* Version 5: always set solid lines at this point. If a dashed */
/* line is wanted there will be a later call to term->dashtype() */
gp_cairo_set_linestyle(&plot, GP_CAIRO_SOLID);
if (cairo_params->mono && lt >= -1)
gp_cairo_set_color(&plot, gp_cairo_linetype2color( -1 ), 0.0);
else
gp_cairo_set_color(&plot, gp_cairo_linetype2color( lt ), 0.0);
}
/* - fonts are selected as strings "name,size".
* - _set_font("") restores the terminal's default font.*/
int cairotrm_set_font (const char *font)
{
char *fontname = NULL;
float fontsize = 0;
if (font && (*font)) {
int sep = strcspn(font,",");
fontname = strdup(font);
if (font[sep] == ',') {
sscanf(&(font[sep+1]), "%f", &fontsize);
fontname[sep] = '\0';
}
} else {
fontname = strdup("");
}
if ( strlen(fontname) == 0 ) {
free(fontname);
if ( !cairo_params->fontname || strlen(cairo_params->fontname) == 0 )
fontname = gp_strdup(CAIROTRM_DEFAULT_FONTNAME);
else
fontname = gp_strdup(cairo_params->fontname);
}
if ( fontsize == 0 ) {
if ( cairo_params->fontsize == 0 )
fontsize = cairo_params_default->fontsize;
else
fontsize = cairo_params->fontsize;
}
/* Reset the term variables (hchar, vchar, h_tic, v_tic).
* They may be taken into account in next plot commands */
gp_cairo_set_font(&plot, fontname, fontsize * cairo_params->fontscale);
gp_cairo_set_termvar(&plot, &(term->v_char), &(term->h_char));
/* Enhanced text processing needs to know the new font also */
if (*fontname) {
free(cairo_enhanced_fontname);
cairo_enhanced_fontname = strdup(fontname);
}
free(fontname);
/* the returned int is not used anywhere */
return 1;
}
int cairotrm_justify_text(enum JUSTIFY mode)
{
gp_cairo_set_justify(&plot,mode);
return 1; /* we can justify */
}
void cairotrm_point(unsigned int x, unsigned int y, int pointstyle)
{
gp_cairo_draw_point(&plot, x, term->ymax - y, pointstyle);
}
void cairotrm_pointsize(double ptsize)
{
ptsize = (ptsize < 0) ? cairo_params->ps : cairo_params->ps * ptsize;
gp_cairo_set_pointsize(&plot, ptsize);
}
void cairotrm_linewidth(double lw)
{
lw *= cairo_params->lw * cairo_params->base_linewidth;
gp_cairo_set_linewidth(&plot, lw);
}
int cairotrm_text_angle(float angle)
{
/* a double is needed to compute cos, sin, etc. */
gp_cairo_set_textangle(&plot, angle);
return 1; /* 1 means we can rotate */
}
void cairotrm_fillbox(int style, unsigned int x, unsigned int y, unsigned int width, unsigned int height)
{
gp_cairo_draw_fillbox(&plot, x, term->ymax - y,
width, height,
style);
}
int cairotrm_make_palette(t_sm_palette * palette)
{
/* we can do continuous colors */
return 0;
}
void cairotrm_set_color(t_colorspec *colorspec)
{
rgb_color rgb1;
double alpha = 0.0;
if (colorspec->type == TC_LT) {
rgb1 = gp_cairo_linetype2color(colorspec->lt);
} else if (colorspec->type == TC_FRAC && cairo_params->mono) {
int save_colorMode = sm_palette.colorMode;
sm_palette.colorMode = SMPAL_COLOR_MODE_GRAY;
rgb1maxcolors_from_gray( colorspec->value, &rgb1 );
sm_palette.colorMode = save_colorMode;
} else if (colorspec->type == TC_FRAC) {
rgb1maxcolors_from_gray( colorspec->value, &rgb1 );
} else if (colorspec->type == TC_RGB) {
rgb1.r = (double) ((colorspec->lt >> 16) & 0xff)/255;
rgb1.g = (double) ((colorspec->lt >> 8) & 0xff)/255;
rgb1.b = (double) ((colorspec->lt) & 0xff)/255;
alpha = (double) ((colorspec->lt >> 24) & 0xff)/255;
} else return;
gp_cairo_set_color(&plot, rgb1, alpha);
#if PSLATEX_DRIVER
/* Used by EPSLATEX_boxed_text */
PSLATEX_opacity = 1.0 - alpha;
#endif
}
/* here we send the polygon command */
void cairotrm_filled_polygon(int n, gpiPoint *corners)
{
gpiPoint *corners_copy;
if (n