1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 font.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
6 Copyright (c) 2003 Derek Foreman
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 See the COPYING file for a copy of the GNU General Public License.
34 static void measure_font(const RrInstance *inst, RrFont *f)
36 PangoFontMetrics *metrics;
37 static PangoLanguage *lang = NULL;
40 #if PANGO_VERSION_MAJOR > 1 || \
41 (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16)
42 lang = pango_language_get_default();
45 /* get the default language from the locale
46 (based on gtk_get_default_language in gtkmain.c) */
47 locale = g_strdup(setlocale(LC_CTYPE, NULL));
48 if ((p = strchr(locale, '.'))) *p = '\0'; /* strip off the . */
49 if ((p = strchr(locale, '@'))) *p = '\0'; /* strip off the @ */
50 lang = pango_language_from_string(locale);
55 /* measure the ascent and descent */
56 metrics = pango_context_get_metrics(inst->pango, f->font_desc, lang);
57 f->ascent = pango_font_metrics_get_ascent(metrics);
58 f->descent = pango_font_metrics_get_descent(metrics);
59 pango_font_metrics_unref(metrics);
63 RrFont *RrFontOpen(const RrInstance *inst, const gchar *name, gint size,
64 RrFontWeight weight, RrFontSlant slant)
69 PangoAttrList *attrlist;
71 out = g_new(RrFont, 1);
74 out->font_desc = pango_font_description_new();
75 out->layout = pango_layout_new(inst->pango);
76 out->shortcut_underline = pango_attr_underline_new(PANGO_UNDERLINE_LOW);
77 out->shortcut_underline->start_index = 0;
78 out->shortcut_underline->end_index = 0;
80 attrlist = pango_attr_list_new();
81 /* shortcut_underline is owned by the attrlist */
82 pango_attr_list_insert(attrlist, out->shortcut_underline);
83 /* the attributes are owned by the layout */
84 pango_layout_set_attributes(out->layout, attrlist);
85 pango_attr_list_unref(attrlist);
88 case RR_FONTWEIGHT_LIGHT: pweight = PANGO_WEIGHT_LIGHT; break;
89 case RR_FONTWEIGHT_NORMAL: pweight = PANGO_WEIGHT_NORMAL; break;
90 case RR_FONTWEIGHT_SEMIBOLD: pweight = PANGO_WEIGHT_SEMIBOLD; break;
91 case RR_FONTWEIGHT_BOLD: pweight = PANGO_WEIGHT_BOLD; break;
92 case RR_FONTWEIGHT_ULTRABOLD: pweight = PANGO_WEIGHT_ULTRABOLD; break;
93 default: g_assert_not_reached();
97 case RR_FONTSLANT_NORMAL: pstyle = PANGO_STYLE_NORMAL; break;
98 case RR_FONTSLANT_ITALIC: pstyle = PANGO_STYLE_ITALIC; break;
99 case RR_FONTSLANT_OBLIQUE: pstyle = PANGO_STYLE_OBLIQUE; break;
100 default: g_assert_not_reached();
104 pango_font_description_set_family(out->font_desc, name);
105 pango_font_description_set_weight(out->font_desc, pweight);
106 pango_font_description_set_style(out->font_desc, pstyle);
107 pango_font_description_set_size(out->font_desc, size * PANGO_SCALE);
109 /* setup the layout */
110 pango_layout_set_font_description(out->layout, out->font_desc);
111 pango_layout_set_wrap(out->layout, PANGO_WRAP_WORD_CHAR);
113 /* get the ascent and descent */
114 measure_font(inst, out);
119 RrFont *RrFontOpenDefault(const RrInstance *inst)
121 return RrFontOpen(inst, RrDefaultFontFamily, RrDefaultFontSize,
122 RrDefaultFontWeight, RrDefaultFontSlant);
125 void RrFontRef(RrFont *f)
130 void RrFontClose(RrFont *f)
134 g_object_unref(f->layout);
135 pango_font_description_free(f->font_desc);
141 static void font_measure_full(const RrFont *f, const gchar *str,
142 gint *x, gint *y, gint shadow_x, gint shadow_y,
143 gboolean flow, gint maxwidth)
147 pango_layout_set_text(f->layout, str, -1);
149 pango_layout_set_single_paragraph_mode(f->layout, FALSE);
150 pango_layout_set_width(f->layout, maxwidth * PANGO_SCALE);
151 pango_layout_set_ellipsize(f->layout, PANGO_ELLIPSIZE_NONE);
154 /* single line mode */
155 pango_layout_set_single_paragraph_mode(f->layout, TRUE);
156 pango_layout_set_width(f->layout, -1);
157 pango_layout_set_ellipsize(f->layout, PANGO_ELLIPSIZE_MIDDLE);
160 /* pango_layout_get_pixel_extents lies! this is the right way to get the
161 size of the text's area */
162 pango_layout_get_extents(f->layout, NULL, &rect);
163 #if PANGO_VERSION_MAJOR > 1 || \
164 (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16)
165 /* pass the logical rect as the ink rect, this is on purpose so we get the
166 full area for the text */
167 pango_extents_to_pixels(&rect, NULL);
169 rect.width = (rect.width + PANGO_SCALE - 1) / PANGO_SCALE;
170 rect.height = (rect.height + PANGO_SCALE - 1) / PANGO_SCALE;
172 *x = rect.width + ABS(shadow_x) + 4 /* we put a 2 px edge on each side */;
173 *y = rect.height + ABS(shadow_y);
176 RrSize *RrFontMeasureString(const RrFont *f, const gchar *str,
177 gint shadow_x, gint shadow_y,
178 gboolean flow, gint maxwidth)
182 g_assert(!flow || maxwidth > 0);
184 size = g_new(RrSize, 1);
185 font_measure_full(f, str, &size->width, &size->height, shadow_x, shadow_y,
190 gint RrFontHeight(const RrFont *f, gint shadow_y)
192 return (f->ascent + f->descent) / PANGO_SCALE + ABS(shadow_y);
195 static inline int font_calculate_baseline(RrFont *f, gint height)
197 /* For my own reference:
199 * ^space/2 ^height ^baseline
202 * | | | | |_ _____ _| |_ _ _
203 * | | | | _/ -_) \ / _| || |
204 * | v_________v \__\___/_\_\\__|\_, |
210 return (((height * PANGO_SCALE) /* height of the space in pango units */
211 - (f->ascent + f->descent)) /* minus space taken up by text */
212 / 2 /* divided by two -> half of the empty space (this is the top
214 + f->ascent) /* now move down to the baseline */
215 / PANGO_SCALE; /* back to pixels */
218 void RrFontDraw(XftDraw *d, RrTextureText *t, RrRect *area)
224 PangoAttrList *attrlist;
225 PangoEllipsizeMode ell;
227 g_assert(!t->flow || t->maxwidth > 0);
231 /* center the text vertically
232 We do this centering based on the 'baseline' since different fonts
233 have different top edges. It looks bad when the whole string is
234 moved when 1 character from a non-default language is included in
236 y += font_calculate_baseline(t->font, area->height);
238 /* the +2 and -4 leave a small blank edge on the sides */
241 if (t->flow) w = MAX(w, t->maxwidth);
246 ell = PANGO_ELLIPSIZE_NONE;
248 switch (t->ellipsize) {
249 case RR_ELLIPSIZE_NONE:
250 ell = PANGO_ELLIPSIZE_NONE;
252 case RR_ELLIPSIZE_START:
253 ell = PANGO_ELLIPSIZE_START;
255 case RR_ELLIPSIZE_MIDDLE:
256 ell = PANGO_ELLIPSIZE_MIDDLE;
258 case RR_ELLIPSIZE_END:
259 ell = PANGO_ELLIPSIZE_END;
264 pango_layout_set_text(t->font->layout, t->string, -1);
265 pango_layout_set_width(t->font->layout, w * PANGO_SCALE);
266 pango_layout_set_ellipsize(t->font->layout, ell);
267 pango_layout_set_single_paragraph_mode(t->font->layout, !t->flow);
269 /* * * end of setting up the layout * * */
271 pango_layout_get_pixel_extents(t->font->layout, NULL, &rect);
274 /* pango_layout_set_alignment doesn't work with
275 pango_xft_render_layout_line */
276 switch (t->justify) {
277 case RR_JUSTIFY_LEFT:
279 case RR_JUSTIFY_RIGHT:
282 case RR_JUSTIFY_CENTER:
287 if (t->shadow_offset_x || t->shadow_offset_y) {
288 c.color.red = t->shadow_color->r | t->shadow_color->r << 8;
289 c.color.green = t->shadow_color->g | t->shadow_color->g << 8;
290 c.color.blue = t->shadow_color->b | t->shadow_color->b << 8;
291 c.color.alpha = 0xffff * t->shadow_alpha / 255;
292 c.pixel = t->shadow_color->pixel;
296 pango_xft_render_layout_line
298 #if PANGO_VERSION_MAJOR > 1 || \
299 (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16)
300 pango_layout_get_line_readonly(t->font->layout, 0),
302 pango_layout_get_line(t->font->layout, 0),
304 (x + t->shadow_offset_x) * PANGO_SCALE,
305 (y + t->shadow_offset_y) * PANGO_SCALE);
308 pango_xft_render_layout(d, &c, t->font->layout,
309 (x + t->shadow_offset_x) * PANGO_SCALE,
310 (y + t->shadow_offset_y) * PANGO_SCALE);
314 c.color.red = t->color->r | t->color->r << 8;
315 c.color.green = t->color->g | t->color->g << 8;
316 c.color.blue = t->color->b | t->color->b << 8;
317 c.color.alpha = 0xff | 0xff << 8; /* fully opaque text */
318 c.pixel = t->color->pixel;
321 const gchar *s = t->string + t->shortcut_pos;
323 t->font->shortcut_underline->start_index = t->shortcut_pos;
324 t->font->shortcut_underline->end_index = t->shortcut_pos +
325 (g_utf8_next_char(s) - s);
327 /* the attributes are owned by the layout.
328 re-add the attributes to the layout after changing the
329 start and end index */
330 attrlist = pango_layout_get_attributes(t->font->layout);
331 pango_attr_list_ref(attrlist);
332 pango_layout_set_attributes(t->font->layout, attrlist);
333 pango_attr_list_unref(attrlist);
336 /* layout_line() uses y to specify the baseline
337 The line doesn't need to be freed, it's a part of the layout */
339 pango_xft_render_layout_line
341 #if PANGO_VERSION_MAJOR > 1 || \
342 (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16)
343 pango_layout_get_line_readonly(t->font->layout, 0),
345 pango_layout_get_line(t->font->layout, 0),
351 pango_xft_render_layout(d, &c, t->font->layout,
357 t->font->shortcut_underline->start_index = 0;
358 t->font->shortcut_underline->end_index = 0;
359 /* the attributes are owned by the layout.
360 re-add the attributes to the layout after changing the
361 start and end index */
362 attrlist = pango_layout_get_attributes(t->font->layout);
363 pango_attr_list_ref(attrlist);
364 pango_layout_set_attributes(t->font->layout, attrlist);
365 pango_attr_list_unref(attrlist);