adg: use G_PRIVATE_ADD and friends
[adg.git] / src / adg / adg-canvas.c
blob917fd4294bf6d173e6a02fec757887212fc755de
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2019 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>.
45 * The ratio between the media unit (typically points on printing
46 * surfaces and pixels on video surfaces) can be changed with
47 * adg_canvas_set_factor().
49 * By default either width and height are autocalculated, i.e. they
50 * are initially set to 0. In this case the arrange() phase is executed.
51 * Margins and paddings are then added to the extents to get the
52 * border coordinates and the final bounding box.
54 * Instead, when the size is explicitely set, the final bounding
55 * box is forcibly set to this value without taking the canvas
56 * extents into account. The margins are then subtracted to get
57 * the coordinates of the border. In this case the paddings are
58 * simply ignoredby the arrange phase. They are still used by
59 * adg_canvas_autoscale() though, if called.
61 * Since: 1.0
62 **/
64 /**
65 * AdgCanvas:
67 * All fields are private and should not be used directly.
68 * Use its public methods instead.
70 * Since: 1.0
71 **/
73 /**
74 * ADG_CANVAS_ERROR:
76 * Error domain for canvas processing. Errors in this domain will be from the
77 * #AdgCanvasError enumeration. See #GError for information on error domains.
79 * Since: 1.0
80 **/
82 /**
83 * AdgCanvasError:
84 * @ADG_CANVAS_ERROR_SURFACE: Invalid surface type.
85 * @ADG_CANVAS_ERROR_CAIRO: The underlying cairo library returned an error.
87 * Error codes returned by #AdgCanvas methods.
89 * Since: 1.0
90 **/
93 #include "adg-internal.h"
95 #include "adg-container.h"
96 #include "adg-table.h"
97 #include "adg-title-block.h"
98 #include "adg-style.h"
99 #include "adg-color-style.h"
100 #include "adg-dress.h"
101 #include "adg-param-dress.h"
103 #include <adg-canvas.h>
104 #include "adg-canvas-private.h"
106 #ifdef CAIRO_HAS_PS_SURFACE
107 #include <cairo-ps.h>
108 #endif
109 #ifdef CAIRO_HAS_PDF_SURFACE
110 #include <cairo-pdf.h>
111 #endif
112 #ifdef CAIRO_HAS_SVG_SURFACE
113 #include <cairo-svg.h>
114 #endif
117 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_canvas_parent_class)
118 #define _ADG_OLD_ENTITY_CLASS ((AdgEntityClass *) adg_canvas_parent_class)
121 G_DEFINE_TYPE_WITH_PRIVATE(AdgCanvas, adg_canvas, ADG_TYPE_CONTAINER)
123 enum {
124 PROP_0,
125 PROP_SIZE,
126 PROP_FACTOR,
127 PROP_SCALES,
128 PROP_BACKGROUND_DRESS,
129 PROP_FRAME_DRESS,
130 PROP_TITLE_BLOCK,
131 PROP_TOP_MARGIN,
132 PROP_RIGHT_MARGIN,
133 PROP_BOTTOM_MARGIN,
134 PROP_LEFT_MARGIN,
135 PROP_HAS_FRAME,
136 PROP_TOP_PADDING,
137 PROP_RIGHT_PADDING,
138 PROP_BOTTOM_PADDING,
139 PROP_LEFT_PADDING
143 static void _adg_dispose (GObject *object);
144 static void _adg_get_property (GObject *object,
145 guint param_id,
146 GValue *value,
147 GParamSpec *pspec);
148 static void _adg_set_property (GObject *object,
149 guint param_id,
150 const GValue *value,
151 GParamSpec *pspec);
152 static void _adg_global_changed (AdgEntity *entity);
153 static void _adg_local_changed (AdgEntity *entity);
154 static void _adg_invalidate (AdgEntity *entity);
155 static void _adg_arrange (AdgEntity *entity);
156 static void _adg_render (AdgEntity *entity,
157 cairo_t *cr);
158 static void _adg_apply_paddings (AdgCanvas *canvas,
159 CpmlExtents *extents);
160 static void _adg_update_margin (AdgCanvas *canvas,
161 gdouble *margin,
162 gdouble *side,
163 gdouble new_margin);
166 GQuark
167 adg_canvas_error_quark(void)
169 static GQuark q;
171 if G_UNLIKELY (q == 0)
172 q = g_quark_from_static_string("adg-canvas-error-quark");
174 return q;
177 static void
178 adg_canvas_class_init(AdgCanvasClass *klass)
180 GObjectClass *gobject_class;
181 AdgEntityClass *entity_class;
182 GParamSpec *param;
184 gobject_class = (GObjectClass *) klass;
185 entity_class = (AdgEntityClass *) klass;
187 gobject_class->dispose = _adg_dispose;
188 gobject_class->get_property = _adg_get_property;
189 gobject_class->set_property = _adg_set_property;
191 entity_class->global_changed = _adg_global_changed;
192 entity_class->local_changed = _adg_local_changed;
193 entity_class->invalidate = _adg_invalidate;
194 entity_class->arrange = _adg_arrange;
195 entity_class->render = _adg_render;
197 param = g_param_spec_boxed("size",
198 P_("Canvas Size"),
199 P_("The size set on this canvas: use 0 to have an automatic dimension based on the canvas extents"),
200 CPML_TYPE_PAIR,
201 G_PARAM_READWRITE);
202 g_object_class_install_property(gobject_class, PROP_SIZE, param);
204 param = g_param_spec_double("factor",
205 P_("Factor"),
206 P_("Global space units per point (1 point == 1/72 inch)"),
207 0, G_MAXDOUBLE, 1,
208 G_PARAM_READWRITE);
209 g_object_class_install_property(gobject_class, PROP_FACTOR, param);
211 param = g_param_spec_boxed("scales",
212 P_("Valid Scales"),
213 P_("List of scales to be tested while autoscaling the drawing"),
214 G_TYPE_STRV,
215 G_PARAM_READWRITE);
216 g_object_class_install_property(gobject_class, PROP_SCALES, param);
218 param = adg_param_spec_dress("background-dress",
219 P_("Background Dress"),
220 P_("The color dress to use for the canvas background"),
221 ADG_DRESS_COLOR_BACKGROUND,
222 G_PARAM_READWRITE);
223 g_object_class_install_property(gobject_class, PROP_BACKGROUND_DRESS, param);
225 param = adg_param_spec_dress("frame-dress",
226 P_("Frame Dress"),
227 P_("Line dress to use while drawing the frame around the canvas"),
228 ADG_DRESS_LINE_FRAME,
229 G_PARAM_READWRITE);
230 g_object_class_install_property(gobject_class, PROP_FRAME_DRESS, param);
232 param = g_param_spec_object("title-block",
233 P_("Title Block"),
234 P_("The title block to assign to this canvas"),
235 ADG_TYPE_TITLE_BLOCK,
236 G_PARAM_READWRITE);
237 g_object_class_install_property(gobject_class, PROP_TITLE_BLOCK, param);
239 param = g_param_spec_double("top-margin",
240 P_("Top Margin"),
241 P_("The margin (in global space) to leave above the frame"),
242 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
243 G_PARAM_READWRITE);
244 g_object_class_install_property(gobject_class, PROP_TOP_MARGIN, param);
246 param = g_param_spec_double("right-margin",
247 P_("Right Margin"),
248 P_("The margin (in global space) to leave empty at the right of the frame"),
249 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
250 G_PARAM_READWRITE);
251 g_object_class_install_property(gobject_class, PROP_RIGHT_MARGIN, param);
253 param = g_param_spec_double("bottom-margin",
254 P_("Bottom Margin"),
255 P_("The margin (in global space) to leave empty below the frame"),
256 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
257 G_PARAM_READWRITE);
258 g_object_class_install_property(gobject_class, PROP_BOTTOM_MARGIN, param);
260 param = g_param_spec_double("left-margin",
261 P_("Left Margin"),
262 P_("The margin (in global space) to leave empty at the left of the frame"),
263 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
264 G_PARAM_READWRITE);
265 g_object_class_install_property(gobject_class, PROP_LEFT_MARGIN, param);
267 param = g_param_spec_boolean("has-frame",
268 P_("Has Frame Flag"),
269 P_("If enabled, a frame using the frame dress will be drawn around the canvas extents, taking into account the margins"),
270 TRUE,
271 G_PARAM_READWRITE);
272 g_object_class_install_property(gobject_class, PROP_HAS_FRAME, param);
274 param = g_param_spec_double("top-padding",
275 P_("Top Padding"),
276 P_("The padding (in global space) to leave empty above between the drawing and the frame"),
277 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
278 G_PARAM_READWRITE);
279 g_object_class_install_property(gobject_class, PROP_TOP_PADDING, param);
281 param = g_param_spec_double("right-padding",
282 P_("Right Padding"),
283 P_("The padding (in global space) to leave empty at the right between the drawing and the frame"),
284 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
285 G_PARAM_READWRITE);
286 g_object_class_install_property(gobject_class, PROP_RIGHT_PADDING, param);
288 param = g_param_spec_double("bottom-padding",
289 P_("Bottom Padding"),
290 P_("The padding (in global space) to leave empty below between the drawing and the frame"),
291 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
292 G_PARAM_READWRITE);
293 g_object_class_install_property(gobject_class, PROP_BOTTOM_PADDING, param);
295 param = g_param_spec_double("left-padding",
296 P_("Left Padding"),
297 P_("The padding (in global space) to leave empty at the left between the drawing and the frame"),
298 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
299 G_PARAM_READWRITE);
300 g_object_class_install_property(gobject_class, PROP_LEFT_PADDING, param);
303 static void
304 adg_canvas_init(AdgCanvas *canvas)
306 AdgCanvasPrivate *data = adg_canvas_get_instance_private(canvas);
307 const gchar *scales[] = {
308 "10:1", "5:1", "3:1", "2:1", "1:1", "1:2", "1:3", "1:5", "1:10",
309 NULL
312 data->size.x = 0;
313 data->size.y = 0;
314 data->factor = 1;
315 data->scales = g_strdupv((gchar **) scales);
316 data->background_dress = ADG_DRESS_COLOR_BACKGROUND;
317 data->frame_dress = ADG_DRESS_LINE_FRAME;
318 data->title_block = NULL;
319 data->top_margin = 15;
320 data->right_margin = 15;
321 data->bottom_margin = 15;
322 data->left_margin = 15;
323 data->has_frame = TRUE;
324 data->top_padding = 15;
325 data->right_padding = 15;
326 data->bottom_padding = 15;
327 data->left_padding = 15;
330 static void
331 _adg_dispose(GObject *object)
333 AdgCanvas *canvas = (AdgCanvas *) object;
334 AdgCanvasPrivate *data = adg_canvas_get_instance_private(canvas);
336 if (data->title_block) {
337 g_object_unref(data->title_block);
338 data->title_block = NULL;
341 if (data->scales != NULL) {
342 g_strfreev(data->scales);
343 data->scales = NULL;
346 if (_ADG_OLD_OBJECT_CLASS->dispose)
347 _ADG_OLD_OBJECT_CLASS->dispose(object);
351 static void
352 _adg_get_property(GObject *object, guint prop_id,
353 GValue *value, GParamSpec *pspec)
355 AdgCanvasPrivate *data = adg_canvas_get_instance_private((AdgCanvas *) object);
357 switch (prop_id) {
358 case PROP_SIZE:
359 g_value_set_boxed(value, &data->size);
360 break;
361 case PROP_FACTOR:
362 g_value_set_double(value, data->factor);
363 break;
364 case PROP_SCALES:
365 g_value_set_boxed(value, data->scales);
366 break;
367 case PROP_BACKGROUND_DRESS:
368 g_value_set_enum(value, data->background_dress);
369 break;
370 case PROP_FRAME_DRESS:
371 g_value_set_enum(value, data->frame_dress);
372 break;
373 case PROP_TITLE_BLOCK:
374 g_value_set_object(value, data->title_block);
375 break;
376 case PROP_TOP_MARGIN:
377 g_value_set_double(value, data->top_margin);
378 break;
379 case PROP_RIGHT_MARGIN:
380 g_value_set_double(value, data->right_margin);
381 break;
382 case PROP_BOTTOM_MARGIN:
383 g_value_set_double(value, data->bottom_margin);
384 break;
385 case PROP_LEFT_MARGIN:
386 g_value_set_double(value, data->left_margin);
387 break;
388 case PROP_HAS_FRAME:
389 g_value_set_boolean(value, data->has_frame);
390 break;
391 case PROP_TOP_PADDING:
392 g_value_set_double(value, data->top_padding);
393 break;
394 case PROP_RIGHT_PADDING:
395 g_value_set_double(value, data->right_padding);
396 break;
397 case PROP_BOTTOM_PADDING:
398 g_value_set_double(value, data->bottom_padding);
399 break;
400 case PROP_LEFT_PADDING:
401 g_value_set_double(value, data->left_padding);
402 break;
403 default:
404 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
405 break;
409 static void
410 _adg_set_property(GObject *object, guint prop_id,
411 const GValue *value, GParamSpec *pspec)
413 AdgCanvas *canvas = (AdgCanvas *) object;
414 AdgCanvasPrivate *data = adg_canvas_get_instance_private(canvas);
415 AdgTitleBlock *title_block;
416 gdouble factor;
418 switch (prop_id) {
419 case PROP_SIZE:
420 cpml_pair_copy(&data->size, g_value_get_boxed(value));
421 break;
422 case PROP_FACTOR:
423 factor = g_value_get_double(value);
424 g_return_if_fail(factor > 0);
425 data->factor = factor;
426 break;
427 case PROP_SCALES:
428 g_strfreev(data->scales);
429 data->scales = g_value_dup_boxed(value);
430 break;
431 case PROP_BACKGROUND_DRESS:
432 data->background_dress = g_value_get_enum(value);
433 break;
434 case PROP_FRAME_DRESS:
435 data->frame_dress = g_value_get_enum(value);
436 break;
437 case PROP_TITLE_BLOCK:
438 title_block = g_value_get_object(value);
439 if (title_block) {
440 g_object_ref(title_block);
441 adg_entity_set_parent((AdgEntity *) title_block,
442 (AdgEntity *) canvas);
444 if (data->title_block)
445 g_object_unref(data->title_block);
446 data->title_block = title_block;
447 break;
448 case PROP_TOP_MARGIN:
449 _adg_update_margin(canvas, &data->top_margin, &data->size.y,
450 g_value_get_double(value));
451 break;
452 case PROP_RIGHT_MARGIN:
453 _adg_update_margin(canvas, &data->right_margin, &data->size.x,
454 g_value_get_double(value));
455 break;
456 case PROP_BOTTOM_MARGIN:
457 _adg_update_margin(canvas, &data->bottom_margin, &data->size.y,
458 g_value_get_double(value));
459 break;
460 case PROP_LEFT_MARGIN:
461 _adg_update_margin(canvas, &data->left_margin, &data->size.x,
462 g_value_get_double(value));
463 break;
464 case PROP_HAS_FRAME:
465 data->has_frame = g_value_get_boolean(value);
466 break;
467 case PROP_TOP_PADDING:
468 data->top_padding = g_value_get_double(value);
469 break;
470 case PROP_RIGHT_PADDING:
471 data->right_padding = g_value_get_double(value);
472 break;
473 case PROP_BOTTOM_PADDING:
474 data->bottom_padding = g_value_get_double(value);
475 break;
476 case PROP_LEFT_PADDING:
477 data->left_padding = g_value_get_double(value);
478 break;
479 default:
480 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
481 break;
487 * adg_canvas_new:
489 * Creates a new empty canvas object.
491 * Returns: (transfer full): the newly created canvas.
493 * Since: 1.0
495 AdgCanvas *
496 adg_canvas_new(void)
498 return g_object_new(ADG_TYPE_CANVAS, NULL);
502 * adg_canvas_set_size:
503 * @canvas: an #AdgCanvas
504 * @size: (transfer none): the new size for the canvas
506 * Sets a specific size on @canvas. The x and/or y
507 * component of @size can be set to 0, in which case
508 * the exact value will be autocalculated, that is the
509 * size component returned by adg_entity_get_extents()
510 * on @canvas will be used instead.
512 * Since: 1.0
514 void
515 adg_canvas_set_size(AdgCanvas *canvas, const CpmlPair *size)
517 g_return_if_fail(ADG_IS_CANVAS(canvas));
518 g_return_if_fail(size != NULL);
520 g_object_set(canvas, "size", size, NULL);
524 * adg_canvas_set_size_explicit:
525 * @canvas: an #AdgCanvas
526 * @x: the new width of the canvas or 0 to reset
527 * @y: the new height of the canvas or 0 to reset
529 * A convenient function to set the size of @canvas using
530 * explicit coordinates. Check adg_canvas_set_size() for
531 * further details.
533 * Since: 1.0
535 void
536 adg_canvas_set_size_explicit(AdgCanvas *canvas, gdouble x, gdouble y)
538 CpmlPair size;
540 size.x = x;
541 size.y = y;
543 adg_canvas_set_size(canvas, &size);
547 * adg_canvas_get_size:
548 * @canvas: an #AdgCanvas
550 * Gets the specific size set on @canvas. The x and/or y
551 * components of the returned #CpmlPair could be 0, in which
552 * case the size returned by adg_entity_get_extents() on @canvas
553 * will be used instead.
555 * Returns: (transfer none): the explicit size set on this canvas or <constant>NULL</constant> on errors.
557 * Since: 1.0
559 const CpmlPair *
560 adg_canvas_get_size(AdgCanvas *canvas)
562 AdgCanvasPrivate *data;
564 g_return_val_if_fail(ADG_IS_CANVAS(canvas), NULL);
566 data = adg_canvas_get_instance_private(canvas);
567 return &data->size;
571 * adg_canvas_set_factor:
572 * @canvas: an #AdgCanvas
573 * @factor: the new factor: must be greater than 0
575 * The ADG library is intentionally unit agnostic, i.e. the global space is
576 * represented in whatever unit you want. There are a couple of cairo APIs
577 * that explicitely requires points though, most notably
578 * cairo_pdf_surface_set_size() and cairo_ps_surface_set_size().
580 * On PDF and postscript surfaces, the AdgCanvas:factor property will be the
581 * number typography points per global space units:
582 * https://en.wikipedia.org/wiki/Point_(typography)
584 * For other surfaces, the factor will be used to scale the final surface to
585 * the specific number of pixels.
587 * As an example, if you are working in millimeters you will set a factor of
588 * 2.83465 on PDF and postscript surfaces (1 mm = 2.83465 points) so the
589 * drawing will show the correct scale on paper. For X11 and PNG surfaces you
590 * will set it depending on the resolution you want to get, e.g. if the
591 * drawing is 100x200 mm and you want a 1000x2000 image, just set it to 10.
593 * Since: 1.0
595 void
596 adg_canvas_set_factor(AdgCanvas *canvas, double factor)
598 g_return_if_fail(ADG_IS_CANVAS(canvas));
599 g_return_if_fail(factor > 0);
601 g_object_set(canvas, "factor", factor, NULL);
605 * adg_canvas_get_factor:
606 * @canvas: an #AdgCanvas
608 * Gets the current factor of @canvas. See adg_canvas_set_factor() to learn
609 * what a factor is in this context.
611 * Returns: the canvas factor or 0 on error.
613 * Since: 1.0
615 double
616 adg_canvas_get_factor(AdgCanvas *canvas)
618 AdgCanvasPrivate *data;
620 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
622 data = adg_canvas_get_instance_private(canvas);
623 return data->factor;
627 * adg_canvas_set_scales:
628 * @canvas: an #AdgCanvas
629 * @...: <constant>NULL</constant> terminated list of strings
631 * Sets the scales allowed by @canvas. Every scale identifies a
632 * specific factor to be applied to the local matrix of @canvas.
633 * When adg_canvas_autoscale() will be called, the greatest
634 * scale that can render every entity inside a box of
635 * #AdgCanvas:size dimensions will be applied. The drawing will
636 * be centered inside that box.
638 * Every scale should be expressed with a string in the form of
639 * "x:y", where x and y are positive integers that identifies
640 * numerator an denominator of a fraction. That string itself
641 * will be put into the title block when used.
643 * Since: 1.0
645 void
646 adg_canvas_set_scales(AdgCanvas *canvas, ...)
648 va_list var_args;
650 va_start(var_args, canvas);
651 adg_canvas_set_scales_valist(canvas, var_args);
652 va_end(var_args);
656 * adg_canvas_set_scales_valist:
657 * @canvas: an #AdgCanvas
658 * @var_args: <constant>NULL</constant> terminated list of strings
660 * Vararg variant of adg_canvas_set_scales().
662 * Since: 1.0
664 void
665 adg_canvas_set_scales_valist(AdgCanvas *canvas, va_list var_args)
667 gchar **scales;
668 const gchar *scale;
669 gint n;
671 g_return_if_fail(ADG_IS_CANVAS(canvas));
673 scales = NULL;
674 n = 0;
675 while ((scale = va_arg(var_args, const gchar *)) != NULL) {
676 ++n;
677 scales = g_realloc(scales, sizeof(const gchar *) * (n + 1));
678 scales[n-1] = g_strdup(scale);
679 scales[n] = NULL;
682 g_object_set(canvas, "scales", scales, NULL);
683 g_strfreev(scales);
687 * adg_canvas_set_scales_array: (rename-to adg_canvas_set_scales)
688 * @canvas: an #AdgCanvas
689 * @scales: (array zero-terminated=1) (allow-none): <constant>NULL</constant> terminated array of scales
691 * Array variant of adg_canvas_set_scales().
693 * Since: 1.0
695 void
696 adg_canvas_set_scales_array(AdgCanvas *canvas, gchar **scales)
698 g_return_if_fail(ADG_IS_CANVAS(canvas));
699 g_object_set(canvas, "scales", scales, NULL);
703 * adg_canvas_get_scales:
704 * @canvas: an #AdgCanvas
706 * Gets the list of scales set on @canvas: check adg_canvas_set_scales()
707 * to get an idea of what scales are supposed to be.
709 * If no scales are set, <constant>NULL</constant> is returned.
711 * Returns: (element-type utf8) (transfer none): the <constant>NULL</constant> terminated list of valid scales.
713 * Since: 1.0
715 gchar **
716 adg_canvas_get_scales(AdgCanvas *canvas)
718 AdgCanvasPrivate *data;
720 g_return_val_if_fail(ADG_IS_CANVAS(canvas), NULL);
722 data = adg_canvas_get_instance_private(canvas);
723 return data->scales;
727 * adg_canvas_autoscale:
728 * @canvas: an #AdgCanvas
730 * Applies one scale per time, in the order they have been provided
731 * in the adg_canvas_set_scales() call, until the drawing can be
732 * entirely contained into the current paper. If successful, the
733 * scale of the title block is changed accordingly and the drawing
734 * is centered inside the paper.
736 * The paddings are taken into account while computing the drawing
737 * extents.
739 * Since: 1.0
741 void
742 adg_canvas_autoscale(AdgCanvas *canvas)
744 AdgCanvasPrivate *data;
745 gchar **p_scale;
746 AdgEntity *entity;
747 cairo_matrix_t map;
748 CpmlExtents extents;
749 AdgTitleBlock *title_block;
750 CpmlPair delta;
752 g_return_if_fail(ADG_IS_CANVAS(canvas));
753 g_return_if_fail(_ADG_OLD_ENTITY_CLASS->arrange != NULL);
755 data = adg_canvas_get_instance_private(canvas);
756 entity = (AdgEntity *) canvas;
757 title_block = data->title_block;
759 /* Manually calling the arrange() method instead of emitting the "arrange"
760 * signal does not invalidate the global matrix: let's do it right now */
761 adg_entity_global_changed(entity);
763 for (p_scale = data->scales; p_scale != NULL && *p_scale != NULL; ++p_scale) {
764 const gchar *scale = *p_scale;
765 gdouble factor = adg_scale_factor(scale);
766 if (factor <= 0)
767 continue;
769 cairo_matrix_init_scale(&map, factor, factor);
770 adg_entity_set_local_map(entity, &map);
771 adg_entity_local_changed(entity);
773 /* Arrange the entities inside the canvas, but not the canvas itself,
774 * just to get the bounding box of the drawing without the paper */
775 _ADG_OLD_ENTITY_CLASS->arrange(entity);
776 cpml_extents_copy(&extents, adg_entity_get_extents(entity));
778 /* Just in case @canvas is empty */
779 if (! extents.is_defined)
780 return;
782 _adg_apply_paddings(canvas, &extents);
784 if (title_block != NULL)
785 adg_title_block_set_scale(title_block, scale);
787 /* Bail out if paper size is not specified or invalid */
788 if (data->size.x <= 0 || data->size.y <= 0)
789 break;
791 /* If the drawing extents are fully contained inside the paper size,
792 * center the drawing in the paper and bail out */
793 delta.x = data->size.x - extents.size.x;
794 delta.y = data->size.y - extents.size.y;
795 if (delta.x >= 0 && delta.y >= 0) {
796 cairo_matrix_t transform;
797 cairo_matrix_init_translate(&transform,
798 delta.x / 2 - extents.org.x,
799 delta.y / 2 - extents.org.y);
800 adg_entity_transform_local_map(entity, &transform,
801 ADG_TRANSFORM_AFTER);
802 break;
808 * adg_canvas_set_background_dress:
809 * @canvas: an #AdgCanvas
810 * @dress: the new #AdgDress to use
812 * Sets a new background dress for rendering @canvas: the new
813 * dress must be a color dress.
815 * Since: 1.0
817 void
818 adg_canvas_set_background_dress(AdgCanvas *canvas, AdgDress dress)
820 g_return_if_fail(ADG_IS_CANVAS(canvas));
821 g_object_set(canvas, "background-dress", dress, NULL);
825 * adg_canvas_get_background_dress:
826 * @canvas: an #AdgCanvas
828 * Gets the background dress to be used in rendering @canvas.
830 * Returns: the current background dress.
832 * Since: 1.0
834 AdgDress
835 adg_canvas_get_background_dress(AdgCanvas *canvas)
837 AdgCanvasPrivate *data;
839 g_return_val_if_fail(ADG_IS_CANVAS(canvas), ADG_DRESS_UNDEFINED);
841 data = adg_canvas_get_instance_private(canvas);
842 return data->background_dress;
846 * adg_canvas_set_frame_dress:
847 * @canvas: an #AdgCanvas
848 * @dress: the new #AdgDress to use
850 * Sets the #AdgCanvas:frame-dress property of @canvas to @dress:
851 * the new dress must be a line dress.
853 * Since: 1.0
855 void
856 adg_canvas_set_frame_dress(AdgCanvas *canvas, AdgDress dress)
858 g_return_if_fail(ADG_IS_CANVAS(canvas));
859 g_object_set(canvas, "frame-dress", dress, NULL);
863 * adg_canvas_get_frame_dress:
864 * @canvas: an #AdgCanvas
866 * Gets the frame dress to be used in rendering the border of @canvas.
868 * Returns: the current frame dress.
870 * Since: 1.0
872 AdgDress
873 adg_canvas_get_frame_dress(AdgCanvas *canvas)
875 AdgCanvasPrivate *data;
877 g_return_val_if_fail(ADG_IS_CANVAS(canvas), ADG_DRESS_UNDEFINED);
879 data = adg_canvas_get_instance_private(canvas);
880 return data->frame_dress;
884 * adg_canvas_set_title_block:
885 * @canvas: an #AdgCanvas
886 * @title_block: (transfer full): a title block
888 * Sets the #AdgCanvas:title-block property of @canvas to @title_block.
890 * Although a title block entity could be added to @canvas in the usual
891 * way, that is using the adg_container_add() method, assigning a title
892 * block with adg_canvas_set_title_block() is somewhat different:
894 * - @title_block will be automatically attached to the bottom right
895 * corner of to the @canvas frame (this could be accomplished in the
896 * usual way too, by resetting the right and bottom paddings);
897 * - the @title_block boundary box is not taken into account while
898 * computing the extents of @canvas.
900 * Since: 1.0
902 void
903 adg_canvas_set_title_block(AdgCanvas *canvas,
904 AdgTitleBlock *title_block)
906 g_return_if_fail(ADG_IS_CANVAS(canvas));
907 g_return_if_fail(title_block == NULL || ADG_IS_TITLE_BLOCK(title_block));
908 g_object_set(canvas, "title-block", title_block, NULL);
912 * adg_canvas_get_title_block:
913 * @canvas: an #AdgCanvas
915 * Gets the #AdgTitleBlock object of @canvas: check
916 * adg_canvas_set_title_block() for details.
918 * The returned entity is owned by @canvas and should not be
919 * modified or freed.
921 * Returns: (transfer none): the title block object or <constant>NULL</constant>.
923 * Since: 1.0
925 AdgTitleBlock *
926 adg_canvas_get_title_block(AdgCanvas *canvas)
928 AdgCanvasPrivate *data;
930 g_return_val_if_fail(ADG_IS_CANVAS(canvas), NULL);
932 data = adg_canvas_get_instance_private(canvas);
933 return data->title_block;
937 * adg_canvas_set_top_margin:
938 * @canvas: an #AdgCanvas
939 * @value: the new margin, in global space
941 * Changes the top margin of @canvas by setting #AdgCanvas:top-margin
942 * to @value. Negative values are allowed.
944 * Since: 1.0
946 void
947 adg_canvas_set_top_margin(AdgCanvas *canvas, gdouble value)
949 g_return_if_fail(ADG_IS_CANVAS(canvas));
950 g_object_set(canvas, "top-margin", value, NULL);
954 * adg_canvas_get_top_margin:
955 * @canvas: an #AdgCanvas
957 * Gets the top margin (in global space) of @canvas.
959 * Returns: the requested margin or 0 on error.
961 * Since: 1.0
963 gdouble
964 adg_canvas_get_top_margin(AdgCanvas *canvas)
966 AdgCanvasPrivate *data;
968 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
970 data = adg_canvas_get_instance_private(canvas);
971 return data->top_margin;
975 * adg_canvas_set_right_margin:
976 * @canvas: an #AdgCanvas
977 * @value: the new margin, in global space
979 * Changes the right margin of @canvas by setting
980 * #AdgCanvas:right-margin to @value. Negative values are allowed.
982 * Since: 1.0
984 void
985 adg_canvas_set_right_margin(AdgCanvas *canvas, gdouble value)
987 g_return_if_fail(ADG_IS_CANVAS(canvas));
988 g_object_set(canvas, "right-margin", value, NULL);
992 * adg_canvas_get_right_margin:
993 * @canvas: an #AdgCanvas
995 * Gets the right margin (in global space) of @canvas.
997 * Returns: the requested margin or 0 on error.
999 * Since: 1.0
1001 gdouble
1002 adg_canvas_get_right_margin(AdgCanvas *canvas)
1004 AdgCanvasPrivate *data;
1006 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
1008 data = adg_canvas_get_instance_private(canvas);
1009 return data->right_margin;
1013 * adg_canvas_set_bottom_margin:
1014 * @canvas: an #AdgCanvas
1015 * @value: the new margin, in global space
1017 * Changes the bottom margin of @canvas by setting
1018 * #AdgCanvas:bottom-margin to @value. Negative values are allowed.
1020 * Since: 1.0
1022 void
1023 adg_canvas_set_bottom_margin(AdgCanvas *canvas, gdouble value)
1025 g_return_if_fail(ADG_IS_CANVAS(canvas));
1026 g_object_set(canvas, "bottom-margin", value, NULL);
1030 * adg_canvas_get_bottom_margin:
1031 * @canvas: an #AdgCanvas
1033 * Gets the bottom margin (in global space) of @canvas.
1035 * Returns: the requested margin or 0 on error.
1037 * Since: 1.0
1039 gdouble
1040 adg_canvas_get_bottom_margin(AdgCanvas *canvas)
1042 AdgCanvasPrivate *data;
1044 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
1046 data = adg_canvas_get_instance_private(canvas);
1047 return data->bottom_margin;
1051 * adg_canvas_set_left_margin:
1052 * @canvas: an #AdgCanvas
1053 * @value: the new margin, in global space
1055 * Changes the left margin of @canvas by setting
1056 * #AdgCanvas:left-margin to @value. Negative values are allowed.
1058 * Since: 1.0
1060 void
1061 adg_canvas_set_left_margin(AdgCanvas *canvas, gdouble value)
1063 g_return_if_fail(ADG_IS_CANVAS(canvas));
1064 g_object_set(canvas, "left-margin", value, NULL);
1068 * adg_canvas_get_left_margin:
1069 * @canvas: an #AdgCanvas
1071 * Gets the left margin (in global space) of @canvas.
1073 * Returns: the requested margin or 0 on error.
1075 * Since: 1.0
1077 gdouble
1078 adg_canvas_get_left_margin(AdgCanvas *canvas)
1080 AdgCanvasPrivate *data;
1082 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
1084 data = adg_canvas_get_instance_private(canvas);
1085 return data->left_margin;
1089 * adg_canvas_set_margins:
1090 * @canvas: an #AdgCanvas
1091 * @top: top margin, in global space
1092 * @right: right margin, in global space
1093 * @bottom: bottom margin, in global space
1094 * @left: left margin, in global space
1096 * Convenient function to set all the margins at once.
1098 * Since: 1.0
1100 void
1101 adg_canvas_set_margins(AdgCanvas *canvas,
1102 gdouble top, gdouble right,
1103 gdouble bottom, gdouble left)
1105 g_return_if_fail(ADG_IS_CANVAS(canvas));
1106 g_object_set(canvas, "top-margin", top, "right-margin", right,
1107 "bottom-margin", bottom, "left-margin", left, NULL);
1111 * adg_canvas_get_margins:
1112 * @canvas: an #AdgCanvas
1113 * @top: (out) (nullable): where to store the top margin or NULL
1114 * @right: (out) (nullable): where to store the right margin or NULL
1115 * @bottom: (out) (nullable): where to store the bottom margin or NULL
1116 * @left: (out) (nullable): where to store the left margin or NULL
1118 * Convenient function to get all the margins at once.
1120 * Since: 1.0
1122 void
1123 adg_canvas_get_margins(AdgCanvas *canvas,
1124 gdouble *top, gdouble *right,
1125 gdouble *bottom, gdouble *left)
1127 AdgCanvasPrivate *data;
1129 g_return_if_fail(ADG_IS_CANVAS(canvas));
1131 data = adg_canvas_get_instance_private(canvas);
1133 if (top != NULL) {
1134 *top = data->top_margin;
1136 if (right != NULL) {
1137 *right = data->right_margin;
1139 if (bottom != NULL) {
1140 *bottom = data->bottom_margin;
1142 if (left != NULL) {
1143 *left = data->left_margin;
1148 * adg_canvas_apply_margins:
1149 * @canvas: an #AdgCanvas
1150 * @extents: where apply the margins
1152 * A convenient function to apply the margins of @canvas to the
1153 * arbitrary #CpmlExtents struct @extents. "Apply" means @extents
1154 * are enlarged of the specific margin values.
1156 * Since: 1.0
1158 void
1159 adg_canvas_apply_margins(AdgCanvas *canvas, CpmlExtents *extents)
1161 g_return_if_fail(ADG_IS_CANVAS(canvas));
1162 g_return_if_fail(extents != NULL);
1164 if (extents->is_defined) {
1165 AdgCanvasPrivate *data = adg_canvas_get_instance_private(canvas);
1167 extents->org.x -= data->left_margin;
1168 extents->org.y -= data->top_margin;
1169 extents->size.x += data->left_margin + data->right_margin;
1170 extents->size.y += data->top_margin + data->bottom_margin;
1175 * adg_canvas_switch_frame:
1176 * @canvas: an #AdgCanvas
1177 * @new_state: the new flag status
1179 * Sets a new status on the #AdgCanvas:has-frame
1180 * property: <constant>TRUE</constant> means a border around
1181 * the canvas extents (less the margins) should be rendered.
1183 * Since: 1.0
1185 void
1186 adg_canvas_switch_frame(AdgCanvas *canvas, gboolean new_state)
1188 g_return_if_fail(ADG_IS_CANVAS(canvas));
1189 g_object_set(canvas, "has-frame", new_state, NULL);
1193 * adg_canvas_has_frame:
1194 * @canvas: an #AdgCanvas
1196 * Gets the current status of the #AdgCanvas:has-frame property,
1197 * that is whether a border around the canvas extents (less the
1198 * margins) should be rendered (<constant>TRUE</constant>) or not
1199 * (<constant>FALSE</constant>).
1201 * Returns: the current status of the frame flag.
1203 * Since: 1.0
1205 gboolean
1206 adg_canvas_has_frame(AdgCanvas *canvas)
1208 AdgCanvasPrivate *data;
1210 g_return_val_if_fail(ADG_IS_CANVAS(canvas), FALSE);
1212 data = adg_canvas_get_instance_private(canvas);
1213 return data->has_frame;
1217 * adg_canvas_set_top_padding:
1218 * @canvas: an #AdgCanvas
1219 * @value: the new padding, in global space
1221 * Changes the top padding of @canvas by setting
1222 * #AdgCanvas:top-padding to @value. Negative values are allowed.
1224 * Since: 1.0
1226 void
1227 adg_canvas_set_top_padding(AdgCanvas *canvas, gdouble value)
1229 g_return_if_fail(ADG_IS_CANVAS(canvas));
1230 g_object_set(canvas, "top-padding", value, NULL);
1234 * adg_canvas_get_top_padding:
1235 * @canvas: an #AdgCanvas
1237 * Gets the top padding (in global space) of @canvas.
1239 * Returns: the requested padding or 0 on error.
1241 * Since: 1.0
1243 gdouble
1244 adg_canvas_get_top_padding(AdgCanvas *canvas)
1246 AdgCanvasPrivate *data;
1248 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
1250 data = adg_canvas_get_instance_private(canvas);
1251 return data->top_padding;
1255 * adg_canvas_set_right_padding:
1256 * @canvas: an #AdgCanvas
1257 * @value: the new padding, in global space
1259 * Changes the right padding of @canvas by setting #AdgCanvas:right-padding
1260 * to @value. Negative values are allowed.
1262 * Since: 1.0
1264 void
1265 adg_canvas_set_right_padding(AdgCanvas *canvas, gdouble value)
1267 g_return_if_fail(ADG_IS_CANVAS(canvas));
1268 g_object_set(canvas, "right-padding", value, NULL);
1272 * adg_canvas_get_right_padding:
1273 * @canvas: an #AdgCanvas
1275 * Gets the right padding (in global space) of @canvas.
1277 * Returns: the requested padding or 0 on error.
1279 * Since: 1.0
1281 gdouble
1282 adg_canvas_get_right_padding(AdgCanvas *canvas)
1284 AdgCanvasPrivate *data;
1286 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
1288 data = adg_canvas_get_instance_private(canvas);
1289 return data->right_padding;
1294 * adg_canvas_set_bottom_padding:
1295 * @canvas: an #AdgCanvas
1296 * @value: the new padding, in global space
1298 * Changes the bottom padding of @canvas by setting
1299 * #AdgCanvas:bottom-padding to @value. Negative values are allowed.
1301 * Since: 1.0
1303 void
1304 adg_canvas_set_bottom_padding(AdgCanvas *canvas, gdouble value)
1306 g_return_if_fail(ADG_IS_CANVAS(canvas));
1307 g_object_set(canvas, "bottom-padding", value, NULL);
1311 * adg_canvas_get_bottom_padding:
1312 * @canvas: an #AdgCanvas
1314 * Gets the bottom padding (in global space) of @canvas.
1316 * Returns: the requested padding or 0 on error.
1318 * Since: 1.0
1320 gdouble
1321 adg_canvas_get_bottom_padding(AdgCanvas *canvas)
1323 AdgCanvasPrivate *data;
1325 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
1327 data = adg_canvas_get_instance_private(canvas);
1328 return data->bottom_padding;
1332 * adg_canvas_set_left_padding:
1333 * @canvas: an #AdgCanvas
1334 * @value: the new padding, in global space
1336 * Changes the left padding of @canvas by setting
1337 * #AdgCanvas:left-padding to @value. Negative values are allowed.
1339 * Since: 1.0
1341 void
1342 adg_canvas_set_left_padding(AdgCanvas *canvas, gdouble value)
1344 g_return_if_fail(ADG_IS_CANVAS(canvas));
1345 g_object_set(canvas, "left-padding", value, NULL);
1349 * adg_canvas_get_left_padding:
1350 * @canvas: an #AdgCanvas
1352 * Gets the left padding (in global space) of @canvas.
1354 * Returns: the requested padding or 0 on error.
1356 * Since: 1.0
1358 gdouble
1359 adg_canvas_get_left_padding(AdgCanvas *canvas)
1361 AdgCanvasPrivate *data;
1363 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
1365 data = adg_canvas_get_instance_private(canvas);
1366 return data->left_padding;
1370 * adg_canvas_set_paddings:
1371 * @canvas: an #AdgCanvas
1372 * @top: top padding, in global space
1373 * @right: right padding, in global space
1374 * @bottom: bottom padding, in global space
1375 * @left: left padding, in global space
1377 * Convenient function to set all the paddings at once.
1379 * Since: 1.0
1381 void
1382 adg_canvas_set_paddings(AdgCanvas *canvas,
1383 gdouble top, gdouble right,
1384 gdouble bottom, gdouble left)
1386 g_return_if_fail(ADG_IS_CANVAS(canvas));
1387 g_object_set(canvas, "top-padding", top, "right-padding", right,
1388 "bottom-padding", bottom, "left-padding", left, NULL);
1392 * adg_canvas_get_paddings:
1393 * @canvas: an #AdgCanvas
1394 * @top: (out) (nullable): where to store the top padding or NULL
1395 * @right: (out) (nullable): where to store the right padding or NULL
1396 * @bottom: (out) (nullable): where to store the bottom padding or NULL
1397 * @left: (out) (nullable): where to store the left padding or NULL
1399 * Convenient function to get all the paddings at once.
1401 * Since: 1.0
1403 void
1404 adg_canvas_get_paddings(AdgCanvas *canvas,
1405 gdouble *top, gdouble *right,
1406 gdouble *bottom, gdouble *left)
1408 AdgCanvasPrivate *data;
1410 g_return_if_fail(ADG_IS_CANVAS(canvas));
1412 data = adg_canvas_get_instance_private(canvas);
1414 if (top != NULL) {
1415 *top = data->top_padding;
1417 if (right != NULL) {
1418 *right = data->right_padding;
1420 if (bottom != NULL) {
1421 *bottom = data->bottom_padding;
1423 if (left != NULL) {
1424 *left = data->left_padding;
1429 static void
1430 _adg_global_changed(AdgEntity *entity)
1432 AdgCanvasPrivate *data = adg_canvas_get_instance_private((AdgCanvas *) entity);
1434 if (_ADG_OLD_ENTITY_CLASS->global_changed)
1435 _ADG_OLD_ENTITY_CLASS->global_changed(entity);
1437 if (data->title_block)
1438 adg_entity_global_changed((AdgEntity *) data->title_block);
1441 static void
1442 _adg_local_changed(AdgEntity *entity)
1444 AdgCanvasPrivate *data = adg_canvas_get_instance_private((AdgCanvas *) entity);
1445 AdgTitleBlock *title_block = data->title_block;
1447 if (_ADG_OLD_ENTITY_CLASS->local_changed)
1448 _ADG_OLD_ENTITY_CLASS->local_changed(entity);
1450 if (title_block != NULL) {
1451 const gchar *scale = adg_title_block_get_scale(title_block);
1453 if (scale != NULL && scale[0] != '\0') {
1454 const cairo_matrix_t *map = adg_entity_get_local_map(entity);
1455 gdouble factor = adg_scale_factor(scale);
1457 if (map->xx != factor || map->yy != factor)
1458 adg_title_block_set_scale(title_block, "---");
1461 adg_entity_local_changed((AdgEntity *) title_block);
1465 static void
1466 _adg_invalidate(AdgEntity *entity)
1468 AdgCanvasPrivate *data = adg_canvas_get_instance_private((AdgCanvas *) entity);
1470 if (_ADG_OLD_ENTITY_CLASS->invalidate)
1471 _ADG_OLD_ENTITY_CLASS->invalidate(entity);
1473 if (data->title_block)
1474 adg_entity_invalidate((AdgEntity *) data->title_block);
1477 static void
1478 _adg_arrange(AdgEntity *entity)
1480 AdgCanvas *canvas;
1481 AdgCanvasPrivate *data;
1482 CpmlExtents extents;
1484 if (_ADG_OLD_ENTITY_CLASS->arrange)
1485 _ADG_OLD_ENTITY_CLASS->arrange(entity);
1487 cpml_extents_copy(&extents, adg_entity_get_extents(entity));
1489 /* The extents should be defined, otherwise there is no drawing */
1490 g_return_if_fail(extents.is_defined);
1492 canvas = (AdgCanvas *) entity;
1493 data = adg_canvas_get_instance_private(canvas);
1495 _adg_apply_paddings(canvas, &extents);
1497 if (data->size.x > 0 || data->size.y > 0) {
1498 const cairo_matrix_t *global = adg_entity_get_global_matrix(entity);
1499 CpmlExtents paper;
1501 paper.org.x = 0;
1502 paper.org.y = 0;
1503 paper.size.x = data->size.x;
1504 paper.size.y = data->size.y;
1506 cairo_matrix_transform_point(global, &paper.org.x, &paper.org.y);
1507 cairo_matrix_transform_distance(global, &paper.size.x, &paper.size.y);
1509 if (data->size.x > 0) {
1510 extents.org.x = paper.org.x;
1511 extents.size.x = paper.size.x;
1513 if (data->size.y > 0) {
1514 extents.org.y = paper.org.y;
1515 extents.size.y = paper.size.y;
1519 if (data->title_block) {
1520 AdgEntity *title_block_entity;
1521 const CpmlExtents *title_block_extents;
1522 CpmlPair shift;
1524 title_block_entity = (AdgEntity *) data->title_block;
1525 adg_entity_arrange(title_block_entity);
1526 title_block_extents = adg_entity_get_extents(title_block_entity);
1528 shift.x = extents.org.x + extents.size.x - title_block_extents->org.x
1529 - title_block_extents->size.x;
1530 shift.y = extents.org.y + extents.size.y - title_block_extents->org.y
1531 - title_block_extents->size.y;
1533 /* The following block could be optimized by skipping tiny shift,
1534 * usually left by rounding errors */
1535 if (shift.x != 0 || shift.y != 0) {
1536 cairo_matrix_t unglobal, map;
1537 adg_matrix_copy(&unglobal, adg_entity_get_global_matrix(entity));
1538 cairo_matrix_invert(&unglobal);
1540 cairo_matrix_transform_distance(&unglobal, &shift.x, &shift.y);
1541 cairo_matrix_init_translate(&map, shift.x, shift.y);
1542 adg_entity_transform_global_map(title_block_entity, &map,
1543 ADG_TRANSFORM_AFTER);
1545 adg_entity_global_changed(title_block_entity);
1546 adg_entity_arrange(title_block_entity);
1547 title_block_extents = adg_entity_get_extents(title_block_entity);
1548 cpml_extents_add(&extents, title_block_extents);
1552 /* Impose the new extents */
1553 adg_entity_set_extents(entity, &extents);
1556 static void
1557 _adg_render(AdgEntity *entity, cairo_t *cr)
1559 AdgCanvasPrivate *data = adg_canvas_get_instance_private((AdgCanvas *) entity);
1560 const CpmlExtents *extents = adg_entity_get_extents(entity);
1562 cairo_save(cr);
1564 /* Background fill */
1565 cairo_rectangle(cr, extents->org.x - data->left_margin,
1566 extents->org.y - data->top_margin,
1567 extents->size.x + data->left_margin + data->right_margin,
1568 extents->size.y + data->top_margin + data->bottom_margin);
1569 adg_entity_apply_dress(entity, data->background_dress, cr);
1570 cairo_fill(cr);
1572 /* Frame line */
1573 if (data->has_frame) {
1574 cairo_rectangle(cr, extents->org.x, extents->org.y,
1575 extents->size.x, extents->size.y);
1576 cairo_transform(cr, adg_entity_get_global_matrix(entity));
1577 adg_entity_apply_dress(entity, data->frame_dress, cr);
1578 cairo_stroke(cr);
1581 cairo_restore(cr);
1583 if (data->title_block)
1584 adg_entity_render((AdgEntity *) data->title_block, cr);
1586 if (_ADG_OLD_ENTITY_CLASS->render)
1587 _ADG_OLD_ENTITY_CLASS->render(entity, cr);
1590 static void
1591 _adg_apply_paddings(AdgCanvas *canvas, CpmlExtents *extents)
1593 AdgCanvasPrivate *data = adg_canvas_get_instance_private(canvas);
1594 extents->org.x -= data->left_padding;
1595 extents->size.x += data->left_padding + data->right_padding;
1596 extents->org.y -= data->top_padding;
1597 extents->size.y += data->top_padding + data->bottom_padding;
1602 * adg_canvas_export:
1603 * @canvas: an #AdgCanvas
1604 * @type: (type gint): the export format
1605 * @file: the name of the resulting file
1606 * @gerror: (allow-none): return location for errors
1608 * A helper function that provides a bare export functionality.
1609 * It basically exports the drawing in @canvas in the @file
1610 * in the @type format. Any error will be reported in @gerror,
1611 * if not <constant>NULL</constant>.
1613 * Returns: <constant>TRUE</constant> on success, <constant>FALSE</constant> otherwise.
1615 * Since: 1.0
1617 gboolean
1618 adg_canvas_export(AdgCanvas *canvas, cairo_surface_type_t type,
1619 const gchar *file, GError **gerror)
1621 AdgEntity *entity;
1622 const CpmlExtents *extents;
1623 gdouble top, bottom, left, right, width, height, factor;
1624 cairo_surface_t *surface;
1625 cairo_t *cr;
1626 cairo_status_t status;
1628 g_return_val_if_fail(ADG_IS_CANVAS(canvas), FALSE);
1629 g_return_val_if_fail(file != NULL, FALSE);
1630 g_return_val_if_fail(gerror == NULL || *gerror == NULL, FALSE);
1632 entity = (AdgEntity *) canvas;
1634 adg_entity_arrange(entity);
1635 extents = adg_entity_get_extents(entity);
1637 factor = adg_canvas_get_factor(canvas);
1638 top = factor * adg_canvas_get_top_margin(canvas);
1639 bottom = factor * adg_canvas_get_bottom_margin(canvas);
1640 left = factor * adg_canvas_get_left_margin(canvas);
1641 right = factor * adg_canvas_get_right_margin(canvas);
1642 width = factor * extents->size.x + left + right;
1643 height = factor * extents->size.y + top + bottom;
1645 switch (type) {
1646 #ifdef CAIRO_HAS_PNG_FUNCTIONS
1647 case CAIRO_SURFACE_TYPE_IMAGE:
1648 surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height);
1649 break;
1650 #endif
1651 #ifdef CAIRO_HAS_PDF_SURFACE
1652 case CAIRO_SURFACE_TYPE_PDF:
1653 surface = cairo_pdf_surface_create(file, width, height);
1654 break;
1655 #endif
1656 #ifdef CAIRO_HAS_PS_SURFACE
1657 case CAIRO_SURFACE_TYPE_PS:
1658 surface = cairo_ps_surface_create(file, width, height);
1659 break;
1660 #endif
1661 #ifdef CAIRO_HAS_SVG_SURFACE
1662 case CAIRO_SURFACE_TYPE_SVG:
1663 surface = cairo_svg_surface_create(file, width, height);
1664 break;
1665 #endif
1666 default:
1667 surface = NULL;
1668 break;
1671 if (surface == NULL) {
1672 g_set_error(gerror, ADG_CANVAS_ERROR, ADG_CANVAS_ERROR_SURFACE,
1673 "unable to handle surface type '%d'",
1674 type);
1675 return FALSE;
1678 cairo_surface_set_device_offset(surface, left, top);
1679 cairo_surface_set_device_scale(surface, factor, factor);
1680 cr = cairo_create(surface);
1681 cairo_surface_destroy(surface);
1683 adg_entity_render(ADG_ENTITY(canvas), cr);
1685 if (cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_IMAGE) {
1686 status = cairo_surface_write_to_png(surface, file);
1687 } else {
1688 cairo_show_page(cr);
1689 status = cairo_status(cr);
1692 cairo_destroy(cr);
1694 if (status != CAIRO_STATUS_SUCCESS) {
1695 g_set_error(gerror, ADG_CANVAS_ERROR, ADG_CANVAS_ERROR_CAIRO,
1696 "cairo reported '%s'",
1697 cairo_status_to_string(status));
1698 return FALSE;
1701 return TRUE;
1705 #if GTK3_ENABLED || GTK2_ENABLED
1706 #include <gtk/gtk.h>
1708 static void
1709 _adg_update_margin(AdgCanvas *canvas, gdouble *margin,
1710 gdouble *side, gdouble new_margin)
1712 GtkPageSetup *page_setup;
1713 gdouble old_margin;
1715 old_margin = *margin;
1716 *margin = new_margin;
1718 page_setup = adg_canvas_get_page_setup(canvas);
1719 if (page_setup == NULL)
1720 return;
1722 *side += old_margin - new_margin;
1726 * adg_canvas_set_paper:
1727 * @canvas: an #AdgCanvas
1728 * @paper_name: a paper name
1729 * @orientation: the page orientation
1731 * A convenient function to set size and margins of @canvas
1732 * using a @paper_name and an @orientation value. This should
1733 * be a PWG 5101.1-2002 paper name and it will be passed as
1734 * is to gtk_paper_size_new(), so use any valid name accepted
1735 * by that function.
1737 * This has the same effect as creating a #GtkPaperSetup object
1738 * and binding it to @canvas with adg_canvas_set_page_setup():
1739 * check its documentation for knowing the implications.
1741 * To reset the size to its default behavior (i.e. autocalculate
1742 * it from the entities) just call adg_canvas_set_size_explicit()
1743 * passing <constant>0, 0</constant>.
1745 * If you want to use your own margins on a named paper size,
1746 * set them <emphasis>after</emphasis> the call to this function.
1748 * Since: 1.0
1750 void
1751 adg_canvas_set_paper(AdgCanvas *canvas,
1752 const gchar *paper_name,
1753 GtkPageOrientation orientation)
1755 GtkPageSetup *page_setup;
1756 GtkPaperSize *paper_size;
1758 g_return_if_fail(ADG_IS_CANVAS(canvas));
1759 g_return_if_fail(paper_name != NULL);
1761 page_setup = gtk_page_setup_new();
1762 paper_size = gtk_paper_size_new(paper_name);
1764 gtk_page_setup_set_paper_size(page_setup, paper_size);
1765 gtk_page_setup_set_orientation(page_setup, orientation);
1766 gtk_paper_size_free(paper_size);
1768 adg_canvas_set_page_setup(canvas, page_setup);
1769 g_object_unref(page_setup);
1773 * adg_canvas_set_page_setup:
1774 * @canvas: an #AdgCanvas
1775 * @page_setup: (allow-none) (transfer none): the page setup
1777 * A convenient function to setup the page of @canvas so it can
1778 * also be subsequentially used for printing. It is allowed to
1779 * pass <constant>NULL</constant> as @page_setup to restore the
1780 * default page setup.
1782 * When a canvas is bound to a page setup, the paper size is kept
1783 * constant. This implies increasing or decreasing the margins
1784 * respectively decreases or increases the page size of the
1785 * relevant dimension, e.g. increasing the right margin decreases
1786 * the width (the x component of the size).
1788 * A reference to @page_setup is added, so there is no need to keep
1789 * alive this object after a call to this function. @page_setup can
1790 * be retrieved at any time with adg_canvas_get_page_setup().
1792 * The size and margins provided by @page_setup are immediately
1793 * used to set size and margins of @canvas. This means using
1794 * <constant>NULL</constant> as @page_setup just releases the
1795 * reference to the previous @page_setup object... all the page
1796 * settings are still retained.
1798 * If you want to use your own margins on a page setup,
1799 * set them on canvas <emphasis>after</emphasis> the call to this
1800 * function or set them on @page_setup <emphasis>before</emphasis>.
1802 * <informalexample><programlisting language="C">
1803 * // By default, canvas does not have an explicit size
1804 * adg_canvas_set_page_setup(canvas, a4);
1805 * g_object_unref(a4);
1806 * // Now canvas has size and margins specified by a4
1807 * // (and a4 should be a prefilled GtkPageSetup object).
1808 * adg_canvas_set_page_setup(canvas, NULL);
1809 * // Now canvas is no more bound to a4 and that object (if
1810 * // not referenced anywhere else) can be garbage-collected.
1811 * // The page setup of canvas has not changed though.
1812 * adg_canvas_set_size_explicit(canvas, 0, 0);
1813 * // Now the page size of canvas has been restored to
1814 * // their default behavior.
1815 * </programlisting></informalexample>
1817 * Since: 1.0
1819 void
1820 adg_canvas_set_page_setup(AdgCanvas *canvas, GtkPageSetup *page_setup)
1822 gdouble top, right, bottom, left;
1823 CpmlPair size;
1825 g_return_if_fail(ADG_IS_CANVAS(canvas));
1827 if (page_setup == NULL) {
1828 /* By convention, NULL resets the default page but
1829 * does not change any other property */
1830 g_object_set_data((GObject *) canvas, "_adg_page_setup", NULL);
1831 return;
1834 g_return_if_fail(GTK_IS_PAGE_SETUP(page_setup));
1836 top = gtk_page_setup_get_top_margin(page_setup, GTK_UNIT_POINTS);
1837 right = gtk_page_setup_get_right_margin(page_setup, GTK_UNIT_POINTS);
1838 bottom = gtk_page_setup_get_bottom_margin(page_setup, GTK_UNIT_POINTS);
1839 left = gtk_page_setup_get_left_margin(page_setup, GTK_UNIT_POINTS);
1840 size.x = gtk_page_setup_get_page_width(page_setup, GTK_UNIT_POINTS);
1841 size.y = gtk_page_setup_get_page_height(page_setup, GTK_UNIT_POINTS);
1843 adg_canvas_set_size(canvas, &size);
1844 adg_canvas_set_margins(canvas, top, right, bottom, left);
1846 g_object_ref(page_setup);
1847 g_object_set_data_full((GObject *) canvas, "_adg_page_setup",
1848 page_setup, g_object_unref);
1852 * adg_canvas_get_page_setup:
1853 * @canvas: an #AdgCanvas
1855 * If adg_canvas_set_page_setup() is called, a #GtkPageSetup object
1856 * is created and bound to @canvas. This metho returns a pointer
1857 * to that internal object or <constant>NULL</constant> if
1858 * adg_canvas_set_page_setup() has not been called before.
1860 * Returns: (allow-none) (transfer none): the #GtkPageSetup with size and margins of @canvas of <constant>NULL</constant> on no setup found or errors.
1862 * Since: 1.0
1864 GtkPageSetup *
1865 adg_canvas_get_page_setup(AdgCanvas *canvas)
1867 g_return_val_if_fail(ADG_IS_CANVAS(canvas), NULL);
1868 return g_object_get_data((GObject *) canvas, "_adg_page_setup");
1871 #else
1873 static void
1874 _adg_update_margin(AdgCanvas *canvas, gdouble *margin,
1875 gdouble *side, gdouble new_margin)
1877 *margin = new_margin;
1879 #endif