output: Add support for Pango markup of fonts and styles.
[pspp] / src / output / ascii.c
index f899c976782bfc7829275a0a6c52d8a3947eb480..8ac2b189abf60bb5dd766e322754ce82dd38c22b 100644 (file)
@@ -172,14 +172,6 @@ make_box_index (int left_, int right_, int top_, int bottom_)
   return idx;
 }
 
-/* How to emphasize text. */
-enum emphasis_style
-  {
-    EMPH_BOLD,                  /* Overstrike for bold. */
-    EMPH_UNDERLINE,             /* Overstrike for underlining. */
-    EMPH_NONE                   /* No emphasis. */
-  };
-
 /* ASCII output driver. */
 struct ascii_driver
   {
@@ -187,7 +179,7 @@ struct ascii_driver
 
     /* User parameters. */
     bool append;                /* Append if output file already exists? */
-    enum emphasis_style emphasis; /* How to emphasize text. */
+    bool emphasis;              /* Enable bold and underline in output? */
     char *chart_file_name;      /* Name of files used for charts. */
 
 #ifdef HAVE_CAIRO
@@ -210,6 +202,7 @@ struct ascii_driver
     struct u8_line *lines;      /* Page content. */
     int allocated_lines;        /* Number of lines allocated. */
     int chart_cnt;              /* Number of charts so far. */
+    struct render_params params;
   };
 
 static const struct output_driver_class ascii_driver_class;
@@ -222,13 +215,15 @@ static int parse_page_size (struct driver_option *);
 static bool ascii_open_page (struct ascii_driver *);
 
 static void ascii_draw_line (void *, int bb[TABLE_N_AXES][2],
-                             enum render_line_style styles[TABLE_N_AXES][2]);
+                             enum render_line_style styles[TABLE_N_AXES][2],
+                             struct cell_color colors[TABLE_N_AXES][2]);
 static void ascii_measure_cell_width (void *, const struct table_cell *,
                                       int *min, int *max);
 static int ascii_measure_cell_height (void *, const struct table_cell *,
                                       int width);
