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_page *page;
427 struct render_pager *p;
431 update_page_size (a, false);
435 /* XXX doesn't do well with very large captions */
436 struct table_cell cell;
437 ascii_init_caption_cell (caption, &cell);
438 caption_height = ascii_measure_cell_height (a, &cell, a->width);
443 params.draw_line = ascii_draw_line;
444 params.measure_cell_width = ascii_measure_cell_width;
445 params.measure_cell_height = ascii_measure_cell_height;
446 params.adjust_break = NULL;
447 params.draw_cell = ascii_draw_cell;
449 params.size[H] = a->width;
450 params.size[V] = a->length - caption_height;
451 params.font_size[H] = 1;
452 params.font_size[V] = 1;
453 for (i = 0; i < RENDER_N_LINES; i++)
455 int width = i == RENDER_LINE_NONE ? 0 : 1;
456 params.line_widths[H][i] = width;
457 params.line_widths[V][i] = width;
459 for (i = 0; i < TABLE_N_AXES; i++)
460 params.min_break[i] = a->min_break[i];
462 if (a->file == NULL && !ascii_open_page (a))
465 page = render_page_create (¶ms, table_item_get_table (table_item));
466 p = render_pager_create (page);
467 while (render_pager_has_next (p))
469 int space = a->length - (a->y + (a->y > 0) + caption_height);
470 struct render_page *slice = render_pager_next (p, space);
474 ascii_close_page (a);
475 if (!ascii_open_page (a))
477 render_pager_destroy (p);
488 struct table_cell cell;
489 int bb[TABLE_N_AXES][2];
491 ascii_init_caption_cell (caption, &cell);
495 bb[V][1] = caption_height;
496 ascii_draw_cell (a, &cell, bb, bb);
497 a->y += caption_height;
500 render_page_draw (slice);
501 a->y += render_page_get_size (slice, V);
502 render_page_unref (slice);
504 render_pager_destroy (p);
508 ascii_output_text (struct ascii_driver *a, const char *text)
510 struct table_item *table_item;
512 table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
513 ascii_output_table_item (a, table_item);
514 table_item_unref (table_item);
518 ascii_submit (struct output_driver *driver,
519 const struct output_item *output_item)
521 struct ascii_driver *a = ascii_driver_cast (driver);
523 output_driver_track_current_command (output_item, &a->command_name);
528 if (is_table_item (output_item))
529 ascii_output_table_item (a, to_table_item (output_item));
531 else if (is_chart_item (output_item) && a->chart_file_name != NULL)
533 struct chart_item *chart_item = to_chart_item (output_item);
536 file_name = xr_draw_png_chart (chart_item, a->chart_file_name,
540 if (file_name != NULL)
542 struct text_item *text_item;
544 text_item = text_item_create_format (
545 TEXT_ITEM_PARAGRAPH, _("See %s for a chart."), file_name);
547 ascii_submit (driver, &text_item->output_item);
548 text_item_unref (text_item);
552 #endif /* HAVE_CAIRO */
553 else if (is_text_item (output_item))
555 const struct text_item *text_item = to_text_item (output_item);
556 enum text_item_type type = text_item_get_type (text_item);
557 const char *text = text_item_get_text (text_item);
561 case TEXT_ITEM_TITLE:
563 a->title = xstrdup (text);
566 case TEXT_ITEM_SUBTITLE:
568 a->subtitle = xstrdup (text);
571 case TEXT_ITEM_COMMAND_OPEN:
572 case TEXT_ITEM_COMMAND_CLOSE:
575 case TEXT_ITEM_BLANK_LINE:
580 case TEXT_ITEM_EJECT_PAGE:
582 ascii_close_page (a);
586 ascii_output_text (a, text);
590 else if (is_message_item (output_item))
592 const struct message_item *message_item = to_message_item (output_item);
593 const struct msg *msg = message_item_get_msg (message_item);
594 char *s = msg_to_string (msg, a->command_name);
595 ascii_output_text (a, s);
600 const struct output_driver_factory txt_driver_factory =
601 { "txt", "-", ascii_create };
602 const struct output_driver_factory list_driver_factory =
603 { "list", "-", ascii_create };
605 static const struct output_driver_class ascii_driver_class =
613 static char *ascii_reserve (struct ascii_driver *, int y, int x0, int x1,
615 static void ascii_layout_cell (struct ascii_driver *,
616 const struct table_cell *,
617 int bb[TABLE_N_AXES][2],
618 int clip[TABLE_N_AXES][2],
619 int *width, int *height);
622 ascii_draw_line (void *a_, int bb[TABLE_N_AXES][2],
623 enum render_line_style styles[TABLE_N_AXES][2])
625 struct ascii_driver *a = a_;
632 /* Clip to the page. */
633 x0 = MAX (bb[H][0] + a->x, 0);
634 y0 = MAX (bb[V][0] + a->y, 0);
635 x1 = MIN (bb[H][1] + a->x, a->width);
636 y1 = MIN (bb[V][1] + a->y, a->length);
637 if (x1 <= 0 || y1 <= 0 || x0 >= a->width || y0 >= a->length)
641 uc = a->box[make_box_index (styles[V][0], styles[V][1],
642 styles[H][0], styles[H][1])];
643 mblen = u8_uctomb (CHAR_CAST (uint8_t *, mbchar), uc, 6);
644 for (y = y0; y < y1; y++)
646 char *p = ascii_reserve (a, y, x0, x1, mblen * (x1 - x0));
647 for (x = x0; x < x1; x++)
649 memcpy (p, mbchar, mblen);
656 ascii_measure_cell_width (void *a_, const struct table_cell *cell,
657 int *min_width, int *max_width)
659 struct ascii_driver *a = a_;
660 int bb[TABLE_N_AXES][2];
661 int clip[TABLE_N_AXES][2];
668 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
669 ascii_layout_cell (a, cell, bb, clip, max_width, &h);
671 if (cell->n_contents != 1
672 || cell->contents[0].table
673 || strchr (cell->contents[0].text, ' '))
676 ascii_layout_cell (a, cell, bb, clip, min_width, &h);
679 *min_width = *max_width;
683 ascii_measure_cell_height (void *a_, const struct table_cell *cell, int width)
685 struct ascii_driver *a = a_;
686 int bb[TABLE_N_AXES][2];
687 int clip[TABLE_N_AXES][2];
694 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
695 ascii_layout_cell (a, cell, bb, clip, &w, &h);
700 ascii_draw_cell (void *a_, const struct table_cell *cell,
701 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
703 struct ascii_driver *a = a_;
706 ascii_layout_cell (a, cell, bb, clip, &w, &h);
710 ascii_reserve (struct ascii_driver *a, int y, int x0, int x1, int n)
712 assert (y < a->allocated_lines);
713 return u8_line_reserve (&a->lines[y], x0, x1, n);
717 text_draw (struct ascii_driver *a, unsigned int options,
718 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
719 int y, const uint8_t *string, int n, size_t width)
721 int x0 = MAX (0, clip[H][0] + a->x);
722 int y0 = MAX (0, clip[V][0] + a->y);
723 int x1 = MIN (a->width, clip[H][1] + a->x);
724 int y1 = MIN (a->length, clip[V][1] + a->y);
728 if (y < y0 || y >= y1)
731 switch (options & TAB_ALIGNMENT)
737 x = (bb[H][0] + bb[H][1] - width + 1) / 2;
740 x = bb[H][1] - width;
757 mblen = u8_mbtouc (&uc, string, n);
762 w = uc_width (uc, "UTF-8");
777 for (ofs = 0; ofs < n; )
783 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
785 w = uc_width (uc, "UTF-8");
788 if (width + w > x1 - x)
799 if (!(options & TAB_EMPH) || a->emphasis == EMPH_NONE)
800 memcpy (ascii_reserve (a, y, x, x + width, n), string, n);
808 /* First figure out how many bytes need to be inserted. */
810 for (ofs = 0; ofs < n; ofs += mblen)
815 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
816 w = uc_width (uc, "UTF-8");
819 n_out += a->emphasis == EMPH_UNDERLINE ? 2 : 1 + mblen;
822 /* Then insert them. */
823 out = ascii_reserve (a, y, x, x + width, n_out);
824 for (ofs = 0; ofs < n; ofs += mblen)
829 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
830 w = uc_width (uc, "UTF-8");
834 if (a->emphasis == EMPH_UNDERLINE)
837 out = mempcpy (out, string + ofs, mblen);
840 out = mempcpy (out, string + ofs, mblen);
846 ascii_layout_cell_text (struct ascii_driver *a,
847 const struct cell_contents *contents,
848 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
851 size_t length = strlen (contents->text);
861 breaks = xmalloc (length + 1);
862 u8_possible_linebreaks (CHAR_CAST (const uint8_t *, contents->text), length,
864 breaks[length] = (breaks[length - 1] == UC_BREAK_MANDATORY
865 ? UC_BREAK_PROHIBITED : UC_BREAK_POSSIBLE);
868 bb_width = bb[H][1] - bb[H][0];
869 for (y = bb[V][0]; y < bb[V][1] && pos < length; y++)
871 const uint8_t *line = CHAR_CAST (const uint8_t *, contents->text + pos);
872 const char *b = breaks + pos;
873 size_t n = length - pos;
875 size_t last_break_ofs = 0;
876 int last_break_width = 0;
881 for (ofs = 0; ofs < n; )
887 mblen = u8_mbtouc (&uc, line + ofs, n - ofs);
888 if (b[ofs] == UC_BREAK_MANDATORY)
890 else if (b[ofs] == UC_BREAK_POSSIBLE)
892 last_break_ofs = ofs;
893 last_break_width = width;
896 w = uc_width (uc, "UTF-8");
899 if (width + w > bb_width)
901 if (isspace (line[ofs]))
903 else if (last_break_ofs != 0)
905 ofs = last_break_ofs;
906 width = last_break_width;
915 /* Trim any trailing spaces off the end of the text to be drawn. */
916 for (graph_ofs = ofs; graph_ofs > 0; graph_ofs--)
917 if (!isspace (line[graph_ofs - 1]))
919 width -= ofs - graph_ofs;
922 text_draw (a, contents->options, bb, clip, y, line, graph_ofs, width);
924 /* If a new-line ended the line, just skip the new-line. Otherwise, skip
925 past any spaces past the end of the line (but not past a new-line). */
926 if (b[ofs] == UC_BREAK_MANDATORY)
929 while (ofs < n && isspace (line[ofs]) && b[ofs] != UC_BREAK_MANDATORY)
943 ascii_layout_subtable (struct ascii_driver *a,
944 const struct cell_contents *contents,
945 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2] UNUSED,
948 const struct table *table = contents->table;
949 struct render_params params;
950 struct render_page *page;
951 int r[TABLE_N_AXES][2];
955 params.draw_line = ascii_draw_line;
956 params.measure_cell_width = ascii_measure_cell_width;
957 params.measure_cell_height = ascii_measure_cell_height;
958 params.adjust_break = NULL;
959 params.draw_cell = ascii_draw_cell,
961 params.size[H] = bb[TABLE_HORZ][1] - bb[TABLE_HORZ][0];
962 params.size[V] = bb[TABLE_VERT][1] - bb[TABLE_VERT][0];
963 params.font_size[H] = 1;
964 params.font_size[V] = 1;
965 for (i = 0; i < RENDER_N_LINES; i++)
967 int width = i == RENDER_LINE_NONE ? 0 : 1;
968 params.line_widths[H][i] = width;
969 params.line_widths[V][i] = width;
972 page = render_page_create (¶ms, table);
973 width = render_page_get_size (page, TABLE_HORZ);
974 height = render_page_get_size (page, TABLE_VERT);
976 /* r = intersect(bb, clip) - bb. */
977 for (i = 0; i < TABLE_N_AXES; i++)
979 r[i][0] = MAX (bb[i][0], clip[i][0]) - bb[i][0];
980 r[i][1] = MIN (bb[i][1], clip[i][1]) - bb[i][0];
983 if (r[H][0] < r[H][1] && r[V][0] < r[V][1])
985 unsigned int alignment = contents->options & TAB_ALIGNMENT;
988 a->x += bb[TABLE_HORZ][0];
989 if (alignment == TAB_RIGHT)
990 a->x += params.size[H] - width;
991 else if (alignment == TAB_CENTER)
992 a->x += (params.size[H] - width) / 2;
993 a->y += bb[TABLE_VERT][0];
994 render_page_draw (page);
995 a->y -= bb[TABLE_VERT][0];
998 render_page_unref (page);
1000 if (width > *widthp)
1002 return bb[V][0] + height;
1006 ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
1007 int bb_[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1008 int *widthp, int *heightp)
1010 int bb[TABLE_N_AXES][2];
1016 memcpy (bb, bb_, sizeof bb);
1017 for (i = 0; i < cell->n_contents && bb[V][0] < bb[V][1]; i++)
1019 const struct cell_contents *contents = &cell->contents[i];
1021 /* Put a blank line between contents. */
1025 if (bb[V][0] >= bb[V][1])
1030 bb[V][0] = ascii_layout_cell_text (a, contents, bb, clip, widthp);
1032 bb[V][0] = ascii_layout_subtable (a, contents, bb, clip, widthp);
1034 *heightp = bb[V][0] - bb_[V][0];
1038 ascii_test_write (struct output_driver *driver,
1039 const char *s, int x, int y, unsigned int options)
1041 struct ascii_driver *a = ascii_driver_cast (driver);
1042 struct cell_contents contents;
1043 struct table_cell cell;
1044 int bb[TABLE_N_AXES][2];
1047 if (a->file == NULL && !ascii_open_page (a))
1051 contents.options = options | TAB_LEFT;
1052 contents.text = CONST_CAST (char *, s);
1053 contents.table = NULL;
1055 memset (&cell, 0, sizeof cell);
1056 cell.contents = &contents;
1057 cell.n_contents = 1;
1059 bb[TABLE_HORZ][0] = x;
1060 bb[TABLE_HORZ][1] = a->width;
1061 bb[TABLE_VERT][0] = y;
1062 bb[TABLE_VERT][1] = a->length;
1064 ascii_layout_cell (a, &cell, bb, bb, &width, &height);
1070 ascii_test_set_length (struct output_driver *driver, int y, int length)
1072 struct ascii_driver *a = ascii_driver_cast (driver);
1074 if (a->file == NULL && !ascii_open_page (a))
1077 if (y < 0 || y >= a->length)
1079 u8_line_set_length (&a->lines[y], length);
1082 /* ascii_close_page () and support routines. */
1084 #if HAVE_DECL_SIGWINCH
1085 static struct ascii_driver *the_driver;
1088 winch_handler (int signum UNUSED)
1090 update_page_size (the_driver, false);
1095 ascii_open_page (struct ascii_driver *a)
1102 if (a->file == NULL)
1104 a->file = fn_open (a->file_name, a->append ? "a" : "w");
1105 if (a->file != NULL)
1107 if ( isatty (fileno (a->file)))
1109 #if HAVE_DECL_SIGWINCH
1110 struct sigaction action;
1111 sigemptyset (&action.sa_mask);
1112 action.sa_flags = 0;
1113 action.sa_handler = winch_handler;
1115 sigaction (SIGWINCH, &action, NULL);
1117 a->auto_width = true;
1118 a->auto_length = true;
1123 msg_error (errno, _("ascii: opening output file `%s'"),
1132 reallocate_lines (a);
1134 for (i = 0; i < a->length; i++)
1135 u8_line_clear (&a->lines[i]);
1141 output_title_line (FILE *out, int width, const char *left, const char *right)
1143 struct string s = DS_EMPTY_INITIALIZER;
1144 ds_put_byte_multiple (&s, ' ', width);
1147 size_t length = MIN (strlen (left), width);
1148 memcpy (ds_end (&s) - width, left, length);
1152 size_t length = MIN (strlen (right), width);
1153 memcpy (ds_end (&s) - length, right, length);
1155 ds_put_byte (&s, '\n');
1156 fputs (ds_cstr (&s), out);
1161 ascii_close_page (struct ascii_driver *a)
1167 if (a->file == NULL)
1170 if (!a->top_margin && !a->bottom_margin && a->squeeze_blank_lines
1171 && !a->paginate && a->page_number > 1)
1172 putc ('\n', a->file);
1174 for (i = 0; i < a->top_margin; i++)
1175 putc ('\n', a->file);
1180 r1 = xasprintf (_("%s - Page %d"), get_start_date (), a->page_number);
1181 r2 = xasprintf ("%s - %s" , version, host_system);
1183 output_title_line (a->file, a->width, a->title, r1);
1184 output_title_line (a->file, a->width, a->subtitle, r2);
1185 putc ('\n', a->file);
1192 for (y = 0; y < a->allocated_lines; y++)
1194 struct u8_line *line = &a->lines[y];
1196 if (a->squeeze_blank_lines && y > 0 && line->width == 0)
1202 putc ('\n', a->file);
1206 while (ds_chomp_byte (&line->s, ' '))
1208 fwrite (ds_data (&line->s), 1, ds_length (&line->s), a->file);
1209 putc ('\n', a->file);
1212 if (!a->squeeze_blank_lines)
1213 for (y = a->allocated_lines; y < a->length; y++)
1214 putc ('\n', a->file);
1216 for (i = 0; i < a->bottom_margin; i++)
1217 putc ('\n', a->file);
1219 putc ('\f', a->file);