aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas White <taw@bitwiz.me.uk>2019-08-18 22:38:13 +0200
committerThomas White <taw@bitwiz.me.uk>2019-08-18 22:38:13 +0200
commit16daa2c43091242b142074405e0beeff16d2b133 (patch)
tree74ea202c4e7b86b361f93f059acecef24eeae967
parent3397fb32fc7a41722d5b4d5027c7a353b205887b (diff)
Handle *bold*, /italic/ etc
-rw-r--r--libstorycode/narrative.c7
-rw-r--r--libstorycode/narrative_priv.h3
-rw-r--r--libstorycode/narrative_render_cairo.c202
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<n_chars_removed; i++ ) {
+ if ( chars_removed[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<n_chars_removed; i++ ) {
+ if ( chars_removed[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; i<n_add; i++ ) {
+
+ switch ( add[i].type ) {
+
+ case 'b' :
+ attr = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
+ break;
+
+ case 'i' :
+ attr = pango_attr_style_new(PANGO_STYLE_ITALIC);
+ break;
+
+ case 'u' :
+ attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
+ break;
+
+ }
+
+ attr->start_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; i<len+1; i++ ) { /* \0 terminator please */
+ if ( item->chars_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);