commit f28ba51069614bc481cc7e396fd12961e16c40f3
parent b1250321e3fc95848914e726871bea1a04f1454e
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(-)
diff --git a/README.md b/README.md
@@ -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.
diff --git a/array.h b/array.h
@@ -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;							\
@@ -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);									\
 }
diff --git a/gap_buffer.h b/gap_buffer.h
@@ -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));					\
@@ -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");					\
@@ -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");					\
@@ -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];			\
 	}									\
@@ -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) {			\
@@ -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);								\
 }
diff --git a/stack.h b/stack.h
@@ -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);									\
+}
diff --git a/text-hb.c b/text-hb.c
@@ -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]);
@@ -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 */
diff --git a/textedit_wip.c b/textedit_wip.c
@@ -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;
@@ -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;
@@ -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
@@ -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);
 }
 
@@ -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);
diff --git a/textedit_wip.h b/textedit_wip.h
@@ -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;
@@ -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 {