]> Dogcows Code - chaz/openbox/blob - render/font.c
pango is now mandatory..
[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 *openfont(const RrInstance *inst, gchar *fontstring)
79 {
80 /* This function is called for each font in the theme file. */
81 /* It returns a pointer to a RrFont struct after filling it. */
82 RrFont *out;
83 FcPattern *pat;
84 gint tint;
85 gchar *sval;
86 gint ival;
87
88 if (!(pat = XftNameParse(fontstring)))
89 return NULL;
90
91 out = g_new(RrFont, 1);
92 out->inst = inst;
93 out->font_desc = pango_font_description_new();
94 out->layout = pango_layout_new(inst->pango);
95
96 /* get the data from the parsed xft string */
97
98 /* get the family */
99 if (FcPatternGetString(pat, "family", 0,
100 (FcChar8**)&sval) == FcResultMatch)
101 pango_font_description_set_family(out->font_desc, sval);
102
103 /* get the weight */
104 if (FcPatternGetInteger(pat, "weight", 0, &ival) == FcResultMatch) {
105 if (ival == FC_WEIGHT_LIGHT)
106 pango_font_description_set_weight(out->font_desc,
107 PANGO_WEIGHT_LIGHT);
108 else if (ival == FC_WEIGHT_DEMIBOLD)
109 pango_font_description_set_weight(out->font_desc,
110 PANGO_WEIGHT_SEMIBOLD);
111 else if (ival == FC_WEIGHT_BOLD)
112 pango_font_description_set_weight(out->font_desc,
113 PANGO_WEIGHT_BOLD);
114 else if (ival == FC_WEIGHT_BLACK)
115 pango_font_description_set_weight(out->font_desc,
116 PANGO_WEIGHT_ULTRABOLD);
117 }
118
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
135 if (FcPatternGetBool(pat, OB_SHADOW, 0, &out->shadow) != FcResultMatch)
136 out->shadow = FALSE;
137
138 if (FcPatternGetInteger(pat, OB_SHADOW_OFFSET, 0, &out->offset) !=
139 FcResultMatch)
140 out->offset = 1;
141
142 if (FcPatternGetInteger(pat, OB_SHADOW_ALPHA, 0, &tint) != FcResultMatch)
143 tint = 25;
144 if (tint > 100) tint = 100;
145 else if (tint < -100) tint = -100;
146 out->tint = tint;
147
148 /* setup the layout */
149 pango_layout_set_font_description(out->layout, out->font_desc);
150 pango_layout_set_single_paragraph_mode(out->layout, TRUE);
151 pango_layout_set_ellipsize(out->layout, PANGO_ELLIPSIZE_MIDDLE);
152
153 /* get the ascent and descent */
154 measure_font(inst, out);
155
156 return out;
157 }
158
159 RrFont *RrFontOpen(const RrInstance *inst, gchar *fontstring)
160 {
161 RrFont *out;
162
163 if (!started) {
164 font_startup();
165 started = TRUE;
166 }
167
168 if ((out = openfont(inst, fontstring)))
169 return out;
170 g_warning(_("Unable to load font: %s\n"), fontstring);
171 g_warning(_("Trying fallback font: %s\n"), "sans");
172
173 if ((out = openfont(inst, "sans")))
174 return out;
175 g_warning(_("Unable to load font: %s\n"), "sans");
176
177 return NULL;
178 }
179
180 void RrFontClose(RrFont *f)
181 {
182 if (f) {
183 g_object_unref(f->layout);
184 pango_font_description_free(f->font_desc);
185 g_free(f);
186 }
187 }
188
189 static void font_measure_full(const RrFont *f, const gchar *str,
190 gint *x, gint *y)
191 {
192 PangoRectangle rect;
193
194 pango_layout_set_text(f->layout, str, -1);
195 pango_layout_set_width(f->layout, -1);
196 pango_layout_get_pixel_extents(f->layout, NULL, &rect);
197 *x = rect.width + (f->shadow ? ABS(f->offset) : 0);
198 *y = rect.height + (f->shadow ? ABS(f->offset) : 0);
199 }
200
201 RrSize *RrFontMeasureString(const RrFont *f, const gchar *str)
202 {
203 RrSize *size;
204 size = g_new(RrSize, 1);
205 font_measure_full(f, str, &size->width, &size->height);
206 return size;
207 }
208
209 gint RrFontHeight(const RrFont *f)
210 {
211 return (f->ascent + f->descent) / PANGO_SCALE +
212 (f->shadow ? f->offset : 0);
213 }
214
215 static inline int font_calculate_baseline(RrFont *f, gint height)
216 {
217 /* For my own reference:
218 * _________
219 * ^space/2 ^height ^baseline
220 * v_________|_ |
221 * | ^ascent | _ _
222 * | | | | |_ _____ _| |_ _ _
223 * | | | | _/ -_) \ / _| || |
224 * | v_________v \__\___/_\_\\__|\_, |
225 * | ^descent |__/
226 * __________|_v
227 * ^space/2 |
228 * V_________v
229 */
230 return (((height * PANGO_SCALE) /* height of the space in pango units */
231 - (f->ascent + f->descent)) /* minus space taken up by text */
232 / 2 /* divided by two -> half of the empty space (this is the top
233 of the text) */
234 + f->ascent) /* now move down to the baseline */
235 / PANGO_SCALE; /* back to pixels */
236 }
237
238 void RrFontDraw(XftDraw *d, RrTextureText *t, RrRect *area)
239 {
240 gint x,y,w,h;
241 XftColor c;
242 gint mw;
243 PangoRectangle rect;
244
245 /* center the text vertically
246 We do this centering based on the 'baseline' since different fonts have
247 different top edges. It looks bad when the whole string is moved when 1
248 character from a non-default language is included in the string */
249 y = area->y +
250 font_calculate_baseline(t->font, area->height);
251
252 /* the +2 and -4 leave a small blank edge on the sides */
253 x = area->x + 2;
254 w = area->width - 4;
255 h = area->height;
256
257 pango_layout_set_text(t->font->layout, t->string, -1);
258 pango_layout_set_width(t->font->layout, w * PANGO_SCALE);
259
260 pango_layout_get_pixel_extents(t->font->layout, NULL, &rect);
261 mw = rect.width;
262
263 /* pango_layout_set_alignment doesn't work with
264 pango_xft_render_layout_line */
265 switch (t->justify) {
266 case RR_JUSTIFY_LEFT:
267 break;
268 case RR_JUSTIFY_RIGHT:
269 x += (w - mw);
270 break;
271 case RR_JUSTIFY_CENTER:
272 x += (w - mw) / 2;
273 break;
274 }
275
276 if (t->font->shadow) {
277 if (t->font->tint >= 0) {
278 c.color.red = 0;
279 c.color.green = 0;
280 c.color.blue = 0;
281 c.color.alpha = 0xffff * t->font->tint / 100;
282 c.pixel = BlackPixel(RrDisplay(t->font->inst),
283 RrScreen(t->font->inst));
284 } else {
285 c.color.red = 0xffff;
286 c.color.green = 0xffff;
287 c.color.blue = 0xffff;
288 c.color.alpha = 0xffff * -t->font->tint / 100;
289 c.pixel = WhitePixel(RrDisplay(t->font->inst),
290 RrScreen(t->font->inst));
291 }
292 /* see below... */
293 pango_xft_render_layout_line
294 (d, &c, pango_layout_get_line(t->font->layout, 0),
295 (x + t->font->offset) * PANGO_SCALE,
296 (y + t->font->offset) * PANGO_SCALE);
297 }
298
299 c.color.red = t->color->r | t->color->r << 8;
300 c.color.green = t->color->g | t->color->g << 8;
301 c.color.blue = t->color->b | t->color->b << 8;
302 c.color.alpha = 0xff | 0xff << 8; /* fully opaque text */
303 c.pixel = t->color->pixel;
304
305 /* layout_line() uses y to specify the baseline
306 The line doesn't need to be freed, it's a part of the layout */
307 pango_xft_render_layout_line
308 (d, &c, pango_layout_get_line(t->font->layout, 0),
309 x * PANGO_SCALE, y * PANGO_SCALE);
310 }
This page took 0.046737 seconds and 5 git commands to generate.