]> icculus.org git repositories - dana/openbox.git/blob - render/font.c
mem leaks
[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-2007   Dana 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 static void measure_font(const RrInstance *inst, RrFont *f)
35 {
36     PangoFontMetrics *metrics;
37
38     /* measure the ascent and descent */
39     metrics = pango_context_get_metrics(inst->pango, f->font_desc,
40                                         pango_language_get_default());
41     f->ascent = pango_font_metrics_get_ascent(metrics);
42     f->descent = pango_font_metrics_get_descent(metrics);
43     pango_font_metrics_unref(metrics);
44 }
45
46 RrFont *RrFontOpen(const RrInstance *inst, const gchar *name, gint size,
47                    RrFontWeight weight, RrFontSlant slant)
48 {
49     RrFont *out;
50     PangoWeight pweight;
51     PangoStyle pstyle;
52     PangoAttrList *attrlist;
53
54     out = g_new(RrFont, 1);
55     out->inst = inst;
56     out->ref = 1;
57     out->font_desc = pango_font_description_new();
58     out->layout = pango_layout_new(inst->pango);
59     out->shortcut_underline = pango_attr_underline_new(PANGO_UNDERLINE_LOW);
60     out->shortcut_underline->start_index = 0;
61     out->shortcut_underline->end_index = 0;
62
63     attrlist = pango_attr_list_new();
64     /* shortcut_underline is owned by the attrlist */
65     pango_attr_list_insert(attrlist, out->shortcut_underline);
66     /* the attributes are owned by the layout */
67     pango_layout_set_attributes(out->layout, attrlist);
68     pango_attr_list_unref(attrlist);
69
70     switch (weight) {
71     case RR_FONTWEIGHT_LIGHT:     pweight = PANGO_WEIGHT_LIGHT;     break;
72     case RR_FONTWEIGHT_NORMAL:    pweight = PANGO_WEIGHT_NORMAL;    break;
73     case RR_FONTWEIGHT_SEMIBOLD:  pweight = PANGO_WEIGHT_SEMIBOLD;  break;
74     case RR_FONTWEIGHT_BOLD:      pweight = PANGO_WEIGHT_BOLD;      break;
75     case RR_FONTWEIGHT_ULTRABOLD: pweight = PANGO_WEIGHT_ULTRABOLD; break;
76     default: g_assert_not_reached();
77     }
78
79     switch (slant) {
80     case RR_FONTSLANT_NORMAL:  pstyle = PANGO_STYLE_NORMAL;    break;
81     case RR_FONTSLANT_ITALIC:  pstyle = PANGO_STYLE_ITALIC;    break;
82     case RR_FONTSLANT_OBLIQUE: pstyle = PANGO_STYLE_OBLIQUE;   break;
83     default: g_assert_not_reached();
84     }
85
86     /* setup the font */
87     pango_font_description_set_family(out->font_desc, name);
88     pango_font_description_set_weight(out->font_desc, pweight);
89     pango_font_description_set_style(out->font_desc, pstyle);
90     pango_font_description_set_size(out->font_desc, size * PANGO_SCALE);
91
92     /* setup the layout */
93     pango_layout_set_font_description(out->layout, out->font_desc);
94     pango_layout_set_single_paragraph_mode(out->layout, TRUE);
95     pango_layout_set_ellipsize(out->layout, PANGO_ELLIPSIZE_MIDDLE);
96
97     /* get the ascent and descent */
98     measure_font(inst, out);
99
100     return out;
101 }
102
103 RrFont *RrFontOpenDefault(const RrInstance *inst)
104 {
105     return RrFontOpen(inst, RrDefaultFontFamily, RrDefaultFontSize,
106                       RrDefaultFontWeight, RrDefaultFontSlant);
107 }
108
109 void RrFontRef(RrFont *f)
110 {
111     ++f->ref;
112 }
113
114 void RrFontClose(RrFont *f)
115 {
116     if (f) {
117         if (--f->ref < 1) {
118             g_object_unref(f->layout);
119             pango_font_description_free(f->font_desc);
120             g_free(f);
121         }
122     }
123 }
124
125 static void font_measure_full(const RrFont *f, const gchar *str,
126                               gint *x, gint *y, gint shadow_x, gint shadow_y)
127 {
128     PangoRectangle rect;
129
130     pango_layout_set_text(f->layout, str, -1);
131     pango_layout_set_width(f->layout, -1);
132     pango_layout_get_pixel_extents(f->layout, NULL, &rect);
133     *x = rect.width + ABS(shadow_x) + 4 /* we put a 2 px edge on each side */;
134     *y = rect.height + ABS(shadow_y);
135 }
136
137 RrSize *RrFontMeasureString(const RrFont *f, const gchar *str,
138                             gint shadow_x, gint shadow_y)
139 {
140     RrSize *size;
141     size = g_new(RrSize, 1);
142     font_measure_full(f, str, &size->width, &size->height, shadow_x, shadow_y);
143     return size;
144 }
145
146 gint RrFontHeight(const RrFont *f, gint shadow_y)
147 {
148     return (f->ascent + f->descent) / PANGO_SCALE + ABS(shadow_y);
149 }
150
151 static inline int font_calculate_baseline(RrFont *f, gint height)
152 {
153 /* For my own reference:
154  *   _________
155  *  ^space/2  ^height     ^baseline
156  *  v_________|_          |
157  *            | ^ascent   |   _           _
158  *            | |         |  | |_ _____ _| |_ _  _
159  *            | |         |  |  _/ -_) \ /  _| || |
160  *            | v_________v   \__\___/_\_\\__|\_, |
161  *            | ^descent                      |__/
162  *  __________|_v
163  *  ^space/2  |
164  *  V_________v
165  */
166     return (((height * PANGO_SCALE) /* height of the space in pango units */
167              - (f->ascent + f->descent)) /* minus space taken up by text */
168             / 2 /* divided by two -> half of the empty space (this is the top
169                    of the text) */
170             + f->ascent) /* now move down to the baseline */
171         / PANGO_SCALE; /* back to pixels */
172 }
173
174 void RrFontDraw(XftDraw *d, RrTextureText *t, RrRect *area)
175 {
176     gint x,y,w,h;
177     XftColor c;
178     gint mw;
179     PangoRectangle rect;
180     PangoAttrList *attrlist;
181
182     /* center the text vertically
183        We do this centering based on the 'baseline' since different fonts have
184        different top edges. It looks bad when the whole string is moved when 1
185        character from a non-default language is included in the string */
186     y = area->y +
187         font_calculate_baseline(t->font, area->height);
188
189     /* the +2 and -4 leave a small blank edge on the sides */
190     x = area->x + 2;
191     w = area->width - 4;
192     h = area->height;
193
194     pango_layout_set_text(t->font->layout, t->string, -1);
195     pango_layout_set_width(t->font->layout, w * PANGO_SCALE);
196
197     /* * * end of setting up the layout * * */
198
199     pango_layout_get_pixel_extents(t->font->layout, NULL, &rect);
200     mw = rect.width;
201
202     /* pango_layout_set_alignment doesn't work with 
203        pango_xft_render_layout_line */
204     switch (t->justify) {
205     case RR_JUSTIFY_LEFT:
206         break;
207     case RR_JUSTIFY_RIGHT:
208         x += (w - mw);
209         break;
210     case RR_JUSTIFY_CENTER:
211         x += (w - mw) / 2;
212         break;
213     }
214
215     if (t->shadow_offset_x || t->shadow_offset_y) {
216         c.color.red = t->shadow_color->r | t->shadow_color->r << 8;
217         c.color.green = t->shadow_color->g | t->shadow_color->g << 8;
218         c.color.blue = t->shadow_color->b | t->shadow_color->b << 8;
219         c.color.alpha = 0xffff * t->shadow_alpha / 255;
220         c.pixel = t->shadow_color->pixel;
221
222         /* see below... */
223         pango_xft_render_layout_line
224             (d, &c, pango_layout_get_line(t->font->layout, 0),
225              (x + t->shadow_offset_x) * PANGO_SCALE,
226              (y + t->shadow_offset_y) * PANGO_SCALE);
227     }
228
229     c.color.red = t->color->r | t->color->r << 8;
230     c.color.green = t->color->g | t->color->g << 8;
231     c.color.blue = t->color->b | t->color->b << 8;
232     c.color.alpha = 0xff | 0xff << 8; /* fully opaque text */
233     c.pixel = t->color->pixel;
234
235     if (t->shortcut) {
236         const gchar *c = t->string + t->shortcut_pos;
237
238         t->font->shortcut_underline->start_index = t->shortcut_pos;
239         t->font->shortcut_underline->end_index = t->shortcut_pos +
240             (g_utf8_next_char(c) - c);
241
242         /* the attributes are owned by the layout.
243            re-add the attributes to the layout after changing the
244            start and end index */
245         attrlist = pango_layout_get_attributes(t->font->layout);
246         pango_attr_list_ref(attrlist);
247         pango_layout_set_attributes(t->font->layout, attrlist);
248         pango_attr_list_unref(attrlist);
249     }
250
251     /* layout_line() uses y to specify the baseline
252        The line doesn't need to be freed, it's a part of the layout */
253     pango_xft_render_layout_line
254         (d, &c, pango_layout_get_line(t->font->layout, 0),
255          x * PANGO_SCALE, y * PANGO_SCALE);
256
257     if (t->shortcut) {
258         t->font->shortcut_underline->start_index = 0;
259         t->font->shortcut_underline->end_index = 0;
260         /* the attributes are owned by the layout.
261            re-add the attributes to the layout after changing the
262            start and end index */
263         attrlist = pango_layout_get_attributes(t->font->layout);
264         pango_attr_list_ref(attrlist);
265         pango_layout_set_attributes(t->font->layout, attrlist);
266         pango_attr_list_unref(attrlist);
267     }
268 }