1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
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
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.
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.
18 See the COPYING file for a copy of the GNU General Public License.
34 #define OB_SHADOW "shadow"
35 #define OB_SHADOW_OFFSET "shadowoffset"
36 #define OB_SHADOW_ALPHA "shadowtint"
38 FcObjectType objs
[] = {
39 { OB_SHADOW
, FcTypeBool
},
40 { OB_SHADOW_OFFSET
, FcTypeInteger
},
41 { OB_SHADOW_ALPHA
, FcTypeInteger
}
44 static gboolean started
= FALSE
;
46 static void font_startup(void)
49 g_warning(_("Couldn't initialize Xft."));
53 /* Here we are teaching xft about the shadow, shadowoffset & shadowtint */
54 FcNameRegisterObjectTypes(objs
, (sizeof(objs
) / sizeof(objs
[0])));
57 static void measure_font(const RrInstance
*inst
, RrFont
*f
)
59 PangoFontMetrics
*metrics
;
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 @ */
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
);
78 static RrFont
*openfontstring(const RrInstance
*inst
, gchar
*fontstring
)
86 if (!(pat
= XftNameParse(fontstring
)))
89 out
= g_new(RrFont
, 1);
92 out
->font_desc
= pango_font_description_new();
93 out
->layout
= pango_layout_new(inst
->pango
);
95 /* get the data from the parsed xft string */
98 if (FcPatternGetString(pat
, "family", 0,
99 (FcChar8
**)&sval
) == FcResultMatch
)
100 pango_font_description_set_family(out
->font_desc
, sval
);
103 if (FcPatternGetInteger(pat
, "weight", 0, &ival
) == FcResultMatch
) {
104 if (ival
== FC_WEIGHT_LIGHT
)
105 pango_font_description_set_weight(out
->font_desc
,
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
,
113 else if (ival
== FC_WEIGHT_BLACK
)
114 pango_font_description_set_weight(out
->font_desc
,
115 PANGO_WEIGHT_ULTRABOLD
);
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
,
123 else if (ival
== FC_SLANT_OBLIQUE
)
124 pango_font_description_set_style(out
->font_desc
,
125 PANGO_STYLE_OBLIQUE
);
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
,
135 pango_font_description_set_size(out
->font_desc
, 8 * PANGO_SCALE
);
137 if (FcPatternGetBool(pat
, OB_SHADOW
, 0, &out
->shadow
) != FcResultMatch
)
140 if (FcPatternGetInteger(pat
, OB_SHADOW_OFFSET
, 0, &out
->offset
) !=
144 if (FcPatternGetInteger(pat
, OB_SHADOW_ALPHA
, 0, &tint
) != FcResultMatch
)
146 if (tint
> 100) tint
= 100;
147 else if (tint
< -100) tint
= -100;
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
);
155 /* get the ascent and descent */
156 measure_font(inst
, out
);
158 FcPatternDestroy(pat
);
163 RrFont
*RrFontOpenByString(const RrInstance
*inst
, gchar
*fontstring
)
172 if ((out
= openfontstring(inst
, fontstring
)))
174 g_warning(_("Unable to load font: %s\n"), fontstring
);
175 g_warning(_("Trying fallback font: %s\n"), "sans");
177 if ((out
= openfontstring(inst
, "sans")))
179 g_warning(_("Unable to load font: %s\n"), "sans");
184 RrFont
*RrFontOpen(const RrInstance
*inst
, gchar
*name
, gint size
,
185 RrFontWeight weight
, RrFontSlant slant
, gboolean shadow
,
186 gint shadowoffset
, gchar shadowtint
)
197 g_assert(shadowtint
<= 100 && shadowtint
>= -100);
199 out
= g_new(RrFont
, 1);
202 out
->font_desc
= pango_font_description_new();
203 out
->layout
= pango_layout_new(inst
->pango
);
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();
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();
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
);
227 /* setup the shadow */
228 out
->shadow
= shadow
;
229 out
->offset
= shadowoffset
;
230 out
->tint
= shadowtint
;
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
);
237 /* get the ascent and descent */
238 measure_font(inst
, out
);
243 RrFont
*RrFontOpenDefault(const RrInstance
*inst
)
245 return RrFontOpen(inst
, RrDefaultFontFamily
, RrDefaultFontSize
,
246 RrDefaultFontWeight
, RrDefaultFontSlant
,
247 RrDefaultFontShadow
, RrDefaultFontShadowOffset
,
248 RrDefaultFontShadowTint
);
251 void RrFontRef(RrFont
*f
)
256 void RrFontClose(RrFont
*f
)
260 g_object_unref(f
->layout
);
261 pango_font_description_free(f
->font_desc
);
267 static void font_measure_full(const RrFont
*f
, const gchar
*str
,
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);
279 RrSize
*RrFontMeasureString(const RrFont
*f
, const gchar
*str
)
282 size
= g_new(RrSize
, 1);
283 font_measure_full(f
, str
, &size
->width
, &size
->height
);
287 gint
RrFontHeight(const RrFont
*f
)
289 return (f
->ascent
+ f
->descent
) / PANGO_SCALE
+
290 (f
->shadow
? f
->offset
: 0);
293 static inline int font_calculate_baseline(RrFont
*f
, gint height
)
295 /* For my own reference:
297 * ^space/2 ^height ^baseline
300 * | | | | |_ _____ _| |_ _ _
301 * | | | | _/ -_) \ / _| || |
302 * | v_________v \__\___/_\_\\__|\_, |
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
312 + f
->ascent
) /* now move down to the baseline */
313 / PANGO_SCALE
; /* back to pixels */
316 void RrFontDraw(XftDraw
*d
, RrTextureText
*t
, RrRect
*area
)
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 */
328 font_calculate_baseline(t
->font
, area
->height
);
330 /* the +2 and -4 leave a small blank edge on the sides */
335 pango_layout_set_text(t
->font
->layout
, t
->string
, -1);
336 pango_layout_set_width(t
->font
->layout
, w
* PANGO_SCALE
);
338 pango_layout_get_pixel_extents(t
->font
->layout
, NULL
, &rect
);
341 /* pango_layout_set_alignment doesn't work with
342 pango_xft_render_layout_line */
343 switch (t
->justify
) {
344 case RR_JUSTIFY_LEFT
:
346 case RR_JUSTIFY_RIGHT
:
349 case RR_JUSTIFY_CENTER
:
354 if (t
->font
->shadow
) {
355 if (t
->font
->tint
>= 0) {
359 c
.color
.alpha
= 0xffff * t
->font
->tint
/ 100;
360 c
.pixel
= BlackPixel(RrDisplay(t
->font
->inst
),
361 RrScreen(t
->font
->inst
));
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
));
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
);
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
;
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
);