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/settings.h"
32 #include "libpspp/assertion.h"
33 #include "libpspp/cast.h"
34 #include "libpspp/compiler.h"
35 #include "libpspp/message.h"
36 #include "libpspp/start-date.h"
37 #include "libpspp/string-map.h"
38 #include "libpspp/u8-line.h"
39 #include "libpspp/version.h"
40 #include "output/ascii.h"
41 #include "output/cairo.h"
42 #include "output/chart-item-provider.h"
43 #include "output/driver-provider.h"
44 #include "output/message-item.h"
45 #include "output/options.h"
46 #include "output/render.h"
47 #include "output/tab.h"
48 #include "output/table-item.h"
49 #include "output/text-item.h"
51 #include "gl/minmax.h"
52 #include "gl/xalloc.h"
55 #define _(msgid) gettext (msgid)
57 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
61 #define N_BOX (RENDER_N_LINES * RENDER_N_LINES \
62 * RENDER_N_LINES * RENDER_N_LINES)
64 static const ucs4_t ascii_box_chars[N_BOX] =
95 static const ucs4_t unicode_box_chars[N_BOX] =
97 0x0020, 0x2575, 0x2551,
98 0x2574, 0x256f, 0x255c,
99 0x2550, 0x255b, 0x255d,
100 0x2577, 0x2502, 0x2551,
101 0x256e, 0x2524, 0x2562,
102 0x2555, 0x2561, 0x2563,
103 0x2551, 0x2551, 0x2551,
104 0x2556, 0x2562, 0x2562,
105 0x2557, 0x2563, 0x2563,
106 0x2576, 0x2570, 0x2559,
107 0x2500, 0x2534, 0x2568,
108 0x2550, 0x2567, 0x2569,
109 0x256d, 0x251c, 0x255f,
110 0x252c, 0x253c, 0x256a,
111 0x2564, 0x256a, 0x256c,
112 0x2553, 0x255f, 0x255f,
113 0x2565, 0x256b, 0x256b,
114 0x2566, 0x256c, 0x256c,
115 0x2550, 0x2558, 0x255a,
116 0x2550, 0x2567, 0x2569,
117 0x2550, 0x2567, 0x2569,
118 0x2552, 0x255e, 0x2560,
119 0x2564, 0x256a, 0x256c,
120 0x2564, 0x256a, 0x256c,
121 0x2554, 0x2560, 0x2560,
122 0x2560, 0x256c, 0x256c,
123 0x2566, 0x256c, 0x256c,
127 make_box_index (int left, int right, int top, int bottom)
129 return ((right * RENDER_N_LINES + bottom) * RENDER_N_LINES + left) * RENDER_N_LINES + top;
132 /* How to emphasize text. */
135 EMPH_BOLD, /* Overstrike for bold. */
136 EMPH_UNDERLINE, /* Overstrike for underlining. */
137 EMPH_NONE /* No emphasis. */
140 /* ASCII output driver. */
143 struct output_driver driver;
145 /* User parameters. */
146 bool append; /* Append if output file already exists? */
147 bool headers; /* Print headers at top of page? */
148 bool paginate; /* Insert formfeeds? */
149 bool squeeze_blank_lines; /* Squeeze multiple blank lines into one? */
150 enum emphasis_style emphasis; /* How to emphasize text. */
151 char *chart_file_name; /* Name of files used for charts. */
154 /* Colours for charts */
159 int width; /* Page width. */
160 int length; /* Page length minus margins and header. */
161 bool auto_width; /* Use viewwidth as page width? */
162 bool auto_length; /* Use viewlength as page width? */
164 int top_margin; /* Top margin in lines. */
165 int bottom_margin; /* Bottom margin in lines. */
167 int min_break[TABLE_N_AXES]; /* Min cell size to break across pages. */
169 const ucs4_t *box; /* Line & box drawing characters. */
171 /* Internal state. */
175 char *file_name; /* Output file name. */
176 FILE *file; /* Output file. */
177 bool error; /* Output error? */
178 int page_number; /* Current page number. */
179 struct u8_line *lines; /* Page content. */
180 int allocated_lines; /* Number of lines allocated. */
181 int chart_cnt; /* Number of charts so far. */
185 static const struct output_driver_class ascii_driver_class;
187 static void ascii_submit (struct output_driver *, const struct output_item *);
189 static int vertical_margins (const struct ascii_driver *);
191 static bool update_page_size (struct ascii_driver *, bool issue_error);
192 static int parse_page_size (struct driver_option *);
194 static void ascii_close_page (struct ascii_driver *);
195 static bool ascii_open_page (struct ascii_driver *);
197 static void ascii_draw_line (void *, int bb[TABLE_N_AXES][2],
198 enum render_line_style styles[TABLE_N_AXES][2]);
199 static void ascii_measure_cell_width (void *, const struct table_cell *,
201 static int ascii_measure_cell_height (void *, const struct table_cell *,
203 static void ascii_draw_cell (void *, const struct table_cell *,
204 int bb[TABLE_N_AXES][2],
205 int clip[TABLE_N_AXES][2]);
208 reallocate_lines (struct ascii_driver *a)
210 if (a->length > a->allocated_lines)
213 a->lines = xnrealloc (a->lines, a->length, sizeof *a->lines);
214 for (i = a->allocated_lines; i < a->length; i++)
215 u8_line_init (&a->lines[i]);
216 a->allocated_lines = a->length;
221 static struct ascii_driver *
222 ascii_driver_cast (struct output_driver *driver)
224 assert (driver->class == &ascii_driver_class);
225 return UP_CAST (driver, struct ascii_driver, driver);
228 static struct driver_option *
229 opt (struct output_driver *d, struct string_map *options, const char *key,
230 const char *default_value)
232 return driver_option_get (d, options, key, default_value);
235 static struct output_driver *
236 ascii_create (const char *file_name, enum settings_output_devices device_type,
237 struct string_map *o)
239 enum { BOX_ASCII, BOX_UNICODE } box;
240 int min_break[TABLE_N_AXES];
241 struct output_driver *d;
242 struct ascii_driver *a;
245 a = xzalloc (sizeof *a);
247 output_driver_init (&a->driver, &ascii_driver_class, file_name, device_type);
248 a->append = parse_boolean (opt (d, o, "append", "false"));
249 a->headers = parse_boolean (opt (d, o, "headers", "false"));
250 a->paginate = parse_boolean (opt (d, o, "paginate", "false"));
251 a->squeeze_blank_lines = parse_boolean (opt (d, o, "squeeze", "true"));
252 a->emphasis = parse_enum (opt (d, o, "emphasis", "none"),
254 "underline", EMPH_UNDERLINE,
258 a->chart_file_name = parse_chart_file_name (opt (d, o, "charts", file_name));
260 a->top_margin = parse_int (opt (d, o, "top-margin", "0"), 0, INT_MAX);
261 a->bottom_margin = parse_int (opt (d, o, "bottom-margin", "0"), 0, INT_MAX);
263 min_break[H] = parse_int (opt (d, o, "min-hbreak", "-1"), -1, INT_MAX);
264 min_break[V] = parse_int (opt (d, o, "min-vbreak", "-1"), -1, INT_MAX);
266 a->width = parse_page_size (opt (d, o, "width", "79"));
267 paper_length = parse_page_size (opt (d, o, "length", "66"));
268 a->auto_width = a->width < 0;
269 a->auto_length = paper_length < 0;
270 a->length = paper_length - vertical_margins (a);
271 a->min_break[H] = min_break[H] >= 0 ? min_break[H] : a->width / 2;
272 a->min_break[V] = min_break[V] >= 0 ? min_break[V] : a->length / 2;
274 parse_color (d, o, "background-color", "#FFFFFFFFFFFF", &a->bg);
275 parse_color (d, o, "foreground-color", "#000000000000", &a->fg);
277 box = parse_enum (opt (d, o, "box", "ascii"),
279 "unicode", BOX_UNICODE,
281 a->box = box == BOX_ASCII ? ascii_box_chars : unicode_box_chars;
283 a->command_name = NULL;
284 a->title = xstrdup ("");
285 a->subtitle = xstrdup ("");
286 a->file_name = xstrdup (file_name);
291 a->allocated_lines = 0;
294 if (!update_page_size (a, true))
300 output_driver_destroy (d);
305 parse_page_size (struct driver_option *option)
307 int dim = atol (option->default_value);
309 if (option->value != NULL)
311 if (!strcmp (option->value, "auto"))
319 value = strtol (option->value, &tail, 0);
320 if (dim >= 1 && errno != ERANGE && *tail == '\0')
323 msg (MW, _("%s: %s must be positive integer or `auto'"),
324 option->driver_name, option->name);
328 driver_option_destroy (option);
334 vertical_margins (const struct ascii_driver *a)
336 return a->top_margin + a->bottom_margin + (a->headers ? 3 : 0);
339 /* Re-calculates the page width and length based on settings,
340 margins, and, if "auto" is set, the size of the user's
341 terminal window or GUI output window. */
343 update_page_size (struct ascii_driver *a, bool issue_error)
345 enum { MIN_WIDTH = 6, MIN_LENGTH = 6 };
348 a->width = settings_get_viewwidth ();
350 a->length = settings_get_viewlength () - vertical_margins (a);
352 if (a->width < MIN_WIDTH || a->length < MIN_LENGTH)
356 _("ascii: page excluding margins and headers "
357 "must be at least %d characters wide by %d lines long, but "
358 "as configured is only %d characters by %d lines"),
359 MIN_WIDTH, MIN_LENGTH,
360 a->width, a->length);
361 if (a->width < MIN_WIDTH)
362 a->width = MIN_WIDTH;
363 if (a->length < MIN_LENGTH)
364 a->length = MIN_LENGTH;
368 reallocate_lines (a);
374 ascii_destroy (struct output_driver *driver)
376 struct ascii_driver *a = ascii_driver_cast (driver);
380 ascii_close_page (a);
383 fn_close (a->file_name, a->file);
384 free (a->command_name);
388 free (a->chart_file_name);
389 for (i = 0; i < a->allocated_lines; i++)
390 u8_line_destroy (&a->lines[i]);
396 ascii_flush (struct output_driver *driver)
398 struct ascii_driver *a = ascii_driver_cast (driver);
401 ascii_close_page (a);
403 if (fn_close (a->file_name, a->file) != 0)
404 msg_error (errno, _("ascii: closing output file `%s'"), a->file_name);
410 ascii_init_caption_cell (const char *caption, struct table_cell *cell)
412 cell->inline_contents.options = TAB_LEFT;
413 cell->inline_contents.text = CONST_CAST (char *, caption);
414 cell->inline_contents.table = NULL;
415 cell->contents = &cell->inline_contents;
416 cell->n_contents = 1;
417 cell->destructor = NULL;
421 ascii_output_table_item (struct ascii_driver *a,
422 const struct table_item *table_item)
424 const char *caption = table_item_get_caption (table_item);
425 struct render_params params;
426 struct render_pager *p;
430 update_page_size (a, false);
434 /* XXX doesn't do well with very large captions */
435 struct table_cell cell;
436 ascii_init_caption_cell (caption, &cell);
437 caption_height = ascii_measure_cell_height (a, &cell, a->width);
442 params.draw_line = ascii_draw_line;
443 params.measure_cell_width = ascii_measure_cell_width;
444 params.measure_cell_height = ascii_measure_cell_height;
445 params.adjust_break = NULL;
446 params.draw_cell = ascii_draw_cell;
448 params.size[H] = a->width;
449 params.size[V] = a->length - caption_height;
450 params.font_size[H] = 1;
451 params.font_size[V] = 1;
452 for (i = 0; i < RENDER_N_LINES; i++)
454 int width = i == RENDER_LINE_NONE ? 0 : 1;
455 params.line_widths[H][i] = width;
456 params.line_widths[V][i] = width;
458 for (i = 0; i < TABLE_N_AXES; i++)
459 params.min_break[i] = a->min_break[i];
461 if (a->file == NULL && !ascii_open_page (a))
464 p = render_pager_create (render_page_create (¶ms, table_item_get_table (
466 while (render_pager_has_next (p))
472 a->y += caption_height;
473 used = render_pager_draw_next (p, a->length - a->y);
477 ascii_close_page (a);
478 if (!ascii_open_page (a))
485 struct table_cell cell;
486 int bb[TABLE_N_AXES][2];
488 ascii_init_caption_cell (caption, &cell);
492 bb[V][1] = caption_height;
493 a->y -= caption_height;
494 ascii_draw_cell (a, &cell, bb, bb);
495 a->y += caption_height;
501 render_pager_destroy (p);
505 ascii_output_text (struct ascii_driver *a, const char *text)
507 struct table_item *table_item;
509 table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
510 ascii_output_table_item (a, table_item);
511 table_item_unref (table_item);
515 ascii_submit (struct output_driver *driver,
516 const struct output_item *output_item)
518 struct ascii_driver *a = ascii_driver_cast (driver);
520 output_driver_track_current_command (output_item, &a->command_name);
525 if (is_table_item (output_item))
526 ascii_output_table_item (a, to_table_item (output_item));
528 else if (is_chart_item (output_item) && a->chart_file_name != NULL)
530 struct chart_item *chart_item = to_chart_item (output_item);
533 file_name = xr_draw_png_chart (chart_item, a->chart_file_name,
537 if (file_name != NULL)
539 struct text_item *text_item;
541 text_item = text_item_create_format (
542 TEXT_ITEM_PARAGRAPH, _("See %s for a chart."), file_name);
544 ascii_submit (driver, &text_item->output_item);
545 text_item_unref (text_item);
549 #endif /* HAVE_CAIRO */
550 else if (is_text_item (output_item))
552 const struct text_item *text_item = to_text_item (output_item);
553 enum text_item_type type = text_item_get_type (text_item);
554 const char *text = text_item_get_text (text_item);
558 case TEXT_ITEM_TITLE:
560 a->title = xstrdup (text);
563 case TEXT_ITEM_SUBTITLE:
565 a->subtitle = xstrdup (text);
568 case TEXT_ITEM_COMMAND_OPEN:
569 case TEXT_ITEM_COMMAND_CLOSE:
572 case TEXT_ITEM_BLANK_LINE:
577 case TEXT_ITEM_EJECT_PAGE:
579 ascii_close_page (a);
583 ascii_output_text (a, text);
587 else if (is_message_item (output_item))
589 const struct message_item *message_item = to_message_item (output_item);
590 const struct msg *msg = message_item_get_msg (message_item);
591 char *s = msg_to_string (msg, a->command_name);
592 ascii_output_text (a, s);
597 const struct output_driver_factory txt_driver_factory =
598 { "txt", "-", ascii_create };
599 const struct output_driver_factory list_driver_factory =
600 { "list", "-", ascii_create };
602 static const struct output_driver_class ascii_driver_class =
610 static char *ascii_reserve (struct ascii_driver *, int y, int x0, int x1,
612 static void ascii_layout_cell (struct ascii_driver *,
613 const struct table_cell *,
614 int bb[TABLE_N_AXES][2],
615 int clip[TABLE_N_AXES][2],
616 int *width, int *height);
619 ascii_draw_line (void *a_, int bb[TABLE_N_AXES][2],
620 enum render_line_style styles[TABLE_N_AXES][2])
622 struct ascii_driver *a = a_;
629 /* Clip to the page. */
630 x0 = MAX (bb[H][0] + a->x, 0);
631 y0 = MAX (bb[V][0] + a->y, 0);
632 x1 = MIN (bb[H][1] + a->x, a->width);
633 y1 = MIN (bb[V][1] + a->y, a->length);
634 if (x1 <= 0 || y1 <= 0 || x0 >= a->width || y0 >= a->length)
638 uc = a->box[make_box_index (styles[V][0], styles[V][1],
639 styles[H][0], styles[H][1])];
640 mblen = u8_uctomb (CHAR_CAST (uint8_t *, mbchar), uc, 6);
641 for (y = y0; y < y1; y++)
643 char *p = ascii_reserve (a, y, x0, x1, mblen * (x1 - x0));
644 for (x = x0; x < x1; x++)
646 memcpy (p, mbchar, mblen);
653 ascii_measure_cell_width (void *a_, const struct table_cell *cell,
654 int *min_width, int *max_width)
656 struct ascii_driver *a = a_;
657 int bb[TABLE_N_AXES][2];
658 int clip[TABLE_N_AXES][2];
665 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
666 ascii_layout_cell (a, cell, bb, clip, max_width, &h);
668 if (cell->n_contents != 1
669 || cell->contents[0].table
670 || strchr (cell->contents[0].text, ' '))
673 ascii_layout_cell (a, cell, bb, clip, min_width, &h);
676 *min_width = *max_width;
680 ascii_measure_cell_height (void *a_, const struct table_cell *cell, int width)
682 struct ascii_driver *a = a_;
683 int bb[TABLE_N_AXES][2];
684 int clip[TABLE_N_AXES][2];
691 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
692 ascii_layout_cell (a, cell, bb, clip, &w, &h);
697 ascii_draw_cell (void *a_, const struct table_cell *cell,
698 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
700 struct ascii_driver *a = a_;
703 ascii_layout_cell (a, cell, bb, clip, &w, &h);
707 ascii_reserve (struct ascii_driver *a, int y, int x0, int x1, int n)
709 assert (y < a->allocated_lines);
710 return u8_line_reserve (&a->lines[y], x0, x1, n);
714 text_draw (struct ascii_driver *a, unsigned int options,
715 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
716 int y, const uint8_t *string, int n, size_t width)
718 int x0 = MAX (0, clip[H][0] + a->x);
719 int y0 = MAX (0, clip[V][0] + a->y);
720 int x1 = MIN (a->width, clip[H][1] + a->x);
721 int y1 = MIN (a->length, clip[V][1] + a->y);
725 if (y < y0 || y >= y1)
728 switch (options & TAB_ALIGNMENT)
734 x = (bb[H][0] + bb[H][1] - width + 1) / 2;
737 x = bb[H][1] - width;
754 mblen = u8_mbtouc (&uc, string, n);
759 w = uc_width (uc, "UTF-8");
774 for (ofs = 0; ofs < n; )
780 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
782 w = uc_width (uc, "UTF-8");
785 if (width + w > x1 - x)
796 if (!(options & TAB_EMPH) || a->emphasis == EMPH_NONE)
797 memcpy (ascii_reserve (a, y, x, x + width, n), string, n);
805 /* First figure out how many bytes need to be inserted. */
807 for (ofs = 0; ofs < n; ofs += mblen)
812 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
813 w = uc_width (uc, "UTF-8");
816 n_out += a->emphasis == EMPH_UNDERLINE ? 2 : 1 + mblen;
819 /* Then insert them. */
820 out = ascii_reserve (a, y, x, x + width, n_out);
821 for (ofs = 0; ofs < n; ofs += mblen)
826 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
827 w = uc_width (uc, "UTF-8");
831 if (a->emphasis == EMPH_UNDERLINE)
834 out = mempcpy (out, string + ofs, mblen);
837 out = mempcpy (out, string + ofs, mblen);
843 ascii_layout_cell_text (struct ascii_driver *a,
844 const struct cell_contents *contents,
845 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
848 size_t length = strlen (contents->text);
858 breaks = xmalloc (length + 1);
859 u8_possible_linebreaks (CHAR_CAST (const uint8_t *, contents->text), length,
861 breaks[length] = (breaks[length - 1] == UC_BREAK_MANDATORY
862 ? UC_BREAK_PROHIBITED : UC_BREAK_POSSIBLE);
865 bb_width = bb[H][1] - bb[H][0];
866 for (y = bb[V][0]; y < bb[V][1] && pos < length; y++)
868 const uint8_t *line = CHAR_CAST (const uint8_t *, contents->text + pos);
869 const char *b = breaks + pos;
870 size_t n = length - pos;
872 size_t last_break_ofs = 0;
873 int last_break_width = 0;
878 for (ofs = 0; ofs < n; )
884 mblen = u8_mbtouc (&uc, line + ofs, n - ofs);
885 if (b[ofs] == UC_BREAK_MANDATORY)
887 else if (b[ofs] == UC_BREAK_POSSIBLE)
889 last_break_ofs = ofs;
890 last_break_width = width;
893 w = uc_width (uc, "UTF-8");
896 if (width + w > bb_width)
898 if (isspace (line[ofs]))
900 else if (last_break_ofs != 0)
902 ofs = last_break_ofs;
903 width = last_break_width;
912 /* Trim any trailing spaces off the end of the text to be drawn. */
913 for (graph_ofs = ofs; graph_ofs > 0; graph_ofs--)
914 if (!isspace (line[graph_ofs - 1]))
916 width -= ofs - graph_ofs;
919 text_draw (a, contents->options, bb, clip, y, line, graph_ofs, width);
921 /* If a new-line ended the line, just skip the new-line. Otherwise, skip
922 past any spaces past the end of the line (but not past a new-line). */
923 if (b[ofs] == UC_BREAK_MANDATORY)
926 while (ofs < n && isspace (line[ofs]) && b[ofs] != UC_BREAK_MANDATORY)
940 ascii_layout_subtable (struct ascii_driver *a,
941 const struct cell_contents *contents,
942 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2] UNUSED,
945 const struct table *table = contents->table;
946 struct render_params params;
947 struct render_pager *p;
948 int r[TABLE_N_AXES][2];
952 params.draw_line = ascii_draw_line;
953 params.measure_cell_width = ascii_measure_cell_width;
954 params.measure_cell_height = ascii_measure_cell_height;
955 params.adjust_break = NULL;
956 params.draw_cell = ascii_draw_cell,
958 params.size[H] = bb[TABLE_HORZ][1] - bb[TABLE_HORZ][0];
959 params.size[V] = bb[TABLE_VERT][1] - bb[TABLE_VERT][0];
960 params.font_size[H] = 1;
961 params.font_size[V] = 1;
962 for (i = 0; i < RENDER_N_LINES; i++)
964 int width = i == RENDER_LINE_NONE ? 0 : 1;
965 params.line_widths[H][i] = width;
966 params.line_widths[V][i] = width;
969 p = render_pager_create (render_page_create (¶ms, table));
970 width = render_pager_get_size (p, TABLE_HORZ);
971 height = render_pager_get_size (p, TABLE_VERT);
973 /* r = intersect(bb, clip) - bb. */
974 for (i = 0; i < TABLE_N_AXES; i++)
976 r[i][0] = MAX (bb[i][0], clip[i][0]) - bb[i][0];
977 r[i][1] = MIN (bb[i][1], clip[i][1]) - bb[i][0];
980 if (r[H][0] < r[H][1] && r[V][0] < r[V][1])
982 unsigned int alignment = contents->options & TAB_ALIGNMENT;
985 a->x += bb[TABLE_HORZ][0];
986 if (alignment == TAB_RIGHT)
987 a->x += params.size[H] - width;
988 else if (alignment == TAB_CENTER)
989 a->x += (params.size[H] - width) / 2;
990 a->y += bb[TABLE_VERT][0];
991 render_pager_draw (p);
992 a->y -= bb[TABLE_VERT][0];
995 render_pager_destroy (p);
999 return bb[V][0] + height;
1003 ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
1004 int bb_[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1005 int *widthp, int *heightp)
1007 int bb[TABLE_N_AXES][2];
1013 memcpy (bb, bb_, sizeof bb);
1014 for (i = 0; i < cell->n_contents && bb[V][0] < bb[V][1]; i++)
1016 const struct cell_contents *contents = &cell->contents[i];
1018 /* Put a blank line between contents. */
1022 if (bb[V][0] >= bb[V][1])
1027 bb[V][0] = ascii_layout_cell_text (a, contents, bb, clip, widthp);
1029 bb[V][0] = ascii_layout_subtable (a, contents, bb, clip, widthp);
1031 *heightp = bb[V][0] - bb_[V][0];
1035 ascii_test_write (struct output_driver *driver,
1036 const char *s, int x, int y, unsigned int options)
1038 struct ascii_driver *a = ascii_driver_cast (driver);
1039 struct cell_contents contents;
1040 struct table_cell cell;
1041 int bb[TABLE_N_AXES][2];
1044 if (a->file == NULL && !ascii_open_page (a))
1048 contents.options = options | TAB_LEFT;
1049 contents.text = CONST_CAST (char *, s);
1050 contents.table = NULL;
1052 memset (&cell, 0, sizeof cell);
1053 cell.contents = &contents;
1054 cell.n_contents = 1;
1056 bb[TABLE_HORZ][0] = x;
1057 bb[TABLE_HORZ][1] = a->width;
1058 bb[TABLE_VERT][0] = y;
1059 bb[TABLE_VERT][1] = a->length;
1061 ascii_layout_cell (a, &cell, bb, bb, &width, &height);
1067 ascii_test_set_length (struct output_driver *driver, int y, int length)
1069 struct ascii_driver *a = ascii_driver_cast (driver);
1071 if (a->file == NULL && !ascii_open_page (a))
1074 if (y < 0 || y >= a->length)
1076 u8_line_set_length (&a->lines[y], length);
1079 /* ascii_close_page () and support routines. */
1081 #if HAVE_DECL_SIGWINCH
1082 static struct ascii_driver *the_driver;
1085 winch_handler (int signum UNUSED)
1087 update_page_size (the_driver, false);
1092 ascii_open_page (struct ascii_driver *a)
1099 if (a->file == NULL)
1101 a->file = fn_open (a->file_name, a->append ? "a" : "w");
1102 if (a->file != NULL)
1104 if ( isatty (fileno (a->file)))
1106 #if HAVE_DECL_SIGWINCH
1107 struct sigaction action;
1108 sigemptyset (&action.sa_mask);
1109 action.sa_flags = 0;
1110 action.sa_handler = winch_handler;
1112 sigaction (SIGWINCH, &action, NULL);
1114 a->auto_width = true;
1115 a->auto_length = true;
1120 msg_error (errno, _("ascii: opening output file `%s'"),
1129 reallocate_lines (a);
1131 for (i = 0; i < a->length; i++)
1132 u8_line_clear (&a->lines[i]);
1138 output_title_line (FILE *out, int width, const char *left, const char *right)
1140 struct string s = DS_EMPTY_INITIALIZER;
1141 ds_put_byte_multiple (&s, ' ', width);
1144 size_t length = MIN (strlen (left), width);
1145 memcpy (ds_end (&s) - width, left, length);
1149 size_t length = MIN (strlen (right), width);
1150 memcpy (ds_end (&s) - length, right, length);
1152 ds_put_byte (&s, '\n');
1153 fputs (ds_cstr (&s), out);
1158 ascii_close_page (struct ascii_driver *a)
1164 if (a->file == NULL)
1167 if (!a->top_margin && !a->bottom_margin && a->squeeze_blank_lines
1168 && !a->paginate && a->page_number > 1)
1169 putc ('\n', a->file);
1171 for (i = 0; i < a->top_margin; i++)
1172 putc ('\n', a->file);
1177 r1 = xasprintf (_("%s - Page %d"), get_start_date (), a->page_number);
1178 r2 = xasprintf ("%s - %s" , version, host_system);
1180 output_title_line (a->file, a->width, a->title, r1);
1181 output_title_line (a->file, a->width, a->subtitle, r2);
1182 putc ('\n', a->file);
1189 for (y = 0; y < a->allocated_lines; y++)
1191 struct u8_line *line = &a->lines[y];
1193 if (a->squeeze_blank_lines && y > 0 && line->width == 0)
1199 putc ('\n', a->file);
1203 while (ds_chomp_byte (&line->s, ' '))
1205 fwrite (ds_data (&line->s), 1, ds_length (&line->s), a->file);
1206 putc ('\n', a->file);
1209 if (!a->squeeze_blank_lines)
1210 for (y = a->allocated_lines; y < a->length; y++)
1211 putc ('\n', a->file);
1213 for (i = 0; i < a->bottom_margin; i++)
1214 putc ('\n', a->file);
1216 putc ('\f', a->file);