]> Dogcows Code - chaz/openbox/blob - render/font.c
fonts are no longer loaded from the theme file. instead, they are created by the...
[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 Ben 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 #define OB_SHADOW "shadow"
35 #define OB_SHADOW_OFFSET "shadowoffset"
36 #define OB_SHADOW_ALPHA "shadowtint"
37
38 FcObjectType objs[] = {
39 { OB_SHADOW, FcTypeBool },
40 { OB_SHADOW_OFFSET, FcTypeInteger },
41 { OB_SHADOW_ALPHA, FcTypeInteger }
42 };
43
44 static gboolean started = FALSE;
45
46 static void font_startup(void)
47 {
48 if (!XftInit(0)) {
49 g_warning(_("Couldn't initialize Xft."));
50 exit(EXIT_FAILURE);
51 }
52
53 /* Here we are teaching xft about the shadow, shadowoffset & shadowtint */
54 FcNameRegisterObjectTypes(objs, (sizeof(objs) / sizeof(objs[0])));
55 }
56
57 static void measure_font(const RrInstance *inst, RrFont *f)
58 {
59 PangoFontMetrics *metrics;
60 gchar *locale, *p;
61
62 /* get the default language from the locale
63 (based on gtk_get_default_language in gtkmain.c) */
64 locale = g_strdup(setlocale(LC_CTYPE, NULL));
65 if ((p = strchr(locale, '.'))) *p = '\0'; /* strip off the . */
66 if ((p = strchr(locale, '@'))) *p = '\0'; /* strip off the @ */
67
68 /* measure the ascent and descent */
69 metrics = pango_context_get_metrics(inst->pango, f->font_desc,
70 pango_language_from_string(locale));
71 f->ascent = pango_font_metrics_get_ascent(metrics);
72 f->descent = pango_font_metrics_get_descent(metrics);
73 pango_font_metrics_unref(metrics);
74
75 g_free(locale);
76 }
77
78 static RrFont *openfontstring(const RrInstance *inst, gchar *fontstring)
79 {
80 RrFont *out;
81 FcPattern *pat;
82 gint tint;
83 gchar *sval;
84 gint ival;
85
86 if (!(pat = XftNameParse(fontstring)))
87 return NULL;
88
89 out = g_new(RrFont, 1);
90 out->inst = inst;
91 out->ref = 1;
92 out->font_desc = pango_font_description_new();
93 out->layout = pango_layout_new(inst->pango);
94
95 /* get the data from the parsed xft string */
96
97 /* get the family */
98 if (FcPatternGetString(pat, "family", 0,
99 (FcChar8**)&sval) == FcResultMatch)
100 pango_font_description_set_family(out->font_desc, sval);
101
102 /* get the weight */
103 if (FcPatternGetInteger(pat, "weight", 0, &ival) == FcResultMatch) {
104 if (ival == FC_WEIGHT_LIGHT)
105 pango_font_description_set_weight(out->font_desc,
106 PANGO_WEIGHT_LIGHT);
107 else if (ival == FC_WEIGHT_DEMIBOLD)
108 pango_font_description_set_weight(out->font_desc,
109 PANGO_WEIGHT_SEMIBOLD);
110 else if (ival == FC_WEIGHT_BOLD)
111 pango_font_description_set_weight(out->font_desc,
112 PANGO_WEIGHT_BOLD);
113 else if (ival == FC_WEIGHT_BLACK)
114 pango_font_description_set_weight(out->font_desc,
115 PANGO_WEIGHT_ULTRABOLD);
116 }
117
118 /* get the style/slant */
119 if (FcPatternGetInteger(pat, "slant", 0, &ival) == FcResultMatch) {
120 if (ival == FC_SLANT_ITALIC)
121 pango_font_description_set_style(out->font_desc,
122 PANGO_STYLE_ITALIC);
123 else if (ival == FC_SLANT_OBLIQUE)
124 pango_font_description_set_style(out->font_desc,
125 PANGO_STYLE_OBLIQUE);
126 }
127
128 /* get the size */
129 if (FcPatternGetInteger(pat, "size", 0, &ival) == FcResultMatch)
130 pango_font_description_set_size(out->font_desc, ival * PANGO_SCALE);
131 else if (FcPatternGetInteger(pat, "pixelsize", 0, &ival) == FcResultMatch)
132 pango_font_description_set_absolute_size(out->font_desc,
133 ival * PANGO_SCALE);
134 else
135 pango_font_description_set_size(out->font_desc, 8 * PANGO_SCALE);
136
137 if (FcPatternGetBool(pat, OB_SHADOW, 0, &out->shadow) != FcResultMatch)
138 out->shadow = FALSE;
139
140 if (FcPatternGetInteger(pat, OB_SHADOW_OFFSET, 0, &out->offset) !=
141 FcResultMatch)
142 out->offset = 1;
143
144 if (FcPatternGetInteger(pat, OB_SHADOW_ALPHA, 0, &tint) != FcResultMatch)
145 tint = 25;
146 if (tint > 100) tint = 100;
147 else if (tint < -100) tint = -100;
148 out->tint = tint;
149
150 /* setup the layout */
151 pango_layout_set_font_description(out->layout, out->font_desc);
152 pango_layout_set_single_paragraph_mode(out->layout, TRUE);
153 pango_layout_set_ellipsize(out->layout, PANGO_ELLIPSIZE_MIDDLE);
154
155 /* get the ascent and descent */
156 measure_font(inst, out);
157
158 FcPatternDestroy(pat);
159
160 return out;
161 }
162
163 RrFont *RrFontOpenByString(const RrInstance *inst, gchar *fontstring)
164 {
165 RrFont *out;
166
167 if (!started) {
168 font_startup();
169 started = TRUE;
170 }
171
172 if ((out = openfontstring(inst, fontstring)))
173 return out;
174 g_warning(_("Unable to load font: %s\n"), fontstring);
175 g_warning(_("Trying fallback font: %s\n"), "sans");
176
177 if ((out = openfontstring(inst, "sans")))
178 return out;
179 g_warning(_("Unable to load font: %s\n"), "sans");
180
181 return NULL;
182 }
183
184 RrFont *RrFontOpen(const RrInstance *inst, gchar *name, gint size,
185 RrFontWeight weight, RrFontSlant slant, gboolean shadow,
186 gint shadowoffset, gchar shadowtint)
187 {
188 RrFont *out;
189 PangoWeight pweight;
190 PangoStyle pstyle;
191
192 if (!started) {
193 font_startup();
194 started = TRUE;
195 }
196
197 g_assert(shadowtint <= 100 && shadowtint >= -100);
198
199 out = g_new(RrFont, 1);
200 out->inst = inst;
201 out->ref = 1;
202 out->font_desc = pango_font_description_new();
203 out->layout = pango_layout_new(inst->pango);
204
205 switch (weight) {
206 case RR_FONTWEIGHT_LIGHT: pweight = PANGO_WEIGHT_LIGHT; break;
207 case RR_FONTWEIGHT_NORMAL: pweight = PANGO_WEIGHT_NORMAL; break;
208 case RR_FONTWEIGHT_SEMIBOLD: pweight = PANGO_WEIGHT_SEMIBOLD; break;
209 case RR_FONTWEIGHT_BOLD: pweight = PANGO_WEIGHT_BOLD; break;
210 case RR_FONTWEIGHT_ULTRABOLD: pweight = PANGO_WEIGHT_ULTRABOLD; break;
211 default: g_assert_not_reached();
212 }
213
214 switch (slant) {
215 case RR_FONTSLANT_NORMAL: pstyle = PANGO_STYLE_NORMAL; break;
216 case RR_FONTSLANT_ITALIC: pstyle = PANGO_STYLE_ITALIC; break;
217 case RR_FONTSLANT_OBLIQUE: pstyle = PANGO_STYLE_OBLIQUE; break;
218 default: g_assert_not_reached();
219 }
220
221 /* setup the font */
222 pango_font_description_set_family(out->font_desc, name);
223 pango_font_description_set_weight(out->font_desc, pweight);
224 pango_font_description_set_style(out->font_desc, pstyle);
225 pango_font_description_set_size(out->font_desc, size * PANGO_SCALE);
226
227 /* setup the shadow */
228 out->shadow = shadow;
229 out->offset = shadowoffset;
230 out->tint = shadowtint;
231
232 /* setup the layout */
233 pango_layout_set_font_description(out->layout, out->font_desc);
234 pango_layout_set_single_paragraph_mode(out->layout, TRUE);
235 pango_layout_set_ellipsize(out->layout, PANGO_ELLIPSIZE_MIDDLE);
236
237 /* get the ascent and descent */
238 measure_font(inst, out);
239
240 return out;
241 }
242
243 RrFont *RrFontOpenDefault(const RrInstance *inst)
244 {
245 return RrFontOpen(inst, RrDefaultFontFamily, RrDefaultFontSize,
246 RrDefaultFontWeight, RrDefaultFontSlant,
247 RrDefaultFontShadow, RrDefaultFontShadowOffset,
248 RrDefaultFontShadowTint);
249 }
250
251 void RrFontRef(RrFont *f)
252 {
253 ++f->ref;
254 }
255
256 void RrFontClose(RrFont *f)
257 {
258 if (f) {
259 if (--f->ref < 1) {
260 g_object_unref(f->layout);
261 pango_font_description_free(f->font_desc);
262 g_free(f);
263 }
264 }
265 }
266
267 static void font_measure_full(const RrFont *f, const gchar *str,
268 gint *x, gint *y)
269 {
270 PangoRectangle rect;
271
272 pango_layout_set_text(f->layout, str, -1);
273 pango_layout_set_width(f->layout, -1);
274 pango_layout_get_pixel_extents(f->layout, NULL, &rect);
275 *x = rect.width + (f->shadow ? ABS(f->offset) : 0);
276 *y = rect.height + (f->shadow ? ABS(f->offset) : 0);
277 }
278
279 RrSize *RrFontMeasureString(const RrFont *f, const gchar *str)
280 {
281 RrSize *size;
282 size = g_new(RrSize, 1);
283 font_measure_full(f, str, &size->width, &size->height);
284 return size;
285 }
286
287 gint RrFontHeight(const RrFont *f)
288 {
289 return (f->ascent + f->descent) / PANGO_SCALE +
290 (f->shadow ? f->offset : 0);
291 }
292
293 static inline int font_calculate_baseline(RrFont *f, gint height)
294 {
295 /* For my own reference:
296 * _________
297 * ^space/2 ^height ^baseline
298 * v_________|_ |
299 * | ^ascent | _ _
300 * | | | | |_ _____ _| |_ _ _
301 * | | | | _/ -_) \ / _| || |
302 * | v_________v \__\___/_\_\\__|\_, |
303 * | ^descent |__/
304 * __________|_v
305 * ^space/2 |
306 * V_________v
307 */
308 return (((height * PANGO_SCALE) /* height of the space in pango units */
309 - (f->ascent + f->descent)) /* minus space taken up by text */
310 / 2 /* divided by two -> half of the empty space (this is the top
311 of the text) */
312 + f->ascent) /* now move down to the baseline */
313 / PANGO_SCALE; /* back to pixels */
314 }
315
316 void RrFontDraw(XftDraw *d, RrTextureText *t, RrRect *area)
317 {
318 gint x,y,w,h;
319 XftColor c;
320 gint mw;
321 PangoRectangle rect;
322
323 /* center the text vertically
324 We do this centering based on the 'baseline' since different fonts have
325 different top edges. It looks bad when the whole string is moved when 1
326 character from a non-default language is included in the string */
327 y = area->y +
328 font_calculate_baseline(t->font, area->height);
329
330 /* the +2 and -4 leave a small blank edge on the sides */
331 x = area->x + 2;
332 w = area->width - 4;
333 h = area->height;
334
335 pango_layout_set_text(t->font->layout, t->string, -1);
336 pango_layout_set_width(t->font->layout, w * PANGO_SCALE);
337
338 pango_layout_get_pixel_extents(t->font->layout, NULL, &rect);
339 mw = rect.width;
340
341 /* pango_layout_set_alignment doesn't work with
342 pango_xft_render_layout_line */
343 switch (t->justify) {
344 case RR_JUSTIFY_LEFT:
345 break;
346 case RR_JUSTIFY_RIGHT:
347 x += (w - mw);
348 break;
349 case RR_JUSTIFY_CENTER:
350 x += (w - mw) / 2;
351 break;
352 }
353
354 if (t->font->shadow) {
355 if (t->font->tint >= 0) {
356 c.color.red = 0;
357 c.color.green = 0;
358 c.color.blue = 0;
359 c.color.alpha = 0xffff * t->font->tint / 100;
360 c.pixel = BlackPixel(RrDisplay(t->font->inst),
361 RrScreen(t->font->inst));
362 } else {
363 c.color.red = 0xffff;
364 c.color.green = 0xffff;
365 c.color.blue = 0xffff;
366 c.color.alpha = 0xffff * -t->font->tint / 100;
367 c.pixel = WhitePixel(RrDisplay(t->font->inst),
368 RrScreen(t->font->inst));
369 }
370 /* see below... */
371 pango_xft_render_layout_line
372 (d, &c, pango_layout_get_line(t->font->layout, 0),
373 (x + t->font->offset) * PANGO_SCALE,
374 (y + t->font->offset) * PANGO_SCALE);
375 }
376
377 c.color.red = t->color->r | t->color->r << 8;
378 c.color.green = t->color->g | t->color->g << 8;
379 c.color.blue = t->color->b | t->color->b << 8;
380 c.color.alpha = 0xff | 0xff << 8; /* fully opaque text */
381 c.pixel = t->color->pixel;
382
383 /* layout_line() uses y to specify the baseline
384 The line doesn't need to be freed, it's a part of the layout */
385 pango_xft_render_layout_line
386 (d, &c, pango_layout_get_line(t->font->layout, 0),
387 x * PANGO_SCALE, y * PANGO_SCALE);
388 }
This page took 0.049707 seconds and 5 git commands to generate.