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