URI: 
       tMake text rendering standalone - ltk - Socket-based GUI for X11 (WIP)
  HTML git clone git://lumidify.org/ltk.git (fast, but not encrypted)
  HTML git clone https://lumidify.org/git/ltk.git (encrypted, but very slow)
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 63762ef33368a564ca5f630cde74981c63d32cd7
   DIR parent c2f5daed6266253ce6b97e240d74fa9a51972bd4
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Sun,  7 Jun 2020 14:24:21 +0200
       
       Make text rendering standalone
       
       Diffstat:
         M button.c                            |       6 ++----
         M grid.c                              |       2 --
         M ltk.c                               |       6 ++----
         M ltk.h                               |       1 -
         M text_common.c                       |     331 ++++++++++++++++++++++---------
         M text_common.h                       |      60 ++++----------------------------
         M text_line.c                         |     127 ++++++-------------------------
         M text_line.h                         |      12 +++++-------
       
       8 files changed, 273 insertions(+), 272 deletions(-)
       ---
   DIR diff --git a/button.c b/button.c
       t@@ -29,8 +29,6 @@
        #include <X11/Xutil.h>
        #include "util.h"
        #include "khash.h"
       -#include "stb_truetype.h"
       -#include <fontconfig/fontconfig.h>
        #include "text_common.h"
        #include "ltk.h"
        #include "text_line.h"
       t@@ -160,7 +158,7 @@ ltk_button_create(ltk_window *window, const char *id, const char *text) {
                    &ltk_button_draw, &ltk_button_destroy, 1, LTK_BUTTON);
                button->widget.mouse_release = &ltk_button_mouse_release;
                uint16_t font_size = window->theme->window->font_size;
       -        button->tl = ltk_text_line_create(window, font_size, strdup(text));
       +        button->tl = ltk_text_line_create(font_size, strdup(text));
                ltk_button_theme *theme = window->theme->button;
                button->widget.rect.w = button->tl->w + theme->border_width * 2 + theme->pad * 2;
                button->widget.rect.h = button->tl->h + theme->border_width * 2 + theme->pad * 2;
       t@@ -178,7 +176,7 @@ ltk_button_destroy(ltk_button *button, int shallow) {
                        (void)printf("WARNING: Tried to destroy NULL button.\n");
                        return;
                }
       -        ltk_text_line_destroy(button->widget.window, button->tl);
       +        ltk_text_line_destroy(button->tl);
                ltk_remove_widget(button->widget.window, button->widget.id);
                free(button->widget.id);
                free(button);
   DIR diff --git a/grid.c b/grid.c
       t@@ -31,8 +31,6 @@
        #include <X11/Xutil.h>
        #include "util.h"
        #include "khash.h"
       -#include "stb_truetype.h"
       -#include <fontconfig/fontconfig.h>
        #include "text_common.h"
        #include "ltk.h"
        #include "grid.h"
   DIR diff --git a/ltk.c b/ltk.c
       t@@ -35,8 +35,6 @@
        #include "util.h"
        #include "khash.h"
        #include "ini.h"
       -#include "stb_truetype.h"
       -#include <fontconfig/fontconfig.h>
        #include "text_common.h"
        #include "ltk.h"
        #include "grid.h"
       t@@ -322,7 +320,7 @@ ltk_create_window(const char *theme_path, const char *title, int x, int y, unsig
                window->wm_delete_msg = XInternAtom(window->dpy, "WM_DELETE_WINDOW", False);
        
                ltk_window_theme *wtheme = window->theme->window;
       -        window->tm = ltk_init_text(wtheme->font);
       +        ltk_init_default_font(wtheme->font);
                window->xwindow =
                    XCreateSimpleWindow(window->dpy, DefaultRootWindow(window->dpy), x, y,
                                        w, h, wtheme->border_width,
       t@@ -371,7 +369,7 @@ ltk_destroy_window(ltk_window *window) {
                        }
                }
                kh_destroy(widget, window->widget_hash);
       -        ltk_destroy_text(window->tm);
       +        ltk_cleanup_text();
                free(window);
        }
        
   DIR diff --git a/ltk.h b/ltk.h
       t@@ -125,7 +125,6 @@ typedef struct ltk_window {
                struct ltk_event_queue *first_event;
                struct ltk_event_queue *last_event;
                khash_t(widget) *widget_hash;
       -        ltk_text *tm;
        } ltk_window;
        
        char *ltk_read_file(const char *path, unsigned long *len);
   DIR diff --git a/text_common.c b/text_common.c
       t@@ -33,6 +33,73 @@
        #include "text_common.h"
        #include "ltk.h"
        
       +typedef struct ltk_font {
       +        stbtt_fontinfo info;
       +        char *path;
       +        int index; /* index in font file */
       +        uint16_t id;
       +        unsigned int refs;
       +} ltk_font;
       +
       +/* Hash definitions */
       +/* glyph id -> glyph info struct */
       +KHASH_MAP_INIT_INT(glyphinfo, ltk_glyph_info*)
       +/* font path, size -> glyph cache hash */
       +KHASH_MAP_INIT_INT(glyphcache, khash_t(glyphinfo)*)
       +
       +static struct {
       +        khash_t(glyphcache) *glyph_cache;
       +        ltk_font **fonts;
       +        int num_fonts;
       +        int fonts_bufsize;
       +        FcPattern *fcpattern;
       +        ltk_font *default_font;
       +        uint16_t font_id_cur;
       +} tm = {NULL, NULL, 0, 0, NULL, NULL, 1};
       +
       +static const char *default_font;
       +
       +static void err(const char *msg);
       +static char *read_file(const char *path, unsigned long *len);
       +static ltk_font *ltk_get_font(char *path, int index);
       +static void ltk_init_text(void);
       +static ltk_glyph_info *ltk_create_glyph_info(ltk_font *font, int id,
       +    float scale);
       +static voidltk_destroy_glyph_info(ltk_glyph_info *gi);
       +static ltk_glyph_info *ltk_get_glyph_info(ltk_font *font, int id,
       +    float scale, khash_t(glyphinfo) *cache);
       +static khash_t(glyphinfo) *ltk_get_glyph_cache(uint16_t font_id,
       +    uint16_t font_size);
       +static khint_t ltk_create_glyph_cache(uint16_t font_id, uint16_t font_size);
       +static void ltk_destroy_glyph_cache(khash_t(glyphinfo) *cache);
       +static void ltk_load_default_font(void);
       +static ltk_font *ltk_create_font(char *path, uint16_t id, int index);
       +static void ltk_destroy_font(ltk_font *font);
       +static ltk_font *ltk_load_font(char *path, int index);
       +static ltk_font *ltk_get_font(char *path, int index);
       +
       +static void
       +err(const char *msg) {
       +        perror(msg);
       +        exit(1);
       +}
       +
       +static char *
       +read_file(const char *path, unsigned long *len) {
       +        FILE *f;
       +        char *file_contents;
       +        f = fopen(path, "rb");
       +        fseek(f, 0, SEEK_END);
       +        *len = ftell(f);
       +        fseek(f, 0, SEEK_SET);
       +        file_contents = malloc(*len + 1);
       +        fread(file_contents, 1, *len, f);
       +        file_contents[*len] = '\0';
       +        fclose(f);
       +
       +        return file_contents;
       +}
       +
        /* These unicode routines are taken from
         * https://github.com/JeffBezanson/cutef8 */
        
       t@@ -45,8 +112,7 @@ static const uint32_t offsetsFromUTF8[6] = {
        };
        
        /* next character without NUL character terminator */
       -uint32_t u8_nextmemchar(const char *s, size_t *i)
       -{
       +uint32_t u8_nextmemchar(const char *s, size_t *i) {
            uint32_t ch = 0;
            size_t sz = 0;
            do {
       t@@ -60,8 +126,7 @@ uint32_t u8_nextmemchar(const char *s, size_t *i)
        }
        
        /* number of characters in NUL-terminated string */
       -size_t u8_strlen(const char *s)
       -{
       +size_t u8_strlen(const char *s) {
            size_t count = 0;
            size_t i = 0, lasti;
        
       t@@ -77,8 +142,7 @@ size_t u8_strlen(const char *s)
            return count;
        }
        
       -size_t u8_wc_toutf8(char *dest, uint32_t ch)
       -{
       +size_t u8_wc_toutf8(char *dest, uint32_t ch) {
            if (ch < 0x80) {
                dest[0] = (char)ch;
                return 1;
       t@@ -104,45 +168,40 @@ size_t u8_wc_toutf8(char *dest, uint32_t ch)
            return 0;
        }
        
       -ltk_text *
       -ltk_init_text(char *font_name)
       -{
       -        ltk_text *tm = malloc(sizeof(ltk_text));
       -        if (!tm) ltk_fatal("Memory exhausted when trying to create text manager.");
       -        tm->fonts = NULL;
       -        tm->num_fonts = 0;
       -        tm->fonts_bufsize = 1;
       -        tm->glyph_cache = kh_init(glyphcache);
       -        /* FIXME: THIS REALLY SHOULD NOT BE UINT16_T! IT GETS MESSY WITH BIT-SHIFTING */
       -        tm->font_id_cur = 1;
       -        tm->fonts = malloc(sizeof(ltk_font *));
       -        if (!tm->fonts) ltk_fatal("Out of memory while trying to create text manager.\n");
       -        ltk_load_default_font(tm, font_name);
       -
       -        return tm;
       +void
       +ltk_init_default_font(const char *font_name) {
       +        default_font = strdup(font_name);
       +        if (!default_font) err("ltk_init_default_font");
       +}
       +
       +static void
       +ltk_init_text(void) {
       +        tm.fonts_bufsize = 1;
       +        tm.glyph_cache = kh_init(glyphcache);
       +        tm.fonts = malloc(sizeof(ltk_font *));
       +        if (!tm.fonts) err("ltk_init_text");
       +        ltk_load_default_font();
        }
        
        void
       -ltk_destroy_text(ltk_text *tm)
       -{
       -        for (int i = 0; i < tm->num_fonts; i++) {
       -                ltk_destroy_font(&tm->fonts[i]);
       +ltk_cleanup_text(void) {
       +        if (default_font) free(default_font);
       +        for (int i = 0; i < tm.num_fonts; i++) {
       +                ltk_destroy_font(&tm.fonts[i]);
                }
       -        for (int k = kh_begin(tm->glyph_cache); k != kh_end(tm->glyph_cache); k++) {
       -                if (kh_exist(tm->glyph_cache, k)) {
       -                        ltk_destroy_glyph_cache(kh_value(tm->glyph_cache, k));
       +        if (!tm.glyph_cache) return;
       +        for (int k = kh_begin(tm.glyph_cache); k != kh_end(tm.glyph_cache); k++) {
       +                if (kh_exist(tm.glyph_cache, k)) {
       +                        ltk_destroy_glyph_cache(kh_value(tm.glyph_cache, k));
                        }
                }
       -        kh_destroy(glyphcache, tm->glyph_cache);
       -
       -        free(tm);
       +        kh_destroy(glyphcache, tm.glyph_cache);
        }
        
       -ltk_glyph_info *
       -ltk_create_glyph_info(ltk_font *font, int id, float scale)
       -{
       +static ltk_glyph_info *
       +ltk_create_glyph_info(ltk_font *font, int id, float scale) {
                ltk_glyph_info *glyph = malloc(sizeof(ltk_glyph_info));
       -        if (!glyph) ltk_fatal("Out of memory while trying to create glyph info.\n");
       +        if (!glyph) err("ltk_create_glyph_info");
        
                glyph->id = id;
                glyph->refs = 0;
       t@@ -154,16 +213,14 @@ ltk_create_glyph_info(ltk_font *font, int id, float scale)
                return glyph;
        }
        
       -void
       -ltk_destroy_glyph_info(ltk_glyph_info *gi)
       -{
       +static void
       +ltk_destroy_glyph_info(ltk_glyph_info *gi) {
                free(gi->alphamap);
                free(gi);
        }
        
       -ltk_glyph_info *
       -ltk_get_glyph_info(ltk_font *font, int id, float scale, khash_t(glyphinfo) *cache)
       -{
       +static ltk_glyph_info *
       +ltk_get_glyph_info(ltk_font *font, int id, float scale, khash_t(glyphinfo) *cache) {
                int ret;
                khint_t k;
                ltk_glyph_info *glyph;
       t@@ -180,33 +237,33 @@ ltk_get_glyph_info(ltk_font *font, int id, float scale, khash_t(glyphinfo) *cach
                return glyph;
        }
        
       -khash_t(glyphinfo) *
       -ltk_get_glyph_cache(ltk_text *tm, uint16_t font_id, uint16_t font_size) {
       +static khash_t(glyphinfo) *
       +ltk_get_glyph_cache(uint16_t font_id, uint16_t font_size) {
       +        if (!tm.glyph_cache) ltk_init_text();
                khint_t k;
                uint32_t attr = (uint32_t)font_id << 16 + font_size;
       -        k = kh_get(glyphcache, tm->glyph_cache, attr);
       -        if (k == kh_end(tm->glyph_cache)) {
       -                k = ltk_create_glyph_cache(tm, font_id, font_size);
       +        k = kh_get(glyphcache, tm.glyph_cache, attr);
       +        if (k == kh_end(tm.glyph_cache)) {
       +                k = ltk_create_glyph_cache(font_id, font_size);
                }
       -        return kh_value(tm->glyph_cache, k);
       +        return kh_value(tm.glyph_cache, k);
        }
        
       -khint_t
       -ltk_create_glyph_cache(ltk_text *tm, uint16_t font_id, uint16_t font_size)
       -{
       +static khint_t
       +ltk_create_glyph_cache(uint16_t font_id, uint16_t font_size) {
       +        if (!tm.glyph_cache) ltk_init_text();
                khash_t(glyphinfo) *cache = kh_init(glyphinfo);
                int ret;
                khint_t k;
                /* I guess I can just ignore ret for now */
       -        k = kh_put(glyphcache, tm->glyph_cache, font_id << 16 + font_size, &ret);
       -        kh_value(tm->glyph_cache, k) = cache;
       +        k = kh_put(glyphcache, tm.glyph_cache, font_id << 16 + font_size, &ret);
       +        kh_value(tm.glyph_cache, k) = cache;
        
                return k;
        }
        
       -void
       -ltk_destroy_glyph_cache(khash_t(glyphinfo) *cache)
       -{
       +static void
       +ltk_destroy_glyph_cache(khash_t(glyphinfo) *cache) {
                int k;
                for (k = kh_begin(cache); k != kh_end(cache); k++) {
                        if (kh_exist(cache, k)) {
       t@@ -216,36 +273,37 @@ ltk_destroy_glyph_cache(khash_t(glyphinfo) *cache)
                kh_destroy(glyphinfo, cache);
        }
        
       -void
       -ltk_load_default_font(ltk_text *tm, char *name)
       -{
       +static void
       +ltk_load_default_font(void) {
                FcPattern *match;
                FcResult result;
                char *file;
                int index;
                uint16_t font;
        
       -        tm->fcpattern = FcNameParse(name);
       -        FcPatternAddString(tm->fcpattern, FC_FONTFORMAT, "truetype");
       -        FcConfigSubstitute(NULL, tm->fcpattern, FcMatchPattern);
       -        FcDefaultSubstitute(tm->fcpattern);
       -        match = FcFontMatch(NULL, tm->fcpattern, &result);
       +        if (default_font)
       +                tm.fcpattern = FcNameParse(default_font);
       +        else
       +                tm.fcpattern = FcPatternCreate();
       +        FcPatternAddString(tm.fcpattern, FC_FONTFORMAT, "truetype");
       +        FcConfigSubstitute(NULL, tm.fcpattern, FcMatchPattern);
       +        FcDefaultSubstitute(tm.fcpattern);
       +        match = FcFontMatch(NULL, tm.fcpattern, &result);
        
                FcPatternGetString(match, FC_FILE, 0, (FcChar8 **) &file);
                FcPatternGetInteger(match, FC_INDEX, 0, &index);
        
       -        tm->default_font = ltk_get_font(tm, file, index);
       +        tm.default_font = ltk_get_font(file, index);
        
                FcPatternDestroy(match);
        }
        
       -ltk_font *
       -ltk_create_font(char *path, uint16_t id, int index)
       -{
       +static ltk_font *
       +ltk_create_font(char *path, uint16_t id, int index) {
                long len;
                ltk_font *font = malloc(sizeof(ltk_font));
       -        if (!font) ltk_fatal("Out of memory while trying to create font.\n");
       -        char *contents = ltk_read_file(path, &len);
       +        if (!font) err("ltk_create_font");
       +        char *contents = read_file(path, &len);
                /* FIXME: error checking */
                int offset = stbtt_GetFontOffsetForIndex(contents, index);
                if (!stbtt_InitFont(&font->info, contents, offset)) {
       t@@ -256,56 +314,139 @@ ltk_create_font(char *path, uint16_t id, int index)
                font->refs = 0;
                font->index = index;
                font->path = strdup(path);
       +        if (!font->path) err("ltk_create_font");
        
                return font;
        }
        
       -void
       -ltk_destroy_font(ltk_font *font)
       -{
       +static void
       +ltk_destroy_font(ltk_font *font) {
                free(font->info.data);
                free(font);
        }
        
       -ltk_font *
       -ltk_load_font(ltk_text *tm, char *path, int index)
       -{
       -        ltk_font *font = ltk_create_font(path, tm->font_id_cur++, index);
       -        if (tm->num_fonts == tm->fonts_bufsize) {
       -                ltk_font *new = realloc(tm->fonts, tm->fonts_bufsize * 2 * sizeof(ltk_font *));
       -                if (!new) ltk_fatal("Out of memory while trying to create font.\n");
       -                tm->fonts = new;
       -                tm->fonts_bufsize = tm->fonts_bufsize * 2;
       +static ltk_font *
       +ltk_load_font(char *path, int index) {
       +        ltk_font *font = ltk_create_font(path, tm.font_id_cur++, index);
       +        if (tm.num_fonts == tm.fonts_bufsize) {
       +                ltk_font *new = realloc(tm.fonts, tm.fonts_bufsize * 2 * sizeof(ltk_font *));
       +                if (!new) err("ltk_load_font");
       +                tm.fonts = new;
       +                tm.fonts_bufsize *= 2;
                }
       -        tm->fonts[tm->num_fonts] = font;
       -        tm->num_fonts++;
       +        tm.fonts[tm.num_fonts] = font;
       +        tm.num_fonts++;
                return font;
        }
        
       -ltk_font *
       -ltk_get_font(ltk_text *tm, char *path, int index)
       -{
       +static ltk_font *
       +ltk_get_font(char *path, int index) {
                ltk_font *font = NULL;
       -        for (int i = 0; i < tm->num_fonts; i++) {
       -                if (tm->fonts[i]->index == index &&
       -                    strcmp(tm->fonts[i]->path, path) == 0) {
       -                        font = &tm->fonts[i];
       +        for (int i = 0; i < tm.num_fonts; i++) {
       +                if (tm.fonts[i]->index == index &&
       +                    strcmp(tm.fonts[i]->path, path) == 0) {
       +                        font = &tm.fonts[i];
                                break;
                        }
                }
                if (!font)
       -                font = ltk_load_font(tm, path, index);
       +                font = ltk_load_font(path, index);
                return font;
        }
        
        void
       -ltk_destroy_glyph(ltk_glyph *glyph, khash_t(glyphinfo) *cache)
       -{
       +ltk_text_to_glyphs(ltk_glyph *glyphs, int num_glyphs, char *text, uint16_t font_size,
       +    int *x_min, int *y_min, int *x_max, int *y_max) {
       +        uint32_t c1, c2 = 0;
       +        int gid;
       +        int index;
       +        char *file;
       +        size_t inc = 0;
       +        int x = 0, y, kern_advance, ax;
       +        int x1_abs, x2_abs;
       +        float scale;
       +        int ascent, descent, line_gap;
       +        *x_min = INT_MAX, *x_max = INT_MIN, *y_min = INT_MAX, *y_max = INT_MIN;
       +        ltk_glyph_info *ginfo;
       +        if (!tm.default_font)
       +                ltk_init_text();
       +
       +        ltk_font *font = tm.default_font;
       +        khash_t(glyphinfo) *glyph_cache = ltk_get_glyph_cache(font->id, font_size);
       +
       +        scale = stbtt_ScaleForPixelHeight(&font->info, font_size);
       +        stbtt_GetFontVMetrics(&font->info, &ascent, &descent, &line_gap);
       +        ascent *= scale;
       +        descent *= scale;
       +
       +        c1 = u8_nextmemchar(text, &inc);
       +        for (int i = 0; i < num_glyphs; i++) {
       +                gid = stbtt_FindGlyphIndex(&font->info, c1);
       +                if (!gid) {
       +                        /* Question: Why does this not work with FcPatternDuplicate? */
       +                        FcPattern *pat = FcPatternCreate();
       +                        FcPattern *match;
       +                        FcResult result;
       +                        FcPatternAddBool(pat, FC_SCALABLE, 1);
       +                        FcConfigSubstitute(NULL, pat, FcMatchPattern);
       +                        FcDefaultSubstitute(pat);
       +                        FcCharSet *cs = FcCharSetCreate();
       +                        FcCharSetAddChar(cs, c1);
       +                        FcPatternAddCharSet(pat, FC_CHARSET, cs);
       +                        match = FcFontMatch(NULL, pat, &result);
       +                        FcPatternGetString(match, FC_FILE, 0, &file);
       +                        FcPatternGetInteger(match, FC_INDEX, 0, &index);
       +                        font = ltk_get_font(file, index);
       +                        glyph_cache = ltk_get_glyph_cache(font->id, font_size);
       +                        FcPatternDestroy(match);
       +                        FcPatternDestroy(pat);
       +                        gid = stbtt_FindGlyphIndex(&font->info, c1);
       +                        scale = stbtt_ScaleForPixelHeight(&font->info, font_size);
       +                        stbtt_GetFontVMetrics(&font->info, &ascent, &descent, &line_gap);
       +                        ascent *= scale;
       +                        descent *= scale;
       +                }
       +                ginfo = ltk_get_glyph_info(font, gid, scale, glyph_cache);
       +                ginfo->refs++;
       +                y = ascent + ginfo->yoff;
       +                x1_abs = x + ginfo->xoff;
       +
       +                glyphs[i].x = x1_abs;
       +                glyphs[i].y = y;
       +
       +                stbtt_GetGlyphHMetrics(&font->info, gid, &ax, 0);
       +                x += (int) (ax * scale);
       +                x2_abs = x;
       +
       +                glyphs[i].info = ginfo;
       +                if (x1_abs < *x_min) *x_min = x1_abs;
       +                if (y < *y_min) *y_min = y;
       +                if (x2_abs > *x_max) *x_max = x2_abs;
       +                if (y + ginfo->h > *y_max) *y_max = y + ginfo->h;
       +
       +                if (i != num_glyphs - 1) {
       +                        c2 = u8_nextmemchar(text, &inc);
       +                        kern_advance = stbtt_GetCodepointKernAdvance(&font->info, c1, c2);
       +                        x += (int) (kern_advance * scale);
       +                }
       +                c1 = c2;
       +        }
       +}
       +
       +/*
       +void
       +ltk_unref_glyph(ltk_glyph *glyph, khash_t(glyphinfo) *cache) {
                int k;
                if (--glyph->info->refs < 1) {
                        k = kh_get(glyphinfo, cache, glyph->info->id);
                        kh_del(glyphinfo, cache, k);
                        ltk_destroy_glyph_info(glyph->info);
                }
       -        free(glyph);
        }
       +
       +void
       +ltk_unref_glyphs(ltk_glyph *glyphs, int num_glyphs) {
       +        for (int i = 0; i < num_glyphs; i++)
       +                ltk_unref_glyph(&glyphs[i]);
       +}
       +*/
   DIR diff --git a/text_common.h b/text_common.h
       t@@ -24,19 +24,8 @@
        #ifndef _TEXT_COMMON_H_
        #define _TEXT_COMMON_H_
        
       -/*
       -Requires the following includes:
       -<X11/Xlib.h>, <X11/Xutil.h>, "stb_truetype.h",
       -"khash.h"
       -*/
       -
       -typedef struct {
       -        stbtt_fontinfo info;
       -        char *path;
       -        int index; /* index in font file */
       -        uint16_t id;
       -        unsigned int refs;
       -} ltk_font;
       +typedef struct ltk_font ltk_font;
       +typedef struct ltk_text ltk_text;
        
        /* Contains general info on glyphs that doesn't change regardless of the context */
        typedef struct {
       t@@ -58,54 +47,17 @@ typedef struct {
                int y;
        } ltk_glyph;
        
       -/* Hash definitions */
       -/* glyph id -> glyph info struct */
       -KHASH_MAP_INIT_INT(glyphinfo, ltk_glyph_info*)
       -/* font path, size -> glyph cache hash */
       -KHASH_MAP_INIT_INT(glyphcache, khash_t(glyphinfo)*)
       -
       -typedef struct {
       -        khash_t(glyphcache) *glyph_cache;
       -        ltk_font **fonts;
       -        int num_fonts;
       -        int fonts_bufsize;
       -        FcPattern *fcpattern;
       -        ltk_font *default_font;
       -        uint16_t font_id_cur;
       -} ltk_text;
       -
        uint32_t u8_nextmemchar(const char *s, size_t *i);
        
        size_t u8_strlen(const char *s);
        
        size_t u8_wc_toutf8(char *dest, uint32_t ch);
        
       -ltk_text *ltk_init_text(char *font_name);
       -
       -void ltk_destroy_text_manager(ltk_text *tm);
       -
       -ltk_glyph_info *ltk_create_glyph_info(ltk_font *font, int id, float scale);
       -
       -void ltk_destroy_glyph_info(ltk_glyph_info *gi);
       -
       -ltk_glyph_info *ltk_get_glyph_info(ltk_font *font, int id, float scale, khash_t(glyphinfo) *cache);
       -
       -khash_t(glyphinfo) *ltk_get_glyph_cache(ltk_text *tm, uint16_t font_id, uint16_t font_size);
       -
       -khint_t ltk_create_glyph_cache(ltk_text *tm, uint16_t font_id, uint16_t font_size);
       -
       -void ltk_destroy_glyph_cache(khash_t(glyphinfo) *cache);
       -
       -void ltk_load_default_font(ltk_text *tm, char *name);
       -
       -ltk_font *ltk_create_font(char *path, uint16_t id, int index);
       -
       -void ltk_destroy_font(ltk_font *font);
       -
       -ltk_font *ltk_load_font(ltk_text *tm, char *path, int index);
       +void ltk_init_default_font(const char *font_name);
        
       -ltk_font *ltk_get_font(ltk_text *tm, char *path, int index);
       +void ltk_cleanup_text(void);
        
       -void ltk_destroy_glyph(ltk_glyph *glyph, khash_t(glyphinfo) *cache);
       +void ltk_text_to_glyphs(ltk_glyph *glyphs, int num_glyphs, char *text,
       +    uint16_t font_size, int *x_min, int *y_min, int *x_max, int *y_max);
        
        #endif /* _TEXT_COMMON_H_ */
   DIR diff --git a/text_line.c b/text_line.c
       t@@ -24,16 +24,24 @@
        #include <stdio.h>
        #include <stdlib.h>
        #include <stdint.h>
       -#include <limits.h>
        #include <X11/Xlib.h>
        #include <X11/Xutil.h>
       -#include "stb_truetype.h" /* http://nothings.org/stb/stb_truetype.h */
       -#include <fontconfig/fontconfig.h>
       -#include "khash.h"
        #include "text_common.h"
       -#include "ltk.h"
        #include "text_line.h"
        
       +static void err(const char *msg);
       +static void ltk_text_line_create_glyphs(struct ltk_text_line *tl);
       +static void ltk_text_line_draw_glyph(ltk_glyph *glyph, int xoff, int yoff,
       +    XImage *img, XColor fg);
       +static XImage *ltk_create_ximage(Display *dpy, int w, int h, int depth,
       +    XColor bg);
       +
       +static void
       +err(const char *msg) {
       +        perror(msg);
       +        exit(1);
       +}
       +
        static XImage *
        ltk_create_ximage(Display *dpy, int w, int h, int depth, XColor bg) {
                XImage *img = XCreateImage(dpy, CopyFromParent, depth, ZPixmap, 0, NULL, w, h, 32, 0);
       t@@ -99,78 +107,10 @@ ltk_text_line_render(
        }
        
        static void
       -ltk_text_line_create_glyphs(ltk_window *window, struct ltk_text_line *tl) {
       -        uint32_t c1, c2 = 0;
       -        int gid;
       -        int index;
       -        char *file;
       -        size_t inc = 0;
       -        int x = 0, y, kern_advance, ax;
       -        int x1_abs, x2_abs;
       -        float scale;
       -        int ascent, descent, line_gap;
       -        int x_min = INT_MAX, x_max = INT_MIN, y_min = INT_MAX, y_max = INT_MIN;
       -        ltk_glyph_info *ginfo;
       -
       -        ltk_font *font = window->tm->default_font;
       -        khash_t(glyphinfo) *glyph_cache = ltk_get_glyph_cache(window->tm, font->id, tl->font_size);
       -
       -        scale = stbtt_ScaleForPixelHeight(&font->info, tl->font_size);
       -        stbtt_GetFontVMetrics(&font->info, &ascent, &descent, &line_gap);
       -        ascent *= scale;
       -        descent *= scale;
       -
       -        c1 = u8_nextmemchar(tl->text, &inc);
       -        for (int i = 0; i < tl->glyph_len; i++) {
       -                gid = stbtt_FindGlyphIndex(&font->info, c1);
       -                if (!gid) {
       -                        /* Question: Why does this not work with FcPatternDuplicate? */
       -                        FcPattern *pat = FcPatternCreate();
       -                        FcPattern *match;
       -                        FcResult result;
       -                        FcPatternAddBool(pat, FC_SCALABLE, 1);
       -                        FcConfigSubstitute(NULL, pat, FcMatchPattern);
       -                        FcDefaultSubstitute(pat);
       -                        FcCharSet *cs = FcCharSetCreate();
       -                        FcCharSetAddChar(cs, c1);
       -                        FcPatternAddCharSet(pat, FC_CHARSET, cs);
       -                        match = FcFontMatch(NULL, pat, &result);
       -                        FcPatternGetString(match, FC_FILE, 0, &file);
       -                        FcPatternGetInteger(match, FC_INDEX, 0, &index);
       -                        font = ltk_get_font(window->tm, file, index);
       -                        glyph_cache = ltk_get_glyph_cache(window->tm, font->id, tl->font_size);
       -                        FcPatternDestroy(match);
       -                        FcPatternDestroy(pat);
       -                        gid = stbtt_FindGlyphIndex(&font->info, c1);
       -                        scale = stbtt_ScaleForPixelHeight(&font->info, tl->font_size);
       -                        stbtt_GetFontVMetrics(&font->info, &ascent, &descent, &line_gap);
       -                        ascent *= scale;
       -                        descent *= scale;
       -                }
       -                ginfo = ltk_get_glyph_info(font, gid, scale, glyph_cache);
       -                y = ascent + ginfo->yoff;
       -                x1_abs = x + ginfo->xoff;
       -
       -                tl->glyphs[i].x = x1_abs;
       -                tl->glyphs[i].y = y;
       -
       -                stbtt_GetGlyphHMetrics(&font->info, gid, &ax, 0);
       -                x += (int) (ax * scale);
       -                x2_abs = x;
       -
       -                tl->glyphs[i].info = ginfo;
       -                if (x1_abs < x_min) x_min = x1_abs;
       -                if (y < y_min) y_min = y;
       -                if (x2_abs > x_max) x_max = x2_abs;
       -                if (y + ginfo->h > y_max) y_max = y + ginfo->h;
       -
       -                if (i != tl->glyph_len - 1) {
       -                        c2 = u8_nextmemchar(tl->text, &inc);
       -                        kern_advance = stbtt_GetCodepointKernAdvance(&font->info, c1, c2);
       -                        x += (int) (kern_advance * scale);
       -                }
       -                c1 = c2;
       -        };
       +ltk_text_line_create_glyphs(struct ltk_text_line *tl) {
       +        int x_min, x_max, y_min, y_max;
       +        ltk_text_to_glyphs(tl->glyphs, tl->glyph_len, tl->text, tl->font_size,
       +            &x_min, &y_min, &x_max, &y_max);
                /* for drawing the glyphs at the right position on the image */
                tl->x_min = x_min;
                tl->y_min = y_min;
       t@@ -179,44 +119,21 @@ ltk_text_line_create_glyphs(ltk_window *window, struct ltk_text_line *tl) {
        }
        
        struct ltk_text_line *
       -ltk_text_line_create(ltk_window *window, uint16_t font_size, char *text) {
       +ltk_text_line_create(uint16_t font_size, char *text) {
                struct ltk_text_line *line = malloc(sizeof(struct ltk_text_line));
       -        if (!line) goto error;
       +        if (!line) err("ltk_text_line_create");
                line->text = text;
                line->glyph_len = u8_strlen(text);
                line->glyphs = malloc(line->glyph_len * sizeof(ltk_glyph));
                line->font_size = font_size;
       -        ltk_text_line_create_glyphs(window, line);
       +        ltk_text_line_create_glyphs(line);
                return line;
       -error:
       -        (void)fprintf(stderr, "No memory left while creating text line\n");
       -        exit(1);
        }
        
        void
       -ltk_text_line_destroy(ltk_window *window, struct ltk_text_line *tl) {
       +ltk_text_line_destroy(struct ltk_text_line *tl) {
                free(tl->text);
       -        /* FIXME: glyph reference counting */
       +        /* FIXME: Reference count glyph infos */
                free(tl->glyphs);
       -        /*
       -        khint_t k;
       -        LtkTextManager *tm = ltk_get_text_manager();
       -        k = kh_get(glyphinfo, tm->glyph_cache, tr->font_id << 16 + tr->font_size);
       -        gcache = kh_value(tm->glyph_cache, k);
       -        for (int i = 0; i < tr->num_glyphs; i++) {
       -                glyph = &tr->glyphs[i];
       -                if (--glyph->info->refs < 1) {
       -                        k = kh_get(glyphinfo, gcache, glyph->info->id);
       -                        kh_del(glyphinfo, gcache, k);
       -                        ltk_destroy_glyph_info(glyph->info);
       -                }
       -        }
       -        k = kh_get(fontstruct, tm->font_cache, tr->font_id);
       -        font = kh_value(tm->font_cache, k);
       -        if (--font->refs < 1) {
       -                kh_del(fontstruct, tm->font_cache, k);
       -                ltk_destroy_font(font);
       -        }
       -        */
                free(tl);
        }
   DIR diff --git a/text_line.h b/text_line.h
       t@@ -26,9 +26,7 @@
        
        /*
        Requires the following includes:
       -<X11/Xlib.h>, <X11/Xutil.h>, "stb_truetype.h",
       -"khash.h", <fontconfig/fontconfig.h>, "text_common.h",
       -"ltk.h"
       +<X11/Xlib.h>, <X11/Xutil.h>, "text_common.h",
        */
        
        struct ltk_text_line {
       t@@ -42,9 +40,9 @@ struct ltk_text_line {
                int y_min;
        };
        
       -XImage *ltk_text_line_render(struct ltk_text_line *tl,
       -    Display *dpy, Window window, GC gc, Colormap colormap, XColor fg, XColor bg);
       -struct ltk_text_line *ltk_text_line_create(ltk_window *window, uint16_t font_size, char *text);
       -void ltk_text_line_destroy(ltk_window *window, struct ltk_text_line *tl);
       +XImage *ltk_text_line_render(struct ltk_text_line *tl, Display *dpy,
       +    Window window, GC gc, Colormap colormap, XColor fg, XColor bg);
       +struct ltk_text_line *ltk_text_line_create(uint16_t font_size, char *text);
       +void ltk_text_line_destroy(struct ltk_text_line *tl);
        
        #endif /* _TEXT_BUFFER_H_ */