Added error checking while building glyphs
[adg.git] / adg / adg-dim.c
blob795e8caa570115492b53056d2824fe708d6929c5
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2008, 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 Library 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 * Library 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., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
20 /**
21 * SECTION:dim
22 * @title: AdgDim
23 * @short_description: Root abstract class for all dimension entities
25 * The #AdgDim class is the base stub of all the dimension entities.
28 #include "adg-dim.h"
29 #include "adg-dim-private.h"
30 #include "adg-util.h"
31 #include "adg-intl.h"
32 #include <string.h>
34 #define PARENT_CLASS ((AdgEntityClass *) adg_dim_parent_class)
37 enum {
38 PROP_0,
39 PROP_REF1,
40 PROP_REF2,
41 PROP_POS1,
42 PROP_POS2,
43 PROP_LEVEL,
44 PROP_QUOTE,
45 PROP_TOLERANCE_UP,
46 PROP_TOLERANCE_DOWN,
47 PROP_NOTE
51 static void finalize (GObject *object);
52 static void get_property (GObject *object,
53 guint param_id,
54 GValue *value,
55 GParamSpec *pspec);
56 static void set_property (GObject *object,
57 guint param_id,
58 const GValue *value,
59 GParamSpec *pspec);
60 static void invalidate (AdgEntity *entity);
61 static gchar * default_quote (AdgDim *dim);
62 static void quote_layout (AdgDim *dim,
63 cairo_t *cr);
64 static gboolean text_cache_update (AdgTextCache *text_cache,
65 const gchar *text,
66 cairo_t *cr,
67 AdgStyle *style);
68 static void text_cache_invalidate (AdgTextCache *text_cache);
69 static void text_cache_move_to (AdgTextCache *text_cache,
70 const CpmlPair *to);
71 static void text_cache_render (AdgTextCache *text_cache,
72 cairo_t *cr);
75 G_DEFINE_ABSTRACT_TYPE(AdgDim, adg_dim, ADG_TYPE_ENTITY);
78 static void
79 adg_dim_class_init(AdgDimClass *klass)
81 GObjectClass *gobject_class;
82 AdgEntityClass *entity_class;
83 GParamSpec *param;
85 gobject_class = (GObjectClass *) klass;
86 entity_class = (AdgEntityClass *) klass;
88 g_type_class_add_private(klass, sizeof(AdgDimPrivate));
90 gobject_class->finalize = finalize;
91 gobject_class->get_property = get_property;
92 gobject_class->set_property = set_property;
94 entity_class->invalidate = invalidate;
96 klass->default_quote = default_quote;
97 klass->quote_layout = quote_layout;
99 param = g_param_spec_boxed("ref1",
100 P_("Reference 1"),
101 P_("First reference point of the dimension"),
102 ADG_TYPE_PAIR,
103 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
104 g_object_class_install_property(gobject_class, PROP_REF1, param);
106 param = g_param_spec_boxed("ref2",
107 P_("Reference 2"),
108 P_("Second reference point of the dimension"),
109 ADG_TYPE_PAIR,
110 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
111 g_object_class_install_property(gobject_class, PROP_REF2, param);
113 param = g_param_spec_boxed("pos1",
114 P_("Position 1"),
115 P_("First position point: it will be computed with the level property to get the real dimension position"),
116 ADG_TYPE_PAIR, G_PARAM_READWRITE);
117 g_object_class_install_property(gobject_class, PROP_POS1, param);
119 param = g_param_spec_boxed("pos2",
120 P_("Position 2"),
121 P_("Second position point: it will be computed with the level property to get the real dimension position"),
122 ADG_TYPE_PAIR, G_PARAM_READWRITE);
123 g_object_class_install_property(gobject_class, PROP_POS2, param);
125 param = g_param_spec_double("level",
126 P_("Level"),
127 P_("The dimension level, that is the factor to multiply dim_style->baseline_spacing to get the offset (in device units) from pos1..pos2 where render the dimension baseline"),
128 -G_MAXDOUBLE, G_MAXDOUBLE, 1.0,
129 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
130 g_object_class_install_property(gobject_class, PROP_LEVEL, param);
132 param = g_param_spec_string("quote",
133 P_("Quote"),
134 P_("The quote to display: set to NULL to get the default quote"),
135 NULL, G_PARAM_READWRITE);
136 g_object_class_install_property(gobject_class, PROP_QUOTE, param);
138 param = g_param_spec_string("tolerance-up",
139 P_("Up Tolerance"),
140 P_("The up tolerance of the quote: set to NULL to suppress"),
141 NULL, G_PARAM_READWRITE);
142 g_object_class_install_property(gobject_class, PROP_TOLERANCE_UP, param);
144 param = g_param_spec_string("tolerance-down",
145 P_("Down Tolerance"),
146 P_("The down tolerance of the quote: set to NULL to suppress"),
147 NULL, G_PARAM_READWRITE);
148 g_object_class_install_property(gobject_class, PROP_TOLERANCE_DOWN, param);
150 param = g_param_spec_string("note",
151 P_("Note"),
152 P_("A custom note appended to the dimension quote"),
153 NULL, G_PARAM_READWRITE);
154 g_object_class_install_property(gobject_class, PROP_NOTE, param);
157 static void
158 adg_dim_init(AdgDim *dim)
160 AdgDimPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE(dim, ADG_TYPE_DIM,
161 AdgDimPrivate);
163 priv->ref1.x = priv->ref1.y = 0.;
164 priv->ref2.x = priv->ref2.y = 0.;
165 priv->pos1.x = priv->pos1.y = 0.;
166 priv->pos2.x = priv->pos2.y = 0.;
167 priv->level = 1.;
168 priv->quote = NULL;
169 priv->tolerance_up = NULL;
170 priv->tolerance_down = NULL;
171 priv->note = NULL;
173 priv->quote_org.x = priv->quote_org.y = 0.;
174 priv->quote_angle = 0.;
175 priv->quote_cache.num_glyphs = 0;
176 priv->quote_cache.glyphs = NULL;
177 priv->tolerance_up_cache.num_glyphs = 0;
178 priv->tolerance_up_cache.glyphs = NULL;
179 priv->tolerance_down_cache.num_glyphs = 0;
180 priv->tolerance_down_cache.glyphs = NULL;
181 priv->note_cache.num_glyphs = 0;
182 priv->note_cache.glyphs = NULL;
184 dim->priv = priv;
187 static void
188 finalize(GObject *object)
190 AdgDim *dim = (AdgDim *) object;
192 g_free(dim->priv->quote);
193 g_free(dim->priv->tolerance_up);
194 g_free(dim->priv->tolerance_down);
196 invalidate((AdgEntity *) dim);
198 ((GObjectClass *) PARENT_CLASS)->finalize(object);
201 static void
202 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
204 AdgDim *dim = (AdgDim *) object;
206 switch (prop_id) {
207 case PROP_REF1:
208 g_value_set_boxed(value, &dim->priv->ref1);
209 break;
210 case PROP_REF2:
211 g_value_set_boxed(value, &dim->priv->ref2);
212 break;
213 case PROP_POS1:
214 g_value_set_boxed(value, &dim->priv->pos1);
215 break;
216 case PROP_POS2:
217 g_value_set_boxed(value, &dim->priv->pos1);
218 break;
219 case PROP_LEVEL:
220 g_value_set_double(value, dim->priv->level);
221 break;
222 case PROP_QUOTE:
223 g_value_set_string(value, dim->priv->quote);
224 break;
225 case PROP_TOLERANCE_UP:
226 g_value_set_string(value, dim->priv->tolerance_up);
227 break;
228 case PROP_TOLERANCE_DOWN:
229 g_value_set_string(value, dim->priv->tolerance_down);
230 break;
231 case PROP_NOTE:
232 g_value_set_string(value, dim->priv->note);
233 break;
234 default:
235 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
236 break;
240 static void
241 set_property(GObject *object, guint prop_id,
242 const GValue *value, GParamSpec *pspec)
244 AdgEntity *entity;
245 AdgDim *dim;
247 entity = (AdgEntity *) object;
248 dim = (AdgDim *) object;
250 switch (prop_id) {
251 case PROP_REF1:
252 cpml_pair_copy(&dim->priv->ref1, (AdgPair *) g_value_get_boxed(value));
253 invalidate(entity);
254 break;
255 case PROP_REF2:
256 cpml_pair_copy(&dim->priv->ref2, (AdgPair *) g_value_get_boxed(value));
257 invalidate(entity);
258 break;
259 case PROP_POS1:
260 cpml_pair_copy(&dim->priv->pos1, (AdgPair *) g_value_get_boxed(value));
261 invalidate(entity);
262 break;
263 case PROP_POS2:
264 cpml_pair_copy(&dim->priv->pos2, (AdgPair *) g_value_get_boxed(value));
265 invalidate(entity);
266 break;
267 case PROP_LEVEL:
268 dim->priv->level = g_value_get_double(value);
269 invalidate(entity);
270 break;
271 case PROP_QUOTE:
272 g_free(dim->priv->quote);
273 dim->priv->quote = g_value_dup_string(value);
274 text_cache_invalidate(&dim->priv->quote_cache);
275 break;
276 case PROP_TOLERANCE_UP:
277 g_free(dim->priv->tolerance_up);
278 dim->priv->tolerance_up = g_value_dup_string(value);
279 text_cache_invalidate(&dim->priv->tolerance_up_cache);
280 break;
281 case PROP_TOLERANCE_DOWN:
282 g_free(dim->priv->tolerance_down);
283 dim->priv->tolerance_down = g_value_dup_string(value);
284 text_cache_invalidate(&dim->priv->tolerance_down_cache);
285 break;
286 case PROP_NOTE:
287 g_free(dim->priv->note);
288 dim->priv->note = g_value_dup_string(value);
289 text_cache_invalidate(&dim->priv->note_cache);
290 break;
291 default:
292 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
293 break;
298 static void
299 invalidate(AdgEntity *entity)
301 AdgDim *dim = (AdgDim *) entity;
303 dim->priv->quote_angle = 0.;
304 dim->priv->quote_org.x = 0.;
305 dim->priv->quote_org.y = 0.;
307 text_cache_invalidate(&dim->priv->quote_cache);
308 text_cache_invalidate(&dim->priv->tolerance_up_cache);
309 text_cache_invalidate(&dim->priv->tolerance_down_cache);
310 text_cache_invalidate(&dim->priv->note_cache);
314 static gchar *
315 default_quote(AdgDim *dim)
317 g_warning("AdgDim::default_quote not implemented for `%s'",
318 g_type_name(G_TYPE_FROM_INSTANCE(dim)));
319 return g_strdup("undef");
322 static void
323 quote_layout(AdgDim *dim, cairo_t *cr)
325 AdgDimPrivate *priv;
326 AdgDimStyle *dim_style;
327 AdgPair shift;
328 CpmlPair cp, org;
329 CpmlPair quote_org, tolerance_up_org, tolerance_down_org, note_org;
330 cairo_text_extents_t extents;
332 priv = dim->priv;
333 dim_style = (AdgDimStyle *) adg_entity_get_style((AdgEntity *) dim,
334 ADG_SLOT_DIM_STYLE);
336 /* Compute the quote */
337 quote_org.x = 0.;
338 quote_org.y = 0.;
339 if (text_cache_update(&priv->quote_cache, priv->quote, cr,
340 adg_dim_style_get_quote_style(dim_style))) {
341 cp.x = priv->quote_cache.extents.width;
342 cp.y = priv->quote_cache.extents.height / -2.;
343 } else {
344 cp.x = 0.;
345 cp.y = 0.;
348 /* Compute the tolerances */
349 if (priv->tolerance_up != NULL || priv->tolerance_down != NULL) {
350 double width;
351 double midspacing;
353 adg_style_apply(adg_dim_style_get_tolerance_style(dim_style), cr);
355 width = 0.;
356 midspacing = adg_dim_style_get_tolerance_spacing(dim_style) / 2.;
357 cpml_pair_copy(&shift, adg_dim_style_get_tolerance_shift(dim_style));
358 cp.x += shift.x;
360 if (text_cache_update(&priv->tolerance_up_cache,
361 priv->tolerance_up, cr, NULL)) {
362 tolerance_up_org.x = cp.x;
363 tolerance_up_org.y = cp.y + shift.y - midspacing;
365 width = priv->tolerance_up_cache.extents.width;
368 if (text_cache_update(&priv->tolerance_down_cache,
369 priv->tolerance_down, cr, NULL)) {
370 tolerance_down_org.x = cp.x;
371 tolerance_down_org.y = cp.y + shift.y + midspacing +
372 priv->tolerance_down_cache.extents.height;
374 if (extents.width > width)
375 width = priv->tolerance_down_cache.extents.width;
378 cp.x += width;
381 /* Compute the note */
382 if (text_cache_update(&priv->note_cache, priv->note, cr,
383 adg_dim_style_get_note_style(dim_style))) {
384 cpml_pair_copy(&shift, adg_dim_style_get_note_shift(dim_style));
385 cp.x += shift.x;
387 note_org.x = cp.x;
388 note_org.y = cp.y + shift.y + priv->note_cache.extents.height / 2.;
390 cp.x += priv->note_cache.extents.width;
393 /* Centering and shifting the whole group */
394 cpml_pair_copy(&shift, adg_dim_style_get_quote_shift(dim_style));
395 shift.x -= cp.x / 2.;
397 if (priv->quote_cache.glyphs) {
398 quote_org.x += shift.x;
399 quote_org.y += shift.y;
400 text_cache_move_to(&priv->quote_cache, &quote_org);
403 if (priv->tolerance_up_cache.glyphs) {
404 tolerance_up_org.x += shift.x;
405 tolerance_up_org.y += shift.y;
406 text_cache_move_to(&priv->tolerance_up_cache, &tolerance_up_org);
409 if (priv->tolerance_down_cache.glyphs) {
410 tolerance_down_org.x += shift.x;
411 tolerance_down_org.y += shift.y;
412 text_cache_move_to(&priv->tolerance_down_cache, &tolerance_down_org);
415 if (priv->note_cache.glyphs) {
416 note_org.x += shift.x;
417 note_org.y += shift.y;
418 text_cache_move_to(&priv->note_cache, &note_org);
423 const AdgPair *
424 adg_dim_get_ref1(AdgDim *dim)
426 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
428 return &dim->priv->ref1;
431 const AdgPair *
432 adg_dim_get_ref2(AdgDim *dim)
434 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
436 return &dim->priv->ref2;
439 void
440 adg_dim_set_ref(AdgDim *dim, const AdgPair *ref1, const AdgPair *ref2)
442 g_return_if_fail(ADG_IS_DIM(dim));
444 if (ref1 != NULL || ref2 != NULL) {
445 GObject *object = (GObject *) dim;
447 g_object_freeze_notify(object);
449 if (ref1 != NULL) {
450 dim->priv->ref1 = *ref1;
451 g_object_notify(object, "ref1");
454 if (ref2 != NULL) {
455 dim->priv->ref2 = *ref2;
456 g_object_notify(object, "ref2");
459 g_object_thaw_notify(object);
460 invalidate((AdgEntity *) dim);
464 void
465 adg_dim_set_ref_explicit(AdgDim *dim, double ref1_x, double ref1_y,
466 double ref2_x, double ref2_y)
468 AdgPair ref1;
469 AdgPair ref2;
471 ref1.x = ref1_x;
472 ref1.y = ref1_y;
473 ref2.x = ref2_x;
474 ref2.y = ref2_y;
476 adg_dim_set_ref(dim, &ref1, &ref2);
479 const AdgPair *
480 adg_dim_get_pos1(AdgDim *dim)
482 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
484 return &dim->priv->pos1;
487 const AdgPair *
488 adg_dim_get_pos2(AdgDim *dim)
490 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
492 return &dim->priv->pos2;
495 void
496 adg_dim_set_pos(AdgDim *dim, AdgPair *pos1, AdgPair *pos2)
498 g_return_if_fail(ADG_IS_DIM(dim));
500 if (pos1 != NULL || pos2 != NULL) {
501 GObject *object = (GObject *) dim;
503 g_object_freeze_notify(object);
505 if (pos1 != NULL) {
506 dim->priv->pos1 = *pos1;
507 g_object_notify(object, "pos1");
509 if (pos2 != NULL) {
510 dim->priv->pos2 = *pos2;
511 g_object_notify(object, "pos2");
514 g_object_thaw_notify(object);
515 invalidate((AdgEntity *) dim);
519 void
520 adg_dim_set_pos_explicit(AdgDim *dim, double pos1_x, double pos1_y,
521 double pos2_x, double pos2_y)
523 AdgPair pos1;
524 AdgPair pos2;
526 pos1.x = pos1_x;
527 pos1.y = pos1_y;
528 pos2.x = pos2_x;
529 pos2.y = pos2_y;
531 adg_dim_set_pos(dim, &pos1, &pos2);
534 double
535 adg_dim_get_level(AdgDim *dim)
537 g_return_val_if_fail(ADG_IS_DIM(dim), 0.0);
539 return dim->priv->level;
542 void
543 adg_dim_set_level(AdgDim *dim, double level)
545 g_return_if_fail(ADG_IS_DIM(dim));
547 dim->priv->level = level;
548 g_object_notify((GObject *) dim, "level");
549 invalidate((AdgEntity *) dim);
552 const gchar *
553 adg_dim_get_quote(AdgDim *dim)
555 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
557 return dim->priv->quote;
560 void
561 adg_dim_set_quote(AdgDim *dim, const gchar *quote)
563 g_return_if_fail(ADG_IS_DIM(dim));
565 g_free(dim->priv->quote);
566 dim->priv->quote = g_strdup(quote);
567 g_object_notify((GObject *) dim, "quote");
569 text_cache_invalidate(&dim->priv->quote_cache);
572 const gchar *
573 adg_dim_get_tolerance_up(AdgDim *dim)
575 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
577 return dim->priv->tolerance_up;
580 void
581 adg_dim_set_tolerance_up(AdgDim *dim, const gchar *tolerance_up)
583 g_return_if_fail(ADG_IS_DIM(dim));
585 g_free(dim->priv->tolerance_up);
586 dim->priv->tolerance_up = g_strdup(tolerance_up);
587 g_object_notify((GObject *) dim, "tolerance-up");
589 text_cache_invalidate(&dim->priv->tolerance_up_cache);
592 const gchar *
593 adg_dim_get_tolerance_down(AdgDim *dim)
595 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
597 return dim->priv->tolerance_down;
600 void
601 adg_dim_set_tolerance_down(AdgDim *dim, const gchar *tolerance_down)
603 g_return_if_fail(ADG_IS_DIM(dim));
605 g_free(dim->priv->tolerance_down);
606 dim->priv->tolerance_down = g_strdup(tolerance_down);
607 g_object_notify((GObject *) dim, "tolerance-down");
609 text_cache_invalidate(&dim->priv->tolerance_down_cache);
612 void
613 adg_dim_set_tolerances(AdgDim *dim,
614 const gchar *tolerance_up, const gchar *tolerance_down)
616 g_return_if_fail(ADG_IS_DIM(dim));
618 g_object_freeze_notify((GObject *) dim);
619 adg_dim_set_tolerance_up(dim, tolerance_up);
620 adg_dim_set_tolerance_down(dim, tolerance_down);
621 g_object_thaw_notify((GObject *) dim);
624 const gchar *
625 adg_dim_get_note(AdgDim *dim)
627 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
629 return dim->priv->note;
632 void
633 adg_dim_set_note(AdgDim *dim, const gchar *note)
635 g_return_if_fail(ADG_IS_DIM(dim));
637 g_free(dim->priv->note);
638 dim->priv->note = g_strdup(note);
639 g_object_notify((GObject *) dim, "note");
641 text_cache_invalidate(&dim->priv->note_cache);
646 * adg_dim_render_quote:
647 * @dim: an #AdgDim object
648 * @cr: a #cairo_t drawing context
650 * Renders the quote of @dim at the @org position. This function
651 * is only useful in new dimension implementations.
653 void
654 adg_dim_render_quote(AdgDim *dim, cairo_t *cr)
656 AdgDimPrivate *priv;
657 AdgDimStyle *dim_style;
659 g_return_if_fail(ADG_IS_DIM(dim));
661 priv = dim->priv;
662 dim_style = (AdgDimStyle *) adg_entity_get_style((AdgEntity *) dim,
663 ADG_SLOT_DIM_STYLE);
665 if (priv->quote == NULL)
666 adg_dim_set_quote(dim, ADG_DIM_GET_CLASS(dim)->default_quote(dim));
668 cairo_save(cr);
670 cairo_set_matrix(cr, adg_entity_get_paper_matrix((AdgEntity *) dim));
671 ADG_DIM_GET_CLASS(dim)->quote_layout(dim, cr);
672 cairo_set_matrix(cr, adg_entity_get_model_matrix((AdgEntity *) dim));
674 cairo_translate(cr, priv->quote_org.x, priv->quote_org.y);
675 adg_entity_scale_to_paper((AdgEntity *) dim, cr);
676 cairo_rotate(cr, priv->quote_angle);
678 /* Rendering quote */
679 adg_style_apply(adg_dim_style_get_quote_style(dim_style), cr);
680 text_cache_render(&priv->quote_cache, cr);
682 /* Rendering tolerances */
683 if (priv->tolerance_up != NULL || priv->tolerance_down != NULL) {
684 adg_style_apply(adg_dim_style_get_tolerance_style(dim_style), cr);
686 if (priv->tolerance_up != NULL)
687 text_cache_render(&priv->tolerance_up_cache, cr);
689 if (priv->tolerance_down != NULL)
690 text_cache_render(&priv->tolerance_down_cache, cr);
693 /* Rendering the note */
694 if (priv->note != NULL) {
695 adg_style_apply(adg_dim_style_get_note_style(dim_style), cr);
696 text_cache_render(&priv->note_cache, cr);
699 cairo_restore(cr);
703 static gboolean
704 text_cache_update(AdgTextCache *text_cache, const gchar *text,
705 cairo_t *cr, AdgStyle *style)
707 if (!text)
708 return FALSE;
710 if (style)
711 adg_style_apply(style, cr);
713 if (!text_cache->glyphs) {
714 cairo_status_t status;
716 status = cairo_scaled_font_text_to_glyphs(cairo_get_scaled_font(cr),
717 0., 0., text, -1,
718 &text_cache->glyphs,
719 &text_cache->num_glyphs,
720 NULL, NULL, NULL);
722 if (status != CAIRO_STATUS_SUCCESS) {
723 g_error("Unable to build glyphs (cairo message: %s)",
724 cairo_status_to_string(status));
725 return FALSE;
728 cairo_glyph_extents(cr, text_cache->glyphs, text_cache->num_glyphs,
729 &text_cache->extents);
732 return TRUE;
735 static void
736 text_cache_invalidate(AdgTextCache *text_cache)
738 if (text_cache->glyphs) {
739 cairo_glyph_free(text_cache->glyphs);
740 text_cache->glyphs = NULL;
742 text_cache->num_glyphs = 0;
743 memset(&text_cache->extents, 0, sizeof(text_cache->extents));
746 static void
747 text_cache_move_to(AdgTextCache *text_cache, const CpmlPair *to)
749 cairo_glyph_t *glyph;
750 int cnt;
751 double x, y;
753 glyph = text_cache->glyphs;
754 cnt = text_cache->num_glyphs;
755 x = to->x - glyph->x;
756 y = to->y - glyph->y;
758 while (cnt --) {
759 glyph->x += x;
760 glyph->y += y;
761 ++ glyph;
765 static void
766 text_cache_render(AdgTextCache *text_cache, cairo_t *cr)
768 cairo_show_glyphs(cr, text_cache->glyphs, text_cache->num_glyphs);