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