2 * kmscon - Pango font backend
4 * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@googlemail.com>
5 * Copyright (c) 2011 University of Tuebingen
7 * Permission is hereby granted, free of charge, to any person obtaining
8 * a copy of this software and associated documentation files
9 * (the "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sublicense, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
15 * The above copyright notice and this permission notice shall be included
16 * in all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 * SECTION:font_pango.c
29 * @short_description: Pango font backend
32 * The pango backend uses pango and freetype2 to render glyphs into memory
33 * buffers. It uses a hashmap to cache all rendered glyphs of a single
34 * font-face. Therefore, rendering should be very fast. Also, when loading a
35 * glyph it pre-renders all common (mostly ASCII) characters, so it can measure
36 * the font and return a valid font hight/width.
38 * This is a _full_ font backend, that is, it provides every feature you expect
39 * from a font renderer. It does glyph substitution if a specific font face does
40 * not provide a requested glyph, it does correct font loading, it does
41 * italic/bold fonts correctly and more.
42 * However, this also means it pulls in a lot of dependencies including glib,
43 * pango, freetype2 and more.
48 #include <pango/pango.h>
49 #include <pango/pangoft2.h>
55 #include "shl_dlist.h"
56 #include "shl_hashtable.h"
58 #include "tsm_unicode.h"
59 #include "uterm_video.h"
61 #define LOG_SUBSYSTEM "font_pango"
65 struct shl_dlist list;
67 struct kmscon_font_attr attr;
68 struct kmscon_font_attr real_attr;
69 unsigned int baseline;
71 pthread_mutex_t glyph_lock;
72 struct shl_hashtable *glyphs;
75 static pthread_mutex_t manager_mutex = PTHREAD_MUTEX_INITIALIZER;
76 static unsigned long manager__refcnt;
77 static PangoFontMap *manager__lib;
78 static struct shl_dlist manager__list = SHL_DLIST_INIT(manager__list);
80 static void manager_lock()
82 pthread_mutex_lock(&manager_mutex);
85 static void manager_unlock()
87 pthread_mutex_unlock(&manager_mutex);
90 static int manager__ref()
92 if (!manager__refcnt++) {
93 manager__lib = pango_ft2_font_map_new();
95 log_warn("cannot create font map");
104 static void manager__unref()
106 if (!--manager__refcnt) {
107 g_object_unref(manager__lib);
112 static int get_glyph(struct face *face, struct kmscon_glyph **out,
113 uint32_t id, const uint32_t *ch, size_t len)
115 struct kmscon_glyph *glyph;
118 PangoLayoutLine *line;
128 cwidth = tsm_ucs4_get_width(*ch);
132 pthread_mutex_lock(&face->glyph_lock);
133 res = shl_hashtable_find(face->glyphs, (void**)&glyph,
135 pthread_mutex_unlock(&face->glyph_lock);
143 glyph = malloc(sizeof(*glyph));
145 log_error("cannot allocate memory for new glyph");
149 memset(glyph, 0, sizeof(*glyph));
150 glyph->width = cwidth;
152 layout = pango_layout_new(face->ctx);
154 /* render one line only */
155 pango_layout_set_height(layout, 0);
157 /* no line spacing */
158 pango_layout_set_spacing(layout, 0);
160 val = tsm_ucs4_to_utf8_alloc(ch, len, &ulen);
165 pango_layout_set_text(layout, val, ulen);
168 cnt = pango_layout_get_line_count(layout);
174 line = pango_layout_get_line_readonly(layout, 0);
176 pango_layout_line_get_pixel_extents(line, NULL, &rec);
177 glyph->buf.width = face->real_attr.width * cwidth;
178 glyph->buf.height = face->real_attr.height;
179 glyph->buf.stride = glyph->buf.width;
180 glyph->buf.format = UTERM_FORMAT_GREY;
182 if (!glyph->buf.width || !glyph->buf.height) {
187 glyph->buf.data = malloc(glyph->buf.height * glyph->buf.stride);
188 if (!glyph->buf.data) {
189 log_error("cannot allocate bitmap memory");
193 memset(glyph->buf.data, 0, glyph->buf.height * glyph->buf.stride);
195 bitmap.rows = glyph->buf.height;
196 bitmap.width = glyph->buf.width;
197 bitmap.pitch = glyph->buf.stride;
198 bitmap.num_grays = 256;
199 bitmap.pixel_mode = FT_PIXEL_MODE_GRAY;
200 bitmap.buffer = glyph->buf.data;
202 pango_ft2_render_layout_line(&bitmap, line, -rec.x, -rec.y);
204 pthread_mutex_lock(&face->glyph_lock);
205 ret = shl_hashtable_insert(face->glyphs, (void*)(long)id, glyph);
206 pthread_mutex_unlock(&face->glyph_lock);
208 log_error("cannot add glyph to hashtable");
216 free(glyph->buf.data);
220 g_object_unref(layout);
226 static void free_glyph(void *data)
228 struct kmscon_glyph *glyph = data;
230 free(glyph->buf.data);
234 static int manager_get_face(struct face **out, struct kmscon_font_attr *attr)
236 struct shl_dlist *iter;
237 struct face *face, *f;
238 PangoFontDescription *desc;
246 shl_dlist_for_each(iter, &manager__list) {
247 face = shl_dlist_entry(iter, struct face, list);
248 if (kmscon_font_attr_match(&face->attr, attr)) {
256 ret = manager__ref();
260 face = malloc(sizeof(*face));
262 log_error("cannot allocate memory for new face");
266 memset(face, 0, sizeof(*face));
268 memcpy(&face->attr, attr, sizeof(*attr));
270 ret = pthread_mutex_init(&face->glyph_lock, NULL);
272 log_error("cannot initialize glyph lock");
276 ret = shl_hashtable_new(&face->glyphs, shl_direct_hash,
277 shl_direct_equal, NULL, free_glyph);
279 log_error("cannot allocate hashtable");
283 face->ctx = pango_font_map_create_context(manager__lib);
284 pango_context_set_base_dir(face->ctx, PANGO_DIRECTION_LTR);
285 pango_context_set_language(face->ctx, pango_language_get_default());
287 desc = pango_font_description_from_string(attr->name);
288 pango_font_description_set_absolute_size(desc,
289 PANGO_SCALE * face->attr.height);
290 pango_font_description_set_weight(desc,
291 attr->bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL);
292 pango_font_description_set_style(desc,
293 attr->italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
294 pango_font_description_set_variant(desc, PANGO_VARIANT_NORMAL);
295 pango_font_description_set_stretch(desc, PANGO_STRETCH_NORMAL);
296 pango_font_description_set_gravity(desc, PANGO_GRAVITY_SOUTH);
297 pango_context_set_font_description(face->ctx, desc);
298 pango_font_description_free(desc);
301 layout = pango_layout_new(face->ctx);
302 pango_layout_set_height(layout, 0);
303 pango_layout_set_spacing(layout, 0);
304 str = "abcdefghijklmnopqrstuvwxyz"
305 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
306 "@!\"$%&/()=?\\}][{°^~+*#'<>|-_.:,;`´";
308 pango_layout_set_text(layout, str, num);
309 pango_layout_get_pixel_extents(layout, NULL, &rec);
311 memcpy(&face->real_attr, &face->attr, sizeof(face->attr));
312 face->real_attr.height = rec.height;
313 face->real_attr.width = rec.width / num + 1;
314 face->baseline = PANGO_PIXELS_CEIL(pango_layout_get_baseline(layout));
315 g_object_unref(layout);
317 kmscon_font_attr_normalize(&face->real_attr);
318 if (!face->real_attr.height || !face->real_attr.width) {
319 log_warning("invalid scaled font sizes");
324 /* The real metrics probably differ from the requested metrics so try
325 * again to find a suitable cached font. */
326 shl_dlist_for_each(iter, &manager__list) {
327 f = shl_dlist_entry(iter, struct face, list);
328 if (kmscon_font_attr_match(&f->real_attr, &face->real_attr)) {
336 shl_dlist_link(&manager__list, &face->list);
342 g_object_unref(face->ctx);
343 shl_hashtable_free(face->glyphs);
345 pthread_mutex_destroy(&face->glyph_lock);
355 static void manager_put_face(struct face *face)
360 shl_dlist_unlink(&face->list);
361 shl_hashtable_free(face->glyphs);
362 pthread_mutex_destroy(&face->glyph_lock);
363 g_object_unref(face->ctx);
371 static int kmscon_font_pango_init(struct kmscon_font *out,
372 const struct kmscon_font_attr *attr)
374 struct face *face = NULL;
377 memcpy(&out->attr, attr, sizeof(*attr));
378 kmscon_font_attr_normalize(&out->attr);
380 log_debug("loading pango font %s", out->attr.name);
382 ret = manager_get_face(&face, &out->attr);
385 memcpy(&out->attr, &face->real_attr, sizeof(out->attr));
386 out->baseline = face->baseline;
392 static void kmscon_font_pango_destroy(struct kmscon_font *font)
396 log_debug("unloading pango font");
398 manager_put_face(face);
401 static int kmscon_font_pango_render(struct kmscon_font *font, uint32_t id,
402 const uint32_t *ch, size_t len,
403 const struct kmscon_glyph **out)
405 struct kmscon_glyph *glyph;
408 ret = get_glyph(font->data, &glyph, id, ch, len);
416 static int kmscon_font_pango_render_empty(struct kmscon_font *font,
417 const struct kmscon_glyph **out)
419 static const uint32_t empty_char = ' ';
420 return kmscon_font_pango_render(font, empty_char, &empty_char, 1, out);
423 static int kmscon_font_pango_render_inval(struct kmscon_font *font,
424 const struct kmscon_glyph **out)
426 static const uint32_t question_mark = '?';
427 return kmscon_font_pango_render(font, question_mark, &question_mark, 1,
431 struct kmscon_font_ops kmscon_font_pango_ops = {
434 .init = kmscon_font_pango_init,
435 .destroy = kmscon_font_pango_destroy,
436 .render = kmscon_font_pango_render,
437 .render_empty = kmscon_font_pango_render_empty,
438 .render_inval = kmscon_font_pango_render_inval,