From 16daa2c43091242b142074405e0beeff16d2b133 Mon Sep 17 00:00:00 2001 From: Thomas White Date: Sun, 18 Aug 2019 22:38:13 +0200 Subject: Handle *bold*, /italic/ etc --- libstorycode/narrative.c | 7 ++ libstorycode/narrative_priv.h | 3 + libstorycode/narrative_render_cairo.c | 202 +++++++++++++++++++++++++++++++++- 3 files changed, 208 insertions(+), 4 deletions(-) diff --git a/libstorycode/narrative.c b/libstorycode/narrative.c index 4d088e4..7db31d9 100644 --- a/libstorycode/narrative.c +++ b/libstorycode/narrative.c @@ -75,6 +75,8 @@ static void narrative_item_destroy(struct narrative_item *item) if ( item->layout != NULL ) { g_object_unref(item->layout); } + free(item->chars_removed); + free(item->layout_text); #endif #ifdef HAVE_CAIRO if ( item->slide_thumbnail != NULL ) { @@ -200,7 +202,12 @@ ImageStore *narrative_get_imagestore(Narrative *n) static void init_item(struct narrative_item *item) { +#ifdef HAVE_PANGO item->layout = NULL; + item->layout_text = NULL; + item->chars_removed = NULL; + item->n_chars_removed = 0; +#endif item->text = NULL; item->slide = NULL; item->slide_thumbnail = NULL; diff --git a/libstorycode/narrative_priv.h b/libstorycode/narrative_priv.h index 9d6e294..ec3ffb5 100644 --- a/libstorycode/narrative_priv.h +++ b/libstorycode/narrative_priv.h @@ -64,6 +64,9 @@ struct narrative_item enum alignment align; #ifdef HAVE_PANGO PangoLayout *layout; + char *layout_text; + int *chars_removed; + int n_chars_removed; #else void *layout; #endif diff --git a/libstorycode/narrative_render_cairo.c b/libstorycode/narrative_render_cairo.c index c22b7ce..17f22aa 100644 --- a/libstorycode/narrative_render_cairo.c +++ b/libstorycode/narrative_render_cairo.c @@ -69,6 +69,199 @@ static PangoAlignment to_pangoalignment(enum alignment align) } +static int find_pair(const gchar *p, gunichar c, gchar **start, gchar **end) +{ + gchar *s; + gchar *e; + gchar *next; + + /* FIXME: Check it's not escaped */ + s = g_utf8_strchr(p, -1, c); + if ( s == NULL ) return 0; + + next = g_utf8_find_next_char(s, NULL); + if ( next == NULL ) return 0; + + e = g_utf8_strchr(next, -1, c); + if ( e == NULL ) return 0; + + *start = s; + *end = e; + return 1; +} + + +struct attr_to_add +{ + int start; + int end; + char type; /* b=bold, i=italic, u=underline */ +}; + + +static int add_range(struct narrative_item *item, int *max_chars_removed, + int start, int end, int *n_add, int *max_add, + struct attr_to_add **add, char type) +{ + if ( item->n_chars_removed == *max_chars_removed ) { + (*max_chars_removed) += 256; + item->chars_removed = realloc(item->chars_removed, *max_chars_removed*sizeof(int)); + if ( item->chars_removed == NULL ) return 1; + } + + item->chars_removed[item->n_chars_removed++] = start; + item->chars_removed[item->n_chars_removed++] = end; + + if ( *n_add == *max_add ) { + *max_add += 64; + *add = realloc(*add, *max_add*sizeof(struct attr_to_add)); + if ( *add == NULL ) return 1; + } + + (*add)[*n_add].start = start; + (*add)[*n_add].end = end; + (*add)[*n_add].type = type; + (*n_add)++; + + return 0; +} + + +/* How many bytes were removed up to idx? */ +int index_before_removal(int *chars_removed, int n_chars_removed, int idx) +{ + int i; + + for ( i=0; i idx ) break; + } + + return idx + i; +} + + +/* How many bytes were removed up to idx? */ +int index_with_removal(int *chars_removed, int n_chars_removed, int idx) +{ + int i; + + for ( i=0; i idx ) break; + } + + return idx - i; +} + + +static int cmpi(const void *a, const void *b) +{ + return *(int *)a - *(int *)b; +} + + +static void process_tags(struct narrative_item *item, PangoAttrList *attrs) +{ + gchar *efstart; + gchar *efend; + gchar *text; + PangoAttribute *attr; + struct attr_to_add *add; + int n_add; + int max_add; + int max_chars_removed; + int i, j, k; + size_t len; + + item->n_chars_removed = 0; + free(item->chars_removed); + item->chars_removed = NULL; + max_chars_removed = 0; + + add = NULL; + n_add = 0; + max_add = 0; + + /* Scan the text, identify characters to remove and character ranges + * (in the original text) which need attributes applied */ + text = item->text; + while ( find_pair(text, '*', &efstart, &efend) ) { + if ( add_range(item, &max_chars_removed, + efstart - item->text, efend - item->text, + &n_add, &max_add, &add, 'b') ) return; + text = g_utf8_find_next_char(efend, NULL); + } + text = item->text; + while ( find_pair(text, '/', &efstart, &efend) ) { + if ( add_range(item, &max_chars_removed, + efstart - item->text, efend - item->text, + &n_add, &max_add, &add, 'i') ) return; + text = g_utf8_find_next_char(efend, NULL); + } + text = item->text; + while ( find_pair(text, '_', &efstart, &efend) ) { + if ( add_range(item, &max_chars_removed, + efstart - item->text, efend - item->text, + &n_add, &max_add, &add, 'u') ) return; + text = g_utf8_find_next_char(efend, NULL); + } + + /* Sort the list of removed characters */ + qsort(item->chars_removed, item->n_chars_removed, sizeof(int), cmpi); + + /* Go through the list of attributes, and correct the character ranges + * so that they refer to the text with characters removed, and add them + * to the PangoAttrList */ + for ( i=0; istart_index = index_with_removal(item->chars_removed, + item->n_chars_removed, + add[i].start); + + attr->end_index = index_with_removal(item->chars_removed, + item->n_chars_removed, + add[i].end) + 1; + + pango_attr_list_insert(attrs, attr); + + } + + /* Create the version of the text with characters removed */ + if ( item->n_chars_removed == 0 ) { + item->layout_text = strdup(item->text); + } else { + len = strlen(item->text); + item->layout_text = malloc(len); + if ( item->layout_text == NULL ) return; + j = 0; + for ( i=0; ichars_removed[k] == i ) { + k++; + } else { + item->layout_text[j++] = item->text[i]; + } + } + } + + free(add); +} + + static void wrap_text(struct narrative_item *item, PangoContext *pc, Stylesheet *ss, const char *stn, double w, size_t sel_start, size_t sel_end) @@ -120,14 +313,15 @@ static void wrap_text(struct narrative_item *item, PangoContext *pc, item->layout = pango_layout_new(pc); } pango_layout_set_width(item->layout, pango_units_from_double(wrap_w)); - pango_layout_set_text(item->layout, item->text, -1); pango_layout_set_alignment(item->layout, palignment); pango_layout_set_font_description(item->layout, fontdesc); + + /* Handle *bold*, _underline_, /italic/ etc. */ + process_tags(item, attrs); pango_layout_set_attributes(item->layout, attrs); + pango_attr_list_unref(attrs); - /* FIXME: Handle *bold*, _underline_, /italic/ etc. */ - //pango_layout_set_attributes(item->layout, attrs); - //pango_attr_list_unref(attrs); + pango_layout_set_text(item->layout, item->layout_text, -1); pango_layout_get_extents(item->layout, NULL, &rect); item->obj_w = pango_units_to_double(rect.width); -- cgit v1.2.3