]> Dogcows Code - chaz/openbox/blob - obrender/font.c
Merge branch 'master' into chaz
[chaz/openbox] / obrender / font.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
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
7
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.
12
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.
17
18 See the COPYING file for a copy of the GNU General Public License.
19 */
20
21 #include "font.h"
22 #include "color.h"
23 #include "mask.h"
24 #include "theme.h"
25 #include "geom.h"
26 #include "instance.h"
27 #include "gettext.h"
28
29 #include <glib.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <locale.h>
33
34 static void measure_font(const RrInstance *inst, RrFont *f)
35 {
36 PangoFontMetrics *metrics;
37 static PangoLanguage *lang = NULL;
38
39 if (lang == NULL) {
40 #if PANGO_VERSION_MAJOR > 1 || \
41 (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16)
42 lang = pango_language_get_default();
43 #else
44 gchar *locale, *p;
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);
51 g_free(locale);
52 #endif
53 }
54
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);
60
61 }
62
63 RrFont *RrFontOpen(const RrInstance *inst, const gchar *name, gint size,
64 RrFontWeight weight, RrFontSlant slant)
65 {
66 RrFont *out;
67 PangoWeight pweight;
68 PangoStyle pstyle;
69 PangoAttrList *attrlist;
70
71 out = g_slice_new(RrFont);
72 out->inst = inst;
73 out->ref = 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;
79
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);
86
87 switch (weight) {
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();
94 }
95
96 switch (slant) {
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();
101 }
102
103 /* setup the font */
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);
108
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);
112
113 /* get the ascent and descent */
114 measure_font(inst, out);
115
116 return out;
117 }
118
119 RrFont *RrFontOpenDefault(const RrInstance *inst)
120 {
121 return RrFontOpen(inst, RrDefaultFontFamily, RrDefaultFontSize,
122 RrDefaultFontWeight, RrDefaultFontSlant);
123 }
124
125 void RrFontRef(RrFont *f)
126 {
127 ++f->ref;
128 }
129
130 void RrFontClose(RrFont *f)
131 {
132 if (f) {
133 if (--f->ref < 1) {
134 g_object_unref(f->layout);
135 pango_font_description_free(f->font_desc);
136 g_slice_free(RrFont, f);
137 }
138 }
139 }
140
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)
144 {
145 PangoRectangle rect;
146
147 pango_layout_set_text(f->layout, str, -1);
148 if (flow) {
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);
152 }
153 else {
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);
158 }
159
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);
168 #else
169 rect.width = (rect.width + PANGO_SCALE - 1) / PANGO_SCALE;
170 rect.height = (rect.height + PANGO_SCALE - 1) / PANGO_SCALE;
171 #endif
172 *x = rect.width + ABS(shadow_x) + 4 /* we put a 2 px edge on each side */;
173 *y = rect.height + ABS(shadow_y);
174 }
175
176 RrSize *RrFontMeasureString(const RrFont *f, const gchar *str,
177 gint shadow_x, gint shadow_y,
178 gboolean flow, gint maxwidth)
179 {
180 RrSize *size;
181
182 g_assert(!flow || maxwidth > 0);
183
184 size = g_slice_new(RrSize);
185 font_measure_full(f, str, &size->width, &size->height, shadow_x, shadow_y,
186 flow, maxwidth);
187 return size;
188 }
189
190 gint RrFontHeight(const RrFont *f, gint shadow_y)
191 {
192 return (f->ascent + f->descent) / PANGO_SCALE + ABS(shadow_y);
193 }
194
195 static inline int font_calculate_baseline(RrFont *f, gint height)
196 {
197 /* For my own reference:
198 * _________
199 * ^space/2 ^height ^baseline
200 * v_________|_ |
201 * | ^ascent | _ _
202 * | | | | |_ _____ _| |_ _ _
203 * | | | | _/ -_) \ / _| || |
204 * | v_________v \__\___/_\_\\__|\_, |
205 * | ^descent |__/
206 * __________|_v
207 * ^space/2 |
208 * V_________v
209 */
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
213 of the text) */
214 + f->ascent) /* now move down to the baseline */
215 / PANGO_SCALE; /* back to pixels */
216 }
217
218 void RrFontDraw(XftDraw *d, RrTextureText *t, RrRect *area)
219 {
220 gint x,y,w;
221 XftColor c;
222 gint mw;
223 PangoRectangle rect;
224 PangoAttrList *attrlist;
225 PangoEllipsizeMode ell;
226
227 g_assert(!t->flow || t->maxwidth > 0);
228
229 y = area->y;
230 if (!t->flow)
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
235 the string */
236 y += font_calculate_baseline(t->font, area->height);
237
238 /* the +2 and -4 leave a small blank edge on the sides */
239 x = area->x + 2;
240 w = area->width;
241 if (t->flow) w = MAX(w, t->maxwidth);
242 w -= 4;
243 /* h = area->height; */
244
245 if (t->flow)
246 ell = PANGO_ELLIPSIZE_NONE;
247 else {
248 switch (t->ellipsize) {
249 case RR_ELLIPSIZE_NONE:
250 ell = PANGO_ELLIPSIZE_NONE;
251 break;
252 case RR_ELLIPSIZE_START:
253 ell = PANGO_ELLIPSIZE_START;
254 break;
255 case RR_ELLIPSIZE_MIDDLE:
256 ell = PANGO_ELLIPSIZE_MIDDLE;
257 break;
258 case RR_ELLIPSIZE_END:
259 ell = PANGO_ELLIPSIZE_END;
260 break;
261 default:
262 g_assert_not_reached();
263 }
264 }
265
266 pango_layout_set_text(t->font->layout, t->string, -1);
267 pango_layout_set_width(t->font->layout, w * PANGO_SCALE);
268 pango_layout_set_ellipsize(t->font->layout, ell);
269 pango_layout_set_single_paragraph_mode(t->font->layout, !t->flow);
270
271 /* * * end of setting up the layout * * */
272
273 pango_layout_get_pixel_extents(t->font->layout, NULL, &rect);
274 mw = rect.width;
275
276 /* pango_layout_set_alignment doesn't work with
277 pango_xft_render_layout_line */
278 switch (t->justify) {
279 case RR_JUSTIFY_LEFT:
280 break;
281 case RR_JUSTIFY_RIGHT:
282 x += (w - mw);
283 break;
284 case RR_JUSTIFY_CENTER:
285 x += (w - mw) / 2;
286 break;
287 case RR_JUSTIFY_NUM_TYPES:
288 g_assert_not_reached();
289 }
290
291 if (t->shadow_offset_x || t->shadow_offset_y) {
292 /* From nvidia's readme (chapter 23):
293
294 When rendering to a 32-bit window, keep in mind that the X RENDER
295 extension, used by most composite managers, expects "premultiplied
296 alpha" colors. This means that if your color has components (r,g,b)
297 and alpha value a, then you must render (a*r, a*g, a*b, a) into the
298 target window.
299 */
300 c.color.red = (t->shadow_color->r | t->shadow_color->r << 8) *
301 t->shadow_alpha / 255;
302 c.color.green = (t->shadow_color->g | t->shadow_color->g << 8) *
303 t->shadow_alpha / 255;
304 c.color.blue = (t->shadow_color->b | t->shadow_color->b << 8) *
305 t->shadow_alpha / 255;
306 c.color.alpha = 0xffff * t->shadow_alpha / 255;
307 c.pixel = t->shadow_color->pixel;
308
309 /* see below... */
310 if (!t->flow) {
311 pango_xft_render_layout_line
312 (d, &c,
313 #if PANGO_VERSION_MAJOR > 1 || \
314 (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16)
315 pango_layout_get_line_readonly(t->font->layout, 0),
316 #else
317 pango_layout_get_line(t->font->layout, 0),
318 #endif
319 (x + t->shadow_offset_x) * PANGO_SCALE,
320 (y + t->shadow_offset_y) * PANGO_SCALE);
321 }
322 else {
323 pango_xft_render_layout(d, &c, t->font->layout,
324 (x + t->shadow_offset_x) * PANGO_SCALE,
325 (y + t->shadow_offset_y) * PANGO_SCALE);
326 }
327 }
328
329 c.color.red = t->color->r | t->color->r << 8;
330 c.color.green = t->color->g | t->color->g << 8;
331 c.color.blue = t->color->b | t->color->b << 8;
332 c.color.alpha = 0xff | 0xff << 8; /* fully opaque text */
333 c.pixel = t->color->pixel;
334
335 if (t->shortcut) {
336 const gchar *s = t->string + t->shortcut_pos;
337
338 t->font->shortcut_underline->start_index = t->shortcut_pos;
339 t->font->shortcut_underline->end_index = t->shortcut_pos +
340 (g_utf8_next_char(s) - s);
341
342 /* the attributes are owned by the layout.
343 re-add the attributes to the layout after changing the
344 start and end index */
345 attrlist = pango_layout_get_attributes(t->font->layout);
346 pango_attr_list_ref(attrlist);
347 pango_layout_set_attributes(t->font->layout, attrlist);
348 pango_attr_list_unref(attrlist);
349 }
350
351 /* layout_line() uses y to specify the baseline
352 The line doesn't need to be freed, it's a part of the layout */
353 if (!t->flow) {
354 pango_xft_render_layout_line
355 (d, &c,
356 #if PANGO_VERSION_MAJOR > 1 || \
357 (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16)
358 pango_layout_get_line_readonly(t->font->layout, 0),
359 #else
360 pango_layout_get_line(t->font->layout, 0),
361 #endif
362 x * PANGO_SCALE,
363 y * PANGO_SCALE);
364 }
365 else {
366 pango_xft_render_layout(d, &c, t->font->layout,
367 x * PANGO_SCALE,
368 y * PANGO_SCALE);
369 }
370
371 if (t->shortcut) {
372 t->font->shortcut_underline->start_index = 0;
373 t->font->shortcut_underline->end_index = 0;
374 /* the attributes are owned by the layout.
375 re-add the attributes to the layout after changing the
376 start and end index */
377 attrlist = pango_layout_get_attributes(t->font->layout);
378 pango_attr_list_ref(attrlist);
379 pango_layout_set_attributes(t->font->layout, attrlist);
380 pango_attr_list_unref(attrlist);
381 }
382 }
This page took 0.044394 seconds and 4 git commands to generate.