]> Dogcows Code - chaz/openbox/blob - render/font.c
583c9f7d7af277b2986429a4036fabc38500f907
[chaz/openbox] / render / 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_new(RrFont, 1);
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_free(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_new(RrSize, 1);
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,h;
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 }
262 }
263
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);
268
269 /* * * end of setting up the layout * * */
270
271 pango_layout_get_pixel_extents(t->font->layout, NULL, &rect);
272 mw = rect.width;
273
274 /* pango_layout_set_alignment doesn't work with
275 pango_xft_render_layout_line */
276 switch (t->justify) {
277 case RR_JUSTIFY_LEFT:
278 break;
279 case RR_JUSTIFY_RIGHT:
280 x += (w - mw);
281 break;
282 case RR_JUSTIFY_CENTER:
283 x += (w - mw) / 2;
284 break;
285 }
286
287 if (t->shadow_offset_x || t->shadow_offset_y) {
288 /* From nvidia's readme (chapter 23):
289
290 When rendering to a 32-bit window, keep in mind that the X RENDER
291 extension, used by most composite managers, expects "premultiplied
292 alpha" colors. This means that if your color has components (r,g,b)
293 and alpha value a, then you must render (a*r, a*g, a*b, a) into the
294 target window.
295 */
296 c.color.red = (t->shadow_color->r | t->shadow_color->r << 8) *
297 t->shadow_alpha / 255;
298 c.color.green = (t->shadow_color->g | t->shadow_color->g << 8) *
299 t->shadow_alpha / 255;
300 c.color.blue = (t->shadow_color->b | t->shadow_color->b << 8) *
301 t->shadow_alpha / 255;
302 c.color.alpha = 0xffff * t->shadow_alpha / 255;
303 c.pixel = t->shadow_color->pixel;
304
305 /* see below... */
306 if (!t->flow) {
307 pango_xft_render_layout_line
308 (d, &c,
309 #if PANGO_VERSION_MAJOR > 1 || \
310 (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16)
311 pango_layout_get_line_readonly(t->font->layout, 0),
312 #else
313 pango_layout_get_line(t->font->layout, 0),
314 #endif
315 (x + t->shadow_offset_x) * PANGO_SCALE,
316 (y + t->shadow_offset_y) * PANGO_SCALE);
317 }
318 else {
319 pango_xft_render_layout(d, &c, t->font->layout,
320 (x + t->shadow_offset_x) * PANGO_SCALE,
321 (y + t->shadow_offset_y) * PANGO_SCALE);
322 }
323 }
324
325 c.color.red = t->color->r | t->color->r << 8;
326 c.color.green = t->color->g | t->color->g << 8;
327 c.color.blue = t->color->b | t->color->b << 8;
328 c.color.alpha = 0xff | 0xff << 8; /* fully opaque text */
329 c.pixel = t->color->pixel;
330
331 if (t->shortcut) {
332 const gchar *s = t->string + t->shortcut_pos;
333
334 t->font->shortcut_underline->start_index = t->shortcut_pos;
335 t->font->shortcut_underline->end_index = t->shortcut_pos +
336 (g_utf8_next_char(s) - s);
337
338 /* the attributes are owned by the layout.
339 re-add the attributes to the layout after changing the
340 start and end index */
341 attrlist = pango_layout_get_attributes(t->font->layout);
342 pango_attr_list_ref(attrlist);
343 pango_layout_set_attributes(t->font->layout, attrlist);
344 pango_attr_list_unref(attrlist);
345 }
346
347 /* layout_line() uses y to specify the baseline
348 The line doesn't need to be freed, it's a part of the layout */
349 if (!t->flow) {
350 pango_xft_render_layout_line
351 (d, &c,
352 #if PANGO_VERSION_MAJOR > 1 || \
353 (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16)
354 pango_layout_get_line_readonly(t->font->layout, 0),
355 #else
356 pango_layout_get_line(t->font->layout, 0),
357 #endif
358 x * PANGO_SCALE,
359 y * PANGO_SCALE);
360 }
361 else {
362 pango_xft_render_layout(d, &c, t->font->layout,
363 x * PANGO_SCALE,
364 y * PANGO_SCALE);
365 }
366
367 if (t->shortcut) {
368 t->font->shortcut_underline->start_index = 0;
369 t->font->shortcut_underline->end_index = 0;
370 /* the attributes are owned by the layout.
371 re-add the attributes to the layout after changing the
372 start and end index */
373 attrlist = pango_layout_get_attributes(t->font->layout);
374 pango_attr_list_ref(attrlist);
375 pango_layout_set_attributes(t->font->layout, attrlist);
376 pango_attr_list_unref(attrlist);
377 }
378 }
This page took 0.047544 seconds and 3 git commands to generate.