gschem: Fix previous fix for VPATH builds
[geda-gaf.git] / libgedacairo / edapangorenderer.c
blobc42979a57c15dc953c6f544b144d2473c8ac65de
1 /* gEDA - GPL Electronic Design Automation
2 * libgedacairo - Rendering gEDA schematics with Cairo
3 * Copyright (C) 2010-2020 gEDA Contributors (see ChangeLog for details)
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 * This file based on GDK's gdkpango.c (LGPL V2+)
20 * Copyright (C) 2000 Red Hat, Inc.
23 #include <config.h>
25 #include <math.h>
26 #include <string.h>
27 #include <glib.h>
28 #include <glib-object.h>
29 #include <cairo.h>
30 #include <pango/pangocairo.h>
31 #include "edapangorenderer.h"
33 /* We don't use gettext */
34 #define _(x) (x)
36 #define MAGIC_OVERBAR_POS_CONSTANT 0.8
38 enum {
39 PROP_CAIRO_CONTEXT = 1,
42 struct _EdaPangoRendererPrivate
44 cairo_t *cr;
45 gboolean overbar;
48 static GObject *eda_pango_renderer_constructor (GType type,
49 guint n_construct_properties,
50 GObjectConstructParam *construct_params);
51 static void eda_pango_renderer_set_property (GObject *object, guint prop_id,
52 const GValue *value,
53 GParamSpec *pspec);
54 static void eda_pango_renderer_get_property (GObject *object, guint prop_id,
55 GValue *value, GParamSpec *pspec);
56 static void eda_pango_renderer_finalize (GObject *object);
58 static void eda_pango_renderer_draw_glyphs (PangoRenderer *renderer,
59 PangoFont *font,
60 PangoGlyphString *glyphs,
61 int x, int y);
62 static void eda_pango_renderer_draw_rectangle (PangoRenderer *renderer,
63 PangoRenderPart part,
64 int x, int y,
65 int width, int height);
66 static void eda_pango_renderer_draw_error_underline (PangoRenderer *renderer,
67 int x, int y,
68 int width, int height);
69 static void eda_pango_renderer_part_changed (PangoRenderer *renderer,
70 PangoRenderPart part);
71 static void eda_pango_renderer_begin (PangoRenderer *renderer);
72 static void eda_pango_renderer_end (PangoRenderer *renderer);
73 static void eda_pango_renderer_prepare_run (PangoRenderer *renderer,
74 PangoLayoutRun *run);
76 G_DEFINE_TYPE_WITH_PRIVATE (EdaPangoRenderer, eda_pango_renderer,
77 PANGO_TYPE_RENDERER)
79 /* ---------------------------------------- */
81 static PangoAttribute *eda_pango_attr_overbar_copy (const PangoAttribute *attr);
82 static gboolean eda_pango_attr_overbar_compare (const PangoAttribute *attr1,
83 const PangoAttribute *attr2);
85 /* ---------------------------------------- */
87 static void
88 eda_pango_renderer_class_init (EdaPangoRendererClass *klass)
90 GObjectClass *object_class = G_OBJECT_CLASS (klass);
91 PangoRendererClass *parent_class = PANGO_RENDERER_CLASS (klass);
93 /* Register functions with base class */
94 object_class->constructor = eda_pango_renderer_constructor;
95 object_class->set_property = eda_pango_renderer_set_property;
96 object_class->get_property = eda_pango_renderer_get_property;
97 object_class->finalize = eda_pango_renderer_finalize;
99 /* Register functions with parent class */
100 parent_class->draw_glyphs = eda_pango_renderer_draw_glyphs;
101 parent_class->draw_rectangle = eda_pango_renderer_draw_rectangle;
102 parent_class->draw_error_underline = eda_pango_renderer_draw_error_underline;
103 parent_class->part_changed = eda_pango_renderer_part_changed;
104 parent_class->begin = eda_pango_renderer_begin;
105 parent_class->end = eda_pango_renderer_end;
106 parent_class->prepare_run = eda_pango_renderer_prepare_run;
108 /* Install properties */
109 g_object_class_install_property (object_class, PROP_CAIRO_CONTEXT,
110 g_param_spec_pointer ("cairo-context",
111 _("Cairo context"),
112 _("The Cairo context for rendering"),
113 G_PARAM_READWRITE
114 | G_PARAM_CONSTRUCT_ONLY
115 | G_PARAM_STATIC_NAME
116 | G_PARAM_STATIC_NICK
117 | G_PARAM_STATIC_BLURB));
120 static void
121 eda_pango_renderer_init (EdaPangoRenderer *renderer)
123 renderer->priv = eda_pango_renderer_get_instance_private (renderer);
126 static GObject *
127 eda_pango_renderer_constructor (GType type,
128 guint n_construct_properties,
129 GObjectConstructParam *construct_params)
131 GObject *object;
132 GObjectClass *parent_object_class;
133 EdaPangoRenderer *renderer;
135 parent_object_class = G_OBJECT_CLASS (eda_pango_renderer_parent_class);
136 object = parent_object_class->constructor (type, n_construct_properties,
137 construct_params);
139 #ifndef G_DISABLE_ASSERT
140 renderer = EDA_PANGO_RENDERER (object);
141 if (renderer->priv->cr == NULL) {
142 g_warning ("EdaPangoRenderer: Cairo context must be specified at construction.");
144 #endif
146 return object;
149 static void
150 eda_pango_renderer_set_property (GObject *object, guint property_id,
151 const GValue *value, GParamSpec *pspec)
153 EdaPangoRenderer *renderer = EDA_PANGO_RENDERER (object);
154 switch (property_id) {
155 case PROP_CAIRO_CONTEXT:
156 renderer->priv->cr = (cairo_t *) g_value_get_pointer (value);
157 if (renderer->priv->cr != NULL) {
158 cairo_reference (renderer->priv->cr);
160 break;
161 default:
162 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
166 static void
167 eda_pango_renderer_get_property (GObject *object, guint property_id,
168 GValue *value, GParamSpec *pspec)
170 EdaPangoRenderer *renderer = EDA_PANGO_RENDERER (object);
171 switch (property_id) {
172 case PROP_CAIRO_CONTEXT:
173 g_value_set_pointer (value, renderer->priv->cr);
174 break;
175 default:
176 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
180 static void
181 eda_pango_renderer_finalize (GObject *object)
183 EdaPangoRenderer *renderer = EDA_PANGO_RENDERER (object);
184 G_OBJECT_CLASS (eda_pango_renderer_parent_class)->finalize (object);
186 if (renderer->priv->cr != NULL) {
187 cairo_destroy (renderer->priv->cr);
191 static void
192 eda_pango_renderer_draw_glyphs (PangoRenderer *renderer,
193 PangoFont *font,
194 PangoGlyphString *glyphs,
195 int x, int y)
197 EdaPangoRenderer *eda_renderer = EDA_PANGO_RENDERER (renderer);
198 cairo_t *cr = eda_renderer->priv->cr;
200 if (eda_renderer->priv->overbar) {
201 PangoFontMetrics *metrics;
202 double rx, ry;
203 double rwidth;
204 double rheight;
205 PangoRectangle glyphs_extents;
207 pango_glyph_string_extents (glyphs, font, NULL, &glyphs_extents);
208 rx = x / PANGO_SCALE;
209 ry = (y - glyphs_extents.height * MAGIC_OVERBAR_POS_CONSTANT) / PANGO_SCALE;
210 rwidth = glyphs_extents.width / PANGO_SCALE;
212 /* Make the thickness the same as for the font's underline */
213 metrics = pango_font_get_metrics (font, NULL);
214 rheight = pango_font_metrics_get_underline_thickness (metrics) / PANGO_SCALE;
215 pango_font_metrics_unref (metrics);
217 /* Allow the overbar to fade out as it becomes < 1px high */
218 if (rheight > 1.0) {
219 rheight = floor (rheight);
222 /* The +1 on width is a hack to ensure hinting doesn't sometimes
223 * cause the overbars to be broken by a 1px gap if the overbar
224 * spans multiple calls to this function. */
225 rwidth += 1.0;
227 cairo_rectangle (cr, floor(rx), floor(ry), floor(rwidth), rheight);
228 cairo_fill (cr);
231 /* Now draw the actual characters */
232 cairo_move_to (cr, (double) x / PANGO_SCALE, (double) y / PANGO_SCALE);
233 pango_cairo_show_glyph_string (cr, font, glyphs);
236 static void
237 eda_pango_renderer_draw_rectangle (PangoRenderer *renderer,
238 PangoRenderPart part,
239 int x, int y, int width, int height)
241 EdaPangoRenderer *eda_renderer = EDA_PANGO_RENDERER (renderer);
242 cairo_t *cr = eda_renderer->priv->cr;
244 cairo_rectangle (cr, (double) x / PANGO_SCALE, (double) y / PANGO_SCALE,
245 (double) width / PANGO_SCALE, (double) height / PANGO_SCALE);
246 cairo_fill (cr);
249 static void
250 eda_pango_renderer_draw_error_underline (PangoRenderer *renderer,
251 int x, int y, int width, int height)
253 cairo_t *cr = EDA_PANGO_RENDERER (renderer)->priv->cr;
254 pango_cairo_show_error_underline (cr, (double) x / PANGO_SCALE,
255 (double) y / PANGO_SCALE,
256 (double) width / PANGO_SCALE,
257 (double) height / PANGO_SCALE);
260 static void
261 eda_pango_renderer_part_changed (PangoRenderer *renderer,
262 PangoRenderPart part)
266 static void
267 eda_pango_renderer_begin (PangoRenderer *renderer)
271 static void
272 eda_pango_renderer_end (PangoRenderer *renderer)
276 static void
277 eda_pango_renderer_prepare_run (PangoRenderer *renderer,
278 PangoLayoutRun *run)
280 EdaPangoRenderer *eda_renderer = EDA_PANGO_RENDERER (renderer);
281 gboolean overbar = FALSE;
282 GSList *l;
284 for (l = run->item->analysis.extra_attrs; l != NULL; l = g_slist_next (l)) {
285 if (eda_is_pango_attr_overbar ((PangoAttribute *) l->data)) {
286 EdaPangoAttrOverbar *attr = (EdaPangoAttrOverbar *) l->data;
287 overbar = attr->overbar;
291 if (eda_renderer->priv->overbar != overbar) {
292 pango_renderer_part_changed (renderer, PANGO_RENDER_PART_FOREGROUND);
293 eda_renderer->priv->overbar = overbar;
296 PANGO_RENDERER_CLASS (eda_pango_renderer_parent_class)->prepare_run (renderer,
297 run);
300 PangoRenderer *
301 eda_pango_renderer_new (cairo_t *cr)
303 return g_object_new (EDA_TYPE_PANGO_RENDERER, "cairo-context", cr, NULL);
306 void
307 eda_pango_renderer_show_layout (EdaPangoRenderer *renderer, PangoLayout *pl,
308 double x, double y)
310 g_return_if_fail (EDA_IS_PANGO_RENDERER (renderer));
311 g_return_if_fail (renderer->priv->cr != NULL);
312 g_return_if_fail (PANGO_IS_LAYOUT (pl));
314 pango_renderer_draw_layout (PANGO_RENDERER (renderer),
315 pl, x * PANGO_SCALE, y * PANGO_SCALE);
319 /* ---------------------------------------- */
321 static PangoAttribute *
322 eda_pango_attr_overbar_copy (const PangoAttribute *attr)
324 const EdaPangoAttrOverbar *a = (const EdaPangoAttrOverbar *) attr;
325 return eda_pango_attr_overbar_new (a->overbar);
328 static gboolean
329 eda_pango_attr_overbar_compare (const PangoAttribute *attr1,
330 const PangoAttribute *attr2)
332 const EdaPangoAttrOverbar *a1 = (const EdaPangoAttrOverbar *) attr1;
333 const EdaPangoAttrOverbar *a2 = (const EdaPangoAttrOverbar *) attr2;
334 return (a1->overbar == a2->overbar);
337 PangoAttrClass *
338 eda_pango_attr_overbar_get_class ()
340 static PangoAttrClass klass = { 0,
341 eda_pango_attr_overbar_copy,
342 (void (*)(PangoAttribute *)) g_free,
343 eda_pango_attr_overbar_compare };
345 if (!klass.type) {
346 klass.type = pango_attr_type_register ("EdaPangoAttrOverbar");
349 return &klass;
352 PangoAttribute *
353 eda_pango_attr_overbar_new (gboolean overbar)
355 EdaPangoAttrOverbar *result = g_new (EdaPangoAttrOverbar, 1);
356 result->attr.klass = eda_pango_attr_overbar_get_class ();
357 result->overbar = overbar;
358 return (PangoAttribute *) result;
361 gboolean
362 eda_is_pango_attr_overbar (PangoAttribute *attr)
364 return attr->klass->type == eda_pango_attr_overbar_get_class()->type;
367 gboolean
368 eda_pango_parse_overbars (const gchar *overbar_text, int length,
369 PangoAttrList **attr_list, gchar **text)
371 const char *in_ptr = NULL;
372 char *out_ptr;
373 char *overbar_start = NULL;
374 char *overbar_end = NULL;
375 const char *escape_start = NULL;
377 g_return_val_if_fail ((overbar_text != NULL), FALSE);
378 g_return_val_if_fail ((attr_list != NULL), FALSE);
379 g_return_val_if_fail ((text != NULL), FALSE);
381 /* Create the attribute list */
382 *attr_list = pango_attr_list_new ();
384 /* We know the length of the output will be <= the length of the
385 * input text. So we just allocate a string of the same length. If
386 * length was given as -1, the input should be null-terminated, so
387 * just use strlen. */
388 if (length == -1) {
389 length = strlen (overbar_text);
391 *text = g_malloc0 (length + 1);
392 out_ptr = *text;
394 for (in_ptr = overbar_text;
395 (in_ptr - overbar_text) <= length; /* Include \0 at end */
396 in_ptr++) {
398 /* If we find an escape character and we are not already in an
399 * escaped state, enter escaped state and don't add the current
400 * character to the output. */
401 if ((*in_ptr == '\\') && !escape_start) {
402 escape_start = in_ptr;
403 continue;
406 /* If the escaped character is '_', this is an overbar delimiter.
407 * Enter or exit overbar state if appropriate. Otherwise, simply
408 * append the character (which may have been escaped) to the
409 * output. */
410 if ((*in_ptr == '_') && escape_start) {
411 if (overbar_start) {
412 overbar_end = out_ptr;
413 } else {
414 overbar_start = out_ptr;
416 } else {
417 *out_ptr++ = *in_ptr;
419 escape_start = NULL;
421 /* If we've previously found an overbar delimiter, and either we
422 * find a null byte or another overbar delimiter, create an
423 * overbar attribute for the intervening run of characters. */
424 if (overbar_start && (overbar_end || (*in_ptr == '\0'))) {
425 /* Create overbar attribute and add to attribute list */
426 PangoAttribute *attr = eda_pango_attr_overbar_new (TRUE);
427 attr->start_index = overbar_start - *text;
428 attr->end_index = overbar_end - *text;
429 pango_attr_list_insert (*attr_list, attr);
430 /* Clear overbar start & end pointers */
431 overbar_start = overbar_end = NULL;
434 /* If we encounter a null character before we were expecting it,
435 * give up anyway. */
436 if (*in_ptr == '\0') break;
439 return TRUE;