From 717ab0c2d1757e10bb4eef17d35ccd6a991705c4 Mon Sep 17 00:00:00 2001 From: Jeremy Attali Date: Sun, 5 Jul 2020 23:33:02 -0400 Subject: [PATCH] fix(text): properly handle utf-8 chars UTF-8 characters are now handled porperly. Also re-worked some helper functions with UTF-8 in mind. I secretly no real C developers will look at this. I might have to ask for witness protection. Closes #43 --- include/swappy.h | 2 +- include/util.h | 5 ++-- src/paint.c | 17 ++++++++----- src/render.c | 5 ++-- src/util.c | 62 ++++++++++++++++++++++++++++++++---------------- 5 files changed, 59 insertions(+), 32 deletions(-) diff --git a/include/swappy.h b/include/swappy.h index c060ae4..21ab3a6 100644 --- a/include/swappy.h +++ b/include/swappy.h @@ -41,7 +41,7 @@ struct swappy_paint_text { double s; gchar *font; gchar *text; - size_t cursor; + glong cursor; struct swappy_point from; struct swappy_point to; enum swappy_text_mode mode; diff --git a/include/util.h b/include/util.h index f1aa47b..590b3ef 100644 --- a/include/util.h +++ b/include/util.h @@ -3,6 +3,7 @@ #include -void string_remove_at(char *str, size_t pos); -gchar *string_insert_chars_at(gchar *str, gchar *chars, size_t pos); +glong string_get_nb_bytes_until(gchar *str, glong until); +gchar *string_remove_at(char *str, glong pos); +gchar *string_insert_chars_at(gchar *str, gchar *chars, glong pos); void pixel_data_print(guint32 pixel); diff --git a/src/paint.c b/src/paint.c index a7a0898..0975c1f 100644 --- a/src/paint.c +++ b/src/paint.c @@ -11,7 +11,7 @@ static void cursor_move_backward(struct swappy_paint_text *text) { } static void cursor_move_forward(struct swappy_paint_text *text) { - if (text->cursor < strlen(text->text)) { + if (text->cursor < g_utf8_strlen(text->text, -1)) { text->cursor++; } } @@ -194,6 +194,7 @@ void paint_update_temporary_text(struct swappy_state *state, GdkEventKey *event) { struct swappy_paint *paint = state->temp_paint; struct swappy_paint_text *text; + char *new_text; char buffer[32]; guint32 unicode; @@ -209,14 +210,18 @@ void paint_update_temporary_text(struct swappy_state *state, paint_commit_temporary(state); break; case GDK_KEY_BackSpace: - if (strlen(text->text) > 0) { - string_remove_at(text->text, text->cursor - 1); + if (g_utf8_strlen(text->text, -1) > 0) { + new_text = string_remove_at(text->text, text->cursor - 1); + g_free(text->text); + text->text = new_text; cursor_move_backward(text); } break; case GDK_KEY_Delete: - if (strlen(text->text) > 0) { - string_remove_at(text->text, text->cursor); + if (g_utf8_strlen(text->text, -1) > 0) { + new_text = string_remove_at(text->text, text->cursor); + g_free(text->text); + text->text = new_text; } break; case GDK_KEY_Left: @@ -264,7 +269,7 @@ void paint_commit_temporary(struct swappy_state *state) { switch (paint->type) { case SWAPPY_PAINT_MODE_TEXT: - if (strlen(paint->content.text.text) == 0) { + if (g_utf8_strlen(paint->content.text.text, -1) == 0) { paint->can_draw = false; } paint->content.text.mode = SWAPPY_TEXT_MODE_DONE; diff --git a/src/render.c b/src/render.c index 7e1509a..af38694 100644 --- a/src/render.c +++ b/src/render.c @@ -5,6 +5,7 @@ #include "algebra.h" #include "swappy.h" +#include "util.h" #define pango_layout_t PangoLayout #define pango_font_description_t PangoFontDescription @@ -201,13 +202,13 @@ static void render_text(cairo_t *cr, struct swappy_paint_text text) { if (text.mode == SWAPPY_TEXT_MODE_EDIT) { pango_rectangle_t strong_pos; - pango_rectangle_t weak_pos; struct swappy_box cursor_box; cairo_set_source_rgba(cr, 0.5, 0.5, 0.5, 0.3); cairo_set_line_width(cr, 5); cairo_rectangle(cr, x, y, w, h); cairo_stroke(cr); - pango_layout_get_cursor_pos(layout, text.cursor, &strong_pos, &weak_pos); + glong bytes_til_cursor = string_get_nb_bytes_until(text.text, text.cursor); + pango_layout_get_cursor_pos(layout, bytes_til_cursor, &strong_pos, NULL); convert_pango_rectangle_to_swappy_box(strong_pos, &cursor_box); cairo_move_to(crt, cursor_box.x, cursor_box.y); cairo_set_source_rgba(crt, 0.3, 0.3, 0.3, 1); diff --git a/src/util.c b/src/util.c index 57bcfa2..d624c8a 100644 --- a/src/util.c +++ b/src/util.c @@ -3,40 +3,60 @@ #include #include -void string_remove_at(gchar *str, size_t pos) { - if (str && strlen(str) > pos) { - memmove(&str[pos], &str[pos + 1], strlen(str) - pos); +gchar *string_remove_at(gchar *str, glong pos) { + glong str_len = strlen(str); + gchar *new_str = g_new0(gchar, MAX(str_len, 1)); + gchar *buffer_source = str; + gchar *buffer_copy = new_str; + glong i = 0; + gint bytes; + gunichar c; + + if (pos <= str_len && g_utf8_validate(str, -1, NULL)) { + while (*buffer_source != '\0') { + c = g_utf8_get_char(buffer_source); + buffer_source = g_utf8_next_char(buffer_source); + if (i != pos) { + bytes = g_unichar_to_utf8(c, buffer_copy); + buffer_copy += bytes; + } + i++; + } } + + return new_str; } -gchar *string_insert_chars_at(gchar *str, gchar *chars, size_t pos) { - gchar *new_str; +gchar *string_insert_chars_at(gchar *str, gchar *chars, glong pos) { + gchar *new_str = NULL; - if (str && chars) { - size_t n = strlen(str); - size_t m = strlen(chars); - size_t i = 0, j = 0; + if (g_utf8_validate(str, -1, NULL) && g_utf8_validate(chars, -1, NULL) && + pos >= 0 && pos <= g_utf8_strlen(str, -1)) { + gchar *from = g_utf8_substring(str, 0, pos); + gchar *end = g_utf8_offset_to_pointer(str, pos); - new_str = g_new(gchar, n + m + 1); + new_str = g_strconcat(from, chars, end, NULL); - while (j < n + m) { - if (j == pos) { - for (size_t k = 0; k < m; k++) { - new_str[j++] = chars[k]; - } - } else { - new_str[j++] = str[i++]; - } - } + g_free(from); - new_str[j] = '\0'; } else { - new_str = NULL; + new_str = g_new0(gchar, 1); } return new_str; } +glong string_get_nb_bytes_until(gchar *str, glong until) { + glong ret = 0; + if (str) { + gchar *sub = g_utf8_substring(str, 0, until); + ret = strlen(sub); + g_free(sub); + } + + return ret; +} + void pixel_data_print(guint32 pixel) { const guint32 r = pixel >> 24 & 0xff; const guint32 g = pixel >> 16 & 0xff;