adg: provide example on how to customize a style
[adg.git] / src / adg / adg-canvas.c
blob1244484578884b1204d42758142c43cd40602c7a
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2017 Nicola Fontana <ntd at entidi.it>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
21 /**
22 * SECTION:adg-canvas
23 * @short_description: The drawing container
25 * The canvas is the toplevel entity of an ADG drawing. It can be
26 * bound to a GTK+ widget, such as #AdgGtkArea, or manually rendered
27 * to a custom surface.
29 * Tipically, the canvas contains the description and properties of
30 * the media used, such as such as size (if relevant), margins,
31 * border and paddings. This approach clearly follows the block model
32 * of the CSS specification.
34 * The paddings specify the distance between the entities contained
35 * by the canvas and the border. The margins specify the distance
36 * between the canvas border and the media extents.
38 * The canvas (hence the media) size can be explicitely specified
39 * by directly writing to the #AdgCanvas:size property or using any
40 * valid setter, such as adg_canvas_set_size(),
41 * adg_canvas_set_size_explicit() or the convenient
42 * adg_canvas_set_paper() GTK+ wrapper. You can also set explicitely
43 * only one dimension and let the other one be computed automatically.
44 * This can be done by setting it to <constant>0</constant>.
46 * By default either width and height are autocalculated, i.e. they
47 * are initially set to 0. In this case the arrange() phase is executed.
48 * Margins and paddings are then added to the extents to get the
49 * border coordinates and the final bounding box.
51 * Instead, when the size is explicitely set, the final bounding
52 * box is forcibly set to this value without taking the canvas
53 * extents into account. The margins are then subtracted to get
54 * the coordinates of the border. In this case the paddings are
55 * simply ignoredby the arrange phase. They are still used by
56 * adg_canvas_autoscale() though, if called.
58 * Since: 1.0
59 **/
61 /**
62 * AdgCanvas:
64 * All fields are private and should not be used directly.
65 * Use its public methods instead.
67 * Since: 1.0
68 **/
70 /**
71 * ADG_CANVAS_ERROR:
73 * Error domain for canvas processing. Errors in this domain will be from the
74 * #AdgCanvasError enumeration. See #GError for information on error domains.
76 * Since: 1.0
77 **/
79 /**
80 * AdgCanvasError:
81 * @ADG_CANVAS_ERROR_SURFACE: Invalid surface type.
82 * @ADG_CANVAS_ERROR_CAIRO: The underlying cairo library returned an error.
84 * Error codes returned by #AdgCanvas methods.
86 * Since: 1.0
87 **/
90 #include "adg-internal.h"
92 #include "adg-container.h"
93 #include "adg-table.h"
94 #include "adg-title-block.h"
95 #include "adg-style.h"
96 #include "adg-color-style.h"
97 #include "adg-dress.h"
98 #include "adg-param-dress.h"
100 #include <adg-canvas.h>
101 #include "adg-canvas-private.h"
103 #ifdef CAIRO_HAS_PS_SURFACE
104 #include <cairo-ps.h>
105 #endif
106 #ifdef CAIRO_HAS_PDF_SURFACE
107 #include <cairo-pdf.h>
108 #endif
109 #ifdef CAIRO_HAS_SVG_SURFACE
110 #include <cairo-svg.h>
111 #endif
114 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_canvas_parent_class)
115 #define _ADG_OLD_ENTITY_CLASS ((AdgEntityClass *) adg_canvas_parent_class)
118 G_DEFINE_TYPE(AdgCanvas, adg_canvas, ADG_TYPE_CONTAINER)
120 enum {
121 PROP_0,
122 PROP_SIZE,
123 PROP_SCALES,
124 PROP_BACKGROUND_DRESS,
125 PROP_FRAME_DRESS,
126 PROP_TITLE_BLOCK,
127 PROP_TOP_MARGIN,
128 PROP_RIGHT_MARGIN,
129 PROP_BOTTOM_MARGIN,
130 PROP_LEFT_MARGIN,
131 PROP_HAS_FRAME,
132 PROP_TOP_PADDING,
133 PROP_RIGHT_PADDING,
134 PROP_BOTTOM_PADDING,
135 PROP_LEFT_PADDING
139 static void _adg_dispose (GObject *object);
140 static void _adg_get_property (GObject *object,
141 guint param_id,
142 GValue *value,
143 GParamSpec *pspec);
144 static void _adg_set_property (GObject *object,
145 guint param_id,
146 const GValue *value,
147 GParamSpec *pspec);
148 static void _adg_global_changed (AdgEntity *entity);
149 static void _adg_local_changed (AdgEntity *entity);
150 static void _adg_invalidate (AdgEntity *entity);
151 static void _adg_arrange (AdgEntity *entity);
152 static void _adg_render (AdgEntity *entity,
153 cairo_t *cr);
154 static void _adg_apply_paddings (AdgCanvas *canvas,
155 CpmlExtents *extents);
156 static void _adg_update_margin (AdgCanvas *canvas,
157 gdouble *margin,
158 gdouble *side,
159 gdouble new_margin);
162 GQuark
163 adg_canvas_error_quark(void)
165 static GQuark q;
167 if G_UNLIKELY (q == 0)
168 q = g_quark_from_static_string("adg-canvas-error-quark");
170 return q;
173 static void
174 adg_canvas_class_init(AdgCanvasClass *klass)
176 GObjectClass *gobject_class;
177 AdgEntityClass *entity_class;
178 GParamSpec *param;
180 gobject_class = (GObjectClass *) klass;
181 entity_class = (AdgEntityClass *) klass;
183 g_type_class_add_private(klass, sizeof(AdgCanvasPrivate));
185 gobject_class->dispose = _adg_dispose;
186 gobject_class->get_property = _adg_get_property;
187 gobject_class->set_property = _adg_set_property;
189 entity_class->global_changed = _adg_global_changed;
190 entity_class->local_changed = _adg_local_changed;
191 entity_class->invalidate = _adg_invalidate;
192 entity_class->arrange = _adg_arrange;
193 entity_class->render = _adg_render;
195 param = g_param_spec_boxed("size",
196 P_("Canvas Size"),
197 P_("The size set on this canvas: use 0 to have an automatic dimension based on the canvas extents"),
198 CPML_TYPE_PAIR,
199 G_PARAM_READWRITE);
200 g_object_class_install_property(gobject_class, PROP_SIZE, param);
202 param = g_param_spec_boxed("scales",
203 P_("Valid Scales"),
204 P_("List of scales to be tested while autoscaling the drawing"),
205 G_TYPE_STRV,
206 G_PARAM_READWRITE);
207 g_object_class_install_property(gobject_class, PROP_SCALES, param);
209 param = adg_param_spec_dress("background-dress",
210 P_("Background Dress"),
211 P_("The color dress to use for the canvas background"),
212 ADG_DRESS_COLOR_BACKGROUND,
213 G_PARAM_READWRITE);
214 g_object_class_install_property(gobject_class, PROP_BACKGROUND_DRESS, param);
216 param = adg_param_spec_dress("frame-dress",
217 P_("Frame Dress"),
218 P_("Line dress to use while drawing the frame around the canvas"),
219 ADG_DRESS_LINE_FRAME,
220 G_PARAM_READWRITE);
221 g_object_class_install_property(gobject_class, PROP_FRAME_DRESS, param);
223 param = g_param_spec_object("title-block",
224 P_("Title Block"),
225 P_("The title block to assign to this canvas"),
226 ADG_TYPE_TITLE_BLOCK,
227 G_PARAM_READWRITE);
228 g_object_class_install_property(gobject_class, PROP_TITLE_BLOCK, param);
230 param = g_param_spec_double("top-margin",
231 P_("Top Margin"),
232 P_("The margin (in global space) to leave above the frame"),
233 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
234 G_PARAM_READWRITE);
235 g_object_class_install_property(gobject_class, PROP_TOP_MARGIN, param);
237 param = g_param_spec_double("right-margin",
238 P_("Right Margin"),
239 P_("The margin (in global space) to leave empty at the right of the frame"),
240 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
241 G_PARAM_READWRITE);
242 g_object_class_install_property(gobject_class, PROP_RIGHT_MARGIN, param);
244 param = g_param_spec_double("bottom-margin",
245 P_("Bottom Margin"),
246 P_("The margin (in global space) to leave empty below the frame"),
247 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
248 G_PARAM_READWRITE);
249 g_object_class_install_property(gobject_class, PROP_BOTTOM_MARGIN, param);
251 param = g_param_spec_double("left-margin",
252 P_("Left Margin"),
253 P_("The margin (in global space) to leave empty at the left of the frame"),
254 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
255 G_PARAM_READWRITE);
256 g_object_class_install_property(gobject_class, PROP_LEFT_MARGIN, param);
258 param = g_param_spec_boolean("has-frame",
259 P_("Has Frame Flag"),
260 P_("If enabled, a frame using the frame dress will be drawn around the canvas extents, taking into account the margins"),
261 TRUE,
262 G_PARAM_READWRITE);
263 g_object_class_install_property(gobject_class, PROP_HAS_FRAME, param);
265 param = g_param_spec_double("top-padding",
266 P_("Top Padding"),
267 P_("The padding (in global space) to leave empty above between the drawing and the frame"),
268 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
269 G_PARAM_READWRITE);
270 g_object_class_install_property(gobject_class, PROP_TOP_PADDING, param);
272 param = g_param_spec_double("right-padding",
273 P_("Right Padding"),
274 P_("The padding (in global space) to leave empty at the right between the drawing and the frame"),
275 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
276 G_PARAM_READWRITE);
277 g_object_class_install_property(gobject_class, PROP_RIGHT_PADDING, param);
279 param = g_param_spec_double("bottom-padding",
280 P_("Bottom Padding"),
281 P_("The padding (in global space) to leave empty below between the drawing and the frame"),
282 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
283 G_PARAM_READWRITE);
284 g_object_class_install_property(gobject_class, PROP_BOTTOM_PADDING, param);
286 param = g_param_spec_double("left-padding",
287 P_("Left Padding"),
288 P_("The padding (in global space) to leave empty at the left between the drawing and the frame"),
289 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
290 G_PARAM_READWRITE);
291 g_object_class_install_property(gobject_class, PROP_LEFT_PADDING, param);
294 static void
295 adg_canvas_init(AdgCanvas *canvas)
297 AdgCanvasPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(canvas,
298 ADG_TYPE_CANVAS,
299 AdgCanvasPrivate);
300 const gchar *scales[] = {
301 "10:1", "5:1", "3:1", "2:1", "1:1", "1:2", "1:3", "1:5", "1:10",
302 NULL
306 data->size.x = 0;
307 data->size.y = 0;
308 data->scales = g_strdupv((gchar **) scales);
309 data->background_dress = ADG_DRESS_COLOR_BACKGROUND;
310 data->frame_dress = ADG_DRESS_LINE_FRAME;
311 data->title_block = NULL;
312 data->top_margin = 15;
313 data->right_margin = 15;
314 data->bottom_margin = 15;
315 data->left_margin = 15;
316 data->has_frame = TRUE;
317 data->top_padding = 15;
318 data->right_padding = 15;
319 data->bottom_padding = 15;
320 data->left_padding = 15;
322 canvas->data = data;
325 static void
326 _adg_dispose(GObject *object)
328 AdgCanvas *canvas;
329 AdgCanvasPrivate *data;
331 canvas = (AdgCanvas *) object;
332 data = canvas->data;
334 if (data->title_block) {
335 g_object_unref(data->title_block);
336 data->title_block = NULL;
339 if (data->scales != NULL) {
340 g_strfreev(data->scales);
341 data->scales = NULL;
344 if (_ADG_OLD_OBJECT_CLASS->dispose)
345 _ADG_OLD_OBJECT_CLASS->dispose(object);
349 static void
350 _adg_get_property(GObject *object, guint prop_id,
351 GValue *value, GParamSpec *pspec)
353 AdgCanvasPrivate *data = ((AdgCanvas *) object)->data;
355 switch (prop_id) {
356 case PROP_SIZE:
357 g_value_set_boxed(value, &data->size);
358 break;
359 case PROP_SCALES:
360 g_value_set_boxed(value, data->scales);
361 break;
362 case PROP_BACKGROUND_DRESS:
363 g_value_set_enum(value, data->background_dress);
364 break;
365 case PROP_FRAME_DRESS:
366 g_value_set_enum(value, data->frame_dress);
367 break;
368 case PROP_TITLE_BLOCK:
369 g_value_set_object(value, data->title_block);
370 break;
371 case PROP_TOP_MARGIN:
372 g_value_set_double(value, data->top_margin);
373 break;
374 case PROP_RIGHT_MARGIN:
375 g_value_set_double(value, data->right_margin);
376 break;
377 case PROP_BOTTOM_MARGIN:
378 g_value_set_double(value, data->bottom_margin);
379 break;
380 case PROP_LEFT_MARGIN:
381 g_value_set_double(value, data->left_margin);
382 break;
383 case PROP_HAS_FRAME:
384 g_value_set_boolean(value, data->has_frame);
385 break;
386 case PROP_TOP_PADDING:
387 g_value_set_double(value, data->top_padding);
388 break;
389 case PROP_RIGHT_PADDING:
390 g_value_set_double(value, data->right_padding);
391 break;
392 case PROP_BOTTOM_PADDING:
393 g_value_set_double(value, data->bottom_padding);
394 break;
395 case PROP_LEFT_PADDING:
396 g_value_set_double(value, data->left_padding);
397 break;
398 default:
399 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
400 break;
404 static void
405 _adg_set_property(GObject *object, guint prop_id,
406 const GValue *value, GParamSpec *pspec)
408 AdgCanvas *canvas;
409 AdgCanvasPrivate *data;
410 AdgTitleBlock *title_block;
412 canvas = (AdgCanvas *) object;
413 data = canvas->data;
415 switch (prop_id) {
416 case PROP_SIZE:
417 cpml_pair_copy(&data->size, g_value_get_boxed(value));
418 break;
419 case PROP_SCALES:
420 g_strfreev(data->scales);
421 data->scales = g_value_dup_boxed(value);
422 break;
423 case PROP_BACKGROUND_DRESS:
424 data->background_dress = g_value_get_enum(value);
425 break;
426 case PROP_FRAME_DRESS:
427 data->frame_dress = g_value_get_enum(value);
428 break;
429 case PROP_TITLE_BLOCK:
430 title_block = g_value_get_object(value);
431 if (title_block) {
432 g_object_ref(title_block);
433 adg_entity_set_parent((AdgEntity *) title_block,
434 (AdgEntity *) canvas);
436 if (data->title_block)
437 g_object_unref(data->title_block);
438 data->title_block = title_block;
439 break;
440 case PROP_TOP_MARGIN:
441 _adg_update_margin(canvas, &data->top_margin, &data->size.y,
442 g_value_get_double(value));
443 break;
444 case PROP_RIGHT_MARGIN:
445 _adg_update_margin(canvas, &data->right_margin, &data->size.x,
446 g_value_get_double(value));
447 break;
448 case PROP_BOTTOM_MARGIN:
449 _adg_update_margin(canvas, &data->bottom_margin, &data->size.y,
450 g_value_get_double(value));
451 break;
452 case PROP_LEFT_MARGIN:
453 _adg_update_margin(canvas, &data->left_margin, &data->size.x,
454 g_value_get_double(value));
455 break;
456 case PROP_HAS_FRAME:
457 data->has_frame = g_value_get_boolean(value);
458 break;
459 case PROP_TOP_PADDING:
460 data->top_padding = g_value_get_double(value);
461 break;
462 case PROP_RIGHT_PADDING:
463 data->right_padding = g_value_get_double(value);
464 break;
465 case PROP_BOTTOM_PADDING:
466 data->bottom_padding = g_value_get_double(value);
467 break;
468 case PROP_LEFT_PADDING:
469 data->left_padding = g_value_get_double(value);
470 break;
471 default:
472 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
473 break;
479 * adg_canvas_new:
481 * Creates a new empty canvas object.
483 * Returns: (transfer full): the newly created canvas.
485 * Since: 1.0
487 AdgCanvas *
488 adg_canvas_new(void)
490 return g_object_new(ADG_TYPE_CANVAS, NULL);
494 * adg_canvas_set_size:
495 * @canvas: an #AdgCanvas
496 * @size: (transfer none): the new size for the canvas
498 * Sets a specific size on @canvas. The x and/or y
499 * component of @size can be set to 0, in which case
500 * the exact value will be autocalculated, that is the
501 * size component returned by adg_entity_get_extents()
502 * on @canvas will be used instead.
504 * Since: 1.0
506 void
507 adg_canvas_set_size(AdgCanvas *canvas, const CpmlPair *size)
509 g_return_if_fail(ADG_IS_CANVAS(canvas));
510 g_return_if_fail(size != NULL);
512 g_object_set(canvas, "size", size, NULL);
516 * adg_canvas_set_size_explicit:
517 * @canvas: an #AdgCanvas
518 * @x: the new width of the canvas or 0 to reset
519 * @y: the new height of the canvas or 0 to reset
521 * A convenient function to set the size of @canvas using
522 * explicit coordinates. Check adg_canvas_set_size() for
523 * further details.
525 * Since: 1.0
527 void
528 adg_canvas_set_size_explicit(AdgCanvas *canvas, gdouble x, gdouble y)
530 CpmlPair size;
532 size.x = x;
533 size.y = y;
535 adg_canvas_set_size(canvas, &size);
539 * adg_canvas_get_size:
540 * @canvas: an #AdgCanvas
542 * Gets the specific size set on @canvas. The x and/or y
543 * components of the returned #CpmlPair could be 0, in which
544 * case the size returned by adg_entity_get_extents() on @canvas
545 * will be used instead.
547 * Returns: (transfer none): the explicit size set on this canvas or <constant>NULL</constant> on errors.
549 * Since: 1.0
551 const CpmlPair *
552 adg_canvas_get_size(AdgCanvas *canvas)
554 AdgCanvasPrivate *data;
556 g_return_val_if_fail(ADG_IS_CANVAS(canvas), NULL);
558 data = canvas->data;
559 return &data->size;
563 * adg_canvas_set_scales:
564 * @canvas: an #AdgCanvas
565 * @...: <constant>NULL</constant> terminated list of strings
567 * Sets the scales allowed by @canvas. Every scale identifies a
568 * specific factor to be applied to the local matrix of @canvas.
569 * When adg_canvas_autoscale() will be called, the greatest
570 * scale that can render every entity inside a box of
571 * #AdgCanvas:size dimensions will be applied. The drawing will
572 * be centered inside that box.
574 * Every scale should be expressed with a string in the form of
575 * "x:y", where x and y are positive integers that identifies
576 * numerator an denominator of a fraction. That string itself
577 * will be put into the title block when used.
579 * Since: 1.0
581 void
582 adg_canvas_set_scales(AdgCanvas *canvas, ...)
584 va_list var_args;
586 va_start(var_args, canvas);
587 adg_canvas_set_scales_valist(canvas, var_args);
588 va_end(var_args);
592 * adg_canvas_set_scales_valist:
593 * @canvas: an #AdgCanvas
594 * @var_args: <constant>NULL</constant> terminated list of strings
596 * Vararg variant of adg_canvas_set_scales().
598 * Since: 1.0
600 void
601 adg_canvas_set_scales_valist(AdgCanvas *canvas, va_list var_args)
603 gchar **scales;
604 const gchar *scale;
605 gint n;
607 g_return_if_fail(ADG_IS_CANVAS(canvas));
609 scales = NULL;
610 n = 0;
611 while ((scale = va_arg(var_args, const gchar *)) != NULL) {
612 ++n;
613 scales = g_realloc(scales, sizeof(const gchar *) * (n + 1));
614 scales[n-1] = g_strdup(scale);
615 scales[n] = NULL;
618 g_object_set(canvas, "scales", scales, NULL);
619 g_strfreev(scales);
623 * adg_canvas_set_scales_array: (rename-to adg_canvas_set_scales)
624 * @canvas: an #AdgCanvas
625 * @scales: (array zero-terminated=1) (allow-none): <constant>NULL</constant> terminated array of scales
627 * Array variant of adg_canvas_set_scales().
629 * Since: 1.0
631 void
632 adg_canvas_set_scales_array(AdgCanvas *canvas, gchar **scales)
634 g_return_if_fail(ADG_IS_CANVAS(canvas));
635 g_object_set(canvas, "scales", scales, NULL);
639 * adg_canvas_get_scales:
640 * @canvas: an #AdgCanvas
642 * Gets the list of scales set on @canvas: check adg_canvas_set_scales()
643 * to get an idea of what scales are supposed to be.
645 * If no scales are set, <constant>NULL</constant> is returned.
647 * Returns: (element-type utf8) (transfer none): the <constant>NULL</constant> terminated list of valid scales.
649 * Since: 1.0
651 gchar **
652 adg_canvas_get_scales(AdgCanvas *canvas)
654 AdgCanvasPrivate *data;
656 g_return_val_if_fail(ADG_IS_CANVAS(canvas), NULL);
658 data = canvas->data;
659 return data->scales;
663 * adg_canvas_autoscale:
664 * @canvas: an #AdgCanvas
666 * Applies one scale per time, in the order they have been provided
667 * in the adg_canvas_set_scales() call, until the drawing can be
668 * entirely contained into the current paper. If successful, the
669 * scale of the title block is changed accordingly and the drawing
670 * is centered inside the paper.
672 * The paddings are taken into account while computing the drawing
673 * extents.
675 * Since: 1.0
677 void
678 adg_canvas_autoscale(AdgCanvas *canvas)
680 AdgCanvasPrivate *data;
681 gchar **p_scale;
682 AdgEntity *entity;
683 cairo_matrix_t map;
684 CpmlExtents extents;
685 AdgTitleBlock *title_block;
686 CpmlPair delta;
688 g_return_if_fail(ADG_IS_CANVAS(canvas));
689 g_return_if_fail(_ADG_OLD_ENTITY_CLASS->arrange != NULL);
691 data = canvas->data;
692 entity = (AdgEntity *) canvas;
693 title_block = data->title_block;
695 /* Manually calling the arrange() method instead of emitting the "arrange"
696 * signal does not invalidate the global matrix: let's do it right now */
697 adg_entity_global_changed(entity);
699 for (p_scale = data->scales; p_scale != NULL && *p_scale != NULL; ++p_scale) {
700 const gchar *scale = *p_scale;
701 gdouble factor = adg_scale_factor(scale);
702 if (factor <= 0)
703 continue;
705 cairo_matrix_init_scale(&map, factor, factor);
706 adg_entity_set_local_map(entity, &map);
707 adg_entity_local_changed(entity);
709 /* Arrange the entities inside the canvas, but not the canvas itself,
710 * just to get the bounding box of the drawing without the paper */
711 _ADG_OLD_ENTITY_CLASS->arrange(entity);
712 cpml_extents_copy(&extents, adg_entity_get_extents(entity));
714 /* Just in case @canvas is emtpy */
715 if (! extents.is_defined)
716 return;
718 _adg_apply_paddings(canvas, &extents);
720 if (title_block != NULL)
721 adg_title_block_set_scale(title_block, scale);
723 /* Bail out if paper size is not specified or invalid */
724 if (data->size.x <= 0 || data->size.y <= 0)
725 break;
727 /* If the drawing extents are fully contained inside the paper size,
728 * center the drawing in the paper and bail out */
729 delta.x = data->size.x - extents.size.x;
730 delta.y = data->size.y - extents.size.y;
731 if (delta.x >= 0 && delta.y >= 0) {
732 cairo_matrix_t transform;
733 cairo_matrix_init_translate(&transform,
734 delta.x / 2 - extents.org.x,
735 delta.y / 2 - extents.org.y);
736 adg_entity_transform_local_map(entity, &transform,
737 ADG_TRANSFORM_AFTER);
738 break;
744 * adg_canvas_set_background_dress:
745 * @canvas: an #AdgCanvas
746 * @dress: the new #AdgDress to use
748 * Sets a new background dress for rendering @canvas: the new
749 * dress must be a color dress.
751 * Since: 1.0
753 void
754 adg_canvas_set_background_dress(AdgCanvas *canvas, AdgDress dress)
756 g_return_if_fail(ADG_IS_CANVAS(canvas));
757 g_object_set(canvas, "background-dress", dress, NULL);
761 * adg_canvas_get_background_dress:
762 * @canvas: an #AdgCanvas
764 * Gets the background dress to be used in rendering @canvas.
766 * Returns: the current background dress.
768 * Since: 1.0
770 AdgDress
771 adg_canvas_get_background_dress(AdgCanvas *canvas)
773 AdgCanvasPrivate *data;
775 g_return_val_if_fail(ADG_IS_CANVAS(canvas), ADG_DRESS_UNDEFINED);
777 data = canvas->data;
779 return data->background_dress;
783 * adg_canvas_set_frame_dress:
784 * @canvas: an #AdgCanvas
785 * @dress: the new #AdgDress to use
787 * Sets the #AdgCanvas:frame-dress property of @canvas to @dress:
788 * the new dress must be a line dress.
790 * Since: 1.0
792 void
793 adg_canvas_set_frame_dress(AdgCanvas *canvas, AdgDress dress)
795 g_return_if_fail(ADG_IS_CANVAS(canvas));
796 g_object_set(canvas, "frame-dress", dress, NULL);
800 * adg_canvas_get_frame_dress:
801 * @canvas: an #AdgCanvas
803 * Gets the frame dress to be used in rendering the border of @canvas.
805 * Returns: the current frame dress.
807 * Since: 1.0
809 AdgDress
810 adg_canvas_get_frame_dress(AdgCanvas *canvas)
812 AdgCanvasPrivate *data;
814 g_return_val_if_fail(ADG_IS_CANVAS(canvas), ADG_DRESS_UNDEFINED);
816 data = canvas->data;
817 return data->frame_dress;
821 * adg_canvas_set_title_block:
822 * @canvas: an #AdgCanvas
823 * @title_block: (transfer full): a title block
825 * Sets the #AdgCanvas:title-block property of @canvas to @title_block.
827 * Although a title block entity could be added to @canvas in the usual
828 * way, that is using the adg_container_add() method, assigning a title
829 * block with adg_canvas_set_title_block() is somewhat different:
831 * - @title_block will be automatically attached to the bottom right
832 * corner of to the @canvas frame (this could be accomplished in the
833 * usual way too, by resetting the right and bottom paddings);
834 * - the @title_block boundary box is not taken into account while
835 * computing the extents of @canvas.
837 * Since: 1.0
839 void
840 adg_canvas_set_title_block(AdgCanvas *canvas,
841 AdgTitleBlock *title_block)
843 g_return_if_fail(ADG_IS_CANVAS(canvas));
844 g_return_if_fail(title_block == NULL || ADG_IS_TITLE_BLOCK(title_block));
845 g_object_set(canvas, "title-block", title_block, NULL);
849 * adg_canvas_get_title_block:
850 * @canvas: an #AdgCanvas
852 * Gets the #AdgTitleBlock object of @canvas: check
853 * adg_canvas_set_title_block() for details.
855 * The returned entity is owned by @canvas and should not be
856 * modified or freed.
858 * Returns: (transfer none): the title block object or <constant>NULL</constant>.
860 * Since: 1.0
862 AdgTitleBlock *
863 adg_canvas_get_title_block(AdgCanvas *canvas)
865 AdgCanvasPrivate *data;
867 g_return_val_if_fail(ADG_IS_CANVAS(canvas), NULL);
869 data = canvas->data;
870 return data->title_block;
874 * adg_canvas_set_top_margin:
875 * @canvas: an #AdgCanvas
876 * @value: the new margin, in global space
878 * Changes the top margin of @canvas by setting #AdgCanvas:top-margin
879 * to @value. Negative values are allowed.
881 * Since: 1.0
883 void
884 adg_canvas_set_top_margin(AdgCanvas *canvas, gdouble value)
886 g_return_if_fail(ADG_IS_CANVAS(canvas));
887 g_object_set(canvas, "top-margin", value, NULL);
891 * adg_canvas_get_top_margin:
892 * @canvas: an #AdgCanvas
894 * Gets the top margin (in global space) of @canvas.
896 * Returns: the requested margin or 0 on error.
898 * Since: 1.0
900 gdouble
901 adg_canvas_get_top_margin(AdgCanvas *canvas)
903 AdgCanvasPrivate *data;
905 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
907 data = canvas->data;
908 return data->top_margin;
912 * adg_canvas_set_right_margin:
913 * @canvas: an #AdgCanvas
914 * @value: the new margin, in global space
916 * Changes the right margin of @canvas by setting
917 * #AdgCanvas:right-margin to @value. Negative values are allowed.
919 * Since: 1.0
921 void
922 adg_canvas_set_right_margin(AdgCanvas *canvas, gdouble value)
924 g_return_if_fail(ADG_IS_CANVAS(canvas));
925 g_object_set(canvas, "right-margin", value, NULL);
929 * adg_canvas_get_right_margin:
930 * @canvas: an #AdgCanvas
932 * Gets the right margin (in global space) of @canvas.
934 * Returns: the requested margin or 0 on error.
936 * Since: 1.0
938 gdouble
939 adg_canvas_get_right_margin(AdgCanvas *canvas)
941 AdgCanvasPrivate *data;
943 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
945 data = canvas->data;
946 return data->right_margin;
950 * adg_canvas_set_bottom_margin:
951 * @canvas: an #AdgCanvas
952 * @value: the new margin, in global space
954 * Changes the bottom margin of @canvas by setting
955 * #AdgCanvas:bottom-margin to @value. Negative values are allowed.
957 * Since: 1.0
959 void
960 adg_canvas_set_bottom_margin(AdgCanvas *canvas, gdouble value)
962 g_return_if_fail(ADG_IS_CANVAS(canvas));
963 g_object_set(canvas, "bottom-margin", value, NULL);
967 * adg_canvas_get_bottom_margin:
968 * @canvas: an #AdgCanvas
970 * Gets the bottom margin (in global space) of @canvas.
972 * Returns: the requested margin or 0 on error.
974 * Since: 1.0
976 gdouble
977 adg_canvas_get_bottom_margin(AdgCanvas *canvas)
979 AdgCanvasPrivate *data;
981 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
983 data = canvas->data;
984 return data->bottom_margin;
988 * adg_canvas_set_left_margin:
989 * @canvas: an #AdgCanvas
990 * @value: the new margin, in global space
992 * Changes the left margin of @canvas by setting
993 * #AdgCanvas:left-margin to @value. Negative values are allowed.
995 * Since: 1.0
997 void
998 adg_canvas_set_left_margin(AdgCanvas *canvas, gdouble value)
1000 g_return_if_fail(ADG_IS_CANVAS(canvas));
1001 g_object_set(canvas, "left-margin", value, NULL);
1005 * adg_canvas_get_left_margin:
1006 * @canvas: an #AdgCanvas
1008 * Gets the left margin (in global space) of @canvas.
1010 * Returns: the requested margin or 0 on error.
1012 * Since: 1.0
1014 gdouble
1015 adg_canvas_get_left_margin(AdgCanvas *canvas)
1017 AdgCanvasPrivate *data;
1019 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
1021 data = canvas->data;
1022 return data->left_margin;
1026 * adg_canvas_set_margins:
1027 * @canvas: an #AdgCanvas
1028 * @top: top margin, in global space
1029 * @right: right margin, in global space
1030 * @bottom: bottom margin, in global space
1031 * @left: left margin, in global space
1033 * Convenient function to set all the margins at once.
1035 * Since: 1.0
1037 void
1038 adg_canvas_set_margins(AdgCanvas *canvas,
1039 gdouble top, gdouble right,
1040 gdouble bottom, gdouble left)
1042 g_return_if_fail(ADG_IS_CANVAS(canvas));
1043 g_object_set(canvas, "top-margin", top, "right-margin", right,
1044 "bottom-margin", bottom, "left-margin", left, NULL);
1048 * adg_canvas_apply_margins:
1049 * @canvas: an #AdgCanvas
1050 * @extents: where apply the margins
1052 * A convenient function to apply the margins of @canvas to the
1053 * arbitrary #CpmlExtents struct @extents. "Apply" means @extents
1054 * are enlarged of the specific margin values.
1056 * Since: 1.0
1058 void
1059 adg_canvas_apply_margins(AdgCanvas *canvas, CpmlExtents *extents)
1061 g_return_if_fail(ADG_IS_CANVAS(canvas));
1062 g_return_if_fail(extents != NULL);
1064 if (extents->is_defined) {
1065 AdgCanvasPrivate *data = canvas->data;
1067 extents->org.x -= data->left_margin;
1068 extents->org.y -= data->top_margin;
1069 extents->size.x += data->left_margin + data->right_margin;
1070 extents->size.y += data->top_margin + data->bottom_margin;
1075 * adg_canvas_switch_frame:
1076 * @canvas: an #AdgCanvas
1077 * @new_state: the new flag status
1079 * Sets a new status on the #AdgCanvas:has-frame
1080 * property: <constant>TRUE</constant> means a border around
1081 * the canvas extents (less the margins) should be rendered.
1083 * Since: 1.0
1085 void
1086 adg_canvas_switch_frame(AdgCanvas *canvas, gboolean new_state)
1088 g_return_if_fail(ADG_IS_CANVAS(canvas));
1089 g_object_set(canvas, "has-frame", new_state, NULL);
1093 * adg_canvas_has_frame:
1094 * @canvas: an #AdgCanvas
1096 * Gets the current status of the #AdgCanvas:has-frame property,
1097 * that is whether a border around the canvas extents (less the
1098 * margins) should be rendered (<constant>TRUE</constant>) or not
1099 * (<constant>FALSE</constant>).
1101 * Returns: the current status of the frame flag.
1103 * Since: 1.0
1105 gboolean
1106 adg_canvas_has_frame(AdgCanvas *canvas)
1108 AdgCanvasPrivate *data;
1110 g_return_val_if_fail(ADG_IS_CANVAS(canvas), FALSE);
1112 data = canvas->data;
1113 return data->has_frame;
1117 * adg_canvas_set_top_padding:
1118 * @canvas: an #AdgCanvas
1119 * @value: the new padding, in global space
1121 * Changes the top padding of @canvas by setting
1122 * #AdgCanvas:top-padding to @value. Negative values are allowed.
1124 * Since: 1.0
1126 void
1127 adg_canvas_set_top_padding(AdgCanvas *canvas, gdouble value)
1129 g_return_if_fail(ADG_IS_CANVAS(canvas));
1130 g_object_set(canvas, "top-padding", value, NULL);
1134 * adg_canvas_get_top_padding:
1135 * @canvas: an #AdgCanvas
1137 * Gets the top padding (in global space) of @canvas.
1139 * Returns: the requested padding or 0 on error.
1141 * Since: 1.0
1143 gdouble
1144 adg_canvas_get_top_padding(AdgCanvas *canvas)
1146 AdgCanvasPrivate *data;
1148 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
1150 data = canvas->data;
1151 return data->top_padding;
1155 * adg_canvas_set_right_padding:
1156 * @canvas: an #AdgCanvas
1157 * @value: the new padding, in global space
1159 * Changes the right padding of @canvas by setting #AdgCanvas:right-padding
1160 * to @value. Negative values are allowed.
1162 * Since: 1.0
1164 void
1165 adg_canvas_set_right_padding(AdgCanvas *canvas, gdouble value)
1167 g_return_if_fail(ADG_IS_CANVAS(canvas));
1168 g_object_set(canvas, "right-padding", value, NULL);
1172 * adg_canvas_get_right_padding:
1173 * @canvas: an #AdgCanvas
1175 * Gets the right padding (in global space) of @canvas.
1177 * Returns: the requested padding or 0 on error.
1179 * Since: 1.0
1181 gdouble
1182 adg_canvas_get_right_padding(AdgCanvas *canvas)
1184 AdgCanvasPrivate *data;
1186 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
1188 data = canvas->data;
1189 return data->right_padding;
1194 * adg_canvas_set_bottom_padding:
1195 * @canvas: an #AdgCanvas
1196 * @value: the new padding, in global space
1198 * Changes the bottom padding of @canvas by setting
1199 * #AdgCanvas:bottom-padding to @value. Negative values are allowed.
1201 * Since: 1.0
1203 void
1204 adg_canvas_set_bottom_padding(AdgCanvas *canvas, gdouble value)
1206 g_return_if_fail(ADG_IS_CANVAS(canvas));
1207 g_object_set(canvas, "bottom-padding", value, NULL);
1211 * adg_canvas_get_bottom_padding:
1212 * @canvas: an #AdgCanvas
1214 * Gets the bottom padding (in global space) of @canvas.
1216 * Returns: the requested padding or 0 on error.
1218 * Since: 1.0
1220 gdouble
1221 adg_canvas_get_bottom_padding(AdgCanvas *canvas)
1223 AdgCanvasPrivate *data;
1225 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
1227 data = canvas->data;
1228 return data->bottom_padding;
1232 * adg_canvas_set_left_padding:
1233 * @canvas: an #AdgCanvas
1234 * @value: the new padding, in global space
1236 * Changes the left padding of @canvas by setting
1237 * #AdgCanvas:left-padding to @value. Negative values are allowed.
1239 * Since: 1.0
1241 void
1242 adg_canvas_set_left_padding(AdgCanvas *canvas, gdouble value)
1244 g_return_if_fail(ADG_IS_CANVAS(canvas));
1245 g_object_set(canvas, "left-padding", value, NULL);
1249 * adg_canvas_get_left_padding:
1250 * @canvas: an #AdgCanvas
1252 * Gets the left padding (in global space) of @canvas.
1254 * Returns: the requested padding or 0 on error.
1256 * Since: 1.0
1258 gdouble
1259 adg_canvas_get_left_padding(AdgCanvas *canvas)
1261 AdgCanvasPrivate *data;
1263 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
1265 data = canvas->data;
1266 return data->left_padding;
1270 * adg_canvas_set_paddings:
1271 * @canvas: an #AdgCanvas
1272 * @top: top padding, in global space
1273 * @right: right padding, in global space
1274 * @bottom: bottom padding, in global space
1275 * @left: left padding, in global space
1277 * Convenient function to set all the paddings at once.
1279 * Since: 1.0
1281 void
1282 adg_canvas_set_paddings(AdgCanvas *canvas,
1283 gdouble top, gdouble right,
1284 gdouble bottom, gdouble left)
1286 g_return_if_fail(ADG_IS_CANVAS(canvas));
1287 g_object_set(canvas, "top-padding", top, "right-padding", right,
1288 "bottom-padding", bottom, "left-padding", left, NULL);
1292 static void
1293 _adg_global_changed(AdgEntity *entity)
1295 AdgCanvasPrivate *data = ((AdgCanvas *) entity)->data;
1297 if (_ADG_OLD_ENTITY_CLASS->global_changed)
1298 _ADG_OLD_ENTITY_CLASS->global_changed(entity);
1300 if (data->title_block)
1301 adg_entity_global_changed((AdgEntity *) data->title_block);
1304 static void
1305 _adg_local_changed(AdgEntity *entity)
1307 AdgCanvasPrivate *data = ((AdgCanvas *) entity)->data;
1308 AdgTitleBlock *title_block = data->title_block;
1310 if (_ADG_OLD_ENTITY_CLASS->local_changed)
1311 _ADG_OLD_ENTITY_CLASS->local_changed(entity);
1313 if (title_block != NULL) {
1314 const gchar *scale = adg_title_block_get_scale(title_block);
1316 if (scale != NULL && scale[0] != '\0') {
1317 const cairo_matrix_t *map = adg_entity_get_local_map(entity);
1318 gdouble factor = adg_scale_factor(scale);
1320 if (map->xx != factor || map->yy != factor)
1321 adg_title_block_set_scale(title_block, "---");
1324 adg_entity_local_changed((AdgEntity *) title_block);
1328 static void
1329 _adg_invalidate(AdgEntity *entity)
1331 AdgCanvasPrivate *data = ((AdgCanvas *) entity)->data;
1333 if (_ADG_OLD_ENTITY_CLASS->invalidate)
1334 _ADG_OLD_ENTITY_CLASS->invalidate(entity);
1336 if (data->title_block)
1337 adg_entity_invalidate((AdgEntity *) data->title_block);
1340 static void
1341 _adg_arrange(AdgEntity *entity)
1343 AdgCanvas *canvas;
1344 AdgCanvasPrivate *data;
1345 CpmlExtents extents;
1347 if (_ADG_OLD_ENTITY_CLASS->arrange)
1348 _ADG_OLD_ENTITY_CLASS->arrange(entity);
1350 cpml_extents_copy(&extents, adg_entity_get_extents(entity));
1352 /* The extents should be defined, otherwise there is no drawing */
1353 g_return_if_fail(extents.is_defined);
1355 canvas = (AdgCanvas *) entity;
1356 data = canvas->data;
1358 _adg_apply_paddings(canvas, &extents);
1360 if (data->size.x > 0 || data->size.y > 0) {
1361 const cairo_matrix_t *global = adg_entity_get_global_matrix(entity);
1362 CpmlExtents paper;
1364 paper.org.x = 0;
1365 paper.org.y = 0;
1366 paper.size.x = data->size.x;
1367 paper.size.y = data->size.y;
1369 cairo_matrix_transform_point(global, &paper.org.x, &paper.org.y);
1370 cairo_matrix_transform_distance(global, &paper.size.x, &paper.size.y);
1372 if (data->size.x > 0) {
1373 extents.org.x = paper.org.x;
1374 extents.size.x = paper.size.x;
1376 if (data->size.y > 0) {
1377 extents.org.y = paper.org.y;
1378 extents.size.y = paper.size.y;
1382 if (data->title_block) {
1383 AdgEntity *title_block_entity;
1384 const CpmlExtents *title_block_extents;
1385 CpmlPair shift;
1387 title_block_entity = (AdgEntity *) data->title_block;
1388 adg_entity_arrange(title_block_entity);
1389 title_block_extents = adg_entity_get_extents(title_block_entity);
1391 shift.x = extents.org.x + extents.size.x - title_block_extents->org.x
1392 - title_block_extents->size.x;
1393 shift.y = extents.org.y + extents.size.y - title_block_extents->org.y
1394 - title_block_extents->size.y;
1396 /* The following block could be optimized by skipping tiny shift,
1397 * usually left by rounding errors */
1398 if (shift.x != 0 || shift.y != 0) {
1399 cairo_matrix_t unglobal, map;
1400 adg_matrix_copy(&unglobal, adg_entity_get_global_matrix(entity));
1401 cairo_matrix_invert(&unglobal);
1403 cairo_matrix_transform_distance(&unglobal, &shift.x, &shift.y);
1404 cairo_matrix_init_translate(&map, shift.x, shift.y);
1405 adg_entity_transform_global_map(title_block_entity, &map,
1406 ADG_TRANSFORM_AFTER);
1408 adg_entity_global_changed(title_block_entity);
1409 adg_entity_arrange(title_block_entity);
1410 title_block_extents = adg_entity_get_extents(title_block_entity);
1411 cpml_extents_add(&extents, title_block_extents);
1415 /* Impose the new extents */
1416 adg_entity_set_extents(entity, &extents);
1419 static void
1420 _adg_render(AdgEntity *entity, cairo_t *cr)
1422 AdgCanvasPrivate *data;
1423 const CpmlExtents *extents;
1425 data = ((AdgCanvas *) entity)->data;
1426 extents = adg_entity_get_extents(entity);
1428 cairo_save(cr);
1430 /* Background fill */
1431 cairo_rectangle(cr, extents->org.x - data->left_margin,
1432 extents->org.y - data->top_margin,
1433 extents->size.x + data->left_margin + data->right_margin,
1434 extents->size.y + data->top_margin + data->bottom_margin);
1435 adg_entity_apply_dress(entity, data->background_dress, cr);
1436 cairo_fill(cr);
1438 /* Frame line */
1439 if (data->has_frame) {
1440 cairo_rectangle(cr, extents->org.x, extents->org.y,
1441 extents->size.x, extents->size.y);
1442 cairo_transform(cr, adg_entity_get_global_matrix(entity));
1443 adg_entity_apply_dress(entity, data->frame_dress, cr);
1444 cairo_stroke(cr);
1447 cairo_restore(cr);
1449 if (data->title_block)
1450 adg_entity_render((AdgEntity *) data->title_block, cr);
1452 if (_ADG_OLD_ENTITY_CLASS->render)
1453 _ADG_OLD_ENTITY_CLASS->render(entity, cr);
1456 static void
1457 _adg_apply_paddings(AdgCanvas *canvas, CpmlExtents *extents)
1459 AdgCanvasPrivate *data = canvas->data;
1461 extents->org.x -= data->left_padding;
1462 extents->size.x += data->left_padding + data->right_padding;
1463 extents->org.y -= data->top_padding;
1464 extents->size.y += data->top_padding + data->bottom_padding;
1469 * adg_canvas_export:
1470 * @canvas: an #AdgCanvas
1471 * @type: (type gint): the export format
1472 * @file: the name of the resulting file
1473 * @gerror: (allow-none): return location for errors
1475 * A helper function that provides a bare export functionality.
1476 * It basically exports the drawing in @canvas in the @file
1477 * in the @type format. Any error will be reported in @gerror,
1478 * if not <constant>NULL</constant>.
1480 * Returns: <constant>TRUE</constant> on success, <constant>FALSE</constant> otherwise.
1482 * Since: 1.0
1484 gboolean
1485 adg_canvas_export(AdgCanvas *canvas, cairo_surface_type_t type,
1486 const gchar *file, GError **gerror)
1488 AdgEntity *entity;
1489 const CpmlExtents *extents;
1490 gdouble top, bottom, left, right, width, height;
1491 cairo_surface_t *surface;
1492 cairo_t *cr;
1493 cairo_status_t status;
1495 g_return_val_if_fail(ADG_IS_CANVAS(canvas), FALSE);
1496 g_return_val_if_fail(file != NULL, FALSE);
1497 g_return_val_if_fail(gerror == NULL || *gerror == NULL, FALSE);
1499 entity = (AdgEntity *) canvas;
1501 adg_entity_arrange(entity);
1502 extents = adg_entity_get_extents(entity);
1504 top = adg_canvas_get_top_margin(canvas);
1505 bottom = adg_canvas_get_bottom_margin(canvas);
1506 left = adg_canvas_get_left_margin(canvas);
1507 right = adg_canvas_get_right_margin(canvas);
1508 width = extents->size.x + left + right;
1509 height = extents->size.y + top + bottom;
1510 surface = NULL;
1512 switch (type) {
1513 #ifdef CAIRO_HAS_PNG_FUNCTIONS
1514 case CAIRO_SURFACE_TYPE_IMAGE:
1515 surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height);
1516 break;
1517 #endif
1518 #ifdef CAIRO_HAS_PDF_SURFACE
1519 case CAIRO_SURFACE_TYPE_PDF:
1520 surface = cairo_pdf_surface_create(file, width, height);
1521 break;
1522 #endif
1523 #ifdef CAIRO_HAS_PS_SURFACE
1524 case CAIRO_SURFACE_TYPE_PS:
1525 surface = cairo_ps_surface_create(file, width, height);
1526 break;
1527 #endif
1528 #ifdef CAIRO_HAS_SVG_SURFACE
1529 case CAIRO_SURFACE_TYPE_SVG:
1530 surface = cairo_svg_surface_create(file, width, height);
1531 break;
1532 #endif
1533 default:
1534 break;
1537 if (surface == NULL) {
1538 g_set_error(gerror, ADG_CANVAS_ERROR, ADG_CANVAS_ERROR_SURFACE,
1539 "unable to handle surface type '%d'",
1540 type);
1541 return FALSE;
1544 cairo_surface_set_device_offset(surface, left, top);
1545 cr = cairo_create(surface);
1546 cairo_surface_destroy(surface);
1548 adg_entity_render(ADG_ENTITY(canvas), cr);
1550 if (cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_IMAGE) {
1551 status = cairo_surface_write_to_png(surface, file);
1552 } else {
1553 cairo_show_page(cr);
1554 status = cairo_status(cr);
1557 cairo_destroy(cr);
1559 if (status != CAIRO_STATUS_SUCCESS) {
1560 g_set_error(gerror, ADG_CANVAS_ERROR, ADG_CANVAS_ERROR_CAIRO,
1561 "cairo reported '%s'",
1562 cairo_status_to_string(status));
1563 return FALSE;
1566 return TRUE;
1570 #if GTK3_ENABLED || GTK2_ENABLED
1571 #include <gtk/gtk.h>
1573 static void
1574 _adg_update_margin(AdgCanvas *canvas, gdouble *margin,
1575 gdouble *side, gdouble new_margin)
1577 GtkPageSetup *page_setup;
1578 gdouble old_margin;
1580 old_margin = *margin;
1581 *margin = new_margin;
1583 page_setup = adg_canvas_get_page_setup(canvas);
1584 if (page_setup == NULL)
1585 return;
1587 *side += old_margin - new_margin;
1591 * adg_canvas_set_paper:
1592 * @canvas: an #AdgCanvas
1593 * @paper_name: a paper name
1594 * @orientation: the page orientation
1596 * A convenient function to set size and margins of @canvas
1597 * using a @paper_name and an @orientation value. This should
1598 * be a PWG 5101.1-2002 paper name and it will be passed as
1599 * is to gtk_paper_size_new(), so use any valid name accepted
1600 * by that function.
1602 * This has the same effect as creating a #GtkPaperSetup object
1603 * and binding it to @canvas with adg_canvas_set_page_setup():
1604 * check its documentation for knowing the implications.
1606 * To reset the size to its default behavior (i.e. autocalculate
1607 * it from the entities) just call adg_canvas_set_size_explicit()
1608 * passing <constant>0, 0</constant>.
1610 * If you want to use your own margins on a named paper size,
1611 * set them <emphasis>after</emphasis> the call to this function.
1613 * Since: 1.0
1615 void
1616 adg_canvas_set_paper(AdgCanvas *canvas,
1617 const gchar *paper_name,
1618 GtkPageOrientation orientation)
1620 GtkPageSetup *page_setup;
1621 GtkPaperSize *paper_size;
1623 g_return_if_fail(ADG_IS_CANVAS(canvas));
1624 g_return_if_fail(paper_name != NULL);
1626 page_setup = gtk_page_setup_new();
1627 paper_size = gtk_paper_size_new(paper_name);
1629 gtk_page_setup_set_paper_size(page_setup, paper_size);
1630 gtk_page_setup_set_orientation(page_setup, orientation);
1631 gtk_paper_size_free(paper_size);
1633 adg_canvas_set_page_setup(canvas, page_setup);
1634 g_object_unref(page_setup);
1638 * adg_canvas_set_page_setup:
1639 * @canvas: an #AdgCanvas
1640 * @page_setup: (allow-none) (transfer none): the page setup
1642 * A convenient function to setup the page of @canvas so it can
1643 * also be subsequentially used for printing. It is allowed to
1644 * pass <constant>NULL</constant> as @page_setup to restore the
1645 * default page setup.
1647 * When a canvas is bound to a page setup, the paper size is kept
1648 * constant. This implies increasing or decreasing the margins
1649 * respectively decreases or increases the page size of the
1650 * relevant dimension, e.g. increasing the right margin decreases
1651 * the width (the x component of the size).
1653 * A reference to @page_setup is added, so there is no need to keep
1654 * alive this object after a call to this function. @page_setup can
1655 * be retrieved at any time with adg_canvas_get_page_setup().
1657 * The size and margins provided by @page_setup are immediately
1658 * used to set size and margins of @canvas. This means using
1659 * <constant>NULL</constant> as @page_setup just releases the
1660 * reference to the previous @page_setup object... all the page
1661 * settings are still retained.
1663 * If you want to use your own margins on a page setup,
1664 * set them on canvas <emphasis>after</emphasis> the call to this
1665 * function or set them on @page_setup <emphasis>before</emphasis>.
1667 * <informalexample><programlisting language="C">
1668 * // By default, canvas does not have an explicit size
1669 * adg_canvas_set_page_setup(canvas, a4);
1670 * g_object_unref(a4);
1671 * // Now canvas has size and margins specified by a4
1672 * // (and a4 should be a prefilled GtkPageSetup object).
1673 * adg_canvas_set_page_setup(canvas, NULL);
1674 * // Now canvas is no more bound to a4 and that object (if
1675 * // not referenced anywhere else) can be garbage-collected.
1676 * // The page setup of canvas has not changed though.
1677 * adg_canvas_set_size_explicit(canvas, 0, 0);
1678 * // Now the page size of canvas has been restored to
1679 * // their default behavior.
1680 * </programlisting></informalexample>
1682 * Since: 1.0
1684 void
1685 adg_canvas_set_page_setup(AdgCanvas *canvas, GtkPageSetup *page_setup)
1687 gdouble top, right, bottom, left;
1688 CpmlPair size;
1690 g_return_if_fail(ADG_IS_CANVAS(canvas));
1692 if (page_setup == NULL) {
1693 /* By convention, NULL resets the default page but
1694 * does not change any other property */
1695 g_object_set_data((GObject *) canvas, "_adg_page_setup", NULL);
1696 return;
1699 g_return_if_fail(GTK_IS_PAGE_SETUP(page_setup));
1701 top = gtk_page_setup_get_top_margin(page_setup, GTK_UNIT_POINTS);
1702 right = gtk_page_setup_get_right_margin(page_setup, GTK_UNIT_POINTS);
1703 bottom = gtk_page_setup_get_bottom_margin(page_setup, GTK_UNIT_POINTS);
1704 left = gtk_page_setup_get_left_margin(page_setup, GTK_UNIT_POINTS);
1705 size.x = gtk_page_setup_get_page_width(page_setup, GTK_UNIT_POINTS);
1706 size.y = gtk_page_setup_get_page_height(page_setup, GTK_UNIT_POINTS);
1708 adg_canvas_set_size(canvas, &size);
1709 adg_canvas_set_margins(canvas, top, right, bottom, left);
1711 g_object_ref(page_setup);
1712 g_object_set_data_full((GObject *) canvas, "_adg_page_setup",
1713 page_setup, g_object_unref);
1717 * adg_canvas_get_page_setup:
1718 * @canvas: an #AdgCanvas
1720 * If adg_canvas_set_page_setup() is called, a #GtkPageSetup object
1721 * is created and bound to @canvas. This metho returns a pointer
1722 * to that internal object or <constant>NULL</constant> if
1723 * adg_canvas_set_page_setup() has not been called before.
1725 * Returns: (allow-none) (transfer none): the #GtkPageSetup with size and margins of @canvas of <constant>NULL</constant> on no setup found or errors.
1727 * Since: 1.0
1729 GtkPageSetup *
1730 adg_canvas_get_page_setup(AdgCanvas *canvas)
1732 g_return_val_if_fail(ADG_IS_CANVAS(canvas), NULL);
1733 return g_object_get_data((GObject *) canvas, "_adg_page_setup");
1736 #else
1738 static void
1739 _adg_update_margin(AdgCanvas *canvas, gdouble *margin,
1740 gdouble *side, gdouble new_margin)
1742 *margin = new_margin;
1744 #endif