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 (¶ms, table_item);
465 while (render_pager_has_next (p))
471 a->y += caption_height;
472 used = render_pager_draw_next (p, a->length - a->y);
476 ascii_close_page (a);
477 if (!ascii_open_page (a))
484 struct table_cell cell;
485 int bb[TABLE_N_AXES][2];
487 ascii_init_caption_cell (caption, &cell);
491 bb[V][1] = caption_height;
492 a->y -= caption_height;
493 ascii_draw_cell (a, &cell, bb, bb);
494 a->y += caption_height;
500 render_pager_destroy (p);
504 ascii_output_text (struct ascii_driver *a, const char *text)
506 struct table_item *table_item;
508 table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
509 ascii_output_table_item (a, table_item);
510 table_item_unref (table_item);
514 ascii_submit (struct output_driver *driver,
515 const struct output_item *output_item)
517 struct ascii_driver *a = ascii_driver_cast (driver);
519 output_driver_track_current_command (output_item, &a->command_name);
524 if (is_table_item (output_item))
525 ascii_output_table_item (a, to_table_item (output_item));
527 else if (is_chart_item (output_item) && a->chart_file_name != NULL)
529 struct chart_item *chart_item = to_chart_item (output_item);
532 file_name = xr_draw_png_chart (chart_item, a->chart_file_name,
536 if (file_name != NULL)
538 struct text_item *text_item;
540 text_item = text_item_create_format (
541 TEXT_ITEM_PARAGRAPH, _("See %s for a chart."), file_name);
543 ascii_submit (driver, &text_item->output_item);
544 text_item_unref (text_item);
548 #endif /* HAVE_CAIRO */
549 else if (is_text_item (output_item))
551 const struct text_item *text_item = to_text_item (output_item);
552 enum text_item_type type = text_item_get_type (text_item);
553 const char *text = text_item_get_text (text_item);
557 case TEXT_ITEM_TITLE:
559 a->title = xstrdup (text);
562 case TEXT_ITEM_SUBTITLE:
564 a->subtitle = xstrdup (text);
567 case TEXT_ITEM_COMMAND_OPEN:
568 case TEXT_ITEM_COMMAND_CLOSE:
571 case TEXT_ITEM_BLANK_LINE:
576 case TEXT_ITEM_EJECT_PAGE:
578 ascii_close_page (a);
582 ascii_output_text (a, text);
586 else if (is_message_item (output_item))
588 const struct message_item *message_item = to_message_item (output_item);
589 const struct msg *msg = message_item_get_msg (message_item);
590 char *s = msg_to_string (msg, a->command_name);
591 ascii_output_text (a, s);
596 const struct output_driver_factory txt_driver_factory =
597 { "txt", "-", ascii_create };
598 const struct output_driver_factory list_driver_factory =
599 { "list", "-", ascii_create };
601 static const struct output_driver_class ascii_driver_class =
609 static char *ascii_reserve (struct ascii_driver *, int y, int x0, int x1,
611 static void ascii_layout_cell (struct ascii_driver *,
612 const struct table_cell *,
613 int bb[TABLE_N_AXES][2],
614 int clip[TABLE_N_AXES][2],
615 int *width, int *height);
618 ascii_draw_line (void *a_, int bb[TABLE_N_AXES][2],
619 enum render_line_style styles[TABLE_N_AXES][2])
621 struct ascii_driver *a = a_;
628 /* Clip to the page. */
629 x0 = MAX (bb[H][0] + a->x, 0);
630 y0 = MAX (bb[V][0] + a->y, 0);
631 x1 = MIN (bb[H][1] + a->x, a->width);
632 y1 = MIN (bb[V][1] + a->y, a->length);
633 if (x1 <= 0 || y1 <= 0 || x0 >= a->width || y0 >= a->length)
637 uc = a->box[make_box_index (styles[V][0], styles[V][1],
638 styles[H][0], styles[H][1])];
639 mblen = u8_uctomb (CHAR_CAST (uint8_t *, mbchar), uc, 6);
640 for (y = y0; y < y1; y++)
642 char *p = ascii_reserve (a, y, x0, x1, mblen * (x1 - x0));
643 for (x = x0; x < x1; x++)
645 memcpy (p, mbchar, mblen);
652 ascii_measure_cell_width (void *a_, const struct table_cell *cell,
653 int *min_width, int *max_width)
655 struct ascii_driver *a = a_;
656 int bb[TABLE_N_AXES][2];
657 int clip[TABLE_N_AXES][2];
664 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
665 ascii_layout_cell (a, cell, bb, clip, max_width, &h);
667 if (cell->n_contents != 1
668 || cell->contents[0].table
669 || strchr (cell->contents[0].text, ' '))
672 ascii_layout_cell (a, cell, bb, clip, min_width, &h);
675 *min_width = *max_width;
679 ascii_measure_cell_height (void *a_, const struct table_cell *cell, int width)
681 struct ascii_driver *a = a_;
682 int bb[TABLE_N_AXES][2];
683 int clip[TABLE_N_AXES][2];
690 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
691 ascii_layout_cell (a, cell, bb, clip, &w, &h);
696 ascii_draw_cell (void *a_, const struct table_cell *cell,
697 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
699 struct ascii_driver *a = a_;
702 ascii_layout_cell (a, cell, bb, clip, &w, &h);
706 ascii_reserve (struct ascii_driver *a, int y, int x0, int x1, int n)
708 assert (y < a->allocated_lines);
709 return u8_line_reserve (&a->lines[y], x0, x1, n);
713 text_draw (struct ascii_driver *a, unsigned int options,
714 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
715 int y, const uint8_t *string, int n, size_t width)
717 int x0 = MAX (0, clip[H][0] + a->x);
718 int y0 = MAX (0, clip[V][0] + a->y);
719 int x1 = MIN (a->width, clip[H][1] + a->x);
720 int y1 = MIN (a->length, clip[V][1] + a->y);
724 if (y < y0 || y >= y1)
727 switch (options & TAB_ALIGNMENT)
733 x = (bb[H][0] + bb[H][1] - width + 1) / 2;
736 x = bb[H][1] - width;
753 mblen = u8_mbtouc (&uc, string, n);
758 w = uc_width (uc, "UTF-8");
773 for (ofs = 0; ofs < n; )
779 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
781 w = uc_width (uc, "UTF-8");
784 if (width + w > x1 - x)
795 if (!(options & TAB_EMPH) || a->emphasis == EMPH_NONE)
796 memcpy (ascii_reserve (a, y, x, x + width, n), string, n);
804 /* First figure out how many bytes need to be inserted. */
806 for (ofs = 0; ofs < n; ofs += mblen)
811 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
812 w = uc_width (uc, "UTF-8");
815 n_out += a->emphasis == EMPH_UNDERLINE ? 2 : 1 + mblen;
818 /* Then insert them. */
819 out = ascii_reserve (a, y, x, x + width, n_out);
820 for (ofs = 0; ofs < n; ofs += mblen)
825 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
826 w = uc_width (uc, "UTF-8");
830 if (a->emphasis == EMPH_UNDERLINE)
833 out = mempcpy (out, string + ofs, mblen);
836 out = mempcpy (out, string + ofs, mblen);
842 ascii_layout_cell_text (struct ascii_driver *a,
843 const struct cell_contents *contents,
844 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
847 size_t length = strlen (contents->text);
857 breaks = xmalloc (length + 1);
858 u8_possible_linebreaks (CHAR_CAST (const uint8_t *, contents->text), length,
860 breaks[length] = (breaks[length - 1] == UC_BREAK_MANDATORY
861 ? UC_BREAK_PROHIBITED : UC_BREAK_POSSIBLE);
864 bb_width = bb[H][1] - bb[H][0];
865 for (y = bb[V][0]; y < bb[V][1] && pos < length; y++)
867 const uint8_t *line = CHAR_CAST (const uint8_t *, contents->text + pos);
868 const char *b = breaks + pos;
869 size_t n = length - pos;
871 size_t last_break_ofs = 0;
872 int last_break_width = 0;
877 for (ofs = 0; ofs < n; )
883 mblen = u8_mbtouc (&uc, line + ofs, n - ofs);
884 if (b[ofs] == UC_BREAK_MANDATORY)
886 else if (b[ofs] == UC_BREAK_POSSIBLE)
888 last_break_ofs = ofs;
889 last_break_width = width;
892 w = uc_width (uc, "UTF-8");
895 if (width + w > bb_width)
897 if (isspace (line[ofs]))
899 else if (last_break_ofs != 0)
901 ofs = last_break_ofs;
902 width = last_break_width;
911 /* Trim any trailing spaces off the end of the text to be drawn. */
912 for (graph_ofs = ofs; graph_ofs > 0; graph_ofs--)
913 if (!isspace (line[graph_ofs - 1]))
915 width -= ofs - graph_ofs;
918 text_draw (a, contents->options, bb, clip, y, line, graph_ofs, width);
920 /* If a new-line ended the line, just skip the new-line. Otherwise, skip
921 past any spaces past the end of the line (but not past a new-line). */
922 if (b[ofs] == UC_BREAK_MANDATORY)
925 while (ofs < n && isspace (line[ofs]) && b[ofs] != UC_BREAK_MANDATORY)
939 ascii_layout_subtable (struct ascii_driver *a,
940 const struct cell_contents *contents,
941 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2] UNUSED,
944 struct render_params params;
945 struct render_pager *p;
946 int r[TABLE_N_AXES][2];
950 params.draw_line = ascii_draw_line;
951 params.measure_cell_width = ascii_measure_cell_width;
952 params.measure_cell_height = ascii_measure_cell_height;
953 params.adjust_break = NULL;
954 params.draw_cell = ascii_draw_cell,
956 params.size[H] = bb[TABLE_HORZ][1] - bb[TABLE_HORZ][0];
957 params.size[V] = bb[TABLE_VERT][1] - bb[TABLE_VERT][0];
958 params.font_size[H] = 1;
959 params.font_size[V] = 1;
960 for (i = 0; i < RENDER_N_LINES; i++)
962 int width = i == RENDER_LINE_NONE ? 0 : 1;
963 params.line_widths[H][i] = width;
964 params.line_widths[V][i] = width;
967 p = render_pager_create (¶ms, contents->table);
968 width = render_pager_get_size (p, TABLE_HORZ);
969 height = render_pager_get_size (p, TABLE_VERT);
971 /* r = intersect(bb, clip) - bb. */
972 for (i = 0; i < TABLE_N_AXES; i++)
974 r[i][0] = MAX (bb[i][0], clip[i][0]) - bb[i][0];
975 r[i][1] = MIN (bb[i][1], clip[i][1]) - bb[i][0];
978 if (r[H][0] < r[H][1] && r[V][0] < r[V][1])
980 unsigned int alignment = contents->options & TAB_ALIGNMENT;
983 a->x += bb[TABLE_HORZ][0];
984 if (alignment == TAB_RIGHT)
985 a->x += params.size[H] - width;
986 else if (alignment == TAB_CENTER)
987 a->x += (params.size[H] - width) / 2;
988 a->y += bb[TABLE_VERT][0];
989 render_pager_draw (p);
990 a->y -= bb[TABLE_VERT][0];
993 render_pager_destroy (p);
997 return bb[V][0] + height;
1001 ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
1002 int bb_[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1003 int *widthp, int *heightp)
1005 int bb[TABLE_N_AXES][2];
1011 memcpy (bb, bb_, sizeof bb);
1012 for (i = 0; i < cell->n_contents && bb[V][0] < bb[V][1]; i++)
1014 const struct cell_contents *contents = &cell->contents[i];
1016 /* Put a blank line between contents. */
1020 if (bb[V][0] >= bb[V][1])
1025 bb[V][0] = ascii_layout_cell_text (a, contents, bb, clip, widthp);
1027 bb[V][0] = ascii_layout_subtable (a, contents, bb, clip, widthp);
1029 *heightp = bb[V][0] - bb_[V][0];
1033 ascii_test_write (struct output_driver *driver,
1034 const char *s, int x, int y, unsigned int options)
1036 struct ascii_driver *a = ascii_driver_cast (driver);
1037 struct cell_contents contents;
1038 struct table_cell cell;
1039 int bb[TABLE_N_AXES][2];
1042 if (a->file == NULL && !ascii_open_page (a))
1046 contents.options = options | TAB_LEFT;
1047 contents.text = CONST_CAST (char *, s);
1048 contents.table = NULL;
1050 memset (&cell, 0, sizeof cell);
1051 cell.contents = &contents;
1052 cell.n_contents = 1;
1054 bb[TABLE_HORZ][0] = x;
1055 bb[TABLE_HORZ][1] = a->width;
1056 bb[TABLE_VERT][0] = y;
1057 bb[TABLE_VERT][1] = a->length;
1059 ascii_layout_cell (a, &cell, bb, bb, &width, &height);
1065 ascii_test_set_length (struct output_driver *driver, int y, int length)
1067 struct ascii_driver *a = ascii_driver_cast (driver);
1069 if (a->file == NULL && !ascii_open_page (a))
1072 if (y < 0 || y >= a->length)
1074 u8_line_set_length (&a->lines[y], length);
1077 /* ascii_close_page () and support routines. */
1079 #if HAVE_DECL_SIGWINCH
1080 static struct ascii_driver *the_driver;
1083 winch_handler (int signum UNUSED)
1085 update_page_size (the_driver, false);
1090 ascii_open_page (struct ascii_driver *a)
1097 if (a->file == NULL)
1099 a->file = fn_open (a->file_name, a->append ? "a" : "w");
1100 if (a->file != NULL)
1102 if ( isatty (fileno (a->file)))
1104 #if HAVE_DECL_SIGWINCH
1105 struct sigaction action;
1106 sigemptyset (&action.sa_mask);
1107 action.sa_flags = 0;
1108 action.sa_handler = winch_handler;
1110 sigaction (SIGWINCH, &action, NULL);
1112 a->auto_width = true;
1113 a->auto_length = true;
1118 msg_error (errno, _("ascii: opening output file `%s'"),
1127 reallocate_lines (a);
1129 for (i = 0; i < a->length; i++)
1130 u8_line_clear (&a->lines[i]);
1136 output_title_line (FILE *out, int width, const char *left, const char *right)
1138 struct string s = DS_EMPTY_INITIALIZER;
1139 ds_put_byte_multiple (&s, ' ', width);
1142 size_t length = MIN (strlen (left), width);
1143 memcpy (ds_end (&s) - width, left, length);
1147 size_t length = MIN (strlen (right), width);
1148 memcpy (ds_end (&s) - length, right, length);
1150 ds_put_byte (&s, '\n');
1151 fputs (ds_cstr (&s), out);
1156 ascii_close_page (struct ascii_driver *a)
1162 if (a->file == NULL)
1165 if (!a->top_margin && !a->bottom_margin && a->squeeze_blank_lines
1166 && !a->paginate && a->page_number > 1)
1167 putc ('\n', a->file);
1169 for (i = 0; i < a->top_margin; i++)
1170 putc ('\n', a->file);
1175 r1 = xasprintf (_("%s - Page %d"), get_start_date (), a->page_number);
1176 r2 = xasprintf ("%s - %s" , version, host_system);
1178 output_title_line (a->file, a->width, a->title, r1);
1179 output_title_line (a->file, a->width, a->subtitle, r2);
1180 putc ('\n', a->file);
1187 for (y = 0; y < a->allocated_lines; y++)
1189 struct u8_line *line = &a->lines[y];
1191 if (a->squeeze_blank_lines && y > 0 && line->width == 0)
1197 putc ('\n', a->file);
1201 while (ds_chomp_byte (&line->s, ' '))
1203 fwrite (ds_data (&line->s), 1, ds_length (&line->s), a->file);
1204 putc ('\n', a->file);
1207 if (!a->squeeze_blank_lines)
1208 for (y = a->allocated_lines; y < a->length; y++)
1209 putc ('\n', a->file);
1211 for (i = 0; i < a->bottom_margin; i++)
1212 putc ('\n', a->file);
1214 putc ('\f', a->file);