doc: update copyright line for 2021
[adg.git] / src / adg / adg-canvas.c
blob87b9b4b4fb6671383dfd5e7a4962fbc4ea52b4dc
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2021 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);
167 * adg_canvas_error_quark:
169 * Registers an error quark specific for #AdgCanvas.
171 * Returns: The error quark used for #AdgCanvas errors.
173 * Since: 1.0
175 GQuark
176 adg_canvas_error_quark(void)
178 static GQuark q;
180 if G_UNLIKELY (q == 0)
181 q = g_quark_from_static_string("adg-canvas-error-quark");
183 return q;
186 static void
187 adg_canvas_class_init(AdgCanvasClass *klass)
189 GObjectClass *gobject_class;
190 AdgEntityClass *entity_class;
191 GParamSpec *param;
193 gobject_class = (GObjectClass *) klass;
194 entity_class = (AdgEntityClass *) klass;
196 gobject_class->dispose = _adg_dispose;
197 gobject_class->get_property = _adg_get_property;
198 gobject_class->set_property = _adg_set_property;
200 entity_class->global_changed = _adg_global_changed;
201 entity_class->local_changed = _adg_local_changed;
202 entity_class->invalidate = _adg_invalidate;
203 entity_class->arrange = _adg_arrange;
204 entity_class->render = _adg_render;
206 param = g_param_spec_boxed("size",
207 P_("Canvas Size"),
208 P_("The size set on this canvas: use 0 to have an automatic dimension based on the canvas extents"),
209 CPML_TYPE_PAIR,
210 G_PARAM_READWRITE);
211 g_object_class_install_property(gobject_class, PROP_SIZE, param);
213 param = g_param_spec_double("factor",
214 P_("Factor"),
215 P_("Global space units per point (1 point == 1/72 inch)"),
216 0, G_MAXDOUBLE, 1,
217 G_PARAM_READWRITE);
218 g_object_class_install_property(gobject_class, PROP_FACTOR, param);
220 param = g_param_spec_boxed("scales",
221 P_("Valid Scales"),
222 P_("List of scales to be tested while autoscaling the drawing"),
223 G_TYPE_STRV,
224 G_PARAM_READWRITE);
225 g_object_class_install_property(gobject_class, PROP_SCALES, param);
227 param = adg_param_spec_dress("background-dress",
228 P_("Background Dress"),
229 P_("The color dress to use for the canvas background"),
230 ADG_DRESS_COLOR_BACKGROUND,
231 G_PARAM_READWRITE);
232 g_object_class_install_property(gobject_class, PROP_BACKGROUND_DRESS, param);
234 param = adg_param_spec_dress("frame-dress",
235 P_("Frame Dress"),
236 P_("Line dress to use while drawing the frame around the canvas"),
237 ADG_DRESS_LINE_FRAME,
238 G_PARAM_READWRITE);
239 g_object_class_install_property(gobject_class, PROP_FRAME_DRESS, param);
241 param = g_param_spec_object("title-block",
242 P_("Title Block"),
243 P_("The title block to assign to this canvas"),
244 ADG_TYPE_TITLE_BLOCK,
245 G_PARAM_READWRITE);
246 g_object_class_install_property(gobject_class, PROP_TITLE_BLOCK, param);
248 param = g_param_spec_double("top-margin",
249 P_("Top Margin"),
250 P_("The margin (in global space) to leave above the frame"),
251 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
252 G_PARAM_READWRITE);
253 g_object_class_install_property(gobject_class, PROP_TOP_MARGIN, param);
255 param = g_param_spec_double("right-margin",
256 P_("Right Margin"),
257 P_("The margin (in global space) to leave empty at the right of the frame"),
258 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
259 G_PARAM_READWRITE);
260 g_object_class_install_property(gobject_class, PROP_RIGHT_MARGIN, param);
262 param = g_param_spec_double("bottom-margin",
263 P_("Bottom Margin"),
264 P_("The margin (in global space) to leave empty below the frame"),
265 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
266 G_PARAM_READWRITE);
267 g_object_class_install_property(gobject_class, PROP_BOTTOM_MARGIN, param);
269 param = g_param_spec_double("left-margin",
270 P_("Left Margin"),
271 P_("The margin (in global space) to leave empty at the left of the frame"),
272 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
273 G_PARAM_READWRITE);
274 g_object_class_install_property(gobject_class, PROP_LEFT_MARGIN, param);
276 param = g_param_spec_boolean("has-frame",
277 P_("Has Frame Flag"),
278 P_("If enabled, a frame using the frame dress will be drawn around the canvas extents, taking into account the margins"),
279 TRUE,
280 G_PARAM_READWRITE);
281 g_object_class_install_property(gobject_class, PROP_HAS_FRAME, param);
283 param = g_param_spec_double("top-padding",
284 P_("Top Padding"),
285 P_("The padding (in global space) to leave empty above between the drawing and the frame"),
286 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
287 G_PARAM_READWRITE);
288 g_object_class_install_property(gobject_class, PROP_TOP_PADDING, param);
290 param = g_param_spec_double("right-padding",
291 P_("Right Padding"),
292 P_("The padding (in global space) to leave empty at the right between the drawing and the frame"),
293 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
294 G_PARAM_READWRITE);
295 g_object_class_install_property(gobject_class, PROP_RIGHT_PADDING, param);
297 param = g_param_spec_double("bottom-padding",
298 P_("Bottom Padding"),
299 P_("The padding (in global space) to leave empty below between the drawing and the frame"),
300 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
301 G_PARAM_READWRITE);
302 g_object_class_install_property(gobject_class, PROP_BOTTOM_PADDING, param);
304 param = g_param_spec_double("left-padding",
305 P_("Left Padding"),
306 P_("The padding (in global space) to leave empty at the left between the drawing and the frame"),
307 -G_MAXDOUBLE, G_MAXDOUBLE, 15,
308 G_PARAM_READWRITE);
309 g_object_class_install_property(gobject_class, PROP_LEFT_PADDING, param);
312 static void
313 adg_canvas_init(AdgCanvas *canvas)
315 AdgCanvasPrivate *data = adg_canvas_get_instance_private(canvas);
316 const gchar *scales[] = {
317 "10:1", "5:1", "3:1", "2:1", "1:1", "1:2", "1:3", "1:5", "1:10",
318 NULL
321 data->size.x = 0;
322 data->size.y = 0;
323 data->factor = 1;
324 data->scales = g_strdupv((gchar **) scales);
325 data->background_dress = ADG_DRESS_COLOR_BACKGROUND;
326 data->frame_dress = ADG_DRESS_LINE_FRAME;
327 data->title_block = NULL;
328 data->top_margin = 15;
329 data->right_margin = 15;
330 data->bottom_margin = 15;
331 data->left_margin = 15;
332 data->has_frame = TRUE;
333 data->top_padding = 15;
334 data->right_padding = 15;
335 data->bottom_padding = 15;
336 data->left_padding = 15;
339 static void
340 _adg_dispose(GObject *object)
342 AdgCanvas *canvas = (AdgCanvas *) object;
343 AdgCanvasPrivate *data = adg_canvas_get_instance_private(canvas);
345 if (data->title_block) {
346 g_object_unref(data->title_block);
347 data->title_block = NULL;
350 if (data->scales != NULL) {
351 g_strfreev(data->scales);
352 data->scales = NULL;
355 if (_ADG_OLD_OBJECT_CLASS->dispose)
356 _ADG_OLD_OBJECT_CLASS->dispose(object);
360 static void
361 _adg_get_property(GObject *object, guint prop_id,
362 GValue *value, GParamSpec *pspec)
364 AdgCanvasPrivate *data = adg_canvas_get_instance_private((AdgCanvas *) object);
366 switch (prop_id) {
367 case PROP_SIZE:
368 g_value_set_boxed(value, &data->size);
369 break;
370 case PROP_FACTOR:
371 g_value_set_double(value, data->factor);
372 break;
373 case PROP_SCALES:
374 g_value_set_boxed(value, data->scales);
375 break;
376 case PROP_BACKGROUND_DRESS:
377 g_value_set_enum(value, data->background_dress);
378 break;
379 case PROP_FRAME_DRESS:
380 g_value_set_enum(value, data->frame_dress);
381 break;
382 case PROP_TITLE_BLOCK:
383 g_value_set_object(value, data->title_block);
384 break;
385 case PROP_TOP_MARGIN:
386 g_value_set_double(value, data->top_margin);
387 break;
388 case PROP_RIGHT_MARGIN:
389 g_value_set_double(value, data->right_margin);
390 break;
391 case PROP_BOTTOM_MARGIN:
392 g_value_set_double(value, data->bottom_margin);
393 break;
394 case PROP_LEFT_MARGIN:
395 g_value_set_double(value, data->left_margin);
396 break;
397 case PROP_HAS_FRAME:
398 g_value_set_boolean(value, data->has_frame);
399 break;
400 case PROP_TOP_PADDING:
401 g_value_set_double(value, data->top_padding);
402 break;
403 case PROP_RIGHT_PADDING:
404 g_value_set_double(value, data->right_padding);
405 break;
406 case PROP_BOTTOM_PADDING:
407 g_value_set_double(value, data->bottom_padding);
408 break;
409 case PROP_LEFT_PADDING:
410 g_value_set_double(value, data->left_padding);
411 break;
412 default:
413 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
414 break;
418 static void
419 _adg_set_property(GObject *object, guint prop_id,
420 const GValue *value, GParamSpec *pspec)
422 AdgCanvas *canvas = (AdgCanvas *) object;
423 AdgCanvasPrivate *data = adg_canvas_get_instance_private(canvas);
424 AdgTitleBlock *title_block;
425 gdouble factor;
427 switch (prop_id) {
428 case PROP_SIZE:
429 cpml_pair_copy(&data->size, g_value_get_boxed(value));
430 break;
431 case PROP_FACTOR:
432 factor = g_value_get_double(value);
433 g_return_if_fail(factor > 0);
434 data->factor = factor;
435 break;
436 case PROP_SCALES:
437 g_strfreev(data->scales);
438 data->scales = g_value_dup_boxed(value);
439 break;
440 case PROP_BACKGROUND_DRESS:
441 data->background_dress = g_value_get_enum(value);
442 break;
443 case PROP_FRAME_DRESS:
444 data->frame_dress = g_value_get_enum(value);
445 break;
446 case PROP_TITLE_BLOCK:
447 title_block = g_value_get_object(value);
448 if (title_block) {
449 g_object_ref(title_block);
450 adg_entity_set_parent((AdgEntity *) title_block,
451 (AdgEntity *) canvas);
453 if (data->title_block)
454 g_object_unref(data->title_block);
455 data->title_block = title_block;
456 break;
457 case PROP_TOP_MARGIN:
458 _adg_update_margin(canvas, &data->top_margin, &data->size.y,
459 g_value_get_double(value));
460 break;
461 case PROP_RIGHT_MARGIN:
462 _adg_update_margin(canvas, &data->right_margin, &data->size.x,
463 g_value_get_double(value));
464 break;
465 case PROP_BOTTOM_MARGIN:
466 _adg_update_margin(canvas, &data->bottom_margin, &data->size.y,
467 g_value_get_double(value));
468 break;
469 case PROP_LEFT_MARGIN:
470 _adg_update_margin(canvas, &data->left_margin, &data->size.x,
471 g_value_get_double(value));
472 break;
473 case PROP_HAS_FRAME:
474 data->has_frame = g_value_get_boolean(value);
475 break;
476 case PROP_TOP_PADDING:
477 data->top_padding = g_value_get_double(value);
478 break;
479 case PROP_RIGHT_PADDING:
480 data->right_padding = g_value_get_double(value);
481 break;
482 case PROP_BOTTOM_PADDING:
483 data->bottom_padding = g_value_get_double(value);
484 break;
485 case PROP_LEFT_PADDING:
486 data->left_padding = g_value_get_double(value);
487 break;
488 default:
489 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
490 break;
496 * adg_canvas_new:
498 * Creates a new empty canvas object.
500 * Returns: (transfer full): the newly created canvas.
502 * Since: 1.0
504 AdgCanvas *
505 adg_canvas_new(void)
507 return g_object_new(ADG_TYPE_CANVAS, NULL);
511 * adg_canvas_set_size:
512 * @canvas: an #AdgCanvas
513 * @size: (transfer none): the new size for the canvas
515 * Sets a specific size on @canvas. The x and/or y
516 * component of @size can be set to 0, in which case
517 * the exact value will be autocalculated, that is the
518 * size component returned by adg_entity_get_extents()
519 * on @canvas will be used instead.
521 * Since: 1.0
523 void
524 adg_canvas_set_size(AdgCanvas *canvas, const CpmlPair *size)
526 g_return_if_fail(ADG_IS_CANVAS(canvas));
527 g_return_if_fail(size != NULL);
529 g_object_set(canvas, "size", size, NULL);
533 * adg_canvas_set_size_explicit:
534 * @canvas: an #AdgCanvas
535 * @x: the new width of the canvas or 0 to reset
536 * @y: the new height of the canvas or 0 to reset
538 * A convenient function to set the size of @canvas using
539 * explicit coordinates. Check adg_canvas_set_size() for
540 * further details.
542 * Since: 1.0
544 void
545 adg_canvas_set_size_explicit(AdgCanvas *canvas, gdouble x, gdouble y)
547 CpmlPair size;
549 size.x = x;
550 size.y = y;
552 adg_canvas_set_size(canvas, &size);
556 * adg_canvas_get_size:
557 * @canvas: an #AdgCanvas
559 * Gets the specific size set on @canvas. The x and/or y
560 * components of the returned #CpmlPair could be 0, in which
561 * case the size returned by adg_entity_get_extents() on @canvas
562 * will be used instead.
564 * Returns: (transfer none): the explicit size set on this canvas or <constant>NULL</constant> on errors.
566 * Since: 1.0
568 const CpmlPair *
569 adg_canvas_get_size(AdgCanvas *canvas)
571 AdgCanvasPrivate *data;
573 g_return_val_if_fail(ADG_IS_CANVAS(canvas), NULL);
575 data = adg_canvas_get_instance_private(canvas);
576 return &data->size;
580 * adg_canvas_set_factor:
581 * @canvas: an #AdgCanvas
582 * @factor: the new factor: must be greater than 0
584 * The ADG library is intentionally unit agnostic, i.e. the global space is
585 * represented in whatever unit you want. There are a couple of cairo APIs
586 * that explicitely requires points though, most notably
587 * cairo_pdf_surface_set_size() and cairo_ps_surface_set_size().
589 * On PDF and postscript surfaces, the AdgCanvas:factor property will be the
590 * number typography points per global space units:
591 * https://en.wikipedia.org/wiki/Point_(typography)
593 * For other surfaces, the factor will be used to scale the final surface to
594 * the specific number of pixels.
596 * As an example, if you are working in millimeters you will set a factor of
597 * 2.83465 on PDF and postscript surfaces (1 mm = 2.83465 points) so the
598 * drawing will show the correct scale on paper. For X11 and PNG surfaces you
599 * will set it depending on the resolution you want to get, e.g. if the
600 * drawing is 100x200 mm and you want a 1000x2000 image, just set it to 10.
602 * Since: 1.0
604 void
605 adg_canvas_set_factor(AdgCanvas *canvas, double factor)
607 g_return_if_fail(ADG_IS_CANVAS(canvas));
608 g_return_if_fail(factor > 0);
610 g_object_set(canvas, "factor", factor, NULL);
614 * adg_canvas_get_factor:
615 * @canvas: an #AdgCanvas
617 * Gets the current factor of @canvas. See adg_canvas_set_factor() to learn
618 * what a factor is in this context.
620 * Returns: the canvas factor or 0 on error.
622 * Since: 1.0
624 double
625 adg_canvas_get_factor(AdgCanvas *canvas)
627 AdgCanvasPrivate *data;
629 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
631 data = adg_canvas_get_instance_private(canvas);
632 return data->factor;
636 * adg_canvas_set_scales:
637 * @canvas: an #AdgCanvas
638 * @...: <constant>NULL</constant> terminated list of strings
640 * Sets the scales allowed by @canvas. Every scale identifies a
641 * specific factor to be applied to the local matrix of @canvas.
642 * When adg_canvas_autoscale() will be called, the greatest
643 * scale that can render every entity inside a box of
644 * #AdgCanvas:size dimensions will be applied. The drawing will
645 * be centered inside that box.
647 * Every scale should be expressed with a string in the form of
648 * "x:y", where x and y are positive integers that identifies
649 * numerator an denominator of a fraction. That string itself
650 * will be put into the title block when used.
652 * Since: 1.0
654 void
655 adg_canvas_set_scales(AdgCanvas *canvas, ...)
657 va_list var_args;
659 va_start(var_args, canvas);
660 adg_canvas_set_scales_valist(canvas, var_args);
661 va_end(var_args);
665 * adg_canvas_set_scales_valist:
666 * @canvas: an #AdgCanvas
667 * @var_args: <constant>NULL</constant> terminated list of strings
669 * Vararg variant of adg_canvas_set_scales().
671 * Since: 1.0
673 void
674 adg_canvas_set_scales_valist(AdgCanvas *canvas, va_list var_args)
676 gchar **scales;
677 const gchar *scale;
678 gint n;
680 g_return_if_fail(ADG_IS_CANVAS(canvas));
682 scales = NULL;
683 n = 0;
684 while ((scale = va_arg(var_args, const gchar *)) != NULL) {
685 ++n;
686 scales = g_realloc(scales, sizeof(const gchar *) * (n + 1));
687 scales[n-1] = g_strdup(scale);
688 scales[n] = NULL;
691 g_object_set(canvas, "scales", scales, NULL);
692 g_strfreev(scales);
696 * adg_canvas_set_scales_array: (rename-to adg_canvas_set_scales)
697 * @canvas: an #AdgCanvas
698 * @scales: (array zero-terminated=1) (allow-none): <constant>NULL</constant> terminated array of scales
700 * Array variant of adg_canvas_set_scales().
702 * Since: 1.0
704 void
705 adg_canvas_set_scales_array(AdgCanvas *canvas, gchar **scales)
707 g_return_if_fail(ADG_IS_CANVAS(canvas));
708 g_object_set(canvas, "scales", scales, NULL);
712 * adg_canvas_get_scales:
713 * @canvas: an #AdgCanvas
715 * Gets the list of scales set on @canvas: check adg_canvas_set_scales()
716 * to get an idea of what scales are supposed to be.
718 * If no scales are set, <constant>NULL</constant> is returned.
720 * Returns: (element-type utf8) (transfer none): the <constant>NULL</constant> terminated list of valid scales.
722 * Since: 1.0
724 gchar **
725 adg_canvas_get_scales(AdgCanvas *canvas)
727 AdgCanvasPrivate *data;
729 g_return_val_if_fail(ADG_IS_CANVAS(canvas), NULL);
731 data = adg_canvas_get_instance_private(canvas);
732 return data->scales;
736 * adg_canvas_autoscale:
737 * @canvas: an #AdgCanvas
739 * Applies one scale per time, in the order they have been provided
740 * in the adg_canvas_set_scales() call, until the drawing can be
741 * entirely contained into the current paper. If successful, the
742 * scale of the title block is changed accordingly and the drawing
743 * is centered inside the paper.
745 * The paddings are taken into account while computing the drawing
746 * extents.
748 * Since: 1.0
750 void
751 adg_canvas_autoscale(AdgCanvas *canvas)
753 AdgCanvasPrivate *data;
754 gchar **p_scale;
755 AdgEntity *entity;
756 cairo_matrix_t map;
757 CpmlExtents extents;
758 AdgTitleBlock *title_block;
759 CpmlPair delta;
761 g_return_if_fail(ADG_IS_CANVAS(canvas));
762 g_return_if_fail(_ADG_OLD_ENTITY_CLASS->arrange != NULL);
764 data = adg_canvas_get_instance_private(canvas);
765 entity = (AdgEntity *) canvas;
766 title_block = data->title_block;
768 /* Manually calling the arrange() method instead of emitting the "arrange"
769 * signal does not invalidate the global matrix: let's do it right now */
770 adg_entity_global_changed(entity);
772 for (p_scale = data->scales; p_scale != NULL && *p_scale != NULL; ++p_scale) {
773 const gchar *scale = *p_scale;
774 gdouble factor = adg_scale_factor(scale);
775 if (factor <= 0)
776 continue;
778 cairo_matrix_init_scale(&map, factor, factor);
779 adg_entity_set_local_map(entity, &map);
780 adg_entity_local_changed(entity);
782 /* Arrange the entities inside the canvas, but not the canvas itself,
783 * just to get the bounding box of the drawing without the paper */
784 _ADG_OLD_ENTITY_CLASS->arrange(entity);
785 cpml_extents_copy(&extents, adg_entity_get_extents(entity));
787 /* Just in case @canvas is empty */
788 if (! extents.is_defined)
789 return;
791 _adg_apply_paddings(canvas, &extents);
793 if (title_block != NULL)
794 adg_title_block_set_scale(title_block, scale);
796 /* Bail out if paper size is not specified or invalid */
797 if (data->size.x <= 0 || data->size.y <= 0)
798 break;
800 /* If the drawing extents are fully contained inside the paper size,
801 * center the drawing in the paper and bail out */
802 delta.x = data->size.x - extents.size.x;
803 delta.y = data->size.y - extents.size.y;
804 if (delta.x >= 0 && delta.y >= 0) {
805 cairo_matrix_t transform;
806 cairo_matrix_init_translate(&transform,
807 delta.x / 2 - extents.org.x,
808 delta.y / 2 - extents.org.y);
809 adg_entity_transform_local_map(entity, &transform,
810 ADG_TRANSFORM_AFTER);
811 break;
817 * adg_canvas_set_background_dress:
818 * @canvas: an #AdgCanvas
819 * @dress: the new #AdgDress to use
821 * Sets a new background dress for rendering @canvas: the new
822 * dress must be a color dress.
824 * Since: 1.0
826 void
827 adg_canvas_set_background_dress(AdgCanvas *canvas, AdgDress dress)
829 g_return_if_fail(ADG_IS_CANVAS(canvas));
830 g_object_set(canvas, "background-dress", dress, NULL);
834 * adg_canvas_get_background_dress:
835 * @canvas: an #AdgCanvas
837 * Gets the background dress to be used in rendering @canvas.
839 * Returns: the current background dress.
841 * Since: 1.0
843 AdgDress
844 adg_canvas_get_background_dress(AdgCanvas *canvas)
846 AdgCanvasPrivate *data;
848 g_return_val_if_fail(ADG_IS_CANVAS(canvas), ADG_DRESS_UNDEFINED);
850 data = adg_canvas_get_instance_private(canvas);
851 return data->background_dress;
855 * adg_canvas_set_frame_dress:
856 * @canvas: an #AdgCanvas
857 * @dress: the new #AdgDress to use
859 * Sets the #AdgCanvas:frame-dress property of @canvas to @dress:
860 * the new dress must be a line dress.
862 * Since: 1.0
864 void
865 adg_canvas_set_frame_dress(AdgCanvas *canvas, AdgDress dress)
867 g_return_if_fail(ADG_IS_CANVAS(canvas));
868 g_object_set(canvas, "frame-dress", dress, NULL);
872 * adg_canvas_get_frame_dress:
873 * @canvas: an #AdgCanvas
875 * Gets the frame dress to be used in rendering the border of @canvas.
877 * Returns: the current frame dress.
879 * Since: 1.0
881 AdgDress
882 adg_canvas_get_frame_dress(AdgCanvas *canvas)
884 AdgCanvasPrivate *data;
886 g_return_val_if_fail(ADG_IS_CANVAS(canvas), ADG_DRESS_UNDEFINED);
888 data = adg_canvas_get_instance_private(canvas);
889 return data->frame_dress;
893 * adg_canvas_set_title_block:
894 * @canvas: an #AdgCanvas
895 * @title_block: (transfer full): a title block
897 * Sets the #AdgCanvas:title-block property of @canvas to @title_block.
899 * Although a title block entity could be added to @canvas in the usual
900 * way, that is using the adg_container_add() method, assigning a title
901 * block with adg_canvas_set_title_block() is somewhat different:
903 * - @title_block will be automatically attached to the bottom right
904 * corner of to the @canvas frame (this could be accomplished in the
905 * usual way too, by resetting the right and bottom paddings);
906 * - the @title_block boundary box is not taken into account while
907 * computing the extents of @canvas.
909 * Since: 1.0
911 void
912 adg_canvas_set_title_block(AdgCanvas *canvas,
913 AdgTitleBlock *title_block)
915 g_return_if_fail(ADG_IS_CANVAS(canvas));
916 g_return_if_fail(title_block == NULL || ADG_IS_TITLE_BLOCK(title_block));
917 g_object_set(canvas, "title-block", title_block, NULL);
921 * adg_canvas_get_title_block:
922 * @canvas: an #AdgCanvas
924 * Gets the #AdgTitleBlock object of @canvas: check
925 * adg_canvas_set_title_block() for details.
927 * The returned entity is owned by @canvas and should not be
928 * modified or freed.
930 * Returns: (transfer none): the title block object or <constant>NULL</constant>.
932 * Since: 1.0
934 AdgTitleBlock *
935 adg_canvas_get_title_block(AdgCanvas *canvas)
937 AdgCanvasPrivate *data;
939 g_return_val_if_fail(ADG_IS_CANVAS(canvas), NULL);
941 data = adg_canvas_get_instance_private(canvas);
942 return data->title_block;
946 * adg_canvas_set_top_margin:
947 * @canvas: an #AdgCanvas
948 * @value: the new margin, in global space
950 * Changes the top margin of @canvas by setting #AdgCanvas:top-margin
951 * to @value. Negative values are allowed.
953 * Since: 1.0
955 void
956 adg_canvas_set_top_margin(AdgCanvas *canvas, gdouble value)
958 g_return_if_fail(ADG_IS_CANVAS(canvas));
959 g_object_set(canvas, "top-margin", value, NULL);
963 * adg_canvas_get_top_margin:
964 * @canvas: an #AdgCanvas
966 * Gets the top margin (in global space) of @canvas.
968 * Returns: the requested margin or 0 on error.
970 * Since: 1.0
972 gdouble
973 adg_canvas_get_top_margin(AdgCanvas *canvas)
975 AdgCanvasPrivate *data;
977 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
979 data = adg_canvas_get_instance_private(canvas);
980 return data->top_margin;
984 * adg_canvas_set_right_margin:
985 * @canvas: an #AdgCanvas
986 * @value: the new margin, in global space
988 * Changes the right margin of @canvas by setting
989 * #AdgCanvas:right-margin to @value. Negative values are allowed.
991 * Since: 1.0
993 void
994 adg_canvas_set_right_margin(AdgCanvas *canvas, gdouble value)
996 g_return_if_fail(ADG_IS_CANVAS(canvas));
997 g_object_set(canvas, "right-margin", value, NULL);
1001 * adg_canvas_get_right_margin:
1002 * @canvas: an #AdgCanvas
1004 * Gets the right margin (in global space) of @canvas.
1006 * Returns: the requested margin or 0 on error.
1008 * Since: 1.0
1010 gdouble
1011 adg_canvas_get_right_margin(AdgCanvas *canvas)
1013 AdgCanvasPrivate *data;
1015 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
1017 data = adg_canvas_get_instance_private(canvas);
1018 return data->right_margin;
1022 * adg_canvas_set_bottom_margin:
1023 * @canvas: an #AdgCanvas
1024 * @value: the new margin, in global space
1026 * Changes the bottom margin of @canvas by setting
1027 * #AdgCanvas:bottom-margin to @value. Negative values are allowed.
1029 * Since: 1.0
1031 void
1032 adg_canvas_set_bottom_margin(AdgCanvas *canvas, gdouble value)
1034 g_return_if_fail(ADG_IS_CANVAS(canvas));
1035 g_object_set(canvas, "bottom-margin", value, NULL);
1039 * adg_canvas_get_bottom_margin:
1040 * @canvas: an #AdgCanvas
1042 * Gets the bottom margin (in global space) of @canvas.
1044 * Returns: the requested margin or 0 on error.
1046 * Since: 1.0
1048 gdouble
1049 adg_canvas_get_bottom_margin(AdgCanvas *canvas)
1051 AdgCanvasPrivate *data;
1053 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
1055 data = adg_canvas_get_instance_private(canvas);
1056 return data->bottom_margin;
1060 * adg_canvas_set_left_margin:
1061 * @canvas: an #AdgCanvas
1062 * @value: the new margin, in global space
1064 * Changes the left margin of @canvas by setting
1065 * #AdgCanvas:left-margin to @value. Negative values are allowed.
1067 * Since: 1.0
1069 void
1070 adg_canvas_set_left_margin(AdgCanvas *canvas, gdouble value)
1072 g_return_if_fail(ADG_IS_CANVAS(canvas));
1073 g_object_set(canvas, "left-margin", value, NULL);
1077 * adg_canvas_get_left_margin:
1078 * @canvas: an #AdgCanvas
1080 * Gets the left margin (in global space) of @canvas.
1082 * Returns: the requested margin or 0 on error.
1084 * Since: 1.0
1086 gdouble
1087 adg_canvas_get_left_margin(AdgCanvas *canvas)
1089 AdgCanvasPrivate *data;
1091 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
1093 data = adg_canvas_get_instance_private(canvas);
1094 return data->left_margin;
1098 * adg_canvas_set_margins:
1099 * @canvas: an #AdgCanvas
1100 * @top: top margin, in global space
1101 * @right: right margin, in global space
1102 * @bottom: bottom margin, in global space
1103 * @left: left margin, in global space
1105 * Convenient function to set all the margins at once.
1107 * Since: 1.0
1109 void
1110 adg_canvas_set_margins(AdgCanvas *canvas,
1111 gdouble top, gdouble right,
1112 gdouble bottom, gdouble left)
1114 g_return_if_fail(ADG_IS_CANVAS(canvas));
1115 g_object_set(canvas, "top-margin", top, "right-margin", right,
1116 "bottom-margin", bottom, "left-margin", left, NULL);
1120 * adg_canvas_get_margins:
1121 * @canvas: an #AdgCanvas
1122 * @top: (out) (nullable): where to store the top margin or NULL
1123 * @right: (out) (nullable): where to store the right margin or NULL
1124 * @bottom: (out) (nullable): where to store the bottom margin or NULL
1125 * @left: (out) (nullable): where to store the left margin or NULL
1127 * Convenient function to get all the margins at once.
1129 * Since: 1.0
1131 void
1132 adg_canvas_get_margins(AdgCanvas *canvas,
1133 gdouble *top, gdouble *right,
1134 gdouble *bottom, gdouble *left)
1136 AdgCanvasPrivate *data;
1138 g_return_if_fail(ADG_IS_CANVAS(canvas));
1140 data = adg_canvas_get_instance_private(canvas);
1142 if (top != NULL) {
1143 *top = data->top_margin;
1145 if (right != NULL) {
1146 *right = data->right_margin;
1148 if (bottom != NULL) {
1149 *bottom = data->bottom_margin;
1151 if (left != NULL) {
1152 *left = data->left_margin;
1157 * adg_canvas_apply_margins:
1158 * @canvas: an #AdgCanvas
1159 * @extents: where apply the margins
1161 * A convenient function to apply the margins of @canvas to the
1162 * arbitrary #CpmlExtents struct @extents. "Apply" means @extents
1163 * are enlarged of the specific margin values.
1165 * Since: 1.0
1167 void
1168 adg_canvas_apply_margins(AdgCanvas *canvas, CpmlExtents *extents)
1170 g_return_if_fail(ADG_IS_CANVAS(canvas));
1171 g_return_if_fail(extents != NULL);
1173 if (extents->is_defined) {
1174 AdgCanvasPrivate *data = adg_canvas_get_instance_private(canvas);
1176 extents->org.x -= data->left_margin;
1177 extents->org.y -= data->top_margin;
1178 extents->size.x += data->left_margin + data->right_margin;
1179 extents->size.y += data->top_margin + data->bottom_margin;
1184 * adg_canvas_switch_frame:
1185 * @canvas: an #AdgCanvas
1186 * @new_state: the new flag status
1188 * Sets a new status on the #AdgCanvas:has-frame
1189 * property: <constant>TRUE</constant> means a border around
1190 * the canvas extents (less the margins) should be rendered.
1192 * Since: 1.0
1194 void
1195 adg_canvas_switch_frame(AdgCanvas *canvas, gboolean new_state)
1197 g_return_if_fail(ADG_IS_CANVAS(canvas));
1198 g_object_set(canvas, "has-frame", new_state, NULL);
1202 * adg_canvas_has_frame:
1203 * @canvas: an #AdgCanvas
1205 * Gets the current status of the #AdgCanvas:has-frame property,
1206 * that is whether a border around the canvas extents (less the
1207 * margins) should be rendered (<constant>TRUE</constant>) or not
1208 * (<constant>FALSE</constant>).
1210 * Returns: the current status of the frame flag.
1212 * Since: 1.0
1214 gboolean
1215 adg_canvas_has_frame(AdgCanvas *canvas)
1217 AdgCanvasPrivate *data;
1219 g_return_val_if_fail(ADG_IS_CANVAS(canvas), FALSE);
1221 data = adg_canvas_get_instance_private(canvas);
1222 return data->has_frame;
1226 * adg_canvas_set_top_padding:
1227 * @canvas: an #AdgCanvas
1228 * @value: the new padding, in global space
1230 * Changes the top padding of @canvas by setting
1231 * #AdgCanvas:top-padding to @value. Negative values are allowed.
1233 * Since: 1.0
1235 void
1236 adg_canvas_set_top_padding(AdgCanvas *canvas, gdouble value)
1238 g_return_if_fail(ADG_IS_CANVAS(canvas));
1239 g_object_set(canvas, "top-padding", value, NULL);
1243 * adg_canvas_get_top_padding:
1244 * @canvas: an #AdgCanvas
1246 * Gets the top padding (in global space) of @canvas.
1248 * Returns: the requested padding or 0 on error.
1250 * Since: 1.0
1252 gdouble
1253 adg_canvas_get_top_padding(AdgCanvas *canvas)
1255 AdgCanvasPrivate *data;
1257 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
1259 data = adg_canvas_get_instance_private(canvas);
1260 return data->top_padding;
1264 * adg_canvas_set_right_padding:
1265 * @canvas: an #AdgCanvas
1266 * @value: the new padding, in global space
1268 * Changes the right padding of @canvas by setting #AdgCanvas:right-padding
1269 * to @value. Negative values are allowed.
1271 * Since: 1.0
1273 void
1274 adg_canvas_set_right_padding(AdgCanvas *canvas, gdouble value)
1276 g_return_if_fail(ADG_IS_CANVAS(canvas));
1277 g_object_set(canvas, "right-padding", value, NULL);
1281 * adg_canvas_get_right_padding:
1282 * @canvas: an #AdgCanvas
1284 * Gets the right padding (in global space) of @canvas.
1286 * Returns: the requested padding or 0 on error.
1288 * Since: 1.0
1290 gdouble
1291 adg_canvas_get_right_padding(AdgCanvas *canvas)
1293 AdgCanvasPrivate *data;
1295 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
1297 data = adg_canvas_get_instance_private(canvas);
1298 return data->right_padding;
1303 * adg_canvas_set_bottom_padding:
1304 * @canvas: an #AdgCanvas
1305 * @value: the new padding, in global space
1307 * Changes the bottom padding of @canvas by setting
1308 * #AdgCanvas:bottom-padding to @value. Negative values are allowed.
1310 * Since: 1.0
1312 void
1313 adg_canvas_set_bottom_padding(AdgCanvas *canvas, gdouble value)
1315 g_return_if_fail(ADG_IS_CANVAS(canvas));
1316 g_object_set(canvas, "bottom-padding", value, NULL);
1320 * adg_canvas_get_bottom_padding:
1321 * @canvas: an #AdgCanvas
1323 * Gets the bottom padding (in global space) of @canvas.
1325 * Returns: the requested padding or 0 on error.
1327 * Since: 1.0
1329 gdouble
1330 adg_canvas_get_bottom_padding(AdgCanvas *canvas)
1332 AdgCanvasPrivate *data;
1334 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
1336 data = adg_canvas_get_instance_private(canvas);
1337 return data->bottom_padding;
1341 * adg_canvas_set_left_padding:
1342 * @canvas: an #AdgCanvas
1343 * @value: the new padding, in global space
1345 * Changes the left padding of @canvas by setting
1346 * #AdgCanvas:left-padding to @value. Negative values are allowed.
1348 * Since: 1.0
1350 void
1351 adg_canvas_set_left_padding(AdgCanvas *canvas, gdouble value)
1353 g_return_if_fail(ADG_IS_CANVAS(canvas));
1354 g_object_set(canvas, "left-padding", value, NULL);
1358 * adg_canvas_get_left_padding:
1359 * @canvas: an #AdgCanvas
1361 * Gets the left padding (in global space) of @canvas.
1363 * Returns: the requested padding or 0 on error.
1365 * Since: 1.0
1367 gdouble
1368 adg_canvas_get_left_padding(AdgCanvas *canvas)
1370 AdgCanvasPrivate *data;
1372 g_return_val_if_fail(ADG_IS_CANVAS(canvas), 0.);
1374 data = adg_canvas_get_instance_private(canvas);
1375 return data->left_padding;
1379 * adg_canvas_set_paddings:
1380 * @canvas: an #AdgCanvas
1381 * @top: top padding, in global space
1382 * @right: right padding, in global space
1383 * @bottom: bottom padding, in global space
1384 * @left: left padding, in global space
1386 * Convenient function to set all the paddings at once.
1388 * Since: 1.0
1390 void
1391 adg_canvas_set_paddings(AdgCanvas *canvas,
1392 gdouble top, gdouble right,
1393 gdouble bottom, gdouble left)
1395 g_return_if_fail(ADG_IS_CANVAS(canvas));
1396 g_object_set(canvas, "top-padding", top, "right-padding", right,
1397 "bottom-padding", bottom, "left-padding", left, NULL);
1401 * adg_canvas_get_paddings:
1402 * @canvas: an #AdgCanvas
1403 * @top: (out) (nullable): where to store the top padding or NULL
1404 * @right: (out) (nullable): where to store the right padding or NULL
1405 * @bottom: (out) (nullable): where to store the bottom padding or NULL
1406 * @left: (out) (nullable): where to store the left padding or NULL
1408 * Convenient function to get all the paddings at once.
1410 * Since: 1.0
1412 void
1413 adg_canvas_get_paddings(AdgCanvas *canvas,
1414 gdouble *top, gdouble *right,
1415 gdouble *bottom, gdouble *left)
1417 AdgCanvasPrivate *data;
1419 g_return_if_fail(ADG_IS_CANVAS(canvas));
1421 data = adg_canvas_get_instance_private(canvas);
1423 if (top != NULL) {
1424 *top = data->top_padding;
1426 if (right != NULL) {
1427 *right = data->right_padding;
1429 if (bottom != NULL) {
1430 *bottom = data->bottom_padding;
1432 if (left != NULL) {
1433 *left = data->left_padding;
1438 static void
1439 _adg_global_changed(AdgEntity *entity)
1441 AdgCanvasPrivate *data = adg_canvas_get_instance_private((AdgCanvas *) entity);
1443 if (_ADG_OLD_ENTITY_CLASS->global_changed)
1444 _ADG_OLD_ENTITY_CLASS->global_changed(entity);
1446 if (data->title_block)
1447 adg_entity_global_changed((AdgEntity *) data->title_block);
1450 static void
1451 _adg_local_changed(AdgEntity *entity)
1453 AdgCanvasPrivate *data = adg_canvas_get_instance_private((AdgCanvas *) entity);
1454 AdgTitleBlock *title_block = data->title_block;
1456 if (_ADG_OLD_ENTITY_CLASS->local_changed)
1457 _ADG_OLD_ENTITY_CLASS->local_changed(entity);
1459 if (title_block != NULL) {
1460 const gchar *scale = adg_title_block_get_scale(title_block);
1462 if (scale != NULL && scale[0] != '\0') {
1463 const cairo_matrix_t *map = adg_entity_get_local_map(entity);
1464 gdouble factor = adg_scale_factor(scale);
1466 if (map->xx != factor || map->yy != factor)
1467 adg_title_block_set_scale(title_block, "---");
1470 adg_entity_local_changed((AdgEntity *) title_block);
1474 static void
1475 _adg_invalidate(AdgEntity *entity)
1477 AdgCanvasPrivate *data = adg_canvas_get_instance_private((AdgCanvas *) entity);
1479 if (_ADG_OLD_ENTITY_CLASS->invalidate)
1480 _ADG_OLD_ENTITY_CLASS->invalidate(entity);
1482 if (data->title_block)
1483 adg_entity_invalidate((AdgEntity *) data->title_block);
1486 static void
1487 _adg_arrange(AdgEntity *entity)
1489 AdgCanvas *canvas;
1490 AdgCanvasPrivate *data;
1491 CpmlExtents extents;
1493 if (_ADG_OLD_ENTITY_CLASS->arrange)
1494 _ADG_OLD_ENTITY_CLASS->arrange(entity);
1496 cpml_extents_copy(&extents, adg_entity_get_extents(entity));
1498 /* The extents should be defined, otherwise there is no drawing */
1499 g_return_if_fail(extents.is_defined);
1501 canvas = (AdgCanvas *) entity;
1502 data = adg_canvas_get_instance_private(canvas);
1504 _adg_apply_paddings(canvas, &extents);
1506 if (data->size.x > 0 || data->size.y > 0) {
1507 const cairo_matrix_t *global = adg_entity_get_global_matrix(entity);
1508 CpmlExtents paper;
1510 paper.org.x = 0;
1511 paper.org.y = 0;
1512 paper.size.x = data->size.x;
1513 paper.size.y = data->size.y;
1515 cairo_matrix_transform_point(global, &paper.org.x, &paper.org.y);
1516 cairo_matrix_transform_distance(global, &paper.size.x, &paper.size.y);
1518 if (data->size.x > 0) {
1519 extents.org.x = paper.org.x;
1520 extents.size.x = paper.size.x;
1522 if (data->size.y > 0) {
1523 extents.org.y = paper.org.y;
1524 extents.size.y = paper.size.y;
1528 if (data->title_block) {
1529 AdgEntity *title_block_entity;
1530 const CpmlExtents *title_block_extents;
1531 CpmlPair shift;
1533 title_block_entity = (AdgEntity *) data->title_block;
1534 adg_entity_arrange(title_block_entity);
1535 title_block_extents = adg_entity_get_extents(title_block_entity);
1537 shift.x = extents.org.x + extents.size.x - title_block_extents->org.x
1538 - title_block_extents->size.x;
1539 shift.y = extents.org.y + extents.size.y - title_block_extents->org.y
1540 - title_block_extents->size.y;
1542 /* The following block could be optimized by skipping tiny shift,
1543 * usually left by rounding errors */
1544 if (shift.x != 0 || shift.y != 0) {
1545 cairo_matrix_t unglobal, map;
1546 adg_matrix_copy(&unglobal, adg_entity_get_global_matrix(entity));
1547 cairo_matrix_invert(&unglobal);
1549 cairo_matrix_transform_distance(&unglobal, &shift.x, &shift.y);
1550 cairo_matrix_init_translate(&map, shift.x, shift.y);
1551 adg_entity_transform_global_map(title_block_entity, &map,
1552 ADG_TRANSFORM_AFTER);
1554 adg_entity_global_changed(title_block_entity);
1555 adg_entity_arrange(title_block_entity);
1556 title_block_extents = adg_entity_get_extents(title_block_entity);
1557 cpml_extents_add(&extents, title_block_extents);
1561 /* Impose the new extents */
1562 adg_entity_set_extents(entity, &extents);
1565 static void
1566 _adg_render(AdgEntity *entity, cairo_t *cr)
1568 AdgCanvasPrivate *data = adg_canvas_get_instance_private((AdgCanvas *) entity);
1569 const CpmlExtents *extents = adg_entity_get_extents(entity);
1571 cairo_save(cr);
1573 /* Background fill */
1574 cairo_rectangle(cr, extents->org.x - data->left_margin,
1575 extents->org.y - data->top_margin,
1576 extents->size.x + data->left_margin + data->right_margin,
1577 extents->size.y + data->top_margin + data->bottom_margin);
1578 adg_entity_apply_dress(entity, data->background_dress, cr);
1579 cairo_fill(cr);
1581 /* Frame line */
1582 if (data->has_frame) {
1583 cairo_rectangle(cr, extents->org.x, extents->org.y,
1584 extents->size.x, extents->size.y);
1585 cairo_transform(cr, adg_entity_get_global_matrix(entity));
1586 adg_entity_apply_dress(entity, data->frame_dress, cr);
1587 cairo_stroke(cr);
1590 cairo_restore(cr);
1592 if (data->title_block)
1593 adg_entity_render((AdgEntity *) data->title_block, cr);
1595 if (_ADG_OLD_ENTITY_CLASS->render)
1596 _ADG_OLD_ENTITY_CLASS->render(entity, cr);
1599 static void
1600 _adg_apply_paddings(AdgCanvas *canvas, CpmlExtents *extents)
1602 AdgCanvasPrivate *data = adg_canvas_get_instance_private(canvas);
1603 extents->org.x -= data->left_padding;
1604 extents->size.x += data->left_padding + data->right_padding;
1605 extents->org.y -= data->top_padding;
1606 extents->size.y += data->top_padding + data->bottom_padding;
1611 * adg_canvas_export:
1612 * @canvas: an #AdgCanvas
1613 * @type: (type gint): the export format
1614 * @file: the name of the resulting file
1615 * @gerror: (allow-none): return location for errors
1617 * A helper function that provides a bare export functionality.
1618 * It basically exports the drawing in @canvas in the @file
1619 * in the @type format. Any error will be reported in @gerror,
1620 * if not <constant>NULL</constant>.
1622 * Returns: <constant>TRUE</constant> on success, <constant>FALSE</constant> otherwise.
1624 * Since: 1.0
1626 gboolean
1627 adg_canvas_export(AdgCanvas *canvas, cairo_surface_type_t type,
1628 const gchar *file, GError **gerror)
1630 AdgEntity *entity;
1631 const CpmlExtents *extents;
1632 gdouble top, bottom, left, right, width, height, factor;
1633 cairo_surface_t *surface;
1634 cairo_t *cr;
1635 cairo_status_t status;
1637 g_return_val_if_fail(ADG_IS_CANVAS(canvas), FALSE);
1638 g_return_val_if_fail(file != NULL, FALSE);
1639 g_return_val_if_fail(gerror == NULL || *gerror == NULL, FALSE);
1641 entity = (AdgEntity *) canvas;
1643 adg_entity_arrange(entity);
1644 extents = adg_entity_get_extents(entity);
1646 factor = adg_canvas_get_factor(canvas);
1647 top = factor * adg_canvas_get_top_margin(canvas);
1648 bottom = factor * adg_canvas_get_bottom_margin(canvas);
1649 left = factor * adg_canvas_get_left_margin(canvas);
1650 right = factor * adg_canvas_get_right_margin(canvas);
1651 width = factor * extents->size.x + left + right;
1652 height = factor * extents->size.y + top + bottom;
1654 switch (type) {
1655 #ifdef CAIRO_HAS_PNG_FUNCTIONS
1656 case CAIRO_SURFACE_TYPE_IMAGE:
1657 surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height);
1658 break;
1659 #endif
1660 #ifdef CAIRO_HAS_PDF_SURFACE
1661 case CAIRO_SURFACE_TYPE_PDF:
1662 surface = cairo_pdf_surface_create(file, width, height);
1663 break;
1664 #endif
1665 #ifdef CAIRO_HAS_PS_SURFACE
1666 case CAIRO_SURFACE_TYPE_PS:
1667 surface = cairo_ps_surface_create(file, width, height);
1668 break;
1669 #endif
1670 #ifdef CAIRO_HAS_SVG_SURFACE
1671 case CAIRO_SURFACE_TYPE_SVG:
1672 surface = cairo_svg_surface_create(file, width, height);
1673 break;
1674 #endif
1675 default:
1676 surface = NULL;
1677 break;
1680 if (surface == NULL) {
1681 g_set_error(gerror, ADG_CANVAS_ERROR, ADG_CANVAS_ERROR_SURFACE,
1682 "unable to handle surface type '%d'",
1683 type);
1684 return FALSE;
1687 cairo_surface_set_device_offset(surface, left, top);
1688 cairo_surface_set_device_scale(surface, factor, factor);
1689 cr = cairo_create(surface);
1690 cairo_surface_destroy(surface);
1692 adg_entity_render(ADG_ENTITY(canvas), cr);
1694 if (cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_IMAGE) {
1695 status = cairo_surface_write_to_png(surface, file);
1696 } else {
1697 cairo_show_page(cr);
1698 status = cairo_status(cr);
1701 cairo_destroy(cr);
1703 if (status != CAIRO_STATUS_SUCCESS) {
1704 g_set_error(gerror, ADG_CANVAS_ERROR, ADG_CANVAS_ERROR_CAIRO,
1705 "cairo reported '%s'",
1706 cairo_status_to_string(status));
1707 return FALSE;
1710 return TRUE;
1714 #if GTK3_ENABLED || GTK2_ENABLED
1715 #include <gtk/gtk.h>
1717 static void
1718 _adg_update_margin(AdgCanvas *canvas, gdouble *margin,
1719 gdouble *side, gdouble new_margin)
1721 GtkPageSetup *page_setup;
1722 gdouble old_margin;
1724 old_margin = *margin;
1725 *margin = new_margin;
1727 page_setup = adg_canvas_get_page_setup(canvas);
1728 if (page_setup == NULL)
1729 return;
1731 *side += old_margin - new_margin;
1735 * adg_canvas_set_paper:
1736 * @canvas: an #AdgCanvas
1737 * @paper_name: a paper name
1738 * @orientation: the page orientation
1740 * A convenient function to set size and margins of @canvas
1741 * using a @paper_name and an @orientation value. This should
1742 * be a PWG 5101.1-2002 paper name and it will be passed as
1743 * is to gtk_paper_size_new(), so use any valid name accepted
1744 * by that function.
1746 * This has the same effect as creating a #GtkPageSetup object
1747 * and binding it to @canvas with adg_canvas_set_page_setup():
1748 * check its documentation for knowing the implications.
1750 * To reset the size to its default behavior (i.e. autocalculate
1751 * it from the entities) just call adg_canvas_set_size_explicit()
1752 * passing <constant>0, 0</constant>.
1754 * If you want to use your own margins on a named paper size,
1755 * set them <emphasis>after</emphasis> the call to this function.
1757 * Since: 1.0
1759 void
1760 adg_canvas_set_paper(AdgCanvas *canvas,
1761 const gchar *paper_name,
1762 GtkPageOrientation orientation)
1764 GtkPageSetup *page_setup;
1765 GtkPaperSize *paper_size;
1767 g_return_if_fail(ADG_IS_CANVAS(canvas));
1768 g_return_if_fail(paper_name != NULL);
1770 page_setup = gtk_page_setup_new();
1771 paper_size = gtk_paper_size_new(paper_name);
1773 gtk_page_setup_set_paper_size(page_setup, paper_size);
1774 gtk_page_setup_set_orientation(page_setup, orientation);
1775 gtk_paper_size_free(paper_size);
1777 adg_canvas_set_page_setup(canvas, page_setup);
1778 g_object_unref(page_setup);
1782 * adg_canvas_set_page_setup:
1783 * @canvas: an #AdgCanvas
1784 * @page_setup: (allow-none) (transfer none): the page setup
1786 * A convenient function to setup the page of @canvas so it can
1787 * also be subsequentially used for printing. It is allowed to
1788 * pass <constant>NULL</constant> as @page_setup to restore the
1789 * default page setup.
1791 * When a canvas is bound to a page setup, the paper size is kept
1792 * constant. This implies increasing or decreasing the margins
1793 * respectively decreases or increases the page size of the
1794 * relevant dimension, e.g. increasing the right margin decreases
1795 * the width (the x component of the size).
1797 * A reference to @page_setup is added, so there is no need to keep
1798 * alive this object after a call to this function. @page_setup can
1799 * be retrieved at any time with adg_canvas_get_page_setup().
1801 * The size and margins provided by @page_setup are immediately
1802 * used to set size and margins of @canvas. This means using
1803 * <constant>NULL</constant> as @page_setup just releases the
1804 * reference to the previous @page_setup object... all the page
1805 * settings are still retained.
1807 * If you want to use your own margins on a page setup,
1808 * set them on canvas <emphasis>after</emphasis> the call to this
1809 * function or set them on @page_setup <emphasis>before</emphasis>.
1811 * <informalexample><programlisting language="C">
1812 * // By default, canvas does not have an explicit size
1813 * adg_canvas_set_page_setup(canvas, a4);
1814 * g_object_unref(a4);
1815 * // Now canvas has size and margins specified by a4
1816 * // (and a4 should be a prefilled GtkPageSetup object).
1817 * adg_canvas_set_page_setup(canvas, NULL);
1818 * // Now canvas is no more bound to a4 and that object (if
1819 * // not referenced anywhere else) can be garbage-collected.
1820 * // The page setup of canvas has not changed though.
1821 * adg_canvas_set_size_explicit(canvas, 0, 0);
1822 * // Now the page size of canvas has been restored to
1823 * // their default behavior.
1824 * </programlisting></informalexample>
1826 * Since: 1.0
1828 void
1829 adg_canvas_set_page_setup(AdgCanvas *canvas, GtkPageSetup *page_setup)
1831 gdouble top, right, bottom, left;
1832 CpmlPair size;
1834 g_return_if_fail(ADG_IS_CANVAS(canvas));
1836 if (page_setup == NULL) {
1837 /* By convention, NULL resets the default page but
1838 * does not change any other property */
1839 g_object_set_data((GObject *) canvas, "_adg_page_setup", NULL);
1840 return;
1843 g_return_if_fail(GTK_IS_PAGE_SETUP(page_setup));
1845 top = gtk_page_setup_get_top_margin(page_setup, GTK_UNIT_POINTS);
1846 right = gtk_page_setup_get_right_margin(page_setup, GTK_UNIT_POINTS);
1847 bottom = gtk_page_setup_get_bottom_margin(page_setup, GTK_UNIT_POINTS);
1848 left = gtk_page_setup_get_left_margin(page_setup, GTK_UNIT_POINTS);
1849 size.x = gtk_page_setup_get_page_width(page_setup, GTK_UNIT_POINTS);
1850 size.y = gtk_page_setup_get_page_height(page_setup, GTK_UNIT_POINTS);
1852 adg_canvas_set_size(canvas, &size);
1853 adg_canvas_set_margins(canvas, top, right, bottom, left);
1855 g_object_ref(page_setup);
1856 g_object_set_data_full((GObject *) canvas, "_adg_page_setup",
1857 page_setup, g_object_unref);
1861 * adg_canvas_get_page_setup:
1862 * @canvas: an #AdgCanvas
1864 * If adg_canvas_set_page_setup() is called, a #GtkPageSetup object
1865 * is created and bound to @canvas. This metho returns a pointer
1866 * to that internal object or <constant>NULL</constant> if
1867 * adg_canvas_set_page_setup() has not been called before.
1869 * Returns: (allow-none) (transfer none): the #GtkPageSetup with size and margins of @canvas of <constant>NULL</constant> on no setup found or errors.
1871 * Since: 1.0
1873 GtkPageSetup *
1874 adg_canvas_get_page_setup(AdgCanvas *canvas)
1876 g_return_val_if_fail(ADG_IS_CANVAS(canvas), NULL);
1877 return g_object_get_data((GObject *) canvas, "_adg_page_setup");
1880 #else
1882 static void
1883 _adg_update_margin(AdgCanvas *canvas, gdouble *margin,
1884 gdouble *side, gdouble new_margin)
1886 *margin = new_margin;
1888 #endif