1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2007, 2009 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/>. */
25 #include <data/file-name.h>
26 #include <data/settings.h>
27 #include <libpspp/assertion.h>
28 #include <libpspp/compiler.h>
29 #include <libpspp/start-date.h>
30 #include <libpspp/string-map.h>
31 #include <libpspp/version.h>
32 #include <output/cairo.h>
33 #include <output/chart-item-provider.h>
34 #include "output/options.h"
35 #include <output/tab.h>
36 #include <output/text-item.h>
37 #include <output/driver-provider.h>
38 #include <output/render.h>
39 #include <output/table-item.h>
46 #define _(msgid) gettext (msgid)
48 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
52 /* Line styles bit shifts. */
64 make_box_index (int left, int right, int top, int bottom)
66 return ((left << LNS_LEFT) | (right << LNS_RIGHT)
67 | (top << LNS_TOP) | (bottom << LNS_BOTTOM));
70 /* Character attributes. */
71 #define ATTR_EMPHASIS 0x100 /* Bold-face. */
72 #define ATTR_BOX 0x200 /* Line drawing character. */
77 unsigned short *chars; /* Characters and attributes. */
78 int n_chars; /* Length. */
79 int allocated_chars; /* Allocated "chars" elements. */
82 /* How to emphasize text. */
85 EMPH_BOLD, /* Overstrike for bold. */
86 EMPH_UNDERLINE, /* Overstrike for underlining. */
87 EMPH_NONE /* No emphasis. */
90 /* ASCII output driver. */
93 struct output_driver driver;
95 /* User parameters. */
96 bool append; /* Append if output-file already exists? */
97 bool headers; /* Print headers at top of page? */
98 bool paginate; /* Insert formfeeds? */
99 bool squeeze_blank_lines; /* Squeeze multiple blank lines into one? */
100 enum emphasis_style emphasis; /* How to emphasize text. */
101 int tab_width; /* Width of a tab; 0 not to use tabs. */
102 char *chart_file_name; /* Name of files used for charts. */
104 int width; /* Page width. */
105 int length; /* Page length minus margins and header. */
106 bool auto_width; /* Use viewwidth as page width? */
107 bool auto_length; /* Use viewlength as page width? */
109 int top_margin; /* Top margin in lines. */
110 int bottom_margin; /* Bottom margin in lines. */
112 char *box[LNS_COUNT]; /* Line & box drawing characters. */
113 char *init; /* Device initialization string. */
115 /* Internal state. */
118 char *file_name; /* Output file name. */
119 FILE *file; /* Output file. */
120 bool reported_error; /* Reported file open error? */
121 int page_number; /* Current page number. */
122 struct ascii_line *lines; /* Page content. */
123 int allocated_lines; /* Number of lines allocated. */
124 int chart_cnt; /* Number of charts so far. */
128 static int vertical_margins (const struct ascii_driver *);
130 static const char *get_default_box (int right, int bottom, int left, int top);
131 static bool update_page_size (struct ascii_driver *, bool issue_error);
132 static int parse_page_size (struct driver_option *);
134 static void ascii_close_page (struct ascii_driver *);
135 static void ascii_open_page (struct ascii_driver *);
137 static void ascii_draw_line (void *, int bb[TABLE_N_AXES][2],
138 enum render_line_style styles[TABLE_N_AXES][2]);
139 static void ascii_measure_cell_width (void *, const struct table_cell *,
141 static int ascii_measure_cell_height (void *, const struct table_cell *,
143 static void ascii_draw_cell (void *, const struct table_cell *,
144 int bb[TABLE_N_AXES][2],
145 int clip[TABLE_N_AXES][2]);
147 static struct ascii_driver *
148 ascii_driver_cast (struct output_driver *driver)
150 assert (driver->class == &ascii_class);
151 return UP_CAST (driver, struct ascii_driver, driver);
154 static struct driver_option *
155 opt (struct output_driver *d, struct string_map *options, const char *key,
156 const char *default_value)
158 return driver_option_get (d, options, key, default_value);
161 static struct output_driver *
162 ascii_create (const char *name, enum output_device_type device_type,
163 struct string_map *o)
165 struct output_driver *d;
166 struct ascii_driver *a;
168 int right, bottom, left, top;
170 a = xzalloc (sizeof *a);
172 output_driver_init (&a->driver, &ascii_class, name, device_type);
173 a->append = parse_boolean (opt (d, o, "append", "false"));
174 a->headers = parse_boolean (opt (d, o, "headers", "true"));
175 a->paginate = parse_boolean (opt (d, o, "paginate", "true"));
176 a->squeeze_blank_lines = parse_boolean (opt (d, o, "squeeze", "false"));
177 a->emphasis = parse_enum (opt (d, o, "emphasis", "bold"),
179 "underline", EMPH_UNDERLINE,
182 a->tab_width = parse_int (opt (d, o, "tab-width", "0"), 8, INT_MAX);
184 if (parse_enum (opt (d, o, "chart-type", "png"),
188 a->chart_file_name = parse_chart_file_name (opt (d, o, "chart-files",
191 a->chart_file_name = NULL;
193 a->top_margin = parse_int (opt (d, o, "top-margin", "2"), 0, INT_MAX);
194 a->bottom_margin = parse_int (opt (d, o, "bottom-margin", "2"), 0, INT_MAX);
196 a->width = parse_page_size (opt (d, o, "width", "79"));
197 paper_length = parse_page_size (opt (d, o, "length", "66"));
198 a->auto_width = a->width < 0;
199 a->auto_length = paper_length < 0;
200 a->length = paper_length - vertical_margins (a);
202 for (right = 0; right < 4; right++)
203 for (bottom = 0; bottom < 4; bottom++)
204 for (left = 0; left < 4; left++)
205 for (top = 0; top < 4; top++)
207 int indx = make_box_index (left, right, top, bottom);
208 const char *default_value;
211 sprintf (name, "box[%d%d%d%d]", right, bottom, left, top);
212 default_value = get_default_box (right, bottom, left, top);
213 a->box[indx] = parse_string (opt (d, o, name, default_value));
215 a->init = parse_string (opt (d, o, "init", ""));
217 a->title = xstrdup ("");
218 a->subtitle = xstrdup ("");
219 a->file_name = parse_string (opt (d, o, "output-file", "pspp.list"));
221 a->reported_error = false;
224 a->allocated_lines = 0;
227 if (!update_page_size (a, true))
233 output_driver_destroy (d);
238 get_default_box (int right, int bottom, int left, int top)
240 switch ((top << 12) | (left << 8) | (bottom << 4) | (right << 0))
245 case 0x0100: case 0x0101: case 0x0001:
248 case 0x1000: case 0x1010: case 0x0010:
251 case 0x0300: case 0x0303: case 0x0003:
252 case 0x0200: case 0x0202: case 0x0002:
256 return left > 1 || top > 1 || right > 1 || bottom > 1 ? "#" : "+";
261 parse_page_size (struct driver_option *option)
263 int dim = atol (option->default_value);
265 if (option->value != NULL)
267 if (!strcmp (option->value, "auto"))
275 value = strtol (option->value, &tail, 0);
276 if (dim >= 1 && errno != ERANGE && *tail == '\0')
279 error (0, 0, _("%s: %s must be positive integer or `auto'"),
280 option->driver_name, option->name);
284 driver_option_destroy (option);
290 vertical_margins (const struct ascii_driver *a)
292 return a->top_margin + a->bottom_margin + (a->headers ? 3 : 0);
295 /* Re-calculates the page width and length based on settings,
296 margins, and, if "auto" is set, the size of the user's
297 terminal window or GUI output window. */
299 update_page_size (struct ascii_driver *a, bool issue_error)
301 enum { MIN_WIDTH = 6, MIN_LENGTH = 6 };
304 a->width = settings_get_viewwidth ();
306 a->length = settings_get_viewlength () - vertical_margins (a);
308 if (a->width < MIN_WIDTH || a->length < MIN_LENGTH)
312 _("ascii: page excluding margins and headers "
313 "must be at least %d characters wide by %d lines long, but "
314 "as configured is only %d characters by %d lines"),
315 MIN_WIDTH, MIN_LENGTH,
316 a->width, a->length);
317 if (a->width < MIN_WIDTH)
318 a->width = MIN_WIDTH;
319 if (a->length < MIN_LENGTH)
320 a->length = MIN_LENGTH;
328 ascii_destroy (struct output_driver *driver)
330 struct ascii_driver *a = ascii_driver_cast (driver);
334 ascii_close_page (a);
339 free (a->chart_file_name);
340 for (i = 0; i < LNS_COUNT; i++)
345 for (i = 0; i < a->allocated_lines; i++)
346 free (a->lines[i].chars);
352 ascii_flush (struct output_driver *driver)
354 struct ascii_driver *a = ascii_driver_cast (driver);
360 ascii_init_caption_cell (const char *caption, struct table_cell *cell)
362 cell->contents = caption;
363 cell->options = TAB_LEFT;
364 cell->destructor = NULL;
368 ascii_submit (struct output_driver *driver,
369 const struct output_item *output_item)
371 struct ascii_driver *a = ascii_driver_cast (driver);
372 if (is_table_item (output_item))
374 struct table_item *table_item = to_table_item (output_item);
375 const char *caption = table_item_get_caption (table_item);
376 struct render_params params;
377 struct render_page *page;
378 struct render_break x_break;
382 update_page_size (a, false);
386 /* XXX doesn't do well with very large captions */
387 struct table_cell cell;
388 ascii_init_caption_cell (caption, &cell);
389 caption_height = ascii_measure_cell_height (a, &cell, a->width);
394 params.draw_line = ascii_draw_line;
395 params.measure_cell_width = ascii_measure_cell_width;
396 params.measure_cell_height = ascii_measure_cell_height;
397 params.draw_cell = ascii_draw_cell,
399 params.size[H] = a->width;
400 params.size[V] = a->length - caption_height;
401 params.font_size[H] = 1;
402 params.font_size[V] = 1;
403 for (i = 0; i < RENDER_N_LINES; i++)
405 int width = i == RENDER_LINE_NONE ? 0 : 1;
406 params.line_widths[H][i] = width;
407 params.line_widths[V][i] = width;
416 page = render_page_create (¶ms, table_item_get_table (table_item));
417 for (render_break_init (&x_break, page, H);
418 render_break_has_next (&x_break); )
420 struct render_page *x_slice;
421 struct render_break y_break;
423 x_slice = render_break_next (&x_break, a->width);
424 for (render_break_init (&y_break, x_slice, V);
425 render_break_has_next (&y_break); )
427 struct render_page *y_slice;
433 space = a->length - a->y - caption_height;
434 if (render_break_next_size (&y_break) > space)
437 ascii_close_page (a);
443 y_slice = render_break_next (&y_break, space);
446 struct table_cell cell;
447 int bb[TABLE_N_AXES][2];
449 ascii_init_caption_cell (caption, &cell);
453 bb[V][1] = caption_height;
454 ascii_draw_cell (a, &cell, bb, bb);
455 a->y += caption_height;
458 render_page_draw (y_slice);
459 a->y += render_page_get_size (y_slice, V);
460 render_page_unref (y_slice);
462 render_break_destroy (&y_break);
464 render_break_destroy (&x_break);
466 else if (is_chart_item (output_item) && a->chart_file_name != NULL)
468 struct chart_item *chart_item = to_chart_item (output_item);
471 file_name = xr_draw_png_chart (chart_item, a->chart_file_name,
473 if (file_name != NULL)
475 struct text_item *text_item;
477 text_item = text_item_create_format (
478 TEXT_ITEM_PARAGRAPH, _("See %s for a chart."), file_name);
480 ascii_submit (driver, &text_item->output_item);
481 text_item_unref (text_item);
485 else if (is_text_item (output_item))
487 const struct text_item *text_item = to_text_item (output_item);
488 enum text_item_type type = text_item_get_type (text_item);
489 const char *text = text_item_get_text (text_item);
493 case TEXT_ITEM_TITLE:
495 a->title = xstrdup (text);
498 case TEXT_ITEM_SUBTITLE:
500 a->subtitle = xstrdup (text);
503 case TEXT_ITEM_COMMAND_CLOSE:
506 case TEXT_ITEM_BLANK_LINE:
511 case TEXT_ITEM_EJECT_PAGE:
513 ascii_close_page (a);
518 struct table_item *item;
520 item = table_item_create (table_from_string (0, text), NULL);
521 ascii_submit (&a->driver, &item->output_item);
522 table_item_unref (item);
529 const struct output_driver_class ascii_class =
545 static void ascii_expand_line (struct ascii_driver *, int y, int length);
546 static void ascii_layout_cell (struct ascii_driver *,
547 const struct table_cell *,
548 int bb[TABLE_N_AXES][2],
549 int clip[TABLE_N_AXES][2], enum wrap_mode wrap,
550 int *width, int *height);
553 ascii_draw_line (void *a_, int bb[TABLE_N_AXES][2],
554 enum render_line_style styles[TABLE_N_AXES][2])
556 struct ascii_driver *a = a_;
557 unsigned short int value;
561 /* Clip to the page. */
562 if (bb[H][0] >= a->width || bb[V][0] + a->y >= a->length)
564 x1 = MIN (bb[H][1], a->width);
565 y1 = MIN (bb[V][1] + a->y, a->length);
568 value = ATTR_BOX | make_box_index (styles[V][0], styles[V][1],
569 styles[H][0], styles[H][1]);
570 for (y = bb[V][0] + a->y; y < y1; y++)
572 ascii_expand_line (a, y, x1);
573 for (x = bb[H][0]; x < x1; x++)
574 a->lines[y].chars[x] = value;
579 ascii_measure_cell_width (void *a_, const struct table_cell *cell,
580 int *min_width, int *max_width)
582 struct ascii_driver *a = a_;
583 int bb[TABLE_N_AXES][2];
584 int clip[TABLE_N_AXES][2];
591 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
592 ascii_layout_cell (a, cell, bb, clip, WRAP_WORD, max_width, &h);
594 if (strchr (cell->contents, ' '))
597 ascii_layout_cell (a, cell, bb, clip, WRAP_WORD, min_width, &h);
600 *min_width = *max_width;
604 ascii_measure_cell_height (void *a_, const struct table_cell *cell, int width)
606 struct ascii_driver *a = a_;
607 int bb[TABLE_N_AXES][2];
608 int clip[TABLE_N_AXES][2];
615 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
616 ascii_layout_cell (a, cell, bb, clip, WRAP_WORD, &w, &h);
621 ascii_draw_cell (void *a_, const struct table_cell *cell,
622 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
624 struct ascii_driver *a = a_;
627 ascii_layout_cell (a, cell, bb, clip, WRAP_WORD, &w, &h);
630 /* Ensures that at least the first LENGTH characters of line Y in
631 ascii driver A have been cleared out. */
633 ascii_expand_line (struct ascii_driver *a, int y, int length)
635 struct ascii_line *line = &a->lines[y];
636 if (line->n_chars < length)
639 if (line->allocated_chars < length)
641 line->allocated_chars = MAX (length, MIN (length * 2, a->width));
642 line->chars = xnrealloc (line->chars, line->allocated_chars,
643 sizeof *line->chars);
645 for (x = line->n_chars; x < length; x++)
646 line->chars[x] = ' ';
647 line->n_chars = length;
652 text_draw (struct ascii_driver *a, const struct table_cell *cell,
653 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
654 int y, const char *string, int n)
656 int x0 = MAX (0, clip[H][0]);
657 int y0 = MAX (0, clip[V][0] + a->y);
659 int y1 = MIN (a->length, clip[V][1] + a->y);
663 if (y < y0 || y >= y1)
666 switch (cell->options & TAB_ALIGNMENT)
672 x = (bb[H][0] + bb[H][1] - n + 1) / 2;
694 int attr = cell->options & TAB_EMPH ? ATTR_EMPHASIS : 0;
697 ascii_expand_line (a, y, x + n);
698 for (i = 0; i < n; i++)
699 a->lines[y].chars[x + i] = string[i] | attr;
704 ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
705 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
706 enum wrap_mode wrap, int *width, int *height)
708 size_t length = strlen (cell->contents);
713 for (y = bb[V][0]; y < bb[V][1] && pos < length; y++)
715 const char *line = &cell->contents[pos];
716 const char *new_line;
719 /* Find line length without considering word wrap. */
720 line_len = MIN (bb[H][1] - bb[H][0], length - pos);
721 new_line = memchr (line, '\n', line_len);
722 if (new_line != NULL)
723 line_len = new_line - line;
726 if (pos + line_len < length && wrap != WRAP_CHAR)
728 size_t space_len = line_len;
729 while (space_len > 0 && !isspace ((unsigned char) line[space_len]))
732 line_len = space_len;
733 else if (wrap == WRAP_WORD)
735 while (pos + line_len < length
736 && !isspace ((unsigned char) line[line_len]))
740 if (line_len > *width)
744 text_draw (a, cell, bb, clip, y, line, line_len);
748 if (pos < length && isspace ((unsigned char) cell->contents[pos]))
751 *height = y - bb[V][0];
754 /* ascii_close_page () and support routines. */
757 ascii_open_page (struct ascii_driver *a)
763 a->file = fn_open (a->file_name, a->append ? "a" : "w");
767 fputs (a->init, a->file);
771 /* Report the error to the user and complete
772 initialization. If we do not finish initialization,
773 then calls to other driver functions will segfault
774 later. It would be better to simply drop the driver
775 entirely, but we do not have a convenient mechanism
777 if (!a->reported_error)
778 error (0, errno, _("ascii: opening output file \"%s\""),
780 a->reported_error = true;
786 if (a->length > a->allocated_lines)
788 a->lines = xnrealloc (a->lines, a->length, sizeof *a->lines);
789 for (i = a->allocated_lines; i < a->length; i++)
791 struct ascii_line *line = &a->lines[i];
793 line->allocated_chars = 0;
795 a->allocated_lines = a->length;
798 for (i = 0; i < a->length; i++)
799 a->lines[i].n_chars = 0;
802 /* Writes LINE to A's output file. */
804 output_line (struct ascii_driver *a, const struct ascii_line *line)
809 length = line->n_chars;
810 while (length > 0 && line->chars[length - 1] == ' ')
813 for (i = 0; i < length; i++)
815 int attribute = line->chars[i] & (ATTR_BOX | ATTR_EMPHASIS);
816 int ch = line->chars[i] & ~(ATTR_BOX | ATTR_EMPHASIS);
821 fputs (a->box[ch], a->file);
825 if (a->emphasis == EMPH_BOLD)
826 fprintf (a->file, "%c\b%c", ch, ch);
827 else if (a->emphasis == EMPH_UNDERLINE)
828 fprintf (a->file, "_\b%c", ch);
839 putc ('\n', a->file);
843 output_title_line (FILE *out, int width, const char *left, const char *right)
845 struct string s = DS_EMPTY_INITIALIZER;
846 ds_put_char_multiple (&s, ' ', width);
849 size_t length = MIN (strlen (left), width);
850 memcpy (ds_end (&s) - width, left, length);
854 size_t length = MIN (strlen (right), width);
855 memcpy (ds_end (&s) - length, right, length);
857 ds_put_char (&s, '\n');
858 fputs (ds_cstr (&s), out);
863 ascii_close_page (struct ascii_driver *a)
871 if (!a->top_margin && !a->bottom_margin && a->squeeze_blank_lines
872 && !a->paginate && a->page_number > 1)
873 putc ('\n', a->file);
875 for (i = 0; i < a->top_margin; i++)
876 putc ('\n', a->file);
881 r1 = xasprintf (_("%s - Page %d"), get_start_date (), a->page_number);
882 r2 = xasprintf ("%s - %s" , version, host_system);
884 output_title_line (a->file, a->width, a->title, r1);
885 output_title_line (a->file, a->width, a->subtitle, r2);
886 putc ('\n', a->file);
893 for (y = 0; y < a->allocated_lines; y++)
895 struct ascii_line *line = &a->lines[y];
897 if (a->squeeze_blank_lines && y > 0 && line->n_chars == 0)
903 putc ('\n', a->file);
907 output_line (a, line);
910 if (!a->squeeze_blank_lines)
911 for (y = a->allocated_lines; y < a->length; y++)
912 putc ('\n', a->file);
914 for (i = 0; i < a->bottom_margin; i++)
915 putc ('\n', a->file);
917 putc ('\f', a->file);