output: Add support for Pango markup of fonts and styles.
authorBen Pfaff <blp@cs.stanford.edu>
Wed, 26 Dec 2018 00:32:25 +0000 (16:32 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Tue, 1 Jan 2019 06:19:40 +0000 (22:19 -0800)
src/output/ascii.c
src/output/cairo.c
src/output/csv.c
src/output/driver-provider.h
src/output/driver.c
src/output/odt.c
src/output/table.h
src/output/text-item.c
src/output/text-item.h

index 7c4e5244eaa97bdf16d58e4ce22fbb9202823b20..8ac2b189abf60bb5dd766e322754ce82dd38c22b 100644 (file)
@@ -480,7 +480,6 @@ ascii_submit (struct output_driver *driver,
     {
       const struct text_item *text_item = to_text_item (output_item);
       enum text_item_type type = text_item_get_type (text_item);
-      const char *text = text_item_get_text (text_item);
 
       switch (type)
         {
@@ -492,7 +491,7 @@ ascii_submit (struct output_driver *driver,
           break;
 
         default:
-          ascii_output_text (a, text);
+          ascii_output_table_item (a, text_item_to_table_item (text_item_ref (text_item)));
           break;
         }
     }
@@ -771,6 +770,16 @@ text_draw (struct ascii_driver *a, unsigned int options,
     }
 }
 
+static char *
+add_footnote_markers (const char *text, const struct cell_contents *contents)
+{
+  struct string s = DS_EMPTY_INITIALIZER;
+  ds_put_cstr (&s, text);
+  for (size_t i = 0; i < contents->n_footnotes; i++)
+    ds_put_format (&s, "[%s]", contents->footnotes[i]->marker);
+  return ds_steal_cstr (&s);
+}
+
 static int
 ascii_layout_cell_text (struct ascii_driver *a,
                         const struct cell_contents *contents,
@@ -778,34 +787,35 @@ ascii_layout_cell_text (struct ascii_driver *a,
                         int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
                         int *widthp)
 {
-  size_t length;
-  const char *text;
   char *breaks;
   int bb_width;
   size_t pos;
-  int y;
 
-  y = bb[V][0];
-  length = strlen (contents->text);
-  if (contents->n_footnotes)
-    {
-      struct string s;
-      int i;
+  int y = bb[V][0];
 
-      ds_init_empty (&s);
-      ds_extend (&s, length + contents->n_footnotes * 4);
-      ds_put_cstr (&s, contents->text);
-      for (i = 0; i < contents->n_footnotes; i++)
-        ds_put_format (&s, "[%s]", contents->footnotes[i]->marker);
+  /* Get the basic textual contents. */
+  const char *plain_text = (contents->options & TAB_MARKUP
+                            ? output_get_text_from_markup (contents->text)
+                            : contents->text);
 
-      length = ds_length (&s);
-      text = ds_steal_cstr (&s);
+  /* Append footnote markers if any. */
+  const char *text;
+  if (contents->n_footnotes)
+    {
+      text = add_footnote_markers (plain_text, contents);
+      if (plain_text != contents->text)
+        free (CONST_CAST (char *, plain_text));
     }
   else
+    text = plain_text;
+
+  /* Calculate length; if it's zero, then there's nothing to do. */
+  size_t length = strlen (text);
+  if (!length)
     {
-      if (length == 0)
-        return y;
-      text = contents->text;
+      if (text != contents->text)
+        free (CONST_CAST (char *, text));
+      return y;
     }
 
   breaks = xmalloc (length + 1);
index 7c6cf925c64ee2418cef9966066729c987637c0a..c96384ae0a6018ceb5b02283203f55f82413d719 100644 (file)
@@ -1020,6 +1020,27 @@ add_attr_with_start (PangoAttrList *list, PangoAttribute *attr, guint start_inde
   pango_attr_list_insert (list, attr);
 }
 
+static void
+markup_escape (const char *in, struct string *out)
+{
+  for (int c = *in++; c; c = *in++)
+    switch (c)
+      {
+      case '&':
+        ds_put_cstr (out, "&amp;");
+        break;
+      case '<':
+        ds_put_cstr (out, "&lt;");
+        break;
+      case '>':
+        ds_put_cstr (out, "&gt;");
+        break;
+      default:
+        ds_put_byte (out, c);
+        break;
+      }
+}
+
 static int
 xr_layout_cell_text (struct xr_driver *xr,
                      const struct cell_contents *contents,
@@ -1114,8 +1135,23 @@ xr_layout_cell_text (struct xr_driver *xr,
         }
       size_t initial_length = ds_length (&tmp);
 
-      cell_contents_format_footnote_markers (contents, &tmp);
-      pango_layout_set_text (font->layout, ds_cstr (&tmp), ds_length (&tmp));
+      for (size_t i = 0; i < contents->n_footnotes; i++)
+        {
+          if (i)
+            ds_put_byte (&tmp, ',');
+
+          const char *marker = contents->footnotes[i]->marker;
+          if (options & TAB_MARKUP)
+            markup_escape (marker, &tmp);
+          else
+            ds_put_cstr (&tmp, marker);
+        }
+
+      if (options & TAB_MARKUP)
+        pango_layout_set_markup (font->layout,
+                                 ds_cstr (&tmp), ds_length (&tmp));
+      else
+        pango_layout_set_text (font->layout, ds_cstr (&tmp), ds_length (&tmp));
 
       PangoAttrList *attrs = pango_attr_list_new ();
       if (style->underline)
@@ -1130,7 +1166,10 @@ xr_layout_cell_text (struct xr_driver *xr,
   else
     {
       const char *content = ds_is_empty (&tmp) ? text : ds_cstr (&tmp);
-      pango_layout_set_text (font->layout, content, -1);
+      if (options & TAB_MARKUP)
+        pango_layout_set_markup (font->layout, content, -1);
+      else
+        pango_layout_set_text (font->layout, content, -1);
 
       if (style->underline)
         {
@@ -1545,7 +1584,6 @@ xr_draw_png_chart (const struct chart_item *item,
 struct xr_table_state
   {
     struct xr_render_fsm fsm;
-    struct table_item *table_item;
     struct render_pager *p;
   };
 
@@ -1575,25 +1613,24 @@ xr_table_destroy (struct xr_render_fsm *fsm)
 {
   struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
 
-  table_item_unref (ts->table_item);
   render_pager_destroy (ts->p);
   free (ts);
 }
 
 static struct xr_render_fsm *
-xr_render_table (struct xr_driver *xr, const struct table_item *table_item)
+xr_render_table (struct xr_driver *xr, struct table_item *table_item)
 {
   struct xr_table_state *ts;
 
   ts = xmalloc (sizeof *ts);
   ts->fsm.render = xr_table_render;
   ts->fsm.destroy = xr_table_destroy;
-  ts->table_item = table_item_ref (table_item);
 
   if (xr->y > 0)
     xr->y += xr->char_height;
 
   ts->p = render_pager_create (xr->params, table_item);
+  table_item_unref (table_item);
 
   return &ts->fsm;
 }
@@ -1673,29 +1710,6 @@ xr_render_eject (void)
   return &eject_renderer;
 }
 \f
-static struct xr_render_fsm *
-xr_create_text_renderer (struct xr_driver *xr, const struct text_item *item)
-{
-  struct tab_table *tab = tab_create (1, 1);
-
-  struct cell_style *style = pool_alloc (tab->container, sizeof *style);
-  *style = (struct cell_style) CELL_STYLE_INITIALIZER;
-  if (item->font)
-    style->font = pool_strdup (tab->container, item->font);
-  style->font_size = item->font_size;
-  style->bold = item->bold;
-  style->italic = item->italic;
-  style->underline = item->underline;
-  tab->styles[0] = style;
-
-  tab_text (tab, 0, 0, TAB_LEFT, text_item_get_text (item));
-  struct table_item *table_item = table_item_create (&tab->table, NULL, NULL);
-  struct xr_render_fsm *fsm = xr_render_table (xr, table_item);
-  table_item_unref (table_item);
-
-  return fsm;
-}
-
 static struct xr_render_fsm *
 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
 {
@@ -1717,7 +1731,8 @@ xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
       break;
 
     default:
-      return xr_create_text_renderer (xr, text_item);
+      return xr_render_table (
+        xr, text_item_to_table_item (text_item_ref (text_item)));
     }
 
   return NULL;
@@ -1730,10 +1745,7 @@ xr_render_message (struct xr_driver *xr,
   char *s = msg_to_string (message_item_get_msg (message_item));
   struct text_item *item = text_item_create (TEXT_ITEM_PARAGRAPH, s);
   free (s);
-  struct xr_render_fsm *fsm = xr_create_text_renderer (xr, item);
-  text_item_unref (item);
-
-  return fsm;
+  return xr_render_table (xr, text_item_to_table_item (item));
 }
 
 static struct xr_render_fsm *
@@ -1741,7 +1753,7 @@ xr_render_output_item (struct xr_driver *xr,
                        const struct output_item *output_item)
 {
   if (is_table_item (output_item))
-    return xr_render_table (xr, to_table_item (output_item));
+    return xr_render_table (xr, table_item_ref (to_table_item (output_item)));
   else if (is_chart_item (output_item))
     return xr_render_chart (to_chart_item (output_item));
   else if (is_text_item (output_item))
index 8493df34c613e02b35c519126f73364ce55e0473..17c26a062605f96523b96e9cb17ed5ca893a0f0b 100644 (file)
@@ -228,7 +228,14 @@ csv_submit (struct output_driver *driver,
                       if (i > 0)
                         ds_put_cstr (&s, "\n\n");
 
-                      ds_put_cstr (&s, c->text);
+                      if (c->options & TAB_MARKUP)
+                        {
+                          char *t = output_get_text_from_markup (c->text);
+                          ds_put_cstr (&s, t);
+                          free (t);
+                        }
+                      else
+                        ds_put_cstr (&s, c->text);
                       csv_format_footnotes (c->footnotes, c->n_footnotes, &s);
                     }
                   csv_output_field (csv, ds_cstr (&s));
@@ -272,7 +279,14 @@ csv_submit (struct output_driver *driver,
         return;
 
       csv_put_separator (csv);
-      csv_output_field (csv, text);
+      if (text_item->markup)
+        {
+          char *plain_text = output_get_text_from_markup (text);
+          csv_output_field (csv, plain_text);
+          free (plain_text);
+        }
+      else
+        csv_output_field (csv, text);
       putc ('\n', csv->file);
     }
   else if (is_message_item (output_item))
index 31503f2175a1ee57c481c8cd3293d02f6bd9fda4..bfeaca83687c534911c0eeffa5ecdec2acd5cf44 100644 (file)
@@ -103,5 +103,6 @@ struct output_driver_factory
                                      struct string_map *options);
   };
 
+char *output_get_text_from_markup (const char *markup);
 
 #endif /* output/driver-provider.h */
index 6e60258329469ed6303c62b63809703e988e62ce..b9e4b785290d38f77cab652e26996b833a7217a7 100644 (file)
@@ -21,6 +21,8 @@
 
 #include <ctype.h>
 #include <errno.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
 #include <limits.h>
 #include <stdlib.h>
 #include <string.h>
@@ -489,3 +491,24 @@ output_driver_parse_option (const char *option, struct string_map *options)
   char *value = xmemdup0 (equals + 1, strlen (equals + 1));
   string_map_insert_nocopy (options, key, value);
 }
+\f
+/* Extracts the actual text content from the given Pango MARKUP and returns it
+   as as a malloc()'d string. */
+char *
+output_get_text_from_markup (const char *markup)
+{
+  xmlDoc *doc = xmlReadMemory (markup, strlen (markup), "noname.xml", "UTF-8",
+                               (XML_PARSE_NOERROR | XML_PARSE_NOWARNING
+                                | XML_PARSE_NONET | XML_PARSE_NOCDATA));
+  if (!doc)
+    return xstrdup (markup);
+
+  char *content = CHAR_CAST (char *,
+                             xmlNodeGetContent (xmlDocGetRootElement (doc)));
+  if (!content)
+    content = xstrdup (markup);
+
+  xmlFreeDoc (doc);
+
+  return content;
+}
index 18827a600e6f0135e8d5d3c0c3469b1a20b869c7..f0143515e0aac73f6a18aff018901ecc2e416a9a 100644 (file)
@@ -530,7 +530,16 @@ write_table (struct odt_driver *odt, const struct table_item *item)
                   else
                     xmlTextWriterWriteAttribute (odt->content_wtr, _xml("text:style-name"), _xml("Table_20_Contents"));
 
-                  write_xml_with_line_breaks (odt, contents->text);
+                  if (contents->options & TAB_MARKUP)
+                    {
+                      /* XXX */
+                      char *s = output_get_text_from_markup (
+                        contents->text);
+                      write_xml_with_line_breaks (odt, s);
+                      free (s);
+                    }
+                  else
+                    write_xml_with_line_breaks (odt, contents->text);
 
                   for (j = 0; j < contents->n_footnotes; j++)
                     write_footnote (odt, contents->footnotes[j]);
index 649ae965f6ce328e20c4c320c65b6c0dd827910f..7949fedd24d4c3e986cad42e7e1ed5f4f8021b02 100644 (file)
@@ -63,11 +63,12 @@ enum
     /* These flags may be combined with any alignment. */
     TAB_EMPH       = 1 << 4,    /* Emphasize cell contents. */
     TAB_FIX        = 1 << 5,    /* Use fixed font. */
+    TAB_MARKUP     = 1 << 6,    /* Text contains Pango markup. */
 
     /* Bits with values (1 << TAB_FIRST_AVAILABLE) and higher are
        not used, so they are available for subclasses to use as
        they wish. */
-    TAB_FIRST_AVAILABLE = 6
+    TAB_FIRST_AVAILABLE = 7
   };
 
 /* Styles for the rules around table cells. */
index 2837c93bb611714ecfee4d3a8bb8b217b8fafd02..9c3645bd3e9966408de6e419e148c1a5bbd4fc8a 100644 (file)
 #include <stdlib.h>
 
 #include "libpspp/cast.h"
+#include "libpspp/pool.h"
 #include "output/driver.h"
 #include "output/output-item-provider.h"
+#include "output/tab.h"
+#include "output/table-item.h"
+#include "output/table-provider.h"
 
 #include "gl/xalloc.h"
 #include "gl/xvasprintf.h"
@@ -89,7 +93,30 @@ text_item_submit (struct text_item *item)
 {
   output_submit (&item->output_item);
 }
-\f
+
+struct table_item *
+text_item_to_table_item (struct text_item *text_item)
+{
+  struct tab_table *tab = tab_create (1, 1);
+
+  struct cell_style *style = pool_alloc (tab->container, sizeof *style);
+  *style = (struct cell_style) CELL_STYLE_INITIALIZER;
+  if (text_item->font)
+    style->font = pool_strdup (tab->container, text_item->font);
+  style->font_size = text_item->font_size;
+  style->bold = text_item->bold;
+  style->italic = text_item->italic;
+  style->underline = text_item->underline;
+  tab->styles[0] = style;
+
+  int opts = TAB_LEFT;
+  if (text_item->markup)
+    opts |= TAB_MARKUP;
+  tab_text (tab, 0, 0, opts, text_item_get_text (text_item));
+  struct table_item *table_item = table_item_create (&tab->table, NULL, NULL);
+  text_item_unref (text_item);
+  return table_item;
+}\f
 static void
 text_item_destroy (struct output_item *output_item)
 {
index 1bfb63689b28cd94f6cce1c14b7c8fa570456e03..9b8345ef827f12e58dbe202a9fc7793ffa858fc5 100644 (file)
@@ -57,7 +57,7 @@ struct text_item
     enum text_item_type type;   /* Type. */
     char *font;
     int font_size;
-    bool bold, italic, underline;
+    bool bold, italic, underline, markup;
   };
 
 struct text_item *text_item_create (enum text_item_type, const char *text);
@@ -68,6 +68,8 @@ struct text_item *text_item_create_format (enum text_item_type,
 
 enum text_item_type text_item_get_type (const struct text_item *);
 const char *text_item_get_text (const struct text_item *);
+
+struct table_item *text_item_to_table_item (struct text_item *);
 \f
 /* This boilerplate for text_item, a subclass of output_item, was
    autogenerated by mk-class-boilerplate. */