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