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 };
357 a->width = settings_get_viewwidth ();
359 a->length = settings_get_viewlength () - vertical_margins (a);
361 if (a->width < MIN_WIDTH || a->length < MIN_LENGTH)
365 _("ascii: page excluding margins and headers "
366 "must be at least %d characters wide by %d lines long, but "
367 "as configured is only %d characters by %d lines"),
368 MIN_WIDTH, MIN_LENGTH,
369 a->width, a->length);
370 if (a->width < MIN_WIDTH)
371 a->width = MIN_WIDTH;
372 if (a->length < MIN_LENGTH)
373 a->length = MIN_LENGTH;
377 reallocate_lines (a);
383 ascii_destroy (struct output_driver *driver)
385 struct ascii_driver *a = ascii_driver_cast (driver);
389 ascii_close_page (a);
392 fn_close (a->handle, a->file);
393 fh_unref (a->handle);
394 free (a->command_name);
397 free (a->chart_file_name);
398 for (i = 0; i < a->allocated_lines; i++)
399 u8_line_destroy (&a->lines[i]);
405 ascii_flush (struct output_driver *driver)
407 struct ascii_driver *a = ascii_driver_cast (driver);
410 ascii_close_page (a);
412 if (fn_close (a->handle, a->file) != 0)
413 msg_error (errno, _("ascii: closing output file `%s'"), fh_get_file_name (a->handle));
419 ascii_output_table_item (struct ascii_driver *a,
420 const struct table_item *table_item)
422 struct render_params params;
423 struct render_pager *p;
426 update_page_size (a, false);
428 params.draw_line = ascii_draw_line;
429 params.measure_cell_width = ascii_measure_cell_width;
430 params.measure_cell_height = ascii_measure_cell_height;
431 params.adjust_break = NULL;
432 params.draw_cell = ascii_draw_cell;
434 params.size[H] = a->width;
435 params.size[V] = a->length;
436 params.font_size[H] = 1;
437 params.font_size[V] = 1;
438 for (i = 0; i < RENDER_N_LINES; i++)
440 int width = i == RENDER_LINE_NONE ? 0 : 1;
441 params.line_widths[H][i] = width;
442 params.line_widths[V][i] = width;
444 for (i = 0; i < TABLE_N_AXES; i++)
445 params.min_break[i] = a->min_break[i];
447 if (a->file == NULL && !ascii_open_page (a))
450 p = render_pager_create (¶ms, table_item);
451 while (render_pager_has_next (p))
457 used = render_pager_draw_next (p, a->length - a->y);
461 ascii_close_page (a);
462 if (!ascii_open_page (a))
468 render_pager_destroy (p);
472 ascii_output_text (struct ascii_driver *a, const char *text)
474 struct table_item *table_item;
476 table_item = table_item_create (table_from_string (TAB_LEFT, text),
478 ascii_output_table_item (a, table_item);
479 table_item_unref (table_item);
483 ascii_submit (struct output_driver *driver,
484 const struct output_item *output_item)
486 struct ascii_driver *a = ascii_driver_cast (driver);
488 output_driver_track_current_command (output_item, &a->command_name);
493 if (is_table_item (output_item))
494 ascii_output_table_item (a, to_table_item (output_item));
496 else if (is_chart_item (output_item) && a->chart_file_name != NULL)
498 struct chart_item *chart_item = to_chart_item (output_item);
501 file_name = xr_draw_png_chart (chart_item, a->chart_file_name,
505 if (file_name != NULL)
507 struct text_item *text_item;
509 text_item = text_item_create_format (
510 TEXT_ITEM_PARAGRAPH, _("See %s for a chart."), file_name);
512 ascii_submit (driver, &text_item->output_item);
513 text_item_unref (text_item);
517 #endif /* HAVE_CAIRO */
518 else if (is_text_item (output_item))
520 const struct text_item *text_item = to_text_item (output_item);
521 enum text_item_type type = text_item_get_type (text_item);
522 const char *text = text_item_get_text (text_item);
526 case TEXT_ITEM_TITLE:
528 a->title = xstrdup (text);
531 case TEXT_ITEM_SUBTITLE:
533 a->subtitle = xstrdup (text);
536 case TEXT_ITEM_COMMAND_OPEN:
537 case TEXT_ITEM_COMMAND_CLOSE:
540 case TEXT_ITEM_BLANK_LINE:
545 case TEXT_ITEM_EJECT_PAGE:
547 ascii_close_page (a);
551 ascii_output_text (a, text);
555 else if (is_message_item (output_item))
557 const struct message_item *message_item = to_message_item (output_item);
558 const struct msg *msg = message_item_get_msg (message_item);
559 char *s = msg_to_string (msg, a->command_name);
560 ascii_output_text (a, s);
565 const struct output_driver_factory txt_driver_factory =
566 { "txt", "-", ascii_create };
567 const struct output_driver_factory list_driver_factory =
568 { "list", "-", ascii_create };
570 static const struct output_driver_class ascii_driver_class =
578 static char *ascii_reserve (struct ascii_driver *, int y, int x0, int x1,
580 static void ascii_layout_cell (struct ascii_driver *,
581 const struct table_cell *,
583 int bb[TABLE_N_AXES][2],
584 int clip[TABLE_N_AXES][2],
585 int *width, int *height);
588 ascii_draw_line (void *a_, int bb[TABLE_N_AXES][2],
589 enum render_line_style styles[TABLE_N_AXES][2])
591 struct ascii_driver *a = a_;
598 /* Clip to the page. */
599 x0 = MAX (bb[H][0] + a->x, 0);
600 y0 = MAX (bb[V][0] + a->y, 0);
601 x1 = MIN (bb[H][1] + a->x, a->width);
602 y1 = MIN (bb[V][1] + a->y, a->length);
603 if (x1 <= 0 || y1 <= 0 || x0 >= a->width || y0 >= a->length)
607 uc = a->box[make_box_index (styles[V][0], styles[V][1],
608 styles[H][0], styles[H][1])];
609 mblen = u8_uctomb (CHAR_CAST (uint8_t *, mbchar), uc, 6);
610 for (y = y0; y < y1; y++)
612 char *p = ascii_reserve (a, y, x0, x1, mblen * (x1 - x0));
613 for (x = x0; x < x1; x++)
615 memcpy (p, mbchar, mblen);
622 ascii_measure_cell_width (void *a_, const struct table_cell *cell,
623 int footnote_idx, int *min_width, int *max_width)
625 struct ascii_driver *a = a_;
626 int bb[TABLE_N_AXES][2];
627 int clip[TABLE_N_AXES][2];
634 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
635 ascii_layout_cell (a, cell, footnote_idx, bb, clip, max_width, &h);
637 if (cell->n_contents != 1
638 || cell->contents[0].table
639 || cell->contents[0].n_footnotes
640 || strchr (cell->contents[0].text, ' '))
643 ascii_layout_cell (a, cell, footnote_idx, bb, clip, min_width, &h);
646 *min_width = *max_width;
650 ascii_measure_cell_height (void *a_, const struct table_cell *cell,
651 int footnote_idx, int width)
653 struct ascii_driver *a = a_;
654 int bb[TABLE_N_AXES][2];
655 int clip[TABLE_N_AXES][2];
662 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
663 ascii_layout_cell (a, cell, footnote_idx, bb, clip, &w, &h);
668 ascii_draw_cell (void *a_, const struct table_cell *cell, int footnote_idx,
669 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
671 struct ascii_driver *a = a_;
674 ascii_layout_cell (a, cell, footnote_idx, bb, clip, &w, &h);
678 ascii_reserve (struct ascii_driver *a, int y, int x0, int x1, int n)
680 assert (y < a->allocated_lines);
681 return u8_line_reserve (&a->lines[y], x0, x1, n);
685 text_draw (struct ascii_driver *a, unsigned int options,
686 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
687 int y, const uint8_t *string, int n, size_t width)
689 int x0 = MAX (0, clip[H][0] + a->x);
690 int y0 = MAX (0, clip[V][0] + a->y);
691 int x1 = MIN (a->width, clip[H][1] + a->x);
692 int y1 = MIN (a->length, clip[V][1] + a->y);
696 if (y < y0 || y >= y1)
699 switch (options & TAB_ALIGNMENT)
705 x = (bb[H][0] + bb[H][1] - width + 1) / 2;
708 x = bb[H][1] - width;
725 mblen = u8_mbtouc (&uc, string, n);
730 w = uc_width (uc, "UTF-8");
745 for (ofs = 0; ofs < n; )
751 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
753 w = uc_width (uc, "UTF-8");
756 if (width + w > x1 - x)
767 if (!(options & TAB_EMPH) || a->emphasis == EMPH_NONE)
768 memcpy (ascii_reserve (a, y, x, x + width, n), string, n);
776 /* First figure out how many bytes need to be inserted. */
778 for (ofs = 0; ofs < n; ofs += mblen)
783 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
784 w = uc_width (uc, "UTF-8");
787 n_out += a->emphasis == EMPH_UNDERLINE ? 2 : 1 + mblen;
790 /* Then insert them. */
791 out = ascii_reserve (a, y, x, x + width, n_out);
792 for (ofs = 0; ofs < n; ofs += mblen)
797 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
798 w = uc_width (uc, "UTF-8");
802 if (a->emphasis == EMPH_UNDERLINE)
805 out = mempcpy (out, string + ofs, mblen);
808 out = mempcpy (out, string + ofs, mblen);
814 ascii_layout_cell_text (struct ascii_driver *a,
815 const struct cell_contents *contents, int *footnote_idx,
816 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
827 length = strlen (contents->text);
828 if (contents->n_footnotes)
834 ds_extend (&s, length + contents->n_footnotes * 4);
835 ds_put_cstr (&s, contents->text);
836 for (i = 0; i < contents->n_footnotes; i++)
840 str_format_26adic (++*footnote_idx, false, marker, sizeof marker);
841 ds_put_format (&s, "[%s]", marker);
844 length = ds_length (&s);
845 text = ds_steal_cstr (&s);
851 text = contents->text;
854 breaks = xmalloc (length + 1);
855 u8_possible_linebreaks (CHAR_CAST (const uint8_t *, text), length,
857 breaks[length] = (breaks[length - 1] == UC_BREAK_MANDATORY
858 ? UC_BREAK_PROHIBITED : UC_BREAK_POSSIBLE);
861 bb_width = bb[H][1] - bb[H][0];
862 for (y = bb[V][0]; y < bb[V][1] && pos < length; y++)
864 const uint8_t *line = CHAR_CAST (const uint8_t *, text + pos);
865 const char *b = breaks + pos;
866 size_t n = length - pos;
868 size_t last_break_ofs = 0;
869 int last_break_width = 0;
874 for (ofs = 0; ofs < n; )
880 mblen = u8_mbtouc (&uc, line + ofs, n - ofs);
881 if (b[ofs] == UC_BREAK_MANDATORY)
883 else if (b[ofs] == UC_BREAK_POSSIBLE)
885 last_break_ofs = ofs;
886 last_break_width = width;
889 w = uc_width (uc, "UTF-8");
892 if (width + w > bb_width)
894 if (isspace (line[ofs]))
896 else if (last_break_ofs != 0)
898 ofs = last_break_ofs;
899 width = last_break_width;
908 /* Trim any trailing spaces off the end of the text to be drawn. */
909 for (graph_ofs = ofs; graph_ofs > 0; graph_ofs--)
910 if (!isspace (line[graph_ofs - 1]))
912 width -= ofs - graph_ofs;
915 text_draw (a, contents->options, bb, clip, y, line, graph_ofs, width);
917 /* If a new-line ended the line, just skip the new-line. Otherwise, skip
918 past any spaces past the end of the line (but not past a new-line). */
919 if (b[ofs] == UC_BREAK_MANDATORY)
922 while (ofs < n && isspace (line[ofs]) && b[ofs] != UC_BREAK_MANDATORY)
931 if (text != contents->text)
932 free (CONST_CAST (char *, text));
938 ascii_layout_subtable (struct ascii_driver *a,
939 const struct cell_contents *contents,
940 int *footnote_idx UNUSED /* XXX */,
941 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2] UNUSED,
944 struct render_params params;
945 struct render_pager *p;
946 int r[TABLE_N_AXES][2];
950 params.draw_line = ascii_draw_line;
951 params.measure_cell_width = ascii_measure_cell_width;
952 params.measure_cell_height = ascii_measure_cell_height;
953 params.adjust_break = NULL;
954 params.draw_cell = ascii_draw_cell,
956 params.size[H] = bb[TABLE_HORZ][1] - bb[TABLE_HORZ][0];
957 params.size[V] = bb[TABLE_VERT][1] - bb[TABLE_VERT][0];
958 params.font_size[H] = 1;
959 params.font_size[V] = 1;
960 for (i = 0; i < RENDER_N_LINES; i++)
962 int width = i == RENDER_LINE_NONE ? 0 : 1;
963 params.line_widths[H][i] = width;
964 params.line_widths[V][i] = width;
967 p = render_pager_create (¶ms, contents->table);
968 width = render_pager_get_size (p, TABLE_HORZ);
969 height = render_pager_get_size (p, TABLE_VERT);
971 /* r = intersect(bb, clip) - bb. */
972 for (i = 0; i < TABLE_N_AXES; i++)
974 r[i][0] = MAX (bb[i][0], clip[i][0]) - bb[i][0];
975 r[i][1] = MIN (bb[i][1], clip[i][1]) - bb[i][0];
978 if (r[H][0] < r[H][1] && r[V][0] < r[V][1])
980 unsigned int alignment = contents->options & TAB_ALIGNMENT;
983 a->x += bb[TABLE_HORZ][0];
984 if (alignment == TAB_RIGHT)
985 a->x += params.size[H] - width;
986 else if (alignment == TAB_CENTER)
987 a->x += (params.size[H] - width) / 2;
988 a->y += bb[TABLE_VERT][0];
989 render_pager_draw (p);
990 a->y -= bb[TABLE_VERT][0];
993 render_pager_destroy (p);
997 return bb[V][0] + height;
1001 ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
1003 int bb_[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1004 int *widthp, int *heightp)
1006 int bb[TABLE_N_AXES][2];
1012 memcpy (bb, bb_, sizeof bb);
1013 for (i = 0; i < cell->n_contents && bb[V][0] < bb[V][1]; i++)
1015 const struct cell_contents *contents = &cell->contents[i];
1017 /* Put a blank line between contents. */
1021 if (bb[V][0] >= bb[V][1])
1026 bb[V][0] = ascii_layout_cell_text (a, contents, &footnote_idx,
1029 bb[V][0] = ascii_layout_subtable (a, contents, &footnote_idx,
1032 *heightp = bb[V][0] - bb_[V][0];
1036 ascii_test_write (struct output_driver *driver,
1037 const char *s, int x, int y, unsigned int options)
1039 struct ascii_driver *a = ascii_driver_cast (driver);
1040 struct cell_contents contents;
1041 struct table_cell cell;
1042 int bb[TABLE_N_AXES][2];
1045 if (a->file == NULL && !ascii_open_page (a))
1049 contents.options = options | TAB_LEFT;
1050 contents.text = CONST_CAST (char *, s);
1051 contents.table = NULL;
1052 contents.n_footnotes = 0;
1054 memset (&cell, 0, sizeof cell);
1055 cell.contents = &contents;
1056 cell.n_contents = 1;
1058 bb[TABLE_HORZ][0] = x;
1059 bb[TABLE_HORZ][1] = a->width;
1060 bb[TABLE_VERT][0] = y;
1061 bb[TABLE_VERT][1] = a->length;
1063 ascii_layout_cell (a, &cell, 0, bb, bb, &width, &height);
1069 ascii_test_set_length (struct output_driver *driver, int y, int length)
1071 struct ascii_driver *a = ascii_driver_cast (driver);
1073 if (a->file == NULL && !ascii_open_page (a))
1076 if (y < 0 || y >= a->length)
1078 u8_line_set_length (&a->lines[y], length);
1081 /* ascii_close_page () and support routines. */
1083 #if HAVE_DECL_SIGWINCH
1084 static struct ascii_driver *the_driver;
1087 winch_handler (int signum UNUSED)
1089 update_page_size (the_driver, false);
1094 ascii_open_page (struct ascii_driver *a)
1101 if (a->file == NULL)
1103 a->file = fn_open (a->handle, a->append ? "a" : "w");
1104 if (a->file != NULL)
1106 if ( isatty (fileno (a->file)))
1108 #if HAVE_DECL_SIGWINCH
1109 struct sigaction action;
1110 sigemptyset (&action.sa_mask);
1111 action.sa_flags = 0;
1112 action.sa_handler = winch_handler;
1114 sigaction (SIGWINCH, &action, NULL);
1116 a->auto_width = true;
1117 a->auto_length = true;
1122 msg_error (errno, _("ascii: opening output file `%s'"),
1123 fh_get_file_name (a->handle));
1131 reallocate_lines (a);
1133 for (i = 0; i < a->length; i++)
1134 u8_line_clear (&a->lines[i]);
1140 output_title_line (FILE *out, int width, const char *left, const char *right)
1142 struct string s = DS_EMPTY_INITIALIZER;
1143 ds_put_byte_multiple (&s, ' ', width);
1146 size_t length = MIN (strlen (left), width);
1147 memcpy (ds_end (&s) - width, left, length);
1151 size_t length = MIN (strlen (right), width);
1152 memcpy (ds_end (&s) - length, right, length);
1154 ds_put_byte (&s, '\n');
1155 fputs (ds_cstr (&s), out);
1160 ascii_close_page (struct ascii_driver *a)
1166 if (a->file == NULL)
1169 if (!a->top_margin && !a->bottom_margin && a->squeeze_blank_lines
1170 && !a->paginate && a->page_number > 1)
1171 putc ('\n', a->file);
1173 for (i = 0; i < a->top_margin; i++)
1174 putc ('\n', a->file);
1179 r1 = xasprintf (_("%s - Page %d"), get_start_date (), a->page_number);
1180 r2 = xasprintf ("%s - %s" , version, host_system);
1182 output_title_line (a->file, a->width, a->title, r1);
1183 output_title_line (a->file, a->width, a->subtitle, r2);
1184 putc ('\n', a->file);
1191 for (y = 0; y < a->allocated_lines; y++)
1193 struct u8_line *line = &a->lines[y];
1195 if (a->squeeze_blank_lines && y > 0 && line->width == 0)
1201 putc ('\n', a->file);
1205 while (ds_chomp_byte (&line->s, ' '))
1207 fwrite (ds_data (&line->s), 1, ds_length (&line->s), a->file);
1208 putc ('\n', a->file);
1211 if (!a->squeeze_blank_lines)
1212 for (y = a->allocated_lines; y < a->length; y++)
1213 putc ('\n', a->file);
1215 for (i = 0; i < a->bottom_margin; i++)
1216 putc ('\n', a->file);
1218 putc ('\f', a->file);