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.
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.
67 * All fields are private and should not be used directly.
68 * Use its public methods instead.
76 * Error domain for canvas processing. Errors in this domain will be from the
77 * #AdgCanvasError enumeration. See #GError for information on error domains.
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.
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>
109 #ifdef CAIRO_HAS_PDF_SURFACE
110 #include <cairo-pdf.h>
112 #ifdef CAIRO_HAS_SVG_SURFACE
113 #include <cairo-svg.h>
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(AdgCanvas
, adg_canvas
, ADG_TYPE_CONTAINER
)
128 PROP_BACKGROUND_DRESS
,
143 static void _adg_dispose (GObject
*object
);
144 static void _adg_get_property (GObject
*object
,
148 static void _adg_set_property (GObject
*object
,
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
,
158 static void _adg_apply_paddings (AdgCanvas
*canvas
,
159 CpmlExtents
*extents
);
160 static void _adg_update_margin (AdgCanvas
*canvas
,
167 adg_canvas_error_quark(void)
171 if G_UNLIKELY (q
== 0)
172 q
= g_quark_from_static_string("adg-canvas-error-quark");
178 adg_canvas_class_init(AdgCanvasClass
*klass
)
180 GObjectClass
*gobject_class
;
181 AdgEntityClass
*entity_class
;
184 gobject_class
= (GObjectClass
*) klass
;
185 entity_class
= (AdgEntityClass
*) klass
;
187 g_type_class_add_private(klass
, sizeof(AdgCanvasPrivate
));
189 gobject_class
->dispose
= _adg_dispose
;
190 gobject_class
->get_property
= _adg_get_property
;
191 gobject_class
->set_property
= _adg_set_property
;
193 entity_class
->global_changed
= _adg_global_changed
;
194 entity_class
->local_changed
= _adg_local_changed
;
195 entity_class
->invalidate
= _adg_invalidate
;
196 entity_class
->arrange
= _adg_arrange
;
197 entity_class
->render
= _adg_render
;
199 param
= g_param_spec_boxed("size",
201 P_("The size set on this canvas: use 0 to have an automatic dimension based on the canvas extents"),
204 g_object_class_install_property(gobject_class
, PROP_SIZE
, param
);
206 param
= g_param_spec_double("factor",
208 P_("Global space units per point (1 point == 1/72 inch)"),
211 g_object_class_install_property(gobject_class
, PROP_FACTOR
, param
);
213 param
= g_param_spec_boxed("scales",
215 P_("List of scales to be tested while autoscaling the drawing"),
218 g_object_class_install_property(gobject_class
, PROP_SCALES
, param
);
220 param
= adg_param_spec_dress("background-dress",
221 P_("Background Dress"),
222 P_("The color dress to use for the canvas background"),
223 ADG_DRESS_COLOR_BACKGROUND
,
225 g_object_class_install_property(gobject_class
, PROP_BACKGROUND_DRESS
, param
);
227 param
= adg_param_spec_dress("frame-dress",
229 P_("Line dress to use while drawing the frame around the canvas"),
230 ADG_DRESS_LINE_FRAME
,
232 g_object_class_install_property(gobject_class
, PROP_FRAME_DRESS
, param
);
234 param
= g_param_spec_object("title-block",
236 P_("The title block to assign to this canvas"),
237 ADG_TYPE_TITLE_BLOCK
,
239 g_object_class_install_property(gobject_class
, PROP_TITLE_BLOCK
, param
);
241 param
= g_param_spec_double("top-margin",
243 P_("The margin (in global space) to leave above the frame"),
244 -G_MAXDOUBLE
, G_MAXDOUBLE
, 15,
246 g_object_class_install_property(gobject_class
, PROP_TOP_MARGIN
, param
);
248 param
= g_param_spec_double("right-margin",
250 P_("The margin (in global space) to leave empty at the right of the frame"),
251 -G_MAXDOUBLE
, G_MAXDOUBLE
, 15,
253 g_object_class_install_property(gobject_class
, PROP_RIGHT_MARGIN
, param
);
255 param
= g_param_spec_double("bottom-margin",
257 P_("The margin (in global space) to leave empty below the frame"),
258 -G_MAXDOUBLE
, G_MAXDOUBLE
, 15,
260 g_object_class_install_property(gobject_class
, PROP_BOTTOM_MARGIN
, param
);
262 param
= g_param_spec_double("left-margin",
264 P_("The margin (in global space) to leave empty at the left of the frame"),
265 -G_MAXDOUBLE
, G_MAXDOUBLE
, 15,
267 g_object_class_install_property(gobject_class
, PROP_LEFT_MARGIN
, param
);
269 param
= g_param_spec_boolean("has-frame",
270 P_("Has Frame Flag"),
271 P_("If enabled, a frame using the frame dress will be drawn around the canvas extents, taking into account the margins"),
274 g_object_class_install_property(gobject_class
, PROP_HAS_FRAME
, param
);
276 param
= g_param_spec_double("top-padding",
278 P_("The padding (in global space) to leave empty above between the drawing and the frame"),
279 -G_MAXDOUBLE
, G_MAXDOUBLE
, 15,
281 g_object_class_install_property(gobject_class
, PROP_TOP_PADDING
, param
);
283 param
= g_param_spec_double("right-padding",
285 P_("The padding (in global space) to leave empty at the right between the drawing and the frame"),
286 -G_MAXDOUBLE
, G_MAXDOUBLE
, 15,
288 g_object_class_install_property(gobject_class
, PROP_RIGHT_PADDING
, param
);
290 param
= g_param_spec_double("bottom-padding",
291 P_("Bottom Padding"),
292 P_("The padding (in global space) to leave empty below between the drawing and the frame"),
293 -G_MAXDOUBLE
, G_MAXDOUBLE
, 15,
295 g_object_class_install_property(gobject_class
, PROP_BOTTOM_PADDING
, param
);
297 param
= g_param_spec_double("left-padding",
299 P_("The padding (in global space) to leave empty at the left between the drawing and the frame"),
300 -G_MAXDOUBLE
, G_MAXDOUBLE
, 15,
302 g_object_class_install_property(gobject_class
, PROP_LEFT_PADDING
, param
);
306 adg_canvas_init(AdgCanvas
*canvas
)
308 AdgCanvasPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(canvas
,
311 const gchar
*scales
[] = {
312 "10:1", "5:1", "3:1", "2:1", "1:1", "1:2", "1:3", "1:5", "1:10",
320 data
->scales
= g_strdupv((gchar
**) scales
);
321 data
->background_dress
= ADG_DRESS_COLOR_BACKGROUND
;
322 data
->frame_dress
= ADG_DRESS_LINE_FRAME
;
323 data
->title_block
= NULL
;
324 data
->top_margin
= 15;
325 data
->right_margin
= 15;
326 data
->bottom_margin
= 15;
327 data
->left_margin
= 15;
328 data
->has_frame
= TRUE
;
329 data
->top_padding
= 15;
330 data
->right_padding
= 15;
331 data
->bottom_padding
= 15;
332 data
->left_padding
= 15;
338 _adg_dispose(GObject
*object
)
341 AdgCanvasPrivate
*data
;
343 canvas
= (AdgCanvas
*) object
;
346 if (data
->title_block
) {
347 g_object_unref(data
->title_block
);
348 data
->title_block
= NULL
;
351 if (data
->scales
!= NULL
) {
352 g_strfreev(data
->scales
);
356 if (_ADG_OLD_OBJECT_CLASS
->dispose
)
357 _ADG_OLD_OBJECT_CLASS
->dispose(object
);
362 _adg_get_property(GObject
*object
, guint prop_id
,
363 GValue
*value
, GParamSpec
*pspec
)
365 AdgCanvasPrivate
*data
= ((AdgCanvas
*) object
)->data
;
369 g_value_set_boxed(value
, &data
->size
);
372 g_value_set_double(value
, data
->factor
);
375 g_value_set_boxed(value
, data
->scales
);
377 case PROP_BACKGROUND_DRESS
:
378 g_value_set_enum(value
, data
->background_dress
);
380 case PROP_FRAME_DRESS
:
381 g_value_set_enum(value
, data
->frame_dress
);
383 case PROP_TITLE_BLOCK
:
384 g_value_set_object(value
, data
->title_block
);
386 case PROP_TOP_MARGIN
:
387 g_value_set_double(value
, data
->top_margin
);
389 case PROP_RIGHT_MARGIN
:
390 g_value_set_double(value
, data
->right_margin
);
392 case PROP_BOTTOM_MARGIN
:
393 g_value_set_double(value
, data
->bottom_margin
);
395 case PROP_LEFT_MARGIN
:
396 g_value_set_double(value
, data
->left_margin
);
399 g_value_set_boolean(value
, data
->has_frame
);
401 case PROP_TOP_PADDING
:
402 g_value_set_double(value
, data
->top_padding
);
404 case PROP_RIGHT_PADDING
:
405 g_value_set_double(value
, data
->right_padding
);
407 case PROP_BOTTOM_PADDING
:
408 g_value_set_double(value
, data
->bottom_padding
);
410 case PROP_LEFT_PADDING
:
411 g_value_set_double(value
, data
->left_padding
);
414 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
420 _adg_set_property(GObject
*object
, guint prop_id
,
421 const GValue
*value
, GParamSpec
*pspec
)
424 AdgCanvasPrivate
*data
;
425 AdgTitleBlock
*title_block
;
428 canvas
= (AdgCanvas
*) object
;
433 cpml_pair_copy(&data
->size
, g_value_get_boxed(value
));
436 factor
= g_value_get_double(value
);
437 g_return_if_fail(factor
> 0);
438 data
->factor
= factor
;
441 g_strfreev(data
->scales
);
442 data
->scales
= g_value_dup_boxed(value
);
444 case PROP_BACKGROUND_DRESS
:
445 data
->background_dress
= g_value_get_enum(value
);
447 case PROP_FRAME_DRESS
:
448 data
->frame_dress
= g_value_get_enum(value
);
450 case PROP_TITLE_BLOCK
:
451 title_block
= g_value_get_object(value
);
453 g_object_ref(title_block
);
454 adg_entity_set_parent((AdgEntity
*) title_block
,
455 (AdgEntity
*) canvas
);
457 if (data
->title_block
)
458 g_object_unref(data
->title_block
);
459 data
->title_block
= title_block
;
461 case PROP_TOP_MARGIN
:
462 _adg_update_margin(canvas
, &data
->top_margin
, &data
->size
.y
,
463 g_value_get_double(value
));
465 case PROP_RIGHT_MARGIN
:
466 _adg_update_margin(canvas
, &data
->right_margin
, &data
->size
.x
,
467 g_value_get_double(value
));
469 case PROP_BOTTOM_MARGIN
:
470 _adg_update_margin(canvas
, &data
->bottom_margin
, &data
->size
.y
,
471 g_value_get_double(value
));
473 case PROP_LEFT_MARGIN
:
474 _adg_update_margin(canvas
, &data
->left_margin
, &data
->size
.x
,
475 g_value_get_double(value
));
478 data
->has_frame
= g_value_get_boolean(value
);
480 case PROP_TOP_PADDING
:
481 data
->top_padding
= g_value_get_double(value
);
483 case PROP_RIGHT_PADDING
:
484 data
->right_padding
= g_value_get_double(value
);
486 case PROP_BOTTOM_PADDING
:
487 data
->bottom_padding
= g_value_get_double(value
);
489 case PROP_LEFT_PADDING
:
490 data
->left_padding
= g_value_get_double(value
);
493 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
502 * Creates a new empty canvas object.
504 * Returns: (transfer full): the newly created canvas.
511 return g_object_new(ADG_TYPE_CANVAS
, NULL
);
515 * adg_canvas_set_size:
516 * @canvas: an #AdgCanvas
517 * @size: (transfer none): the new size for the canvas
519 * Sets a specific size on @canvas. The x and/or y
520 * component of @size can be set to 0, in which case
521 * the exact value will be autocalculated, that is the
522 * size component returned by adg_entity_get_extents()
523 * on @canvas will be used instead.
528 adg_canvas_set_size(AdgCanvas
*canvas
, const CpmlPair
*size
)
530 g_return_if_fail(ADG_IS_CANVAS(canvas
));
531 g_return_if_fail(size
!= NULL
);
533 g_object_set(canvas
, "size", size
, NULL
);
537 * adg_canvas_set_size_explicit:
538 * @canvas: an #AdgCanvas
539 * @x: the new width of the canvas or 0 to reset
540 * @y: the new height of the canvas or 0 to reset
542 * A convenient function to set the size of @canvas using
543 * explicit coordinates. Check adg_canvas_set_size() for
549 adg_canvas_set_size_explicit(AdgCanvas
*canvas
, gdouble x
, gdouble y
)
556 adg_canvas_set_size(canvas
, &size
);
560 * adg_canvas_get_size:
561 * @canvas: an #AdgCanvas
563 * Gets the specific size set on @canvas. The x and/or y
564 * components of the returned #CpmlPair could be 0, in which
565 * case the size returned by adg_entity_get_extents() on @canvas
566 * will be used instead.
568 * Returns: (transfer none): the explicit size set on this canvas or <constant>NULL</constant> on errors.
573 adg_canvas_get_size(AdgCanvas
*canvas
)
575 AdgCanvasPrivate
*data
;
577 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), NULL
);
584 * adg_canvas_set_factor:
585 * @canvas: an #AdgCanvas
586 * @factor: the new factor: must be greater than 0
588 * The ADG library is intentionally unit agnostic, i.e. the global space is
589 * represented in whatever unit you want. There are a couple of cairo APIs
590 * that explicitely requires points though, most notably
591 * cairo_pdf_surface_set_size() and cairo_ps_surface_set_size().
593 * On PDF and postscript surfaces, the AdgCanvas:factor property will be the
594 * number typography points per global space units:
595 * https://en.wikipedia.org/wiki/Point_(typography)
597 * For other surfaces, the factor will be used to scale the final surface to
598 * the specific number of pixels.
600 * As an example, if you are working in millimeters you will set a factor of
601 * 2.83465 on PDF and postscript surfaces (1 mm = 2.83465 points) so the
602 * drawing will show the correct scale on paper. For X11 and PNG surfaces you
603 * will set it depending on the resolution you want to get, e.g. if the
604 * drawing is 100x200 mm and you want a 1000x2000 image, just set it to 10.
609 adg_canvas_set_factor(AdgCanvas
*canvas
, double factor
)
611 g_return_if_fail(ADG_IS_CANVAS(canvas
));
612 g_return_if_fail(factor
> 0);
614 g_object_set(canvas
, "factor", factor
, NULL
);
618 * adg_canvas_get_factor:
619 * @canvas: an #AdgCanvas
621 * Gets the current factor of @canvas. See adg_canvas_set_factor() to learn
622 * what a factor is in this context.
624 * Returns: the canvas factor or 0 on error.
629 adg_canvas_get_factor(AdgCanvas
*canvas
)
631 AdgCanvasPrivate
*data
;
633 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), 0.);
640 * adg_canvas_set_scales:
641 * @canvas: an #AdgCanvas
642 * @...: <constant>NULL</constant> terminated list of strings
644 * Sets the scales allowed by @canvas. Every scale identifies a
645 * specific factor to be applied to the local matrix of @canvas.
646 * When adg_canvas_autoscale() will be called, the greatest
647 * scale that can render every entity inside a box of
648 * #AdgCanvas:size dimensions will be applied. The drawing will
649 * be centered inside that box.
651 * Every scale should be expressed with a string in the form of
652 * "x:y", where x and y are positive integers that identifies
653 * numerator an denominator of a fraction. That string itself
654 * will be put into the title block when used.
659 adg_canvas_set_scales(AdgCanvas
*canvas
, ...)
663 va_start(var_args
, canvas
);
664 adg_canvas_set_scales_valist(canvas
, var_args
);
669 * adg_canvas_set_scales_valist:
670 * @canvas: an #AdgCanvas
671 * @var_args: <constant>NULL</constant> terminated list of strings
673 * Vararg variant of adg_canvas_set_scales().
678 adg_canvas_set_scales_valist(AdgCanvas
*canvas
, va_list var_args
)
684 g_return_if_fail(ADG_IS_CANVAS(canvas
));
688 while ((scale
= va_arg(var_args
, const gchar
*)) != NULL
) {
690 scales
= g_realloc(scales
, sizeof(const gchar
*) * (n
+ 1));
691 scales
[n
-1] = g_strdup(scale
);
695 g_object_set(canvas
, "scales", scales
, NULL
);
700 * adg_canvas_set_scales_array: (rename-to adg_canvas_set_scales)
701 * @canvas: an #AdgCanvas
702 * @scales: (array zero-terminated=1) (allow-none): <constant>NULL</constant> terminated array of scales
704 * Array variant of adg_canvas_set_scales().
709 adg_canvas_set_scales_array(AdgCanvas
*canvas
, gchar
**scales
)
711 g_return_if_fail(ADG_IS_CANVAS(canvas
));
712 g_object_set(canvas
, "scales", scales
, NULL
);
716 * adg_canvas_get_scales:
717 * @canvas: an #AdgCanvas
719 * Gets the list of scales set on @canvas: check adg_canvas_set_scales()
720 * to get an idea of what scales are supposed to be.
722 * If no scales are set, <constant>NULL</constant> is returned.
724 * Returns: (element-type utf8) (transfer none): the <constant>NULL</constant> terminated list of valid scales.
729 adg_canvas_get_scales(AdgCanvas
*canvas
)
731 AdgCanvasPrivate
*data
;
733 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), NULL
);
740 * adg_canvas_autoscale:
741 * @canvas: an #AdgCanvas
743 * Applies one scale per time, in the order they have been provided
744 * in the adg_canvas_set_scales() call, until the drawing can be
745 * entirely contained into the current paper. If successful, the
746 * scale of the title block is changed accordingly and the drawing
747 * is centered inside the paper.
749 * The paddings are taken into account while computing the drawing
755 adg_canvas_autoscale(AdgCanvas
*canvas
)
757 AdgCanvasPrivate
*data
;
762 AdgTitleBlock
*title_block
;
765 g_return_if_fail(ADG_IS_CANVAS(canvas
));
766 g_return_if_fail(_ADG_OLD_ENTITY_CLASS
->arrange
!= NULL
);
769 entity
= (AdgEntity
*) canvas
;
770 title_block
= data
->title_block
;
772 /* Manually calling the arrange() method instead of emitting the "arrange"
773 * signal does not invalidate the global matrix: let's do it right now */
774 adg_entity_global_changed(entity
);
776 for (p_scale
= data
->scales
; p_scale
!= NULL
&& *p_scale
!= NULL
; ++p_scale
) {
777 const gchar
*scale
= *p_scale
;
778 gdouble factor
= adg_scale_factor(scale
);
782 cairo_matrix_init_scale(&map
, factor
, factor
);
783 adg_entity_set_local_map(entity
, &map
);
784 adg_entity_local_changed(entity
);
786 /* Arrange the entities inside the canvas, but not the canvas itself,
787 * just to get the bounding box of the drawing without the paper */
788 _ADG_OLD_ENTITY_CLASS
->arrange(entity
);
789 cpml_extents_copy(&extents
, adg_entity_get_extents(entity
));
791 /* Just in case @canvas is empty */
792 if (! extents
.is_defined
)
795 _adg_apply_paddings(canvas
, &extents
);
797 if (title_block
!= NULL
)
798 adg_title_block_set_scale(title_block
, scale
);
800 /* Bail out if paper size is not specified or invalid */
801 if (data
->size
.x
<= 0 || data
->size
.y
<= 0)
804 /* If the drawing extents are fully contained inside the paper size,
805 * center the drawing in the paper and bail out */
806 delta
.x
= data
->size
.x
- extents
.size
.x
;
807 delta
.y
= data
->size
.y
- extents
.size
.y
;
808 if (delta
.x
>= 0 && delta
.y
>= 0) {
809 cairo_matrix_t transform
;
810 cairo_matrix_init_translate(&transform
,
811 delta
.x
/ 2 - extents
.org
.x
,
812 delta
.y
/ 2 - extents
.org
.y
);
813 adg_entity_transform_local_map(entity
, &transform
,
814 ADG_TRANSFORM_AFTER
);
821 * adg_canvas_set_background_dress:
822 * @canvas: an #AdgCanvas
823 * @dress: the new #AdgDress to use
825 * Sets a new background dress for rendering @canvas: the new
826 * dress must be a color dress.
831 adg_canvas_set_background_dress(AdgCanvas
*canvas
, AdgDress dress
)
833 g_return_if_fail(ADG_IS_CANVAS(canvas
));
834 g_object_set(canvas
, "background-dress", dress
, NULL
);
838 * adg_canvas_get_background_dress:
839 * @canvas: an #AdgCanvas
841 * Gets the background dress to be used in rendering @canvas.
843 * Returns: the current background dress.
848 adg_canvas_get_background_dress(AdgCanvas
*canvas
)
850 AdgCanvasPrivate
*data
;
852 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), ADG_DRESS_UNDEFINED
);
856 return data
->background_dress
;
860 * adg_canvas_set_frame_dress:
861 * @canvas: an #AdgCanvas
862 * @dress: the new #AdgDress to use
864 * Sets the #AdgCanvas:frame-dress property of @canvas to @dress:
865 * the new dress must be a line dress.
870 adg_canvas_set_frame_dress(AdgCanvas
*canvas
, AdgDress dress
)
872 g_return_if_fail(ADG_IS_CANVAS(canvas
));
873 g_object_set(canvas
, "frame-dress", dress
, NULL
);
877 * adg_canvas_get_frame_dress:
878 * @canvas: an #AdgCanvas
880 * Gets the frame dress to be used in rendering the border of @canvas.
882 * Returns: the current frame dress.
887 adg_canvas_get_frame_dress(AdgCanvas
*canvas
)
889 AdgCanvasPrivate
*data
;
891 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), ADG_DRESS_UNDEFINED
);
894 return data
->frame_dress
;
898 * adg_canvas_set_title_block:
899 * @canvas: an #AdgCanvas
900 * @title_block: (transfer full): a title block
902 * Sets the #AdgCanvas:title-block property of @canvas to @title_block.
904 * Although a title block entity could be added to @canvas in the usual
905 * way, that is using the adg_container_add() method, assigning a title
906 * block with adg_canvas_set_title_block() is somewhat different:
908 * - @title_block will be automatically attached to the bottom right
909 * corner of to the @canvas frame (this could be accomplished in the
910 * usual way too, by resetting the right and bottom paddings);
911 * - the @title_block boundary box is not taken into account while
912 * computing the extents of @canvas.
917 adg_canvas_set_title_block(AdgCanvas
*canvas
,
918 AdgTitleBlock
*title_block
)
920 g_return_if_fail(ADG_IS_CANVAS(canvas
));
921 g_return_if_fail(title_block
== NULL
|| ADG_IS_TITLE_BLOCK(title_block
));
922 g_object_set(canvas
, "title-block", title_block
, NULL
);
926 * adg_canvas_get_title_block:
927 * @canvas: an #AdgCanvas
929 * Gets the #AdgTitleBlock object of @canvas: check
930 * adg_canvas_set_title_block() for details.
932 * The returned entity is owned by @canvas and should not be
935 * Returns: (transfer none): the title block object or <constant>NULL</constant>.
940 adg_canvas_get_title_block(AdgCanvas
*canvas
)
942 AdgCanvasPrivate
*data
;
944 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), NULL
);
947 return data
->title_block
;
951 * adg_canvas_set_top_margin:
952 * @canvas: an #AdgCanvas
953 * @value: the new margin, in global space
955 * Changes the top margin of @canvas by setting #AdgCanvas:top-margin
956 * to @value. Negative values are allowed.
961 adg_canvas_set_top_margin(AdgCanvas
*canvas
, gdouble value
)
963 g_return_if_fail(ADG_IS_CANVAS(canvas
));
964 g_object_set(canvas
, "top-margin", value
, NULL
);
968 * adg_canvas_get_top_margin:
969 * @canvas: an #AdgCanvas
971 * Gets the top margin (in global space) of @canvas.
973 * Returns: the requested margin or 0 on error.
978 adg_canvas_get_top_margin(AdgCanvas
*canvas
)
980 AdgCanvasPrivate
*data
;
982 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), 0.);
985 return data
->top_margin
;
989 * adg_canvas_set_right_margin:
990 * @canvas: an #AdgCanvas
991 * @value: the new margin, in global space
993 * Changes the right margin of @canvas by setting
994 * #AdgCanvas:right-margin to @value. Negative values are allowed.
999 adg_canvas_set_right_margin(AdgCanvas
*canvas
, gdouble value
)
1001 g_return_if_fail(ADG_IS_CANVAS(canvas
));
1002 g_object_set(canvas
, "right-margin", value
, NULL
);
1006 * adg_canvas_get_right_margin:
1007 * @canvas: an #AdgCanvas
1009 * Gets the right margin (in global space) of @canvas.
1011 * Returns: the requested margin or 0 on error.
1016 adg_canvas_get_right_margin(AdgCanvas
*canvas
)
1018 AdgCanvasPrivate
*data
;
1020 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), 0.);
1022 data
= canvas
->data
;
1023 return data
->right_margin
;
1027 * adg_canvas_set_bottom_margin:
1028 * @canvas: an #AdgCanvas
1029 * @value: the new margin, in global space
1031 * Changes the bottom margin of @canvas by setting
1032 * #AdgCanvas:bottom-margin to @value. Negative values are allowed.
1037 adg_canvas_set_bottom_margin(AdgCanvas
*canvas
, gdouble value
)
1039 g_return_if_fail(ADG_IS_CANVAS(canvas
));
1040 g_object_set(canvas
, "bottom-margin", value
, NULL
);
1044 * adg_canvas_get_bottom_margin:
1045 * @canvas: an #AdgCanvas
1047 * Gets the bottom margin (in global space) of @canvas.
1049 * Returns: the requested margin or 0 on error.
1054 adg_canvas_get_bottom_margin(AdgCanvas
*canvas
)
1056 AdgCanvasPrivate
*data
;
1058 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), 0.);
1060 data
= canvas
->data
;
1061 return data
->bottom_margin
;
1065 * adg_canvas_set_left_margin:
1066 * @canvas: an #AdgCanvas
1067 * @value: the new margin, in global space
1069 * Changes the left margin of @canvas by setting
1070 * #AdgCanvas:left-margin to @value. Negative values are allowed.
1075 adg_canvas_set_left_margin(AdgCanvas
*canvas
, gdouble value
)
1077 g_return_if_fail(ADG_IS_CANVAS(canvas
));
1078 g_object_set(canvas
, "left-margin", value
, NULL
);
1082 * adg_canvas_get_left_margin:
1083 * @canvas: an #AdgCanvas
1085 * Gets the left margin (in global space) of @canvas.
1087 * Returns: the requested margin or 0 on error.
1092 adg_canvas_get_left_margin(AdgCanvas
*canvas
)
1094 AdgCanvasPrivate
*data
;
1096 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), 0.);
1098 data
= canvas
->data
;
1099 return data
->left_margin
;
1103 * adg_canvas_set_margins:
1104 * @canvas: an #AdgCanvas
1105 * @top: top margin, in global space
1106 * @right: right margin, in global space
1107 * @bottom: bottom margin, in global space
1108 * @left: left margin, in global space
1110 * Convenient function to set all the margins at once.
1115 adg_canvas_set_margins(AdgCanvas
*canvas
,
1116 gdouble top
, gdouble right
,
1117 gdouble bottom
, gdouble left
)
1119 g_return_if_fail(ADG_IS_CANVAS(canvas
));
1120 g_object_set(canvas
, "top-margin", top
, "right-margin", right
,
1121 "bottom-margin", bottom
, "left-margin", left
, NULL
);
1125 * adg_canvas_get_margins:
1126 * @canvas: an #AdgCanvas
1127 * @top: (out) (nullable): where to store the top margin or NULL
1128 * @right: (out) (nullable): where to store the right margin or NULL
1129 * @bottom: (out) (nullable): where to store the bottom margin or NULL
1130 * @left: (out) (nullable): where to store the left margin or NULL
1132 * Convenient function to get all the margins at once.
1137 adg_canvas_get_margins(AdgCanvas
*canvas
,
1138 gdouble
*top
, gdouble
*right
,
1139 gdouble
*bottom
, gdouble
*left
)
1141 AdgCanvasPrivate
*data
;
1143 g_return_if_fail(ADG_IS_CANVAS(canvas
));
1145 data
= canvas
->data
;
1148 *top
= data
->top_margin
;
1150 if (right
!= NULL
) {
1151 *right
= data
->right_margin
;
1153 if (bottom
!= NULL
) {
1154 *bottom
= data
->bottom_margin
;
1157 *left
= data
->left_margin
;
1162 * adg_canvas_apply_margins:
1163 * @canvas: an #AdgCanvas
1164 * @extents: where apply the margins
1166 * A convenient function to apply the margins of @canvas to the
1167 * arbitrary #CpmlExtents struct @extents. "Apply" means @extents
1168 * are enlarged of the specific margin values.
1173 adg_canvas_apply_margins(AdgCanvas
*canvas
, CpmlExtents
*extents
)
1175 g_return_if_fail(ADG_IS_CANVAS(canvas
));
1176 g_return_if_fail(extents
!= NULL
);
1178 if (extents
->is_defined
) {
1179 AdgCanvasPrivate
*data
= canvas
->data
;
1181 extents
->org
.x
-= data
->left_margin
;
1182 extents
->org
.y
-= data
->top_margin
;
1183 extents
->size
.x
+= data
->left_margin
+ data
->right_margin
;
1184 extents
->size
.y
+= data
->top_margin
+ data
->bottom_margin
;
1189 * adg_canvas_switch_frame:
1190 * @canvas: an #AdgCanvas
1191 * @new_state: the new flag status
1193 * Sets a new status on the #AdgCanvas:has-frame
1194 * property: <constant>TRUE</constant> means a border around
1195 * the canvas extents (less the margins) should be rendered.
1200 adg_canvas_switch_frame(AdgCanvas
*canvas
, gboolean new_state
)
1202 g_return_if_fail(ADG_IS_CANVAS(canvas
));
1203 g_object_set(canvas
, "has-frame", new_state
, NULL
);
1207 * adg_canvas_has_frame:
1208 * @canvas: an #AdgCanvas
1210 * Gets the current status of the #AdgCanvas:has-frame property,
1211 * that is whether a border around the canvas extents (less the
1212 * margins) should be rendered (<constant>TRUE</constant>) or not
1213 * (<constant>FALSE</constant>).
1215 * Returns: the current status of the frame flag.
1220 adg_canvas_has_frame(AdgCanvas
*canvas
)
1222 AdgCanvasPrivate
*data
;
1224 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), FALSE
);
1226 data
= canvas
->data
;
1227 return data
->has_frame
;
1231 * adg_canvas_set_top_padding:
1232 * @canvas: an #AdgCanvas
1233 * @value: the new padding, in global space
1235 * Changes the top padding of @canvas by setting
1236 * #AdgCanvas:top-padding to @value. Negative values are allowed.
1241 adg_canvas_set_top_padding(AdgCanvas
*canvas
, gdouble value
)
1243 g_return_if_fail(ADG_IS_CANVAS(canvas
));
1244 g_object_set(canvas
, "top-padding", value
, NULL
);
1248 * adg_canvas_get_top_padding:
1249 * @canvas: an #AdgCanvas
1251 * Gets the top padding (in global space) of @canvas.
1253 * Returns: the requested padding or 0 on error.
1258 adg_canvas_get_top_padding(AdgCanvas
*canvas
)
1260 AdgCanvasPrivate
*data
;
1262 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), 0.);
1264 data
= canvas
->data
;
1265 return data
->top_padding
;
1269 * adg_canvas_set_right_padding:
1270 * @canvas: an #AdgCanvas
1271 * @value: the new padding, in global space
1273 * Changes the right padding of @canvas by setting #AdgCanvas:right-padding
1274 * to @value. Negative values are allowed.
1279 adg_canvas_set_right_padding(AdgCanvas
*canvas
, gdouble value
)
1281 g_return_if_fail(ADG_IS_CANVAS(canvas
));
1282 g_object_set(canvas
, "right-padding", value
, NULL
);
1286 * adg_canvas_get_right_padding:
1287 * @canvas: an #AdgCanvas
1289 * Gets the right padding (in global space) of @canvas.
1291 * Returns: the requested padding or 0 on error.
1296 adg_canvas_get_right_padding(AdgCanvas
*canvas
)
1298 AdgCanvasPrivate
*data
;
1300 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), 0.);
1302 data
= canvas
->data
;
1303 return data
->right_padding
;
1308 * adg_canvas_set_bottom_padding:
1309 * @canvas: an #AdgCanvas
1310 * @value: the new padding, in global space
1312 * Changes the bottom padding of @canvas by setting
1313 * #AdgCanvas:bottom-padding to @value. Negative values are allowed.
1318 adg_canvas_set_bottom_padding(AdgCanvas
*canvas
, gdouble value
)
1320 g_return_if_fail(ADG_IS_CANVAS(canvas
));
1321 g_object_set(canvas
, "bottom-padding", value
, NULL
);
1325 * adg_canvas_get_bottom_padding:
1326 * @canvas: an #AdgCanvas
1328 * Gets the bottom padding (in global space) of @canvas.
1330 * Returns: the requested padding or 0 on error.
1335 adg_canvas_get_bottom_padding(AdgCanvas
*canvas
)
1337 AdgCanvasPrivate
*data
;
1339 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), 0.);
1341 data
= canvas
->data
;
1342 return data
->bottom_padding
;
1346 * adg_canvas_set_left_padding:
1347 * @canvas: an #AdgCanvas
1348 * @value: the new padding, in global space
1350 * Changes the left padding of @canvas by setting
1351 * #AdgCanvas:left-padding to @value. Negative values are allowed.
1356 adg_canvas_set_left_padding(AdgCanvas
*canvas
, gdouble value
)
1358 g_return_if_fail(ADG_IS_CANVAS(canvas
));
1359 g_object_set(canvas
, "left-padding", value
, NULL
);
1363 * adg_canvas_get_left_padding:
1364 * @canvas: an #AdgCanvas
1366 * Gets the left padding (in global space) of @canvas.
1368 * Returns: the requested padding or 0 on error.
1373 adg_canvas_get_left_padding(AdgCanvas
*canvas
)
1375 AdgCanvasPrivate
*data
;
1377 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), 0.);
1379 data
= canvas
->data
;
1380 return data
->left_padding
;
1384 * adg_canvas_set_paddings:
1385 * @canvas: an #AdgCanvas
1386 * @top: top padding, in global space
1387 * @right: right padding, in global space
1388 * @bottom: bottom padding, in global space
1389 * @left: left padding, in global space
1391 * Convenient function to set all the paddings at once.
1396 adg_canvas_set_paddings(AdgCanvas
*canvas
,
1397 gdouble top
, gdouble right
,
1398 gdouble bottom
, gdouble left
)
1400 g_return_if_fail(ADG_IS_CANVAS(canvas
));
1401 g_object_set(canvas
, "top-padding", top
, "right-padding", right
,
1402 "bottom-padding", bottom
, "left-padding", left
, NULL
);
1406 * adg_canvas_get_paddings:
1407 * @canvas: an #AdgCanvas
1408 * @top: (out) (nullable): where to store the top padding or NULL
1409 * @right: (out) (nullable): where to store the right padding or NULL
1410 * @bottom: (out) (nullable): where to store the bottom padding or NULL
1411 * @left: (out) (nullable): where to store the left padding or NULL
1413 * Convenient function to get all the paddings at once.
1418 adg_canvas_get_paddings(AdgCanvas
*canvas
,
1419 gdouble
*top
, gdouble
*right
,
1420 gdouble
*bottom
, gdouble
*left
)
1422 AdgCanvasPrivate
*data
;
1424 g_return_if_fail(ADG_IS_CANVAS(canvas
));
1426 data
= canvas
->data
;
1429 *top
= data
->top_padding
;
1431 if (right
!= NULL
) {
1432 *right
= data
->right_padding
;
1434 if (bottom
!= NULL
) {
1435 *bottom
= data
->bottom_padding
;
1438 *left
= data
->left_padding
;
1444 _adg_global_changed(AdgEntity
*entity
)
1446 AdgCanvasPrivate
*data
= ((AdgCanvas
*) entity
)->data
;
1448 if (_ADG_OLD_ENTITY_CLASS
->global_changed
)
1449 _ADG_OLD_ENTITY_CLASS
->global_changed(entity
);
1451 if (data
->title_block
)
1452 adg_entity_global_changed((AdgEntity
*) data
->title_block
);
1456 _adg_local_changed(AdgEntity
*entity
)
1458 AdgCanvasPrivate
*data
= ((AdgCanvas
*) entity
)->data
;
1459 AdgTitleBlock
*title_block
= data
->title_block
;
1461 if (_ADG_OLD_ENTITY_CLASS
->local_changed
)
1462 _ADG_OLD_ENTITY_CLASS
->local_changed(entity
);
1464 if (title_block
!= NULL
) {
1465 const gchar
*scale
= adg_title_block_get_scale(title_block
);
1467 if (scale
!= NULL
&& scale
[0] != '\0') {
1468 const cairo_matrix_t
*map
= adg_entity_get_local_map(entity
);
1469 gdouble factor
= adg_scale_factor(scale
);
1471 if (map
->xx
!= factor
|| map
->yy
!= factor
)
1472 adg_title_block_set_scale(title_block
, "---");
1475 adg_entity_local_changed((AdgEntity
*) title_block
);
1480 _adg_invalidate(AdgEntity
*entity
)
1482 AdgCanvasPrivate
*data
= ((AdgCanvas
*) entity
)->data
;
1484 if (_ADG_OLD_ENTITY_CLASS
->invalidate
)
1485 _ADG_OLD_ENTITY_CLASS
->invalidate(entity
);
1487 if (data
->title_block
)
1488 adg_entity_invalidate((AdgEntity
*) data
->title_block
);
1492 _adg_arrange(AdgEntity
*entity
)
1495 AdgCanvasPrivate
*data
;
1496 CpmlExtents extents
;
1498 if (_ADG_OLD_ENTITY_CLASS
->arrange
)
1499 _ADG_OLD_ENTITY_CLASS
->arrange(entity
);
1501 cpml_extents_copy(&extents
, adg_entity_get_extents(entity
));
1503 /* The extents should be defined, otherwise there is no drawing */
1504 g_return_if_fail(extents
.is_defined
);
1506 canvas
= (AdgCanvas
*) entity
;
1507 data
= canvas
->data
;
1509 _adg_apply_paddings(canvas
, &extents
);
1511 if (data
->size
.x
> 0 || data
->size
.y
> 0) {
1512 const cairo_matrix_t
*global
= adg_entity_get_global_matrix(entity
);
1517 paper
.size
.x
= data
->size
.x
;
1518 paper
.size
.y
= data
->size
.y
;
1520 cairo_matrix_transform_point(global
, &paper
.org
.x
, &paper
.org
.y
);
1521 cairo_matrix_transform_distance(global
, &paper
.size
.x
, &paper
.size
.y
);
1523 if (data
->size
.x
> 0) {
1524 extents
.org
.x
= paper
.org
.x
;
1525 extents
.size
.x
= paper
.size
.x
;
1527 if (data
->size
.y
> 0) {
1528 extents
.org
.y
= paper
.org
.y
;
1529 extents
.size
.y
= paper
.size
.y
;
1533 if (data
->title_block
) {
1534 AdgEntity
*title_block_entity
;
1535 const CpmlExtents
*title_block_extents
;
1538 title_block_entity
= (AdgEntity
*) data
->title_block
;
1539 adg_entity_arrange(title_block_entity
);
1540 title_block_extents
= adg_entity_get_extents(title_block_entity
);
1542 shift
.x
= extents
.org
.x
+ extents
.size
.x
- title_block_extents
->org
.x
1543 - title_block_extents
->size
.x
;
1544 shift
.y
= extents
.org
.y
+ extents
.size
.y
- title_block_extents
->org
.y
1545 - title_block_extents
->size
.y
;
1547 /* The following block could be optimized by skipping tiny shift,
1548 * usually left by rounding errors */
1549 if (shift
.x
!= 0 || shift
.y
!= 0) {
1550 cairo_matrix_t unglobal
, map
;
1551 adg_matrix_copy(&unglobal
, adg_entity_get_global_matrix(entity
));
1552 cairo_matrix_invert(&unglobal
);
1554 cairo_matrix_transform_distance(&unglobal
, &shift
.x
, &shift
.y
);
1555 cairo_matrix_init_translate(&map
, shift
.x
, shift
.y
);
1556 adg_entity_transform_global_map(title_block_entity
, &map
,
1557 ADG_TRANSFORM_AFTER
);
1559 adg_entity_global_changed(title_block_entity
);
1560 adg_entity_arrange(title_block_entity
);
1561 title_block_extents
= adg_entity_get_extents(title_block_entity
);
1562 cpml_extents_add(&extents
, title_block_extents
);
1566 /* Impose the new extents */
1567 adg_entity_set_extents(entity
, &extents
);
1571 _adg_render(AdgEntity
*entity
, cairo_t
*cr
)
1573 AdgCanvasPrivate
*data
;
1574 const CpmlExtents
*extents
;
1576 data
= ((AdgCanvas
*) entity
)->data
;
1577 extents
= adg_entity_get_extents(entity
);
1581 /* Background fill */
1582 cairo_rectangle(cr
, extents
->org
.x
- data
->left_margin
,
1583 extents
->org
.y
- data
->top_margin
,
1584 extents
->size
.x
+ data
->left_margin
+ data
->right_margin
,
1585 extents
->size
.y
+ data
->top_margin
+ data
->bottom_margin
);
1586 adg_entity_apply_dress(entity
, data
->background_dress
, cr
);
1590 if (data
->has_frame
) {
1591 cairo_rectangle(cr
, extents
->org
.x
, extents
->org
.y
,
1592 extents
->size
.x
, extents
->size
.y
);
1593 cairo_transform(cr
, adg_entity_get_global_matrix(entity
));
1594 adg_entity_apply_dress(entity
, data
->frame_dress
, cr
);
1600 if (data
->title_block
)
1601 adg_entity_render((AdgEntity
*) data
->title_block
, cr
);
1603 if (_ADG_OLD_ENTITY_CLASS
->render
)
1604 _ADG_OLD_ENTITY_CLASS
->render(entity
, cr
);
1608 _adg_apply_paddings(AdgCanvas
*canvas
, CpmlExtents
*extents
)
1610 AdgCanvasPrivate
*data
= canvas
->data
;
1612 extents
->org
.x
-= data
->left_padding
;
1613 extents
->size
.x
+= data
->left_padding
+ data
->right_padding
;
1614 extents
->org
.y
-= data
->top_padding
;
1615 extents
->size
.y
+= data
->top_padding
+ data
->bottom_padding
;
1620 * adg_canvas_export:
1621 * @canvas: an #AdgCanvas
1622 * @type: (type gint): the export format
1623 * @file: the name of the resulting file
1624 * @gerror: (allow-none): return location for errors
1626 * A helper function that provides a bare export functionality.
1627 * It basically exports the drawing in @canvas in the @file
1628 * in the @type format. Any error will be reported in @gerror,
1629 * if not <constant>NULL</constant>.
1631 * Returns: <constant>TRUE</constant> on success, <constant>FALSE</constant> otherwise.
1636 adg_canvas_export(AdgCanvas
*canvas
, cairo_surface_type_t type
,
1637 const gchar
*file
, GError
**gerror
)
1640 const CpmlExtents
*extents
;
1641 gdouble top
, bottom
, left
, right
, width
, height
, factor
;
1642 cairo_surface_t
*surface
;
1644 cairo_status_t status
;
1646 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), FALSE
);
1647 g_return_val_if_fail(file
!= NULL
, FALSE
);
1648 g_return_val_if_fail(gerror
== NULL
|| *gerror
== NULL
, FALSE
);
1650 entity
= (AdgEntity
*) canvas
;
1652 adg_entity_arrange(entity
);
1653 extents
= adg_entity_get_extents(entity
);
1655 factor
= adg_canvas_get_factor(canvas
);
1656 top
= factor
* adg_canvas_get_top_margin(canvas
);
1657 bottom
= factor
* adg_canvas_get_bottom_margin(canvas
);
1658 left
= factor
* adg_canvas_get_left_margin(canvas
);
1659 right
= factor
* adg_canvas_get_right_margin(canvas
);
1660 width
= factor
* extents
->size
.x
+ left
+ right
;
1661 height
= factor
* extents
->size
.y
+ top
+ bottom
;
1664 #ifdef CAIRO_HAS_PNG_FUNCTIONS
1665 case CAIRO_SURFACE_TYPE_IMAGE
:
1666 surface
= cairo_image_surface_create(CAIRO_FORMAT_RGB24
, width
, height
);
1669 #ifdef CAIRO_HAS_PDF_SURFACE
1670 case CAIRO_SURFACE_TYPE_PDF
:
1671 surface
= cairo_pdf_surface_create(file
, width
, height
);
1674 #ifdef CAIRO_HAS_PS_SURFACE
1675 case CAIRO_SURFACE_TYPE_PS
:
1676 surface
= cairo_ps_surface_create(file
, width
, height
);
1679 #ifdef CAIRO_HAS_SVG_SURFACE
1680 case CAIRO_SURFACE_TYPE_SVG
:
1681 surface
= cairo_svg_surface_create(file
, width
, height
);
1689 if (surface
== NULL
) {
1690 g_set_error(gerror
, ADG_CANVAS_ERROR
, ADG_CANVAS_ERROR_SURFACE
,
1691 "unable to handle surface type '%d'",
1696 cairo_surface_set_device_offset(surface
, left
, top
);
1697 cairo_surface_set_device_scale(surface
, factor
, factor
);
1698 cr
= cairo_create(surface
);
1699 cairo_surface_destroy(surface
);
1701 adg_entity_render(ADG_ENTITY(canvas
), cr
);
1703 if (cairo_surface_get_type(surface
) == CAIRO_SURFACE_TYPE_IMAGE
) {
1704 status
= cairo_surface_write_to_png(surface
, file
);
1706 cairo_show_page(cr
);
1707 status
= cairo_status(cr
);
1712 if (status
!= CAIRO_STATUS_SUCCESS
) {
1713 g_set_error(gerror
, ADG_CANVAS_ERROR
, ADG_CANVAS_ERROR_CAIRO
,
1714 "cairo reported '%s'",
1715 cairo_status_to_string(status
));
1723 #if GTK3_ENABLED || GTK2_ENABLED
1724 #include <gtk/gtk.h>
1727 _adg_update_margin(AdgCanvas
*canvas
, gdouble
*margin
,
1728 gdouble
*side
, gdouble new_margin
)
1730 GtkPageSetup
*page_setup
;
1733 old_margin
= *margin
;
1734 *margin
= new_margin
;
1736 page_setup
= adg_canvas_get_page_setup(canvas
);
1737 if (page_setup
== NULL
)
1740 *side
+= old_margin
- new_margin
;
1744 * adg_canvas_set_paper:
1745 * @canvas: an #AdgCanvas
1746 * @paper_name: a paper name
1747 * @orientation: the page orientation
1749 * A convenient function to set size and margins of @canvas
1750 * using a @paper_name and an @orientation value. This should
1751 * be a PWG 5101.1-2002 paper name and it will be passed as
1752 * is to gtk_paper_size_new(), so use any valid name accepted
1755 * This has the same effect as creating a #GtkPaperSetup object
1756 * and binding it to @canvas with adg_canvas_set_page_setup():
1757 * check its documentation for knowing the implications.
1759 * To reset the size to its default behavior (i.e. autocalculate
1760 * it from the entities) just call adg_canvas_set_size_explicit()
1761 * passing <constant>0, 0</constant>.
1763 * If you want to use your own margins on a named paper size,
1764 * set them <emphasis>after</emphasis> the call to this function.
1769 adg_canvas_set_paper(AdgCanvas
*canvas
,
1770 const gchar
*paper_name
,
1771 GtkPageOrientation orientation
)
1773 GtkPageSetup
*page_setup
;
1774 GtkPaperSize
*paper_size
;
1776 g_return_if_fail(ADG_IS_CANVAS(canvas
));
1777 g_return_if_fail(paper_name
!= NULL
);
1779 page_setup
= gtk_page_setup_new();
1780 paper_size
= gtk_paper_size_new(paper_name
);
1782 gtk_page_setup_set_paper_size(page_setup
, paper_size
);
1783 gtk_page_setup_set_orientation(page_setup
, orientation
);
1784 gtk_paper_size_free(paper_size
);
1786 adg_canvas_set_page_setup(canvas
, page_setup
);
1787 g_object_unref(page_setup
);
1791 * adg_canvas_set_page_setup:
1792 * @canvas: an #AdgCanvas
1793 * @page_setup: (allow-none) (transfer none): the page setup
1795 * A convenient function to setup the page of @canvas so it can
1796 * also be subsequentially used for printing. It is allowed to
1797 * pass <constant>NULL</constant> as @page_setup to restore the
1798 * default page setup.
1800 * When a canvas is bound to a page setup, the paper size is kept
1801 * constant. This implies increasing or decreasing the margins
1802 * respectively decreases or increases the page size of the
1803 * relevant dimension, e.g. increasing the right margin decreases
1804 * the width (the x component of the size).
1806 * A reference to @page_setup is added, so there is no need to keep
1807 * alive this object after a call to this function. @page_setup can
1808 * be retrieved at any time with adg_canvas_get_page_setup().
1810 * The size and margins provided by @page_setup are immediately
1811 * used to set size and margins of @canvas. This means using
1812 * <constant>NULL</constant> as @page_setup just releases the
1813 * reference to the previous @page_setup object... all the page
1814 * settings are still retained.
1816 * If you want to use your own margins on a page setup,
1817 * set them on canvas <emphasis>after</emphasis> the call to this
1818 * function or set them on @page_setup <emphasis>before</emphasis>.
1820 * <informalexample><programlisting language="C">
1821 * // By default, canvas does not have an explicit size
1822 * adg_canvas_set_page_setup(canvas, a4);
1823 * g_object_unref(a4);
1824 * // Now canvas has size and margins specified by a4
1825 * // (and a4 should be a prefilled GtkPageSetup object).
1826 * adg_canvas_set_page_setup(canvas, NULL);
1827 * // Now canvas is no more bound to a4 and that object (if
1828 * // not referenced anywhere else) can be garbage-collected.
1829 * // The page setup of canvas has not changed though.
1830 * adg_canvas_set_size_explicit(canvas, 0, 0);
1831 * // Now the page size of canvas has been restored to
1832 * // their default behavior.
1833 * </programlisting></informalexample>
1838 adg_canvas_set_page_setup(AdgCanvas
*canvas
, GtkPageSetup
*page_setup
)
1840 gdouble top
, right
, bottom
, left
;
1843 g_return_if_fail(ADG_IS_CANVAS(canvas
));
1845 if (page_setup
== NULL
) {
1846 /* By convention, NULL resets the default page but
1847 * does not change any other property */
1848 g_object_set_data((GObject
*) canvas
, "_adg_page_setup", NULL
);
1852 g_return_if_fail(GTK_IS_PAGE_SETUP(page_setup
));
1854 top
= gtk_page_setup_get_top_margin(page_setup
, GTK_UNIT_POINTS
);
1855 right
= gtk_page_setup_get_right_margin(page_setup
, GTK_UNIT_POINTS
);
1856 bottom
= gtk_page_setup_get_bottom_margin(page_setup
, GTK_UNIT_POINTS
);
1857 left
= gtk_page_setup_get_left_margin(page_setup
, GTK_UNIT_POINTS
);
1858 size
.x
= gtk_page_setup_get_page_width(page_setup
, GTK_UNIT_POINTS
);
1859 size
.y
= gtk_page_setup_get_page_height(page_setup
, GTK_UNIT_POINTS
);
1861 adg_canvas_set_size(canvas
, &size
);
1862 adg_canvas_set_margins(canvas
, top
, right
, bottom
, left
);
1864 g_object_ref(page_setup
);
1865 g_object_set_data_full((GObject
*) canvas
, "_adg_page_setup",
1866 page_setup
, g_object_unref
);
1870 * adg_canvas_get_page_setup:
1871 * @canvas: an #AdgCanvas
1873 * If adg_canvas_set_page_setup() is called, a #GtkPageSetup object
1874 * is created and bound to @canvas. This metho returns a pointer
1875 * to that internal object or <constant>NULL</constant> if
1876 * adg_canvas_set_page_setup() has not been called before.
1878 * Returns: (allow-none) (transfer none): the #GtkPageSetup with size and margins of @canvas of <constant>NULL</constant> on no setup found or errors.
1883 adg_canvas_get_page_setup(AdgCanvas
*canvas
)
1885 g_return_val_if_fail(ADG_IS_CANVAS(canvas
), NULL
);
1886 return g_object_get_data((GObject
*) canvas
, "_adg_page_setup");
1892 _adg_update_margin(AdgCanvas
*canvas
, gdouble
*margin
,
1893 gdouble
*side
, gdouble new_margin
)
1895 *margin
= new_margin
;