-static void ascii_draw_cell (void *, const struct table_cell *,
+static void ascii_draw_cell (void *, const struct table_cell *, int color_idx,
                              int bb[TABLE_N_AXES][2],
+                             int spill[TABLE_N_AXES][2],
                              int clip[TABLE_N_AXES][2]);
 
 static struct ascii_driver *
@@ -258,11 +253,7 @@ ascii_create (struct  file_handle *fh, enum settings_output_devices device_type,
   d = &a->driver;
   output_driver_init (&a->driver, &ascii_driver_class, fh_get_file_name (fh), device_type);
   a->append = parse_boolean (opt (d, o, "append", "false"));
-  a->emphasis = parse_enum (opt (d, o, "emphasis", "none"),
-                            "bold", EMPH_BOLD,
-                            "underline", EMPH_UNDERLINE,
-                            "none", EMPH_NONE,
-                            NULL_SENTINEL);
+  a->emphasis = parse_boolean (opt (d, o, "emphasis", "false"));
 
   a->chart_file_name = parse_chart_file_name (opt (d, o, "charts", fh_get_file_name (fh)));
   a->handle = fh;
@@ -288,6 +279,27 @@ ascii_create (struct  file_handle *fh, enum settings_output_devices device_type,
   a->allocated_lines = 0;
   a->chart_cnt = 1;
 
+  a->params.draw_line = ascii_draw_line;
+  a->params.measure_cell_width = ascii_measure_cell_width;
+  a->params.measure_cell_height = ascii_measure_cell_height;
+  a->params.adjust_break = NULL;
+  a->params.draw_cell = ascii_draw_cell;
+  a->params.aux = a;
+  a->params.size[H] = a->width;
+  a->params.size[V] = INT_MAX;
+  a->params.font_size[H] = 1;
+  a->params.font_size[V] = 1;
+  for (int i = 0; i < RENDER_N_LINES; i++)
+    {
+      int width = i == RENDER_LINE_NONE ? 0 : 1;
+      a->params.line_widths[H][i] = width;
+      a->params.line_widths[V][i] = width;
+    }
+  for (int i = 0; i < TABLE_N_AXES; i++)
+    a->params.min_break[i] = a->min_break[i];
+  a->params.supports_margins = false;
+  a->params.rtl = render_direction_rtl ();
+
   if (!update_page_size (a, true))
     goto error;
 
@@ -336,8 +348,8 @@ update_page_size (struct ascii_driver *a, bool issue_error)
 
   if (a->auto_width)
     {
-      a->width = settings_get_viewwidth ();
-      a->min_break[H] = a->width / 2;
+      a->params.size[H] = a->width = settings_get_viewwidth ();
+      a->params.min_break[H] = a->min_break[H] = a->width / 2;
     }
 
   if (a->width < MIN_WIDTH)
@@ -349,7 +361,7 @@ update_page_size (struct ascii_driver *a, bool issue_error)
                MIN_WIDTH,
                a->width);
       if (a->width < MIN_WIDTH)
-        a->width = MIN_WIDTH;
+        a->params.size[H] = a->width = MIN_WIDTH;
       return false;
     }
 
@@ -400,38 +412,16 @@ static void
 ascii_output_table_item (struct ascii_driver *a,
                          const struct table_item *table_item)
 {
-  struct render_params params;
   struct render_pager *p;
-  int i;
 
   update_page_size (a, false);
 
-  params.draw_line = ascii_draw_line;
-  params.measure_cell_width = ascii_measure_cell_width;
-  params.measure_cell_height = ascii_measure_cell_height;
-  params.adjust_break = NULL;
-  params.draw_cell = ascii_draw_cell;
-  params.aux = a;
-  params.size[H] = a->width;
-  params.size[V] = INT_MAX;
-  params.font_size[H] = 1;
-  params.font_size[V] = 1;
-  for (i = 0; i < RENDER_N_LINES; i++)
-    {
-      int width = i == RENDER_LINE_NONE ? 0 : 1;
-      params.line_widths[H][i] = width;
-      params.line_widths[V][i] = width;
-    }
-  for (i = 0; i < TABLE_N_AXES; i++)
-    params.min_break[i] = a->min_break[i];
-  params.supports_margins = false;
-
   if (a->file)
     putc ('\n', a->file);
   else if (!ascii_open_page (a))
     return;
 
-  p = render_pager_create (&params, table_item);
+  p = render_pager_create (&a->params, table_item);
   for (int i = 0; render_pager_has_next (p); i++)
     {
       if (i)
@@ -490,16 +480,10 @@ 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)
         {
-        case TEXT_ITEM_TITLE:
-        case TEXT_ITEM_SUBTITLE:
-        case TEXT_ITEM_COMMAND_OPEN:
-        case TEXT_ITEM_COMMAND_CLOSE:
-          break;
-
+        case TEXT_ITEM_PAGE_TITLE:
         case TEXT_ITEM_BLANK_LINE:
           break;
 
@@ -507,15 +491,14 @@ 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;
         }
     }
   else if (is_message_item (output_item))
     {
       const struct message_item *message_item = to_message_item (output_item);
-      const struct msg *msg = message_item_get_msg (message_item);
-      char *s = msg_to_string (msg, message_item->command_name);
+      char *s = msg_to_string (message_item_get_msg (message_item));
       ascii_output_text (a, s);
       free (s);
     }
@@ -544,7 +527,8 @@ static void ascii_layout_cell (struct ascii_driver *,
 
 static void
 ascii_draw_line (void *a_, int bb[TABLE_N_AXES][2],
-                 enum render_line_style styles[TABLE_N_AXES][2])
+                 enum render_line_style styles[TABLE_N_AXES][2],
+                 struct cell_color colors[TABLE_N_AXES][2] UNUSED)
 {
   struct ascii_driver *a = a_;
   char mbchar[6];
@@ -621,8 +605,10 @@ ascii_measure_cell_height (void *a_, const struct table_cell *cell, int width)
 }
 
 static void
-ascii_draw_cell (void *a_, const struct table_cell *cell,
-                 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
+ascii_draw_cell (void *a_, const struct table_cell *cell, int color_idx UNUSED,
+                 int bb[TABLE_N_AXES][2],
+                 int spill[TABLE_N_AXES][2] UNUSED,
+                 int clip[TABLE_N_AXES][2])
 {
   struct ascii_driver *a = a_;
   int w, h;
@@ -648,6 +634,7 @@ ascii_reserve (struct ascii_driver *a, int y, int x0, int x1, int n)
 
 static void
 text_draw (struct ascii_driver *a, unsigned int options,
+           bool bold, bool underline,
            int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
            int y, const uint8_t *string, int n, size_t width)
 {
@@ -727,7 +714,7 @@ text_draw (struct ascii_driver *a, unsigned int options,
         return;
     }
 
-  if (!(options & TAB_EMPH) || a->emphasis == EMPH_NONE)
+  if (!a->emphasis || (!bold && !underline))
     memcpy (ascii_reserve (a, y, x, x + width, n), string, n);
   else
     {
@@ -747,7 +734,12 @@ text_draw (struct ascii_driver *a, unsigned int options,
           w = uc_width (uc, "UTF-8");
 
           if (w > 0)
-            n_out += a->emphasis == EMPH_UNDERLINE ? 2 : 1 + mblen;
+            {
+              if (bold)
+                n_out += 1 + mblen;
+              if (underline)
+                n_out += 2;
+            }
         }
 
       /* Then insert them. */
@@ -762,51 +754,68 @@ text_draw (struct ascii_driver *a, unsigned int options,
 
           if (w > 0)
             {
-              if (a->emphasis == EMPH_UNDERLINE)
-                *out++ = '_';
-              else
-                out = mempcpy (out, string + ofs, mblen);
-              *out++ = '\b';
+              if (bold)
+                {
+                  out = mempcpy (out, string + ofs, mblen);
+                  *out++ = '\b';
+                }
+              if (underline)
+                {
+                  *out++ = '_';
+                  *out++ = '\b';
+                }
             }
           out = mempcpy (out, string + ofs, mblen);
         }
     }
 }
 
+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,
+                        bool bold, bool underline,
                         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);
@@ -870,7 +879,8 @@ ascii_layout_cell_text (struct ascii_driver *a,
       width -= ofs - graph_ofs;
 
       /* Draw text. */
-      text_draw (a, contents->options, bb, clip, y, line, graph_ofs, width);
+      text_draw (a, contents->options, bold, underline,
+                 bb, clip, y, line, graph_ofs, width);
 
       /* If a new-line ended the line, just skip the new-line.  Otherwise, skip
          past any spaces past the end of the line (but not past a new-line). */
@@ -916,14 +926,16 @@ ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
             break;
         }
 
-      bb[V][0] = ascii_layout_cell_text (a, contents, bb, clip, widthp);
+      bb[V][0] = ascii_layout_cell_text (a, contents, cell->style->bold,
+                                         cell->style->underline,
+                                         bb, clip, widthp);
     }
   *heightp = bb[V][0] - bb_[V][0];
 }
 
 void
 ascii_test_write (struct output_driver *driver,
-                  const char *s, int x, int y, unsigned int options)
+                  const char *s, int x, int y, bool bold, bool underline)
 {
   struct ascii_driver *a = ascii_driver_cast (driver);
   int bb[TABLE_N_AXES][2];
@@ -933,13 +945,17 @@ ascii_test_write (struct output_driver *driver,
     return;
 
   struct cell_contents contents = {
-    .options = options | TAB_LEFT,
+    .options = TAB_LEFT,
     .text = CONST_CAST (char *, s),
   };
-
+  struct cell_style cell_style = {
+    .bold = bold,
+    .underline = underline,
+  };
   struct table_cell cell = {
     .contents = &contents,
     .n_contents = 1,
+    .style = &cell_style,
   };
 
   bb[TABLE_HORZ][0] = x;