URI: 
       tImplement rendering for textedit (no, nothing actually works yet) - ltkx - GUI toolkit for X11 (WIP)
  HTML git clone git://lumidify.org/ltkx.git
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit f28ba51069614bc481cc7e396fd12961e16c40f3
   DIR parent b1250321e3fc95848914e726871bea1a04f1454e
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Sun, 10 May 2020 20:44:27 +0200
       
       Implement rendering for textedit (no, nothing actually works yet)
       
       Diffstat:
         M README.md                           |       4 ++++
         M array.h                             |      33 ++++++++++++++++++++-----------
         M gap_buffer.h                        |      87 +++++++++++++++++++------------
         A stack.h                             |     107 +++++++++++++++++++++++++++++++
         M text-hb.c                           |      20 ++++++++------------
         M textedit_wip.c                      |     598 +++++++++++++++++++------------
         M textedit_wip.h                      |      31 +++++++++++++++----------------
       
       7 files changed, 584 insertions(+), 296 deletions(-)
       ---
   DIR diff --git a/README.md b/README.md
       t@@ -14,4 +14,8 @@ Note that stb_truetype.h was split into a .c and .h file to make it fit in bette
        
        [cutef8](https://github.com/JeffBezanson/cutef8/) by Jeff Bezanson: [Public Domain](https://github.com/JeffBezanson/cutef8/blob/ce8607864ef59ceef39fc20c9653265f6b91d4bc/utf8.c#L4)
        
       +Bits and pieces stolen from this: 
       +
       +[libraqm](https://github.com/HOST-Oman/libraqm) by Information Technology Authority (ITA) and Khaled Hosny: [MIT](https://raw.githubusercontent.com/HOST-Oman/libraqm/master/COPYING)
       +
        Note: LTK is in no way affiliated with any of the projects listed above.
   DIR diff --git a/array.h b/array.h
       t@@ -28,23 +28,23 @@
        #include <stdlib.h>
        
        #define LTK_ARRAY_INIT_DECL(name, type)                                                        \
       -struct ltk_array_##name## {                                                                \
       +struct ltk_array_##name {                                                                \
                type *buf;                                                                        \
                size_t buf_size;                                                                \
                size_t len;                                                                        \
        }                                                                                        \
       -struct ltk_array_##name## *ltk_array_create_##name##(size_t initial_len);                \
       -void ltk_array_resize_##name##(struct ltk_array_##name## *ar, size_t size);                \
       -void ltk_array_destroy_##name##(struct ltk_array_##name## *ar);
       +struct ltk_array_##name *ltk_array_create_##name(size_t initial_len);                        \
       +void ltk_array_resize_##name(struct ltk_array_##name *ar, size_t size);                        \
       +void ltk_array_destroy_##name(struct ltk_array_##name *ar);
        
        #define LTK_ARRAY_INIT_IMPL(name, type)                                                        \
       -struct ltk_array_##name## *                                                                \
       -ltk_array_create_##name##(size_t initial_len) {                                                \
       +struct ltk_array_##name *                                                                \
       +ltk_array_create_##name(size_t initial_len) {                                                \
                if (initial_len == 0) {                                                                \
                        (void)fprintf(stderr, "Array length is zero\n");                        \
                        exit(1);                                                                \
                }                                                                                \
       -        struct ltk_gap_buffer_##name## *ar = malloc(sizeof(struct ltk_array_##name##));        \
       +        struct ltk_gap_buffer_##name *ar = malloc(sizeof(struct ltk_array_##name));        \
                if (!ar) goto error;                                                                \
                ar->buf = malloc(initial_len * sizeof(type));                                        \
                if (!ar->buf) goto error;                                                        \
       t@@ -56,19 +56,28 @@ error:                                                                                        \
        }                                                                                        \
                                                                                                \
        void                                                                                        \
       -ltk_array_resize_##name##(struct ltk_array_##name## *ar, size_t size) {                        \
       -        type *new = realloc(ar->buf, size);                                                \
       +ltk_array_resize_##name(struct ltk_array_##name *ar, size_t len) {                        \
       +        size_t new_size;                                                                \
       +        if (4 * len <= ar->buf_size) {                                                        \
       +                new_size = 2 * len;                                                        \
       +        }                                                                                \
       +        } else if (len > ar->len) {                                                        \
       +                new_size = 2 * len;                                                        \
       +        } else {                                                                        \
       +                return;                                                                        \
       +        }                                                                                \
       +        type *new = realloc(ar->buf, new_size);                                                \
                if (!new) {                                                                        \
                        (void)fprintf(stderr, "Cannot realloc array\n");                        \
                        exit(1);                                                                \
                }                                                                                \
                ar->buf = new;                                                                        \
       -        ar->buf_size = size;                                                                \
       -        ar->len = ar->len < size ? ar->len : size;                                        \
       +        ar->buf_size = new_size;                                                        \
       +        ar->len = ar->len < new_size ? ar->len : new_size;                                \
        }                                                                                        \
                                                                                                \
        void                                                                                        \
       -ltk_array_destroy_##name##(struct ltk_array_##name## *ar) {                                \
       +ltk_array_destroy_##name(struct ltk_array_##name *ar) {                                        \
                free(ar->buf);                                                                        \
                free(ar);                                                                        \
        }
   DIR diff --git a/gap_buffer.h b/gap_buffer.h
       t@@ -28,32 +28,32 @@
        #include <stdlib.h>
        
        #define LTK_GAP_BUFFER_INIT_DECL(name, type)                                        \
       -struct ltk_gap_buffer_##name## {                                                \
       +struct ltk_gap_buffer_##name {                                                        \
                type *buf;                                                                \
                size_t buf_size;                                                        \
                size_t gap_left;                                                        \
                size_t gap_size;                                                        \
        };                                                                                \
                                                                                        \
       -struct ltk_gap_buffer_##name## * ltk_gap_buffer_create_##name##(void);                \
       -struct ltk_gap_buffer_##name## *                                                \
       -ltk_gap_buffer_create_from_data_##name##(type *data, size_t len);                \
       -void ltk_gap_buffer_resize_gap_##name##(                                        \
       -    struct ltk_gap_buffer_##name## *gb, int len);                                \
       -void ltk_gap_buffer_insert_##name##(struct ltk_gap_buffer_##type## *gb,                \
       +struct ltk_gap_buffer_##name * ltk_gap_buffer_create_##name(void);                \
       +struct ltk_gap_buffer_##name *                                                        \
       +ltk_gap_buffer_create_from_data_##name(type *data, size_t len);                        \
       +void ltk_gap_buffer_resize_gap_##name(                                                \
       +    struct ltk_gap_buffer_##name *gb, int len);                                        \
       +void ltk_gap_buffer_insert_##name(struct ltk_gap_buffer_##type *gb,                \
            type *new, size_t start, size_t len);                                        \
       -void ltk_gap_buffer_insert_single_##name##(                                        \
       -    struct ltk_gap_buffer_##name## *gb, type new);                                \
       -void ltk_gap_buffer_move_gap_##name##(                                                \
       -    struct ltk_gap_buffer_##name## *gb, size_t pos);                                \
       -void ltk_gap_buffer_clear_##name##(struct ltk_gap_buffer_##name## *gb);                \
       -void ltk_gap_buffer_destroy_##name##(struct ltk_gap_buffer_##name## *gb);
       +void ltk_gap_buffer_insert_single_##name(                                        \
       +    struct ltk_gap_buffer_##name *gb, type new);                                \
       +void ltk_gap_buffer_move_gap_##name(                                                \
       +    struct ltk_gap_buffer_##name *gb, size_t pos);                                \
       +void ltk_gap_buffer_clear_##name(struct ltk_gap_buffer_##name *gb);                \
       +void ltk_gap_buffer_destroy_##name(struct ltk_gap_buffer_##name *gb);
        
        #define LTK_GAP_BUFFER_INIT_IMPL(name, type)                                        \
       -struct ltk_gap_buffer_##name## *                                                \
       -ltk_gap_buffer_create_##name##(void) {                                                \
       -        struct ltk_gap_buffer_##name## *gb =                                        \
       -            malloc(sizeof(struct ltk_gap_buffer_##name##));                        \
       +struct ltk_gap_buffer_##name *                                                        \
       +ltk_gap_buffer_create_##name(void) {                                                \
       +        struct ltk_gap_buffer_##name *gb =                                        \
       +            malloc(sizeof(struct ltk_gap_buffer_##name));                        \
                if (!gb)                                                                \
                        goto error;                                                        \
                gb->buf = malloc(8 * sizeof(type));                                        \
       t@@ -69,10 +69,10 @@ error:                                                                                \
                exit(1);                                                                \
        }                                                                                \
                                                                                        \
       -struct ltk_gap_buffer_##name## *                                                \
       -ltk_gap_buffer_create_from_data_##name##(type *data, size_t len) {                \
       -        struct ltk_gap_buffer_##name## *gb =                                        \
       -            malloc(sizeof(struct ltk_gap_buffer_##name##));                        \
       +struct ltk_gap_buffer_##name *                                                        \
       +ltk_gap_buffer_create_from_data_##name(type *data, size_t len) {                \
       +        struct ltk_gap_buffer_##name *gb =                                        \
       +            malloc(sizeof(struct ltk_gap_buffer_##name));                        \
                if (!gb) {                                                                \
                        (void)fprintf(stderr, "Out of memory while trying to"                \
                            "allocate gap buffer\n");                                        \
       t@@ -85,16 +85,37 @@ ltk_gap_buffer_create_from_data_##name##(type *data, size_t len) {                \
                return gb;                                                                \
        }                                                                                \
                                                                                        \
       +type                                                                                \
       +ltk_gap_buffer_get_##name(struct ltk_gap_buffer_##name *gb, size_t index) {        \
       +        if (index < gb->gap_left)                                                \
       +                return gb->buf[index];                                                \
       +        else if (index < gb->buf_size - gb->gap_size)                                \
       +                return gb->buf[index - gb->gap_size];                                \
       +        (void)fprintf("Gap buffer index out of bounds\n");                        \
       +        exit(1);                                                                \
       +}                                                                                \
       +                                                                                \
       +void                                                                                \
       +ltk_gap_buffer_get_##name(                                                        \
       +    struct ltk_gap_buffer_##name *gb, size_t index, type data) {                \
       +        if (index < gb->gap_left)                                                \
       +                gb->buf[index] = data;                                                \
       +        else if (index < gb->buf_size - gb->gap_size)                                \
       +                gb->buf[index - gb->gap_size] = data;                                \
       +        (void)fprintf("Gap buffer index out of bounds\n");                        \
       +        exit(1);                                                                \
       +}                                                                                \
       +                                                                                \
        void                                                                                \
       -ltk_gap_buffer_resize_gap_##name##(                                                \
       -    struct ltk_gap_buffer_##name## *gb, int len) {                                \
       +ltk_gap_buffer_resize_gap_##name(                                                \
       +    struct ltk_gap_buffer_##name *gb, int len) {                                \
                /* FIXME: Should this use realloc? It's usually more efficient, but        \
                   in this case, I would still need to copy the part after the gap        \
                   manually, so it could potentially be copied twice, which really        \
                   wouldn't be good. Maybe use realloc if only a small part is after        \
                   the gap and just regular malloc otherwise? */                        \
                int new_size = gb->buf_size - gb->gap-size + len;                        \
       -        struct ltk_gap_buffer_##name## *new = malloc(new_size * sizeof(type));        \
       +        struct ltk_gap_buffer_##name *new = malloc(new_size * sizeof(type));        \
                if (!new) {                                                                \
                        (void)fprintf(stderr, "Out of memory while trying to"                \
                            "resize gap buffer\n");                                        \
       t@@ -111,10 +132,10 @@ ltk_gap_buffer_resize_gap_##name##(                                                \
        }                                                                                \
                                                                                        \
        void                                                                                \
       -ltk_gap_buffer_insert_##name##(struct ltk_gap_buffer_##name## *gb,                \
       +ltk_gap_buffer_insert_##name(struct ltk_gap_buffer_##name *gb,                        \
            type *new, size_t start, size_t len) {                                        \
                if (gb->gap_size < len)                                                        \
       -                ltk_gap_buffer_resize_gap_##name##(gb, len + 8);                \
       +                ltk_gap_buffer_resize_gap_##name(gb, len + 8);                        \
                for (int i = 0; i < len; i++) {                                                \
                        gb->buf[gb->gap_left + i] = new[start + i];                        \
                }                                                                        \
       t@@ -123,14 +144,14 @@ ltk_gap_buffer_insert_##name##(struct ltk_gap_buffer_##name## *gb,                \
        }                                                                                \
                                                                                        \
        void                                                                                \
       -ltk_gap_buffer_insert_single_##name##(                                                \
       -    struct ltk_gap_buffer_##name## *gb, type new) {                                \
       -        ltk_gap_buffer_insert_##name##(gb, &new, 0, 1);                                \
       +ltk_gap_buffer_insert_single_##name(                                                \
       +    struct ltk_gap_buffer_##name *gb, type new) {                                \
       +        ltk_gap_buffer_insert_##name(gb, &new, 0, 1);                                \
        }                                                                                \
                                                                                        \
        void                                                                                \
       -ltk_gap_buffer_move_gap_##name##(                                                \
       -    struct ltk_gap_buffer_##name## *gb, size_t pos) {                                \
       +ltk_gap_buffer_move_gap_##name(                                                        \
       +    struct ltk_gap_buffer_##name *gb, size_t pos) {                                \
                if (pos == gb->gap_left)                                                \
                        return;                                                                \
                if (pos < 0 || pos > gb->buf_size - gb->gap_size) {                        \
       t@@ -150,13 +171,13 @@ ltk_gap_buffer_move_gap_##name##(                                                \
                gb->gap_left = pos;                                                        \
        }                                                                                \
                                                                                        \
       -void ltk_gap_buffer_clear_##name##(struct ltk_gap_buffer_##name## *gb) {        \
       +void ltk_gap_buffer_clear_##name(struct ltk_gap_buffer_##name *gb) {                \
                gb->gap_left = 0;                                                        \
                gb->gap_size = gb->buf_size;                                                \
        }                                                                                \
                                                                                        \
        void                                                                                \
       -ltk_gap_buffer_destroy_##name##(struct ltk_gap_buffer_##name## *gb) {                \
       +ltk_gap_buffer_destroy_##name(struct ltk_gap_buffer_##name *gb) {                \
                free(gb->buf);                                                                \
                free(gb);                                                                \
        }
   DIR diff --git a/stack.h b/stack.h
       t@@ -0,0 +1,107 @@
       +/*
       + * This file is part of the Lumidify ToolKit (LTK)
       + * Copyright (c) 2020 lumidify <nobody@lumidify.org>
       + * Based on the stack from libraqm.
       + *
       + * Permission is hereby granted, free of charge, to any person obtaining a copy
       + * of this software and associated documentation files (the "Software"), to deal
       + * in the Software without restriction, including without limitation the rights
       + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       + * copies of the Software, and to permit persons to whom the Software is
       + * furnished to do so, subject to the following conditions:
       + *
       + * The above copyright notice and this permission notice shall be included in all
       + * copies or substantial portions of the Software.
       + *
       + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       + * SOFTWARE.
       + */
       +
       +/* This is a "macro'd" and modified version of raqm's stack */
       +
       +#define LTK_INIT_STACK_DECL(name, type1, type2, data1, data2)                        \
       +struct ltk_stack_##name {                                                        \
       +  size_t capacity;                                                                \
       +  size_t size;                                                                        \
       +  type1 *data1;                                                                        \
       +  type2 *data2;                                                                        \
       +};                                                                                \
       +                                                                                \
       +static int ltk_stack_pop_##name(ltk_stack_##name *stack);                        \
       +static data1 ltk_stack_top1_##name(ltk_stack##name *stack);                        \
       +static void ltk_stack_destroy_##name(ltk_stack_##name *stack);                        \
       +static int ltk_stack_push_##name(                                                \
       +    ltk_stack_##name *stack, type1 data1, type2 data2);                                \
       +static data2 ltk_stack_top2_##name(ltk_stack##name *stack);                        \
       +static ltk_stack_##name *ltk_stack_create_##name (size_t max);
       +
       +#define LTK_INIT_STACK_IMPL(name, type1, type2, data1, data2)                        \
       +static ltk_stack_##name *                                                        \
       +ltk_stack_create_##name (size_t max) {                                                \
       +        ltk_stack_##name *stack;                                                \
       +        stack = malloc(sizeof(ltk_stack_##name));                                \
       +        if (!stack) goto error;                                                        \
       +                                                                                \
       +        stack->data1 = malloc(sizeof(data1) * max);                                \
       +        if (!stack->data1) goto error;                                                \
       +        stack->data2 = malloc(sizeof(data2) * max);                                \
       +        if (!stack->data2) goto error;                                                \
       +                                                                                \
       +        stack->size = 0;                                                        \
       +        stack->capacity = max;                                                        \
       +                                                                                \
       +        return stack;                                                                \
       +error:                                                                                \
       +        (void)fprintf("Cannot allocate memory for stack\n");                        \
       +        exit(1);                                                                \
       +}                                                                                \
       +                                                                                \
       +static int                                                                        \
       +ltk_stack_pop_##name(ltk_stack_##name *stack) {                                        \
       +        if (!stack->size)                                                        \
       +                return 0;                                                        \
       +                                                                                \
       +  stack->size--;                                                                \
       +                                                                                \
       +  return 1;                                                                        \
       +}                                                                                \
       +                                                                                \
       +static data1                                                                        \
       +ltk_stack_top1_##name(ltk_stack##name *stack, type1 default) {                        \
       +        if (!stack->size)                                                        \
       +                return default;                                                        \
       +                                                                                \
       +        return stack->data1[stack->size];                                        \
       +}                                                                                \
       +                                                                                \
       +static data2                                                                        \
       +ltk_stack_top2_##name(ltk_stack##name *stack, type2 default) {                        \
       +        if (!stack->size)                                                        \
       +                return default;                                                        \
       +                                                                                \
       +        return stack->data2[stack->size];                                        \
       +}                                                                                \
       +                                                                                \
       +static int                                                                        \
       +ltk_stack_push_##name(ltk_stack_##name *stack, type1 data1, type2 data2) {        \
       +        if (stack->size == stack->capacity)                                        \
       +                return 0;                                                        \
       +                                                                                \
       +        stack->size++;                                                                \
       +        stack->data1[stack->size] = data1;                                        \
       +        stack->data2[stack->size] = data2;                                        \
       +                                                                                \
       +        return 1;                                                                \
       +}                                                                                \
       +                                                                                \
       +static void                                                                        \
       +ltk_stack_destroy_##name(ltk_stack_##name *stack) {                                \
       +  free(stack->data1);                                                                \
       +  free(stack->data2);                                                                \
       +  free(stack);                                                                        \
       +}
   DIR diff --git a/text-hb.c b/text-hb.c
       t@@ -59,19 +59,13 @@ ltk_create_text_line(LtkTextManager *tm, char *text, uint16_t fontid, uint16_t s
                for (int i = 0; i < ulen; i++) {
                        log_str[i] = u8_nextmemchar(text, &inc);
                }
       -        FriBidiCharType *pbase_dir = malloc(sizeof(FriBidiCharType) * ulen);
       -        for (int i = 0; i < ulen; i++) {
       -                pbase_dir[i] = i;
       -        }
       -        FriBidiCharType pbase_dir1 = FRIBIDI_TYPE_ON;
       +        FriBidiLevel *levels = malloc(ulen * sizeof(FriBidiLevel));
       +        int *l2v = malloc(ulen * sizeof(int));
       +        int *v2l = malloc(ulen * sizeof(int));
       +        FriBidiCharType pbase_dir = FRIBIDI_TYPE_ON;
                FriBidiChar *vis_str = malloc(sizeof(FriBidiChar) * ulen);
                ulen = fribidi_charset_to_unicode(FRIBIDI_CHAR_SET_UTF8, text, strlen(text), log_str);
       -        fribidi_log2vis(log_str, ulen, &pbase_dir1, vis_str, pbase_dir, NULL, NULL);
       -        printf("%d\n", pbase_dir1);
       -        for (int i = 0; i < ulen; i++) {
       -                printf("%d ", pbase_dir[i]);
       -        }
       -        printf("\n");
       +        fribidi_log2vis(log_str, ulen, &pbase_dir, vis_str, l2v, v2l, levels);
        
                hb_unicode_funcs_t *ufuncs = hb_unicode_funcs_get_default();
                hb_script_t cur_script = hb_unicode_script(ufuncs, vis_str[0]);
       t@@ -130,7 +124,9 @@ ltk_create_text_line(LtkTextManager *tm, char *text, uint16_t fontid, uint16_t s
        
                free(vis_str);
                free(log_str);
       -        free(pbase_dir);
       +        free(levels);
       +        free(l2v);
       +        free(v2l);
        
                /* calculate width of text line
                   NOTE: doesn't work with mixed horizontal and vertical text */
   DIR diff --git a/textedit_wip.c b/textedit_wip.c
       t@@ -42,96 +42,12 @@
        extern Ltk *ltk_global;
        
        LTK_GAP_BUFFER_INIT_IMPL(uint32, uint32_t)
       +LTK_GAP_BUFFER_INIT_IMPL(script, hb_script_t)
        LTK_GAP_BUFFER_INIT_IMPL(int, int)
       -LTK_GAP_BUFFER_INIT_IMPL(glyph, struct ltk_glyph)
        LTK_ARRAY_INIT_IMPL(char_type, FriBidiCharType)
        LTK_ARRAY_INIT_IMPL(level, FriBidiLevel)
       +LTK_STACK_INIT_IMPL(script, int, hb_script_t, pair_index, script);
        
       -/* FIXME: allow to either use fribidi for basic shaping and don't use harfbuzz then,
       -          or just use harfbuzz (then fribidi doesn't need to do any shaping) */
       -LtkTextLine *
       -ltk_create_text_line(LtkTextManager *tm, char *text, uint16_t fontid, uint16_t size)
       -{
       -        /* NOTE: This doesn't actually take fontid into account right now - should it? */
       -        LtkTextLine *tl = malloc(sizeof(LtkTextLine));
       -        tl->start_segment = NULL;
       -        LtkTextSegment *cur_ts = NULL;
       -        LtkTextSegment *new_ts = NULL;
       -        uint16_t cur_font_id = fontid;
       -        int k;
       -        LtkFont *font;
       -
       -        unsigned int ulen = u8_strlen(text);
       -        uint32_t *log_str = malloc(sizeof(uint32_t) * ulen);
       -        size_t inc = 0;
       -        for (int i = 0; i < ulen; i++) {
       -                log_str[i] = u8_nextmemchar(text, &inc);
       -        }
       -        FriBidiChar *vis_str = malloc(sizeof(FriBidiChar) * ulen);
       -        ulen = fribidi_charset_to_unicode(FRIBIDI_CHAR_SET_UTF8, text, strlen(text), log_str);
       -        fribidi_log2vis(log_str, ulen, pbase_dir, vis_str, NULL, NULL, NULL);
       -
       -        hb_unicode_funcs_t *ufuncs = hb_unicode_funcs_get_default();
       -        hb_script_t cur_script = hb_unicode_script(ufuncs, vis_str[0]);
       -        hb_script_t last_script = cur_script;
       -        size_t pos = 0;
       -        size_t last_pos = 0;
       -        size_t start_pos = 0;
       -        uint32_t ch;
       -
       -        for (int p = 0; p <= ulen; p++) {
       -                cur_script = hb_unicode_script(ufuncs, vis_str[p]);
       -                if (p == ulen ||
       -                        (last_script != cur_script &&
       -                         cur_script != HB_SCRIPT_INHERITED &&
       -                         cur_script != HB_SCRIPT_COMMON)) {
       -                        FcPattern *pat = FcPatternDuplicate(tm->fcpattern);
       -                        FcPattern *match;
       -                        FcResult result;
       -                        FcPatternAddBool(pat, FC_SCALABLE, 1);
       -                        FcConfigSubstitute(NULL, pat, FcMatchPattern);
       -                        FcDefaultSubstitute(pat);
       -                        FcCharSet *cs = FcCharSetCreate();
       -                        for (int i = start_pos; i < p; i++) {
       -                                FcCharSetAddChar(cs, vis_str[i]);
       -                        }
       -                        FcPatternAddCharSet(pat, FC_CHARSET, cs);
       -                        match = FcFontMatch(NULL, pat, &result);
       -                        char *file;
       -                        FcPatternGetString(match, FC_FILE, 0, &file);
       -                        cur_font_id = ltk_get_font(tm, file);
       -                        k = kh_get(fontstruct, tm->font_cache, cur_font_id);
       -                        font = kh_value(tm->font_cache, k);
       -                        FcPatternDestroy(match);
       -                        FcPatternDestroy(pat);
       -                        // handle case that this is the last character
       -                        if (p == ulen) {
       -                                last_script = cur_script;
       -                        }
       -                        /* FIXME: There should be better handling for cases
       -                           where an error occurs while creating the segment */
       -                        new_ts = ltk_create_text_segment(
       -                                tm, vis_str + start_pos,
       -                                p - start_pos, cur_font_id,
       -                                size, last_script
       -                        );
       -                        if (!new_ts) continue;
       -                        new_ts->next = NULL;
       -                        if (!tl->start_segment) tl->start_segment = new_ts;
       -                        if (cur_ts) cur_ts->next = new_ts;
       -                        cur_ts = new_ts;
       -
       -                        start_pos = p;
       -                        last_script = cur_script;
       -                }
       -        }
       -
       -        free(vis_str);
       -        free(log_str);
       -
       -        /* calculate width of text line
       -           NOTE: doesn't work with mixed horizontal and vertical text */
       -        LtkTextSegment *ts = tl->start_segment;
                int is_hor = HB_DIRECTION_IS_HORIZONTAL(ts->dir);
                tl->y_max = tl->x_max = INT_MIN;
                tl->y_min = tl->x_min = INT_MAX;
       t@@ -170,139 +86,6 @@ ltk_create_text_line(LtkTextManager *tm, char *text, uint16_t fontid, uint16_t s
        }
        
        void
       -ltk_destroy_text_line(LtkTextLine *tl) {
       -        LtkTextSegment *last_ts;
       -        LtkTextSegment *cur_ts = tl->start_segment;
       -        while (cur_ts) {
       -                last_ts = cur_ts;
       -                cur_ts = cur_ts->next;
       -                ltk_destroy_text_segment(last_ts);
       -        }
       -}
       -
       -/* FIXME: could use unsigned int for fontid and size as long as there is code to check neither of them become too large
       -   -> in case I want to get rid of uint_16_t, etc. */
       -LtkTextSegment *
       -ltk_create_text_segment(LtkTextManager *tm, uint32_t *text, unsigned int len, uint16_t fontid, uint16_t size, hb_script_t script)
       -{
       -        /* (x1*, y1*): top left corner (relative to origin and absolute)
       -           (x2*, y2*): bottom right corner (relative to origin and absolute) */
       -        LtkFont *font;
       -        khash_t(glyphinfo) *glyph_cache;
       -        khint_t k;
       -
       -        k = kh_get(fontstruct, tm->font_cache, fontid);
       -        font = kh_value(tm->font_cache, k);
       -
       -        uint32_t attr = fontid << 16 + size;
       -        /* FIXME: turn this into ltk_get_glyph_cache */
       -        k = kh_get(glyphcache, tm->glyph_cache, attr);
       -        if (k == kh_end(tm->glyph_cache)) {
       -                k = ltk_create_glyph_cache(tm, fontid, size);
       -        }
       -        glyph_cache = kh_value(tm->glyph_cache, k);
       -
       -        LtkTextSegment *ts = malloc(sizeof(LtkTextSegment));
       -        if (!ts) {
       -                (void)fprintf(stderr, "Out of memory!\n");
       -                exit(1);
       -        }
       -        ts->str = malloc(sizeof(uint32_t) * (len + 1));
       -        memcpy(ts->str, text, len * sizeof(uint32_t));
       -        ts->str[len] = '\0';
       -        ts->font_id = fontid;
       -        ts->font_size = size;
       -
       -        hb_buffer_t *buf;
       -        hb_glyph_info_t *ginf, *gi;
       -        hb_glyph_position_t *gpos, *gp;
       -        unsigned int text_len = 0;
       -        if (len < 1) {
       -                (void)printf("WARNING: ltk_render_text_segment: length of text is less than 1.\n");
       -                return NULL;
       -        }
       -
       -        buf = hb_buffer_create();
       -        hb_direction_t dir = hb_script_get_horizontal_direction(script);
       -        hb_buffer_set_direction(buf, dir);
       -        hb_buffer_set_script(buf, script);
       -        hb_buffer_add_codepoints(buf, ts->str, len, 0, len);
       -        /* According to https://harfbuzz.github.io/the-distinction-between-levels-0-and-1.html
       -         * this should be level 1 clustering instead of level 0 */
       -        hb_buffer_set_cluster_level(buf, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS);
       -        hb_shape(font->hb, buf, NULL, 0);
       -        ts->dir = hb_buffer_get_direction(buf);
       -        ginf = hb_buffer_get_glyph_infos(buf, &text_len);
       -        gpos = hb_buffer_get_glyph_positions(buf, &text_len);
       -        float scale = stbtt_ScaleForMappingEmToPixels(&font->info, size);
       -        LtkGlyph *last_glyph = NULL;
       -
       -        int x_min = INT_MAX, x_max = INT_MIN, y_min = INT_MAX, y_max = INT_MIN;
       -        int x_abs = 0, y_abs = 0, x1_abs, y1_abs, x2_abs, y2_abs;
       -        /* magic, do not touch */
       -        LtkGlyph *glyph;
       -        for (int i = 0; i < text_len; i++) {
       -                gi = &ginf[i];
       -                gp = &gpos[i];
       -                glyph = malloc(sizeof(LtkGlyph));
       -                glyph->info = ltk_get_glyph_info(font, gi->codepoint, scale, glyph_cache);
       -                glyph->info->refs++;
       -                /* FIXME: round instead of just casting */
       -                glyph->x_offset = (int)(gp->x_offset * scale);
       -                glyph->y_offset = (int)(gp->y_offset * scale);
       -                glyph->x_advance = (int)(gp->x_advance * scale);
       -                glyph->y_advance = (int)(gp->y_advance * scale);
       -                glyph->next = NULL;
       -                if (i == 0) {
       -                        ts->start_glyph = glyph;
       -                } else {
       -                        last_glyph->next = glyph;
       -                }
       -                last_glyph = glyph;
       -
       -                /* Calculate position in order to determine full size of text segment */
       -                x1_abs = x_abs + glyph->info->xoff + glyph->x_offset;
       -                y1_abs = y_abs + glyph->info->yoff - glyph->y_offset;
       -                /* Okay, wait, so should I check if the script is horizontal, and then add
       -                   x_advance instead of glyph->info->w? It seems that the glyph width is
       -                   usually smaller than x_advance, and spaces etc. are completely lost
       -                   because their glyph width is 0. I have to distinguish between horizontal
       -                   and vertical scripts, though because to calculate the maximum y position
       -                   for horizontal scripts, I still need to use the glyph height since
       -                   y_advance doesn't really do much there. I dunno, at least *something*
       -                   works now... */
       -                /* FIXME: THIS PROBABLY DOESN'T REALLY WORK */
       -                if (HB_DIRECTION_IS_HORIZONTAL(dir)) {
       -                        x2_abs = x1_abs + glyph->x_advance;
       -                        y2_abs = y1_abs + glyph->info->h;
       -                } else {
       -                        x2_abs = x1_abs + glyph->info->w;
       -                        y2_abs = y1_abs - glyph->y_advance;
       -                }
       -                glyph->x_abs = x1_abs;
       -                glyph->y_abs = y1_abs;
       -                if (x1_abs < x_min) x_min = x1_abs;
       -                if (y1_abs < y_min) y_min = y1_abs;
       -                if (x2_abs > x_max) x_max = x2_abs;
       -                if (y2_abs > y_max) y_max = y2_abs;
       -                x_abs += glyph->x_advance;
       -                y_abs -= glyph->y_advance;
       -        }
       -        ts->start_x = -x_min;
       -        ts->start_y = -y_min;
       -        ts->w = x_max - x_min;
       -        ts->h = y_max - y_min;
       -        ts->x_min = x_min;
       -        ts->y_min = y_min;
       -        ts->x_max = x_max;
       -        ts->y_max = y_max;
       -
       -        font->refs++;
       -
       -        return ts;
       -}
       -
       -void
        ltk_destroy_text_segment(LtkTextSegment *ts)
        {
                LtkGlyph *glyph, *next_glyph;
       t@@ -429,11 +212,371 @@ soon as a different script is inserted, everything has to be redone anyways. Als
        when reshaping with context, only the text in the current run has to be passed at all.
        */
        
       -void
       -ltk_text_line_recalculate(struct ltk_text_line *tl) {
       +/* Begin stuff stolen from raqm */
       +
       +/* Special paired characters for script detection */
       +static size_t paired_len = 34;
       +static const FriBidiChar paired_chars[] = {
       +  0x0028, 0x0029, /* ascii paired punctuation */
       +  0x003c, 0x003e,
       +  0x005b, 0x005d,
       +  0x007b, 0x007d,
       +  0x00ab, 0x00bb, /* guillemets */
       +  0x2018, 0x2019, /* general punctuation */
       +  0x201c, 0x201d,
       +  0x2039, 0x203a,
       +  0x3008, 0x3009, /* chinese paired punctuation */
       +  0x300a, 0x300b,
       +  0x300c, 0x300d,
       +  0x300e, 0x300f,
       +  0x3010, 0x3011,
       +  0x3014, 0x3015,
       +  0x3016, 0x3017,
       +  0x3018, 0x3019,
       +  0x301a, 0x301b
       +};
       +
       +static int
       +get_pair_index (const FriBidiChar ch) {
       +  int lower = 0;
       +  int upper = paired_len - 1;
       +
       +  while (lower <= upper)
       +  {
       +    int mid = (lower + upper) / 2;
       +    if (ch < paired_chars[mid])
       +      upper = mid - 1;
       +    else if (ch > paired_chars[mid])
       +      lower = mid + 1;
       +    else
       +      return mid;
       +  }
       +
       +  return -1;
       +}
       +
       +#define STACK_IS_EMPTY(stack) ((stack)->size <= 0)
       +#define IS_OPEN(pair_index) (((pair_index) & 1) == 0)
       +
       +/* Resolve the script for each character in the input string, if the character
       + * script is common or inherited it takes the script of the character before it
       + * except paired characters which we try to make them use the same script. We
       + * then split the BiDi runs, if necessary, on script boundaries.
       + */
       +static int
       +ltk_resolve_scripts(struct ltk_text_line *tl) {
       +        int last_script_index = -1;
       +        int last_set_index = -1;
       +        hb_script_t last_script = HB_SCRIPT_INVALID;
       +        hb_script_t cur_script;
       +        ltk_stack_script *stack = NULL;
       +        hb_unicode_funcs_t* unicode_funcs = hb_unicode_funcs_get_default();
       +
       +        stack = ltk_stack_create_script(tl->len);
       +
       +        for (int i = 0; i < (int) tl->len; i++) {
       +                cur_script = ltk_gap_buffer_get_script(tl->scripts, i);
       +                if (cur_script == HB_SCRIPT_COMMON && last_script_index != -1) {
       +                        int pair_index = get_pair_index(ltk_gap_buffer_get_uint32(tl->log_buf, i));
       +                        if (pair_index >= 0) {
       +                                if (IS_OPEN (pair_index)) {
       +                                        /* is a paired character */
       +                                        ltk_gap_buffer_set_script(tl->scripts, i, last_script);
       +                                        last_set_index = i;
       +                                        ltk_stack_push_script(stack, cur_script, pair_index);
       +                                } else {
       +                                        /* is a close paired character */
       +                                        /* find matching opening (by getting the last
       +                                         * even index for current odd index) */
       +                                        while (!STACK_IS_EMPTY(stack) &&
       +                                            stack->pair_index[stack->size] != (pair_index & ~1)) {
       +                                                ltk_stack_pop_script(stack);
       +                                        }
       +                                        if (!STACK_IS_EMPTY(stack)) {
       +                                                ltk_gap_buffer_set_script(
       +                                                    tl->scripts, i,
       +                                                    ltk_stack_top2_script(stack, HB_SCRIPT_INVALID));
       +                                                last_script = cur_script;
       +                                                last_set_index = i;
       +                                        } else {
       +                                                ltk_gap_buffer_set_script(tl->scripts, i, last_script);
       +                                                last_set_index = i;
       +                                        }
       +                                }
       +                        } else {
       +                                ltk_gap_buffer_set_script(tl->scripts, i, last_script);
       +                                last_set_index = i;
       +                        }
       +                } else if (cur_script == HB_SCRIPT_INHERITED && last_script_index != -1) {
       +                        ltk_gap_buffer_set_script(tl->scripts, i, last_script);
       +                        last_set_index = i;
       +                } else {
       +                        for (int j = last_set_index + 1; j < i; ++j)
       +                                ltk_gap_buffer_set_script(tl->scripts, j, cur_script);
       +                        last_script = cur_script;
       +                        last_script_index = i;
       +                        last_set_index = i;
       +                }
       +        }
       +
       +        /* Loop backwards and change any remaining Common or Inherit characters to
       +        * take the script if the next character.
       +        * https://github.com/HOST-Oman/libraqm/issues/95
       +        */
       +        hb_script_t scr;
       +        for (int i = tl->len - 2; i >= 0;  --i) {
       +                scr = ltk_gap_buffer_get_script(tl->scripts, i);
       +                if (scr == HB_SCRIPT_INHERITED || scr == HB_SCRIPT_COMMON) {
       +                        ltk_gap_buffer_set_script(tl->scripts,
       +                            ltk_gap_buffer_get_script(tl->scripts, i + 1));
       +                }
       +        }
       +
       +        ltk_stack_destroy_script(stack);
       +
       +        return 1;
       +}
       +
       +/* End stuff stolen from raqm */
       +/* Update: That's a lie; much more is stolen from raqm. */
       +
       +static struct
       +ltk_text_run_create(size_t start_index, size_t len, hb_script_t script, hb_direction_t dir) {
       +        struct ltk_text_run *run = malloc(sizeof(struct ltk_text_run));
       +        if (!run) {
       +                (void)fprintf(stderr, "Cannot allocate memory for text run.\n");
       +                exit(1);
       +        }
       +        run->start_index = start_index;
       +        run->len = len;
       +        run->script = script;
       +        run->dir = dir;
       +        run->next = NULL;
       +}
       +
       +static void
       +ltk_text_line_itemize(struct ltk_text_line *tl) {
       +        ltk_resolve_scripts(tl);
       +        struct ltk_text_run *first_run = NULL;
       +        struct ltk_text_run *cur_run = NULL;
       +        FriBidilevel last_level;
       +        FriBidilevel cur_level;
       +        hb_script_t last_script;
       +        hb_script_t cur_script;
       +        size_t start_index = 0;
       +        size_t end_index;
       +        hb_direction_t dir;
       +        while (start_index < tl->len) {
       +                end_index = start_index;
       +                cur_level = last_level = ltk_gap_buffer_get_level(tl->bidi_levels, start_index);
       +                cur_script = last_script = ltk_gap_buffer_get_script(tl->scripts, start_index);
       +                while (end_index < tl->len &&
       +                    cur_level == last_level && cur_script == last_script) {
       +                        end_index++;
       +                        cur_level = ltk_gap_buffer_get_level(tl->bidi_levels, end_index);
       +                        cur_script = ltk_gap_buffer_get_script(tl->scripts, end_index);
       +                }
       +                dir = HB_DIRECTION_LTR;
       +                if (FRIBIDI_LEVEL_IS_RTL(last_level))
       +                        dir = HB_DIRECTION_RTL;
       +                struct ltk_text_run *new = ltk_text_run_create(
       +                    start_index, end_index - start_index, last_script, dir);
       +                if (!first_run) {
       +                        first_run = cur_run = new;
       +                } else {
       +                        cur->next = new;
       +                        cur = new;
       +                }
       +                start_index = end_index;
       +        }
       +        tl->runs = tl->cur_run = first_run;
       +}
       +
       +static void
       +ltk_text_run_shape(LtkTextManager *tm, struct ltk_text_run *tr,
       +    struct ltk_text_line *tl, uint16_t font_size, uint16_t font_id) {
       +        khash_t(glyphinfo) *glyph_cache;
       +        khint_t k;
       +
       +        uint32_t attr = font_id << 16 + font_size;
       +        /* FIXME: turn this into ltk_get_glyph_cache */
       +        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);
       +        }
       +        glyph_cache = kh_value(tm->glyph_cache, k);
       +
       +        hb_buffer_t *buf;
       +        hb_glyph_info_t *ginf, *gi;
       +        hb_glyph_position_t *gpos, *gp;
       +        unsigned int num_glyphs = 0;
       +
       +        buf = hb_buffer_create();
       +        hb_buffer_set_direction(buf, run->dir);
       +        hb_buffer_set_script(buf, run->script);
       +        /* WARNING: vis_buf has to be normalized (without gap) for this! */
       +        hb_buffer_add_codepoints(buf, tl->vis-buf, tl->len, tr->start_index, tr->len);
       +        /* According to https://harfbuzz.github.io/the-distinction-between-levels-0-and-1.html
       +         * this should be level 1 clustering instead of level 0 */
       +        hb_buffer_set_cluster_level(buf, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS);
       +        hb_shape(font->hb, buf, NULL, 0);
       +        ginf = hb_buffer_get_glyph_infos(buf, &num_glyphs);
       +        gpos = hb_buffer_get_glyph_positions(buf, &num_glyphs);
       +        float scale = stbtt_ScaleForMappingEmToPixels(&tr->font->info, font_size);
       +        LtkGlyph *last_glyph = NULL;
       +
       +        int x_min = INT_MAX, x_max = INT_MIN, y_min = INT_MAX, y_max = INT_MIN;
       +        int x_abs = 0, y_abs = 0, x1_abs, y1_abs, x2_abs, y2_abs;
       +        /* magic, do not touch */
       +        /* FIXME: array instead of linked list */
       +        LtkGlyph *glyph;
       +        for (int i = 0; i < num_glyphs; i++) {
       +                gi = &ginf[i];
       +                gp = &gpos[i];
       +                glyph = malloc(sizeof(LtkGlyph));
       +                if (!glyph) {
       +                        (void)fprintf(stderr, "Cannot allocate glyph.\n");
       +                        exit(1);
       +                }
       +                glyph->cluster = gi->cluster;
       +                glyph->info = ltk_get_glyph_info(tr->font, gi->codepoint, scale, glyph_cache);
       +                glyph->info->refs++;
       +                /* FIXME: round instead of just casting */
       +                glyph->x_offset = (int)(gp->x_offset * scale);
       +                glyph->y_offset = (int)(gp->y_offset * scale);
       +                glyph->x_advance = (int)(gp->x_advance * scale);
       +                glyph->y_advance = (int)(gp->y_advance * scale);
       +                glyph->next = NULL;
       +                if (i == 0) {
       +                        tr->start_glyph = glyph;
       +                } else {
       +                        last_glyph->next = glyph;
       +                }
       +                last_glyph = glyph;
       +
       +                /* Calculate position in order to determine full size of text segment */
       +                x1_abs = x_abs + glyph->info->xoff + glyph->x_offset;
       +                y1_abs = y_abs + glyph->info->yoff - glyph->y_offset;
       +                /* Okay, wait, so should I check if the script is horizontal, and then add
       +                   x_advance instead of glyph->info->w? It seems that the glyph width is
       +                   usually smaller than x_advance, and spaces etc. are completely lost
       +                   because their glyph width is 0. I have to distinguish between horizontal
       +                   and vertical scripts, though because to calculate the maximum y position
       +                   for horizontal scripts, I still need to use the glyph height since
       +                   y_advance doesn't really do much there. I dunno, at least *something*
       +                   works now... */
       +                /* FIXME: THIS PROBABLY DOESN'T REALLY WORK */
       +                if (HB_DIRECTION_IS_HORIZONTAL(tr->dir)) {
       +                        x2_abs = x1_abs + glyph->x_advance;
       +                        y2_abs = y1_abs + glyph->info->h;
       +                } else {
       +                        x2_abs = x1_abs + glyph->info->w;
       +                        y2_abs = y1_abs - glyph->y_advance;
       +                }
       +                glyph->x_abs = x1_abs;
       +                glyph->y_abs = y1_abs;
       +                if (x1_abs < x_min) x_min = x1_abs;
       +                if (y1_abs < y_min) y_min = y1_abs;
       +                if (x2_abs > x_max) x_max = x2_abs;
       +                if (y2_abs > y_max) y_max = y2_abs;
       +                x_abs += glyph->x_advance;
       +                y_abs -= glyph->y_advance;
       +        }
       +        tr->start_x = -x_min;
       +        tr->start_y = -y_min;
       +        tr->w = x_max - x_min;
       +        tr->h = y_max - y_min;
       +        tr->x_min = x_min;
       +        tr->y_min = y_min;
       +        tr->x_max = x_max;
       +        tr->y_max = y_max;
       +
       +        tr->font->refs++;
       +}
       +
       +static void
       +ltk_text_line_shape(LtkTextManager *tm, struct ltk_text_line *tl) {
       +        struct ltk_text_run *run = tl->runs;
       +        while (run) {
       +                FcPattern *pat = FcPatternDuplicate(tm->fcpattern);
       +                FcPattern *match;
       +                FcResult result;
       +                FcPatternAddBool(pat, FC_SCALABLE, 1);
       +                FcConfigSubstitute(NULL, pat, FcMatchPattern);
       +                FcDefaultSubstitute(pat);
       +                FcCharSet *cs = FcCharSetCreate();
       +                for (int i = run->start_index; i < run->start_index + run->len; i++) {
       +                        FcCharSetAddChar(cs, ltk_gap_buffer_get_uint32(tl->vis_buf, i));
       +                }
       +                FcPatternAddCharSet(pat, FC_CHARSET, cs);
       +                match = FcFontMatch(NULL, pat, &result);
       +                char *file;
       +                FcPatternGetString(match, FC_FILE, 0, &file);
       +                uint16_t font_id = ltk_get_font(tm, file);
       +                khint_t k = kh_get(fontstruct, tm->font_cache, font_id);
       +                run->font = kh_value(tm->font_cache, k);
       +                FcPatternDestroy(match);
       +                FcPatternDestroy(pat);
       +                ltk_text_run_shape(tm, run, tl->font_size, font_id);
       +                run = run->next;
       +        }
       +
       +        /* calculate width of text line
       +           NOTE: doesn't work with mixed horizontal and vertical text */
       +        /* Another note: none of this works at all with vertical text anyways */
       +        struct ltk_text_run *tr = tl->runs;
       +        int is_hor = HB_DIRECTION_IS_HORIZONTAL(tr->dir);
       +        tl->y_max = tl->x_max = INT_MIN;
       +        tl->y_min = tl->x_min = INT_MAX;
       +        tl->w = tl->h = 0;
       +        while (tr) {
       +                if (HB_DIRECTION_IS_HORIZONTAL(tr->dir) != is_hor) {
       +                        (void)fprintf(stderr, "WARNING: mixed horizontal/vertical"
       +                            "text is not supported; ignoring\n");
       +                        continue;
       +                }
       +                if (is_hor) {
       +                        if (tl->y_max < tr->y_max) {
       +                                tl->y_max = tr->y_max;
       +                        }
       +                        if (tl->y_min > tr->y_min) {
       +                                tl->y_min = tr->y_min;
       +                        }
       +                        tl->w += tr->w;
       +                } else {
       +                        if (tl->x_max < tr->x_max) {
       +                                tl->x_max = tr->x_max;
       +                        }
       +                        if (tl->x_min > tr->x_min) {
       +                                tl->x_min = tr->x_min;
       +                        }
       +                        tl->h += tr->h;
       +                }
       +                tr = tr->next;
       +        }
       +        if (is_hor) {
       +                tl->h = tl->y_max - tl->y_min;
       +        } else {
       +                tl->w = tl->x_max - tl->x_min;
       +        }
       +}
       +
       +static void
       +ltk_text_line_recalculate(LtkTextManager *tm, struct ltk_text_line *tl) {
                ltk_gap_buffer_clear_uint32(tl->vis_buf);
                ltk_gap_buffer_clear_int(tl->log2vis);
                ltk_gap_buffer_clear_int(tl->vis2log);
       +        size_t gap_pos = tl->log_buf->gap_left;
       +        size_t gap_end = tl->log_buf->buf_size - tl->log_buf->gap_size;
       +        ltk_gap_buffer_move_gap_uint32(tl->log_buf, gap_end);
       +        fribidi_log2vis(
       +            tl->log_buf, tl->log_buf->buf_size - tl->log_buf->gap_size,
       +            &tl->dir, tl->vis_buf, tl->log2vis, tl->vis2log, tl->bidi_levels
       +        );
       +        ltk_gap_buffer_move_gap_uint32(tl->log_buf, gap_pos);
       +        ltk_text_line_destroy_runs(tl); /* FIXME: IMPLEMENT */
       +        ltk_text_line_itemize(tl);
       +        ltk_text_line_shape(tm, tl);
        }
        
        void
       t@@ -454,16 +597,22 @@ ltk_text_line_insert_text(struct ltk_text_line *tl, uint32_t *text, size_t len) 
                }
                */
                ltk_gap_buffer_insert_uint32(tl->log_buf, text, 0, len);
       +        if (len > tl->scripts->gap-size)
       +                ltk_gap_buffer_resize_gap_script(tl->scripts, len + 8);
       +        hb_unicode_funcs_t *ufuncs = hb_unicode_funcs_get_default();
       +        for (int i = 0; i < len; i++) {
       +                ltk_gap_buffer_insert_single_script(
       +                    tl->scripts, hb_unicode_script(ufuncs, text[i]));
       +        }
                if (len > tl->vis_buf->gap_size)
                        ltk_gap_buffer_resize_gap_uint32(tl->vis_buf, len + 8);
                if (len > tl->log2vis->gap_size)
                        ltk_gap_buffer_resize_gap_int(tl->log2vis, len + 8);
                if (len > tl->vis2log->gap_size)
                        ltk_gap_buffer_resize_gap_int(tl->vis2log, len + 8);
       -        if (len + tl->bidi_types->len > tl->bidi_types->buf_size)
       -                ltk_array_resize_char_type(tl->bidi_types, tl->bidi_types->len + len + 8);
                if (len + tl->bidi_levels->len > tl->bidi_levels->buf_size)
                        ltk_array_resize_levels(tl->bidi_levels, tl->bidi_levels->len + len + 8);
       +        tl->len += len;
                ltk_text_line_recalculate(tl);
        }
        
       t@@ -472,14 +621,17 @@ ltk_text_line_create(void) {
                struct ltk_text_line *line = malloc(sizeof(struct ltk_text_line));
                if (!line) goto error;
                line->log_buf = ltk_gap_buffer_create_uint32();
       +        line->scripts = ltk_gap_buffer_create_script();
                line->vis_buf = ltk_gap_buffer_create_uint32();
                line->log2vis = ltk_gap_buffer_create_int();
                line->vis2log = ltk_gap_buffer_create_int();
       +        line->bidi_levels = ltk_array_create_levels(8);
                line->runs = NULL;
                line->cur_run = NULL;
                line->next = NULL;
                line->height = 0;
                line->dir = FRIBIDI_TYPE_ON;
       +        line->len = 0;
        error:
                (void)fprintf(stderr, "No memory left while creating text line\n");
                exit(1);
   DIR diff --git a/textedit_wip.h b/textedit_wip.h
       t@@ -33,28 +33,19 @@ Requires the following includes:
        
        #include "gap_buffer.h"
        #include "array.h"
       -
       -/* Contains glyph info specific to one run of text */
       -struct ltk_glyph {
       -        LtkGlyphInfo *info;
       -        int x_offset; /* additional x offset given by harfbuzz */
       -        int y_offset; /* additional y offset given by harfbuzz */
       -        int x_advance;
       -        int y_advance;
       -        int x_abs;
       -        int y_abs;
       -        uint32_t cluster; /* index of char in original text - from harfbuzz */
       -};
       +#include "stack.h"
        
        LTK_GAP_BUFFER_INIT_DECL(uint32, uint32_t)
       +LTK_GAP_BUFFER_INIT_DECL(script, hb_script_t)
        LTK_GAP_BUFFER_INIT_DECL(int, int)
       -LTK_GAP_BUFFER_INIT_DECL(glyph, struct ltk_glyph)
       -LTK_ARRAY_INIT_DECL(char_type, FriBidiCharType)
        LTK_ARRAY_INIT_DECL(level, FriBidiLevel)
       +LTK_STACK_INIT_DECL(script, int, hb_script_t, pair_index, script);
        
        struct ltk_text_run {
       -        struct ltk_gap_buffer_glyph *glyphs;
       +        struct ltk_glyph *start_glyph;
                struct ltk_text_run *next;
       +        size_t start_index;
       +        size_t len;
                LtkFont *font;
                unsigned int w;
                unsigned int h;
       t@@ -70,16 +61,24 @@ struct ltk_text_run {
        
        struct ltk_text_line {
                struct ltk_gap_buffer_uint32 *log_buf; /* buffer of the logical text */
       +        struct ltk_gap_buffer_script *scripts;
                struct ltk_gap_buffer_uint32 *vis_buf; /* buffer of visual text */
                struct ltk_gap_buffer_int *log2vis;
                struct ltk_gap_buffer_int *vis2log;
       -        struct ltk_array_char_type *bidi_types;
                struct ltk_array_level *bidi_levels;
                struct ltk_text_run *runs; /* first node in the linked list of runs */
                struct ltk_text_run *cur_run; /* current node in the linked list of runs */
                struct ltk_text_line *next; /* next text line in the buffer */
                unsigned int height; /* height of the line (including wrapping) */
                FribidiCharType dir; /* overall paragraph direction */
       +        size_t len;
       +        uint16_t font_size;
       +        int x_max;
       +        int x_min;
       +        int y_max;
       +        int y_min;
       +        int w;
       +        int h;
        };
        
        struct ltk_text_buffer {