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 return ((right * RENDER_N_LINES + bottom) * RENDER_N_LINES + left) * RENDER_N_LINES + top;
133 /* How to emphasize text. */
136 EMPH_BOLD, /* Overstrike for bold. */
137 EMPH_UNDERLINE, /* Overstrike for underlining. */
138 EMPH_NONE /* No emphasis. */
141 /* ASCII output driver. */
144 struct output_driver driver;
146 /* User parameters. */
147 bool append; /* Append if output file already exists? */
148 bool headers; /* Print headers at top of page? */
149 bool paginate; /* Insert formfeeds? */
150 bool squeeze_blank_lines; /* Squeeze multiple blank lines into one? */
151 enum emphasis_style emphasis; /* How to emphasize text. */
152 char *chart_file_name; /* Name of files used for charts. */
155 /* Colours for charts */
160 int width; /* Page width. */
161 int length; /* Page length minus margins and header. */
162 bool auto_width; /* Use viewwidth as page width? */
163 bool auto_length; /* Use viewlength as page width? */
165 int top_margin; /* Top margin in lines. */
166 int bottom_margin; /* Bottom margin in lines. */
168 int min_break[TABLE_N_AXES]; /* Min cell size to break across pages. */
170 const ucs4_t *box; /* Line & box drawing characters. */
172 /* Internal state. */
176 struct file_handle *handle;
177 FILE *file; /* Output file. */
178 bool error; /* Output error? */
179 int page_number; /* Current page number. */
180 struct u8_line *lines; /* Page content. */
181 int allocated_lines; /* Number of lines allocated. */
182 int chart_cnt; /* Number of charts so far. */
186 static const struct output_driver_class ascii_driver_class;
188 static void ascii_submit (struct output_driver *, const struct output_item *);
190 static int vertical_margins (const struct ascii_driver *);
192 static bool update_page_size (struct ascii_driver *, bool issue_error);
193 static int parse_page_size (struct driver_option *);
195 static void ascii_close_page (struct ascii_driver *);
196 static bool ascii_open_page (struct ascii_driver *);
198 static void ascii_draw_line (void *, int bb[TABLE_N_AXES][2],
199 enum render_line_style styles[TABLE_N_AXES][2]);
200 static void ascii_measure_cell_width (void *, const struct table_cell *,
201 int footnote_idx, int *min, int *max);
202 static int ascii_measure_cell_height (void *, const struct table_cell *,
203 int footnote_idx, int width);
204 static void ascii_draw_cell (void *, const struct table_cell *,
205 int footnote_idx, int bb[TABLE_N_AXES][2],
206 int clip[TABLE_N_AXES][2]);
209 reallocate_lines (struct ascii_driver *a)
211 if (a->length > a->allocated_lines)
214 a->lines = xnrealloc (a->lines, a->length, sizeof *a->lines);
215 for (i = a->allocated_lines; i < a->length; i++)
216 u8_line_init (&a->lines[i]);
217 a->allocated_lines = a->length;
222 static struct ascii_driver *
223 ascii_driver_cast (struct output_driver *driver)
225 assert (driver->class == &ascii_driver_class);
226 return UP_CAST (driver, struct ascii_driver, driver);
229 static struct driver_option *
230 opt (struct output_driver *d, struct string_map *options, const char *key,
231 const char *default_value)
233 return driver_option_get (d, options, key, default_value);
236 static struct output_driver *
237 ascii_create (struct file_handle *fh, enum settings_output_devices device_type,
238 struct string_map *o)
240 enum { BOX_ASCII, BOX_UNICODE } box;
241 int min_break[TABLE_N_AXES];
242 struct output_driver *d;
243 struct ascii_driver *a;
246 a = xzalloc (sizeof *a);
248 output_driver_init (&a->driver, &ascii_driver_class, fh_get_file_name (fh), device_type);
249 a->append = parse_boolean (opt (d, o, "append", "false"));
250 a->headers = parse_boolean (opt (d, o, "headers", "false"));
251 a->paginate = parse_boolean (opt (d, o, "paginate", "false"));
252 a->squeeze_blank_lines = parse_boolean (opt (d, o, "squeeze", "true"));
253 a->emphasis = parse_enum (opt (d, o, "emphasis", "none"),
255 "underline", EMPH_UNDERLINE,
259 a->chart_file_name = parse_chart_file_name (opt (d, o, "charts", fh_get_file_name (fh)));
262 a->top_margin = parse_int (opt (d, o, "top-margin", "0"), 0, INT_MAX);
263 a->bottom_margin = parse_int (opt (d, o, "bottom-margin", "0"), 0, INT_MAX);
265 min_break[H] = parse_int (opt (d, o, "min-hbreak", "-1"), -1, INT_MAX);
266 min_break[V] = parse_int (opt (d, o, "min-vbreak", "-1"), -1, INT_MAX);
268 a->width = parse_page_size (opt (d, o, "width", "79"));
269 paper_length = parse_page_size (opt (d, o, "length", "66"));
270 a->auto_width = a->width < 0;
271 a->auto_length = paper_length < 0;
272 a->length = paper_length - vertical_margins (a);
273 a->min_break[H] = min_break[H] >= 0 ? min_break[H] : a->width / 2;
274 a->min_break[V] = min_break[V] >= 0 ? min_break[V] : a->length / 2;
276 parse_color (d, o, "background-color", "#FFFFFFFFFFFF", &a->bg);
277 parse_color (d, o, "foreground-color", "#000000000000", &a->fg);
279 box = parse_enum (opt (d, o, "box", "ascii"),
281 "unicode", BOX_UNICODE,
283 a->box = box == BOX_ASCII ? ascii_box_chars : unicode_box_chars;
285 a->command_name = NULL;
286 a->title = xstrdup ("");
287 a->subtitle = xstrdup ("");
292 a->allocated_lines = 0;
295 if (!update_page_size (a, true))
301 output_driver_destroy (d);
306 parse_page_size (struct driver_option *option)
308 int dim = atol (option->default_value);
310 if (option->value != NULL)
312 if (!strcmp (option->value, "auto"))
320 value = strtol (option->value, &tail, 0);
321 if (dim >= 1 && errno != ERANGE && *tail == '\0')
324 msg (MW, _("%s: %s must be positive integer or `auto'"),
325 option->driver_name, option->name);
329 driver_option_destroy (option);
335 vertical_margins (const struct ascii_driver *a)
337 return a->top_margin + a->bottom_margin + (a->headers ? 3 : 0);
340 /* Re-calculates the page width and length based on settings,
341 margins, and, if "auto" is set, the size of the user's
342 terminal window or GUI output window. */
344 update_page_size (struct ascii_driver *a, bool issue_error)
346 enum { MIN_WIDTH = 6, MIN_LENGTH = 6 };
349 a->width = settings_get_viewwidth ();
351 a->length = settings_get_viewlength () - vertical_margins (a);
353 if (a->width < MIN_WIDTH || a->length < MIN_LENGTH)
357 _("ascii: page excluding margins and headers "
358 "must be at least %d characters wide by %d lines long, but "
359 "as configured is only %d characters by %d lines"),
360 MIN_WIDTH, MIN_LENGTH,
361 a->width, a->length);
362 if (a->width < MIN_WIDTH)
363 a->width = MIN_WIDTH;
364 if (a->length < MIN_LENGTH)
365 a->length = MIN_LENGTH;
369 reallocate_lines (a);
375 ascii_destroy (struct output_driver *driver)
377 struct ascii_driver *a = ascii_driver_cast (driver);
381 ascii_close_page (a);
384 fn_close (a->handle, a->file);
385 fh_unref (a->handle);
386 free (a->command_name);
389 free (a->chart_file_name);
390 for (i = 0; i < a->allocated_lines; i++)
391 u8_line_destroy (&a->lines[i]);
397 ascii_flush (struct output_driver *driver)
399 struct ascii_driver *a = ascii_driver_cast (driver);
402 ascii_close_page (a);
404 if (fn_close (a->handle, a->file) != 0)
405 msg_error (errno, _("ascii: closing output file `%s'"), fh_get_file_name (a->handle));
411 ascii_output_table_item (struct ascii_driver *a,
412 const struct table_item *table_item)
414 struct render_params params;
415 struct render_pager *p;
418 update_page_size (a, false);
420 params.draw_line = ascii_draw_line;
421 params.measure_cell_width = ascii_measure_cell_width;
422 params.measure_cell_height = ascii_measure_cell_height;
423 params.adjust_break = NULL;
424 params.draw_cell = ascii_draw_cell;
426 params.size[H] = a->width;
427 params.size[V] = a->length;
428 params.font_size[H] = 1;
429 params.font_size[V] = 1;
430 for (i = 0; i < RENDER_N_LINES; i++)
432 int width = i == RENDER_LINE_NONE ? 0 : 1;
433 params.line_widths[H][i] = width;
434 params.line_widths[V][i] = width;
436 for (i = 0; i < TABLE_N_AXES; i++)
437 params.min_break[i] = a->min_break[i];
439 if (a->file == NULL && !ascii_open_page (a))
442 p = render_pager_create (¶ms, table_item);
443 while (render_pager_has_next (p))
449 used = render_pager_draw_next (p, a->length - a->y);
453 ascii_close_page (a);
454 if (!ascii_open_page (a))
460 render_pager_destroy (p);
464 ascii_output_text (struct ascii_driver *a, const char *text)
466 struct table_item *table_item;
468 table_item = table_item_create (table_from_string (TAB_LEFT, text),
470 ascii_output_table_item (a, table_item);
471 table_item_unref (table_item);
475 ascii_submit (struct output_driver *driver,
476 const struct output_item *output_item)
478 struct ascii_driver *a = ascii_driver_cast (driver);
480 output_driver_track_current_command (output_item, &a->command_name);
485 if (is_table_item (output_item))
486 ascii_output_table_item (a, to_table_item (output_item));
488 else if (is_chart_item (output_item) && a->chart_file_name != NULL)
490 struct chart_item *chart_item = to_chart_item (output_item);
493 file_name = xr_draw_png_chart (chart_item, a->chart_file_name,
497 if (file_name != NULL)
499 struct text_item *text_item;
501 text_item = text_item_create_format (
502 TEXT_ITEM_PARAGRAPH, _("See %s for a chart."), file_name);
504 ascii_submit (driver, &text_item->output_item);
505 text_item_unref (text_item);
509 #endif /* HAVE_CAIRO */
510 else if (is_text_item (output_item))
512 const struct text_item *text_item = to_text_item (output_item);
513 enum text_item_type type = text_item_get_type (text_item);
514 const char *text = text_item_get_text (text_item);
518 case TEXT_ITEM_TITLE:
520 a->title = xstrdup (text);
523 case TEXT_ITEM_SUBTITLE:
525 a->subtitle = xstrdup (text);
528 case TEXT_ITEM_COMMAND_OPEN:
529 case TEXT_ITEM_COMMAND_CLOSE:
532 case TEXT_ITEM_BLANK_LINE:
537 case TEXT_ITEM_EJECT_PAGE:
539 ascii_close_page (a);
543 ascii_output_text (a, text);
547 else if (is_message_item (output_item))
549 const struct message_item *message_item = to_message_item (output_item);
550 const struct msg *msg = message_item_get_msg (message_item);
551 char *s = msg_to_string (msg, a->command_name);
552 ascii_output_text (a, s);
557 const struct output_driver_factory txt_driver_factory =
558 { "txt", "-", ascii_create };
559 const struct output_driver_factory list_driver_factory =
560 { "list", "-", ascii_create };
562 static const struct output_driver_class ascii_driver_class =
570 static char *ascii_reserve (struct ascii_driver *, int y, int x0, int x1,
572 static void ascii_layout_cell (struct ascii_driver *,
573 const struct table_cell *,
575 int bb[TABLE_N_AXES][2],
576 int clip[TABLE_N_AXES][2],
577 int *width, int *height);
580 ascii_draw_line (void *a_, int bb[TABLE_N_AXES][2],
581 enum render_line_style styles[TABLE_N_AXES][2])
583 struct ascii_driver *a = a_;
590 /* Clip to the page. */
591 x0 = MAX (bb[H][0] + a->x, 0);
592 y0 = MAX (bb[V][0] + a->y, 0);
593 x1 = MIN (bb[H][1] + a->x, a->width);
594 y1 = MIN (bb[V][1] + a->y, a->length);
595 if (x1 <= 0 || y1 <= 0 || x0 >= a->width || y0 >= a->length)
599 uc = a->box[make_box_index (styles[V][0], styles[V][1],
600 styles[H][0], styles[H][1])];
601 mblen = u8_uctomb (CHAR_CAST (uint8_t *, mbchar), uc, 6);
602 for (y = y0; y < y1; y++)
604 char *p = ascii_reserve (a, y, x0, x1, mblen * (x1 - x0));
605 for (x = x0; x < x1; x++)
607 memcpy (p, mbchar, mblen);
614 ascii_measure_cell_width (void *a_, const struct table_cell *cell,
615 int footnote_idx, int *min_width, int *max_width)
617 struct ascii_driver *a = a_;
618 int bb[TABLE_N_AXES][2];
619 int clip[TABLE_N_AXES][2];
626 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
627 ascii_layout_cell (a, cell, footnote_idx, bb, clip, max_width, &h);
629 if (cell->n_contents != 1
630 || cell->contents[0].table
631 || cell->contents[0].n_footnotes
632 || strchr (cell->contents[0].text, ' '))
635 ascii_layout_cell (a, cell, footnote_idx, bb, clip, min_width, &h);
638 *min_width = *max_width;
642 ascii_measure_cell_height (void *a_, const struct table_cell *cell,
643 int footnote_idx, int width)
645 struct ascii_driver *a = a_;
646 int bb[TABLE_N_AXES][2];
647 int clip[TABLE_N_AXES][2];
654 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
655 ascii_layout_cell (a, cell, footnote_idx, bb, clip, &w, &h);
660 ascii_draw_cell (void *a_, const struct table_cell *cell, int footnote_idx,
661 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
663 struct ascii_driver *a = a_;
666 ascii_layout_cell (a, cell, footnote_idx, bb, clip, &w, &h);
670 ascii_reserve (struct ascii_driver *a, int y, int x0, int x1, int n)
672 assert (y < a->allocated_lines);
673 return u8_line_reserve (&a->lines[y], x0, x1, n);
677 text_draw (struct ascii_driver *a, unsigned int options,
678 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
679 int y, const uint8_t *string, int n, size_t width)
681 int x0 = MAX (0, clip[H][0] + a->x);
682 int y0 = MAX (0, clip[V][0] + a->y);
683 int x1 = MIN (a->width, clip[H][1] + a->x);
684 int y1 = MIN (a->length, clip[V][1] + a->y);
688 if (y < y0 || y >= y1)
691 switch (options & TAB_ALIGNMENT)
697 x = (bb[H][0] + bb[H][1] - width + 1) / 2;
700 x = bb[H][1] - width;
717 mblen = u8_mbtouc (&uc, string, n);
722 w = uc_width (uc, "UTF-8");
737 for (ofs = 0; ofs < n; )
743 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
745 w = uc_width (uc, "UTF-8");
748 if (width + w > x1 - x)
759 if (!(options & TAB_EMPH) || a->emphasis == EMPH_NONE)
760 memcpy (ascii_reserve (a, y, x, x + width, n), string, n);
768 /* First figure out how many bytes need to be inserted. */
770 for (ofs = 0; ofs < n; ofs += mblen)
775 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
776 w = uc_width (uc, "UTF-8");
779 n_out += a->emphasis == EMPH_UNDERLINE ? 2 : 1 + mblen;
782 /* Then insert them. */
783 out = ascii_reserve (a, y, x, x + width, n_out);
784 for (ofs = 0; ofs < n; ofs += mblen)
789 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
790 w = uc_width (uc, "UTF-8");
794 if (a->emphasis == EMPH_UNDERLINE)
797 out = mempcpy (out, string + ofs, mblen);
800 out = mempcpy (out, string + ofs, mblen);
806 ascii_layout_cell_text (struct ascii_driver *a,
807 const struct cell_contents *contents, int *footnote_idx,
808 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
819 length = strlen (contents->text);
820 if (contents->n_footnotes)
826 ds_extend (&s, length + contents->n_footnotes * 4);
827 ds_put_cstr (&s, contents->text);
828 for (i = 0; i < contents->n_footnotes; i++)
832 str_format_26adic (++*footnote_idx, false, marker, sizeof marker);
833 ds_put_format (&s, "[%s]", marker);
836 length = ds_length (&s);
837 text = ds_steal_cstr (&s);
843 text = contents->text;
846 breaks = xmalloc (length + 1);
847 u8_possible_linebreaks (CHAR_CAST (const uint8_t *, text), length,
849 breaks[length] = (breaks[length - 1] == UC_BREAK_MANDATORY
850 ? UC_BREAK_PROHIBITED : UC_BREAK_POSSIBLE);
853 bb_width = bb[H][1] - bb[H][0];
854 for (y = bb[V][0]; y < bb[V][1] && pos < length; y++)
856 const uint8_t *line = CHAR_CAST (const uint8_t *, text + pos);
857 const char *b = breaks + pos;
858 size_t n = length - pos;
860 size_t last_break_ofs = 0;
861 int last_break_width = 0;
866 for (ofs = 0; ofs < n; )
872 mblen = u8_mbtouc (&uc, line + ofs, n - ofs);
873 if (b[ofs] == UC_BREAK_MANDATORY)
875 else if (b[ofs] == UC_BREAK_POSSIBLE)
877 last_break_ofs = ofs;
878 last_break_width = width;
881 w = uc_width (uc, "UTF-8");
884 if (width + w > bb_width)
886 if (isspace (line[ofs]))
888 else if (last_break_ofs != 0)
890 ofs = last_break_ofs;
891 width = last_break_width;
900 /* Trim any trailing spaces off the end of the text to be drawn. */
901 for (graph_ofs = ofs; graph_ofs > 0; graph_ofs--)
902 if (!isspace (line[graph_ofs - 1]))
904 width -= ofs - graph_ofs;
907 text_draw (a, contents->options, bb, clip, y, line, graph_ofs, width);
909 /* If a new-line ended the line, just skip the new-line. Otherwise, skip
910 past any spaces past the end of the line (but not past a new-line). */
911 if (b[ofs] == UC_BREAK_MANDATORY)
914 while (ofs < n && isspace (line[ofs]) && b[ofs] != UC_BREAK_MANDATORY)
923 if (text != contents->text)
924 free (CONST_CAST (char *, text));
930 ascii_layout_subtable (struct ascii_driver *a,
931 const struct cell_contents *contents,
932 int *footnote_idx UNUSED /* XXX */,
933 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2] UNUSED,
936 struct render_params params;
937 struct render_pager *p;
938 int r[TABLE_N_AXES][2];
942 params.draw_line = ascii_draw_line;
943 params.measure_cell_width = ascii_measure_cell_width;
944 params.measure_cell_height = ascii_measure_cell_height;
945 params.adjust_break = NULL;
946 params.draw_cell = ascii_draw_cell,
948 params.size[H] = bb[TABLE_HORZ][1] - bb[TABLE_HORZ][0];
949 params.size[V] = bb[TABLE_VERT][1] - bb[TABLE_VERT][0];
950 params.font_size[H] = 1;
951 params.font_size[V] = 1;
952 for (i = 0; i < RENDER_N_LINES; i++)
954 int width = i == RENDER_LINE_NONE ? 0 : 1;
955 params.line_widths[H][i] = width;
956 params.line_widths[V][i] = width;
959 p = render_pager_create (¶ms, contents->table);
960 width = render_pager_get_size (p, TABLE_HORZ);
961 height = render_pager_get_size (p, TABLE_VERT);
963 /* r = intersect(bb, clip) - bb. */
964 for (i = 0; i < TABLE_N_AXES; i++)
966 r[i][0] = MAX (bb[i][0], clip[i][0]) - bb[i][0];
967 r[i][1] = MIN (bb[i][1], clip[i][1]) - bb[i][0];
970 if (r[H][0] < r[H][1] && r[V][0] < r[V][1])
972 unsigned int alignment = contents->options & TAB_ALIGNMENT;
975 a->x += bb[TABLE_HORZ][0];
976 if (alignment == TAB_RIGHT)
977 a->x += params.size[H] - width;
978 else if (alignment == TAB_CENTER)
979 a->x += (params.size[H] - width) / 2;
980 a->y += bb[TABLE_VERT][0];
981 render_pager_draw (p);
982 a->y -= bb[TABLE_VERT][0];
985 render_pager_destroy (p);
989 return bb[V][0] + height;
993 ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
995 int bb_[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
996 int *widthp, int *heightp)
998 int bb[TABLE_N_AXES][2];
1004 memcpy (bb, bb_, sizeof bb);
1005 for (i = 0; i < cell->n_contents && bb[V][0] < bb[V][1]; i++)
1007 const struct cell_contents *contents = &cell->contents[i];
1009 /* Put a blank line between contents. */
1013 if (bb[V][0] >= bb[V][1])
1018 bb[V][0] = ascii_layout_cell_text (a, contents, &footnote_idx,
1021 bb[V][0] = ascii_layout_subtable (a, contents, &footnote_idx,
1024 *heightp = bb[V][0] - bb_[V][0];
1028 ascii_test_write (struct output_driver *driver,
1029 const char *s, int x, int y, unsigned int options)
1031 struct ascii_driver *a = ascii_driver_cast (driver);
1032 struct cell_contents contents;
1033 struct table_cell cell;
1034 int bb[TABLE_N_AXES][2];
1037 if (a->file == NULL && !ascii_open_page (a))
1041 contents.options = options | TAB_LEFT;
1042 contents.text = CONST_CAST (char *, s);
1043 contents.table = NULL;
1044 contents.n_footnotes = 0;
1046 memset (&cell, 0, sizeof cell);
1047 cell.contents = &contents;
1048 cell.n_contents = 1;
1050 bb[TABLE_HORZ][0] = x;
1051 bb[TABLE_HORZ][1] = a->width;
1052 bb[TABLE_VERT][0] = y;
1053 bb[TABLE_VERT][1] = a->length;
1055 ascii_layout_cell (a, &cell, 0, bb, bb, &width, &height);
1061 ascii_test_set_length (struct output_driver *driver, int y, int length)
1063 struct ascii_driver *a = ascii_driver_cast (driver);
1065 if (a->file == NULL && !ascii_open_page (a))
1068 if (y < 0 || y >= a->length)
1070 u8_line_set_length (&a->lines[y], length);
1073 /* ascii_close_page () and support routines. */
1075 #if HAVE_DECL_SIGWINCH
1076 static struct ascii_driver *the_driver;
1079 winch_handler (int signum UNUSED)
1081 update_page_size (the_driver, false);
1086 ascii_open_page (struct ascii_driver *a)
1093 if (a->file == NULL)
1095 a->file = fn_open (a->handle, a->append ? "a" : "w");
1096 if (a->file != NULL)
1098 if ( isatty (fileno (a->file)))
1100 #if HAVE_DECL_SIGWINCH
1101 struct sigaction action;
1102 sigemptyset (&action.sa_mask);
1103 action.sa_flags = 0;
1104 action.sa_handler = winch_handler;
1106 sigaction (SIGWINCH, &action, NULL);
1108 a->auto_width = true;
1109 a->auto_length = true;
1114 msg_error (errno, _("ascii: opening output file `%s'"),
1115 fh_get_file_name (a->handle));
1123 reallocate_lines (a);
1125 for (i = 0; i < a->length; i++)
1126 u8_line_clear (&a->lines[i]);
1132 output_title_line (FILE *out, int width, const char *left, const char *right)
1134 struct string s = DS_EMPTY_INITIALIZER;
1135 ds_put_byte_multiple (&s, ' ', width);
1138 size_t length = MIN (strlen (left), width);
1139 memcpy (ds_end (&s) - width, left, length);
1143 size_t length = MIN (strlen (right), width);
1144 memcpy (ds_end (&s) - length, right, length);
1146 ds_put_byte (&s, '\n');
1147 fputs (ds_cstr (&s), out);
1152 ascii_close_page (struct ascii_driver *a)
1158 if (a->file == NULL)
1161 if (!a->top_margin && !a->bottom_margin && a->squeeze_blank_lines
1162 && !a->paginate && a->page_number > 1)
1163 putc ('\n', a->file);
1165 for (i = 0; i < a->top_margin; i++)
1166 putc ('\n', a->file);
1171 r1 = xasprintf (_("%s - Page %d"), get_start_date (), a->page_number);
1172 r2 = xasprintf ("%s - %s" , version, host_system);
1174 output_title_line (a->file, a->width, a->title, r1);
1175 output_title_line (a->file, a->width, a->subtitle, r2);
1176 putc ('\n', a->file);
1183 for (y = 0; y < a->allocated_lines; y++)
1185 struct u8_line *line = &a->lines[y];
1187 if (a->squeeze_blank_lines && y > 0 && line->width == 0)
1193 putc ('\n', a->file);
1197 while (ds_chomp_byte (&line->s, ' '))
1199 fwrite (ds_data (&line->s), 1, ds_length (&line->s), a->file);
1200 putc ('\n', a->file);
1203 if (!a->squeeze_blank_lines)
1204 for (y = a->allocated_lines; y < a->length; y++)
1205 putc ('\n', a->file);
1207 for (i = 0; i < a->bottom_margin; i++)
1208 putc ('\n', a->file);
1210 putc ('\f', a->file);