1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2007, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
30 #include "data/file-name.h"
31 #include "data/file-handle-def.h"
32 #include "data/settings.h"
33 #include "libpspp/assertion.h"
34 #include "libpspp/cast.h"
35 #include "libpspp/compiler.h"
36 #include "libpspp/message.h"
37 #include "libpspp/start-date.h"
38 #include "libpspp/string-map.h"
39 #include "libpspp/u8-line.h"
40 #include "libpspp/version.h"
41 #include "output/ascii.h"
42 #include "output/cairo.h"
43 #include "output/chart-item-provider.h"
44 #include "output/driver-provider.h"
45 #include "output/message-item.h"
46 #include "output/options.h"
47 #include "output/render.h"
48 #include "output/tab.h"
49 #include "output/table-item.h"
50 #include "output/text-item.h"
52 #include "gl/minmax.h"
53 #include "gl/xalloc.h"
56 #define _(msgid) gettext (msgid)
58 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
62 #define N_BOX (RENDER_N_LINES * RENDER_N_LINES \
63 * RENDER_N_LINES * RENDER_N_LINES)
65 static const ucs4_t ascii_box_chars[N_BOX] =
96 static const ucs4_t unicode_box_chars[N_BOX] =
98 0x0020, 0x2575, 0x2551,
99 0x2574, 0x256f, 0x255c,
100 0x2550, 0x255b, 0x255d,
101 0x2577, 0x2502, 0x2551,
102 0x256e, 0x2524, 0x2562,
103 0x2555, 0x2561, 0x2563,
104 0x2551, 0x2551, 0x2551,
105 0x2556, 0x2562, 0x2562,
106 0x2557, 0x2563, 0x2563,
107 0x2576, 0x2570, 0x2559,
108 0x2500, 0x2534, 0x2568,
109 0x2550, 0x2567, 0x2569,
110 0x256d, 0x251c, 0x255f,
111 0x252c, 0x253c, 0x256a,
112 0x2564, 0x256a, 0x256c,
113 0x2553, 0x255f, 0x255f,
114 0x2565, 0x256b, 0x256b,
115 0x2566, 0x256c, 0x256c,
116 0x2550, 0x2558, 0x255a,
117 0x2550, 0x2567, 0x2569,
118 0x2550, 0x2567, 0x2569,
119 0x2552, 0x255e, 0x2560,
120 0x2564, 0x256a, 0x256c,
121 0x2564, 0x256a, 0x256c,
122 0x2554, 0x2560, 0x2560,
123 0x2560, 0x256c, 0x256c,
124 0x2566, 0x256c, 0x256c,
128 make_box_index (int left, int right, int top, int bottom)
130 int start_side = left;
131 int end_side = right;
132 if (render_direction_rtl ())
138 return ((end_side * RENDER_N_LINES + bottom) * RENDER_N_LINES + start_side) * RENDER_N_LINES + top;
141 /* How to emphasize text. */
144 EMPH_BOLD, /* Overstrike for bold. */
145 EMPH_UNDERLINE, /* Overstrike for underlining. */
146 EMPH_NONE /* No emphasis. */
149 /* ASCII output driver. */
152 struct output_driver driver;
154 /* User parameters. */
155 bool append; /* Append if output file already exists? */
156 bool headers; /* Print headers at top of page? */
157 bool paginate; /* Insert formfeeds? */
158 bool squeeze_blank_lines; /* Squeeze multiple blank lines into one? */
159 enum emphasis_style emphasis; /* How to emphasize text. */
160 char *chart_file_name; /* Name of files used for charts. */
163 /* Colours for charts */
168 int width; /* Page width. */
169 int length; /* Page length minus margins and header. */
170 bool auto_width; /* Use viewwidth as page width? */
171 bool auto_length; /* Use viewlength as page width? */
173 int top_margin; /* Top margin in lines. */
174 int bottom_margin; /* Bottom margin in lines. */
176 int min_break[TABLE_N_AXES]; /* Min cell size to break across pages. */
178 const ucs4_t *box; /* Line & box drawing characters. */
180 /* Internal state. */
184 struct file_handle *handle;
185 FILE *file; /* Output file. */
186 bool error; /* Output error? */
187 int page_number; /* Current page number. */
188 struct u8_line *lines; /* Page content. */
189 int allocated_lines; /* Number of lines allocated. */
190 int chart_cnt; /* Number of charts so far. */
194 static const struct output_driver_class ascii_driver_class;
196 static void ascii_submit (struct output_driver *, const struct output_item *);
198 static int vertical_margins (const struct ascii_driver *);
200 static bool update_page_size (struct ascii_driver *, bool issue_error);
201 static int parse_page_size (struct driver_option *);
203 static void ascii_close_page (struct ascii_driver *);
204 static bool ascii_open_page (struct ascii_driver *);
206 static void ascii_draw_line (void *, int bb[TABLE_N_AXES][2],
207 enum render_line_style styles[TABLE_N_AXES][2]);
208 static void ascii_measure_cell_width (void *, const struct table_cell *,
209 int footnote_idx, int *min, int *max);
210 static int ascii_measure_cell_height (void *, const struct table_cell *,
211 int footnote_idx, int width);
212 static void ascii_draw_cell (void *, const struct table_cell *,
213 int footnote_idx, int bb[TABLE_N_AXES][2],
214 int clip[TABLE_N_AXES][2]);
217 reallocate_lines (struct ascii_driver *a)
219 if (a->length > a->allocated_lines)
222 a->lines = xnrealloc (a->lines, a->length, sizeof *a->lines);
223 for (i = a->allocated_lines; i < a->length; i++)
224 u8_line_init (&a->lines[i]);
225 a->allocated_lines = a->length;
230 static struct ascii_driver *
231 ascii_driver_cast (struct output_driver *driver)
233 assert (driver->class == &ascii_driver_class);
234 return UP_CAST (driver, struct ascii_driver, driver);
237 static struct driver_option *
238 opt (struct output_driver *d, struct string_map *options, const char *key,
239 const char *default_value)
241 return driver_option_get (d, options, key, default_value);
244 static struct output_driver *
245 ascii_create (struct file_handle *fh, enum settings_output_devices device_type,
246 struct string_map *o)
248 enum { BOX_ASCII, BOX_UNICODE } box;
249 int min_break[TABLE_N_AXES];
250 struct output_driver *d;
251 struct ascii_driver *a;
254 a = xzalloc (sizeof *a);
256 output_driver_init (&a->driver, &ascii_driver_class, fh_get_file_name (fh), device_type);
257 a->append = parse_boolean (opt (d, o, "append", "false"));
258 a->headers = parse_boolean (opt (d, o, "headers", "false"));
259 a->paginate = parse_boolean (opt (d, o, "paginate", "false"));
260 a->squeeze_blank_lines = parse_boolean (opt (d, o, "squeeze", "true"));
261 a->emphasis = parse_enum (opt (d, o, "emphasis", "none"),
263 "underline", EMPH_UNDERLINE,
267 a->chart_file_name = parse_chart_file_name (opt (d, o, "charts", fh_get_file_name (fh)));
270 a->top_margin = parse_int (opt (d, o, "top-margin", "0"), 0, INT_MAX);
271 a->bottom_margin = parse_int (opt (d, o, "bottom-margin", "0"), 0, INT_MAX);
273 min_break[H] = parse_int (opt (d, o, "min-hbreak", "-1"), -1, INT_MAX);
274 min_break[V] = parse_int (opt (d, o, "min-vbreak", "-1"), -1, INT_MAX);
276 a->width = parse_page_size (opt (d, o, "width", "79"));
277 paper_length = parse_page_size (opt (d, o, "length", "66"));
278 a->auto_width = a->width < 0;
279 a->auto_length = paper_length < 0;
280 a->length = paper_length - vertical_margins (a);
281 a->min_break[H] = min_break[H] >= 0 ? min_break[H] : a->width / 2;
282 a->min_break[V] = min_break[V] >= 0 ? min_break[V] : a->length / 2;
284 parse_color (d, o, "background-color", "#FFFFFFFFFFFF", &a->bg);
285 parse_color (d, o, "foreground-color", "#000000000000", &a->fg);
287 box = parse_enum (opt (d, o, "box", "ascii"),
289 "unicode", BOX_UNICODE,
291 a->box = box == BOX_ASCII ? ascii_box_chars : unicode_box_chars;
293 a->command_name = NULL;
294 a->title = xstrdup ("");
295 a->subtitle = xstrdup ("");
300 a->allocated_lines = 0;
303 if (!update_page_size (a, true))
309 output_driver_destroy (d);
314 parse_page_size (struct driver_option *option)
316 int dim = atol (option->default_value);
318 if (option->value != NULL)
320 if (!strcmp (option->value, "auto"))
328 value = strtol (option->value, &tail, 0);
329 if (dim >= 1 && errno != ERANGE && *tail == '\0')
332 msg (MW, _("%s: %s must be positive integer or `auto'"),
333 option->driver_name, option->name);
337 driver_option_destroy (option);
343 vertical_margins (const struct ascii_driver *a)
345 return a->top_margin + a->bottom_margin + (a->headers ? 3 : 0);
348 /* Re-calculates the page width and length based on settings,
349 margins, and, if "auto" is set, the size of the user's
350 terminal window or GUI output window. */
352 update_page_size (struct ascii_driver *a, bool issue_error)
354 enum { MIN_WIDTH = 6, MIN_LENGTH = 6 };
358 a->width = settings_get_viewwidth ();
359 a->min_break[H] = a->width / 2;
363 a->length = settings_get_viewlength () - vertical_margins (a);
364 a->min_break[V] = a->length / 2;
367 if (a->width < MIN_WIDTH || a->length < MIN_LENGTH)
371 _("ascii: page excluding margins and headers "
372 "must be at least %d characters wide by %d lines long, but "
373 "as configured is only %d characters by %d lines"),
374 MIN_WIDTH, MIN_LENGTH,
375 a->width, a->length);
376 if (a->width < MIN_WIDTH)
377 a->width = MIN_WIDTH;
378 if (a->length < MIN_LENGTH)
379 a->length = MIN_LENGTH;
383 reallocate_lines (a);
389 ascii_destroy (struct output_driver *driver)
391 struct ascii_driver *a = ascii_driver_cast (driver);
395 ascii_close_page (a);
398 fn_close (a->handle, a->file);
399 fh_unref (a->handle);
400 free (a->command_name);
403 free (a->chart_file_name);
404 for (i = 0; i < a->allocated_lines; i++)
405 u8_line_destroy (&a->lines[i]);
411 ascii_flush (struct output_driver *driver)
413 struct ascii_driver *a = ascii_driver_cast (driver);
416 ascii_close_page (a);
418 if (fn_close (a->handle, a->file) != 0)
419 msg_error (errno, _("ascii: closing output file `%s'"), fh_get_file_name (a->handle));
425 ascii_output_table_item (struct ascii_driver *a,
426 const struct table_item *table_item)
428 struct render_params params;
429 struct render_pager *p;
432 update_page_size (a, false);
434 params.draw_line = ascii_draw_line;
435 params.measure_cell_width = ascii_measure_cell_width;
436 params.measure_cell_height = ascii_measure_cell_height;
437 params.adjust_break = NULL;
438 params.draw_cell = ascii_draw_cell;
440 params.size[H] = a->width;
441 params.size[V] = a->length;
442 params.font_size[H] = 1;
443 params.font_size[V] = 1;
444 for (i = 0; i < RENDER_N_LINES; i++)
446 int width = i == RENDER_LINE_NONE ? 0 : 1;
447 params.line_widths[H][i] = width;
448 params.line_widths[V][i] = width;
450 for (i = 0; i < TABLE_N_AXES; i++)
451 params.min_break[i] = a->min_break[i];
453 if (a->file == NULL && !ascii_open_page (a))
456 p = render_pager_create (¶ms, table_item);
457 while (render_pager_has_next (p))
463 used = render_pager_draw_next (p, a->length - a->y);
467 ascii_close_page (a);
468 if (!ascii_open_page (a))
474 render_pager_destroy (p);
478 ascii_output_text (struct ascii_driver *a, const char *text)
480 struct table_item *table_item;
482 table_item = table_item_create (table_from_string (TAB_LEFT, text),
484 ascii_output_table_item (a, table_item);
485 table_item_unref (table_item);
489 ascii_submit (struct output_driver *driver,
490 const struct output_item *output_item)
492 struct ascii_driver *a = ascii_driver_cast (driver);
494 output_driver_track_current_command (output_item, &a->command_name);
499 if (is_table_item (output_item))
500 ascii_output_table_item (a, to_table_item (output_item));
502 else if (is_chart_item (output_item) && a->chart_file_name != NULL)
504 struct chart_item *chart_item = to_chart_item (output_item);
507 file_name = xr_draw_png_chart (chart_item, a->chart_file_name,
511 if (file_name != NULL)
513 struct text_item *text_item;
515 text_item = text_item_create_format (
516 TEXT_ITEM_PARAGRAPH, _("See %s for a chart."), file_name);
518 ascii_submit (driver, &text_item->output_item);
519 text_item_unref (text_item);
523 #endif /* HAVE_CAIRO */
524 else if (is_text_item (output_item))
526 const struct text_item *text_item = to_text_item (output_item);
527 enum text_item_type type = text_item_get_type (text_item);
528 const char *text = text_item_get_text (text_item);
532 case TEXT_ITEM_TITLE:
534 a->title = xstrdup (text);
537 case TEXT_ITEM_SUBTITLE:
539 a->subtitle = xstrdup (text);
542 case TEXT_ITEM_COMMAND_OPEN:
543 case TEXT_ITEM_COMMAND_CLOSE:
546 case TEXT_ITEM_BLANK_LINE:
551 case TEXT_ITEM_EJECT_PAGE:
553 ascii_close_page (a);
557 ascii_output_text (a, text);
561 else if (is_message_item (output_item))
563 const struct message_item *message_item = to_message_item (output_item);
564 const struct msg *msg = message_item_get_msg (message_item);
565 char *s = msg_to_string (msg, a->command_name);
566 ascii_output_text (a, s);
571 const struct output_driver_factory txt_driver_factory =
572 { "txt", "-", ascii_create };
573 const struct output_driver_factory list_driver_factory =
574 { "list", "-", ascii_create };
576 static const struct output_driver_class ascii_driver_class =
584 static char *ascii_reserve (struct ascii_driver *, int y, int x0, int x1,
586 static void ascii_layout_cell (struct ascii_driver *,
587 const struct table_cell *,
589 int bb[TABLE_N_AXES][2],
590 int clip[TABLE_N_AXES][2],
591 int *width, int *height);
594 ascii_draw_line (void *a_, int bb[TABLE_N_AXES][2],
595 enum render_line_style styles[TABLE_N_AXES][2])
597 struct ascii_driver *a = a_;
604 /* Clip to the page. */
605 x0 = MAX (bb[H][0] + a->x, 0);
606 y0 = MAX (bb[V][0] + a->y, 0);
607 x1 = MIN (bb[H][1] + a->x, a->width);
608 y1 = MIN (bb[V][1] + a->y, a->length);
609 if (x1 <= 0 || y1 <= 0 || x0 >= a->width || y0 >= a->length)
613 uc = a->box[make_box_index (styles[V][0], styles[V][1],
614 styles[H][0], styles[H][1])];
615 mblen = u8_uctomb (CHAR_CAST (uint8_t *, mbchar), uc, 6);
616 for (y = y0; y < y1; y++)
618 char *p = ascii_reserve (a, y, x0, x1, mblen * (x1 - x0));
619 for (x = x0; x < x1; x++)
621 memcpy (p, mbchar, mblen);
628 ascii_measure_cell_width (void *a_, const struct table_cell *cell,
629 int footnote_idx, int *min_width, int *max_width)
631 struct ascii_driver *a = a_;
632 int bb[TABLE_N_AXES][2];
633 int clip[TABLE_N_AXES][2];
640 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
641 ascii_layout_cell (a, cell, footnote_idx, bb, clip, max_width, &h);
643 if (cell->n_contents != 1
644 || cell->contents[0].table
645 || cell->contents[0].n_footnotes
646 || strchr (cell->contents[0].text, ' '))
649 ascii_layout_cell (a, cell, footnote_idx, bb, clip, min_width, &h);
652 *min_width = *max_width;
656 ascii_measure_cell_height (void *a_, const struct table_cell *cell,
657 int footnote_idx, int 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, footnote_idx, bb, clip, &w, &h);
674 ascii_draw_cell (void *a_, const struct table_cell *cell, int footnote_idx,
675 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
677 struct ascii_driver *a = a_;
680 ascii_layout_cell (a, cell, footnote_idx, bb, clip, &w, &h);
684 ascii_reserve (struct ascii_driver *a, int y, int x0, int x1, int n)
686 assert (y < a->allocated_lines);
687 return u8_line_reserve (&a->lines[y], x0, x1, n);
691 text_draw (struct ascii_driver *a, unsigned int options,
692 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
693 int y, const uint8_t *string, int n, size_t width)
695 int x0 = MAX (0, clip[H][0] + a->x);
696 int y0 = MAX (0, clip[V][0] + a->y);
697 int x1 = MIN (a->width, clip[H][1] + a->x);
698 int y1 = MIN (a->length, clip[V][1] + a->y);
702 if (y < y0 || y >= y1)
705 switch (options & TAB_ALIGNMENT)
711 x = (bb[H][0] + bb[H][1] - width + 1) / 2;
714 x = bb[H][1] - width;
731 mblen = u8_mbtouc (&uc, string, n);
736 w = uc_width (uc, "UTF-8");
751 for (ofs = 0; ofs < n; )
757 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
759 w = uc_width (uc, "UTF-8");
762 if (width + w > x1 - x)
773 if (!(options & TAB_EMPH) || a->emphasis == EMPH_NONE)
774 memcpy (ascii_reserve (a, y, x, x + width, n), string, n);
782 /* First figure out how many bytes need to be inserted. */
784 for (ofs = 0; ofs < n; ofs += mblen)
789 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
790 w = uc_width (uc, "UTF-8");
793 n_out += a->emphasis == EMPH_UNDERLINE ? 2 : 1 + mblen;
796 /* Then insert them. */
797 out = ascii_reserve (a, y, x, x + width, n_out);
798 for (ofs = 0; ofs < n; ofs += mblen)
803 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
804 w = uc_width (uc, "UTF-8");
808 if (a->emphasis == EMPH_UNDERLINE)
811 out = mempcpy (out, string + ofs, mblen);
814 out = mempcpy (out, string + ofs, mblen);
820 ascii_layout_cell_text (struct ascii_driver *a,
821 const struct cell_contents *contents, int *footnote_idx,
822 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
833 length = strlen (contents->text);
834 if (contents->n_footnotes)
840 ds_extend (&s, length + contents->n_footnotes * 4);
841 ds_put_cstr (&s, contents->text);
842 for (i = 0; i < contents->n_footnotes; i++)
846 str_format_26adic (++*footnote_idx, false, marker, sizeof marker);
847 ds_put_format (&s, "[%s]", marker);
850 length = ds_length (&s);
851 text = ds_steal_cstr (&s);
857 text = contents->text;
860 breaks = xmalloc (length + 1);
861 u8_possible_linebreaks (CHAR_CAST (const uint8_t *, text), length,
863 breaks[length] = (breaks[length - 1] == UC_BREAK_MANDATORY
864 ? UC_BREAK_PROHIBITED : UC_BREAK_POSSIBLE);
867 bb_width = bb[H][1] - bb[H][0];
868 for (y = bb[V][0]; y < bb[V][1] && pos < length; y++)
870 const uint8_t *line = CHAR_CAST (const uint8_t *, text + pos);
871 const char *b = breaks + pos;
872 size_t n = length - pos;
874 size_t last_break_ofs = 0;
875 int last_break_width = 0;
880 for (ofs = 0; ofs < n; )
886 mblen = u8_mbtouc (&uc, line + ofs, n - ofs);
887 if (b[ofs] == UC_BREAK_MANDATORY)
889 else if (b[ofs] == UC_BREAK_POSSIBLE)
891 last_break_ofs = ofs;
892 last_break_width = width;
895 w = uc_width (uc, "UTF-8");
898 if (width + w > bb_width)
900 if (isspace (line[ofs]))
902 else if (last_break_ofs != 0)
904 ofs = last_break_ofs;
905 width = last_break_width;
914 /* Trim any trailing spaces off the end of the text to be drawn. */
915 for (graph_ofs = ofs; graph_ofs > 0; graph_ofs--)
916 if (!isspace (line[graph_ofs - 1]))
918 width -= ofs - graph_ofs;
921 text_draw (a, contents->options, bb, clip, y, line, graph_ofs, width);
923 /* If a new-line ended the line, just skip the new-line. Otherwise, skip
924 past any spaces past the end of the line (but not past a new-line). */
925 if (b[ofs] == UC_BREAK_MANDATORY)
928 while (ofs < n && isspace (line[ofs]) && b[ofs] != UC_BREAK_MANDATORY)
937 if (text != contents->text)
938 free (CONST_CAST (char *, text));
944 ascii_layout_subtable (struct ascii_driver *a,
945 const struct cell_contents *contents,
946 int *footnote_idx UNUSED /* XXX */,
947 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2] UNUSED,
950 struct render_params params;
951 struct render_pager *p;
952 int r[TABLE_N_AXES][2];
956 params.draw_line = ascii_draw_line;
957 params.measure_cell_width = ascii_measure_cell_width;
958 params.measure_cell_height = ascii_measure_cell_height;
959 params.adjust_break = NULL;
960 params.draw_cell = ascii_draw_cell,
962 params.size[H] = bb[TABLE_HORZ][1] - bb[TABLE_HORZ][0];
963 params.size[V] = bb[TABLE_VERT][1] - bb[TABLE_VERT][0];
964 params.font_size[H] = 1;
965 params.font_size[V] = 1;
966 for (i = 0; i < RENDER_N_LINES; i++)
968 int width = i == RENDER_LINE_NONE ? 0 : 1;
969 params.line_widths[H][i] = width;
970 params.line_widths[V][i] = width;
973 p = render_pager_create (¶ms, contents->table);
974 width = render_pager_get_size (p, TABLE_HORZ);
975 height = render_pager_get_size (p, TABLE_VERT);
977 /* r = intersect(bb, clip) - bb. */
978 for (i = 0; i < TABLE_N_AXES; i++)
980 r[i][0] = MAX (bb[i][0], clip[i][0]) - bb[i][0];
981 r[i][1] = MIN (bb[i][1], clip[i][1]) - bb[i][0];
984 if (r[H][0] < r[H][1] && r[V][0] < r[V][1])
986 unsigned int alignment = contents->options & TAB_ALIGNMENT;
989 a->x += bb[TABLE_HORZ][0];
990 if (alignment == TAB_RIGHT)
991 a->x += params.size[H] - width;
992 else if (alignment == TAB_CENTER)
993 a->x += (params.size[H] - width) / 2;
994 a->y += bb[TABLE_VERT][0];
995 render_pager_draw (p);
996 a->y -= bb[TABLE_VERT][0];
999 render_pager_destroy (p);
1001 if (width > *widthp)
1003 return bb[V][0] + height;
1007 ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
1009 int bb_[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1010 int *widthp, int *heightp)
1012 int bb[TABLE_N_AXES][2];
1018 memcpy (bb, bb_, sizeof bb);
1019 for (i = 0; i < cell->n_contents && bb[V][0] < bb[V][1]; i++)
1021 const struct cell_contents *contents = &cell->contents[i];
1023 /* Put a blank line between contents. */
1027 if (bb[V][0] >= bb[V][1])
1032 bb[V][0] = ascii_layout_cell_text (a, contents, &footnote_idx,
1035 bb[V][0] = ascii_layout_subtable (a, contents, &footnote_idx,
1038 *heightp = bb[V][0] - bb_[V][0];
1042 ascii_test_write (struct output_driver *driver,
1043 const char *s, int x, int y, unsigned int options)
1045 struct ascii_driver *a = ascii_driver_cast (driver);
1046 struct cell_contents contents;
1047 struct table_cell cell;
1048 int bb[TABLE_N_AXES][2];
1051 if (a->file == NULL && !ascii_open_page (a))
1055 contents.options = options | TAB_LEFT;
1056 contents.text = CONST_CAST (char *, s);
1057 contents.table = NULL;
1058 contents.n_footnotes = 0;
1060 memset (&cell, 0, sizeof cell);
1061 cell.contents = &contents;
1062 cell.n_contents = 1;
1064 bb[TABLE_HORZ][0] = x;
1065 bb[TABLE_HORZ][1] = a->width;
1066 bb[TABLE_VERT][0] = y;
1067 bb[TABLE_VERT][1] = a->length;
1069 ascii_layout_cell (a, &cell, 0, bb, bb, &width, &height);
1075 ascii_test_set_length (struct output_driver *driver, int y, int length)
1077 struct ascii_driver *a = ascii_driver_cast (driver);
1079 if (a->file == NULL && !ascii_open_page (a))
1082 if (y < 0 || y >= a->length)
1084 u8_line_set_length (&a->lines[y], length);
1087 /* ascii_close_page () and support routines. */
1089 #if HAVE_DECL_SIGWINCH
1090 static struct ascii_driver *the_driver;
1093 winch_handler (int signum UNUSED)
1095 update_page_size (the_driver, false);
1100 ascii_open_page (struct ascii_driver *a)
1107 if (a->file == NULL)
1109 a->file = fn_open (a->handle, a->append ? "a" : "w");
1110 if (a->file != NULL)
1112 if ( isatty (fileno (a->file)))
1114 #if HAVE_DECL_SIGWINCH
1115 struct sigaction action;
1116 sigemptyset (&action.sa_mask);
1117 action.sa_flags = 0;
1118 action.sa_handler = winch_handler;
1120 sigaction (SIGWINCH, &action, NULL);
1122 a->auto_width = true;
1123 a->auto_length = true;
1128 msg_error (errno, _("ascii: opening output file `%s'"),
1129 fh_get_file_name (a->handle));
1137 reallocate_lines (a);
1139 for (i = 0; i < a->length; i++)
1140 u8_line_clear (&a->lines[i]);
1146 output_title_line (FILE *out, int width, const char *left, const char *right)
1148 struct string s = DS_EMPTY_INITIALIZER;
1149 ds_put_byte_multiple (&s, ' ', width);
1152 size_t length = MIN (strlen (left), width);
1153 memcpy (ds_end (&s) - width, left, length);
1157 size_t length = MIN (strlen (right), width);
1158 memcpy (ds_end (&s) - length, right, length);
1160 ds_put_byte (&s, '\n');
1161 fputs (ds_cstr (&s), out);
1166 ascii_close_page (struct ascii_driver *a)
1172 if (a->file == NULL)
1175 if (!a->top_margin && !a->bottom_margin && a->squeeze_blank_lines
1176 && !a->paginate && a->page_number > 1)
1177 putc ('\n', a->file);
1179 for (i = 0; i < a->top_margin; i++)
1180 putc ('\n', a->file);
1185 r1 = xasprintf (_("%s - Page %d"), get_start_date (), a->page_number);
1186 r2 = xasprintf ("%s - %s" , version, host_system);
1188 output_title_line (a->file, a->width, a->title, r1);
1189 output_title_line (a->file, a->width, a->subtitle, r2);
1190 putc ('\n', a->file);
1197 for (y = 0; y < a->allocated_lines; y++)
1199 struct u8_line *line = &a->lines[y];
1201 if (a->squeeze_blank_lines && y > 0 && line->width == 0)
1207 putc ('\n', a->file);
1211 while (ds_chomp_byte (&line->s, ' '))
1213 fwrite (ds_data (&line->s), 1, ds_length (&line->s), a->file);
1214 putc ('\n', a->file);
1217 if (!a->squeeze_blank_lines)
1218 for (y = a->allocated_lines; y < a->length; y++)
1219 putc ('\n', a->file);
1221 for (i = 0; i < a->bottom_margin; i++)
1222 putc ('\n', a->file);
1224 putc ('\f', a->file);