1 /* GNUPLOT - gp_cairo.c */
2 
3 /*[
4  * Copyright 2005,2006   Timothee Lecomte
5  *
6  * Permission to use, copy, and distribute this software and its
7  * documentation for any purpose with or without fee is hereby granted,
8  * provided that the above copyright notice appear in all copies and
9  * that both that copyright notice and this permission notice appear
10  * in supporting documentation.
11  *
12  * Permission to modify the software is granted, but not the right to
13  * distribute the complete modified source code.  Modifications are to
14  * be distributed as patches to the released version.  Permission to
15  * distribute binaries produced by compiling modified sources is granted,
16  * provided you
17  *   1. distribute the corresponding source modifications from the
18  *    released version in the form of a patch file along with the binaries,
19  *   2. add special version identification to distinguish your version
20  *    in addition to the base release version number,
21  *   3. provide your name and address as the primary contact for the
22  *    support of your modified version, and
23  *   4. retain our contact information in regard to use of the base
24  *    software.
25  * Permission to distribute the released version of the source code along
26  * with corresponding source modifications in the form of a patch file is
27  * granted with same provisions 2 through 4 for binary distributions.
28  *
29  * This software is provided "as is" without express or implied warranty
30  * to the extent permitted by applicable law.
31  *
32  *
33  * Alternatively, the contents of this file may be used under the terms of the
34  * GNU General Public License Version 2 or later (the "GPL"), in which case the
35  * provisions of GPL are applicable instead of those above. If you wish to allow
36  * use of your version of this file only under the terms of the GPL and not
37  * to allow others to use your version of this file under the above gnuplot
38  * license, indicate your decision by deleting the provisions above and replace
39  * them with the notice and other provisions required by the GPL. If you do not
40  * delete the provisions above, a recipient may use your version of this file
41  * under either the GPL or the gnuplot license.
42 ]*/
43 
44 /* -----------------------------------------------------
45  * This code uses the cairo library, a 2D graphics library with
46  * support for multiple output devices.
47  * Cairo is distributed under the LGPL licence.
48  *
49  * See http://www.cairographics.org for details.
50 
51  * It also uses the pango library, a text-layout rendering library.
52  * Pango is distributed under the LGPL licence.
53  *
54  * See http://www.pango.org for details.
55  * -----------------------------------------------------*/
56 
57 /* ------------------------------------------------------
58  * This file implements all cairo related functions,
59  * which provide drawing facilities.
60  *
61  * In particular, we have here :
62  * - all the basic calls (lines, polygons for pm3d, custom patterns),
63  * - image support,
64  * - enhanced text mode
65  *
66  * The text rendering is done via pango.
67  * ------------------------------------------------------*/
68 
69 #include "gp_cairo.h"
70 
71 #include "alloc.h"
72 
73 #include <pango/pangocairo.h>
74 #include <glib.h>
75 
76 #ifdef _MSC_VER
77 #define rint(x) floor((x)+0.5L)
78 #endif
79 
80 /* undef this to see what happens without the Symbol-to-unicode processing */
81 #define MAP_SYMBOL
82 
83 /* ========  enhanced text mode ======== */
84 /* copies of internal variables */
85 static char gp_cairo_enhanced_font[100] = "";
86 static const char* gp_cairo_enhanced_get_fontname(plot_struct *plot);
87 static double gp_cairo_enhanced_fontsize = 0;
88 static double gp_cairo_enhanced_base = 0;
89 static TBOOLEAN gp_cairo_enhanced_widthflag = TRUE;
90 static TBOOLEAN gp_cairo_enhanced_showflag = TRUE;
91 static int gp_cairo_enhanced_overprint = FALSE;
92 static TBOOLEAN gp_cairo_enhanced_opened_string  = FALSE;  /* try to cut out empty ()'s */
93 static char *gp_cairo_enhanced_string;
94 static char *gp_cairo_enhanced_char;
95 /* utf8 text to draw and its attributes */
96 static gchar gp_cairo_utf8[2048] = "";
97 static PangoAttrList *gp_cairo_enhanced_AttrList = NULL;
98 /* save/restore facilitiy */
99 static TBOOLEAN gp_cairo_enhanced_restore_now = FALSE;
100 static TBOOLEAN gp_cairo_enhanced_save = FALSE;
101 static gchar gp_cairo_save_utf8[2048] = "";
102 static PangoAttrList *gp_cairo_enhanced_save_AttrList = NULL;
103 /* underprint/overprint facility */
104 static gchar gp_cairo_underprinted_utf8[2048] = "";
105 static PangoAttrList *gp_cairo_enhanced_underprinted_AttrList = NULL;
106 /* converts text from symbol encoding to utf8 encoding */
107 static gchar* gp_cairo_convert_symbol_to_unicode(plot_struct *plot, const char* string);
108 /* add standard attributes (fontsize,fontfamily, rise) to
109  * the specified characters in a PangoAttrList */
110 static void gp_cairo_add_attr(plot_struct *plot, PangoAttrList * AttrList, int start, int end );
111 /* add a blank character to the text string and an associated custom shape to the attribute list */
112 static void gp_cairo_add_shape( PangoRectangle rect,int position);
113 
114 /* Average character height as reported back through term->v_char */
115 static int avg_vchar = 150;
116 
117 /* set a cairo pattern or solid fill depending on parameters */
118 static void gp_cairo_fill(plot_struct *plot, int fillstyle, int fillpar);
119 static void gp_cairo_fill_pattern(plot_struct *plot, int fillstyle, int fillpar);
120 
121 /* Boxed text support */
122 static int bounding_box[4];
123 static double bounding_xmargin = 1.0;
124 static double bounding_ymargin = 1.0;
125 static double box_rotation = 0.0;
126 static double box_origin_x;
127 static double box_origin_y;
128 static TBOOLEAN in_textbox = FALSE;
129 
130 /* array of colors
131  * FIXME could be shared with all gnuplot terminals */
132 static rgb_color gp_cairo_colorlist[12] = {
133 {1,1,1}, /* white */
134 {0,0,0}, /* black */
135 {0,0,0}, /* black */
136 {1,0,0}, /* red */
137 {0,1,0}, /* green */
138 {0,0,1}, /* blue */
139 {1,0,1}, /* magenta */
140 {0,1,1}, /* cyan */
141 {1,1,0}, /* yellow */
142 {0,0,0}, /* black */
143 {1,0.3,0}, /* orange */
144 {0.5,0.5,0.5} /* grey */
145 };
146 
147 /* correspondence between gnuplot linetypes and terminal colors */
gp_cairo_set_background(rgb_color background)148 void gp_cairo_set_background( rgb_color background )
149 {
150 	gp_cairo_colorlist[0] = background;
151 }
152 
gp_cairo_linetype2color(int linetype)153 rgb_color gp_cairo_linetype2color( int linetype )
154 {
155 	if (linetype<=LT_NODRAW)
156 		return gp_cairo_colorlist[ 0 ];
157 	else
158 		return gp_cairo_colorlist[ linetype%9 +3 ];
159 }
160 
161 /* initialize all fields of the plot structure */
gp_cairo_initialize_plot(plot_struct * plot)162 void gp_cairo_initialize_plot(plot_struct *plot)
163 {
164 	plot->xscale = 1.0; plot->yscale = 1.0;
165 	plot->device_xmax = 1; plot->device_ymax = 1;
166 	plot->xmax = 1; plot->ymax = 1;
167 
168 	plot->justify_mode = LEFT;
169 	plot->linetype = 1;
170 	plot->linewidth = 1.0;
171 	plot->linestyle = GP_CAIRO_SOLID;
172 	plot->pointsize = 1.0;
173 	plot->dashlength = 1.0;
174 	plot->text_angle = 0.0;
175 	plot->color.r = 0.0; plot->color.g = 0.0; plot->color.b = 0.0;
176 	plot->background.r = 1.0; plot->background.g = 1.0; plot->background.b = 1.0;
177 
178 	plot->opened_path = FALSE;
179 
180 	plot->current_x = -1; plot->current_y = -1;
181 
182 	safe_strncpy(plot->fontname, "", sizeof(plot->fontname));
183 	plot->fontsize = 1.0;
184 	plot->encoding = S_ENC_DEFAULT;
185 
186 	plot->success = FALSE;
187 
188 	plot->antialiasing = TRUE;
189 
190 	plot->oversampling = TRUE;
191 	plot->oversampling_scale = GP_CAIRO_SCALE;
192 
193 	plot->upsampling_rate = 1.0;
194 
195 	plot->linecap = BUTT;
196 
197 	plot->hinting = 100;
198 
199 	plot->polygons_saturate = TRUE;
200 
201 	plot->cr = NULL;
202 
203 	plot->polygon_path_last = NULL;
204 
205 	plot->interrupt = FALSE;
206 
207 	in_textbox = FALSE;
208 }
209 
210 /* set the transformation matrix of the context, and other details */
211 /* NOTE : depends on the setting of xscale and yscale */
gp_cairo_initialize_context(plot_struct * plot)212 void gp_cairo_initialize_context(plot_struct *plot)
213 {
214 	cairo_matrix_t matrix;
215 
216 	if (plot->oversampling)
217 		plot->oversampling_scale = GP_CAIRO_SCALE;
218 	else
219 		plot->oversampling_scale = 1;
220 
221 	if (plot->antialiasing)
222 		cairo_set_antialias(plot->cr,CAIRO_ANTIALIAS_DEFAULT);
223 	else
224 		cairo_set_antialias(plot->cr,CAIRO_ANTIALIAS_NONE);
225 
226 	cairo_matrix_init(&matrix,
227 			plot->xscale/plot->oversampling_scale,
228 			0, 0,
229 			plot->yscale/plot->oversampling_scale,
230 			0.5, 0.5);
231 	cairo_set_matrix(plot->cr, &matrix);
232 
233 	/* Default is square caps, mitered joins */
234 	if (plot->linecap == ROUNDED) {
235 	    cairo_set_line_cap  (plot->cr, CAIRO_LINE_CAP_ROUND);
236 	    cairo_set_line_join (plot->cr, CAIRO_LINE_JOIN_ROUND);
237 	} else if (plot->linecap == SQUARE) {
238 	    cairo_set_line_cap  (plot->cr, CAIRO_LINE_CAP_SQUARE);
239 	    cairo_set_line_join (plot->cr, CAIRO_LINE_JOIN_MITER);
240 	    cairo_set_miter_limit(plot->cr, 3.8);
241 	} else {
242 	    cairo_set_line_cap  (plot->cr, CAIRO_LINE_CAP_BUTT);
243 	    cairo_set_line_join (plot->cr, CAIRO_LINE_JOIN_MITER);
244 	    cairo_set_miter_limit(plot->cr, 3.8);
245 	}
246 
247 }
248 
249 
gp_cairo_set_color(plot_struct * plot,rgb_color color,double alpha)250 void gp_cairo_set_color(plot_struct *plot, rgb_color color, double alpha)
251 {
252 	/*stroke any open path */
253 	gp_cairo_stroke(plot);
254 
255 	FPRINTF((stderr,"set_color %lf %lf %lf\n",color.r, color.g, color.b));
256 
257 	plot->color.r = color.r;
258 	plot->color.g = color.g;
259 	plot->color.b = color.b;
260 	plot->color.alpha = alpha;
261 }
262 
263 
gp_cairo_set_linestyle(plot_struct * plot,int linestyle)264 void gp_cairo_set_linestyle(plot_struct *plot, int linestyle)
265 {
266 	/*stroke any open path */
267 	gp_cairo_stroke(plot);
268 	/* draw any open polygon set */
269 	gp_cairo_end_polygon(plot);
270 
271 	FPRINTF((stderr,"set_linestyle %d\n",linestyle));
272 
273 	plot->linestyle = linestyle;
274 }
275 
276 
gp_cairo_set_linetype(plot_struct * plot,int linetype)277 void gp_cairo_set_linetype(plot_struct *plot, int linetype)
278 {
279 	/*stroke any open path */
280 	gp_cairo_stroke(plot);
281 	/* draw any open polygon set */
282 	gp_cairo_end_polygon(plot);
283 
284 	FPRINTF((stderr,"set_linetype %d\n",linetype));
285 
286 	plot->linetype = linetype;
287 }
288 
289 
gp_cairo_set_pointsize(plot_struct * plot,double pointsize)290 void gp_cairo_set_pointsize(plot_struct *plot, double pointsize)
291 {
292 	FPRINTF((stderr,"set_pointsize %lf\n",pointsize));
293 
294 	plot->pointsize = pointsize;
295 }
296 
297 
gp_cairo_set_justify(plot_struct * plot,JUSTIFY mode)298 void gp_cairo_set_justify(plot_struct *plot, JUSTIFY mode)
299 {
300 	FPRINTF((stderr,"set_justify\n"));
301 
302 	plot->justify_mode = mode;
303 }
304 
305 
gp_cairo_set_font(plot_struct * plot,const char * name,float fontsize)306 void gp_cairo_set_font(plot_struct *plot, const char *name, float fontsize)
307 {
308     char *c;
309     char *fname;
310 
311 	FPRINTF((stderr,"set_font \"%s\" %f\n", name,fontsize));
312 
313 	/* Split out Bold and Italic attributes from font name */
314 	fname = strdup(name);
315 	for (c=fname; *c; c++) {
316 	    if (*c == '\\') {
317 		char *d = c;
318 		do { *d = *(d+1); } while (*d++);
319 	    } else {
320 		if (*c == '-') *c = ' ';
321 		if (*c == ':') *c = ' ';
322 	    }
323 	}
324 	if ((c = strstr(fname, " Bold"))) {
325 	    do { *c = *(c+5); } while (*c++);
326 	    plot->fontweight = PANGO_WEIGHT_BOLD;
327 	} else
328 	    plot->fontweight = PANGO_WEIGHT_NORMAL;
329 	if ((c = strstr(fname, " Italic"))) {
330 	    do { *c = *(c+7); } while (*c++);
331 	    plot->fontstyle = PANGO_STYLE_ITALIC;
332 	} else
333 	    plot->fontstyle = PANGO_STYLE_NORMAL;
334 
335 	safe_strncpy( plot->fontname, fname, sizeof(plot->fontname) );
336 	plot->fontsize = fontsize;
337 	free(fname);
338 }
339 
340 
gp_cairo_set_linewidth(plot_struct * plot,double linewidth)341 void gp_cairo_set_linewidth(plot_struct *plot, double linewidth)
342 {
343 	FPRINTF((stderr,"set_linewidth %lf\n",linewidth));
344 
345 	/*stroke any open path */
346 	gp_cairo_stroke(plot);
347 	/* draw any open polygon set */
348 	gp_cairo_end_polygon(plot);
349 
350 	if (!strcmp(term->name,"pdfcairo"))
351 		linewidth *= 2;
352 
353 	if (linewidth < 0.20)	/* Admittedly arbitrary */
354 	    linewidth = 0.20;
355 	plot->linewidth = linewidth;
356 
357 }
358 
359 
gp_cairo_set_textangle(plot_struct * plot,double angle)360 void gp_cairo_set_textangle(plot_struct *plot, double angle)
361 {
362 	FPRINTF((stderr,"set_textangle %lf\n",angle));
363 
364 	plot->text_angle =angle;
365 }
366 
367 /* By default, Cairo uses an antialiasing algorithm which may
368  * leave a seam between polygons which share a common edge.
369  * Several solutions allow to workaround this behaviour :
370  * - don't antialias the polygons
371  *   Problem : aliased lines are ugly
372  * - stroke on each edge
373  *   Problem : stroking is a very time-consuming operation
374  * - draw without antialiasing to a separate context of a bigger size
375  *   Problem : not really in the spirit of the rest of the drawing.
376  * - enlarge the polygons so that they overlap slightly
377  *   Problem : It is really more time-consuming that it may seem.
378  *   It implies inspecting each corner to find which direction to move it
379  *   (making the difference between the inside and the outside of the polygon).
380  * - using CAIRO_OPERATOR_SATURATE
381  *   Problem : for each set of polygons, we have to draw front-to-back
382  *   on a separate context and then copy back to this one.
383  *   Time-consuming but probably less than stroking all the edges.
384  *
385  * The last solution is implemented if plot->polygons_saturate is set to TRUE
386  * Otherwise the default (antialiasing but may have seams) is used.
387  */
388 
gp_cairo_draw_polygon(plot_struct * plot,int n,gpiPoint * corners)389 void gp_cairo_draw_polygon(plot_struct *plot, int n, gpiPoint *corners)
390 {
391 	/* begin by stroking any open path */
392 	gp_cairo_stroke(plot);
393 
394 	if (plot->polygons_saturate) {
395 		int i;
396 		path_item *path;
397 
398 		path = (path_item*) gp_alloc(sizeof(path_item), "gp_cairo : polygon path");
399 
400 		path->n = n;
401 		path->corners = (gpiPoint*) gp_alloc(n*sizeof(gpiPoint), "gp_cairo : polygon corners");
402 		for(i=0;i<n;i++)
403 			*(path->corners + i) = *corners++;
404 
405 		path->color = plot->color;
406 
407 		if (plot->polygon_path_last == NULL) {
408 			FPRINTF((stderr,"creating a polygon path\n"));
409 			path->previous = NULL;
410 			plot->polygon_path_last = path;
411 		} else {
412 			FPRINTF((stderr,"adding a polygon to the polygon path\n"));
413 			path->previous = plot->polygon_path_last;
414 			plot->polygon_path_last = path;
415 		}
416 	} else {
417 		int i;
418 		/* draw the polygon directly */
419 		FPRINTF((stderr,"processing one polygon\n"));
420 		cairo_move_to(plot->cr, corners[0].x, corners[0].y);
421 		for (i=1;i<n;++i)
422 			cairo_line_to(plot->cr, corners[i].x, corners[i].y);
423 		cairo_close_path(plot->cr);
424 		gp_cairo_fill( plot, corners->style & 0xf, corners->style >> 4 );
425 		cairo_fill(plot->cr);
426 	}
427 }
428 
429 
gp_cairo_end_polygon(plot_struct * plot)430 void gp_cairo_end_polygon(plot_struct *plot)
431 {
432 	int i;
433 	path_item *path;
434 	path_item *path2;
435 	rgba_color color_sav;
436 	cairo_t *context;
437 	cairo_t *context_sav;
438 	cairo_surface_t *surface;
439 	cairo_matrix_t matrix;
440 	cairo_matrix_t matrix2;
441 	cairo_pattern_t *pattern;
442 
443 	/* when we are not using OPERATOR_SATURATE, the polygons are drawn
444 	 * directly in gp_cairo_draw_polygon */
445 	if (!plot->polygons_saturate)
446 		return;
447 
448 	if (plot->polygon_path_last == NULL)
449 		return;
450 
451 	path = plot->polygon_path_last;
452 	color_sav = plot->color;
453 
454 	/* if there's only one polygon, draw it directly */
455 	if (path->previous == NULL) {
456 		FPRINTF((stderr,"processing one polygon\n"));
457 		cairo_move_to(plot->cr, path->corners[0].x, path->corners[0].y);
458 		for (i=1;i<path->n;++i)
459 			cairo_line_to(plot->cr, path->corners[i].x, path->corners[i].y);
460 		cairo_close_path(plot->cr);
461 		plot->color = path->color;
462 		gp_cairo_fill( plot, path->corners->style & 0xf, path->corners->style >> 4 );
463 		cairo_fill(plot->cr);
464 		free(path->corners);
465 		free(path);
466 		plot->polygon_path_last = NULL;
467 		plot->color = color_sav;
468 		return;
469 	}
470 
471 	FPRINTF((stderr,"processing several polygons\n"));
472 
473 /* this is meant to test Full-Scene-Anti-Aliasing by supersampling,
474  * in association with CAIRO_ANTIALIAS_NONE a few lines below */
475 #define SCALE 1
476 
477 	/* otherwise, draw front-to-back to a separate context,
478 	 * using CAIRO_OPERATOR_SATURATE */
479 	context_sav = plot->cr;
480 	surface = cairo_surface_create_similar(cairo_get_target(plot->cr),
481                                              CAIRO_CONTENT_COLOR_ALPHA,
482                                              plot->device_xmax*plot->upsampling_rate*SCALE,
483                                              plot->device_ymax*plot->upsampling_rate*SCALE);
484 	context = cairo_create(surface);
485 	cairo_set_operator(context,CAIRO_OPERATOR_SATURATE);
486 	if (plot->antialiasing)
487 		cairo_set_antialias(context,CAIRO_ANTIALIAS_DEFAULT);
488 	else
489 		cairo_set_antialias(context,CAIRO_ANTIALIAS_NONE);
490 
491 	/* transformation matrix between gnuplot and cairo coordinates */
492 	cairo_matrix_init(&matrix,
493 			plot->xscale/SCALE/plot->oversampling_scale,
494 			0,0,
495 			plot->yscale/SCALE/plot->oversampling_scale,
496 			0.5,0.5);
497 	cairo_set_matrix(context, &matrix);
498 
499  	plot->cr = context;
500 	path = plot->polygon_path_last;
501 
502 	while (path != NULL) {
503 		/* check for interrupt */
504 		if (plot->interrupt)
505 			break;
506 		/* build the path */
507 		cairo_move_to(plot->cr, path->corners[0].x, path->corners[0].y);
508 		for (i=1;i<(path->n);++i)
509 			cairo_line_to(plot->cr, path->corners[i].x, path->corners[i].y);
510 		cairo_close_path(plot->cr);
511 		/* set the fill pattern */
512 		plot->color = path->color;
513 		gp_cairo_fill( plot, path->corners->style & 0xf, path->corners->style >> 4 );
514 		cairo_fill(plot->cr);
515 		/* free the resources, and go to the next point */
516 		free(path->corners);
517 		path2 = path->previous;
518 		free(path);
519 		path = path2;
520 	}
521 
522 	plot->polygon_path_last = NULL;
523 
524 	pattern = cairo_pattern_create_for_surface( surface );
525 	cairo_destroy( context );
526 
527 	/* compensate the transformation matrix of the main context */
528 	cairo_matrix_init(&matrix2,
529 			plot->xscale*SCALE/plot->oversampling_scale,
530 			0,0,
531 			plot->yscale*SCALE/plot->oversampling_scale,
532 			0.5,0.5);
533 	cairo_pattern_set_matrix( pattern, &matrix2 );
534 
535 	plot->cr = context_sav;
536 	plot->color = color_sav;
537 	cairo_surface_destroy( surface );
538 	cairo_set_source( plot->cr, pattern );
539 	cairo_pattern_destroy( pattern );
540 	cairo_paint( plot->cr );
541 }
542 
gp_cairo_set_dashtype(plot_struct * plot,int type,t_dashtype * custom_dash_type)543 void gp_cairo_set_dashtype(plot_struct *plot, int type, t_dashtype *custom_dash_type)
544 {
545 	static double dashpattern[4][8] =
546 	{
547 	    {5, 8, 5, 8, 5, 8, 5, 8},	/* Medium dash */
548 	    {1, 4, 1, 4, 1, 4, 1, 4},	/* dots */
549 	    {8, 4, 2, 4, 8, 4, 2, 4},	/* dash dot */
550 	    {9, 4, 1, 4, 1, 4, 0, 0}	/* dash dot dot */
551 	};
552 	int lt = (type) % 5;
553 
554 	if (type == DASHTYPE_CUSTOM && custom_dash_type) {
555 		/* Convert to internal representation */
556 		int i;
557 		double empirical_scale;
558 
559 		if (!strcmp(term->name,"pngcairo"))
560 			empirical_scale = 0.25;
561 		else
562 			empirical_scale = 0.55;
563 
564 		if (plot->linewidth > 1)
565 			empirical_scale *= plot->linewidth;
566 
567 		for (i=0; i<8; i++)
568 			plot->current_dashpattern[i] = custom_dash_type->pattern[i]
569 				* plot->dashlength
570 				* plot->oversampling_scale * empirical_scale;
571 		gp_cairo_set_linestyle(plot, GP_CAIRO_DASH);
572 
573 	} else if (type > 0 && lt != 0) {
574 		/* Use old (version 4) set of linetype patterns */
575 		int i;
576 		double empirical_scale = 1.;
577 		if (plot->linewidth > 1)
578 			empirical_scale *= plot->linewidth;
579 
580 		for (i=0; i<8; i++)
581 			plot->current_dashpattern[i] = dashpattern[lt-1][i]
582 				* plot->dashlength
583 				* plot->oversampling_scale
584 				* empirical_scale;
585 		gp_cairo_set_linestyle(plot, GP_CAIRO_DASH);
586 
587 	} else {
588 		/* Every 5th pattern in the old set is solid */
589 		gp_cairo_set_linestyle(plot, GP_CAIRO_SOLID);
590 	}
591 }
592 
gp_cairo_stroke(plot_struct * plot)593 void gp_cairo_stroke(plot_struct *plot)
594 {
595 	int lt = plot->linetype;
596 	double lw = plot->linewidth * plot->oversampling_scale;
597 
598 	if (!plot->opened_path) {
599 		FPRINTF((stderr,"stroke with non-opened path !\n"));
600 		return;
601 	}
602 
603 	/* add last point */
604 	cairo_line_to (plot->cr, plot->current_x, plot->current_y);
605 
606 
607 	cairo_save(plot->cr);
608 
609 	if (plot->linetype == LT_NODRAW) {
610 		cairo_set_operator(plot->cr, CAIRO_OPERATOR_DEST);
611 		lw = 0.0;
612 
613 	} else if (lt == LT_AXIS || plot->linestyle == GP_CAIRO_DOTS) {
614 		/* Grid lines (lt 0) */
615 		double dashes[2];
616 		double empirical_scale = 1.0;
617 		if (plot->linewidth > 1)
618 			empirical_scale *= plot->linewidth;
619 		dashes[0] = 0.4 * plot->oversampling_scale * plot->dashlength * empirical_scale;
620 		dashes[1] = 4.0 * plot->oversampling_scale * plot->dashlength * empirical_scale;
621 		cairo_set_dash(plot->cr, dashes, 2 /*num_dashes*/, 0 /*offset*/);
622 	}
623 
624 	else if (plot->linestyle == GP_CAIRO_DASH) {
625 		cairo_set_dash(plot->cr, &(plot->current_dashpattern[0]), 8 /*num_dashes*/, 0 /*offset*/);
626 	}
627 
628 	cairo_set_source_rgba(plot->cr, plot->color.r, plot->color.g, plot->color.b,
629 				1. - plot->color.alpha);
630 	cairo_set_line_width(plot->cr, lw);
631 
632 	cairo_stroke(plot->cr);
633 
634 	cairo_restore(plot->cr);
635 
636 	plot->opened_path = FALSE;
637 }
638 
639 
gp_cairo_move(plot_struct * plot,int x,int y)640 void gp_cairo_move(plot_struct *plot, int x, int y)
641 {
642 	/* Dec 2014 - Do not let zero-length moves interrupt     */
643 	/* the current line/polyline context, e.g. dash pattern. */
644 	if (x == plot->current_x && y == plot->current_y)
645 		return;
646 
647 	/* begin by stroking any open path */
648 	gp_cairo_stroke(plot);
649 	/* also draw any open polygon set */
650 	gp_cairo_end_polygon(plot);
651 
652 	plot->current_x = x;
653 	plot->current_y = y;
654 	plot->orig_current_x = x;
655 	plot->orig_current_y = y;
656 }
657 
658 
gp_cairo_vector(plot_struct * plot,int x,int y)659 void gp_cairo_vector(plot_struct *plot, int x, int y)
660 {
661 	double x1 = x, y1 = y;
662 	double new_pos;
663 	double weight1 = (double) plot->hinting/100;
664 	double weight2 = 1.0 - weight1;
665 
666 	/* begin by drawing any open polygon set */
667 	gp_cairo_end_polygon(plot);
668 
669 	FPRINTF((stderr,"vector\n"));
670 
671 	/* hinting magic when we are using antialiasing+oversampling */
672 	if (plot->antialiasing && plot->oversampling) {
673 		if (plot->hinting < 0 || plot->hinting > 100) {
674 			fprintf(stderr,"wxt terminal : hinting error, setting to default\n");
675 			plot->hinting = 100;
676 		}
677 
678 		/* detect and handle vertical lines */
679 		/* the second test is there to avoid artefacts when you choose
680 		* a high sampling ('set samples 10000'), so that a smooth function
681 		* may be drawn as lines between very close points */
682 		if (plot->orig_current_x == x1 && fabs(plot->orig_current_y - y1)>plot->oversampling_scale) {
683 			new_pos = rint(plot->current_x*plot->xscale/plot->oversampling_scale);
684 			new_pos *= plot->oversampling_scale/plot->xscale;
685 			plot->current_x = weight1*new_pos + weight2*plot->current_x;
686 			x1 = plot->current_x;
687 			new_pos = rint(plot->current_y*plot->yscale/plot->oversampling_scale);
688 			new_pos *= plot->oversampling_scale/plot->yscale;
689 			plot->current_y = weight1*new_pos + weight2*plot->current_y;
690 			new_pos = rint(y1*plot->yscale/plot->oversampling_scale);
691 			new_pos *= plot->oversampling_scale/plot->yscale;
692 			y1 = weight1*new_pos + weight2*y1;
693 		}
694 		/* do the same for horizontal lines */
695 		if (plot->orig_current_y == y1 && fabs(plot->orig_current_x - x1)>plot->oversampling_scale) {
696 			new_pos = rint(plot->current_y*plot->yscale/plot->oversampling_scale);
697 			new_pos *= plot->oversampling_scale/plot->yscale;
698 			plot->current_y = weight1*new_pos + weight2*plot->current_y;
699 			y1 = plot->current_y;
700 			new_pos = rint(plot->current_x*plot->xscale/plot->oversampling_scale);
701 			new_pos *= plot->oversampling_scale/plot->xscale;
702 			plot->current_x = weight1*new_pos + weight2*plot->current_x;
703 			new_pos = rint(x1*plot->xscale/plot->oversampling_scale);
704 			new_pos *= plot->oversampling_scale/plot->xscale;
705 			x1 = weight1*new_pos + weight2*x1;
706 		}
707 	}
708 
709 	if (!plot->opened_path) {
710 		plot->opened_path = TRUE;
711 		cairo_move_to (plot->cr, plot->current_x, plot->current_y);
712 	} else
713 		cairo_line_to (plot->cr, plot->current_x, plot->current_y);
714 
715 	plot->current_x = x1;
716 	plot->current_y = y1;
717 	plot->orig_current_x = x;
718 	plot->orig_current_y = y;
719 }
720 
721 /* pango needs a string encoded in utf-8. We use g_convert from glib.
722  * gp_cairo_get_encoding() gives the encoding set via 'set enconding'
723  * memory allocated for the string has to be freed */
gp_cairo_convert(plot_struct * plot,const char * string)724 static gchar * gp_cairo_convert(plot_struct *plot, const char* string)
725 {
726 	gsize bytes_read;
727 	GError *error = NULL;
728 	const char *charset = NULL;
729 	gchar * string_utf8;
730 
731 	if (g_utf8_validate(string, -1, NULL)) {
732 	    string_utf8 = g_strdup(string);
733 	} else {
734 	    charset = gp_cairo_get_encoding(plot);
735 	    string_utf8 = g_convert(string, -1, "UTF-8", charset, &bytes_read, NULL, &error);
736 	}
737 
738 	/* handle error case */
739 	if (error != NULL) {
740 		/* fatal error in conversion */
741 		if (error->code != G_CONVERT_ERROR_ILLEGAL_SEQUENCE) {
742 			fprintf(stderr, "Unable to convert \"%s\": %s\n", string, error->message);
743 			g_error_free (error);
744 			return strdup("");
745 		}
746 		/* The sequence is invalid in the chosen charset.
747 		 * we will try to fall back to iso_8859_1, and if it doesn't work,
748 		 * we'll use bytes_read to convert up to the faulty character,
749 		 * and throw the rest. */
750 		g_error_free (error);
751 		error = NULL;
752 		string_utf8 = g_convert(string, -1, "UTF-8", "ISO-8859-1", NULL, NULL, &error);
753 		if (error != NULL) {
754 			fprintf(stderr, "Unable to convert \"%s\": the sequence is invalid "\
755 				"in the current charset (%s), %d bytes read out of %d\n",
756 				string, charset, (int)bytes_read, (int)strlen(string));
757 			string_utf8 = g_convert(string, bytes_read, "UTF-8", charset, NULL, NULL, NULL);
758 			g_error_free (error);
759 		} else
760 			fprintf(stderr, "Unable to convert \"%s\": the sequence is invalid "\
761 				"in the current charset (%s), falling back to iso_8859_1\n",
762 				string, charset);
763 	}
764 
765 	return string_utf8;
766 }
767 
768 /*
769  * The following #ifdef _WIN32 section is all to work around a bug in
770  * the cairo/win32 backend for font rendering.  It has the effect of
771  * testing for libfreetype support, and using that instead if possible.
772  * Suggested by cairo developer Behdad Esfahbod.
773  * Allin Cottrell suggests that this not necessary anymore for newer
774  * versions of cairo.
775  */
776 #if defined(_WIN32) && (CAIRO_VERSION_MAJOR < 2) && (CAIRO_VERSION_MINOR < 10)
777 static PangoLayout *
gp_cairo_create_layout(cairo_t * cr)778 gp_cairo_create_layout(cairo_t *cr)
779 {
780     static PangoFontMap *fontmap;
781     PangoContext *context;
782     PangoLayout *layout;
783 
784     if (fontmap == NULL) {
785         fontmap = pango_cairo_font_map_new_for_font_type(CAIRO_FONT_TYPE_FT);
786         if (fontmap == NULL) {
787 	    fontmap = pango_cairo_font_map_get_default();
788         }
789     }
790 
791 #if PANGO_VERSION_MAJOR > 1 || PANGO_VERSION_MINOR >= 22
792     context = pango_font_map_create_context(fontmap);
793 #else
794     context = pango_cairo_font_map_create_context((PangoCairoFontMap *) fontmap);
795 #endif
796 
797     layout = pango_layout_new(context);
798     g_clear_object (&context);
799 
800     return layout;
801 }
802 #else
803 static PangoLayout *
gp_cairo_create_layout(cairo_t * cr)804 gp_cairo_create_layout(cairo_t *cr)
805 {
806     return pango_cairo_create_layout(cr);
807 }
808 #endif
809 
gp_cairo_draw_text(plot_struct * plot,int x1,int y1,const char * string,int * width,int * height)810 void gp_cairo_draw_text(plot_struct *plot, int x1, int y1, const char* string,
811 		    int *width, int *height)
812 {
813 	double x,y;
814 	double arg;
815 	double vert_just, delta, deltax, deltay;
816 	double box_x, box_y;
817 	PangoRectangle ink_rect;
818 	PangoRectangle logical_rect;
819 	PangoLayout *layout;
820 	PangoFontDescription *desc;
821 	gchar* string_utf8;
822 #ifdef MAP_SYMBOL
823 	TBOOLEAN symbol_font_parsed = FALSE;
824 #endif /*MAP_SYMBOL*/
825 	int baseline_offset;
826 
827 
828 	/* begin by stroking any open path */
829 	gp_cairo_stroke(plot);
830 	/* also draw any open polygon set */
831 	gp_cairo_end_polygon(plot);
832 
833 	FPRINTF((stderr,"draw_text\n"));
834 
835 #ifdef MAP_SYMBOL
836 	/* we have to treat Symbol font as a special case */
837 	if (!strcmp(plot->fontname,"Symbol")) {
838 		FPRINTF((stderr,"Parsing a Symbol string\n"));
839 		string_utf8 = gp_cairo_convert_symbol_to_unicode(plot, string);
840 		safe_strncpy(plot->fontname, gp_cairo_default_font(), sizeof(plot->fontname));
841 		symbol_font_parsed = TRUE;
842 	} else
843 #endif /*MAP_SYMBOL*/
844 	{
845 		/* convert the input string to utf8 */
846 		string_utf8 = gp_cairo_convert(plot, string);
847 	}
848 
849 	/* Create a PangoLayout, set the font and text */
850 	layout = gp_cairo_create_layout (plot->cr);
851 
852 	pango_layout_set_text (layout, string_utf8, -1);
853 	g_free(string_utf8);
854 	desc = pango_font_description_new ();
855 	pango_font_description_set_family (desc, (const char*) plot->fontname);
856 #ifdef MAP_SYMBOL
857 	/* restore the Symbol font setting */
858 	if (symbol_font_parsed)
859 		safe_strncpy(plot->fontname, "Symbol", sizeof(plot->fontname));
860 #endif /*MAP_SYMBOL*/
861 	pango_font_description_set_size (desc, (int) (plot->fontsize*PANGO_SCALE*plot->oversampling_scale) );
862 
863 	pango_font_description_set_weight (desc, plot->fontweight);
864 	pango_font_description_set_style (desc,
865 		plot->fontstyle ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
866 	pango_layout_set_font_description (layout, desc);
867 	FPRINTF((stderr, "pango font description: %s\n", pango_font_description_to_string(desc)));
868 	pango_font_description_free (desc);
869 
870 	pango_layout_get_extents(layout, &ink_rect, &logical_rect);
871 	if (width)
872 		*width = logical_rect.width / PANGO_SCALE;
873 	if (height)
874 		*height = logical_rect.height / PANGO_SCALE;
875 
876 	/* EAM Mar 2009 - Adjusting the vertical position for every character fragment	*/
877 	/* leads to uneven baselines. Better to adjust to the "average" character height */
878 	/* EAM Dec 2012 - The problem is that avg_vchar is not kept in sync with the	*/
879 	/* font size.  It is changed when the set_font command is received, not when	*/
880 	/* it is executed in the display list. Try basing off plot->fontsize instead. 	*/
881 
882 	baseline_offset = pango_layout_get_baseline(layout) / PANGO_SCALE;
883 	vert_just = 0.5 * (float)(plot->fontsize * plot->oversampling_scale);
884 	vert_just = baseline_offset - vert_just;
885 
886 	arg = plot->text_angle * M_PI/180;
887 	x = (double)x1 - vert_just * sin(arg);
888 	y = (double)y1 - vert_just * cos(arg);
889 
890 	delta = ((double)logical_rect.width/2) / PANGO_SCALE;
891 
892 	deltax = delta * cos(arg);
893 	deltay = delta * sin(arg);
894 
895 	switch (plot->justify_mode) {
896 	case LEFT :
897 		break;
898 	case CENTRE :
899 		x -= deltax;
900 		y += deltay;
901 		break;
902 	case RIGHT :
903 		x -= 2*deltax;
904 		y += 2*deltay;
905 		break;
906 	}
907 
908 #if 0 /* helper point */
909 	gp_cairo_draw_point(plot, x1, y1, 0);
910 #endif /* helper point */
911 
912 	cairo_save (plot->cr);
913 	cairo_translate(plot->cr, x, y);
914 	cairo_rotate(plot->cr, -arg);
915 
916 	cairo_set_source_rgba(plot->cr, plot->color.r, plot->color.g, plot->color.b,
917 				1. - plot->color.alpha);
918 
919 	/* Inform Pango to re-layout the text with the new transformation */
920 	pango_cairo_update_layout (plot->cr, layout);
921 	pango_cairo_show_layout (plot->cr, layout);
922 	/* pango_cairo_show_layout does not clear the path (here a starting point)
923 	 * Do it by ourselves, or we can get spurious lines on future calls. */
924 	cairo_new_path(plot->cr);
925 
926 	if (in_textbox) {
927 		box_rotation = -arg;
928 		box_origin_x = x1;
929 		box_origin_y = y1;
930 		box_y = box_origin_y - vert_just;
931 		switch (plot->justify_mode) {
932 		case LEFT:
933 			box_x = box_origin_x;
934 			break;
935 		default:
936 		case CENTRE:
937 			box_x = box_origin_x - delta;
938 			break;
939 		case RIGHT:
940 			box_x = box_origin_x - 2*delta;
941 			break;
942 		}
943 
944 		/* Update bounding box for boxed label text */
945 		pango_layout_get_pixel_extents (layout, &ink_rect, &logical_rect);
946 
947 		/* Auto-initialization */
948 		if (bounding_box[0] < 0 && bounding_box[1] < 0) {
949 		    bounding_box[0] = bounding_box[2] = box_x;
950 		    bounding_box[1] = bounding_box[3] = box_y;
951 		}
952 		if (bounding_box[0] > box_x + ink_rect.x)
953 		    bounding_box[0] = box_x + ink_rect.x;
954 		if (bounding_box[2] < box_x + ink_rect.x + ink_rect.width)
955 		    bounding_box[2] = box_x + ink_rect.x + ink_rect.width;
956 		if (bounding_box[1] > box_y + ink_rect.y)
957 		    bounding_box[1] = box_y + ink_rect.y;
958 		if (bounding_box[3] < box_y + ink_rect.y + ink_rect.height)
959 		    bounding_box[3] = box_y + ink_rect.y + ink_rect.height;
960 	}
961 
962 	/* free the layout object */
963 	g_clear_object (&layout);
964 	cairo_restore (plot->cr);
965 }
966 
967 
gp_cairo_draw_point(plot_struct * plot,int x1,int y1,int style)968 void gp_cairo_draw_point(plot_struct *plot, int x1, int y1, int style)
969 {
970 	double x = x1;
971 	double y = y1;
972 	double new_pos;
973 	double weight1 = (double) plot->hinting/100;
974 	double weight2 = 1.0 - weight1;
975 	double size = plot->pointsize*3*plot->oversampling_scale;
976 
977 	/* begin by stroking any open path */
978 	gp_cairo_stroke(plot);
979 	/* also draw any open polygon set */
980 	gp_cairo_end_polygon(plot);
981 
982 	FPRINTF((stderr,"drawpoint\n"));
983 
984 	/* hinting magic when we are using antialiasing+oversampling */
985 	if (plot->antialiasing && plot->oversampling) {
986 		if (plot->hinting < 0 || plot->hinting > 100) {
987 			fprintf(stderr,"wxt terminal : hinting error, setting to default\n");
988 			plot->hinting = 100;
989 		}
990 
991 		new_pos = rint(x*plot->xscale/plot->oversampling_scale);
992 		new_pos *= plot->oversampling_scale/plot->xscale;
993 		x = weight1*new_pos + weight2*x;
994 
995 		new_pos = rint(y*plot->yscale/plot->oversampling_scale);
996 		new_pos *= plot->oversampling_scale/plot->yscale;
997 		y = weight1*new_pos + weight2*y;
998 	}
999 
1000 	cairo_save(plot->cr);
1001 	cairo_set_line_width(plot->cr, plot->linewidth*plot->oversampling_scale);
1002 	cairo_set_source_rgba(plot->cr, plot->color.r, plot->color.g, plot->color.b,
1003 				1. - plot->color.alpha);
1004 
1005 	/* Dot	FIXME: because this is drawn as a filled circle, it's quite slow */
1006 	if (style < 0) {
1007 		cairo_arc (plot->cr, x, y, 0.5*plot->oversampling_scale, 0, 2*M_PI);
1008 		cairo_fill (plot->cr);
1009 	}
1010 
1011 
1012 	style = style % 15;
1013 	switch (style) {
1014 	case 0: /* plus */
1015 		cairo_move_to(plot->cr, x-size, y);
1016 		cairo_line_to(plot->cr, x+size,y);
1017 		cairo_stroke(plot->cr);
1018 		cairo_move_to(plot->cr, x, y-size);
1019 		cairo_line_to(plot->cr, x,y+size);
1020 		cairo_stroke(plot->cr);
1021 		break;
1022 	case 1: /* plot->cross */
1023 		cairo_move_to(plot->cr, x-size, y-size);
1024 		cairo_line_to(plot->cr, x+size,y+size);
1025 		cairo_stroke(plot->cr);
1026 		cairo_move_to(plot->cr, x-size, y+size);
1027 		cairo_line_to(plot->cr, x+size,y-size);
1028 		cairo_stroke(plot->cr);
1029 		break;
1030 	case 2: /* star */
1031 		cairo_move_to(plot->cr, x-size, y);
1032 		cairo_line_to(plot->cr, x+size,y);
1033 		cairo_stroke(plot->cr);
1034 		cairo_move_to(plot->cr, x, y-size);
1035 		cairo_line_to(plot->cr, x,y+size);
1036 		cairo_stroke(plot->cr);
1037 		cairo_move_to(plot->cr, x-size, y-size);
1038 		cairo_line_to(plot->cr, x+size,y+size);
1039 		cairo_stroke(plot->cr);
1040 		cairo_move_to(plot->cr, x-size, y+size);
1041 		cairo_line_to(plot->cr, x+size,y-size);
1042 		cairo_stroke(plot->cr);
1043 		break;
1044 	case 3: /* box */
1045 	case 4: /* filled box */
1046 		cairo_move_to(plot->cr, x-size, y-size);
1047 		cairo_line_to(plot->cr, x-size,y+size);
1048 		cairo_line_to(plot->cr, x+size,y+size);
1049 		cairo_line_to(plot->cr, x+size,y-size);
1050 		cairo_close_path(plot->cr);
1051 		if (style == 4)
1052 			cairo_fill_preserve(plot->cr);
1053 		cairo_stroke(plot->cr);
1054 		break;
1055 	case 5: /* circle */
1056 		cairo_arc (plot->cr, x, y, size, 0, 2*M_PI);
1057 		cairo_stroke (plot->cr);
1058 		break;
1059 	case 6: /* filled circle */
1060 		cairo_arc (plot->cr, x, y, size, 0, 2*M_PI);
1061 		cairo_fill_preserve(plot->cr);
1062 		cairo_stroke(plot->cr);
1063 		break;
1064 	case 7: /* triangle */
1065 	case 8: /* filled triangle */
1066 		cairo_move_to(plot->cr, x-size, y+size-plot->oversampling_scale);
1067 		cairo_line_to(plot->cr, x,y-size);
1068 		cairo_line_to(plot->cr, x+size,y+size-plot->oversampling_scale);
1069 		cairo_close_path(plot->cr);
1070 		if (style == 8)
1071 			cairo_fill_preserve(plot->cr);
1072 		cairo_stroke(plot->cr);
1073 		break;
1074 	case 9: /* upside down triangle */
1075 	case 10: /* filled upside down triangle */
1076 		cairo_move_to(plot->cr, x-size, y-size+plot->oversampling_scale);
1077 		cairo_line_to(plot->cr, x,y+size);
1078 		cairo_line_to(plot->cr, x+size,y-size+plot->oversampling_scale);
1079 		cairo_close_path(plot->cr);
1080 		if (style == 10)
1081 			cairo_fill_preserve(plot->cr);
1082 		cairo_stroke(plot->cr);
1083 		break;
1084 	case 11: /* diamond */
1085 	case 12: /* filled diamond */
1086 		cairo_move_to(plot->cr, x-size, y);
1087 		cairo_line_to(plot->cr, x,y+size);
1088 		cairo_line_to(plot->cr, x+size,y);
1089 		cairo_line_to(plot->cr, x,y-size);
1090 		cairo_close_path(plot->cr);
1091 		if (style == 12)
1092 			cairo_fill_preserve(plot->cr);
1093 		cairo_stroke(plot->cr);
1094 		break;
1095 	case 13: /* pentagon */
1096 	case 14: /* filled pentagon */
1097 		cairo_move_to(plot->cr, x+size*0.5878, y-size*0.8090);
1098 		cairo_line_to(plot->cr, x-size*0.5878, y-size*0.8090);
1099 		cairo_line_to(plot->cr, x-size*0.9511, y+size*0.3090);
1100 		cairo_line_to(plot->cr, x,             y+size);
1101 		cairo_line_to(plot->cr, x+size*0.9511, y+size*0.3090);
1102 		cairo_close_path(plot->cr);
1103 		if (style == 14)
1104 			cairo_fill_preserve(plot->cr);
1105 		cairo_stroke(plot->cr);
1106 		break;
1107 	default :
1108 		break;
1109 	}
1110 	cairo_restore(plot->cr);
1111 }
1112 
1113 
1114 
gp_cairo_draw_fillbox(plot_struct * plot,int x,int y,int width,int height,int style)1115 void gp_cairo_draw_fillbox(plot_struct *plot, int x, int y, int width, int height, int style)
1116 {
1117 	int fillpar = style >> 4;
1118 	int fillstyle = style & 0xf;
1119 
1120 	/* begin by stroking any open path */
1121 	gp_cairo_stroke(plot);
1122 	/* also draw any open polygon set */
1123 	gp_cairo_end_polygon(plot);
1124 
1125 	FPRINTF((stderr,"fillbox fillpar = %d, fillstyle = %d\n",fillpar, fillstyle));
1126 	gp_cairo_fill( plot, fillstyle, fillpar);
1127 
1128 	cairo_move_to(plot->cr, x, y);
1129 	cairo_rel_line_to(plot->cr, 0, -height);
1130 	cairo_rel_line_to(plot->cr, width, 0);
1131 	cairo_rel_line_to(plot->cr, 0, height);
1132 	cairo_rel_line_to(plot->cr, -width, 0);
1133 	cairo_close_path(plot->cr);
1134 	cairo_fill(plot->cr);
1135 }
1136 
1137 
1138 /*	corner[0] = (x1,y1) is the upper left corner (in terms of plot location) of
1139  *	the outer edge of the image.  Similarly, corner[1] = (x2,y2) is the lower
1140  *	right corner of the outer edge of the image.  (Outer edge means the
1141  *	outer extent of the corner pixels, not the middle of the corner pixels).
1142  *	corner[2] and corner[3] = (x3,y3) and (x4,y4) define a clipping box in
1143  *	the primary plot into which all or part of the image will be rendered.
1144  */
gp_cairo_draw_image(plot_struct * plot,unsigned int * image,int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4,int M,int N)1145 void gp_cairo_draw_image(plot_struct *plot, unsigned int * image, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, int M, int N)
1146 {
1147 	double scale_x, scale_y;
1148 	cairo_surface_t *image_surface;
1149 	cairo_pattern_t *pattern;
1150 	cairo_matrix_t matrix;
1151 
1152 	/* begin by stroking any open path */
1153 	gp_cairo_stroke(plot);
1154 	/* also draw any open polygon set */
1155 	gp_cairo_end_polygon(plot);
1156 
1157 	image_surface = cairo_image_surface_create_for_data((unsigned char*) image,
1158 				CAIRO_FORMAT_ARGB32, M, N, 4*M);
1159 
1160 	scale_x = (double)M/(double)abs( x2 - x1 );
1161 	scale_y = (double)N/(double)abs( y2 - y1 );
1162 
1163 	FPRINTF((stderr,"M %d N %d x1 %d y1 %d\n", M, N, x1, y1));
1164 	cairo_save( plot->cr );
1165 
1166 	/* Set clipping boundaries for image copy.
1167 	 * The bounds were originally possed in corners[2] and corners[3]
1168 	 */
1169 	cairo_move_to(plot->cr, x3, y3);
1170 	cairo_line_to(plot->cr, x3, y4);
1171 	cairo_line_to(plot->cr, x4, y4);
1172 	cairo_line_to(plot->cr, x4, y3);
1173 	cairo_close_path(plot->cr);
1174 	cairo_clip(plot->cr);
1175 
1176 	pattern = cairo_pattern_create_for_surface( image_surface );
1177 	/* scale and keep sharp edges */
1178 	cairo_pattern_set_filter( pattern, CAIRO_FILTER_FAST );
1179 	cairo_matrix_init_scale( &matrix, scale_x, scale_y );
1180 	/* x1 and y1 give the user-space coordinate
1181 	 * at which the surface origin should appear.
1182 	 * (The surface origin is its upper-left corner
1183 	 * before any transformation has been applied.) */
1184 	cairo_matrix_translate( &matrix, -x1, -y1 );
1185 	cairo_pattern_set_matrix( pattern, &matrix );
1186 	cairo_set_source( plot->cr, pattern );
1187 
1188 	cairo_paint( plot->cr );
1189 
1190 	cairo_restore( plot->cr );
1191 
1192 	cairo_pattern_destroy( pattern );
1193 	cairo_surface_destroy( image_surface );
1194 }
1195 
1196 /* =======================================================================
1197  * Enhanced text mode support
1198  * =====================================================================*/
1199 
gp_cairo_add_attr(plot_struct * plot,PangoAttrList * AttrList,int start,int end)1200 void gp_cairo_add_attr(plot_struct *plot, PangoAttrList * AttrList, int start, int end )
1201 {
1202 	PangoAttribute *p_attr_rise, *p_attr_size, *p_attr_family;
1203 	PangoAttribute *p_attr_weight, *p_attr_style;
1204 
1205 	p_attr_size = pango_attr_size_new ((int) (gp_cairo_enhanced_fontsize*PANGO_SCALE));
1206 	p_attr_size->start_index = start;
1207 	p_attr_size->end_index = end;
1208 	pango_attr_list_insert (AttrList, p_attr_size);
1209 
1210 	p_attr_rise = pango_attr_rise_new ((int) (gp_cairo_enhanced_base*PANGO_SCALE));
1211 	p_attr_rise->start_index = start;
1212 	p_attr_rise->end_index = end;
1213 	pango_attr_list_insert (AttrList, p_attr_rise);
1214 
1215 	p_attr_family = pango_attr_family_new (gp_cairo_enhanced_get_fontname(plot));
1216 	p_attr_family->start_index = start;
1217 	p_attr_family->end_index = end;
1218 	pango_attr_list_insert (AttrList, p_attr_family);
1219 
1220 	p_attr_weight = pango_attr_weight_new (plot->fontweight);
1221 	p_attr_weight->start_index = start;
1222 	p_attr_weight->end_index = end;
1223 	pango_attr_list_insert (AttrList, p_attr_weight);
1224 
1225 	p_attr_style = pango_attr_style_new (plot->fontstyle);
1226 	p_attr_style->start_index = start;
1227 	p_attr_style->end_index = end;
1228 	pango_attr_list_insert (AttrList, p_attr_style);
1229 }
1230 
1231 /* add a blank character to the text string with a custom shape */
gp_cairo_add_shape(PangoRectangle rect,int position)1232 void gp_cairo_add_shape( PangoRectangle rect,int position)
1233 {
1234 	PangoAttribute *p_attr_shape;
1235 
1236 	FPRINTF((stderr, "adding blank custom shape\n"));
1237 
1238 	strncat(gp_cairo_utf8, " ", sizeof(gp_cairo_utf8)-strlen(gp_cairo_utf8)-1);
1239 	p_attr_shape = pango_attr_shape_new (&rect,&rect);
1240 	p_attr_shape->start_index = position;
1241 	p_attr_shape->end_index = position+1;
1242 	pango_attr_list_insert (gp_cairo_enhanced_AttrList, p_attr_shape);
1243 }
1244 
1245 /* gp_cairo_enhanced_flush() draws enhanced_text, which has been filled by _writec()*/
gp_cairo_enhanced_flush(plot_struct * plot)1246 void gp_cairo_enhanced_flush(plot_struct *plot)
1247 {
1248 	PangoRectangle save_logical_rect;
1249 	PangoLayout *save_layout;
1250 
1251 	PangoLayout *current_layout;
1252 	PangoRectangle current_ink_rect;
1253 	PangoRectangle current_logical_rect;
1254 	PangoFontDescription *current_desc;
1255 
1256 	PangoRectangle underprinted_logical_rect;
1257 	int overprinted_width = 0;
1258 	PangoLayout *underprinted_layout;
1259 	int start, end;
1260 	PangoLayout *hide_layout;
1261 
1262 	PangoRectangle hide_ink_rect;
1263 	PangoRectangle hide_logical_rect;
1264 	PangoFontDescription *hide_desc;
1265 	PangoLayout *zerowidth_layout;
1266 	PangoFontDescription *zerowidth_desc;
1267 	PangoRectangle zerowidth_logical_rect;
1268 	/* PangoRectangle zerowidth_ink_rect; */
1269 
1270 	int save_start, save_end;
1271 	int underprinted_start, underprinted_end;
1272 
1273 	gchar* enhanced_text_utf8;
1274 
1275 #ifdef MAP_SYMBOL
1276 	TBOOLEAN symbol_font_parsed = FALSE;
1277 #endif /*MAP_SYMBOL*/
1278 
1279 	if (!gp_cairo_enhanced_opened_string)
1280 		return;
1281 
1282 	FPRINTF((stderr, "enhanced flush str=\"%s\" font=%s op=%d sf=%d wf=%d base=%f os=%d wt=%d sl=%d\n",
1283 		gp_cairo_enhanced_string,
1284 		gp_cairo_enhanced_font,
1285 		gp_cairo_enhanced_overprint,
1286 		gp_cairo_enhanced_showflag,
1287 		gp_cairo_enhanced_widthflag,
1288 		gp_cairo_enhanced_base,
1289 		gp_cairo_enhanced_opened_string,
1290 		plot->fontweight,
1291 		plot->fontstyle ));
1292 
1293 	gp_cairo_enhanced_opened_string = FALSE;
1294 
1295 #ifdef MAP_SYMBOL
1296 	/* we have to treat Symbol font as a special case */
1297 	if (!strcmp(gp_cairo_enhanced_font,"Symbol")) {
1298 		FPRINTF((stderr,"Parsing a Symbol string\n"));
1299 
1300 		enhanced_text_utf8 = gp_cairo_convert_symbol_to_unicode(plot, gp_cairo_enhanced_string);
1301 
1302 		if (!strcmp(plot->fontname,"Symbol")) {
1303 			safe_strncpy(gp_cairo_enhanced_font,
1304 				plot->fontname,
1305 				sizeof(gp_cairo_enhanced_font));
1306 		} else {
1307 			safe_strncpy(gp_cairo_enhanced_font,
1308 				gp_cairo_default_font(), sizeof(gp_cairo_enhanced_font));
1309 		}
1310 		symbol_font_parsed = TRUE;
1311 	} else
1312 #endif /*MAP_SYMBOL*/
1313 	{
1314 		/* convert the input string to utf8 */
1315 		enhanced_text_utf8 = gp_cairo_convert(plot, gp_cairo_enhanced_string);
1316 	}
1317 
1318 	start = strlen(gp_cairo_utf8);
1319 
1320 	if (gp_cairo_enhanced_restore_now) {
1321 		/* restore saved position */
1322 		/* the idea is to use a space character, drawn with a negative width */
1323 
1324 		/* we first compute the size of the text drawn since the 'save' command */
1325 
1326 		/* Create a PangoLayout, set the font and text
1327 		 * with the saved attributes list, get extents */
1328 		save_layout = gp_cairo_create_layout (plot->cr);
1329 		pango_layout_set_text (save_layout, gp_cairo_save_utf8, -1);
1330 		pango_layout_set_attributes (save_layout, gp_cairo_enhanced_save_AttrList);
1331 		pango_layout_get_extents(save_layout, NULL, &save_logical_rect);
1332 		g_clear_object (&save_layout);
1333 
1334 		pango_attr_list_unref( gp_cairo_enhanced_save_AttrList );
1335 		gp_cairo_enhanced_save_AttrList = NULL;
1336 
1337 		/* invert the size, so we will go back to the saved state */
1338 		save_logical_rect.width = -save_logical_rect.width;
1339 		/* EAM FIXME:  Zero height necessary but I don't understand why */
1340 		save_logical_rect.height = 0;
1341 		/* adding a blank character with the corresponding shape */
1342 		gp_cairo_add_shape(save_logical_rect,start);
1343 
1344 		safe_strncpy(gp_cairo_save_utf8, "", sizeof(gp_cairo_save_utf8));
1345 		gp_cairo_enhanced_restore_now = FALSE;
1346 		start++;
1347 	}
1348 
1349 	if (gp_cairo_enhanced_overprint==2) {
1350 		/* the idea is first to use a space character, drawn with an appropriate negative width */
1351 
1352 		/* we first compute the size of the text drawn since overprint==1 was used */
1353 
1354 		/* Create a PangoLayout, set the font and text with
1355 		 * the saved attributes list, get extents */
1356 		underprinted_layout = gp_cairo_create_layout (plot->cr);
1357 		pango_layout_set_text (underprinted_layout, gp_cairo_underprinted_utf8, -1);
1358 		if (!gp_cairo_enhanced_underprinted_AttrList)
1359 			fprintf(stderr,"uninitialized gp_cairo_enhanced_underprinted_AttrList!\n");
1360 		else
1361 			pango_layout_set_attributes (underprinted_layout, gp_cairo_enhanced_underprinted_AttrList);
1362 		pango_layout_get_extents(underprinted_layout, NULL, &underprinted_logical_rect);
1363 		g_clear_object (&underprinted_layout);
1364 
1365 		/* compute the size of the text to overprint*/
1366 
1367 		/* Create a PangoLayout, set the font and text */
1368 		current_layout = gp_cairo_create_layout (plot->cr);
1369 		pango_layout_set_text (current_layout, enhanced_text_utf8, -1);
1370 		current_desc = pango_font_description_new ();
1371 		pango_font_description_set_family (current_desc, gp_cairo_enhanced_get_fontname(plot));
1372 		pango_font_description_set_size(current_desc,(int) gp_cairo_enhanced_fontsize*PANGO_SCALE);
1373 		pango_font_description_set_weight (current_desc, plot->fontweight);
1374 		pango_font_description_set_style (current_desc,
1375 			plot->fontstyle ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
1376 
1377 		pango_layout_set_font_description (current_layout, current_desc);
1378 		pango_font_description_free (current_desc);
1379 		pango_layout_get_extents(current_layout, &current_ink_rect, &current_logical_rect);
1380 		g_clear_object (&current_layout);
1381 
1382 		/* calculate the distance to remove to center the overprinted text */
1383 		underprinted_logical_rect.width = -(underprinted_logical_rect.width + current_logical_rect.width)/2;
1384 		overprinted_width = current_logical_rect.width;
1385 
1386 		/* adding a blank character with the corresponding shape */
1387 		gp_cairo_add_shape(underprinted_logical_rect, start);
1388 
1389 		safe_strncpy(gp_cairo_underprinted_utf8, "", sizeof(gp_cairo_underprinted_utf8));
1390 		/* increment the position as we added a character */
1391 		start++;
1392 	}
1393 
1394 	if (gp_cairo_enhanced_showflag) {
1395 		strncat(gp_cairo_utf8, enhanced_text_utf8, sizeof(gp_cairo_utf8)-strlen(gp_cairo_utf8)-1);
1396 		end = strlen(gp_cairo_utf8);
1397 
1398 		/* add text attributes to the main list */
1399 		gp_cairo_add_attr(plot, gp_cairo_enhanced_AttrList, start, end);
1400 
1401 	} else {
1402 		/* position must be modified, but text not actually drawn */
1403 		/* the idea is to use a blank character, drawn with the width of the text*/
1404 
1405 		current_layout = gp_cairo_create_layout (plot->cr);
1406 		pango_layout_set_text (current_layout, gp_cairo_utf8, -1);
1407 		pango_layout_set_attributes (current_layout, gp_cairo_enhanced_AttrList);
1408 		pango_layout_get_extents(current_layout, &current_ink_rect, &current_logical_rect);
1409 		g_clear_object (&current_layout);
1410 
1411 		/* we first compute the size of the text */
1412 		/* Create a PangoLayout, set the font and text */
1413 		hide_layout = gp_cairo_create_layout (plot->cr);
1414 		pango_layout_set_text (hide_layout, enhanced_text_utf8, -1);
1415 		hide_desc = pango_font_description_new ();
1416 		pango_font_description_set_family (hide_desc, gp_cairo_enhanced_get_fontname(plot));
1417 		pango_font_description_set_size(hide_desc,(int) gp_cairo_enhanced_fontsize*PANGO_SCALE);
1418 		pango_font_description_set_weight (hide_desc, plot->fontweight);
1419 		pango_font_description_set_style (hide_desc,
1420 			plot->fontstyle ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
1421 		pango_layout_set_font_description (hide_layout, hide_desc);
1422 		pango_font_description_free (hide_desc);
1423 
1424 		pango_layout_get_extents(hide_layout, &hide_ink_rect, &hide_logical_rect);
1425 		g_clear_object (&hide_layout);
1426 
1427 		/* rect.y must be reworked to take previous text into account, which may be smaller */
1428 		/* hide_logical_rect.y is always initialized at zero, but should be : */
1429 		if (current_logical_rect.height<hide_logical_rect.height)
1430 			hide_logical_rect.y = current_logical_rect.height - hide_logical_rect.height;
1431 
1432 		/* adding a blank character with the corresponding shape */
1433 		gp_cairo_add_shape(hide_logical_rect, start);
1434 
1435 		end = start+1; /* end *must* be defined, as it is used if widthflag is false */
1436 	}
1437 
1438 	if (!gp_cairo_enhanced_widthflag) {
1439 		/* the idea is to use a blank character, drawn with the inverted width of the text*/
1440 		/* we first compute the size of the text */
1441 
1442 		/* Create a PangoLayout, set the font and text */
1443 		zerowidth_layout = gp_cairo_create_layout (plot->cr);
1444 		pango_layout_set_text (zerowidth_layout, enhanced_text_utf8, -1);
1445 		zerowidth_desc = pango_font_description_new ();
1446 		pango_font_description_set_family (zerowidth_desc, gp_cairo_enhanced_get_fontname(plot));
1447 		pango_font_description_set_size(zerowidth_desc,(int) gp_cairo_enhanced_fontsize*PANGO_SCALE);
1448 		pango_font_description_set_weight (zerowidth_desc, plot->fontweight);
1449 		pango_font_description_set_style (zerowidth_desc,
1450 			plot->fontstyle ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
1451 		pango_layout_set_font_description (zerowidth_layout, zerowidth_desc);
1452 		pango_font_description_free (zerowidth_desc);
1453 		pango_layout_get_extents(zerowidth_layout, NULL, &zerowidth_logical_rect);
1454 		g_clear_object (&zerowidth_layout);
1455 
1456 		/* invert the size, so we will go back to the start of the string */
1457 		zerowidth_logical_rect.width = -zerowidth_logical_rect.width;
1458 
1459 		/* adding a blank character with the corresponding shape */
1460 		gp_cairo_add_shape(zerowidth_logical_rect,end);
1461 		end++;
1462 	}
1463 
1464 	if (gp_cairo_enhanced_overprint==2) {
1465 		/* revert the previous negative space to go back to starting point.
1466 		*  Take centered overprinted text width into account  */
1467 		underprinted_logical_rect.width = -underprinted_logical_rect.width - overprinted_width/2;
1468 		/* adding a blank character with the corresponding shape */
1469 		gp_cairo_add_shape(underprinted_logical_rect,end);
1470 	}
1471 
1472 	if (gp_cairo_enhanced_save) /* we aim at restoring position later */ {
1473 		save_start = strlen( gp_cairo_save_utf8);
1474 		strncat(gp_cairo_save_utf8, enhanced_text_utf8, sizeof(gp_cairo_utf8)-strlen(gp_cairo_utf8)-1);
1475 		save_end = strlen( gp_cairo_save_utf8);
1476 
1477 		/* add text attributes to the save list */
1478 		gp_cairo_add_attr(plot, gp_cairo_enhanced_save_AttrList, save_start, save_end);
1479 	}
1480 
1481 	if (gp_cairo_enhanced_overprint==1) /* save underprinted text with its attributes */{
1482 		underprinted_start = strlen(gp_cairo_underprinted_utf8);
1483 		strncat(gp_cairo_underprinted_utf8,
1484 			enhanced_text_utf8,
1485 			sizeof(gp_cairo_underprinted_utf8)-strlen(gp_cairo_underprinted_utf8)-1);
1486 		underprinted_end = strlen(gp_cairo_underprinted_utf8);
1487 
1488 		if (gp_cairo_enhanced_underprinted_AttrList)
1489 			pango_attr_list_unref( gp_cairo_enhanced_underprinted_AttrList );
1490 		gp_cairo_enhanced_underprinted_AttrList = pango_attr_list_new();
1491 
1492 		/* add text attributes to the underprinted list */
1493 		gp_cairo_add_attr(plot, gp_cairo_enhanced_underprinted_AttrList,
1494 			underprinted_start, underprinted_end);
1495 	}
1496 
1497 #ifdef MAP_SYMBOL
1498 	if (symbol_font_parsed)
1499 		safe_strncpy(gp_cairo_enhanced_font, "Symbol", sizeof(gp_cairo_enhanced_font));
1500 #endif /* MAP_SYMBOL */
1501 
1502 	g_free(enhanced_text_utf8);
1503 }
1504 
1505 /* brace is TRUE to keep processing to },
1506  *         FALSE to do one character only
1507  * fontname & fontsize are obvious
1508  * base is the current baseline
1509  * widthflag is TRUE if the width of this should count,
1510  *              FALSE for zero width boxes
1511  * showflag is TRUE if this should be shown,
1512  *             FALSE if it should not be shown (like TeX \phantom)
1513  * overprint is 0 for normal operation,
1514  *              1 for the underprinted text (included in width calculation),
1515  *              2 for the overprinted text (not included in width calc, through widhtflag=false),
1516  *              (overprinted text is centered horizontally on underprinted text)
1517  *              3 means "save current position",
1518  *              4 means "restore saved position" */
gp_cairo_enhanced_open(plot_struct * plot,char * fontname,double fontsize,double base,TBOOLEAN widthflag,TBOOLEAN showflag,int overprint)1519 void gp_cairo_enhanced_open(plot_struct *plot, char* fontname, double fontsize, double base, TBOOLEAN widthflag, TBOOLEAN showflag, int overprint)
1520 {
1521 	if (overprint == 3) {
1522 		gp_cairo_enhanced_save = TRUE;
1523 		gp_cairo_enhanced_restore_now = FALSE;
1524 		gp_cairo_enhanced_save_AttrList = pango_attr_list_new();
1525 		return;
1526 	}
1527 
1528 	if (overprint == 4) {
1529 		gp_cairo_enhanced_save = FALSE;
1530 		gp_cairo_enhanced_restore_now = TRUE;
1531 		return;
1532 	}
1533 
1534 	if (!gp_cairo_enhanced_opened_string) {
1535 		/* Strip off Bold or Italic and apply immediately
1536 		 * Is it really necessary to preserve plot->fontname?
1537 		 */
1538 		char *save_plot_font = strdup(plot->fontname);
1539 		gp_cairo_set_font(plot, fontname, plot->fontsize);
1540 		safe_strncpy(gp_cairo_enhanced_font, plot->fontname, sizeof(gp_cairo_enhanced_font));
1541 		strcpy(plot->fontname, save_plot_font);
1542 		free(save_plot_font);
1543 
1544 		gp_cairo_enhanced_opened_string = TRUE;
1545 		gp_cairo_enhanced_char = gp_cairo_enhanced_string;
1546 		gp_cairo_enhanced_fontsize = fontsize*plot->oversampling_scale;
1547 		gp_cairo_enhanced_base = base*plot->oversampling_scale;
1548 		gp_cairo_enhanced_showflag = showflag;
1549 		gp_cairo_enhanced_overprint = overprint;
1550 		gp_cairo_enhanced_widthflag = widthflag;
1551 	}
1552 }
1553 
gp_cairo_enhanced_writec(plot_struct * plot,int character)1554 void gp_cairo_enhanced_writec(plot_struct *plot, int character)
1555 {
1556 	*gp_cairo_enhanced_char++ = character;
1557 	*gp_cairo_enhanced_char = '\0';
1558 }
1559 
gp_cairo_enhanced_init(plot_struct * plot,int len)1560 void gp_cairo_enhanced_init(plot_struct *plot, int len)
1561 {
1562 	/* begin by stroking any open path */
1563 	gp_cairo_stroke(plot);
1564 	/* also draw any open polygon set */
1565 	gp_cairo_end_polygon(plot);
1566 
1567 
1568 	gp_cairo_enhanced_string = (char*) malloc(len+1);
1569 	gp_cairo_enhanced_opened_string = FALSE;
1570 	gp_cairo_enhanced_overprint = FALSE;
1571 	gp_cairo_enhanced_showflag = TRUE;
1572 	gp_cairo_enhanced_fontsize = plot->fontsize*plot->oversampling_scale;
1573 	safe_strncpy(gp_cairo_enhanced_font, plot->fontname, sizeof(gp_cairo_enhanced_font));
1574 	gp_cairo_enhanced_AttrList = pango_attr_list_new();
1575 }
1576 
gp_cairo_enhanced_finish(plot_struct * plot,int x,int y)1577 void gp_cairo_enhanced_finish(plot_struct *plot, int x, int y)
1578 {
1579 	PangoRectangle ink_rect, logical_rect;
1580 	PangoLayout *layout;
1581 	double vert_just, arg, enh_x, enh_y, delta, deltax, deltay;
1582 	double box_x, box_y;
1583 	int baseline_offset;
1584 
1585 	/* Create a PangoLayout, set the font and text */
1586 	layout = gp_cairo_create_layout (plot->cr);
1587 
1588 	pango_layout_set_text (layout, gp_cairo_utf8, -1);
1589 
1590 	pango_layout_set_attributes (layout, gp_cairo_enhanced_AttrList);
1591 
1592 	pango_layout_get_extents(layout, &ink_rect, &logical_rect);
1593 
1594 	/* NB: See explanatory comments in gp_cairo_draw_text() */
1595 	baseline_offset = pango_layout_get_baseline(layout) / PANGO_SCALE;
1596 	vert_just = 0.5 * (float)(plot->fontsize * plot->oversampling_scale);
1597 	vert_just = baseline_offset - vert_just;
1598 
1599 	arg = plot->text_angle * M_PI/180;
1600 	enh_x = x - vert_just * sin(arg);
1601 	enh_y = y - vert_just * cos(arg);
1602 
1603 	delta = ((double)logical_rect.width/2) / PANGO_SCALE;
1604 
1605 	deltax = delta * cos(arg);
1606 	deltay = delta * sin(arg);
1607 
1608 	switch (plot->justify_mode) {
1609 	case LEFT :
1610 		break;
1611 	case CENTRE :
1612 		enh_x -= deltax;
1613 		enh_y += deltay;
1614 		break;
1615 	case RIGHT :
1616 		enh_x -= 2*deltax;
1617 		enh_y += 2*deltay;
1618 		break;
1619 	}
1620 
1621 	cairo_save(plot->cr);
1622 	cairo_translate(plot->cr, enh_x, enh_y);
1623 	cairo_rotate(plot->cr, -arg);
1624 
1625 	cairo_set_source_rgba(plot->cr, plot->color.r, plot->color.g, plot->color.b,
1626 				1. - plot->color.alpha);
1627 	/* Inform Pango to re-layout the text with the new transformation */
1628 	pango_cairo_update_layout (plot->cr, layout);
1629 	pango_cairo_show_layout (plot->cr, layout);
1630 	/* pango_cairo_show_layout does not clear the path (here a starting point)
1631 	 * Do it by ourselves, or we can get spurious lines on future calls. */
1632 	cairo_new_path(plot->cr);
1633 
1634 	if (in_textbox) {
1635 		box_rotation = -arg;
1636 		box_origin_x = x;
1637 		box_origin_y = y;
1638 		box_y = box_origin_y - vert_just;
1639 		switch (plot->justify_mode) {
1640 		case LEFT:
1641 			box_x = box_origin_x;
1642 			break;
1643 		default:
1644 		case CENTRE:
1645 			box_x = box_origin_x - delta;
1646 			break;
1647 		case RIGHT:
1648 			box_x = box_origin_x - 2*delta;
1649 			break;
1650 		}
1651 
1652 		/* Update bounding box for boxed label text */
1653 		pango_layout_get_pixel_extents (layout, &ink_rect, &logical_rect);
1654 
1655 		/* Auto-initialization */
1656 		if (bounding_box[0] < 0 && bounding_box[1] < 0) {
1657 		    bounding_box[0] = bounding_box[2] = box_x;
1658 		    bounding_box[1] = bounding_box[3] = box_y;
1659 		}
1660 		if (bounding_box[0] > box_x + ink_rect.x)
1661 		    bounding_box[0] = box_x + ink_rect.x;
1662 		if (bounding_box[2] < box_x + ink_rect.x + ink_rect.width)
1663 		    bounding_box[2] = box_x + ink_rect.x + ink_rect.width;
1664 		if (bounding_box[1] > box_y + ink_rect.y)
1665 		    bounding_box[1] = box_y + ink_rect.y;
1666 		if (bounding_box[3] < box_y + ink_rect.y + ink_rect.height)
1667 		    bounding_box[3] = box_y + ink_rect.y + ink_rect.height;
1668 	}
1669 
1670 	/* free the layout object */
1671 	pango_attr_list_unref( gp_cairo_enhanced_AttrList );
1672 	gp_cairo_enhanced_AttrList = NULL;
1673 	g_clear_object (&layout);
1674 	cairo_restore(plot->cr);
1675 	safe_strncpy(gp_cairo_utf8, "", sizeof(gp_cairo_utf8));
1676 	free(gp_cairo_enhanced_string);
1677 }
1678 
1679 /* obtain the right pattern or solid fill from fillstyle and fillpar.
1680  * Used to draw fillboxes and polygons */
gp_cairo_fill(plot_struct * plot,int fillstyle,int fillpar)1681 void gp_cairo_fill(plot_struct *plot, int fillstyle, int fillpar)
1682 {
1683 	double red = 0, green = 0, blue = 0, fact = 0;
1684 
1685 	switch (fillstyle) {
1686 	case FS_SOLID: /* solid fill */
1687 		if (plot->color.alpha > 0) {
1688 			fillpar = 100. * (1. - plot->color.alpha);
1689 			/* Fall through to FS_TRANSPARENT_SOLID */
1690 		} else if (fillpar==100)
1691 			/* treated as a special case to accelerate common situation */ {
1692 			cairo_set_source_rgb(plot->cr, plot->color.r, plot->color.g, plot->color.b);
1693 			FPRINTF((stderr,"solid %lf %lf %lf\n",plot->color.r, plot->color.g, plot->color.b));
1694 			return;
1695 		} else {
1696 			red   = plot->color.r;
1697 			green = plot->color.g;
1698 			blue  = plot->color.b;
1699 
1700 			fact = (double)(100 - fillpar) /100;
1701 
1702 			if (fact >= 0 && fact <= 1) {
1703 				red   += (1 - red) * fact;
1704 				green += (1 - green) * fact;
1705 				blue  += (1 - blue) * fact;
1706 			}
1707 			cairo_set_source_rgb(plot->cr, red, green, blue);
1708 			FPRINTF((stderr,"transparent solid %lf %lf %lf\n",red, green, blue));
1709 			return;
1710 		}
1711 	case FS_TRANSPARENT_SOLID:
1712 		red   = plot->color.r;
1713 		green = plot->color.g;
1714 		blue  = plot->color.b;
1715 		cairo_set_source_rgba(plot->cr, red, green, blue, (double)fillpar/100.);
1716 		return;
1717 	case FS_PATTERN: /* pattern fill */
1718 	case FS_TRANSPARENT_PATTERN:
1719 		gp_cairo_fill_pattern(plot, fillstyle, fillpar);
1720 		FPRINTF((stderr,"pattern fillpar = %d %lf %lf %lf\n",fillpar, plot->color.r, plot->color.g, plot->color.b));
1721 		return;
1722 	case FS_EMPTY: /* fill with background plot->color */
1723 		cairo_set_source_rgb(plot->cr, plot->background.r, plot->background.g, plot->background.b);
1724 		FPRINTF((stderr,"empty\n"));
1725 		return;
1726 	default:
1727 		cairo_set_source_rgb(plot->cr, plot->color.r, plot->color.g, plot->color.b);
1728 		FPRINTF((stderr,"default %lf %lf %lf\n",plot->color.r, plot->color.g, plot->color.b));
1729 		return;
1730 	}
1731 }
1732 
gp_cairo_boxed_text(plot_struct * plot,int x,int y,int option)1733 void gp_cairo_boxed_text(plot_struct *plot, int x, int y, int option)
1734 {
1735 	int dx, dy;
1736 
1737 	switch (option) {
1738 	case TEXTBOX_INIT:
1739 		/* Initialize bounding box for this text string */
1740 		bounding_box[0] = bounding_box[2] = x;
1741 		bounding_box[1] = bounding_box[3] = y;
1742 		in_textbox = TRUE;
1743 		break;
1744 
1745 	case TEXTBOX_OUTLINE:
1746 		/* Stroke the outline of the bounding box for previous text */
1747 	case TEXTBOX_BACKGROUNDFILL:
1748 	case TEXTBOX_GREY:
1749 		/* Fill the bounding box with background color */
1750 		/* begin by stroking any open path */
1751 		gp_cairo_stroke(plot);
1752 		gp_cairo_end_polygon(plot);
1753 
1754 		cairo_save(plot->cr);
1755 
1756 		/* In progress: textbox rotation
1757 		 * 1) translate to text origin
1758 		 * 2) subtract translation from bounding box
1759 		 * 3) rotate about new origin
1760 		 * 4) stroke/fill
1761 		 */
1762 		cairo_translate(plot->cr, box_origin_x, box_origin_y);
1763 		cairo_rotate(plot->cr, box_rotation);
1764 		cairo_translate(plot->cr, -box_origin_x, -box_origin_y);
1765 
1766 		dx = 0.25 * bounding_xmargin * (float)(plot->fontsize * plot->oversampling_scale);
1767 		dy = 0.25 * bounding_ymargin * (float)(plot->fontsize * plot->oversampling_scale);
1768 		if (option == TEXTBOX_GREY)
1769 		    dy = 0;
1770 		gp_cairo_move(plot,   bounding_box[0]-dx, bounding_box[1]-dy);
1771 		gp_cairo_vector(plot, bounding_box[0]-dx, bounding_box[3]+dy);
1772 		gp_cairo_vector(plot, bounding_box[2]+dx, bounding_box[3]+dy);
1773 		gp_cairo_vector(plot, bounding_box[2]+dx, bounding_box[1]-dy);
1774 		gp_cairo_vector(plot, bounding_box[0]+dx, bounding_box[1]-dy);
1775 		cairo_close_path(plot->cr);
1776 		if (option == TEXTBOX_BACKGROUNDFILL) {
1777 		    cairo_set_source_rgba(plot->cr, plot->color.r, plot->color.g,
1778 					  plot->color.b, 1. - plot->color.alpha);
1779 		    cairo_fill(plot->cr);
1780 		} else if (option == TEXTBOX_GREY) {
1781 		    cairo_set_source_rgba(plot->cr, 0.75, 0.75, 0.75, 0.50);
1782 		    cairo_fill(plot->cr);
1783 		} else {  /* option == TEXTBOX_OUTLINE */
1784 		    cairo_set_line_width(plot->cr,
1785 					 0.5*plot->linewidth*plot->oversampling_scale);
1786 		    cairo_set_source_rgba(plot->cr, plot->color.r, plot->color.g,
1787 					  plot->color.b, 1. - plot->color.alpha);
1788 		    cairo_stroke(plot->cr);
1789 		}
1790 
1791 		cairo_restore(plot->cr);
1792 		in_textbox = FALSE;
1793 		break;
1794 
1795 	case TEXTBOX_MARGINS: /* Change the margin between text and box */
1796 		bounding_xmargin = (double)x/100.;
1797 		bounding_ymargin = (double)y/100.;
1798 		break;
1799 
1800 	default:
1801 		break;
1802 	}
1803 }
1804 
1805 #define PATTERN_SIZE 8
1806 
1807 /* return a pattern used for fillboxes and polygons */
gp_cairo_fill_pattern(plot_struct * plot,int fillstyle,int fillpar)1808 void gp_cairo_fill_pattern(plot_struct *plot, int fillstyle, int fillpar)
1809 {
1810 	cairo_surface_t *pattern_surface;
1811 	cairo_t *pattern_cr;
1812 	cairo_pattern_t *pattern;
1813 	cairo_matrix_t context_matrix;
1814 	cairo_matrix_t matrix;
1815 
1816 	pattern_surface = cairo_surface_create_similar(cairo_get_target(plot->cr),
1817                                              CAIRO_CONTENT_COLOR_ALPHA,
1818                                              PATTERN_SIZE,
1819                                              PATTERN_SIZE);
1820 	pattern_cr = cairo_create(pattern_surface);
1821 
1822 	cairo_matrix_init_scale(&context_matrix,
1823 		PATTERN_SIZE,
1824 		PATTERN_SIZE);
1825 	cairo_set_matrix(pattern_cr,&context_matrix);
1826 
1827 	if (fillstyle == FS_TRANSPARENT_PATTERN)
1828 	    cairo_set_source_rgba( pattern_cr, 1.0, 1.0, 1.0, 0.0);
1829 	else
1830 	    cairo_set_source_rgb( pattern_cr, 1.0, 1.0, 1.0);
1831 
1832 	cairo_paint(pattern_cr);
1833 
1834 	if (!strcmp(term->name,"pdfcairo")) /* Work-around for poor scaling in cairo */
1835 	    cairo_set_line_width(pattern_cr, PATTERN_SIZE/150.);
1836 	else
1837 	    cairo_set_line_width(pattern_cr, PATTERN_SIZE/50.);
1838 	cairo_set_line_cap  (pattern_cr, CAIRO_LINE_CAP_BUTT);
1839 	cairo_set_source_rgb(pattern_cr, plot->color.r, plot->color.g, plot->color.b);
1840 
1841 	switch (fillpar%8) {
1842 	case 0: /* no fill */
1843 	default:
1844 		break;
1845 	case 1: /* cross-hatch */
1846 	case 2: /* double cross-hatch */
1847 		cairo_move_to(pattern_cr, 0,0);
1848 		cairo_line_to(pattern_cr, 1.0,1.0);
1849 		cairo_stroke(pattern_cr);
1850 		cairo_move_to(pattern_cr, 0,1.0);
1851 		cairo_line_to(pattern_cr, 1.0,0);
1852 		cairo_stroke(pattern_cr);
1853 		break;
1854 	case 3: /* solid */
1855 		cairo_paint(pattern_cr);
1856 		break;
1857 	case 4: /* diagonal hatch */
1858 	case 5:
1859 	case 6:
1860 	case 7:
1861 		cairo_move_to(pattern_cr, 0.5,0.);
1862 		cairo_line_to(pattern_cr, 0.5,1.);
1863 		cairo_stroke(pattern_cr);
1864 		break;
1865 	}
1866 
1867 	pattern = cairo_pattern_create_for_surface( pattern_surface );
1868 	cairo_pattern_set_extend( pattern, CAIRO_EXTEND_REPEAT );
1869 
1870 	/* compensate the transformation matrix of the main context */
1871 	cairo_matrix_init_scale(&matrix,
1872 		1.0/plot->oversampling_scale,
1873 		1.0/plot->oversampling_scale);
1874 
1875 	switch (fillpar%8) {
1876 	case 0: /* no fill */
1877 	case 1: /* cross-hatch */
1878 	case 3: /* solid */
1879 	default:
1880 		break;
1881 	case 2: /* double cross-hatch */
1882 		cairo_matrix_scale( &matrix, 2.,2.);
1883 		break;
1884 	case 4: /* diagonal hatch */
1885 		cairo_matrix_rotate( &matrix, M_PI/4);
1886 		break;
1887 	case 5:
1888 		cairo_matrix_rotate( &matrix, -M_PI/4);
1889 		break;
1890 	case 6:
1891 		cairo_matrix_rotate( &matrix, M_PI/4);
1892 		cairo_matrix_scale( &matrix, 2.,2.);
1893 		break;
1894 	case 7:
1895 		cairo_matrix_rotate( &matrix, -M_PI/4);
1896 		cairo_matrix_scale( &matrix, 2.,2.);
1897 		break;
1898 	}
1899 
1900 	cairo_pattern_set_matrix(pattern,&matrix);
1901 
1902 	cairo_destroy( pattern_cr );
1903 	cairo_set_source( plot->cr, pattern );
1904 	cairo_pattern_destroy( pattern );
1905 	cairo_surface_destroy( pattern_surface );
1906 }
1907 
1908 
1909 /* Sets term vars v_char, h_char, v_tic, h_tic
1910  * Depends on plot->fontsize and fontname */
gp_cairo_set_termvar(plot_struct * plot,unsigned int * v_char,unsigned int * h_char)1911 void gp_cairo_set_termvar(plot_struct *plot, unsigned int *v_char,
1912                                              unsigned int *h_char)
1913 {
1914 	PangoLayout *layout;
1915 	PangoFontDescription *desc;
1916 	PangoRectangle ink_rect;
1917 	PangoRectangle logical_rect;
1918 	unsigned int tmp_v_char, tmp_h_char;
1919 	extern int debug;
1920 
1921 	/* Create a PangoLayout, set the font and text */
1922 	layout = gp_cairo_create_layout (plot->cr);
1923 	pango_layout_set_text (layout, "0123456789", -1);
1924 	desc = pango_font_description_new ();
1925 	pango_font_description_set_family (desc, plot->fontname);
1926 	pango_font_description_set_size(desc,(int) (plot->fontsize*PANGO_SCALE*plot->oversampling_scale));
1927 	pango_font_description_set_weight (desc, plot->fontweight);
1928 	pango_font_description_set_style (desc,
1929 		plot->fontstyle ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
1930 	pango_layout_set_font_description (layout, desc);
1931 	pango_font_description_free (desc);
1932 	pango_layout_get_extents(layout, &ink_rect, &logical_rect);
1933 	g_clear_object (&layout);
1934 
1935 	/* we don't use gnuplot_x() and gnuplot_y() in the following
1936 	 * as the scale should have just been updated to 1.
1937 	 * Although PANGO works with integer, it scales them via a huge number (usually ~1000).
1938 	 * That's why I use ceil() instead of direct division result */
1939 	tmp_v_char = (int) ceil( (double) logical_rect.height/PANGO_SCALE) - 1;
1940 	tmp_h_char = (int) ceil( (double) logical_rect.width/(10*PANGO_SCALE));
1941 
1942 	/* Desperation fallback case.
1943 	 * There have been reports of failure to obtain font metrics.
1944 	 * We don't know why (pango use of harfbuzz is a suspect).
1945 	 */
1946 	if (tmp_v_char <= 1 || tmp_h_char <= 1 || debug == 7) {
1947 		tmp_h_char = 140 + (plot->fontsize - 10.) * 16.;
1948 		tmp_v_char = 300 * (plot->fontsize / 10.);
1949 		fprintf(stderr, "warning: problem determining pango font metrics\n");
1950 	}
1951 
1952 	if (v_char)
1953 		*v_char = tmp_v_char;
1954 	if (h_char)
1955 		*h_char = tmp_h_char;
1956 
1957 /* FIXME!!! So far, so good. But now we have a problem. This routine	*/
1958 /* is called synchronously with the set_font command, but avg_vchar is	*/
1959 /* needed asynchronously during execution of the display list. 		*/
1960 	avg_vchar = tmp_v_char;
1961 }
1962 
gp_cairo_solid_background(plot_struct * plot)1963 void gp_cairo_solid_background(plot_struct *plot)
1964 {
1965 	if (cairo_status (plot->cr)) {
1966 		fprintf(stderr, "Cairo is unhappy: %s\n",
1967 			cairo_status_to_string (cairo_status (plot->cr)));
1968 		gp_exit(EXIT_FAILURE);
1969 	}
1970 	cairo_set_source_rgb(plot->cr, plot->background.r, plot->background.g, plot->background.b);
1971 	cairo_paint(plot->cr);
1972 }
1973 
gp_cairo_clear_background(plot_struct * plot)1974 void gp_cairo_clear_background(plot_struct *plot)
1975 {
1976 	if (cairo_status (plot->cr)) {
1977 		fprintf(stderr, "Cairo is unhappy: %s\n",
1978 			cairo_status_to_string (cairo_status (plot->cr)));
1979 		gp_exit(EXIT_FAILURE);
1980 	}
1981 	cairo_set_source_rgba(plot->cr, 0.0, 0.0, 0.0, 0.0);
1982 	cairo_paint(plot->cr);
1983 }
1984 
1985 /*----------------------------------------------------------------------------
1986    font functions
1987 ----------------------------------------------------------------------------*/
1988 
1989 
1990 /* in enhanced text mode, look if enhanced mode has set the font,
1991  * otherwise return the default */
gp_cairo_enhanced_get_fontname(plot_struct * plot)1992 const char* gp_cairo_enhanced_get_fontname(plot_struct *plot)
1993 {
1994 	if ( strlen(gp_cairo_enhanced_font)==0 )
1995 		return plot->fontname;
1996 	else
1997 		return gp_cairo_enhanced_font;
1998 }
1999 
2000 /* BM: New function to determine the default font.
2001  * On Windows, the "Sans" alias normally is equivalent to
2002  * "Tahoma" but the resolution fails on some systems. */
2003 const char *
gp_cairo_default_font(void)2004 gp_cairo_default_font(void)
2005 {
2006 #ifdef _WIN32
2007 	return "Tahoma";
2008 #else
2009 	return "Sans";
2010 #endif
2011 }
2012 
2013 /*----------------------------------------------------------------------------
2014    coordinates functions
2015 ----------------------------------------------------------------------------*/
2016 
2017 #define OFFSET 0
2018 
device_x(plot_struct * plot,double x)2019 double device_x(plot_struct *plot, double x)
2020 {
2021 	double scaled_x;
2022 	scaled_x = plot->xscale*x/plot->oversampling_scale ;
2023 	return scaled_x + OFFSET;
2024 }
2025 
device_y(plot_struct * plot,double y)2026 double device_y(plot_struct *plot, double y)
2027 {
2028 	double scaled_and_mirrored_y;
2029 	scaled_and_mirrored_y = (plot->ymax - y)*plot->yscale/plot->oversampling_scale;
2030 	return scaled_and_mirrored_y + OFFSET;
2031 }
2032 
gnuplot_x(plot_struct * plot,double x)2033 double gnuplot_x(plot_struct *plot, double x)
2034 {
2035 	double scaled_x;
2036 	scaled_x = (x + OFFSET)/plot->xscale*plot->oversampling_scale ;
2037 	return scaled_x;
2038 }
2039 
gnuplot_y(plot_struct * plot,double y)2040 double gnuplot_y(plot_struct *plot, double y)
2041 {
2042 	double scaled_and_mirrored_y;
2043 	scaled_and_mirrored_y = plot->ymax +(-y + OFFSET)/plot->yscale*plot->oversampling_scale;
2044 	return scaled_and_mirrored_y;
2045 }
2046 
2047 
2048 /* return the charset as a string accepted by glib routines,
2049  * default to the locale charset,
2050  * the returned char* doesn't have to be freed. */
gp_cairo_get_encoding(plot_struct * plot)2051 const char* gp_cairo_get_encoding(plot_struct *plot)
2052 {
2053 	const char * charset;
2054 
2055 	switch (plot->encoding) {
2056 	case S_ENC_ISO8859_2 : return "ISO-8859-2";
2057 	case S_ENC_ISO8859_15 : return "ISO-8859-15";
2058 	case S_ENC_CP437 : return "cp437";
2059 	case S_ENC_CP850 :  return "cp850";
2060 	case S_ENC_CP852 :  return "cp852";
2061 	case S_ENC_CP1250 : return "windows-1250";
2062 	case S_ENC_CP1252 : return "windows-1252";
2063 	case S_ENC_KOI8_R : return "KOI8-R";
2064 	case S_ENC_KOI8_U :  return "KOI8-U";
2065 	case S_ENC_ISO8859_1 : return "ISO-8859-1";
2066 	case S_ENC_UTF8 : return "UTF-8";
2067 	case S_ENC_DEFAULT :
2068 	case S_ENC_INVALID :
2069 	default :
2070  		g_get_charset(&charset);
2071 		return charset;
2072 	}
2073 }
2074 
2075 /* Symbol font handling.
2076  * To ensure compatibility with other terminals,
2077  * use the map provided by http://www.unicode.org/ to
2078  * translate character codes to their unicode counterparts.
2079  * The returned string has te be freed by the calling function. */
gp_cairo_convert_symbol_to_unicode(plot_struct * plot,const char * string)2080 gchar* gp_cairo_convert_symbol_to_unicode(plot_struct *plot, const char* string)
2081 {
2082 	gchar *string_utf8;
2083 	gchar *output;
2084 	gchar *iter;
2085 	gchar *iter_mod;
2086 	int i;
2087 	int imax;
2088 	GError *error = NULL;
2089 
2090 	/* first step, get a valid utf8 string, without taking care of Symbol.
2091 	 * The input string is likely to be encoded in iso_8859_1, with characters
2092 	 * going from 1 to 255. Try this first. If it's not the case, fall back to
2093 	 * routine based on the encoding variable. */
2094 	string_utf8 = g_convert(string, -1, "UTF-8", "ISO-8859-1", NULL, NULL, &error);
2095 	if (error != NULL) {
2096 		fprintf(stderr,"Symbol font : fallback to iso_8859_1 did not work\n");
2097 		g_error_free(error);
2098 		string_utf8 = gp_cairo_convert(plot, string);
2099 	}
2100 
2101 	iter = string_utf8;
2102 	/* Assume that the output string in utf8 won't use more than 8 bytes per character/
2103 	 * The utf8 specification fixes the limit to 4 bytes per character, but here we can also
2104 	 * composite two characters */
2105 	output = (gchar*) gp_alloc((4*strlen(string)+1)*sizeof(gchar),"Symbol to unicode");
2106 	iter_mod = output;
2107 	imax = g_utf8_strlen(string_utf8,-1) + 1;
2108 
2109 	for (i=0; i<imax; ++i) {
2110 		switch(g_utf8_get_char(iter)) {
2111 #define SYMB_UNICODE(symbol_char,unicode) case symbol_char : g_unichar_to_utf8(unicode, iter_mod); break;
2112 		/* not modifying ASCII characters */
2113 		/* SYMB_UNICODE(0x20,0x0020); */ /* SPACE */
2114 		/* SYMB_UNICODE(0x21,0x0021); */ /* EXCLAMATION MARK */
2115 		SYMB_UNICODE(0x22,0x2200); /* FOR ALL */
2116 		/* SYMB_UNICODE(0x23,0x0023); */ /* NUMBER SIGN */
2117 		SYMB_UNICODE(0x24,0x2203); /* THERE EXISTS */
2118 		/* SYMB_UNICODE(0x25,0x0025); */ /* PERCENT SIGN */
2119 		/* SYMB_UNICODE(0x26,0x0026); */ /* AMPERSAND */
2120 		SYMB_UNICODE(0x27,0x220D); /* SMALL CONTAINS AS MEMBER */
2121 		/* SYMB_UNICODE(0x28,0x0028); */ /* LEFT PARENTHESIS */
2122 		/* SYMB_UNICODE(0x29,0x0029); */ /* RIGHT PARENTHESIS */
2123 		/* SYMB_UNICODE(0x2A,0x2217); */ /* ASTERISK OPERATOR */
2124 		/* SYMB_UNICODE(0x2B,0x002B); */ /* PLUS SIGN */
2125 		/* SYMB_UNICODE(0x2C,0x002C); */ /* COMMA */
2126 		/* SYMB_UNICODE(0x2D,0x2212); */ /* MINUS SIGN */
2127 		/* SYMB_UNICODE(0x2E,0x002E); */ /* FULL STOP */
2128 		/* SYMB_UNICODE(0x2F,0x002F); */ /* SOLIDUS */
2129 		/* SYMB_UNICODE(0x30,0x0030); */ /* DIGIT ZERO */
2130 		/* SYMB_UNICODE(0x31,0x0031); */ /* DIGIT ONE */
2131 		/* SYMB_UNICODE(0x32,0x0032); */ /* DIGIT TWO */
2132 		/* SYMB_UNICODE(0x33,0x0033); */ /* DIGIT THREE */
2133 		/* SYMB_UNICODE(0x34,0x0034); */ /* DIGIT FOUR */
2134 		/* SYMB_UNICODE(0x35,0x0035); */ /* DIGIT FIVE */
2135 		/* SYMB_UNICODE(0x36,0x0036); */ /* DIGIT SIX */
2136 		/* SYMB_UNICODE(0x37,0x0037); */ /* DIGIT SEVEN */
2137 		/* SYMB_UNICODE(0x38,0x0038); */ /* DIGIT EIGHT */
2138 		/* SYMB_UNICODE(0x39,0x0039); */ /* DIGIT NINE */
2139 		/* SYMB_UNICODE(0x3A,0x003A); */ /* COLON */
2140 		/* SYMB_UNICODE(0x3B,0x003B); */ /* SEMICOLON */
2141 		/* SYMB_UNICODE(0x3C,0x003C); */ /* LESS-THAN SIGN */
2142 		/* SYMB_UNICODE(0x3D,0x003D); */ /* EQUALS SIGN */
2143 		/* SYMB_UNICODE(0x3E,0x003E); */ /* GREATER-THAN SIGN */
2144 		/* SYMB_UNICODE(0x3F,0x003F); */ /* QUESTION MARK */
2145 		SYMB_UNICODE(0x40,0x2245); /* APPROXIMATELY EQUAL TO */
2146 		SYMB_UNICODE(0x41,0x0391); /* GREEK CAPITAL LETTER ALPHA */
2147 		SYMB_UNICODE(0x42,0x0392); /* GREEK CAPITAL LETTER BETA */
2148 		SYMB_UNICODE(0x43,0x03A7); /* GREEK CAPITAL LETTER CHI */
2149 		SYMB_UNICODE(0x44,0x0394); /* GREEK CAPITAL LETTER DELTA */
2150 		SYMB_UNICODE(0x45,0x0395); /* GREEK CAPITAL LETTER EPSILON */
2151 		SYMB_UNICODE(0x46,0x03A6); /* GREEK CAPITAL LETTER PHI */
2152 		SYMB_UNICODE(0x47,0x0393); /* GREEK CAPITAL LETTER GAMMA */
2153 		SYMB_UNICODE(0x48,0x0397); /* GREEK CAPITAL LETTER ETA */
2154 		SYMB_UNICODE(0x49,0x0399); /* GREEK CAPITAL LETTER IOTA */
2155 		SYMB_UNICODE(0x4A,0x03D1); /* GREEK THETA SYMBOL */
2156 		SYMB_UNICODE(0x4B,0x039A); /* GREEK CAPITAL LETTER KAPPA */
2157 		SYMB_UNICODE(0x4C,0x039B); /* GREEK CAPITAL LETTER LAMDA */
2158 		SYMB_UNICODE(0x4D,0x039C); /* GREEK CAPITAL LETTER MU */
2159 		SYMB_UNICODE(0x4E,0x039D); /* GREEK CAPITAL LETTER NU */
2160 		SYMB_UNICODE(0x4F,0x039F); /* GREEK CAPITAL LETTER OMICRON */
2161 		SYMB_UNICODE(0x50,0x03A0); /* GREEK CAPITAL LETTER PI */
2162 		SYMB_UNICODE(0x51,0x0398); /* GREEK CAPITAL LETTER THETA */
2163 		SYMB_UNICODE(0x52,0x03A1); /* GREEK CAPITAL LETTER RHO */
2164 		SYMB_UNICODE(0x53,0x03A3); /* GREEK CAPITAL LETTER SIGMA */
2165 		SYMB_UNICODE(0x54,0x03A4); /* GREEK CAPITAL LETTER TAU */
2166 		SYMB_UNICODE(0x55,0x03A5); /* GREEK CAPITAL LETTER UPSILON */
2167 		SYMB_UNICODE(0x56,0x03C2); /* GREEK SMALL LETTER FINAL SIGMA */
2168 		SYMB_UNICODE(0x57,0x03A9); /* GREEK CAPITAL LETTER OMEGA */
2169 		SYMB_UNICODE(0x58,0x039E); /* GREEK CAPITAL LETTER XI */
2170 		SYMB_UNICODE(0x59,0x03A8); /* GREEK CAPITAL LETTER PSI */
2171 		SYMB_UNICODE(0x5A,0x0396); /* GREEK CAPITAL LETTER ZETA */
2172 		SYMB_UNICODE(0x5B,0x005B); /* LEFT SQUARE BRACKET */
2173 		SYMB_UNICODE(0x5C,0x2234); /* THEREFORE */
2174 		SYMB_UNICODE(0x5D,0x005D); /* RIGHT SQUARE BRACKET */
2175 		SYMB_UNICODE(0x5E,0x22A5); /* UP TACK */
2176 		SYMB_UNICODE(0x5F,0x005F); /* LOW LINE */
2177 		SYMB_UNICODE(0x60,0xF8E5); /* radical extender corporate char */
2178 		SYMB_UNICODE(0x61,0x03B1); /* GREEK SMALL LETTER ALPHA */
2179 		SYMB_UNICODE(0x62,0x03B2); /* GREEK SMALL LETTER BETA */
2180 		SYMB_UNICODE(0x63,0x03C7); /* GREEK SMALL LETTER CHI */
2181 		SYMB_UNICODE(0x64,0x03B4); /* GREEK SMALL LETTER DELTA */
2182 		SYMB_UNICODE(0x65,0x03B5); /* GREEK SMALL LETTER EPSILON */
2183 		SYMB_UNICODE(0x66,0x03C6); /* GREEK SMALL LETTER PHI */
2184 		SYMB_UNICODE(0x67,0x03B3); /* GREEK SMALL LETTER GAMMA */
2185 		SYMB_UNICODE(0x68,0x03B7); /* GREEK SMALL LETTER ETA */
2186 		SYMB_UNICODE(0x69,0x03B9); /* GREEK SMALL LETTER IOTA */
2187 		SYMB_UNICODE(0x6A,0x03D5); /* GREEK PHI SYMBOL */
2188 		SYMB_UNICODE(0x6B,0x03BA); /* GREEK SMALL LETTER KAPPA */
2189 		SYMB_UNICODE(0x6C,0x03BB); /* GREEK SMALL LETTER LAMDA */
2190 		/* SYMB_UNICODE(0x6D,0x03BC); */ /* GREEK SMALL LETTER MU */
2191 		SYMB_UNICODE(0x6D,0x00B5); /* GREEK SMALL LETTER MU */
2192 		SYMB_UNICODE(0x6E,0x03BD); /* GREEK SMALL LETTER NU */
2193 		SYMB_UNICODE(0x6F,0x03BF); /* GREEK SMALL LETTER OMICRON */
2194 		SYMB_UNICODE(0x70,0x03C0); /* GREEK SMALL LETTER PI */
2195 		SYMB_UNICODE(0x71,0x03B8); /* GREEK SMALL LETTER THETA */
2196 		SYMB_UNICODE(0x72,0x03C1); /* GREEK SMALL LETTER RHO */
2197 		SYMB_UNICODE(0x73,0x03C3); /* GREEK SMALL LETTER SIGMA */
2198 		SYMB_UNICODE(0x74,0x03C4); /* GREEK SMALL LETTER TAU */
2199 		SYMB_UNICODE(0x75,0x03C5); /* GREEK SMALL LETTER UPSILON */
2200 		SYMB_UNICODE(0x76,0x03D6); /* GREEK PI SYMBOL */
2201 		SYMB_UNICODE(0x77,0x03C9); /* GREEK SMALL LETTER OMEGA */
2202 		SYMB_UNICODE(0x78,0x03BE); /* GREEK SMALL LETTER XI */
2203 		SYMB_UNICODE(0x79,0x03C8); /* GREEK SMALL LETTER PSI */
2204 		SYMB_UNICODE(0x7A,0x03B6); /* GREEK SMALL LETTER ZETA */
2205 		SYMB_UNICODE(0x7B,0x007B); /* LEFT CURLY BRACKET */
2206 		SYMB_UNICODE(0x7C,0x007C); /* VERTICAL LINE */
2207 		SYMB_UNICODE(0x7D,0x007D); /* RIGHT CURLY BRACKET */
2208 		SYMB_UNICODE(0x7E,0x223C); /* TILDE OPERATOR */
2209 
2210 		SYMB_UNICODE(0xA0,0x20AC); /* EURO SIGN */
2211 		SYMB_UNICODE(0xA1,0x03D2); /* GREEK UPSILON WITH HOOK SYMBOL */
2212 		SYMB_UNICODE(0xA2,0x2032); /* PRIME minute */
2213 		SYMB_UNICODE(0xA3,0x2264); /* LESS-THAN OR EQUAL TO */
2214 		SYMB_UNICODE(0xA4,0x2044); /* FRACTION SLASH */
2215 		SYMB_UNICODE(0xA5,0x221E); /* INFINITY */
2216 		SYMB_UNICODE(0xA6,0x0192); /* LATIN SMALL LETTER F WITH HOOK */
2217 		SYMB_UNICODE(0xA7,0x2663); /* BLACK CLUB SUIT */
2218 		SYMB_UNICODE(0xA8,0x2666); /* BLACK DIAMOND SUIT */
2219 		SYMB_UNICODE(0xA9,0x2665); /* BLACK HEART SUIT */
2220 		SYMB_UNICODE(0xAA,0x2660); /* BLACK SPADE SUIT */
2221 		SYMB_UNICODE(0xAB,0x2194); /* LEFT RIGHT ARROW */
2222 		SYMB_UNICODE(0xAC,0x2190); /* LEFTWARDS ARROW */
2223 		SYMB_UNICODE(0xAD,0x2191); /* UPWARDS ARROW */
2224 		SYMB_UNICODE(0xAE,0x2192); /* RIGHTWARDS ARROW */
2225 		SYMB_UNICODE(0xAF,0x2193); /* DOWNWARDS ARROW */
2226 		SYMB_UNICODE(0xB0,0x00B0); /* DEGREE SIGN */
2227 		SYMB_UNICODE(0xB1,0x00B1); /* PLUS-MINUS SIGN */
2228 		SYMB_UNICODE(0xB2,0x2033); /* DOUBLE PRIME second */
2229 		SYMB_UNICODE(0xB3,0x2265); /* GREATER-THAN OR EQUAL TO */
2230 		SYMB_UNICODE(0xB4,0x00D7); /* MULTIPLICATION SIGN */
2231 		SYMB_UNICODE(0xB5,0x221D); /* PROPORTIONAL TO */
2232 		SYMB_UNICODE(0xB6,0x2202); /* PARTIAL DIFFERENTIAL */
2233 		SYMB_UNICODE(0xB7,0x2022); /* BULLET */
2234 		SYMB_UNICODE(0xB8,0x00F7); /* DIVISION SIGN */
2235 		SYMB_UNICODE(0xB9,0x2260); /* NOT EQUAL TO */
2236 		SYMB_UNICODE(0xBA,0x2261); /* IDENTICAL TO */
2237 		SYMB_UNICODE(0xBB,0x2248); /* ALMOST EQUAL TO */
2238 		SYMB_UNICODE(0xBC,0x2026); /* HORIZONTAL ELLIPSIS */
2239 		SYMB_UNICODE(0xBD,0x23D0); /* VERTICAL LINE EXTENSION (for arrows) */
2240 		SYMB_UNICODE(0xBE,0x23AF); /* HORIZONTAL LINE EXTENSION (for arrows) */
2241 		SYMB_UNICODE(0xBF,0x21B5); /* DOWNWARDS ARROW WITH CORNER LEFTWARDS */
2242 		SYMB_UNICODE(0xC0,0x2135); /* ALEF SYMBOL */
2243 		SYMB_UNICODE(0xC1,0x2111); /* BLACK-LETTER CAPITAL I */
2244 		SYMB_UNICODE(0xC2,0x211C); /* BLACK-LETTER CAPITAL R */
2245 		SYMB_UNICODE(0xC3,0x2118); /* SCRIPT CAPITAL P */
2246 		SYMB_UNICODE(0xC4,0x2297); /* CIRCLED TIMES */
2247 		SYMB_UNICODE(0xC5,0x2295); /* CIRCLED PLUS */
2248 		SYMB_UNICODE(0xC6,0x2205); /* EMPTY SET */
2249 		SYMB_UNICODE(0xC7,0x2229); /* INTERSECTION */
2250 		SYMB_UNICODE(0xC8,0x222A); /* UNION */
2251 		SYMB_UNICODE(0xC9,0x2283); /* SUPERSET OF */
2252 		SYMB_UNICODE(0xCA,0x2287); /* SUPERSET OF OR EQUAL TO */
2253 		SYMB_UNICODE(0xCB,0x2284); /* NOT A SUBSET OF */
2254 		SYMB_UNICODE(0xCC,0x2282); /* SUBSET OF */
2255 		SYMB_UNICODE(0xCD,0x2286); /* SUBSET OF OR EQUAL TO */
2256 		SYMB_UNICODE(0xCE,0x2208); /* ELEMENT OF */
2257 		SYMB_UNICODE(0xCF,0x2209); /* NOT AN ELEMENT OF */
2258 		SYMB_UNICODE(0xD0,0x2220); /* ANGLE */
2259 		SYMB_UNICODE(0xD1,0x2207); /* NABLA */
2260 		SYMB_UNICODE(0xD2,0x00AE); /* REGISTERED SIGN serif */
2261 		SYMB_UNICODE(0xD3,0x00A9); /* COPYRIGHT SIGN serif */
2262 		SYMB_UNICODE(0xD4,0x2122); /* TRADE MARK SIGN serif */
2263 		SYMB_UNICODE(0xD5,0x220F); /* N-ARY PRODUCT */
2264 		SYMB_UNICODE(0xD6,0x221A); /* SQUARE ROOT */
2265 		SYMB_UNICODE(0xD7,0x22C5); /* DOT OPERATOR */
2266 		SYMB_UNICODE(0xD8,0x00AC); /* NOT SIGN */
2267 		SYMB_UNICODE(0xD9,0x2227); /* LOGICAL AND */
2268 		SYMB_UNICODE(0xDA,0x2228); /* LOGICAL OR */
2269 		SYMB_UNICODE(0xDB,0x21D4); /* LEFT RIGHT DOUBLE ARROW */
2270 		SYMB_UNICODE(0xDC,0x21D0); /* LEFTWARDS DOUBLE ARROW */
2271 		SYMB_UNICODE(0xDD,0x21D1); /* UPWARDS DOUBLE ARROW */
2272 		SYMB_UNICODE(0xDE,0x21D2); /* RIGHTWARDS DOUBLE ARROW */
2273 		SYMB_UNICODE(0xDF,0x21D3); /* DOWNWARDS DOUBLE ARROW */
2274 		SYMB_UNICODE(0xE0,0x25CA); /* LOZENGE previously mapped to 0x22C4 DIAMOND OPERATOR */
2275 		SYMB_UNICODE(0xE1,0x3008); /* LEFT ANGLE BRACKET */
2276 		SYMB_UNICODE(0xE5,0x2211); /* N-ARY SUMMATION */
2277 		SYMB_UNICODE(0xE6,0x239B); /* LEFT PARENTHESIS UPPER HOOK */
2278 		SYMB_UNICODE(0xE7,0x239C); /* LEFT PARENTHESIS EXTENSION */
2279 		SYMB_UNICODE(0xE8,0x239D); /* LEFT PARENTHESIS LOWER HOOK */
2280 		SYMB_UNICODE(0xE9,0x23A1); /* LEFT SQUARE BRACKET UPPER CORNER */
2281 		SYMB_UNICODE(0xEA,0x23A2); /* LEFT SQUARE BRACKET EXTENSION */
2282 		SYMB_UNICODE(0xEB,0x23A3); /* LEFT SQUARE BRACKET LOWER CORNER */
2283 		SYMB_UNICODE(0xEC,0x23A7); /* LEFT CURLY BRACKET UPPER HOOK */
2284 		SYMB_UNICODE(0xED,0x23A8); /* LEFT CURLY BRACKET MIDDLE PIECE */
2285 		SYMB_UNICODE(0xEE,0x23A9); /* LEFT CURLY BRACKET LOWER HOOK */
2286 		SYMB_UNICODE(0xEF,0x23AA); /* CURLY BRACKET EXTENSION */
2287 		SYMB_UNICODE(0xF0,0xF8FF); /* Apple logo */
2288 		SYMB_UNICODE(0xF1,0x3009); /* RIGHT ANGLE BRACKET */
2289 		SYMB_UNICODE(0xF2,0x222B); /* INTEGRAL */
2290 		SYMB_UNICODE(0xF3,0x2320); /* TOP HALF INTEGRAL */
2291 		SYMB_UNICODE(0xF4,0x23AE); /* INTEGRAL EXTENSION */
2292 		SYMB_UNICODE(0xF5,0x2321); /* BOTTOM HALF INTEGRAL */
2293 		SYMB_UNICODE(0xF6,0x239E); /* RIGHT PARENTHESIS UPPER HOOK */
2294 		SYMB_UNICODE(0xF7,0x239F); /* RIGHT PARENTHESIS EXTENSION */
2295 		SYMB_UNICODE(0xF8,0x23A0); /* RIGHT PARENTHESIS LOWER HOOK */
2296 		SYMB_UNICODE(0xF9,0x23A4); /* RIGHT SQUARE BRACKET UPPER CORNER */
2297 		SYMB_UNICODE(0xFA,0x23A5); /* RIGHT SQUARE BRACKET EXTENSION */
2298 		SYMB_UNICODE(0xFB,0x23A6); /* RIGHT SQUARE BRACKET LOWER CORNER */
2299 		SYMB_UNICODE(0xFC,0x23AB); /* RIGHT CURLY BRACKET UPPER HOOK */
2300 		SYMB_UNICODE(0xFD,0x23AC); /* RIGHT CURLY BRACKET MIDDLE PIECE */
2301 		SYMB_UNICODE(0xFE,0x23AD); /* RIGHT CURLY BRACKET LOWER HOOK */
2302 
2303 		/* to be treated specifically : composed characters */
2304 		case 0xE2 : /* REGISTERED SIGN, alternate: sans serif */
2305 			g_unichar_to_utf8(0x00AE,iter_mod);
2306 			iter_mod = g_utf8_next_char(iter_mod);
2307 			g_unichar_to_utf8(0xF87F,iter_mod);
2308 			break;
2309 		case 0xE3 : /* COPYRIGHT SIGN, alternate: sans serif */
2310 			g_unichar_to_utf8(0x00A9,iter_mod);
2311 			iter_mod = g_utf8_next_char(iter_mod);
2312 			g_unichar_to_utf8(0xF87F,iter_mod);
2313 			break;
2314 		case 0xE4 : /* TRADE MARK SIGN, alternate: sans serif */
2315 			g_unichar_to_utf8(0x2122,iter_mod);
2316 			iter_mod = g_utf8_next_char(iter_mod);
2317 			g_unichar_to_utf8(0xF87F,iter_mod);
2318 			break;
2319 		default : g_unichar_to_utf8( g_utf8_get_char(iter), iter_mod); break;
2320 		}
2321 		iter = g_utf8_next_char(iter);
2322 		iter_mod = g_utf8_next_char(iter_mod);
2323 	}
2324 
2325 	g_free(string_utf8);
2326 	return output;
2327 }
2328