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 *,
200 int footnote_idx, int *min, int *max);
201 static int ascii_measure_cell_height (void *, const struct table_cell *,
202 int footnote_idx, int width);
203 static void ascii_draw_cell (void *, const struct table_cell *,
204 int footnote_idx, 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_output_table_item (struct ascii_driver *a,
411 const struct table_item *table_item)
413 struct render_params params;
414 struct render_pager *p;
417 update_page_size (a, false);
419 params.draw_line = ascii_draw_line;
420 params.measure_cell_width = ascii_measure_cell_width;
421 params.measure_cell_height = ascii_measure_cell_height;
422 params.adjust_break = NULL;
423 params.draw_cell = ascii_draw_cell;
425 params.size[H] = a->width;
426 params.size[V] = a->length;
427 params.font_size[H] = 1;
428 params.font_size[V] = 1;
429 for (i = 0; i < RENDER_N_LINES; i++)
431 int width = i == RENDER_LINE_NONE ? 0 : 1;
432 params.line_widths[H][i] = width;
433 params.line_widths[V][i] = width;
435 for (i = 0; i < TABLE_N_AXES; i++)
436 params.min_break[i] = a->min_break[i];
438 if (a->file == NULL && !ascii_open_page (a))
441 p = render_pager_create (¶ms, table_item);
442 while (render_pager_has_next (p))
448 used = render_pager_draw_next (p, a->length - a->y);
452 ascii_close_page (a);
453 if (!ascii_open_page (a))
459 render_pager_destroy (p);
463 ascii_output_text (struct ascii_driver *a, const char *text)
465 struct table_item *table_item;
467 table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
468 ascii_output_table_item (a, table_item);
469 table_item_unref (table_item);
473 ascii_submit (struct output_driver *driver,
474 const struct output_item *output_item)
476 struct ascii_driver *a = ascii_driver_cast (driver);
478 output_driver_track_current_command (output_item, &a->command_name);
483 if (is_table_item (output_item))
484 ascii_output_table_item (a, to_table_item (output_item));
486 else if (is_chart_item (output_item) && a->chart_file_name != NULL)
488 struct chart_item *chart_item = to_chart_item (output_item);
491 file_name = xr_draw_png_chart (chart_item, a->chart_file_name,
495 if (file_name != NULL)
497 struct text_item *text_item;
499 text_item = text_item_create_format (
500 TEXT_ITEM_PARAGRAPH, _("See %s for a chart."), file_name);
502 ascii_submit (driver, &text_item->output_item);
503 text_item_unref (text_item);
507 #endif /* HAVE_CAIRO */
508 else if (is_text_item (output_item))
510 const struct text_item *text_item = to_text_item (output_item);
511 enum text_item_type type = text_item_get_type (text_item);
512 const char *text = text_item_get_text (text_item);
516 case TEXT_ITEM_TITLE:
518 a->title = xstrdup (text);
521 case TEXT_ITEM_SUBTITLE:
523 a->subtitle = xstrdup (text);
526 case TEXT_ITEM_COMMAND_OPEN:
527 case TEXT_ITEM_COMMAND_CLOSE:
530 case TEXT_ITEM_BLANK_LINE:
535 case TEXT_ITEM_EJECT_PAGE:
537 ascii_close_page (a);
541 ascii_output_text (a, text);
545 else if (is_message_item (output_item))
547 const struct message_item *message_item = to_message_item (output_item);
548 const struct msg *msg = message_item_get_msg (message_item);
549 char *s = msg_to_string (msg, a->command_name);
550 ascii_output_text (a, s);
555 const struct output_driver_factory txt_driver_factory =
556 { "txt", "-", ascii_create };
557 const struct output_driver_factory list_driver_factory =
558 { "list", "-", ascii_create };
560 static const struct output_driver_class ascii_driver_class =
568 static char *ascii_reserve (struct ascii_driver *, int y, int x0, int x1,
570 static void ascii_layout_cell (struct ascii_driver *,
571 const struct table_cell *,
573 int bb[TABLE_N_AXES][2],
574 int clip[TABLE_N_AXES][2],
575 int *width, int *height);
578 ascii_draw_line (void *a_, int bb[TABLE_N_AXES][2],
579 enum render_line_style styles[TABLE_N_AXES][2])
581 struct ascii_driver *a = a_;
588 /* Clip to the page. */
589 x0 = MAX (bb[H][0] + a->x, 0);
590 y0 = MAX (bb[V][0] + a->y, 0);
591 x1 = MIN (bb[H][1] + a->x, a->width);
592 y1 = MIN (bb[V][1] + a->y, a->length);
593 if (x1 <= 0 || y1 <= 0 || x0 >= a->width || y0 >= a->length)
597 uc = a->box[make_box_index (styles[V][0], styles[V][1],
598 styles[H][0], styles[H][1])];
599 mblen = u8_uctomb (CHAR_CAST (uint8_t *, mbchar), uc, 6);
600 for (y = y0; y < y1; y++)
602 char *p = ascii_reserve (a, y, x0, x1, mblen * (x1 - x0));
603 for (x = x0; x < x1; x++)
605 memcpy (p, mbchar, mblen);
612 ascii_measure_cell_width (void *a_, const struct table_cell *cell,
613 int footnote_idx, int *min_width, int *max_width)
615 struct ascii_driver *a = a_;
616 int bb[TABLE_N_AXES][2];
617 int clip[TABLE_N_AXES][2];
624 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
625 ascii_layout_cell (a, cell, footnote_idx, bb, clip, max_width, &h);
627 if (cell->n_contents != 1
628 || cell->contents[0].table
629 || cell->contents[0].n_footnotes
630 || strchr (cell->contents[0].text, ' '))
633 ascii_layout_cell (a, cell, footnote_idx, bb, clip, min_width, &h);
636 *min_width = *max_width;
640 ascii_measure_cell_height (void *a_, const struct table_cell *cell,
641 int footnote_idx, int width)
643 struct ascii_driver *a = a_;
644 int bb[TABLE_N_AXES][2];
645 int clip[TABLE_N_AXES][2];
652 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
653 ascii_layout_cell (a, cell, footnote_idx, bb, clip, &w, &h);
658 ascii_draw_cell (void *a_, const struct table_cell *cell, int footnote_idx,
659 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
661 struct ascii_driver *a = a_;
664 ascii_layout_cell (a, cell, footnote_idx, bb, clip, &w, &h);
668 ascii_reserve (struct ascii_driver *a, int y, int x0, int x1, int n)
670 assert (y < a->allocated_lines);
671 return u8_line_reserve (&a->lines[y], x0, x1, n);
675 text_draw (struct ascii_driver *a, unsigned int options,
676 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
677 int y, const uint8_t *string, int n, size_t width)
679 int x0 = MAX (0, clip[H][0] + a->x);
680 int y0 = MAX (0, clip[V][0] + a->y);
681 int x1 = MIN (a->width, clip[H][1] + a->x);
682 int y1 = MIN (a->length, clip[V][1] + a->y);
686 if (y < y0 || y >= y1)
689 switch (options & TAB_ALIGNMENT)
695 x = (bb[H][0] + bb[H][1] - width + 1) / 2;
698 x = bb[H][1] - width;
715 mblen = u8_mbtouc (&uc, string, n);
720 w = uc_width (uc, "UTF-8");
735 for (ofs = 0; ofs < n; )
741 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
743 w = uc_width (uc, "UTF-8");
746 if (width + w > x1 - x)
757 if (!(options & TAB_EMPH) || a->emphasis == EMPH_NONE)
758 memcpy (ascii_reserve (a, y, x, x + width, n), string, n);
766 /* First figure out how many bytes need to be inserted. */
768 for (ofs = 0; ofs < n; ofs += mblen)
773 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
774 w = uc_width (uc, "UTF-8");
777 n_out += a->emphasis == EMPH_UNDERLINE ? 2 : 1 + mblen;
780 /* Then insert them. */
781 out = ascii_reserve (a, y, x, x + width, n_out);
782 for (ofs = 0; ofs < n; ofs += mblen)
787 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
788 w = uc_width (uc, "UTF-8");
792 if (a->emphasis == EMPH_UNDERLINE)
795 out = mempcpy (out, string + ofs, mblen);
798 out = mempcpy (out, string + ofs, mblen);
804 ascii_layout_cell_text (struct ascii_driver *a,
805 const struct cell_contents *contents, int *footnote_idx,
806 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
817 length = strlen (contents->text);
818 if (contents->n_footnotes)
824 ds_extend (&s, length + contents->n_footnotes * 4);
825 ds_put_cstr (&s, contents->text);
826 for (i = 0; i < contents->n_footnotes; i++)
830 str_format_26adic (++*footnote_idx, false, marker, sizeof marker);
831 ds_put_format (&s, "[%s]", marker);
834 length = ds_length (&s);
835 text = ds_steal_cstr (&s);
841 text = contents->text;
844 breaks = xmalloc (length + 1);
845 u8_possible_linebreaks (CHAR_CAST (const uint8_t *, text), length,
847 breaks[length] = (breaks[length - 1] == UC_BREAK_MANDATORY
848 ? UC_BREAK_PROHIBITED : UC_BREAK_POSSIBLE);
851 bb_width = bb[H][1] - bb[H][0];
852 for (y = bb[V][0]; y < bb[V][1] && pos < length; y++)
854 const uint8_t *line = CHAR_CAST (const uint8_t *, text + pos);
855 const char *b = breaks + pos;
856 size_t n = length - pos;
858 size_t last_break_ofs = 0;
859 int last_break_width = 0;
864 for (ofs = 0; ofs < n; )
870 mblen = u8_mbtouc (&uc, line + ofs, n - ofs);
871 if (b[ofs] == UC_BREAK_MANDATORY)
873 else if (b[ofs] == UC_BREAK_POSSIBLE)
875 last_break_ofs = ofs;
876 last_break_width = width;
879 w = uc_width (uc, "UTF-8");
882 if (width + w > bb_width)
884 if (isspace (line[ofs]))
886 else if (last_break_ofs != 0)
888 ofs = last_break_ofs;
889 width = last_break_width;
898 /* Trim any trailing spaces off the end of the text to be drawn. */
899 for (graph_ofs = ofs; graph_ofs > 0; graph_ofs--)
900 if (!isspace (line[graph_ofs - 1]))
902 width -= ofs - graph_ofs;
905 text_draw (a, contents->options, bb, clip, y, line, graph_ofs, width);
907 /* If a new-line ended the line, just skip the new-line. Otherwise, skip
908 past any spaces past the end of the line (but not past a new-line). */
909 if (b[ofs] == UC_BREAK_MANDATORY)
912 while (ofs < n && isspace (line[ofs]) && b[ofs] != UC_BREAK_MANDATORY)
921 if (text != contents->text)
922 free (CONST_CAST (char *, text));
928 ascii_layout_subtable (struct ascii_driver *a,
929 const struct cell_contents *contents,
930 int *footnote_idx UNUSED /* XXX */,
931 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2] UNUSED,
934 struct render_params params;
935 struct render_pager *p;
936 int r[TABLE_N_AXES][2];
940 params.draw_line = ascii_draw_line;
941 params.measure_cell_width = ascii_measure_cell_width;
942 params.measure_cell_height = ascii_measure_cell_height;
943 params.adjust_break = NULL;
944 params.draw_cell = ascii_draw_cell,
946 params.size[H] = bb[TABLE_HORZ][1] - bb[TABLE_HORZ][0];
947 params.size[V] = bb[TABLE_VERT][1] - bb[TABLE_VERT][0];
948 params.font_size[H] = 1;
949 params.font_size[V] = 1;
950 for (i = 0; i < RENDER_N_LINES; i++)
952 int width = i == RENDER_LINE_NONE ? 0 : 1;
953 params.line_widths[H][i] = width;
954 params.line_widths[V][i] = width;
957 p = render_pager_create (¶ms, contents->table);
958 width = render_pager_get_size (p, TABLE_HORZ);
959 height = render_pager_get_size (p, TABLE_VERT);
961 /* r = intersect(bb, clip) - bb. */
962 for (i = 0; i < TABLE_N_AXES; i++)
964 r[i][0] = MAX (bb[i][0], clip[i][0]) - bb[i][0];
965 r[i][1] = MIN (bb[i][1], clip[i][1]) - bb[i][0];
968 if (r[H][0] < r[H][1] && r[V][0] < r[V][1])
970 unsigned int alignment = contents->options & TAB_ALIGNMENT;
973 a->x += bb[TABLE_HORZ][0];
974 if (alignment == TAB_RIGHT)
975 a->x += params.size[H] - width;
976 else if (alignment == TAB_CENTER)
977 a->x += (params.size[H] - width) / 2;
978 a->y += bb[TABLE_VERT][0];
979 render_pager_draw (p);
980 a->y -= bb[TABLE_VERT][0];
983 render_pager_destroy (p);
987 return bb[V][0] + height;
991 ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
993 int bb_[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
994 int *widthp, int *heightp)
996 int bb[TABLE_N_AXES][2];
1002 memcpy (bb, bb_, sizeof bb);
1003 for (i = 0; i < cell->n_contents && bb[V][0] < bb[V][1]; i++)
1005 const struct cell_contents *contents = &cell->contents[i];
1007 /* Put a blank line between contents. */
1011 if (bb[V][0] >= bb[V][1])
1016 bb[V][0] = ascii_layout_cell_text (a, contents, &footnote_idx,
1019 bb[V][0] = ascii_layout_subtable (a, contents, &footnote_idx,
1022 *heightp = bb[V][0] - bb_[V][0];
1026 ascii_test_write (struct output_driver *driver,
1027 const char *s, int x, int y, unsigned int options)
1029 struct ascii_driver *a = ascii_driver_cast (driver);
1030 struct cell_contents contents;
1031 struct table_cell cell;
1032 int bb[TABLE_N_AXES][2];
1035 if (a->file == NULL && !ascii_open_page (a))
1039 contents.options = options | TAB_LEFT;
1040 contents.text = CONST_CAST (char *, s);
1041 contents.table = NULL;
1042 contents.n_footnotes = 0;
1044 memset (&cell, 0, sizeof cell);
1045 cell.contents = &contents;
1046 cell.n_contents = 1;
1048 bb[TABLE_HORZ][0] = x;
1049 bb[TABLE_HORZ][1] = a->width;
1050 bb[TABLE_VERT][0] = y;
1051 bb[TABLE_VERT][1] = a->length;
1053 ascii_layout_cell (a, &cell, 0, bb, bb, &width, &height);
1059 ascii_test_set_length (struct output_driver *driver, int y, int length)
1061 struct ascii_driver *a = ascii_driver_cast (driver);
1063 if (a->file == NULL && !ascii_open_page (a))
1066 if (y < 0 || y >= a->length)
1068 u8_line_set_length (&a->lines[y], length);
1071 /* ascii_close_page () and support routines. */
1073 #if HAVE_DECL_SIGWINCH
1074 static struct ascii_driver *the_driver;
1077 winch_handler (int signum UNUSED)
1079 update_page_size (the_driver, false);
1084 ascii_open_page (struct ascii_driver *a)
1091 if (a->file == NULL)
1093 a->file = fn_open (a->file_name, a->append ? "a" : "w");
1094 if (a->file != NULL)
1096 if ( isatty (fileno (a->file)))
1098 #if HAVE_DECL_SIGWINCH
1099 struct sigaction action;
1100 sigemptyset (&action.sa_mask);
1101 action.sa_flags = 0;
1102 action.sa_handler = winch_handler;
1104 sigaction (SIGWINCH, &action, NULL);
1106 a->auto_width = true;
1107 a->auto_length = true;
1112 msg_error (errno, _("ascii: opening output file `%s'"),
1121 reallocate_lines (a);
1123 for (i = 0; i < a->length; i++)
1124 u8_line_clear (&a->lines[i]);
1130 output_title_line (FILE *out, int width, const char *left, const char *right)
1132 struct string s = DS_EMPTY_INITIALIZER;
1133 ds_put_byte_multiple (&s, ' ', width);
1136 size_t length = MIN (strlen (left), width);
1137 memcpy (ds_end (&s) - width, left, length);
1141 size_t length = MIN (strlen (right), width);
1142 memcpy (ds_end (&s) - length, right, length);
1144 ds_put_byte (&s, '\n');
1145 fputs (ds_cstr (&s), out);
1150 ascii_close_page (struct ascii_driver *a)
1156 if (a->file == NULL)
1159 if (!a->top_margin && !a->bottom_margin && a->squeeze_blank_lines
1160 && !a->paginate && a->page_number > 1)
1161 putc ('\n', a->file);
1163 for (i = 0; i < a->top_margin; i++)
1164 putc ('\n', a->file);
1169 r1 = xasprintf (_("%s - Page %d"), get_start_date (), a->page_number);
1170 r2 = xasprintf ("%s - %s" , version, host_system);
1172 output_title_line (a->file, a->width, a->title, r1);
1173 output_title_line (a->file, a->width, a->subtitle, r2);
1174 putc ('\n', a->file);
1181 for (y = 0; y < a->allocated_lines; y++)
1183 struct u8_line *line = &a->lines[y];
1185 if (a->squeeze_blank_lines && y > 0 && line->width == 0)
1191 putc ('\n', a->file);
1195 while (ds_chomp_byte (&line->s, ' '))
1197 fwrite (ds_data (&line->s), 1, ds_length (&line->s), a->file);
1198 putc ('\n', a->file);
1201 if (!a->squeeze_blank_lines)
1202 for (y = a->allocated_lines; y < a->length; y++)
1203 putc ('\n', a->file);
1205 for (i = 0; i < a->bottom_margin; i++)
1206 putc ('\n', a->file);
1208 putc ('\f', a->file);