1 /****************************************************************************
2  * Copyright (C) 2008-2011 by Matteo Franchin                               *
3  *                                                                          *
4  * This file is part of Box.                                                *
5  *                                                                          *
6  *   Box is free software: you can redistribute it and/or modify it         *
7  *   under the terms of the GNU Lesser General Public License as published  *
8  *   by the Free Software Foundation, either version 3 of the License, or   *
9  *   (at your option) any later version.                                    *
10  *                                                                          *
11  *   Box is distributed in the hope that it will be useful,                 *
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of         *
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
14  *   GNU Lesser General Public License for more details.                    *
15  *                                                                          *
16  *   You should have received a copy of the GNU Lesser General Public       *
17  *   License along with Box.  If not, see <http://www.gnu.org/licenses/>.   *
18  ****************************************************************************/
19 
20 #include <stdlib.h>
21 #include <math.h>
22 #include <string.h>
23 #include <assert.h>
24 
25 #include <cairo.h>
26 #ifdef CAIRO_HAS_PS_SURFACE
27 #  include <cairo-ps.h>
28 #endif
29 #ifdef CAIRO_HAS_PDF_SURFACE
30 #  include <cairo-pdf.h>
31 #endif
32 #ifdef CAIRO_HAS_SVG_SURFACE
33 #  include <cairo-svg.h>
34 #endif
35 
36 #include <box/types.h>
37 #include <box/mem.h>
38 #include <box/array.h>
39 
40 #include "error.h"
41 #include "graphic.h"
42 #include "g.h"
43 #include "wincairo.h"
44 #include "psfonts.h"
45 #include "formatter.h"
46 #include "obj.h"
47 #include "cmd.h"
48 
49 /*#define DEBUG*/
50 
51 /*****************************************************************************
52  * Below we provide functionality to save and restore just the line state.
53  * This is useful to implement separate border and line settings so that the
54  * border for polygons does not affect the way lines are traced (Line command).
55  *
56  * NOTE ON SEPARATION BETWEEN BORDER LINE STYLE AND Line LINE STYLE:
57  * This makes sense, as often Line, Poly, etc are used to draw different parts
58  * of the same things, while the border setting is conceptually different.
59  */
60 
61 typedef struct {
62   cairo_pattern_t   *pattern;
63   double            width,
64                     miter_limit;
65   cairo_line_cap_t  cap;
66   cairo_line_join_t join;
67   int               dash_count;
68   double            *dashes,
69                     dash_offset;
70 
71 } MyLineState;
72 
73 /** Always call the Init method before using a MyLineState. */
MyLineState_Init(MyLineState * ls)74 static void MyLineState_Init(MyLineState *ls) {
75   ls->pattern = NULL;
76   ls->width = 0.0;
77   ls->miter_limit = 10.0;
78   ls->cap = CAIRO_LINE_CAP_BUTT;
79   ls->join = CAIRO_LINE_JOIN_MITER;
80   ls->dash_count = 0;
81   ls->dashes = NULL;
82 }
83 
84 /** Copy 'src' to 'uninit_dest' assuming 'uninit_dest' points to uninitialized
85  * memory which is big enough to hold a MyLineState object.
86  */
MyLineState_Duplicate(MyLineState * uninit_dest,MyLineState * src)87 static void MyLineState_Duplicate(MyLineState *uninit_dest, MyLineState *src) {
88   size_t dashes_size = Box_Mem_Safe_AX(sizeof(double), src->dash_count);
89   *uninit_dest = *src;
90   cairo_pattern_reference(uninit_dest->pattern);
91   uninit_dest->dashes = Box_Mem_Safe_Alloc(dashes_size);
92   (void) memcpy(uninit_dest->dashes, src->dashes, dashes_size);
93 }
94 
95 /** Relocate the MyLineState object at location 'src' to 'uninit_dest'.
96  * 'src' will be cleared after the operation (so that you can freely call
97  * MyLineState_Finish without compromising the data in 'uninit_dest'.
98  * Note that 'uninit_dest' is assumed to be raw uninitialized memory
99  * (in other words MyLineState_Finish is not called before overwriting it
100  * with 'src').
101  */
MyLineState_Relocate(MyLineState * uninit_dest,MyLineState * src)102 static void MyLineState_Relocate(MyLineState *uninit_dest, MyLineState *src) {
103   *uninit_dest = *src;
104   MyLineState_Init(src);
105 }
106 
107 /** Always call the Finish method before using a MyLineState. */
MyLineState_Finish(MyLineState * ls)108 static void MyLineState_Finish(MyLineState *ls) {
109   cairo_pattern_destroy(ls->pattern);
110   Box_Mem_Free(ls->dashes);
111   ls->pattern = NULL;
112   ls->dashes = NULL;
113 }
114 
115 /** Save the line state from the cairo context 'cr' to 'ls'. */
MyLineState_Save(MyLineState * ls,cairo_t * cr)116 static void MyLineState_Save(MyLineState *ls, cairo_t *cr) {
117   MyLineState_Finish(ls);
118 
119   ls->width = cairo_get_line_width(cr);
120   ls->cap = cairo_get_line_cap(cr);
121   ls->join = cairo_get_line_join(cr);
122   ls->miter_limit = cairo_get_miter_limit(cr);
123 
124   ls->dash_count = cairo_get_dash_count(cr);
125   ls->dashes = Box_Mem_Safe_Alloc_Items(sizeof(double), ls->dash_count);
126   cairo_get_dash(cr, ls->dashes, & ls->dash_offset);
127 
128   ls->pattern = cairo_get_source(cr);
129   cairo_pattern_reference(ls->pattern);
130 }
131 
132 /** Restore the line state from 'ls' back to the cairo context 'cr'. */
MyLineState_Restore(MyLineState * ls,cairo_t * cr)133 static void MyLineState_Restore(MyLineState *ls, cairo_t *cr) {
134   cairo_set_line_width(cr, ls->width);
135   cairo_set_line_cap(cr, ls->cap);
136   cairo_set_line_join(cr, ls->join);
137   cairo_set_miter_limit(cr, ls->miter_limit);
138 
139   if (ls->pattern != NULL)
140     cairo_set_source(cr, ls->pattern);
141 
142   assert(ls->dashes != NULL || ls->dash_count == 0);
143   cairo_set_dash(cr, ls->dashes, ls->dash_count, ls->dash_offset);
144 }
145 
MyLineState_Exchange(MyLineState * ls,cairo_t * cr)146 static void MyLineState_Exchange(MyLineState *ls, cairo_t *cr) {
147   MyLineState my_ls;
148   MyLineState_Init(& my_ls);
149   MyLineState_Save(& my_ls, cr);
150   MyLineState_Restore(ls, cr);
151   MyLineState_Finish(ls);
152   *ls = my_ls;
153 }
154 
155 /*****************************************************************************/
156 
157 /** Type of the BoxGWin->data pointer, containing data specific to the Cairo
158  * window type
159  */
160 typedef struct {
161   cairo_pattern_t *pattern;
162   MyLineState     line_state;
163   BoxArr          saved_line_states;
164 
165 } MyCairoWinData;
166 
My_Saved_LineState_Finalizer(void * item)167 static void My_Saved_LineState_Finalizer(void *item) {
168   MyLineState_Finish((MyLineState *) item);
169 }
170 
MyCairoWinData_Init(MyCairoWinData * wd)171 static void MyCairoWinData_Init(MyCairoWinData *wd) {
172   wd->pattern = NULL;
173   MyLineState_Init(& wd->line_state);
174   BoxArr_Init(& wd->saved_line_states, sizeof(MyLineState), 2);
175   BoxArr_Set_Finalizer(& wd->saved_line_states, My_Saved_LineState_Finalizer);
176 }
177 
MyCairoWinData_Finish(MyCairoWinData * wd)178 static void MyCairoWinData_Finish(MyCairoWinData *wd) {
179   if (wd->pattern != NULL)
180     cairo_pattern_destroy(wd->pattern);
181   MyLineState_Finish(& wd->line_state);
182   BoxArr_Finish(& wd->saved_line_states);
183 }
184 
MyCairoWinData_LineState_Push_Any(MyCairoWinData * wd,MyLineState * ls)185 static void MyCairoWinData_LineState_Push_Any(MyCairoWinData *wd,
186                                               MyLineState *ls) {
187   MyLineState *new_ls = BoxArr_Push(& wd->saved_line_states, NULL);
188   assert(new_ls != NULL);
189   MyLineState_Duplicate(new_ls, ls);
190 }
191 
MyCairoWinData_LineState_Pop_Any(MyCairoWinData * wd,MyLineState * ls)192 static void MyCairoWinData_LineState_Pop_Any(MyCairoWinData *wd,
193                                              MyLineState *ls) {
194   MyLineState *top_ls = BoxArr_Last_Item_Ptr(& wd->saved_line_states);
195   MyLineState_Finish(ls);
196   MyLineState_Relocate(ls, top_ls);
197   BoxArr_Pop(& wd->saved_line_states, NULL);
198 }
199 
MyCairoWinData_LineState_Push(MyCairoWinData * wd)200 static void MyCairoWinData_LineState_Push(MyCairoWinData *wd) {
201   MyCairoWinData_LineState_Push_Any(wd, & wd->line_state);
202 }
203 
MyCairoWinData_LineState_Pop(MyCairoWinData * wd)204 static void MyCairoWinData_LineState_Pop(MyCairoWinData *wd) {
205   MyCairoWinData_LineState_Pop_Any(wd, & wd->line_state);
206 }
207 
208 
209 /** Macro to access Cairo specific window data. */
210 #define MY_DATA(w) ((MyCairoWinData *) (w)->data)
211 
212 /*****************************************************************************/
213 
214 /* Invert the cairo matrix 'in' and put the result in 'result'.
215  * The determinant of 'in' is returned and is 0.0, if the matrix
216  * couldn't be inverted.
217  */
My_Invert_Cairo_Matrix(cairo_matrix_t * result,cairo_matrix_t * in)218 static BoxReal My_Invert_Cairo_Matrix(cairo_matrix_t *result, cairo_matrix_t *in) {
219   BoxReal det = in->xx*in->yy - in->xy*in->yx, inv_det;
220   if (det == 0.0) return 0.0;
221   inv_det = 1.0/det;
222   result->xx =  inv_det*in->yy; result->xy = -inv_det*in->xy;
223   result->yx = -inv_det*in->yx; result->yy =  inv_det*in->xx;
224   result->x0 = -result->xx * in->x0 - result->xy * in->y0;
225   result->y0 = -result->yx * in->x0 - result->yy * in->y0;
226   return det;
227 }
228 
229 static char *wincairo_image_id_string   = "cairo:image";
230 static char *wincairo_stream_id_string  = "cairo:stream";
231 
My_WinCairo_Finish(BoxGWin * w)232 static void My_WinCairo_Finish(BoxGWin *w) {
233   cairo_t *cr = (cairo_t *) w->ptr;
234   cairo_surface_t *surface = cairo_get_target(cr);
235 
236   cairo_show_page(cr);
237   cairo_destroy(cr);
238   cairo_surface_destroy(surface);
239 
240   MyCairoWinData_Finish(MY_DATA(w));
241   Box_Mem_Free(MY_DATA(w));
242 }
243 
244 /* Variabili usate dalle procedure per scrivere il file postscript */
245 static int beginning_of_path = 1;
246 static BoxPoint previous;
247 
same_points(BoxPoint * a,BoxPoint * b)248 static int same_points(BoxPoint *a, BoxPoint *b) {
249   return (fabs(a->x - b->x) < 1e-10 && fabs(a->y - b->y) < 1e-10);
250 }
251 
252 /** This is the function used to map points. */
My_Map_Point(BoxGWin * w,BoxPoint * out,BoxPoint * in)253 static void My_Map_Point(BoxGWin *w, BoxPoint *out, BoxPoint *in) {
254   out->x = (in->x - w->ltx)*w->resx;
255   out->y = (in->y - w->lty)*w->resy;
256 }
257 
258 /* Macros used to scale the point coordinates */
259 #define MY_POINT(w, a) \
260   BoxPoint my_a; My_Map_Point((w), & my_a, (a)); (a) = & my_a
261 
262 #define MY_2POINTS(w, a, b) \
263   BoxPoint my_a, my_b; \
264   My_Map_Point((w), & my_a, (a)); My_Map_Point((w), & my_b, (b)); \
265   (a) = & my_a; (b) = & my_b
266 
267 #define MY_3POINTS(w, a, b, c) \
268   BoxPoint my_a, my_b, my_c; \
269   My_Map_Point((w), & my_a, (a)); My_Map_Point((w), & my_b, (b)); \
270   My_Map_Point((w), & my_c, (c)); \
271   (a) = & my_a; (b) = & my_b; (c) = & my_c
272 
273 /* This is broken, but is required for fonts (fonts support is broken as well
274  * and needs to be better done: font drawing should depend on Points, not on
275  * a given Real size!)
276  */
277 #define MY_REAL(w, r) ((r)*(w)->resx)
278 
279 /* BEGIN OF TEXT FORMATTING IMPLEMENTATION **********************************
280  * Here we implement some basic text formatting features, such as           *
281  * subscripts, superscripts and multi-line paragraphs. We exploit           *
282  * the parser provided by the formatter.c module.                           *
283  ****************************************************************************/
284 
285 typedef struct {
286   cairo_t *cr;
287   BoxArr  saved_states;
288   BoxPoint   sub_vec,
289           sup_vec;
290   BoxReal    sub_scale,
291           sup_scale;
292 } MyTextPrivate;
293 
294 typedef struct {
295   BoxPoint          cur_pos;
296   cairo_matrix_t m;
297 } MyTextState;
298 
My_Text_Fmt_Draw(BoxGFmtStack * stack)299 static void My_Text_Fmt_Draw(BoxGFmtStack *stack) {
300   BoxGFmt *fmt = BoxGFmt_Get(stack);
301   MyTextPrivate *private = BoxGFmt_Get_Private(fmt);
302   char *text = BoxGFmt_Get_Buffer(stack);
303   cairo_text_path(private->cr, text);
304   BoxGFmt_Clear_Buffer(stack);
305 }
306 
My_Text_Fmt_Save(BoxGFmtStack * stack)307 static void My_Text_Fmt_Save(BoxGFmtStack *stack) {
308   BoxGFmt *fmt = BoxGFmt_Get(stack);
309   MyTextPrivate *private = BoxGFmt_Get_Private(fmt);
310   MyTextState ss;
311   cairo_get_matrix(private->cr, & ss.m);
312   cairo_get_current_point(private->cr, & ss.cur_pos.x, & ss.cur_pos.y);
313   (void) BoxArr_Push(& private->saved_states, & ss);
314 }
315 
My_Text_Fmt_Restore(BoxGFmtStack * stack)316 static void My_Text_Fmt_Restore(BoxGFmtStack *stack) {
317   BoxGFmt *fmt = BoxGFmt_Get(stack);
318   MyTextPrivate *private = BoxGFmt_Get_Private(fmt);
319   MyTextState *ts = BoxArr_Last_Item_Ptr(& private->saved_states);
320   double x, y;
321   cairo_set_matrix(private->cr, & ts->m);
322   cairo_get_current_point(private->cr, & x, & y);
323   cairo_move_to(private->cr, x, ts->cur_pos.y);
324   BoxArr_Pop(& private->saved_states, NULL);
325 }
326 
My_Text_Fmt_Change(cairo_t * cr,BoxPoint * vec,BoxReal scale)327 static void My_Text_Fmt_Change(cairo_t *cr, BoxPoint *vec, BoxReal scale) {
328   cairo_matrix_t m;
329   cairo_get_current_point(cr, & m.x0, & m.y0);
330   m.x0 += vec->x;
331   m.y0 += vec->y;
332   m.xx = m.yy = scale;
333   m.xy = m.yx = 0.0;
334   cairo_transform(cr, & m);
335   cairo_move_to(cr, (double) 0.0, (double) 0.0);
336 }
337 
My_Text_Fmt_Superscript(BoxGFmtStack * stack)338 static void My_Text_Fmt_Superscript(BoxGFmtStack *stack) {
339   BoxGFmt *fmt = BoxGFmt_Get(stack);
340   MyTextPrivate *private = BoxGFmt_Get_Private(fmt);
341   My_Text_Fmt_Change(private->cr, & private->sup_vec, private->sup_scale);
342 }
343 
My_Text_Fmt_Subscript(BoxGFmtStack * stack)344 static void My_Text_Fmt_Subscript(BoxGFmtStack *stack) {
345   BoxGFmt *fmt = BoxGFmt_Get(stack);
346   MyTextPrivate *private = BoxGFmt_Get_Private(fmt);
347   My_Text_Fmt_Change(private->cr, & private->sub_vec, private->sub_scale);
348 }
349 
My_Text_Fmt_Newline(BoxGFmtStack * stack)350 static void My_Text_Fmt_Newline(BoxGFmtStack *stack) {
351   BoxGFmt *fmt = BoxGFmt_Get(stack);
352   MyTextPrivate *private = BoxGFmt_Get_Private(fmt);
353   cairo_translate(private->cr, 0.0, -1.0);
354   cairo_move_to(private->cr, 0.0, 0.0);
355 }
356 
My_Text_Fmt_Init(BoxGFmt * fmt)357 static void My_Text_Fmt_Init(BoxGFmt *fmt) {
358   BoxGFmt_Init(fmt);
359   fmt->draw = My_Text_Fmt_Draw;
360   fmt->subscript = My_Text_Fmt_Subscript;
361   fmt->superscript = My_Text_Fmt_Superscript;
362   fmt->newline = My_Text_Fmt_Newline;
363   fmt->save = My_Text_Fmt_Save;
364   fmt->restore = My_Text_Fmt_Restore;
365 }
366 
My_Cairo_Text_Path(BoxGWin * w,BoxPoint * ctr,BoxPoint * right,BoxPoint * up,BoxPoint * from,const char * text)367 static void My_Cairo_Text_Path(BoxGWin *w, BoxPoint *ctr, BoxPoint *right,
368                                BoxPoint *up, BoxPoint *from, const char *text) {
369   cairo_t *cr = (cairo_t *) w->ptr;
370 
371   /* Here we need to first generate the path, calculate its extent
372    * and then position it according to the value of 'from'. The problem:
373    * how do we do this without disturbing the path sitting in the main cairo
374    * context 'cr'. At the moment, the only option I see is to create a second
375    * cairo_t from 'cr' and do the path computation in there.
376    * NOTE: it may be worth to store 'my_cr' so that it can be reused in other
377    *   calls to this function (mainly to reuse the font face).
378    */
379   cairo_surface_t *cs = cairo_get_target(cr);
380   assert(cairo_surface_status(cs) == CAIRO_STATUS_SUCCESS);
381   /* Status should always be successful, unless cr is broken. In this case
382    * also cs will be "broken" (see doc for cairo_get_target).
383    * The assertion above excludes this case.
384    */
385 
386   cairo_t *my_cr = cairo_create(cs);
387   if (cairo_status(my_cr) == CAIRO_STATUS_SUCCESS) {
388     /* We first copy settings from 'cr' to 'my_cr' */
389     cairo_font_face_t *ff = cairo_get_font_face(cr);
390     if (cairo_font_face_status(ff) == CAIRO_STATUS_SUCCESS) {
391       /* Copy the coordinate system, first */
392       cairo_matrix_t m, m0;
393       cairo_get_matrix(cr, & m0);
394 
395       /* Set the matrix to the region defined by 'ctr', 'up' and 'right' */
396       m.xx = right->x - ctr->x;  m.yx = right->y - ctr->y;
397       m.xy = up->x - ctr->x;  m.yy = up->y - ctr->y;
398       m.x0 = ctr->x; m.y0 = ctr->y;
399       cairo_transform(cr, & m);
400 
401       cairo_get_matrix(cr, & m);
402       cairo_set_matrix(my_cr, & m);
403       cairo_move_to(my_cr, 0.0, 0.0);
404 
405       /* Set the font face and matrix */
406       cairo_set_font_face(my_cr, ff);
407       m.xx = 1.0; m.yy = -1.0;
408       m.xy = m.yx = m.x0 = m.y0 = 0.0;
409       cairo_set_font_matrix(my_cr, & m);
410 
411       {
412         /* We now draw the path to 'my_cr' */
413         MyTextPrivate private;
414         BoxGFmt fmt;
415 
416         My_Text_Fmt_Init(& fmt);
417         BoxGFmt_Set_Private(& fmt, & private);
418         private.cr = my_cr;
419         private.sup_vec.x = 0.0;
420         private.sup_vec.y = 0.5;
421         private.sup_scale = 0.5;
422         private.sub_vec.x = 0.0;
423         private.sub_vec.y = -0.1;
424         private.sub_scale = 0.5;
425         BoxArr_Init(& private.saved_states, sizeof(MyTextState), 8);
426         BoxGFmt_Draw_Text(& fmt, text);
427 
428         BoxArr_Finish(& private.saved_states);
429       }
430 
431       {
432         cairo_path_t *text_path;
433         double x1, y1, x2, y2;
434 
435         /* Go back to cr's coordinates */
436         cairo_get_matrix(cr, & m);
437         cairo_set_matrix(my_cr, & m);
438 
439         /* Get extent and path */
440         cairo_fill_extents(my_cr, & x1, & y1, & x2, & y2);
441         text_path = cairo_copy_path(my_cr);
442         assert(text_path->status == CAIRO_STATUS_SUCCESS);
443 
444         /* Translate 'cr' as specified by 'from', and draw the stored path */
445         cairo_translate(cr,
446                         -x1 - (x2 - x1)*from->x,
447                         -y1 - (y2 - y1)*from->y);
448         cairo_append_path(cr, text_path);
449         cairo_path_destroy(text_path);
450 
451         /* Restore the coordinate system */
452         cairo_set_matrix(cr, & m0);
453       }
454 
455       beginning_of_path = 0;
456     }
457 
458   } else
459     g_warning("My_Cairo_Text_Path: Cannot create cairo context. ");
460 
461   cairo_destroy(my_cr);
462 }
463 
464 /* END OF TEXT FORMATTING IMPLEMENTATION ************************************/
465 
My_Cairo_JoinArc(cairo_t * cr,const BoxPoint * a,const BoxPoint * b,const BoxPoint * c)466 static void My_Cairo_JoinArc(cairo_t *cr,
467                              const BoxPoint *a, const BoxPoint *b, const BoxPoint *c) {
468   cairo_matrix_t previous_m, m;
469 
470   cairo_get_matrix(cr, & previous_m);
471   m.xx = b->x - c->x; m.yx = b->y - c->y;
472   m.xy = b->x - a->x; m.yy = b->y - a->y;
473   m.x0 = a->x - m.xx; m.y0 = a->y - m.yx;
474   cairo_transform(cr, & m);
475 
476   cairo_arc(cr,
477             (double) 0.0, (double) 0.0,      /* center */
478             (double) 1.0,                    /* radius */
479             (double) 0.0, (double) M_PI/2.0); /* angle begin and end */
480 
481   cairo_set_matrix(cr, & previous_m);
482 }
483 
My_Cairo_Arc(cairo_t * cr,const BoxPoint * ctr,const BoxPoint * a,const BoxPoint * b,BoxReal angle_begin,BoxReal angle_end)484 static void My_Cairo_Arc(cairo_t *cr,
485                          const BoxPoint *ctr, const BoxPoint *a, const BoxPoint *b,
486                          BoxReal angle_begin, BoxReal angle_end) {
487   cairo_matrix_t previous_m, m;
488 
489   cairo_get_matrix(cr, & previous_m);
490   m.xx = a->x - ctr->x;  m.yx = a->y - ctr->y;
491   m.xy = b->x - ctr->x;  m.yy = b->y - ctr->y;
492   m.x0 = ctr->x; m.y0 = ctr->y;
493   cairo_transform(cr, & m);
494 
495   cairo_arc(cr,
496             (double) 0, (double) 0,  /* center */
497             (double) 1,              /* radius */
498             angle_begin, angle_end); /* angle begin and end */
499 
500   cairo_set_matrix(cr, & previous_m);
501 }
502 
My_Cairo_Set_Font(BoxGWin * w,const char * font_name)503 static void My_Cairo_Set_Font(BoxGWin *w, const char *font_name) {
504   cairo_t *cr = (cairo_t *) w->ptr;
505   const char *name;
506   FontSlant fs;
507   FontWeight fw;
508   cairo_font_slant_t cs;
509   cairo_font_weight_t cw;
510   cairo_font_face_t *ff;
511   cairo_status_t status;
512   cairo_matrix_t m;
513 
514   if (ps_font_get_info(font_name, & name, & fs, & fw)) {
515     switch(fs) {
516     case FONT_SLANT_NORMAL: cs = CAIRO_FONT_SLANT_NORMAL; break;
517     case FONT_SLANT_ITALIC: cs = CAIRO_FONT_SLANT_ITALIC; break;
518     case FONT_SLANT_OBLIQUE: cs = CAIRO_FONT_SLANT_OBLIQUE; break;
519     default: cs = CAIRO_FONT_SLANT_NORMAL; break;
520     }
521 
522     switch(fw) {
523     case FONT_WEIGHT_NORMAL: cw = CAIRO_FONT_WEIGHT_NORMAL; break;
524     case FONT_WEIGHT_BOLD: cw = CAIRO_FONT_WEIGHT_BOLD; break;
525     default: cw = CAIRO_FONT_WEIGHT_NORMAL; break;
526     }
527 
528   } else {
529     name = font_name;
530     cs = CAIRO_FONT_SLANT_NORMAL;
531     cw = CAIRO_FONT_WEIGHT_NORMAL;
532   }
533 
534   cairo_select_font_face(cr, name, cs, cw);
535   ff = cairo_get_font_face(cr);
536   status = cairo_font_face_status(ff);
537   if (status != CAIRO_STATUS_SUCCESS) {
538     fprintf(stderr, "Cannot set font: %s.\n", cairo_status_to_string(status));
539     cairo_select_font_face(cr, "sans", CAIRO_FONT_SLANT_NORMAL,
540                            CAIRO_FONT_WEIGHT_NORMAL);
541   }
542 
543   /* Cairo coordinates use the following convention: increasing x goes from
544    * the left to the right side of the screen, while increasing y goes from
545    * the top to the bottom side. Postscript coordinates use the opposite
546    * convention. The Box graphics library also sticks to the Postscript
547    * convention, since this is the usual choice in geometry and mathematics.
548    * Care has to be put in converting from one to the other convention:
549    * we don't want to simply mirror the image: we want to do that only for
550    * graphics, while preserving the direction for fonts: mirroring fonts
551    * is not really what we want! Therefore we DON'T choose the identity
552    * matrix for fonts! We choose diag(1, -1)!
553    */
554   m.xx = 1.0; m.yy = -1.0;
555   m.xy = m.yx = m.x0 = m.y0 = 0.0;
556   cairo_set_font_matrix(cr, & m);
557 }
558 
559 static cairo_pattern_t *
My_Cairo_Pattern_Create_Radial(BoxPoint * p1,BoxPoint * xone,BoxPoint * yone,BoxPoint * p2,BoxReal rad1,BoxReal rad2)560 My_Cairo_Pattern_Create_Radial(BoxPoint *p1, BoxPoint *xone, BoxPoint *yone,
561                                BoxPoint *p2, BoxReal rad1, BoxReal rad2) {
562   cairo_pattern_t *pattern;
563   BoxPoint xaxis, yaxis;
564   cairo_matrix_t m, m_inv;
565   double p2xt = p2->x, p2yt = p2->y;
566 
567   xaxis.x = xone->x - p1->x;
568   xaxis.y = xone->y - p1->y;
569   yaxis.x = yone->x - p1->x;
570   yaxis.y = yone->y - p1->y;
571 
572   m.xx = xaxis.x; m.yx = xaxis.y;
573   m.xy = yaxis.x; m.yy = yaxis.y;
574   m.x0 = p1->x; m.y0 = p1->y;
575 
576   if (My_Invert_Cairo_Matrix(& m_inv, & m) == 0.0)
577     return NULL;
578 
579   cairo_matrix_transform_point(& m_inv, & p2xt, & p2yt);
580   pattern = cairo_pattern_create_radial(0.0, 0.0, rad1,
581                                         p2xt, p2yt, rad2);
582   cairo_pattern_set_matrix(pattern, & m_inv);
583   return pattern;
584 }
585 
586 static cairo_pattern_t *
My_Cairo_Pattern_Create_Image(cairo_t * cr,const BoxPoint * zero,const BoxPoint * xone,const BoxPoint * yone,const BoxPoint * from,const char * filename)587 My_Cairo_Pattern_Create_Image(cairo_t *cr,
588                               const BoxPoint *zero,
589                               const BoxPoint *xone, const BoxPoint *yone,
590                               const BoxPoint *from,
591                               const char *filename)
592 {
593   cairo_pattern_t *pattern;
594   cairo_surface_t *image = cairo_image_surface_create_from_png(filename);
595   double lx = cairo_image_surface_get_width(image),
596          ly = cairo_image_surface_get_height(image);
597 
598   cairo_matrix_t m, m_inv;
599 
600   m.xx = (xone->x - zero->x)/lx; m.yx = (xone->y - zero->y)/lx;
601   m.xy = (zero->x - yone->x)/ly; m.yy = (zero->y - yone->y)/ly;
602   m.x0 = yone->x; m.y0 = yone->y;
603 
604   if (My_Invert_Cairo_Matrix(& m_inv, & m) == 0.0)
605     return NULL;
606 
607   m_inv.x0 += from->x*lx;
608   m_inv.y0 -= from->y*ly;
609 
610   pattern = cairo_pattern_create_for_surface(image);
611   if (cairo_pattern_status(pattern) != CAIRO_STATUS_SUCCESS)
612     return NULL;
613 
614   cairo_pattern_set_matrix(pattern, & m_inv);
615   return pattern;
616 }
617 
My_Cairo_Fill_And_Stroke(BoxGWin * w)618 static void My_Cairo_Fill_And_Stroke(BoxGWin *w) {
619   cairo_t *cr = (cairo_t *) w->ptr;
620   double line_width = MY_DATA(w)->line_state.width;
621   if (line_width > 0.0) {
622     cairo_fill_preserve(cr);
623     MyLineState_Exchange(& MY_DATA(w)->line_state, cr);
624     cairo_stroke(cr);
625     MyLineState_Exchange(& MY_DATA(w)->line_state, cr);
626 
627   } else
628     cairo_fill(cr);
629 }
630 
My_Cairo_Operator_Of_Int(BoxInt v)631 static cairo_operator_t My_Cairo_Operator_Of_Int(BoxInt v) {
632   switch (v) {
633   case  0: return CAIRO_OPERATOR_CLEAR;
634   case  1: return CAIRO_OPERATOR_SOURCE;
635   case  2: return CAIRO_OPERATOR_OVER;
636   case  3: return CAIRO_OPERATOR_IN;
637   case  4: return CAIRO_OPERATOR_OUT;
638   case  5: return CAIRO_OPERATOR_ATOP;
639   case  6: return CAIRO_OPERATOR_DEST;
640   case  7: return CAIRO_OPERATOR_DEST_OVER;
641   case  8: return CAIRO_OPERATOR_DEST_IN;
642   case  9: return CAIRO_OPERATOR_DEST_OUT;
643   case 10: return CAIRO_OPERATOR_DEST_ATOP;
644   case 11: return CAIRO_OPERATOR_XOR;
645   case 12: return CAIRO_OPERATOR_ADD;
646   case 13: return CAIRO_OPERATOR_SATURATE;
647 #if defined(CAIRO_VERSION) && defined(CAIRO_VERSION_ENCODE)
648 #  if CAIRO_VERSION > CAIRO_VERSION_ENCODE(1, 10, 0)
649   case 14: return CAIRO_OPERATOR_MULTIPLY;
650   case 15: return CAIRO_OPERATOR_SCREEN;
651   case 16: return CAIRO_OPERATOR_OVERLAY;
652   case 17: return CAIRO_OPERATOR_DARKEN;
653   case 18: return CAIRO_OPERATOR_LIGHTEN;
654   case 19: return CAIRO_OPERATOR_COLOR_DODGE;
655   case 20: return CAIRO_OPERATOR_COLOR_BURN;
656   case 21: return CAIRO_OPERATOR_HARD_LIGHT;
657   case 22: return CAIRO_OPERATOR_SOFT_LIGHT;
658   case 23: return CAIRO_OPERATOR_DIFFERENCE;
659   case 24: return CAIRO_OPERATOR_EXCLUSION;
660   case 25: return CAIRO_OPERATOR_HSL_HUE;
661   case 26: return CAIRO_OPERATOR_HSL_SATURATION;
662   case 27: return CAIRO_OPERATOR_HSL_COLOR;
663   case 28: return CAIRO_OPERATOR_HSL_LUMINOSITY;
664 #  endif
665 #endif
666   default: return CAIRO_OPERATOR_OVER;
667   }
668 }
669 
670 /** Structure used to pass data to the interator function
671  * My_WinCairo_Interpret_Iter (which is called by My_WinCairo_Interpret).
672  */
673 typedef struct {
674   BoxGWin    *win;
675   BoxGWinMap *map;
676 } MyInterpretData;
677 
678 static BoxGErr
My_WinCairo_Interpret_Iter(BoxGCmd cmd,BoxGCmdSig sig,int num_args,BoxGCmdArgKind * kinds,void ** args,BoxGCmdArg * aux,void * pass)679 My_WinCairo_Interpret_Iter(BoxGCmd cmd, BoxGCmdSig sig, int num_args,
680                            BoxGCmdArgKind *kinds, void **args,
681                            BoxGCmdArg *aux, void *pass) {
682   BoxGWin *w = ((MyInterpretData *) pass)->win;
683   BoxGWinMap *map = ((MyInterpretData *) pass)->map;
684   cairo_t *cr = (cairo_t *) w->ptr;
685   size_t i;
686 
687   /* We first transform the arguments */
688   for (i = 0; i < num_args; i++) {
689     void *arg_in = args[i];
690     BoxGCmdArg *arg_out = & aux[i];
691 
692     switch (kinds[i]) {
693     case BOXGCMDARGKIND_POINT:
694       BoxGWinMap_Map_Point(map, & arg_out->p, (BoxPoint *) arg_in);
695       args[i] = & arg_out->p;
696       break;
697 
698     case BOXGCMDARGKIND_VECTOR:
699       BoxGWinMap_Map_Vector(map, & arg_out->p, (BoxPoint *) arg_in);
700       args[i] = & arg_out->p;
701       break;
702 
703     case BOXGCMDARGKIND_WIDTH:
704       BoxGWinMap_Map_Width(map, & arg_out->w, (BoxReal *) arg_in);
705       args[i] = & arg_out->w;
706       break;
707 
708     default:
709       break;
710     }
711   }
712 
713   switch (cmd) {
714   case BOXGCMD_SAVE:
715     cairo_save(cr);
716     return BOXGERR_NO_ERR;
717 
718   case BOXGCMD_RESTORE:
719     cairo_restore(cr);
720     return BOXGERR_NO_ERR;
721 
722   case BOXGCMD_SET_ANTIALIAS:
723     {
724       BoxInt *arg1 = args[0];
725       cairo_antialias_t v;
726       switch(*arg1) {
727       default:
728       case 0: v = CAIRO_ANTIALIAS_DEFAULT; break;
729       case 1: v = CAIRO_ANTIALIAS_NONE; break;
730       case 2: v = CAIRO_ANTIALIAS_GRAY; break;
731       case 3: v = CAIRO_ANTIALIAS_SUBPIXEL; break;
732       }
733       cairo_set_antialias(cr, v);
734       return BOXGERR_NO_ERR;
735     }
736     break;
737 
738   case BOXGCMD_MOVE_TO:
739     {
740       BoxPoint *arg1 = args[0];
741       cairo_move_to(cr, arg1->x, arg1->y);
742       return BOXGERR_NO_ERR;
743     }
744     break;
745 
746   case BOXGCMD_LINE_TO:
747     {
748       BoxPoint *arg1 = args[0];
749       cairo_line_to(cr, arg1->x, arg1->y);
750       return BOXGERR_NO_ERR;
751     }
752     break;
753 
754   case BOXGCMD_CURVE_TO:
755     {
756       BoxPoint *arg1 = args[0], *arg2 = args[1], *arg3 = args[2];
757       cairo_curve_to(cr, arg1->x, arg1->y, arg2->x, arg2->y, arg3->x, arg3->y);
758       return BOXGERR_NO_ERR;
759     }
760     break;
761 
762   case BOXGCMD_CLOSE_PATH:
763     cairo_close_path(cr);
764     return BOXGERR_NO_ERR;
765 
766   case BOXGCMD_NEW_PATH:
767     cairo_new_path(cr);
768     return BOXGERR_NO_ERR;
769 
770   case BOXGCMD_NEW_SUB_PATH:
771     cairo_new_sub_path(cr);
772     return BOXGERR_NO_ERR;
773 
774   case BOXGCMD_STROKE:
775     cairo_stroke(cr);
776     return BOXGERR_NO_ERR;
777 
778   case BOXGCMD_STROKE_PRESERVE:
779     cairo_stroke_preserve(cr);
780     return BOXGERR_NO_ERR;
781 
782   case BOXGCMD_FILL:
783     cairo_fill(cr);
784     return BOXGERR_NO_ERR;
785 
786   case BOXGCMD_FILL_PRESERVE:
787     cairo_fill_preserve(cr);
788     return BOXGERR_NO_ERR;
789 
790   case BOXGCMD_CLIP:
791     cairo_clip(cr);
792     return BOXGERR_NO_ERR;
793 
794   case BOXGCMD_CLIP_PRESERVE:
795     cairo_clip_preserve(cr);
796     return BOXGERR_NO_ERR;
797 
798   case BOXGCMD_RESET_CLIP:
799     cairo_reset_clip(cr);
800     return BOXGERR_NO_ERR;
801 
802   case BOXGCMD_PUSH_GROUP:
803     cairo_push_group(cr);
804     return BOXGERR_NO_ERR;
805 
806   case BOXGCMD_POP_GROUP_TO_SOURCE:
807     cairo_pop_group_to_source(cr);
808     return BOXGERR_NO_ERR;
809 
810   case BOXGCMD_SET_OPERATOR:
811     {
812       BoxInt *arg1 = args[0];
813       cairo_set_operator(cr, My_Cairo_Operator_Of_Int(*arg1));
814       return BOXGERR_NO_ERR;
815     }
816     break;
817 
818   case BOXGCMD_PAINT:
819     cairo_paint(cr);
820     return BOXGERR_NO_ERR;
821 
822   case BOXGCMD_PAINT_WITH_ALPHA:
823     {
824       BoxReal *arg1 = args[0];
825       cairo_paint_with_alpha(cr, *arg1);
826       return BOXGERR_NO_ERR;
827     }
828     return BOXGERR_NO_ERR;
829 
830   case BOXGCMD_COPY_PAGE:
831     cairo_copy_page(cr);
832     return BOXGERR_NO_ERR;
833 
834   case BOXGCMD_SHOW_PAGE:
835     cairo_show_page(cr);
836     return BOXGERR_NO_ERR;
837 
838   case BOXGCMD_SET_LINE_WIDTH:
839     {
840       BoxReal *arg1 = args[0];
841       cairo_set_line_width(cr, *arg1);
842       return BOXGERR_NO_ERR;
843     }
844     break;
845 
846   case BOXGCMD_SET_LINE_CAP:
847     {
848       BoxInt *arg1 = args[0];
849       cairo_line_cap_t v;
850       switch(*arg1) {
851       default:
852       case 0: v = CAIRO_LINE_CAP_BUTT; break;
853       case 1: v = CAIRO_LINE_CAP_ROUND; break;
854       case 2: v = CAIRO_LINE_CAP_SQUARE; break;
855       }
856       cairo_set_line_cap(cr, v);
857       return BOXGERR_NO_ERR;
858     }
859     break;
860 
861   case BOXGCMD_SET_LINE_JOIN:
862     {
863       BoxInt *arg1 = args[0];
864       cairo_line_join_t v;
865       switch(*arg1) {
866       default:
867       case 0: v = CAIRO_LINE_JOIN_MITER; break;
868       case 1: v = CAIRO_LINE_JOIN_ROUND; break;
869       case 2: v = CAIRO_LINE_JOIN_BEVEL; break;
870       }
871       cairo_set_line_join(cr, v);
872       return BOXGERR_NO_ERR;
873     }
874     break;
875 
876   case BOXGCMD_SET_MITER_LIMIT:
877     {
878       BoxReal *arg1 = args[0];
879       cairo_set_miter_limit(cr, *arg1);
880       return BOXGERR_NO_ERR;
881     }
882 
883   case BOXGCMD_SET_DASH:
884     {
885       int num_dashes = num_args - 1;
886       double offset = *((BoxReal *) args[0]),
887              *dashes = NULL;
888 
889       assert(num_args >= 1);
890 
891       if (num_dashes > 0) {
892         size_t i;
893         dashes = Box_Mem_Safe_Alloc(num_dashes*sizeof(double));
894         for (i = 0; i < num_dashes; i++)
895           dashes[i] = *((BoxReal *) args[1 + i]);
896 
897       }
898 
899       cairo_set_dash(cr, dashes, num_dashes, offset);
900       Box_Mem_Free(dashes);
901       return BOXGERR_NO_ERR;
902     }
903 
904   case BOXGCMD_SET_FILL_RULE:
905     {
906       BoxInt *arg1 = args[0];
907       cairo_fill_rule_t v;
908       switch(*arg1) {
909       default:
910       case 0: v = CAIRO_FILL_RULE_WINDING; break;
911       case 1: v = CAIRO_FILL_RULE_EVEN_ODD; break;
912       }
913       cairo_set_fill_rule(cr, v);
914       return BOXGERR_NO_ERR;
915     }
916     break;
917 
918   case BOXGCMD_SET_SOURCE_RGBA:
919     {
920       BoxReal *arg1 = args[0], *arg2 = args[1],
921               *arg3 = args[2], *arg4 = args[3];
922       cairo_set_source_rgba(cr, *arg1, *arg2, *arg3, *arg4);
923       return BOXGERR_NO_ERR;
924     }
925     break;
926 
927   case BOXGCMD_TEXT_PATH:
928     {
929       BoxStr *s = args[0];
930       char *text = BoxStr_To_C_String(s);
931       if (text != NULL) {
932         cairo_text_path(cr, text);
933         Box_Mem_Free(text);
934         return BOXGERR_NO_ERR;
935       }
936     }
937     break;
938 
939   case BOXGCMD_TRANSLATE:
940     {
941       BoxPoint *arg1 = args[1];
942       cairo_translate(cr, arg1->x, arg1->y);
943       return BOXGERR_NO_ERR;
944     }
945     break;
946 
947   case BOXGCMD_SCALE:
948     {
949       BoxPoint *arg1 = args[1];
950       cairo_scale(cr, arg1->x, arg1->y);
951       return BOXGERR_NO_ERR;
952     }
953     break;
954 
955   case BOXGCMD_ROTATE:
956     {
957       BoxReal *arg1 = args[0];
958       cairo_rotate(cr, *arg1);
959       return BOXGERR_NO_ERR;
960     }
961     break;
962 
963   case BOXGCMD_MASK:
964     if (MY_DATA(w)->pattern != NULL)
965       cairo_mask(cr, MY_DATA(w)->pattern);
966     break;
967 
968   case BOXGCMD_PATTERN_CREATE_RGB:
969     {
970       BoxReal *arg1 = args[0], *arg2 = args[1], *arg3 = args[2];
971       if (MY_DATA(w)->pattern != NULL)
972         cairo_pattern_destroy(MY_DATA(w)->pattern);
973 
974       MY_DATA(w)->pattern =
975         cairo_pattern_create_rgb(*arg1, *arg2, *arg3);
976       return BOXGERR_NO_ERR;
977     }
978     break;
979 
980   case BOXGCMD_PATTERN_CREATE_RGBA:
981     {
982       BoxReal *arg1 = args[0], *arg2 = args[1],
983               *arg3 = args[2], *arg4 = args[3];
984       if (MY_DATA(w)->pattern != NULL)
985         cairo_pattern_destroy(MY_DATA(w)->pattern);
986 
987       MY_DATA(w)->pattern =
988         cairo_pattern_create_rgba(*arg1, *arg2, *arg3, *arg4);
989       return BOXGERR_NO_ERR;
990     }
991     break;
992 
993   case BOXGCMD_PATTERN_CREATE_LINEAR:
994     {
995       BoxPoint *arg1 = args[0], *arg2 = args[1];
996       if (MY_DATA(w)->pattern != NULL)
997         cairo_pattern_destroy(MY_DATA(w)->pattern);
998 
999       MY_DATA(w)->pattern =
1000         cairo_pattern_create_linear(arg1->x, arg1->y, arg2->x, arg2->y);
1001       return BOXGERR_NO_ERR;
1002     }
1003     break;
1004 
1005   case BOXGCMD_PATTERN_CREATE_RADIAL:
1006     {
1007       BoxPoint *arg1 = args[0], *arg2 = args[1],
1008                *arg3 = args[2], *arg4 = args[3];
1009       BoxReal *arg5 = args[4], *arg6 = args[5];
1010 
1011       if (MY_DATA(w)->pattern != NULL)
1012         cairo_pattern_destroy(MY_DATA(w)->pattern);
1013 
1014       MY_DATA(w)->pattern =
1015         My_Cairo_Pattern_Create_Radial(arg1, arg2, arg3, arg4, *arg5, *arg6);
1016       return (MY_DATA(w)->pattern != NULL) ?
1017         BOXGERR_NO_ERR : BOXGERR_CAIRO_PATTERN_ERR;
1018     }
1019     break;
1020 
1021   case BOXGCMD_PATTERN_CREATE_IMAGE:
1022     {
1023       BoxPoint *arg1 = args[0], *arg2 = args[1],
1024                *arg3 = args[2], *arg4 = args[3];
1025       BoxStr *arg5 = args[4];
1026       char *filename = BoxStr_To_C_String(arg5);
1027       if (filename != NULL) {
1028         MY_DATA(w)->pattern =
1029           My_Cairo_Pattern_Create_Image(cr, arg1, arg2, arg3,
1030                                         arg4, filename);
1031         Box_Mem_Free(filename);
1032         return (MY_DATA(w)->pattern != NULL) ?
1033           BOXGERR_NO_ERR : BOXGERR_CAIRO_PATTERN_ERR;
1034 
1035       } else
1036         return BOXGERR_CMD_EXEC;
1037     }
1038     break;
1039 
1040   case BOXGCMD_PATTERN_ADD_COLOR_STOP_RGB:
1041     {
1042       BoxReal *arg1 = args[0], *arg2 = args[1], *arg3 = args[2],
1043               *arg4 = args[3];
1044       if (MY_DATA(w)->pattern != NULL)
1045         cairo_pattern_add_color_stop_rgb(MY_DATA(w)->pattern,
1046                                          *arg1, *arg2, *arg3, *arg4);
1047       return BOXGERR_NO_ERR;
1048     }
1049     break;
1050 
1051   case BOXGCMD_PATTERN_ADD_COLOR_STOP_RGBA:
1052     {
1053       BoxReal *arg1 = args[0], *arg2 = args[1], *arg3 = args[2],
1054               *arg4 = args[3], *arg5 = args[4];
1055       if (MY_DATA(w)->pattern != NULL)
1056         cairo_pattern_add_color_stop_rgba(MY_DATA(w)->pattern, *arg1, *arg2,
1057                                           *arg3, *arg4, *arg5);
1058       return BOXGERR_NO_ERR;
1059     }
1060     break;
1061 
1062   case BOXGCMD_PATTERN_SET_EXTEND:
1063     {
1064       BoxInt *arg1 = args[0];
1065       cairo_extend_t v;
1066       switch(*arg1) {
1067       default:
1068       case 0: v = CAIRO_EXTEND_NONE; break;
1069       case 1: v = CAIRO_EXTEND_REPEAT; break;
1070       case 2: v = CAIRO_EXTEND_REFLECT; break;
1071       case 3: v = CAIRO_EXTEND_PAD; break;
1072       }
1073       cairo_pattern_set_extend(MY_DATA(w)->pattern, v);
1074       return BOXGERR_NO_ERR;
1075     }
1076     break;
1077 
1078   case BOXGCMD_PATTERN_SET_FILTER:
1079     {
1080       BoxInt *arg1 = args[0];
1081       cairo_filter_t v;
1082       switch(*arg1) {
1083       default:
1084       case 0: v = CAIRO_FILTER_FAST; break;
1085       case 1: v = CAIRO_FILTER_GOOD; break;
1086       case 2: v = CAIRO_FILTER_BEST; break;
1087       case 3: v = CAIRO_FILTER_NEAREST; break;
1088       case 4: v = CAIRO_FILTER_BILINEAR; break;
1089       case 5: v = CAIRO_FILTER_GAUSSIAN; break;
1090       }
1091       cairo_pattern_set_filter(MY_DATA(w)->pattern, v);
1092       return BOXGERR_NO_ERR;
1093     }
1094     break;
1095 
1096   case BOXGCMD_PATTERN_DESTROY:
1097     if (MY_DATA(w)->pattern != NULL) {
1098       cairo_pattern_destroy(MY_DATA(w)->pattern);
1099       MY_DATA(w)->pattern = NULL;
1100     }
1101     return BOXGERR_NO_ERR;
1102 
1103   case BOXGCMD_SET_SOURCE:
1104     if (MY_DATA(w)->pattern != NULL)
1105       cairo_set_source(cr, MY_DATA(w)->pattern);
1106     return BOXGERR_NO_ERR;
1107 
1108   case BOXGCMD_EXT_SET_AUTO_BBOX:
1109   case BOXGCMD_EXT_UNSET_BBOX:
1110     return BOXGERR_NO_ERR;
1111 
1112   case BOXGCMD_EXT_JOINARC_TO:
1113     {
1114       BoxPoint *arg1 = args[0], *arg2 = args[1], *arg3 = args[2];
1115       My_Cairo_JoinArc(cr, arg1, arg2, arg3);
1116       return BOXGERR_NO_ERR;
1117     }
1118     break;
1119 
1120   case BOXGCMD_EXT_ARC_TO:
1121     {
1122       BoxPoint *arg1 = args[0], *arg2 = args[1], *arg3 = args[2];
1123       BoxReal *arg4 = args[3], *arg5 = args[4];
1124       My_Cairo_Arc(cr, arg1, arg2, arg3, *arg4, *arg5);
1125       return BOXGERR_NO_ERR;
1126     }
1127     break;
1128 
1129   case BOXGCMD_EXT_SET_FONT:
1130     {
1131       BoxStr *arg1 = args[0];
1132       char *font = BoxStr_To_C_String(arg1);
1133       if (font != NULL) {
1134         My_Cairo_Set_Font(w, font);
1135         Box_Mem_Free(font);
1136         return BOXGERR_NO_ERR;
1137       }
1138     }
1139     break;
1140 
1141   case BOXGCMD_EXT_TEXT_PATH:
1142     {
1143       BoxPoint *arg1 = args[0], *arg2 = args[1],
1144                *arg3 = args[2], *arg4 = args[3];
1145       BoxStr *arg5 = args[4];
1146       char *text = BoxStr_To_C_String(arg5);
1147       if (text != NULL) {
1148         My_Cairo_Text_Path(w, arg1, arg2, arg3, arg4, text);
1149         Box_Mem_Free(text);
1150         return BOXGERR_NO_ERR;
1151       }
1152     }
1153     break;
1154 
1155   case BOXGCMD_EXT_TRANSFORM:
1156     {
1157       /*BoxPoint *arg1 = args[0], *arg2 = args[1], *arg3 = args[2];*/
1158 
1159       /* this command should change the ref frame to the one which
1160        * has origin in the first point and coordinate (1, 0) at the
1161        * second point and (0, 1) at the third (not an ortogonal frame)
1162        * NOT IMPLEMENTED, YET.
1163        */
1164       return BOXGERR_NO_ERR;
1165     }
1166     break;
1167 
1168   case BOXGCMD_EXT_BORDER_EXCHANGE:
1169     MyLineState_Exchange(& MY_DATA(w)->line_state, cr);
1170     return BOXGERR_NO_ERR;
1171 
1172   case BOXGCMD_EXT_BORDER_SAVE:
1173     MyCairoWinData_LineState_Push(MY_DATA(w));
1174     return BOXGERR_NO_ERR;
1175 
1176   case BOXGCMD_EXT_BORDER_RESTORE:
1177     MyCairoWinData_LineState_Pop(MY_DATA(w));
1178     return BOXGERR_NO_ERR;
1179 
1180   case BOXGCMD_EXT_FILL_AND_STROKE:
1181     My_Cairo_Fill_And_Stroke(w);
1182     return BOXGERR_NO_ERR;
1183 
1184   default:
1185     return BOXGERR_CMD_EXEC;
1186   }
1187 
1188   return BOXGERR_CMD_EXEC;
1189 }
1190 
1191 
My_WinCairo_Interpret(BoxGWin * w,BoxGObj * obj,BoxGWinMap * map)1192 static BoxTask My_WinCairo_Interpret(BoxGWin *w, BoxGObj *obj, BoxGWinMap *map) {
1193   BoxGWinMap my_map;
1194   BoxGMatrix
1195     *user_mx = BoxGWinMap_Get_Matrix(map),
1196     *my_mx = BoxGWinMap_Get_Matrix(& my_map);
1197 
1198   MyInterpretData data;
1199   data.win = w;
1200   data.map = & my_map;
1201 
1202   my_mx->m11 = w->resx; my_mx->m22 = w->resy;
1203   my_mx->m12 = my_mx->m21 = 0.0;
1204   my_mx->m13 = -w->ltx*w->resx;
1205   my_mx->m23 = -w->lty*w->resy;
1206 
1207   BoxGMatrix_Mul(my_mx, my_mx, user_mx);
1208   BoxGWinMap_Compute_Width_Transform(& my_map);
1209 
1210   return BoxGCmdIter_Iter(My_WinCairo_Interpret_Iter, obj, & data);
1211 }
1212 
1213 
1214 
1215 
My_WinCairo_Create_Path(BoxGWin * w)1216 static void My_WinCairo_Create_Path(BoxGWin *w) {
1217   beginning_of_path = 1;
1218 }
1219 
My_WinCairo_Begin_Drawing(BoxGWin * w)1220 static void My_WinCairo_Begin_Drawing(BoxGWin *w) {}
1221 
My_WinCairo_Draw_Path(BoxGWin * w,DrawStyle * style)1222 static void My_WinCairo_Draw_Path(BoxGWin *w, DrawStyle *style) {
1223   cairo_t *cr = (cairo_t *) w->ptr;
1224   int do_fill, do_clip, do_even_odd, do_border = (style->bord_width > 0.0);
1225 
1226   if ( ! beginning_of_path ) {
1227     BoxReal scale = style->scale;
1228 
1229     switch(style->fill_style) {
1230     case FILLSTYLE_VOID:   do_fill = do_clip = do_even_odd = 0; break;
1231     case FILLSTYLE_PLAIN:  do_fill = 1; do_clip = 0; do_even_odd = 0; break;
1232     case FILLSTYLE_EO:     do_fill = 1; do_clip = 0; do_even_odd = 1; break;
1233     case FILLSTYLE_CLIP:   do_fill = 0; do_clip = 1; do_even_odd = 0; break;
1234     case FILLSTYLE_EOCLIP: do_fill = 0; do_clip = 1; do_even_odd = 1; break;
1235     default:
1236       do_fill = 1; do_clip = 0; do_even_odd = 1;
1237       g_warning("Unsupported drawing style: using even-odd fill algorithm!");
1238       break;
1239     }
1240 
1241     cairo_set_fill_rule(cr, do_even_odd ? CAIRO_FILL_RULE_EVEN_ODD
1242                                         : CAIRO_FILL_RULE_WINDING);
1243     if (do_border) {
1244       Color *c = & style->bord_color;
1245       BoxReal border = MY_REAL(w, scale*style->bord_width);
1246       cairo_line_join_t lj;
1247       cairo_line_cap_t lc;
1248 
1249       switch(style->bord_join_style) {
1250       case JOIN_STYLE_MITER: lj = CAIRO_LINE_JOIN_MITER; break;
1251       case JOIN_STYLE_ROUND: lj = CAIRO_LINE_JOIN_ROUND; break;
1252       case JOIN_STYLE_BEVEL: lj = CAIRO_LINE_JOIN_BEVEL; break;
1253       default: lj = CAIRO_LINE_JOIN_ROUND; break;
1254       }
1255 
1256       switch(style->bord_cap) {
1257       case CAP_STYLE_BUTT:   lc = CAIRO_LINE_CAP_BUTT; break;
1258       case CAP_STYLE_ROUND:  lc = CAIRO_LINE_CAP_ROUND; break;
1259       case CAP_STYLE_SQUARE: lc = CAIRO_LINE_CAP_SQUARE; break;
1260       default: lc = CAIRO_LINE_CAP_BUTT; break;
1261       }
1262 
1263       if (do_fill) cairo_fill_preserve(cr);
1264       if (do_clip) cairo_clip_preserve(cr);
1265       cairo_save(cr);
1266       cairo_set_source_rgba(cr, c->r, c->g, c->b, c->a);
1267       cairo_set_line_width(cr, border);
1268       cairo_set_line_join(cr, lj);
1269       cairo_set_line_cap(cr, lc);
1270       if (lj == CAIRO_LINE_JOIN_MITER) {
1271         BoxReal miter_limit = MY_REAL(w, scale*style->bord_miter_limit);
1272         cairo_set_miter_limit(cr, miter_limit);
1273       }
1274       if (style->bord_num_dashes > 0) {
1275         BoxInt num_dashes = style->bord_num_dashes,
1276             size_dashes = num_dashes*sizeof(BoxReal);
1277         BoxReal *dashes = (BoxReal *) malloc(size_dashes);
1278         if (dashes != (BoxReal *) NULL) {
1279           BoxInt i;
1280           BoxReal dash_offset = MY_REAL(w, scale*style->bord_dash_offset);
1281           for(i=0; i<num_dashes; i++)
1282             dashes[i] = MY_REAL(w, scale*style->bord_dashes[i]);
1283           cairo_set_dash(cr, dashes, num_dashes, dash_offset);
1284           free(dashes);
1285         }
1286       }
1287       cairo_stroke(cr);
1288       cairo_restore(cr);
1289 
1290     } else {
1291       if (do_fill) cairo_fill(cr);
1292       if (do_clip) cairo_clip(cr);
1293     }
1294   }
1295 }
1296 
My_WinCairo_Set_Fg_Color(BoxGWin * w,Color * c)1297 static void My_WinCairo_Set_Fg_Color(BoxGWin *w, Color *c) {
1298   cairo_t *cr = (cairo_t *) w->ptr;
1299   cairo_set_source_rgba(cr, c->r, c->g, c->b, c->a);
1300 }
1301 
My_WinCairo_Set_Gradient(BoxGWin * w,ColorGrad * cg)1302 static void My_WinCairo_Set_Gradient(BoxGWin *w, ColorGrad *cg) {
1303   cairo_t *cr = (cairo_t *) w->ptr;
1304   cairo_pattern_t *p;
1305   cairo_matrix_t m, m_inv;
1306   BoxPoint p1, p2, ref1, ref2;
1307   BoxInt i;
1308 
1309   switch(cg->type) {
1310   case COLOR_GRAD_TYPE_LINEAR:
1311     My_Map_Point(w, & p1, & cg->point1);
1312     My_Map_Point(w, & p2, & cg->point2);
1313     p = cairo_pattern_create_linear(p1.x, p1.y, p2.x, p2.y);
1314     break;
1315 
1316   case COLOR_GRAD_TYPE_RADIAL:
1317     My_Map_Point(w, & p1, & cg->point1);
1318     My_Map_Point(w, & p2, & cg->point2);
1319     My_Map_Point(w, & ref1, & cg->ref1);
1320     My_Map_Point(w, & ref2, & cg->ref2);
1321     ref1.x -= p1.x; ref1.y -= p1.y;
1322     ref2.x -= p1.x; ref2.y -= p1.y;
1323 
1324     m.xx = ref1.x; m.yx = ref1.y;
1325     m.xy = ref2.x; m.yy = ref2.y;
1326     m.x0 = p1.x; m.y0 = p1.y;
1327     if (My_Invert_Cairo_Matrix(& m_inv, & m) == 0.0) {
1328       g_warning("wincairo_rgradient: gradient matrix is non invertible. "
1329                 "Ignoring gradient setting!");
1330       return;
1331     }
1332 
1333     p = cairo_pattern_create_radial(0.0, 0.0, cg->radius1,
1334                                     p2.x - p1.x, p2.y - p1.y, cg->radius2);
1335     cairo_pattern_set_matrix(p, & m_inv);
1336     break;
1337 
1338   default:
1339     g_warning("Unknown color gradient type. Ignoring gradient setting.");
1340     return;
1341   }
1342 
1343   cairo_pattern_set_extend(p, cg->extend);
1344   for (i = 0; i < cg->num_items; i++) {
1345     ColorGradItem *cgi = & cg->items[i];
1346     Color *c = & cgi->color;
1347     cairo_pattern_add_color_stop_rgba(p, cgi->position,
1348                                       c->r, c->g, c->b, c->a);
1349   }
1350   cairo_set_source(cr, p);
1351   cairo_pattern_destroy(p);
1352 }
1353 
My_WinCairo_Add_Line_Path(BoxGWin * w,BoxPoint * a,BoxPoint * b)1354 static void My_WinCairo_Add_Line_Path(BoxGWin *w, BoxPoint *a, BoxPoint *b) {
1355   cairo_t *cr = (cairo_t *) w->ptr;
1356   MY_2POINTS(w, a, b);
1357 
1358   int continuing = same_points(a, & previous),
1359       length_zero = same_points(a, b);
1360 
1361   if (continuing && length_zero) return;
1362 
1363   if (beginning_of_path) {
1364     cairo_new_path(cr);
1365     beginning_of_path = 0;
1366     continuing = 0;
1367   }
1368 
1369   if (!continuing) cairo_move_to(cr, a->x, a->y);
1370 
1371   cairo_line_to(cr, b->x, b->y);
1372   previous = *b;
1373 }
1374 
My_WinCairo_Add_Join_Path(BoxGWin * w,BoxPoint * a,BoxPoint * b,BoxPoint * c)1375 static void My_WinCairo_Add_Join_Path(BoxGWin *w,
1376                                       BoxPoint *a, BoxPoint *b, BoxPoint *c) {
1377   cairo_t *cr = (cairo_t *) w->ptr;
1378   BoxPoint *first = a, *last = c;
1379   MY_3POINTS(w, a, b, c);
1380 
1381   if (same_points(a, c))
1382     return;
1383 
1384   else if (same_points(a, b) || same_points(b, c)) {
1385     My_WinCairo_Add_Line_Path(w, first, last);
1386     return;
1387 
1388   } else {
1389     if (beginning_of_path) {
1390       cairo_new_path(cr);
1391       beginning_of_path = 0;
1392     }
1393 
1394     My_Cairo_JoinArc(cr, a, b, c);
1395     previous = *c;
1396   }
1397 }
1398 
My_WinCairo_Close_Path(BoxGWin * w)1399 static void My_WinCairo_Close_Path(BoxGWin *w) {
1400   cairo_t *cr = (cairo_t *) w->ptr;
1401   if (!beginning_of_path)
1402     cairo_close_path(cr);
1403 }
1404 
My_WinCairo_Add_Circle_Path(BoxGWin * w,BoxPoint * ctr,BoxPoint * a,BoxPoint * b)1405 static void My_WinCairo_Add_Circle_Path(BoxGWin *w,
1406                                         BoxPoint *ctr, BoxPoint *a, BoxPoint *b) {
1407   cairo_t *cr = (cairo_t *) w->ptr;
1408   MY_3POINTS(w, ctr, a, b);
1409 
1410   if (beginning_of_path)
1411     cairo_new_path(cr);
1412 
1413   cairo_move_to(cr, a->x, a->y);
1414   My_Cairo_Arc(cr, ctr, a, b, 0.0, 2.0*M_PI);
1415   beginning_of_path = 0;
1416 }
1417 
My_WinCairo_Text_Path(BoxGWin * w,BoxPoint * ctr,BoxPoint * right,BoxPoint * up,BoxPoint * from,const char * text)1418 static void My_WinCairo_Text_Path(BoxGWin *w, BoxPoint *ctr, BoxPoint *right,
1419                                   BoxPoint *up, BoxPoint *from,
1420                                   const char *text) {
1421   MY_3POINTS(w, ctr, right, up);
1422   My_Cairo_Text_Path(w, ctr, right, up, from, text);
1423 }
1424 
My_WinCairo_Save_To_File(BoxGWin * w,const char * file_name)1425 static int My_WinCairo_Save_To_File(BoxGWin *w, const char *file_name) {
1426   cairo_t *cr = (cairo_t *) w->ptr;
1427   cairo_surface_t *surface = cairo_get_target(cr);
1428   char *exts[] = {"png", "pdf", (char *) NULL};
1429   enum {EXT_PNG=0};
1430   cairo_status_t status;
1431 
1432   if (w->win_type_str != wincairo_image_id_string)
1433     return 1;
1434 
1435   switch(file_extension(exts, file_name)) {
1436   default:
1437     g_warning("Unrecognized extension: using PNG!");
1438   case EXT_PNG:
1439 #ifdef CAIRO_HAS_PNG_FUNCTIONS
1440     status = cairo_surface_write_to_png(surface, file_name);
1441 #else
1442     g_error("Cairo was not compiled with PNG support!");
1443     return 0;
1444 #endif
1445   }
1446 
1447   switch(status) {
1448   case CAIRO_STATUS_SUCCESS:
1449     return 1;
1450 
1451   default:
1452     g_error("Cannot save the window!");
1453     return 0;
1454   }
1455 }
1456 
1457 /** Set the default methods to the cairo windows */
My_WinCairo_Repair(BoxGWin * w)1458 static void My_WinCairo_Repair(BoxGWin *w) {
1459   BoxGWin_Block(w);
1460   w->interpret = My_WinCairo_Interpret;
1461   w->save_to_file = My_WinCairo_Save_To_File;
1462   w->finish = My_WinCairo_Finish;
1463   w->create_path = My_WinCairo_Create_Path;
1464   w->begin_drawing = My_WinCairo_Begin_Drawing;
1465   w->draw_path = My_WinCairo_Draw_Path;
1466   w->set_fg_color = My_WinCairo_Set_Fg_Color;
1467   w->set_gradient = My_WinCairo_Set_Gradient;
1468   w->add_line_path = My_WinCairo_Add_Line_Path;
1469   w->add_join_path = My_WinCairo_Add_Join_Path;
1470   w->close_path = My_WinCairo_Close_Path;
1471   w->add_circle_path = My_WinCairo_Add_Circle_Path;
1472   w->set_font = My_Cairo_Set_Font;
1473   w->add_text_path = My_WinCairo_Text_Path;
1474 }
1475 
BoxGWin_Create_Cairo(BoxGWinPlan * plan,BoxGErr * err)1476 BoxGWin *BoxGWin_Create_Cairo(BoxGWinPlan *plan, BoxGErr *err) {
1477   BoxGWin *w;
1478   cairo_surface_t *surface;
1479   cairo_t *cr;
1480   cairo_status_t status;
1481   WT wt;
1482   int numptx = 0, numpty = 0;
1483   int paint_background = 0;
1484   enum {WC_NONE=-1, WC_IMAGE=1, WC_STREAM} win_class;
1485   cairo_format_t format=CAIRO_FORMAT_ARGB32; /* Just to avoid warnings! */
1486   typedef cairo_surface_t *(*StreamSurfaceCreate)(const char *filename,
1487                                                   double width_in_points,
1488                                                   double height_in_points);
1489   StreamSurfaceCreate stream_surface_create = NULL;
1490 
1491   /* If err == NULL, err points to a dummy BoxGErr, which simplifies coding */
1492   if (err != NULL)
1493     *err = BOXGERR_NO_ERR;
1494 
1495   else {
1496     static BoxGErr dummy_err;
1497     err = & dummy_err;
1498   }
1499 
1500   if (!plan->have.type) {
1501     *err = BOXGERR_MISS_WIN_TYPE;
1502     return NULL;
1503   }
1504 
1505   wt = (WT) plan->type;
1506 
1507   /* Allocate space for the BoxGWin object */
1508   w = BoxGWin_Create_Invalid(err);
1509   if (*err)
1510     return NULL;
1511   assert(w != NULL);
1512 
1513   /* Allocate WinCairo-specific data */
1514   assert(MY_DATA(w) == NULL);
1515   MyCairoWinData *wd = Box_Mem_Alloc(sizeof(MyCairoWinData));
1516   if (wd == NULL) {
1517     BoxGWin_Destroy(w);
1518     return NULL;
1519   }
1520   MyCairoWinData_Init(wd);
1521   w->data = wd;
1522 
1523   switch(wt) {
1524   case WT_A1:
1525     win_class = WC_IMAGE;
1526     format = CAIRO_FORMAT_A1;
1527     break;
1528 
1529   case WT_A8:
1530     win_class = WC_IMAGE;
1531     format = CAIRO_FORMAT_A8;
1532     break;
1533 
1534   case WT_RGB24:
1535     win_class = WC_IMAGE;
1536     format = CAIRO_FORMAT_RGB24;
1537     paint_background = 1;
1538     break;
1539 
1540   case WT_ARGB32:
1541     win_class = WC_IMAGE;
1542     format = CAIRO_FORMAT_ARGB32;
1543     break;
1544 
1545   case WT_PS:
1546 #ifdef CAIRO_HAS_PS_SURFACE
1547     stream_surface_create = cairo_ps_surface_create;
1548     win_class = WC_STREAM;
1549     break;
1550 #else
1551     *err = BOXGWIN_CAIRO_MISSES_PS;
1552     return NULL;
1553 #endif
1554 
1555   case WT_EPS:
1556 #ifdef CAIRO_HAS_PS_SURFACE
1557     stream_surface_create = cairo_ps_surface_create;
1558     win_class = WC_STREAM;
1559     break;
1560 #else
1561     *err = BOXGWIN_CAIRO_MISSES_PS;
1562     return NULL;
1563 #endif
1564 
1565   case WT_PDF:
1566 #ifdef CAIRO_HAS_PDF_SURFACE
1567     stream_surface_create = cairo_pdf_surface_create;
1568     win_class = WC_STREAM;
1569     break;
1570 #else
1571     *err = BOXGWIN_CAIRO_MISSES_PDF;
1572     return NULL;
1573 #endif
1574 
1575   case WT_SVG:
1576 #ifdef CAIRO_HAS_SVG_SURFACE
1577     stream_surface_create = cairo_svg_surface_create;
1578     win_class = WC_STREAM;
1579     break;
1580 #else
1581     *err = BOXGWIN_CAIRO_MISSES_PDF;
1582     return NULL;
1583 #endif
1584 
1585   default:
1586     *err = BOXGERR_UNKNOWN_WIN_TYPE;
1587     return NULL;
1588   }
1589 
1590   if (!plan->have.size ) {
1591     *err = BOXGERR_WIN_SIZE_MISSING;
1592     return NULL;
1593   }
1594 
1595   w->lx = plan->size.x;
1596   w->ly = plan->size.y;
1597 
1598   if (plan->have.origin) {
1599     w->ltx = plan->origin.x;
1600     w->lty = plan->origin.y;
1601 
1602   } else {
1603     w->ltx = 0.0;
1604     w->lty = 0.0;
1605   }
1606 
1607   w->rdx = w->ltx + plan->size.x;
1608   w->rdy = w->lty + plan->size.y;
1609 
1610   if (win_class == WC_IMAGE) {
1611     if (!plan->have.resolution) {
1612       *err = BOXGERR_WIN_RES_MISSING;
1613       return NULL;
1614     }
1615 
1616     w->resx = plan->resolution.x * (plan->size.x < 0.0 ? -1.0 : 1.0);
1617     w->resy = plan->resolution.y * (plan->size.y < 0.0 ? -1.0 : 1.0);
1618 
1619     numptx = fabs(plan->size.x * plan->resolution.x);
1620     numpty = fabs(plan->size.y * plan->resolution.y);
1621     surface = cairo_image_surface_create(format, numptx, numpty);
1622     w->win_type_str = wincairo_image_id_string;
1623 
1624  } else if (win_class == WC_STREAM) {
1625     double width, height;
1626 
1627     if (! plan->have.file_name) {
1628       *err = BOXGERR_WIN_FILENAME_MISSING;
1629       return NULL;
1630     }
1631 
1632     /* These quantities are used in the function My_Map_Point (macros
1633      * MY_2POINTS and MY_3POINTS) to scale the coordinates of every point.
1634      * They express the number of postscript units per mm.
1635      */
1636     w->resy = w->resx = 1.0 / (grp_mm_per_inch * grp_inch_per_psunit);
1637 
1638     /* All sizes and coordinates are expressed in mm, we must
1639      * therefore convert the size of the window in postscript units:
1640      * 1 postscript unit (also called points) = 1/72 inch
1641      * 1 inch = 25.4 mm
1642      */
1643     width  = plan->size.x * w->resx;
1644     height = plan->size.y * w->resy;
1645 
1646     if (stream_surface_create == NULL)
1647       return NULL;
1648 
1649     surface = stream_surface_create(plan->file_name, width, height);
1650     w->win_type_str = wincairo_stream_id_string;
1651 
1652     if (wt == WT_EPS) {
1653 #ifdef HAVE_CAIRO_EPS
1654       cairo_ps_surface_set_eps(surface, (cairo_bool_t) 1);
1655 #else
1656       g_warning("This version of Cairo does not support EPS format: "
1657                 "using PS.");
1658 #endif
1659     }
1660 
1661   } else {
1662     *err = BOXGERR_UNEXPECTED;
1663     return NULL;
1664   }
1665 
1666   if (plan->size.y >= 0.0) {
1667     /* In this case, the origin is placed at the bottom of the screen! */
1668     w->lty += plan->size.y;
1669     w->resy = -w->resy;
1670   }
1671 
1672   if (plan->size.x < 0.0) {
1673     /* In this case, the origin is placed at the bottom of the screen! */
1674     w->ltx += plan->size.x;
1675     w->resx = -w->resx;
1676   }
1677 
1678   status = cairo_surface_status(surface);
1679   if (status != CAIRO_STATUS_SUCCESS) {
1680     *err = BOXGERR_CAIRO_SURFACE_ERR;
1681     return NULL;
1682   }
1683 
1684   cr = cairo_create(surface);
1685   status = cairo_status(cr);
1686   if (status != CAIRO_STATUS_SUCCESS) {
1687     *err = BOXGERR_CAIRO_CONTEXT_ERR;
1688     return NULL;
1689   }
1690 
1691   /* If we need white background, we paint the window accordingly. */
1692   if (paint_background && numptx > 0 && numpty > 0) {
1693     cairo_save(cr);
1694     cairo_rectangle(cr, 0.0, 0.0, numptx, numpty);
1695     cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
1696     cairo_fill(cr);
1697     cairo_restore(cr);
1698   }
1699 
1700   w->ptr = (void *) cr;
1701 
1702   /* We now set the Cairo-specific methods to handle the window */
1703   w->quiet = 0;
1704   w->repair = My_WinCairo_Repair;
1705   w->repair(w);
1706 
1707   /* We now allocate Cairo specific data in the Window */
1708 
1709   return w;
1710 }
1711