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