1 /*
2 * gog-renderer.c :
3 *
4 * Copyright (C) 2003-2004 Jody Goldberg (jody@gnome.org)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) version 3.
10 *
11 * This program 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 General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19 * USA
20 */
21 #include <goffice/goffice-config.h>
22 #include <goffice/goffice-debug.h>
23 #include <goffice/graph/gog-renderer.h>
24
25 #include <gsf/gsf-impl-utils.h>
26 #include <gsf/gsf-output-stdio.h>
27
28 #include <glib/gi18n-lib.h>
29
30 #include <pango/pangocairo.h>
31
32 #include <cairo.h>
33 #ifdef CAIRO_HAS_PDF_SURFACE
34 #include <cairo-pdf.h>
35 #endif
36 #ifdef CAIRO_HAS_PS_SURFACE
37 #include <cairo-ps.h>
38 #endif
39 #ifdef CAIRO_HAS_SVG_SURFACE
40 #include <cairo-svg.h>
41 #endif
42
43 #include <math.h>
44
45 /**
46 * SECTION: gog-renderer
47 * @short_description: Rendering
48 *
49 * Note that #GogGraph has a functions for export/rendering, so you do not always
50 * need to use a #GogRenderer directly.
51 */
52
53 enum {
54 RENDERER_PROP_0,
55 RENDERER_PROP_MODEL,
56 RENDERER_PROP_VIEW,
57 RENDERER_PROP_ZOOM
58 };
59 enum {
60 RENDERER_SIGNAL_REQUEST_UPDATE,
61 RENDERER_SIGNAL_LAST
62 };
63 static gulong renderer_signals [RENDERER_SIGNAL_LAST] = { 0, };
64
65 static GObjectClass *parent_klass;
66
67 struct _GogRenderer {
68 GObject base;
69
70 GogGraph *model;
71 GogView *view;
72 double scale, scale_x, scale_y;
73
74 GClosure *font_watcher;
75 gboolean needs_update;
76
77 GOStyle const *cur_style;
78 GSList *style_stack;
79
80 GOLineDashSequence *line_dash;
81
82 GOStyle *grip_style;
83 GOStyle *selection_style;
84
85 int w, h;
86
87 gboolean is_vector;
88
89 cairo_t *cairo;
90 cairo_surface_t *cairo_surface;
91
92 GdkPixbuf *pixbuf;
93
94 gboolean marker_as_surface;
95 cairo_surface_t *marker_surface;
96 double marker_offset;
97 GOMarker *marker;
98 };
99
100 typedef struct {
101 GObjectClass base;
102
103 /* Signals */
104 void (*request_update) (GogRenderer *renderer);
105 } GogRendererClass;
106
107 #define GOG_RENDERER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOG_TYPE_RENDERER, GogRendererClass))
108 #define GOG_IS_RENDERER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOG_TYPE_RENDERER))
109
110 #define GRC_LINEAR_SCALE(w,scale) (go_sub_epsilon (w) <= 0.0 ? GOG_RENDERER_HAIRLINE_WIDTH_PTS : w) * scale
111
112 static double
_grc_line_size(GogRenderer const * rend,double width,gboolean sharp)113 _grc_line_size (GogRenderer const *rend, double width, gboolean sharp)
114 {
115 if (rend->is_vector)
116 return GRC_LINEAR_SCALE (width, rend->scale);
117
118 if (go_sub_epsilon (width) <= 0.) /* cheesy version of hairline */
119 return 1.;
120
121 width *= rend->scale;
122 if (!sharp || width <= 1.)
123 return width;
124
125 return go_fake_round (width);
126 }
127
128 static double
_line_size(GogRenderer const * rend,double width,gboolean sharp)129 _line_size (GogRenderer const *rend, double width, gboolean sharp)
130 {
131 double size = _grc_line_size (rend, width, sharp);
132
133 if (!rend->is_vector && sharp && size < 1.0)
134 return ceil (size);
135
136 return size;
137 }
138
139 static void
_update_dash(GogRenderer * rend)140 _update_dash (GogRenderer *rend)
141 {
142 double size;
143
144 go_line_dash_sequence_free (rend->line_dash);
145 rend->line_dash = NULL;
146
147 if (rend->cur_style == NULL)
148 return;
149
150 size = _line_size (rend, rend->cur_style->line.width, FALSE);
151 rend->line_dash = go_line_dash_get_sequence (rend->cur_style->line.dash_type, size);
152 }
153
154 double
gog_renderer_line_size(GogRenderer const * rend,double width)155 gog_renderer_line_size (GogRenderer const *rend, double width)
156 {
157 return _line_size (rend, width, TRUE);
158 }
159
160 double
gog_renderer_pt2r_x(GogRenderer const * rend,double d)161 gog_renderer_pt2r_x (GogRenderer const *rend, double d)
162 {
163 return d * rend->scale_x;
164 }
165
166 double
gog_renderer_pt2r_y(GogRenderer const * rend,double d)167 gog_renderer_pt2r_y (GogRenderer const *rend, double d)
168 {
169 return d * rend->scale_y;
170 }
171
172 double
gog_renderer_pt2r(GogRenderer const * rend,double d)173 gog_renderer_pt2r (GogRenderer const *rend, double d)
174 {
175 return d * rend->scale;
176 }
177
178 /*****************************************************************************/
179
180 static void
emit_line(GogRenderer * rend,gboolean preserve,GOPathOptions options)181 emit_line (GogRenderer *rend, gboolean preserve, GOPathOptions options)
182 {
183 GOStyle const *style = rend->cur_style;
184 cairo_t *cr = rend->cairo;
185 double width;
186
187 if (!go_style_is_line_visible (style)) {
188 if (!preserve)
189 cairo_new_path (cr);
190 return;
191 }
192
193 width = _grc_line_size (rend, style->line.width, options & GO_PATH_OPTIONS_SNAP_WIDTH);
194 cairo_set_line_width (cr, width);
195 if (rend->line_dash != NULL)
196 cairo_set_dash (cr,
197 rend->line_dash->dash,
198 rend->line_dash->n_dash,
199 rend->line_dash->offset);
200 else
201 cairo_set_dash (cr, NULL, 0, 0);
202
203 cairo_set_source_rgba (cr, GO_COLOR_TO_CAIRO (style->line.color));
204 cairo_set_line_cap (cr, (width <= 2.0 && !rend->is_vector) ?
205 CAIRO_LINE_CAP_SQUARE : CAIRO_LINE_CAP_ROUND);
206
207 if (preserve)
208 cairo_stroke_preserve (cr);
209 else
210 cairo_stroke (cr);
211 }
212
213 static void
emit_fill(GogRenderer * rend,gboolean preserve)214 emit_fill (GogRenderer *rend, gboolean preserve)
215 {
216 go_style_fill (rend->cur_style, rend->cairo, preserve);
217 }
218
219 static void
path_raw_move_to(void * closure,GOPathPoint const * point)220 path_raw_move_to (void *closure, GOPathPoint const *point)
221 {
222 cairo_move_to ((cairo_t *) closure, point->x, point->y);
223 }
224
225 static void
path_raw_line_to(void * closure,GOPathPoint const * point)226 path_raw_line_to (void *closure, GOPathPoint const *point)
227 {
228 cairo_line_to ((cairo_t *) closure, point->x, point->y);
229 }
230
231 static void
path_snap_even_move_to(void * closure,GOPathPoint const * point)232 path_snap_even_move_to (void *closure, GOPathPoint const *point)
233 {
234 cairo_move_to ((cairo_t *) closure,
235 floor (point->x + .5),
236 floor (point->y + .5));
237 }
238
239 static void
path_snap_even_line_to(void * closure,GOPathPoint const * point)240 path_snap_even_line_to (void *closure, GOPathPoint const *point)
241 {
242 cairo_line_to ((cairo_t *) closure,
243 floor (point->x + .5),
244 floor (point->y + .5));
245 }
246
247 static void
path_snap_odd_move_to(void * closure,GOPathPoint const * point)248 path_snap_odd_move_to (void *closure, GOPathPoint const *point)
249 {
250 cairo_move_to ((cairo_t *) closure,
251 floor (point->x) + .5,
252 floor (point->y) + .5);
253 }
254
255 static void
path_snap_odd_line_to(void * closure,GOPathPoint const * point)256 path_snap_odd_line_to (void *closure, GOPathPoint const *point)
257 {
258 cairo_line_to ((cairo_t *) closure,
259 floor (point->x) + .5,
260 floor (point->y) + .5);
261 }
262
263 static void
path_curve_to(void * closure,GOPathPoint const * point0,GOPathPoint const * point1,GOPathPoint const * point2)264 path_curve_to (void *closure,
265 GOPathPoint const *point0,
266 GOPathPoint const *point1,
267 GOPathPoint const *point2)
268 {
269 cairo_curve_to ((cairo_t *) closure,
270 point0->x, point0->y,
271 point1->x, point1->y,
272 point2->x, point2->y);
273 }
274
275 static void
path_close_path(void * closure)276 path_close_path (void *closure)
277 {
278 cairo_close_path ((cairo_t *) closure);
279 }
280
281 static void
path_interpret(GogRenderer * rend,GOPath const * path,double line_width)282 path_interpret (GogRenderer *rend,
283 GOPath const *path,
284 double line_width)
285 {
286 if (rend->is_vector
287 || (go_path_get_options (path) & GO_PATH_OPTIONS_SNAP_COORDINATES) == 0) {
288 go_path_interpret (path, GO_PATH_DIRECTION_FORWARD,
289 path_raw_move_to,
290 path_raw_line_to,
291 path_curve_to,
292 path_close_path,
293 rend->cairo);
294 return;
295 }
296
297 if (((int) (go_fake_ceil (line_width)) % 2 == 0) && (line_width > 1.0 || line_width <= 0.0))
298 go_path_interpret (path, GO_PATH_DIRECTION_FORWARD,
299 path_snap_even_move_to,
300 path_snap_even_line_to,
301 path_curve_to,
302 path_close_path,
303 rend->cairo);
304 else
305 go_path_interpret (path, GO_PATH_DIRECTION_FORWARD,
306 path_snap_odd_move_to,
307 path_snap_odd_line_to,
308 path_curve_to,
309 path_close_path,
310 rend->cairo);
311 }
312
313 typedef struct {
314 cairo_t *cairo;
315 gboolean first_point;
316 } FillPathData;
317
318 static void
fill_path_line_to(void * closure,GOPathPoint const * point)319 fill_path_line_to (void *closure, GOPathPoint const *point)
320 {
321 FillPathData *data = closure;
322
323 if (data->first_point) {
324 cairo_move_to (data->cairo, point->x, point->y);
325 data->first_point = FALSE;
326 }
327 else
328 cairo_line_to (data->cairo, point->x, point->y);
329 }
330
331 static void
fill_path_curve_to(void * closure,GOPathPoint const * point0,GOPathPoint const * point1,GOPathPoint const * point2)332 fill_path_curve_to (void *closure,
333 GOPathPoint const *point0,
334 GOPathPoint const *point1,
335 GOPathPoint const *point2)
336 {
337 FillPathData *data = closure;
338
339 cairo_curve_to (data->cairo,
340 point0->x, point0->y,
341 point1->x, point1->y,
342 point2->x, point2->y);
343 }
344
345 static void
fill_path_close_path(void * closure)346 fill_path_close_path (void *closure)
347 {
348 }
349
350 static void
fill_path_interpret(GogRenderer * rend,GOPath const * path_0,GOPath const * path_1)351 fill_path_interpret (GogRenderer *rend,
352 GOPath const *path_0,
353 GOPath const *path_1)
354 {
355 FillPathData data;
356
357 data.first_point = TRUE;
358 data.cairo = rend->cairo;
359
360 go_path_interpret (path_0,
361 GO_PATH_DIRECTION_FORWARD,
362 fill_path_line_to,
363 fill_path_line_to,
364 fill_path_curve_to,
365 fill_path_close_path,
366 &data);
367 if (path_1 != NULL)
368 go_path_interpret (path_1,
369 GO_PATH_DIRECTION_BACKWARD,
370 fill_path_line_to,
371 fill_path_line_to,
372 fill_path_curve_to,
373 fill_path_close_path,
374 &data);
375 cairo_close_path (rend->cairo);
376 }
377
378 void
gog_renderer_stroke_serie(GogRenderer * renderer,GOPath const * path)379 gog_renderer_stroke_serie (GogRenderer *renderer,
380 GOPath const *path)
381 {
382 GOStyle const *style;
383 GOPathOptions line_options;
384 double width;
385
386 g_return_if_fail (GOG_IS_RENDERER (renderer));
387 g_return_if_fail (renderer->cur_style != NULL);
388 g_return_if_fail (GO_IS_PATH (path));
389
390 style = renderer->cur_style;
391 line_options = go_path_get_options (path);
392 width = _grc_line_size (renderer,
393 style->line.width,
394 line_options & GO_PATH_OPTIONS_SNAP_WIDTH);
395
396 if (go_style_is_line_visible (style)) {
397 path_interpret (renderer, path, width);
398 emit_line (renderer, FALSE, go_path_get_options (path));
399 }
400 }
401
402 void
gog_renderer_fill_serie(GogRenderer * renderer,GOPath const * path,GOPath const * close_path)403 gog_renderer_fill_serie (GogRenderer *renderer,
404 GOPath const *path,
405 GOPath const *close_path)
406 {
407 GOStyle const *style;
408
409 g_return_if_fail (GOG_IS_RENDERER (renderer));
410 g_return_if_fail (renderer->cur_style != NULL);
411 g_return_if_fail (GO_IS_PATH (path));
412
413 style = renderer->cur_style;
414
415 if (go_style_is_fill_visible (style)) {
416 fill_path_interpret (renderer, path, close_path);
417 emit_fill (renderer, FALSE);
418 }
419 }
420
421 static void
_draw_shape(GogRenderer * renderer,GOPath const * path,gboolean fill,gboolean stroke)422 _draw_shape (GogRenderer *renderer, GOPath const *path, gboolean fill, gboolean stroke)
423 {
424 GOStyle const *style;
425 GOPathOptions line_options;
426 double width;
427
428 g_return_if_fail (GOG_IS_RENDERER (renderer));
429 g_return_if_fail (renderer->cur_style != NULL);
430 g_return_if_fail (GO_IS_PATH (path));
431
432 style = renderer->cur_style;
433
434 line_options = go_path_get_options (path);
435 width = stroke ? _grc_line_size (renderer, style->line.width,
436 line_options & GO_PATH_OPTIONS_SNAP_WIDTH) : 0;
437
438 path_interpret (renderer, path, width);
439
440 if (fill)
441 emit_fill (renderer, stroke);
442
443 if (stroke)
444 emit_line (renderer, FALSE, go_path_get_options (path));
445 }
446
447 void
gog_renderer_draw_shape(GogRenderer * renderer,GOPath const * path)448 gog_renderer_draw_shape (GogRenderer *renderer,
449 GOPath const *path)
450 {
451 _draw_shape (renderer, path, TRUE, TRUE);
452 }
453
454 void
gog_renderer_stroke_shape(GogRenderer * renderer,GOPath const * path)455 gog_renderer_stroke_shape (GogRenderer *renderer, GOPath const *path)
456 {
457 _draw_shape (renderer, path, FALSE, TRUE);
458 }
459
460 void
gog_renderer_fill_shape(GogRenderer * renderer,GOPath const * path)461 gog_renderer_fill_shape (GogRenderer *renderer, GOPath const *path)
462 {
463 _draw_shape (renderer, path, TRUE, FALSE);
464 }
465
466 static void
_draw_rotated_shape(GogRenderer * renderer,GOPath const * path,gboolean fill,gboolean stroke,gboolean rotate_bg)467 _draw_rotated_shape (GogRenderer *renderer, GOPath const *path, gboolean fill, gboolean stroke, gboolean rotate_bg)
468 {
469 GOStyle const *style;
470 GOPathOptions line_options;
471 double width;
472
473 g_return_if_fail (GOG_IS_RENDERER (renderer));
474 g_return_if_fail (renderer->cur_style != NULL);
475 g_return_if_fail (GO_IS_PATH (path));
476
477 style = renderer->cur_style;
478
479 line_options = go_path_get_options (path);
480 width = stroke ? _grc_line_size (renderer, style->line.width,
481 line_options & GO_PATH_OPTIONS_SNAP_WIDTH) : 0;
482
483 cairo_save (renderer->cairo);
484 cairo_rotate (renderer->cairo, -renderer->cur_style->text_layout.angle *M_PI / 180.);
485 path_interpret (renderer, path, width);
486
487 if (fill) {
488 if (rotate_bg) {
489 emit_fill (renderer, stroke);
490 cairo_restore (renderer->cairo);
491 } else {
492 cairo_restore (renderer->cairo);
493 emit_fill (renderer, stroke);
494 }
495 } else
496 cairo_restore (renderer->cairo);
497
498 if (stroke)
499 emit_line (renderer, FALSE, go_path_get_options (path));
500 }
501
502 /*****************************************************************************/
503
504 /**
505 * gog_renderer_push_clip:
506 * @rend: #GogRenderer
507 * @path: a #GOPath
508 *
509 * Defines the current clipping region.
510 **/
511
512 void
gog_renderer_push_clip(GogRenderer * rend,GOPath const * path)513 gog_renderer_push_clip (GogRenderer *rend, GOPath const *path)
514 {
515 g_return_if_fail (GOG_IS_RENDERER (rend));
516 g_return_if_fail (GO_IS_PATH (path));
517
518 cairo_save (rend->cairo);
519
520 path_interpret (rend, path, 0);
521
522 cairo_clip (rend->cairo);
523 }
524
525 /**
526 * gog_renderer_push_clip_rectangle:
527 * @rend: #GogRenderer
528 * @x: left coordinate
529 * @y: top coordinate
530 * @w: width of clipping rectangle
531 * @h: height of clipping rectangle
532 *
533 * Defines a rectangular clipping region. For efficient screen rendering,
534 * this function takes care to round the coordinates.
535 **/
536
537 void
gog_renderer_push_clip_rectangle(GogRenderer * rend,double x,double y,double w,double h)538 gog_renderer_push_clip_rectangle (GogRenderer *rend, double x, double y, double w, double h)
539 {
540 GOPath *path;
541
542 path = go_path_new ();
543 if (rend->is_vector)
544 go_path_rectangle (path, x, y, w, h);
545 else {
546 double xx = go_fake_floor (x);
547 double yy = go_fake_floor (y);
548 go_path_rectangle (path, xx, yy,
549 go_fake_ceil (x + w) - xx,
550 go_fake_ceil (y + h) - yy);
551 }
552 gog_renderer_push_clip (rend, path);
553 go_path_free (path);
554 }
555
556 /**
557 * gog_renderer_pop_clip:
558 * @rend: #GogRenderer
559 *
560 * End the current clipping.
561 **/
562
563 void
gog_renderer_pop_clip(GogRenderer * rend)564 gog_renderer_pop_clip (GogRenderer *rend)
565 {
566 g_return_if_fail (GOG_IS_RENDERER (rend));
567
568 cairo_restore (rend->cairo);
569 }
570
571 static void
_draw_circle(GogRenderer * rend,double x,double y,double r,gboolean fill,gboolean stroke)572 _draw_circle (GogRenderer *rend, double x, double y, double r, gboolean fill, gboolean stroke)
573 {
574 GOStyle const *style;
575 GOPath *path;
576 gboolean narrow = r < 1.5;
577 double o, o_2;
578
579 g_return_if_fail (GOG_IS_RENDERER (rend));
580 g_return_if_fail (GO_IS_STYLE (rend->cur_style));
581
582 style = rend->cur_style;
583 narrow |= !go_style_is_outline_visible (style);
584
585 path = go_path_new ();
586 go_path_set_options (path, GO_PATH_OPTIONS_SHARP);
587
588 if (!narrow) {
589 o = gog_renderer_line_size (rend, style->line.width);
590 o_2 = o / 2.;
591 } else
592 o = o_2 = 0.;
593
594 go_path_arc (path, x, y, r, r, 0, 2 * M_PI);
595
596 _draw_shape (rend, path, fill, stroke && !narrow);
597
598 go_path_free (path);
599 }
600
601 void
gog_renderer_draw_circle(GogRenderer * rend,double x,double y,double r)602 gog_renderer_draw_circle (GogRenderer *rend, double x, double y, double r)
603 {
604 _draw_circle (rend, x, y, r, TRUE, TRUE);
605 }
606
607 void
gog_renderer_stroke_circle(GogRenderer * rend,double x,double y,double r)608 gog_renderer_stroke_circle (GogRenderer *rend, double x, double y, double r)
609 {
610 _draw_circle (rend, x, y, r, FALSE, TRUE);
611 }
612
613 void
gog_renderer_fill_circle(GogRenderer * rend,double x,double y,double r)614 gog_renderer_fill_circle (GogRenderer *rend, double x, double y, double r)
615 {
616 _draw_circle (rend, x, y, r, TRUE, FALSE);
617 }
618
619 /**
620 * gog_renderer_draw_rectangle:
621 * @rend: a #GogRenderer
622 * @rect: position and extent of rectangle
623 *
624 * A utility routine to build a closed rectangle vpath.
625 *
626 **/
627
628 static void
_draw_rectangle(GogRenderer * rend,GogViewAllocation const * rect,gboolean fill,gboolean stroke)629 _draw_rectangle (GogRenderer *rend, GogViewAllocation const *rect, gboolean fill, gboolean stroke)
630 {
631 GOStyle const *style;
632 GOPath *path;
633 gboolean narrow = (rect->w < 3.) || (rect->h < 3.);
634 double o, o_2;
635
636 g_return_if_fail (GOG_IS_RENDERER (rend));
637 g_return_if_fail (GO_IS_STYLE (rend->cur_style));
638
639 style = rend->cur_style;
640 narrow |= !go_style_is_outline_visible (style);
641
642 path = go_path_new ();
643 go_path_set_options (path, GO_PATH_OPTIONS_SHARP);
644
645 if (!narrow) {
646 o = gog_renderer_line_size (rend, style->line.width);
647 o_2 = o / 2.;
648 } else
649 o = o_2 = 0.;
650
651 go_path_rectangle (path, rect->x + o_2, rect->y + o_2, rect->w - o, rect->h - o);
652
653 _draw_shape (rend, path, fill, stroke && !narrow);
654
655 go_path_free (path);
656 }
657
658 void
gog_renderer_draw_rectangle(GogRenderer * rend,GogViewAllocation const * rect)659 gog_renderer_draw_rectangle (GogRenderer *rend, GogViewAllocation const *rect)
660 {
661 _draw_rectangle (rend, rect, TRUE, TRUE);
662 }
663
664 void
gog_renderer_stroke_rectangle(GogRenderer * rend,GogViewAllocation const * rect)665 gog_renderer_stroke_rectangle (GogRenderer *rend, GogViewAllocation const *rect)
666 {
667 _draw_rectangle (rend, rect, FALSE, TRUE);
668 }
669
670 void
gog_renderer_fill_rectangle(GogRenderer * rend,GogViewAllocation const * rect)671 gog_renderer_fill_rectangle (GogRenderer *rend, GogViewAllocation const *rect)
672 {
673 _draw_rectangle (rend, rect, TRUE, FALSE);
674 }
675
676 static void
_draw_rotated_rectangle(GogRenderer * rend,GogViewAllocation const * rect,gboolean fill,gboolean stroke,gboolean rotate_bg)677 _draw_rotated_rectangle (GogRenderer *rend, GogViewAllocation const *rect, gboolean fill, gboolean stroke, gboolean rotate_bg)
678 {
679 GOStyle const *style;
680 GOPath *path;
681 gboolean narrow = (rect->w < 3.) || (rect->h < 3.);
682 double o, o_2;
683
684 g_return_if_fail (GOG_IS_RENDERER (rend));
685 g_return_if_fail (GO_IS_STYLE (rend->cur_style));
686
687 style = rend->cur_style;
688 narrow |= !go_style_is_outline_visible (style);
689
690 path = go_path_new ();
691 go_path_set_options (path, GO_PATH_OPTIONS_SHARP);
692
693 if (!narrow) {
694 o = gog_renderer_line_size (rend, style->line.width);
695 o_2 = o / 2.;
696 } else
697 o = o_2 = 0.;
698
699 go_path_rectangle (path, 0., 0., rect->w - o, rect->h - o);
700
701 cairo_save (rend->cairo);
702 cairo_translate (rend->cairo, rect->x - o_2, rect->y - o_2);
703 _draw_rotated_shape (rend, path, fill, stroke && !narrow, rotate_bg);
704 cairo_restore (rend->cairo);
705
706 go_path_free (path);
707 }
708
709 void
gog_renderer_draw_rotated_rectangle(GogRenderer * rend,GogViewAllocation const * rect,gboolean rotate_bg)710 gog_renderer_draw_rotated_rectangle (GogRenderer *rend, GogViewAllocation const *rect, gboolean rotate_bg)
711 {
712 _draw_rotated_rectangle (rend, rect, TRUE, TRUE, rotate_bg);
713 }
714
715 /**
716 * gog_renderer_draw_grip:
717 * @renderer: #GogRenderer
718 * @x: x position of grip
719 * @y: y position of grip
720 *
721 * Draw a grip, used for moving/resizing of objects.
722 **/
723
724 void
gog_renderer_draw_grip(GogRenderer * renderer,double x,double y)725 gog_renderer_draw_grip (GogRenderer *renderer, double x, double y)
726 {
727 GogViewAllocation rectangle;
728
729 if (renderer->grip_style == NULL) {
730 GOStyle *style;
731
732 style = go_style_new ();
733 style->line.dash_type = GO_LINE_SOLID;
734 style->line.width = 0.0;
735 style->line.color =
736 style->fill.pattern.back = 0xff000080;
737 style->fill.pattern.pattern = GO_PATTERN_SOLID;
738 style->fill.type = GO_STYLE_FILL_PATTERN;
739 style->interesting_fields = GO_STYLE_FILL | GO_STYLE_OUTLINE;
740
741 renderer->grip_style = style;
742 }
743
744 rectangle.x = x - GOG_RENDERER_GRIP_SIZE;
745 rectangle.y = y - GOG_RENDERER_GRIP_SIZE;
746 rectangle.w = rectangle.h = 2 * GOG_RENDERER_GRIP_SIZE;
747
748 gog_renderer_push_style (renderer, renderer->grip_style);
749
750 gog_renderer_draw_rectangle (renderer, &rectangle);
751
752 gog_renderer_pop_style (renderer);
753 }
754
755 void
gog_renderer_draw_selection_rectangle(GogRenderer * renderer,GogViewAllocation const * rectangle)756 gog_renderer_draw_selection_rectangle (GogRenderer *renderer, GogViewAllocation const *rectangle)
757 {
758 if (renderer->selection_style == NULL) {
759 GOStyle *style;
760
761 style = go_style_new ();
762 style->line.dash_type = GO_LINE_DOT;
763 style->line.width = 0.0;
764 style->line.color = 0x0000ffB0;
765 style->fill.type = GO_STYLE_FILL_NONE;
766 style->interesting_fields = GO_STYLE_OUTLINE;
767
768 renderer->selection_style = style;
769 }
770
771 gog_renderer_push_style (renderer, renderer->selection_style);
772
773 gog_renderer_draw_rectangle (renderer, rectangle);
774
775 gog_renderer_pop_style (renderer);
776 }
777
778 static cairo_surface_t *
_get_marker_surface(GogRenderer * rend)779 _get_marker_surface (GogRenderer *rend)
780 {
781 GOMarker *marker = rend->marker;
782 double width;
783
784 if (rend->marker_surface != NULL)
785 return rend->marker_surface;
786
787 rend->marker_surface = go_marker_create_cairo_surface (marker, rend->cairo, rend->scale, &width, NULL);
788
789 rend->marker_offset = width * 0.5;
790 return rend->marker_surface;
791 }
792
793 /**
794 * gog_renderer_draw_marker:
795 * @rend: #GogRenderer
796 * @x: X-coordinate
797 * @y: Y-coordinate
798 **/
799 void
gog_renderer_draw_marker(GogRenderer * rend,double x,double y)800 gog_renderer_draw_marker (GogRenderer *rend, double x, double y)
801 {
802 cairo_surface_t *surface;
803
804 g_return_if_fail (GOG_IS_RENDERER (rend));
805 g_return_if_fail (rend->cur_style != NULL);
806
807 if (rend->marker == NULL) {
808 if (rend->cur_style->marker.auto_fill_color &&
809 !go_marker_is_closed_shape (rend->cur_style->marker.mark)) {
810 rend->marker = go_marker_dup (rend->cur_style->marker.mark);
811 go_marker_set_fill_color (rend->marker, 0);
812 } else
813 rend->marker = g_object_ref (rend->cur_style->marker.mark);
814 }
815
816 if (rend->is_vector && !rend->marker_as_surface) {
817 go_marker_render (rend->marker, rend->cairo,
818 x, y, rend->scale);
819 return;
820 }
821
822 surface = _get_marker_surface (rend);
823
824 if (surface == NULL)
825 return;
826
827 if (rend->is_vector)
828 cairo_set_source_surface (rend->cairo, surface,
829 x - rend->marker_offset,
830 y - rend->marker_offset);
831 else
832 cairo_set_source_surface (rend->cairo, surface,
833 floor (x - rend->marker_offset),
834 floor (y - rend->marker_offset));
835
836 cairo_paint (rend->cairo);
837 }
838
839 /**
840 * GoJustification:
841 * @GO_JUSTIFY_LEFT: The text is placed at the left edge of the label.
842 * @GO_JUSTIFY_RIGHT: The text is placed at the right edge of the label.
843 * @GO_JUSTIFY_CENTER: The text is placed in the center of the label.
844 * @GO_JUSTIFY_FILL: The text is placed is distributed across the label.
845 *
846 * Used for justifying the text inside multiline label.
847 */
848
849 /**
850 * gog_renderer_draw_gostring:
851 * @rend: #GogRenderer
852 * @str: the #GOString to draw
853 * @pos: #GogViewAllocation
854 * @anchor: #GOAnchorType how to draw relative to @pos
855 * @justification: #GoJustification for multiline string.
856 * @width: if positive, the maximum width to get a multiline string if needed.
857 *
858 * Have @rend draw @layout in the at @pos.{x,y} anchored by the @anchor corner.
859 * If @pos.w or @pos.h are >= 0 then clip the results to less than that size.
860 **/
861
862 void
gog_renderer_draw_gostring(GogRenderer * rend,GOString * str,GogViewAllocation const * pos,GOAnchorType anchor,GoJustification justification,double width)863 gog_renderer_draw_gostring (GogRenderer *rend, GOString *str,
864 GogViewAllocation const *pos, GOAnchorType anchor,
865 GoJustification justification, double width)
866 {
867 PangoLayout *layout;
868 PangoContext *context;
869 cairo_t *cairo;
870 GOGeometryOBR obr;
871 GOGeometryAABR aabr;
872 GOStyle const *style;
873 int iw, ih;
874 PangoAttrList *attr;
875
876 g_return_if_fail (str != NULL);
877 g_return_if_fail (GOG_IS_RENDERER (rend));
878 g_return_if_fail (rend->cur_style != NULL);
879
880 cairo = rend->cairo;
881 style = rend->cur_style;
882
883 /* Note: orig layout may not have been created using cairo! */
884 layout = pango_cairo_create_layout (cairo);
885 context = pango_layout_get_context (layout);
886 pango_layout_set_text (layout, str->str, -1);
887 if (width > 0)
888 pango_layout_set_width (layout, width * PANGO_SCALE / rend->scale);
889 switch (justification) {
890 case GO_JUSTIFY_CENTER:
891 pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
892 break;
893 case GO_JUSTIFY_LEFT:
894 pango_layout_set_alignment (layout, PANGO_ALIGN_LEFT);
895 break;
896 case GO_JUSTIFY_RIGHT:
897 pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT);
898 break;
899 case GO_JUSTIFY_FILL:
900 pango_layout_set_justify (layout, TRUE);
901 break;
902 }
903 attr = go_string_get_markup (str);
904 if (attr) {
905 pango_layout_set_attributes (layout, attr);
906 go_pango_translate_layout (layout);
907 }
908 pango_cairo_context_set_resolution (context, 72.0);
909 pango_layout_set_font_description (layout, style->font.font->desc);
910 pango_layout_get_size (layout, &iw, &ih);
911
912 obr.w = rend->scale * ((double) iw + (double) PANGO_SCALE / 2.0)
913 / (double) PANGO_SCALE;
914 obr.h = rend->scale * ((double) ih + (double) PANGO_SCALE / 2.0)
915 /(double) PANGO_SCALE;
916 obr.alpha = -style->text_layout.angle * M_PI / 180.0;
917 obr.x = pos->x;
918 obr.y = pos->y;
919 go_geometry_OBR_to_AABR (&obr, &aabr);
920
921 switch (anchor) {
922 case GO_ANCHOR_NW: case GO_ANCHOR_W: case GO_ANCHOR_SW:
923 obr.x += aabr.w / 2.0;
924 break;
925 case GO_ANCHOR_NE : case GO_ANCHOR_SE : case GO_ANCHOR_E :
926 obr.x -= aabr.w / 2.0;
927 break;
928 default : break;
929 }
930
931 switch (anchor) {
932 case GO_ANCHOR_NW: case GO_ANCHOR_N: case GO_ANCHOR_NE:
933 obr.y += aabr.h / 2.0;
934 break;
935 case GO_ANCHOR_SE : case GO_ANCHOR_S : case GO_ANCHOR_SW :
936 obr.y -= aabr.h / 2.0;
937 break;
938 default : break;
939 }
940
941 cairo_save (cairo);
942 cairo_set_source_rgba (cairo, GO_COLOR_TO_CAIRO (style->font.color));
943 cairo_move_to (cairo, obr.x - (obr.w / 2.0) * cos (obr.alpha) +
944 (obr.h / 2.0) * sin (obr.alpha),
945 obr.y - (obr.w / 2.0) * sin (obr.alpha) -
946 (obr.h / 2.0) * cos (obr.alpha));
947 cairo_rotate (cairo, obr.alpha);
948 cairo_scale (cairo, rend->scale, rend->scale);
949 pango_cairo_show_layout (cairo, layout);
950 cairo_restore (cairo);
951 g_object_unref (layout);
952 }
953
954
955 /**
956 * gog_renderer_draw_text:
957 * @rend: #GogRenderer
958 * @text: the string to draw
959 * @pos: #GogViewAllocation
960 * @anchor: #GOAnchorType how to draw relative to @pos
961 * @use_markup: wether to use pango markup
962 * @justification: #GoJustification for multiline text.
963 * @width: if positive, the maximum width to get a multiline text if needed.
964 *
965 * Have @rend draw @text in the at @pos.{x,y} anchored by the @anchor corner.
966 * If @pos.w or @pos.h are >= 0 then clip the results to less than that size.
967 **/
968
969 void
gog_renderer_draw_text(GogRenderer * rend,char const * text,GogViewAllocation const * pos,GOAnchorType anchor,gboolean use_markup,GoJustification justification,double width)970 gog_renderer_draw_text (GogRenderer *rend, char const *text,
971 GogViewAllocation const *pos, GOAnchorType anchor,
972 gboolean use_markup, GoJustification justification,
973 double width)
974 {
975 GOString *str;
976 PangoAttrList *attr_list = NULL;
977 char *m_text = NULL;
978
979 g_return_if_fail (GOG_IS_RENDERER (rend));
980 g_return_if_fail (text != NULL);
981
982 if (*text == '\0')
983 return;
984
985 if (use_markup && pango_parse_markup (text, -1, 0,
986 &attr_list, &m_text,
987 NULL, NULL))
988 str = go_string_new_rich_nocopy (m_text, -1, attr_list, NULL);
989 else
990 str = go_string_new (text);
991 gog_renderer_draw_gostring (rend, str, pos, anchor, justification, width);
992 go_string_unref (str);
993 }
994
995 /**
996 * gog_renderer_get_gostring_OBR:
997 * @rend: #GogRenderer
998 * @str: the string to draw
999 * @obr: #GOGeometryOBR to store the Object Bounding Rectangle of @text.
1000 * @max_width: maximum width or -1 for unrestricted
1001 **/
1002 void
gog_renderer_get_gostring_OBR(GogRenderer * rend,GOString * str,GOGeometryOBR * obr,double max_width)1003 gog_renderer_get_gostring_OBR (GogRenderer *rend, GOString *str,
1004 GOGeometryOBR *obr, double max_width)
1005 {
1006 GOStyle const *style;
1007 PangoLayout *layout;
1008 PangoContext *context;
1009 PangoRectangle logical;
1010 cairo_t *cairo;
1011 PangoAttrList *attr;
1012
1013 g_return_if_fail (GOG_IS_RENDERER (rend));
1014 g_return_if_fail (rend->cur_style != NULL);
1015 g_return_if_fail (obr != NULL);
1016
1017 cairo = rend->cairo;
1018 obr->x = obr->y = 0;
1019 if (str->str == NULL || *(str->str) == '\0') {
1020 /* Make sure invisible things don't skew size */
1021 obr->w = obr->h = 0;
1022 obr->alpha = 0;
1023 return;
1024 }
1025
1026 style = rend->cur_style;
1027 layout = pango_cairo_create_layout (cairo);
1028 context = pango_layout_get_context (layout);
1029 pango_layout_set_text (layout, str->str, -1);
1030 if (max_width > 0)
1031 pango_layout_set_width (layout, max_width * PANGO_SCALE / rend->scale);
1032 attr = go_string_get_markup (str);
1033 if (attr) {
1034 pango_layout_set_attributes (layout, attr);
1035 go_pango_translate_layout (layout);
1036 }
1037 pango_cairo_context_set_resolution (context, 72.0);
1038 pango_layout_set_font_description (layout, style->font.font->desc);
1039 pango_layout_get_extents (layout, NULL, &logical);
1040 g_object_unref (layout);
1041
1042 obr->w = rend->scale * ((double) logical.width + (double) PANGO_SCALE / 2.0)
1043 / (double) PANGO_SCALE;
1044 obr->h = rend->scale * ((double) logical.height + (double) PANGO_SCALE / 2.0)
1045 /(double) PANGO_SCALE;
1046
1047 /* Make sure invisible things don't skew size */
1048 if (obr->w == 0)
1049 obr->h = 0;
1050 else if (obr->h == 0)
1051 obr->w = 0;
1052
1053 obr->alpha = - style->text_layout.angle * M_PI / 180.0;
1054 }
1055
1056
1057 /**
1058 * gog_renderer_get_text_OBR:
1059 * @rend: #GogRenderer
1060 * @text: the string to draw
1061 * @use_markup: wether to use pango markup
1062 * @obr: #GOGeometryOBR to store the Object Bounding Rectangle of @text.
1063 * @max_width: maximum width or -1 for unrestricted
1064 **/
1065 void
gog_renderer_get_text_OBR(GogRenderer * rend,char const * text,gboolean use_markup,GOGeometryOBR * obr,double max_width)1066 gog_renderer_get_text_OBR (GogRenderer *rend, char const *text,
1067 gboolean use_markup, GOGeometryOBR *obr,
1068 double max_width)
1069 {
1070 GOString *str;
1071 PangoAttrList *attr_list = NULL;
1072 char *m_text = NULL;
1073
1074 g_return_if_fail (GOG_IS_RENDERER (rend));
1075 g_return_if_fail (text != NULL);
1076
1077 if (use_markup && pango_parse_markup (text, -1, 0,
1078 &attr_list, &m_text,
1079 NULL, NULL))
1080 str = go_string_new_rich_nocopy (m_text, -1, attr_list, NULL);
1081 else
1082 str = go_string_new (text);
1083 gog_renderer_get_gostring_OBR (rend, str, obr, max_width);
1084 go_string_unref (str);
1085
1086 }
1087
1088 /**
1089 * gog_renderer_get_text_AABR:
1090 * @rend: #GogRenderer
1091 * @text: the string to draw
1092 * @use_markup: whether to use pango markup
1093 * @aabr: #GOGeometryAABR to store the Axis Aligned Bounding Rectangle of @text.
1094 * @max_width: maximum width or -1 for unrestricted
1095 **/
1096 void
gog_renderer_get_text_AABR(GogRenderer * rend,char const * text,gboolean use_markup,GOGeometryAABR * aabr,double max_width)1097 gog_renderer_get_text_AABR (GogRenderer *rend, char const *text,
1098 gboolean use_markup, GOGeometryAABR *aabr,
1099 double max_width)
1100 {
1101 GOGeometryOBR obr;
1102
1103 gog_renderer_get_text_OBR (rend, text, use_markup, &obr, max_width);
1104 go_geometry_OBR_to_AABR (&obr, aabr);
1105 }
1106
1107 /**
1108 * gog_renderer_get_gostring_AABR:
1109 * @rend: #GogRenderer
1110 * @str: the string to draw
1111 * @aabr: #GOGeometryAABR to store the Axis Aligned Bounding Rectangle of @text.
1112 * @max_width: maximum width or -1 for unrestricted
1113 **/
1114 void
gog_renderer_get_gostring_AABR(GogRenderer * rend,GOString * str,GOGeometryAABR * aabr,double max_width)1115 gog_renderer_get_gostring_AABR (GogRenderer *rend, GOString *str,
1116 GOGeometryAABR *aabr, double max_width)
1117 {
1118 GOGeometryOBR obr;
1119
1120 gog_renderer_get_gostring_OBR (rend, str, &obr, max_width);
1121 go_geometry_OBR_to_AABR (&obr, aabr);
1122 }
1123
1124 void
gog_renderer_draw_data_label(GogRenderer * rend,GogSeriesLabelElt const * elt,GogViewAllocation const * pos,GOAnchorType anchor,GOStyle * legend_style)1125 gog_renderer_draw_data_label (GogRenderer *rend, GogSeriesLabelElt const *elt,
1126 GogViewAllocation const *pos, GOAnchorType anchor,
1127 GOStyle *legend_style)
1128 {
1129 /* things are a bit different from gog_renderer_draw_gostring, so the
1130 * code must be copied */
1131 PangoLayout *layout;
1132 PangoContext *context;
1133 cairo_t *cairo;
1134 GOGeometryOBR obr;
1135 GOGeometryAABR aabr;
1136 GOStyle const *style;
1137 int iw, ih;
1138 PangoAttrList *attrs;
1139 PangoRectangle rect;
1140
1141 g_return_if_fail (elt != NULL && elt->str != NULL);
1142 g_return_if_fail (GOG_IS_RENDERER (rend));
1143 g_return_if_fail (rend->cur_style != NULL);
1144
1145 cairo = rend->cairo;
1146 style = (GO_IS_STYLED_OBJECT (elt->point))?
1147 go_styled_object_get_style (GO_STYLED_OBJECT (elt->point)):
1148 rend->cur_style;
1149
1150 /* Note: orig layout may not have been created using cairo! */
1151 layout = pango_cairo_create_layout (cairo);
1152 pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
1153 context = pango_layout_get_context (layout);
1154 pango_layout_set_text (layout, elt->str->str, -1);
1155 attrs = pango_attr_list_copy (go_string_get_markup (elt->str));
1156 pango_layout_set_font_description (layout, style->font.font->desc);
1157 if (attrs)
1158 pango_layout_set_attributes (layout, attrs);
1159 pango_cairo_context_set_resolution (context, 72.0);
1160 /*now get the real size */
1161 pango_layout_get_size (layout, &iw, &ih);
1162 if (elt->legend_pos >= 0) {
1163 /* we need to add enough room to draw the legend entry */
1164 PangoRectangle rect;
1165 PangoAttribute *attr;
1166 rect.x = rect.y = 0;
1167 pango_layout_get_size (layout, &iw, &ih);
1168 rect.height = 1; /* only the width is important */
1169 rect.width = (legend_style->interesting_fields & GO_STYLE_LINE)? 2 * ih: ih;
1170 attr = pango_attr_shape_new (&rect, &rect);
1171 attr->start_index = elt->legend_pos;
1172 attr->end_index = elt->legend_pos + 1;
1173 pango_attr_list_insert (attrs, attr);
1174 pango_layout_set_attributes (layout, attrs);
1175 pango_layout_get_size (layout, &iw, &ih);
1176 }
1177 pango_attr_list_unref (attrs);
1178
1179 obr.w = rend->scale * ((double) iw + (double) PANGO_SCALE / 2.0)
1180 / (double) PANGO_SCALE;
1181 obr.h = rend->scale * ((double) ih + (double) PANGO_SCALE / 2.0)
1182 /(double) PANGO_SCALE;
1183 obr.alpha = -style->text_layout.angle * M_PI / 180.0;
1184 obr.x = pos->x;
1185 obr.y = pos->y;
1186 go_geometry_OBR_to_AABR (&obr, &aabr);
1187
1188 switch (anchor) {
1189 case GO_ANCHOR_NW: case GO_ANCHOR_W: case GO_ANCHOR_SW:
1190 obr.x += aabr.w / 2.0;
1191 break;
1192 case GO_ANCHOR_NE : case GO_ANCHOR_SE : case GO_ANCHOR_E :
1193 obr.x -= aabr.w / 2.0;
1194 break;
1195 default : break;
1196 }
1197
1198 switch (anchor) {
1199 case GO_ANCHOR_NW: case GO_ANCHOR_N: case GO_ANCHOR_NE:
1200 obr.y += aabr.h / 2.0;
1201 break;
1202 case GO_ANCHOR_SE : case GO_ANCHOR_S : case GO_ANCHOR_SW :
1203 obr.y -= aabr.h / 2.0;
1204 break;
1205 default : break;
1206 }
1207
1208 cairo_save (cairo);
1209 cairo_set_source_rgba (cairo, GO_COLOR_TO_CAIRO (style->font.color));
1210 cairo_translate (cairo, obr.x - (obr.w / 2.0) * cos (obr.alpha) +
1211 (obr.h / 2.0) * sin (obr.alpha),
1212 obr.y - (obr.w / 2.0) * sin (obr.alpha) -
1213 (obr.h / 2.0) * cos (obr.alpha));
1214 cairo_rotate (cairo, obr.alpha);
1215 /* now draw the legen entry if needed */
1216 if (elt->legend_pos >= 0 && legend_style != NULL) {
1217 GOStyle *style = go_style_dup (legend_style);
1218 GogViewAllocation rectangle;
1219 double x, y,w, h;
1220 pango_layout_index_to_pos (layout, elt->legend_pos, &rect);
1221 x = (double) rect.x / PANGO_SCALE * rend->scale;
1222 y = (double) rect.y / PANGO_SCALE * rend->scale;
1223 w = (double) rect.width / PANGO_SCALE * rend->scale;
1224 h = (double) rect.height / PANGO_SCALE * rend->scale;
1225 if (style->interesting_fields & GO_STYLE_LINE) { /* line and marker */
1226 GOPath *line_path;
1227 if (style->line.width > h / 3.)
1228 style->line.width = h / 3.;
1229 if (go_marker_get_size (style->marker.mark) > h)
1230 go_marker_set_size (style->marker.mark, h);
1231 gog_renderer_push_style (rend, style);
1232 line_path = go_path_new ();
1233 y += h / 2.;
1234 go_path_move_to (line_path, x, y);
1235 go_path_line_to (line_path, x + w, y);
1236 if (style->interesting_fields & GO_STYLE_FILL) {
1237 rectangle.x = x;
1238 rectangle.y = y;
1239 rectangle.w = w;
1240 rectangle.h = h / 2.0;
1241 gog_renderer_fill_rectangle (rend, &rectangle);
1242 }
1243 gog_renderer_stroke_serie (rend, line_path);
1244 go_path_free (line_path);
1245 gog_renderer_draw_marker (rend, x + w / 2., y);
1246 } else if (style->interesting_fields & GO_STYLE_FILL) {/* area */
1247 if (style->line.width > h / 3.)
1248 style->line.width = h / 3.;
1249
1250 rectangle.x = x;
1251 rectangle.y = y;
1252 rectangle.w = w;
1253 rectangle.h = h;
1254
1255 gog_renderer_push_style (rend, style);
1256 gog_renderer_draw_rectangle (rend, &rectangle);
1257 } else if (style->interesting_fields & GO_STYLE_MARKER) { /* markers only */
1258 if (go_marker_get_size (style->marker.mark) > h)
1259 go_marker_set_size (style->marker.mark, h);
1260 gog_renderer_push_style (rend, style);
1261 gog_renderer_draw_marker (rend, x + w / 2., y + h / 2.);
1262 }
1263 gog_renderer_pop_style (rend);
1264 g_object_unref (style);
1265 }
1266 cairo_scale (cairo, rend->scale, rend->scale);
1267 pango_cairo_show_layout (cairo, layout);
1268 cairo_restore (cairo);
1269 g_object_unref (layout);
1270 }
1271
1272 static void
_free_marker_data(GogRenderer * rend)1273 _free_marker_data (GogRenderer *rend)
1274 {
1275 if (rend->marker_surface != NULL) {
1276 cairo_surface_destroy (rend->marker_surface);
1277 rend->marker_surface = NULL;
1278 }
1279 if (rend->marker) {
1280 g_object_unref (rend->marker);
1281 rend->marker = NULL;
1282 }
1283 }
1284
1285 void
gog_renderer_draw_color_map(GogRenderer * rend,GogAxisColorMap const * map,int discrete,gboolean horizontal,GogViewAllocation const * rect)1286 gog_renderer_draw_color_map (GogRenderer *rend, GogAxisColorMap const *map,
1287 int discrete, gboolean horizontal,
1288 GogViewAllocation const *rect)
1289 {
1290 cairo_save (rend->cairo);
1291 cairo_translate (rend->cairo, rect->x, rect->y);
1292 gog_axis_color_map_to_cairo (map, rend->cairo, discrete, horizontal,
1293 rect->w, rect->h);
1294 cairo_restore (rend->cairo);
1295 }
1296
1297 void
gog_renderer_push_style(GogRenderer * rend,GOStyle const * style)1298 gog_renderer_push_style (GogRenderer *rend, GOStyle const *style)
1299 {
1300 g_return_if_fail (GOG_IS_RENDERER (rend));
1301 g_return_if_fail (GO_IS_STYLE (style));
1302
1303 if (rend->cur_style != NULL)
1304 rend->style_stack = g_slist_prepend (
1305 rend->style_stack, (gpointer)rend->cur_style);
1306 g_object_ref ((gpointer)style);
1307 rend->cur_style = style;
1308
1309 _free_marker_data (rend);
1310
1311 _update_dash (rend);
1312 }
1313
1314 void
gog_renderer_pop_style(GogRenderer * rend)1315 gog_renderer_pop_style (GogRenderer *rend)
1316 {
1317 g_return_if_fail (GOG_IS_RENDERER (rend));
1318 g_return_if_fail (GO_IS_STYLE (rend->cur_style));
1319
1320 g_object_unref ((gpointer)rend->cur_style);
1321 if (rend->style_stack != NULL) {
1322 rend->cur_style = rend->style_stack->data;
1323 rend->style_stack = g_slist_remove (rend->style_stack,
1324 rend->cur_style);
1325 } else
1326 rend->cur_style = NULL;
1327
1328 _free_marker_data (rend);
1329
1330 _update_dash (rend);
1331 }
1332
1333 void
gog_renderer_request_update(GogRenderer * renderer)1334 gog_renderer_request_update (GogRenderer *renderer)
1335 {
1336 g_return_if_fail (GOG_IS_RENDERER (renderer));
1337
1338 if (renderer->needs_update)
1339 return;
1340 renderer->needs_update = TRUE;
1341 g_signal_emit (G_OBJECT (renderer),
1342 renderer_signals [RENDERER_SIGNAL_REQUEST_UPDATE], 0);
1343 }
1344
1345 /**
1346 * gog_renderer_update:
1347 * @renderer: a #GogRenderer
1348 * @w: requested width
1349 * @h: requested height
1350 *
1351 * Requests a renderer update, only useful for pixbuf based renderer.
1352 *
1353 * Returns: %TRUE if a redraw is necessary.
1354 **/
1355 gboolean
gog_renderer_update(GogRenderer * rend,double w,double h)1356 gog_renderer_update (GogRenderer *rend, double w, double h)
1357 {
1358 GogGraph *graph;
1359 GogView *view;
1360 GogViewAllocation allocation;
1361 gboolean redraw = TRUE;
1362 gboolean size_changed;
1363
1364 if (w <= 0 || h <= 0)
1365 return FALSE;
1366 g_return_val_if_fail (GOG_IS_RENDERER (rend), FALSE);
1367 g_return_val_if_fail (GOG_IS_VIEW (rend->view), FALSE);
1368
1369 size_changed = rend->w != w || rend->h != h;
1370 if (size_changed) {
1371 if (rend->cairo_surface != NULL) {
1372 cairo_surface_destroy (rend->cairo_surface);
1373 rend->cairo_surface = NULL;
1374 }
1375
1376 if (w == 0 || h == 0)
1377 return FALSE;
1378
1379 rend->w = w;
1380 rend->h = h;
1381
1382 rend->cairo_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, rend->w, rend->h);
1383 }
1384
1385 view = rend->view;
1386 graph = GOG_GRAPH (view->model);
1387 gog_graph_force_update (graph);
1388
1389 allocation.x = allocation.y = 0.;
1390 allocation.w = rend->w;
1391 allocation.h = rend->h;
1392
1393 rend->cairo = cairo_create (rend->cairo_surface);
1394
1395 /* we need to update scale even if size did not change since graph size might have changed (#599887) */
1396 rend->scale_x = (graph->width >= 1.) ? (rend->w / graph->width) : 1.;
1397 rend->scale_y = (graph->height >= 1.) ? (rend->h / graph->height) : 1.;
1398 rend->scale = MIN (rend->scale_x, rend->scale_y);
1399
1400 if (size_changed) {
1401 /* make sure we dont try to queue an update while updating */
1402 rend->needs_update = TRUE;
1403
1404 /* scale just changed need to recalculate sizes */
1405 gog_renderer_request_update (rend);
1406 gog_view_size_allocate (view, &allocation);
1407 } else
1408 if (rend->w != view->allocation.w || rend->h != view->allocation.h)
1409 gog_view_size_allocate (view, &allocation);
1410 else
1411 redraw = gog_view_update_sizes (view);
1412
1413 redraw |= rend->needs_update;
1414 rend->needs_update = FALSE;
1415
1416 if (redraw) {
1417 if (rend->pixbuf) {
1418 g_object_unref (rend->pixbuf);
1419 rend->pixbuf = NULL;
1420 }
1421
1422 if (!size_changed) {
1423 cairo_set_operator (rend->cairo, CAIRO_OPERATOR_CLEAR);
1424 cairo_paint (rend->cairo);
1425 }
1426
1427 cairo_set_operator (rend->cairo, CAIRO_OPERATOR_OVER);
1428
1429 cairo_set_line_join (rend->cairo, CAIRO_LINE_JOIN_ROUND);
1430 cairo_set_line_cap (rend->cairo, CAIRO_LINE_CAP_ROUND);
1431
1432 rend->is_vector = FALSE;
1433 gog_view_render (view, NULL);
1434 }
1435
1436 cairo_destroy (rend->cairo);
1437 rend->cairo = NULL;
1438
1439 return redraw;
1440 }
1441
1442 /**
1443 * gog_renderer_get_pixbuf:
1444 * @renderer: #GogRenderer
1445 *
1446 * Returns: (transfer none): current pixbuf buffer from a renderer that can render into a pixbuf.
1447 * or %NULL on error.
1448 **/
1449 GdkPixbuf *
gog_renderer_get_pixbuf(GogRenderer * rend)1450 gog_renderer_get_pixbuf (GogRenderer *rend)
1451 {
1452 g_return_val_if_fail (GOG_IS_RENDERER (rend), NULL);
1453
1454 if (rend->cairo_surface == NULL)
1455 return NULL;
1456
1457 if (rend->pixbuf == NULL) {
1458 int width = cairo_image_surface_get_width (rend->cairo_surface);
1459 int height = cairo_image_surface_get_height (rend->cairo_surface);
1460
1461 if (width <= 0 || height <= 0)
1462 rend->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, 1, 1);
1463 else {
1464 int rowstride = cairo_image_surface_get_stride (rend->cairo_surface);
1465 unsigned char *data = cairo_image_surface_get_data (rend->cairo_surface);
1466 rend->pixbuf = gdk_pixbuf_new_from_data (data, GDK_COLORSPACE_RGB, TRUE, 8,
1467 width, height, rowstride, NULL, NULL);
1468 go_cairo_convert_data_to_pixbuf (data, NULL, width, height, rowstride);
1469 }
1470 }
1471
1472 return rend->pixbuf;
1473 }
1474
1475 cairo_surface_t *
gog_renderer_get_cairo_surface(GogRenderer * rend)1476 gog_renderer_get_cairo_surface (GogRenderer *rend)
1477 {
1478 g_return_val_if_fail (GOG_IS_RENDERER (rend), NULL);
1479
1480 return rend->cairo_surface;
1481 }
1482
1483 static gboolean
_gsf_gdk_pixbuf_save(const gchar * buf,gsize count,GError ** error,gpointer data)1484 _gsf_gdk_pixbuf_save (const gchar *buf,
1485 gsize count,
1486 GError **error,
1487 gpointer data)
1488 {
1489 GsfOutput *output = GSF_OUTPUT (data);
1490 gboolean ok = gsf_output_write (output, count, buf);
1491
1492 if (!ok && error)
1493 *error = g_error_copy (gsf_output_error (output));
1494
1495 return ok;
1496 }
1497
1498 static cairo_status_t
_cairo_write_func(void * closure,const unsigned char * data,unsigned int length)1499 _cairo_write_func (void *closure,
1500 const unsigned char *data,
1501 unsigned int length)
1502 {
1503 gboolean result;
1504 GsfOutput *output = GSF_OUTPUT (closure);
1505
1506 result = gsf_output_write (output, length, data);
1507
1508 return result ? CAIRO_STATUS_SUCCESS : CAIRO_STATUS_WRITE_ERROR;
1509 }
1510
1511 gboolean
gog_renderer_render_to_cairo(GogRenderer * renderer,cairo_t * cairo,double width,double height)1512 gog_renderer_render_to_cairo (GogRenderer *renderer, cairo_t *cairo, double width, double height)
1513 {
1514 GogViewAllocation allocation;
1515 double width_in_pts, height_in_pts;
1516
1517 g_return_val_if_fail (GOG_IS_RENDERER (renderer), FALSE);
1518 g_return_val_if_fail (GOG_IS_VIEW (renderer->view), FALSE);
1519 g_return_val_if_fail (GOG_IS_GRAPH (renderer->model), FALSE);
1520
1521 gog_graph_force_update (renderer->model);
1522 gog_graph_get_size (renderer->model, &width_in_pts, &height_in_pts);
1523
1524 renderer->cairo = cairo;
1525 renderer->is_vector = go_cairo_surface_is_vector (cairo_get_target (cairo));
1526
1527 cairo_set_line_join (renderer->cairo, CAIRO_LINE_JOIN_ROUND);
1528 cairo_set_line_cap (renderer->cairo, CAIRO_LINE_CAP_ROUND);
1529
1530 renderer->w = width;
1531 renderer->h = height;
1532
1533 allocation.x = 0.;
1534 allocation.y = 0.;
1535 allocation.w = width;
1536 allocation.h = height;
1537
1538 renderer->scale_x = (width_in_pts >= 1.) ? (width / width_in_pts) : 1.;
1539 renderer->scale_y = (height_in_pts >= 1.) ? (height / height_in_pts) : 1.;
1540 renderer->scale = MIN (renderer->scale_x, renderer->scale_y);
1541
1542 gog_view_size_allocate (renderer->view, &allocation);
1543 gog_view_render (renderer->view, NULL);
1544
1545 renderer->cairo = NULL;
1546
1547 return cairo_status (cairo) == CAIRO_STATUS_SUCCESS;
1548 }
1549
1550 /**
1551 * gog_renderer_export_image:
1552 * @renderer: a #GogRenderer
1553 * @format: image format for export
1554 * @output: a #GsfOutput stream
1555 * @x_dpi: x resolution of exported graph
1556 * @y_dpi: y resolution of exported graph
1557 *
1558 * Exports an image of @graph in given @format, writing results in a #GsfOutput stream.
1559 * If export format type is a bitmap one, it computes image size with x_dpi, y_dpi and
1560 * @graph size (see gog_graph_get_size()).
1561 *
1562 * returns: %TRUE if export succeed.
1563 **/
1564
1565 gboolean
gog_renderer_export_image(GogRenderer * rend,GOImageFormat format,GsfOutput * output,double x_dpi,double y_dpi)1566 gog_renderer_export_image (GogRenderer *rend, GOImageFormat format,
1567 GsfOutput *output, double x_dpi, double y_dpi)
1568 {
1569 GOImageFormatInfo const *format_info;
1570 cairo_t *cairo;
1571 cairo_surface_t *surface = NULL;
1572 gboolean status;
1573 GdkPixbuf *pixbuf;
1574 GdkPixbuf *output_pixbuf;
1575 gboolean result;
1576 double width_in_pts, height_in_pts;
1577
1578 g_return_val_if_fail (GOG_IS_RENDERER (rend), FALSE);
1579
1580 if (x_dpi <= 0.)
1581 x_dpi = 96.;
1582 if (y_dpi <= 0.)
1583 y_dpi = 96.;
1584 gog_graph_force_update (rend->model);
1585
1586 gog_graph_get_size (rend->model, &width_in_pts, &height_in_pts);
1587
1588 /* Prevent Cairo from faulting. */
1589 width_in_pts = CLAMP (width_in_pts, 1, 32767 * 72.0 / x_dpi);
1590 height_in_pts = CLAMP (height_in_pts, 1, 32767 * 72.0 / y_dpi);
1591
1592 switch (format) {
1593 case GO_IMAGE_FORMAT_EPS:
1594 rend->marker_as_surface = FALSE;
1595 surface = cairo_ps_surface_create_for_stream
1596 (_cairo_write_func,
1597 output, width_in_pts, height_in_pts);
1598 cairo_ps_surface_set_eps (surface, TRUE);
1599 cairo_surface_set_fallback_resolution (surface, x_dpi, y_dpi);
1600 goto do_export_vectorial;
1601 case GO_IMAGE_FORMAT_PDF:
1602 rend->marker_as_surface = FALSE;
1603 surface = cairo_pdf_surface_create_for_stream
1604 (_cairo_write_func,
1605 output, width_in_pts, height_in_pts);
1606 cairo_surface_set_fallback_resolution (surface, x_dpi, y_dpi);
1607 goto do_export_vectorial;
1608 case GO_IMAGE_FORMAT_PS:
1609 rend->marker_as_surface = FALSE;
1610 surface = cairo_ps_surface_create_for_stream
1611 (_cairo_write_func,
1612 output, width_in_pts, height_in_pts);
1613 cairo_surface_set_fallback_resolution (surface, x_dpi, y_dpi);
1614 goto do_export_vectorial;
1615 case GO_IMAGE_FORMAT_SVG:
1616 #ifdef CAIRO_BUG_63633_FIXED
1617 rend->marker_as_surface = TRUE;
1618 #else
1619 rend->marker_as_surface = FALSE;
1620 #endif
1621 surface = cairo_svg_surface_create_for_stream
1622 (_cairo_write_func,
1623 output, width_in_pts, height_in_pts);
1624 cairo_surface_set_fallback_resolution (surface, x_dpi, y_dpi);
1625 do_export_vectorial:
1626 rend->scale = 1.0;
1627 cairo = cairo_create (surface);
1628 cairo_surface_destroy (surface);
1629 status = gog_renderer_render_to_cairo (rend, cairo, width_in_pts, height_in_pts);
1630 cairo_destroy (cairo);
1631
1632 return status;
1633
1634 break;
1635 default:
1636 format_info = go_image_get_format_info (format);
1637 if (!format_info->has_pixbuf_saver) {
1638 g_warning ("[GogRendererCairo:export_image] unsupported format");
1639 return FALSE;
1640 }
1641
1642 gog_renderer_update (rend, width_in_pts * x_dpi / 72.0, height_in_pts * y_dpi / 72.0);
1643 pixbuf = gog_renderer_get_pixbuf (rend);
1644 if (pixbuf == NULL)
1645 return FALSE;
1646 format_info = go_image_get_format_info (format);
1647 if (!format_info->alpha_support)
1648 output_pixbuf = gdk_pixbuf_composite_color_simple
1649 (pixbuf,
1650 gdk_pixbuf_get_width (pixbuf),
1651 gdk_pixbuf_get_height (pixbuf),
1652 GDK_INTERP_NEAREST,
1653 255, 256, 0xffffffff,
1654 0xffffffff);
1655 else
1656 output_pixbuf = pixbuf;
1657 result = gdk_pixbuf_save_to_callback (output_pixbuf,
1658 _gsf_gdk_pixbuf_save,
1659 output, format_info->name,
1660 NULL, NULL);
1661 if (!format_info->alpha_support)
1662 g_object_unref (output_pixbuf);
1663 return result;
1664 }
1665
1666 return FALSE;
1667 }
1668
1669 /**
1670 * gog_renderer_get_hairline_width_pts:
1671 * @rend: a #GogRenderer
1672 *
1673 * Returns: the hairline width in pts.
1674 **/
1675 double
gog_renderer_get_hairline_width_pts(GogRenderer const * rend)1676 gog_renderer_get_hairline_width_pts (GogRenderer const *rend)
1677 {
1678 g_return_val_if_fail (GOG_IS_RENDERER (rend), GOG_RENDERER_HAIRLINE_WIDTH_PTS);
1679
1680 if (rend->is_vector
1681 || go_sub_epsilon (rend->scale) <= 0.0)
1682 return GOG_RENDERER_HAIRLINE_WIDTH_PTS;
1683
1684 return 1.0 / rend->scale;
1685 }
1686
1687 #ifdef GOFFICE_WITH_LASEM
1688
1689 void
gog_renderer_draw_equation(GogRenderer * renderer,LsmDomView * mathml_view,double x,double y)1690 gog_renderer_draw_equation (GogRenderer *renderer, LsmDomView *mathml_view, double x, double y)
1691 {
1692 cairo_t *cairo;
1693 double w, h, alpha;
1694
1695 g_return_if_fail (GOG_IS_RENDERER (renderer));
1696 g_return_if_fail (LSM_IS_DOM_VIEW (mathml_view));
1697 g_return_if_fail (renderer->cur_style != NULL);
1698
1699 cairo = renderer->cairo;
1700
1701 alpha = -renderer->cur_style->text_layout.angle * M_PI / 180.0;
1702 lsm_dom_view_get_size (LSM_DOM_VIEW (mathml_view), &w, &h, NULL);
1703 w *= renderer->scale;
1704 h *= renderer->scale;
1705 x = x - (w / 2.0) * cos (alpha) + (h / 2.0) * sin (alpha);
1706 y = y - (w / 2.0) * sin (alpha) - (h / 2.0) * cos (alpha);
1707
1708 cairo_save (cairo);
1709
1710 cairo_translate (cairo, x, y);
1711 cairo_rotate (cairo, alpha);
1712 cairo_scale (cairo, renderer->scale_x, renderer->scale_y);
1713
1714 lsm_dom_view_render (LSM_DOM_VIEW (mathml_view), cairo, 0., 0.);
1715
1716 cairo_restore (cairo);
1717 }
1718
1719 #endif
1720
1721 /**
1722 * gog_renderer_new:
1723 * @graph: graph model
1724 *
1725 * Returns: a new #GogRenderer which can render into a pixbuf, and sets @graph
1726 * as its model.
1727 **/
1728 GogRenderer*
gog_renderer_new(GogGraph * graph)1729 gog_renderer_new (GogGraph *graph)
1730 {
1731 return g_object_new (GOG_TYPE_RENDERER, "model", graph, NULL);
1732 }
1733
1734 static void
_cb_font_removed(GogRenderer * rend,GOFont const * font)1735 _cb_font_removed (GogRenderer *rend, GOFont const *font)
1736 {
1737 g_return_if_fail (GOG_IS_RENDERER (rend));
1738
1739 gog_debug (0, g_warning ("notify a '%s' that %p is invalid",
1740 G_OBJECT_TYPE_NAME (rend), font););
1741 }
1742
1743 static void
gog_renderer_init(GogRenderer * rend)1744 gog_renderer_init (GogRenderer *rend)
1745 {
1746 rend->cairo = NULL;
1747 rend->cairo_surface = NULL;
1748 rend->marker_surface = NULL;
1749 rend->w = rend->h = 0;
1750 rend->is_vector = FALSE;
1751
1752 rend->line_dash = NULL;
1753
1754 rend->needs_update = FALSE;
1755 rend->cur_style = NULL;
1756 rend->style_stack = NULL;
1757 rend->font_watcher = g_cclosure_new_swap (G_CALLBACK (_cb_font_removed),
1758 rend, NULL);
1759 go_font_cache_register (rend->font_watcher);
1760
1761 rend->grip_style = NULL;
1762 rend->selection_style = NULL;
1763
1764 rend->pixbuf = NULL;
1765 }
1766
1767 static void
gog_renderer_finalize(GObject * obj)1768 gog_renderer_finalize (GObject *obj)
1769 {
1770 GogRenderer *rend = GOG_RENDERER (obj);
1771
1772 if (rend->pixbuf != NULL) {
1773 g_object_unref (rend->pixbuf);
1774 rend->pixbuf = NULL;
1775 }
1776
1777 if (rend->cairo_surface != NULL) {
1778 cairo_surface_destroy (rend->cairo_surface);
1779 rend->cairo_surface = NULL;
1780 }
1781
1782 _free_marker_data (rend);
1783
1784 go_line_dash_sequence_free (rend->line_dash);
1785 rend->line_dash = NULL;
1786
1787 if (rend->grip_style != NULL) {
1788 g_object_unref (rend->grip_style);
1789 rend->grip_style = NULL;
1790 }
1791
1792 if (rend->selection_style != NULL) {
1793 g_object_unref (rend->selection_style);
1794 rend->selection_style = NULL;
1795 }
1796
1797 if (rend->cur_style != NULL) {
1798 g_warning ("Missing calls to gog_renderer_style_pop left dangling style references");
1799 g_slist_foreach (rend->style_stack,
1800 (GFunc)g_object_unref, NULL);
1801 g_slist_free (rend->style_stack);
1802 rend->style_stack = NULL;
1803 g_object_unref ((gpointer)rend->cur_style);
1804 rend->cur_style = NULL;
1805 }
1806
1807 if (rend->view != NULL) {
1808 g_object_unref (rend->view);
1809 rend->view = NULL;
1810 }
1811
1812 if (rend->font_watcher != NULL) {
1813 go_font_cache_unregister (rend->font_watcher);
1814 g_closure_unref (rend->font_watcher);
1815 rend->font_watcher = NULL;
1816 }
1817
1818 (*parent_klass->finalize) (obj);
1819 }
1820
1821 static void
gog_renderer_set_property(GObject * obj,guint param_id,GValue const * value,GParamSpec * pspec)1822 gog_renderer_set_property (GObject *obj, guint param_id,
1823 GValue const *value, GParamSpec *pspec)
1824 {
1825 GogRenderer *rend = GOG_RENDERER (obj);
1826
1827 switch (param_id) {
1828 case RENDERER_PROP_MODEL:
1829 rend->model = GOG_GRAPH (g_value_get_object (value));
1830 if (rend->view != NULL)
1831 g_object_unref (rend->view);
1832 rend->view = g_object_new (gog_graph_view_get_type (),
1833 "renderer", rend,
1834 "model", rend->model,
1835 NULL);
1836 gog_renderer_request_update (rend);
1837 break;
1838
1839 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
1840 return; /* NOTE : RETURN */
1841 }
1842 }
1843
1844 static void
gog_renderer_get_property(GObject * obj,guint param_id,GValue * value,GParamSpec * pspec)1845 gog_renderer_get_property (GObject *obj, guint param_id,
1846 GValue *value, GParamSpec *pspec)
1847 {
1848 GogRenderer const *rend = GOG_RENDERER (obj);
1849
1850 switch (param_id) {
1851 case RENDERER_PROP_MODEL:
1852 g_value_set_object (value, rend->model);
1853 break;
1854 case RENDERER_PROP_VIEW:
1855 g_value_set_object (value, rend->view);
1856 break;
1857 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
1858 break;
1859 }
1860 }
1861
1862 static void
gog_renderer_class_init(GogRendererClass * renderer_klass)1863 gog_renderer_class_init (GogRendererClass *renderer_klass)
1864 {
1865 GObjectClass *gobject_klass = (GObjectClass *)renderer_klass;
1866
1867 parent_klass = g_type_class_peek_parent (gobject_klass);
1868 gobject_klass->finalize = gog_renderer_finalize;
1869 gobject_klass->set_property = gog_renderer_set_property;
1870 gobject_klass->get_property = gog_renderer_get_property;
1871
1872 g_object_class_install_property (gobject_klass, RENDERER_PROP_MODEL,
1873 g_param_spec_object ("model",
1874 _("Model"),
1875 _("The GogGraph this renderer displays"),
1876 GOG_TYPE_GRAPH,
1877 GSF_PARAM_STATIC | G_PARAM_READWRITE));
1878 g_object_class_install_property (gobject_klass, RENDERER_PROP_VIEW,
1879 g_param_spec_object ("view",
1880 _("View"),
1881 _("the GogView this renderer is displaying"),
1882 GOG_TYPE_VIEW,
1883 GSF_PARAM_STATIC | G_PARAM_READABLE));
1884
1885 renderer_signals [RENDERER_SIGNAL_REQUEST_UPDATE] = g_signal_new ("request-update",
1886 G_TYPE_FROM_CLASS (renderer_klass),
1887 G_SIGNAL_RUN_LAST,
1888 G_STRUCT_OFFSET (GogRendererClass, request_update),
1889 NULL, NULL,
1890 g_cclosure_marshal_VOID__VOID,
1891 G_TYPE_NONE, 0);
1892 }
1893
1894 double
gog_renderer_get_scale(GogRenderer * renderer)1895 gog_renderer_get_scale (GogRenderer *renderer)
1896 {
1897 return renderer->scale;
1898 }
1899
1900 GSF_CLASS (GogRenderer, gog_renderer,
1901 gog_renderer_class_init, gog_renderer_init,
1902 G_TYPE_OBJECT)
1903