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.
28 #include <glib-object.h>
30 #include <pango/pangocairo.h>
31 #include "edapangorenderer.h"
33 /* We don't use gettext */
36 #define MAGIC_OVERBAR_POS_CONSTANT 0.8
39 PROP_CAIRO_CONTEXT
= 1,
42 struct _EdaPangoRendererPrivate
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
,
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
,
60 PangoGlyphString
*glyphs
,
62 static void eda_pango_renderer_draw_rectangle (PangoRenderer
*renderer
,
65 int width
, int height
);
66 static void eda_pango_renderer_draw_error_underline (PangoRenderer
*renderer
,
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
,
76 G_DEFINE_TYPE_WITH_PRIVATE (EdaPangoRenderer
, eda_pango_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 /* ---------------------------------------- */
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",
112 _("The Cairo context for rendering"),
114 | G_PARAM_CONSTRUCT_ONLY
115 | G_PARAM_STATIC_NAME
116 | G_PARAM_STATIC_NICK
117 | G_PARAM_STATIC_BLURB
));
121 eda_pango_renderer_init (EdaPangoRenderer
*renderer
)
123 renderer
->priv
= eda_pango_renderer_get_instance_private (renderer
);
127 eda_pango_renderer_constructor (GType type
,
128 guint n_construct_properties
,
129 GObjectConstructParam
*construct_params
)
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
,
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.");
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
);
162 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
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
);
176 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
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
);
192 eda_pango_renderer_draw_glyphs (PangoRenderer
*renderer
,
194 PangoGlyphString
*glyphs
,
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
;
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 */
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. */
227 cairo_rectangle (cr
, floor(rx
), floor(ry
), floor(rwidth
), rheight
);
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
);
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
);
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
);
261 eda_pango_renderer_part_changed (PangoRenderer
*renderer
,
262 PangoRenderPart part
)
267 eda_pango_renderer_begin (PangoRenderer
*renderer
)
272 eda_pango_renderer_end (PangoRenderer
*renderer
)
277 eda_pango_renderer_prepare_run (PangoRenderer
*renderer
,
280 EdaPangoRenderer
*eda_renderer
= EDA_PANGO_RENDERER (renderer
);
281 gboolean overbar
= FALSE
;
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
,
301 eda_pango_renderer_new (cairo_t
*cr
)
303 return g_object_new (EDA_TYPE_PANGO_RENDERER
, "cairo-context", cr
, NULL
);
307 eda_pango_renderer_show_layout (EdaPangoRenderer
*renderer
, PangoLayout
*pl
,
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
);
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
);
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
};
346 klass
.type
= pango_attr_type_register ("EdaPangoAttrOverbar");
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
;
362 eda_is_pango_attr_overbar (PangoAttribute
*attr
)
364 return attr
->klass
->type
== eda_pango_attr_overbar_get_class()->type
;
368 eda_pango_parse_overbars (const gchar
*overbar_text
, int length
,
369 PangoAttrList
**attr_list
, gchar
**text
)
371 const char *in_ptr
= NULL
;
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. */
389 length
= strlen (overbar_text
);
391 *text
= g_malloc0 (length
+ 1);
394 for (in_ptr
= overbar_text
;
395 (in_ptr
- overbar_text
) <= length
; /* Include \0 at end */
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
;
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
410 if ((*in_ptr
== '_') && escape_start
) {
412 overbar_end
= out_ptr
;
414 overbar_start
= out_ptr
;
417 *out_ptr
++ = *in_ptr
;
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,
436 if (*in_ptr
== '\0') break;