1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2007, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
30 #include "data/file-name.h"
31 #include "data/file-handle-def.h"
32 #include "data/settings.h"
33 #include "libpspp/assertion.h"
34 #include "libpspp/cast.h"
35 #include "libpspp/compiler.h"
36 #include "libpspp/message.h"
37 #include "libpspp/start-date.h"
38 #include "libpspp/string-map.h"
39 #include "libpspp/u8-line.h"
40 #include "libpspp/version.h"
41 #include "output/ascii.h"
42 #include "output/cairo.h"
43 #include "output/chart-item-provider.h"
44 #include "output/driver-provider.h"
45 #include "output/message-item.h"
46 #include "output/options.h"
47 #include "output/render.h"
48 #include "output/table-item.h"
49 #include "output/text-item.h"
51 #include "gl/minmax.h"
52 #include "gl/xalloc.h"
56 #define _(msgid) gettext (msgid)
58 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
70 #define N_BOX (ASCII_N_LINES * ASCII_N_LINES \
71 * ASCII_N_LINES * ASCII_N_LINES)
73 static const ucs4_t ascii_box_chars[N_BOX] =
104 static const ucs4_t unicode_box_chars[N_BOX] =
106 0x0020, 0x2575, 0x2551,
107 0x2574, 0x256f, 0x255c,
108 0x2550, 0x255b, 0x255d,
109 0x2577, 0x2502, 0x2551,
110 0x256e, 0x2524, 0x2562,
111 0x2555, 0x2561, 0x2563,
112 0x2551, 0x2551, 0x2551,
113 0x2556, 0x2562, 0x2562,
114 0x2557, 0x2563, 0x2563,
115 0x2576, 0x2570, 0x2559,
116 0x2500, 0x2534, 0x2568,
117 0x2550, 0x2567, 0x2569,
118 0x256d, 0x251c, 0x255f,
119 0x252c, 0x253c, 0x256a,
120 0x2564, 0x256a, 0x256c,
121 0x2553, 0x255f, 0x255f,
122 0x2565, 0x256b, 0x256b,
123 0x2566, 0x256c, 0x256c,
124 0x2550, 0x2558, 0x255a,
125 0x2550, 0x2567, 0x2569,
126 0x2550, 0x2567, 0x2569,
127 0x2552, 0x255e, 0x2560,
128 0x2564, 0x256a, 0x256c,
129 0x2564, 0x256a, 0x256c,
130 0x2554, 0x2560, 0x2560,
131 0x2560, 0x256c, 0x256c,
132 0x2566, 0x256c, 0x256c,
136 ascii_line_from_render_line (int render_line)
140 case RENDER_LINE_NONE:
141 return ASCII_LINE_NONE;
143 case RENDER_LINE_SINGLE:
144 case RENDER_LINE_DASHED:
145 case RENDER_LINE_THICK:
146 case RENDER_LINE_THIN:
147 return ASCII_LINE_SINGLE;
149 case RENDER_LINE_DOUBLE:
150 return ASCII_LINE_DOUBLE;
153 return ASCII_LINE_NONE;
159 make_box_index (int left_, int right_, int top_, int bottom_)
161 bool rtl = render_direction_rtl ();
162 int left = ascii_line_from_render_line (rtl ? right_ : left_);
163 int right = ascii_line_from_render_line (rtl ? left_ : right_);
164 int top = ascii_line_from_render_line (top_);
165 int bottom = ascii_line_from_render_line (bottom_);
168 idx = idx * ASCII_N_LINES + bottom;
169 idx = idx * ASCII_N_LINES + left;
170 idx = idx * ASCII_N_LINES + top;
174 /* ASCII output driver. */
177 struct output_driver driver;
179 /* User parameters. */
180 bool append; /* Append if output file already exists? */
181 bool emphasis; /* Enable bold and underline in output? */
182 char *chart_file_name; /* Name of files used for charts. */
185 /* Colours for charts */
190 int width; /* Page width. */
191 bool auto_width; /* Use viewwidth as page width? */
193 int min_break[TABLE_N_AXES]; /* Min cell size to break across pages. */
195 const ucs4_t *box; /* Line & box drawing characters. */
197 /* Internal state. */
198 struct file_handle *handle;
199 FILE *file; /* Output file. */
200 bool error; /* Output error? */
201 struct u8_line *lines; /* Page content. */
202 int allocated_lines; /* Number of lines allocated. */
203 int chart_cnt; /* Number of charts so far. */
204 struct render_params params;
207 static const struct output_driver_class ascii_driver_class;
209 static void ascii_submit (struct output_driver *, const struct output_item *);
211 static bool update_page_size (struct ascii_driver *, bool issue_error);
212 static int parse_page_size (struct driver_option *);
214 static bool ascii_open_page (struct ascii_driver *);
216 static void ascii_draw_line (void *, int bb[TABLE_N_AXES][2],
217 enum render_line_style styles[TABLE_N_AXES][2],
218 struct cell_color colors[TABLE_N_AXES][2]);
219 static void ascii_measure_cell_width (void *, const struct table_cell *,
221 static int ascii_measure_cell_height (void *, const struct table_cell *,
223 static void ascii_draw_cell (void *, const struct table_cell *, int color_idx,
224 int bb[TABLE_N_AXES][2],
225 int spill[TABLE_N_AXES][2],
226 int clip[TABLE_N_AXES][2]);
228 static struct ascii_driver *
229 ascii_driver_cast (struct output_driver *driver)
231 assert (driver->class == &ascii_driver_class);
232 return UP_CAST (driver, struct ascii_driver, driver);
235 static struct driver_option *
236 opt (struct output_driver *d, struct string_map *options, const char *key,
237 const char *default_value)
239 return driver_option_get (d, options, key, default_value);
242 static struct output_driver *
243 ascii_create (struct file_handle *fh, enum settings_output_devices device_type,
244 struct string_map *o)
246 enum { BOX_ASCII, BOX_UNICODE } box;
247 int min_break[TABLE_N_AXES];
248 struct output_driver *d;
249 struct ascii_driver *a;
251 a = xzalloc (sizeof *a);
253 output_driver_init (&a->driver, &ascii_driver_class, fh_get_file_name (fh), device_type);
254 a->append = parse_boolean (opt (d, o, "append", "false"));
255 a->emphasis = parse_boolean (opt (d, o, "emphasis", "false"));
257 a->chart_file_name = parse_chart_file_name (opt (d, o, "charts", fh_get_file_name (fh)));
260 min_break[H] = parse_int (opt (d, o, "min-hbreak", "-1"), -1, INT_MAX);
262 a->width = parse_page_size (opt (d, o, "width", "79"));
263 a->auto_width = a->width < 0;
264 a->min_break[H] = min_break[H] >= 0 ? min_break[H] : a->width / 2;
266 parse_color (d, o, "background-color", "#FFFFFFFFFFFF", &a->bg);
267 parse_color (d, o, "foreground-color", "#000000000000", &a->fg);
269 box = parse_enum (opt (d, o, "box", "ascii"),
271 "unicode", BOX_UNICODE,
273 a->box = box == BOX_ASCII ? ascii_box_chars : unicode_box_chars;
278 a->allocated_lines = 0;
281 a->params.draw_line = ascii_draw_line;
282 a->params.measure_cell_width = ascii_measure_cell_width;
283 a->params.measure_cell_height = ascii_measure_cell_height;
284 a->params.adjust_break = NULL;
285 a->params.draw_cell = ascii_draw_cell;
287 a->params.size[H] = a->width;
288 a->params.size[V] = INT_MAX;
289 a->params.font_size[H] = 1;
290 a->params.font_size[V] = 1;
291 for (int i = 0; i < RENDER_N_LINES; i++)
293 int width = i == RENDER_LINE_NONE ? 0 : 1;
294 a->params.line_widths[H][i] = width;
295 a->params.line_widths[V][i] = width;
297 for (int i = 0; i < TABLE_N_AXES; i++)
298 a->params.min_break[i] = a->min_break[i];
299 a->params.supports_margins = false;
300 a->params.rtl = render_direction_rtl ();
302 if (!update_page_size (a, true))
308 output_driver_destroy (d);
313 parse_page_size (struct driver_option *option)
315 int dim = atol (option->default_value);
317 if (option->value != NULL)
319 if (!strcmp (option->value, "auto"))
327 value = strtol (option->value, &tail, 0);
328 if (dim >= 1 && errno != ERANGE && *tail == '\0')
331 msg (MW, _("%s: %s must be positive integer or `auto'"),
332 option->driver_name, option->name);
336 driver_option_destroy (option);
341 /* Re-calculates the page width based on settings, margins, and, if "auto" is
342 set, the size of the user's terminal window or GUI output window. */
344 update_page_size (struct ascii_driver *a, bool issue_error)
346 enum { MIN_WIDTH = 6, MIN_LENGTH = 6 };
350 a->params.size[H] = a->width = settings_get_viewwidth ();
351 a->params.min_break[H] = a->min_break[H] = a->width / 2;
354 if (a->width < MIN_WIDTH)
358 _("ascii: page must be at least %d characters wide, but "
359 "as configured is only %d characters"),
362 if (a->width < MIN_WIDTH)
363 a->params.size[H] = a->width = MIN_WIDTH;
371 ascii_destroy (struct output_driver *driver)
373 struct ascii_driver *a = ascii_driver_cast (driver);
377 fn_close (a->handle, a->file);
378 fh_unref (a->handle);
379 free (a->chart_file_name);
380 for (i = 0; i < a->allocated_lines; i++)
381 u8_line_destroy (&a->lines[i]);
387 ascii_flush (struct output_driver *driver)
389 struct ascii_driver *a = ascii_driver_cast (driver);
395 ascii_output_lines (struct ascii_driver *a, size_t n_lines)
397 for (size_t y = 0; y < n_lines; y++)
399 struct u8_line *line = &a->lines[y];
401 while (ds_chomp_byte (&line->s, ' '))
403 fwrite (ds_data (&line->s), 1, ds_length (&line->s), a->file);
404 putc ('\n', a->file);
406 u8_line_clear (&a->lines[y]);
411 ascii_output_table_item (struct ascii_driver *a,
412 const struct table_item *table_item)
414 struct render_pager *p;
416 update_page_size (a, false);
419 putc ('\n', a->file);
420 else if (!ascii_open_page (a))
423 p = render_pager_create (&a->params, table_item);
424 for (int i = 0; render_pager_has_next (p); i++)
427 putc ('\n', a->file);
428 ascii_output_lines (a, render_pager_draw_next (p, INT_MAX));
430 render_pager_destroy (p);
434 ascii_output_text (struct ascii_driver *a, const char *text)
436 struct table_item *table_item;
438 table_item = table_item_create (table_from_string (TABLE_HALIGN_LEFT, text),
440 ascii_output_table_item (a, table_item);
441 table_item_unref (table_item);
445 ascii_submit (struct output_driver *driver,
446 const struct output_item *output_item)
448 struct ascii_driver *a = ascii_driver_cast (driver);
453 if (is_table_item (output_item))
454 ascii_output_table_item (a, to_table_item (output_item));
456 else if (is_chart_item (output_item) && a->chart_file_name != NULL)
458 struct chart_item *chart_item = to_chart_item (output_item);
461 file_name = xr_draw_png_chart (chart_item, a->chart_file_name,
465 if (file_name != NULL)
467 struct text_item *text_item;
469 text_item = text_item_create_format (
470 TEXT_ITEM_PARAGRAPH, _("See %s for a chart."), file_name);
472 ascii_submit (driver, &text_item->output_item);
473 text_item_unref (text_item);
477 #endif /* HAVE_CAIRO */
478 else if (is_text_item (output_item))
480 const struct text_item *text_item = to_text_item (output_item);
481 enum text_item_type type = text_item_get_type (text_item);
485 case TEXT_ITEM_PAGE_TITLE:
486 case TEXT_ITEM_BLANK_LINE:
489 case TEXT_ITEM_EJECT_PAGE:
493 ascii_output_table_item (a, text_item_to_table_item (text_item_ref (text_item)));
497 else if (is_message_item (output_item))
499 const struct message_item *message_item = to_message_item (output_item);
500 char *s = msg_to_string (message_item_get_msg (message_item));
501 ascii_output_text (a, s);
506 const struct output_driver_factory txt_driver_factory =
507 { "txt", "-", ascii_create };
508 const struct output_driver_factory list_driver_factory =
509 { "list", "-", ascii_create };
511 static const struct output_driver_class ascii_driver_class =
519 static char *ascii_reserve (struct ascii_driver *, int y, int x0, int x1,
521 static void ascii_layout_cell (struct ascii_driver *,
522 const struct table_cell *,
523 int bb[TABLE_N_AXES][2],
524 int clip[TABLE_N_AXES][2],
525 int *width, int *height);
528 ascii_draw_line (void *a_, int bb[TABLE_N_AXES][2],
529 enum render_line_style styles[TABLE_N_AXES][2],
530 struct cell_color colors[TABLE_N_AXES][2] UNUSED)
532 struct ascii_driver *a = a_;
539 /* Clip to the page. */
540 x0 = MAX (bb[H][0], 0);
541 y0 = MAX (bb[V][0], 0);
542 x1 = MIN (bb[H][1], a->width);
544 if (x1 <= 0 || y1 <= 0 || x0 >= a->width)
548 uc = a->box[make_box_index (styles[V][0], styles[V][1],
549 styles[H][0], styles[H][1])];
550 mblen = u8_uctomb (CHAR_CAST (uint8_t *, mbchar), uc, 6);
551 for (y = y0; y < y1; y++)
553 char *p = ascii_reserve (a, y, x0, x1, mblen * (x1 - x0));
554 for (x = x0; x < x1; x++)
556 memcpy (p, mbchar, mblen);
563 ascii_measure_cell_width (void *a_, const struct table_cell *cell,
564 int *min_width, int *max_width)
566 struct ascii_driver *a = a_;
567 int bb[TABLE_N_AXES][2];
568 int clip[TABLE_N_AXES][2];
575 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
576 ascii_layout_cell (a, cell, bb, clip, max_width, &h);
578 if (cell->n_footnotes || strchr (cell->text, ' '))
581 ascii_layout_cell (a, cell, bb, clip, min_width, &h);
584 *min_width = *max_width;
588 ascii_measure_cell_height (void *a_, const struct table_cell *cell, int width)
590 struct ascii_driver *a = a_;
591 int bb[TABLE_N_AXES][2];
592 int clip[TABLE_N_AXES][2];
599 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
600 ascii_layout_cell (a, cell, bb, clip, &w, &h);
605 ascii_draw_cell (void *a_, const struct table_cell *cell, int color_idx UNUSED,
606 int bb[TABLE_N_AXES][2],
607 int spill[TABLE_N_AXES][2] UNUSED,
608 int clip[TABLE_N_AXES][2])
610 struct ascii_driver *a = a_;
613 ascii_layout_cell (a, cell, bb, clip, &w, &h);
617 ascii_reserve (struct ascii_driver *a, int y, int x0, int x1, int n)
619 if (y >= a->allocated_lines)
621 size_t new_alloc = MAX (25, a->allocated_lines);
622 while (new_alloc <= y)
623 new_alloc = xtimes (new_alloc, 2);
624 a->lines = xnrealloc (a->lines, new_alloc, sizeof *a->lines);
625 for (size_t i = a->allocated_lines; i < new_alloc; i++)
626 u8_line_init (&a->lines[i]);
627 a->allocated_lines = new_alloc;
629 return u8_line_reserve (&a->lines[y], x0, x1, n);
633 text_draw (struct ascii_driver *a, enum table_halign halign, int options,
634 bool bold, bool underline,
635 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
636 int y, const uint8_t *string, int n, size_t width)
638 int x0 = MAX (0, clip[H][0]);
639 int y0 = MAX (0, clip[V][0]);
640 int x1 = MIN (a->width, clip[H][1]);
644 if (y < y0 || y >= y1)
647 switch (table_halign_interpret (halign, options & TAB_NUMERIC))
649 case TABLE_HALIGN_LEFT:
652 case TABLE_HALIGN_CENTER:
653 x = (bb[H][0] + bb[H][1] - width + 1) / 2;
655 case TABLE_HALIGN_RIGHT:
656 case TABLE_HALIGN_DECIMAL:
657 x = bb[H][1] - width;
673 mblen = u8_mbtouc (&uc, string, n);
678 w = uc_width (uc, "UTF-8");
693 for (ofs = 0; ofs < n; )
699 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
701 w = uc_width (uc, "UTF-8");
704 if (width + w > x1 - x)
715 if (!a->emphasis || (!bold && !underline))
716 memcpy (ascii_reserve (a, y, x, x + width, n), string, n);
724 /* First figure out how many bytes need to be inserted. */
726 for (ofs = 0; ofs < n; ofs += mblen)
731 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
732 w = uc_width (uc, "UTF-8");
743 /* Then insert them. */
744 out = ascii_reserve (a, y, x, x + width, n_out);
745 for (ofs = 0; ofs < n; ofs += mblen)
750 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
751 w = uc_width (uc, "UTF-8");
757 out = mempcpy (out, string + ofs, mblen);
766 out = mempcpy (out, string + ofs, mblen);
772 add_footnote_markers (const char *text, const struct table_cell *cell)
774 struct string s = DS_EMPTY_INITIALIZER;
775 ds_put_cstr (&s, text);
776 for (size_t i = 0; i < cell->n_footnotes; i++)
777 ds_put_format (&s, "[%s]", cell->footnotes[i]->marker);
778 return ds_steal_cstr (&s);
782 ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
783 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
784 int *widthp, int *heightp)
789 /* Get the basic textual contents. */
790 const char *plain_text = (cell->options & TAB_MARKUP
791 ? output_get_text_from_markup (cell->text)
794 /* Append footnote markers if any. */
796 if (cell->n_footnotes)
798 text = add_footnote_markers (plain_text, cell);
799 if (plain_text != cell->text)
800 free (CONST_CAST (char *, plain_text));
805 /* Calculate length; if it's zero, then there's nothing to do. */
806 size_t length = strlen (text);
809 if (text != cell->text)
810 free (CONST_CAST (char *, text));
814 char *breaks = xmalloc (length + 1);
815 u8_possible_linebreaks (CHAR_CAST (const uint8_t *, text), length,
817 breaks[length] = (breaks[length - 1] == UC_BREAK_MANDATORY
818 ? UC_BREAK_PROHIBITED : UC_BREAK_POSSIBLE);
821 int bb_width = bb[H][1] - bb[H][0];
822 for (int y = bb[V][0]; y < bb[V][1] && pos < length; y++)
824 const uint8_t *line = CHAR_CAST (const uint8_t *, text + pos);
825 const char *b = breaks + pos;
826 size_t n = length - pos;
828 size_t last_break_ofs = 0;
829 int last_break_width = 0;
834 for (ofs = 0; ofs < n; )
840 mblen = u8_mbtouc (&uc, line + ofs, n - ofs);
841 if (b[ofs] == UC_BREAK_MANDATORY)
843 else if (b[ofs] == UC_BREAK_POSSIBLE)
845 last_break_ofs = ofs;
846 last_break_width = width;
849 w = uc_width (uc, "UTF-8");
852 if (width + w > bb_width)
854 if (isspace (line[ofs]))
856 else if (last_break_ofs != 0)
858 ofs = last_break_ofs;
859 width = last_break_width;
868 /* Trim any trailing spaces off the end of the text to be drawn. */
869 for (graph_ofs = ofs; graph_ofs > 0; graph_ofs--)
870 if (!isspace (line[graph_ofs - 1]))
872 width -= ofs - graph_ofs;
875 text_draw (a, cell->style->cell_style.halign, cell->options,
876 cell->style->font_style.bold,
877 cell->style->font_style.underline,
878 bb, clip, y, line, graph_ofs, width);
880 /* If a new-line ended the line, just skip the new-line. Otherwise, skip
881 past any spaces past the end of the line (but not past a new-line). */
882 if (b[ofs] == UC_BREAK_MANDATORY)
885 while (ofs < n && isspace (line[ofs]) && b[ofs] != UC_BREAK_MANDATORY)
895 if (text != cell->text)
896 free (CONST_CAST (char *, text));
900 ascii_test_write (struct output_driver *driver,
901 const char *s, int x, int y, bool bold, bool underline)
903 struct ascii_driver *a = ascii_driver_cast (driver);
904 int bb[TABLE_N_AXES][2];
907 if (a->file == NULL && !ascii_open_page (a))
910 struct area_style style = {
911 .cell_style.halign = TABLE_HALIGN_LEFT,
912 .font_style.bold = bold,
913 .font_style.underline = underline,
915 struct table_cell cell = {
916 .text = CONST_CAST (char *, s),
920 bb[TABLE_HORZ][0] = x;
921 bb[TABLE_HORZ][1] = a->width;
922 bb[TABLE_VERT][0] = y;
923 bb[TABLE_VERT][1] = INT_MAX;
925 ascii_layout_cell (a, &cell, bb, bb, &width, &height);
929 ascii_test_set_length (struct output_driver *driver, int y, int length)
931 struct ascii_driver *a = ascii_driver_cast (driver);
933 if (a->file == NULL && !ascii_open_page (a))
938 u8_line_set_length (&a->lines[y], length);
942 ascii_test_flush (struct output_driver *driver)
944 struct ascii_driver *a = ascii_driver_cast (driver);
946 for (size_t i = a->allocated_lines; i-- > 0; )
947 if (a->lines[i].width)
949 ascii_output_lines (a, i + 1);
954 /* ascii_close_page () and support routines. */
956 #if HAVE_DECL_SIGWINCH
957 static struct ascii_driver *the_driver;
960 winch_handler (int signum UNUSED)
962 update_page_size (the_driver, false);
967 ascii_open_page (struct ascii_driver *a)
974 a->file = fn_open (a->handle, a->append ? "a" : "w");
977 if ( isatty (fileno (a->file)))
979 #if HAVE_DECL_SIGWINCH
980 struct sigaction action;
981 sigemptyset (&action.sa_mask);
983 action.sa_handler = winch_handler;
985 sigaction (SIGWINCH, &action, NULL);
987 a->auto_width = true;
992 msg_error (errno, _("ascii: opening output file `%s'"),
993 fh_get_file_name (a->handle));