1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2007, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
30 #include "data/file-name.h"
31 #include "data/settings.h"
32 #include "libpspp/assertion.h"
33 #include "libpspp/cast.h"
34 #include "libpspp/compiler.h"
35 #include "libpspp/message.h"
36 #include "libpspp/start-date.h"
37 #include "libpspp/string-map.h"
38 #include "libpspp/u8-line.h"
39 #include "libpspp/version.h"
40 #include "output/ascii.h"
41 #include "output/cairo.h"
42 #include "output/chart-item-provider.h"
43 #include "output/driver-provider.h"
44 #include "output/message-item.h"
45 #include "output/options.h"
46 #include "output/render.h"
47 #include "output/tab.h"
48 #include "output/table-item.h"
49 #include "output/text-item.h"
51 #include "gl/minmax.h"
52 #include "gl/xalloc.h"
55 #define _(msgid) gettext (msgid)
57 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
61 #define N_BOX (RENDER_N_LINES * RENDER_N_LINES \
62 * RENDER_N_LINES * RENDER_N_LINES)
64 static const ucs4_t ascii_box_chars[N_BOX] =
95 static const ucs4_t unicode_box_chars[N_BOX] =
97 0x0020, 0x2575, 0x2551,
98 0x2574, 0x256f, 0x255c,
99 0x2550, 0x255b, 0x255d,
100 0x2577, 0x2502, 0x2551,
101 0x256e, 0x2524, 0x2562,
102 0x2555, 0x2561, 0x2563,
103 0x2551, 0x2551, 0x2551,
104 0x2556, 0x2562, 0x2562,
105 0x2557, 0x2563, 0x2563,
106 0x2576, 0x2570, 0x2559,
107 0x2500, 0x2534, 0x2568,
108 0x2550, 0x2567, 0x2569,
109 0x256d, 0x251c, 0x255f,
110 0x252c, 0x253c, 0x256a,
111 0x2564, 0x256a, 0x256c,
112 0x2553, 0x255f, 0x255f,
113 0x2565, 0x256b, 0x256b,
114 0x2566, 0x256c, 0x256c,
115 0x2550, 0x2558, 0x255a,
116 0x2550, 0x2567, 0x2569,
117 0x2550, 0x2567, 0x2569,
118 0x2552, 0x255e, 0x2560,
119 0x2564, 0x256a, 0x256c,
120 0x2564, 0x256a, 0x256c,
121 0x2554, 0x2560, 0x2560,
122 0x2560, 0x256c, 0x256c,
123 0x2566, 0x256c, 0x256c,
127 make_box_index (int left, int right, int top, int bottom)
129 return ((right * RENDER_N_LINES + bottom) * RENDER_N_LINES + left) * RENDER_N_LINES + top;
132 /* How to emphasize text. */
135 EMPH_BOLD, /* Overstrike for bold. */
136 EMPH_UNDERLINE, /* Overstrike for underlining. */
137 EMPH_NONE /* No emphasis. */
140 /* ASCII output driver. */
143 struct output_driver driver;
145 /* User parameters. */
146 bool append; /* Append if output file already exists? */
147 bool headers; /* Print headers at top of page? */
148 bool paginate; /* Insert formfeeds? */
149 bool squeeze_blank_lines; /* Squeeze multiple blank lines into one? */
150 enum emphasis_style emphasis; /* How to emphasize text. */
151 char *chart_file_name; /* Name of files used for charts. */
154 /* Colours for charts */
159 int width; /* Page width. */
160 int length; /* Page length minus margins and header. */
161 bool auto_width; /* Use viewwidth as page width? */
162 bool auto_length; /* Use viewlength as page width? */
164 int top_margin; /* Top margin in lines. */
165 int bottom_margin; /* Bottom margin in lines. */
167 int min_break[TABLE_N_AXES]; /* Min cell size to break across pages. */
169 const ucs4_t *box; /* Line & box drawing characters. */
171 /* Internal state. */
175 char *file_name; /* Output file name. */
176 FILE *file; /* Output file. */
177 bool error; /* Output error? */
178 int page_number; /* Current page number. */
179 struct u8_line *lines; /* Page content. */
180 int allocated_lines; /* Number of lines allocated. */
181 int chart_cnt; /* Number of charts so far. */
185 static const struct output_driver_class ascii_driver_class;
187 static void ascii_submit (struct output_driver *, const struct output_item *);
189 static int vertical_margins (const struct ascii_driver *);
191 static bool update_page_size (struct ascii_driver *, bool issue_error);
192 static int parse_page_size (struct driver_option *);
194 static void ascii_close_page (struct ascii_driver *);
195 static bool ascii_open_page (struct ascii_driver *);
197 static void ascii_draw_line (void *, int bb[TABLE_N_AXES][2],
198 enum render_line_style styles[TABLE_N_AXES][2]);
199 static void ascii_measure_cell_width (void *, const struct table_cell *,
201 static int ascii_measure_cell_height (void *, const struct table_cell *,
203 static void ascii_draw_cell (void *, const struct table_cell *,
204 int bb[TABLE_N_AXES][2],
205 int clip[TABLE_N_AXES][2]);
208 reallocate_lines (struct ascii_driver *a)
210 if (a->length > a->allocated_lines)
213 a->lines = xnrealloc (a->lines, a->length, sizeof *a->lines);
214 for (i = a->allocated_lines; i < a->length; i++)
215 u8_line_init (&a->lines[i]);
216 a->allocated_lines = a->length;
221 static struct ascii_driver *
222 ascii_driver_cast (struct output_driver *driver)
224 assert (driver->class == &ascii_driver_class);
225 return UP_CAST (driver, struct ascii_driver, driver);
228 static struct driver_option *
229 opt (struct output_driver *d, struct string_map *options, const char *key,
230 const char *default_value)
232 return driver_option_get (d, options, key, default_value);
235 static struct output_driver *
236 ascii_create (const char *file_name, enum settings_output_devices device_type,
237 struct string_map *o)
239 enum { BOX_ASCII, BOX_UNICODE } box;
240 int min_break[TABLE_N_AXES];
241 struct output_driver *d;
242 struct ascii_driver *a;
245 a = xzalloc (sizeof *a);
247 output_driver_init (&a->driver, &ascii_driver_class, file_name, device_type);
248 a->append = parse_boolean (opt (d, o, "append", "false"));
249 a->headers = parse_boolean (opt (d, o, "headers", "false"));
250 a->paginate = parse_boolean (opt (d, o, "paginate", "false"));
251 a->squeeze_blank_lines = parse_boolean (opt (d, o, "squeeze", "true"));
252 a->emphasis = parse_enum (opt (d, o, "emphasis", "none"),
254 "underline", EMPH_UNDERLINE,
258 a->chart_file_name = parse_chart_file_name (opt (d, o, "charts", file_name));
260 a->top_margin = parse_int (opt (d, o, "top-margin", "0"), 0, INT_MAX);
261 a->bottom_margin = parse_int (opt (d, o, "bottom-margin", "0"), 0, INT_MAX);
263 min_break[H] = parse_int (opt (d, o, "min-hbreak", "-1"), -1, INT_MAX);
264 min_break[V] = parse_int (opt (d, o, "min-vbreak", "-1"), -1, INT_MAX);
266 a->width = parse_page_size (opt (d, o, "width", "79"));
267 paper_length = parse_page_size (opt (d, o, "length", "66"));
268 a->auto_width = a->width < 0;
269 a->auto_length = paper_length < 0;
270 a->length = paper_length - vertical_margins (a);
271 a->min_break[H] = min_break[H] >= 0 ? min_break[H] : a->width / 2;
272 a->min_break[V] = min_break[V] >= 0 ? min_break[V] : a->length / 2;
274 parse_color (d, o, "background-color", "#FFFFFFFFFFFF", &a->bg);
275 parse_color (d, o, "foreground-color", "#000000000000", &a->fg);
277 box = parse_enum (opt (d, o, "box", "ascii"),
279 "unicode", BOX_UNICODE,
281 a->box = box == BOX_ASCII ? ascii_box_chars : unicode_box_chars;
283 a->command_name = NULL;
284 a->title = xstrdup ("");
285 a->subtitle = xstrdup ("");
286 a->file_name = xstrdup (file_name);
291 a->allocated_lines = 0;
294 if (!update_page_size (a, true))
300 output_driver_destroy (d);
305 parse_page_size (struct driver_option *option)
307 int dim = atol (option->default_value);
309 if (option->value != NULL)
311 if (!strcmp (option->value, "auto"))
319 value = strtol (option->value, &tail, 0);
320 if (dim >= 1 && errno != ERANGE && *tail == '\0')
323 msg (MW, _("%s: %s must be positive integer or `auto'"),
324 option->driver_name, option->name);
328 driver_option_destroy (option);
334 vertical_margins (const struct ascii_driver *a)
336 return a->top_margin + a->bottom_margin + (a->headers ? 3 : 0);
339 /* Re-calculates the page width and length based on settings,
340 margins, and, if "auto" is set, the size of the user's
341 terminal window or GUI output window. */
343 update_page_size (struct ascii_driver *a, bool issue_error)
345 enum { MIN_WIDTH = 6, MIN_LENGTH = 6 };
348 a->width = settings_get_viewwidth ();
350 a->length = settings_get_viewlength () - vertical_margins (a);
352 if (a->width < MIN_WIDTH || a->length < MIN_LENGTH)
356 _("ascii: page excluding margins and headers "
357 "must be at least %d characters wide by %d lines long, but "
358 "as configured is only %d characters by %d lines"),
359 MIN_WIDTH, MIN_LENGTH,
360 a->width, a->length);
361 if (a->width < MIN_WIDTH)
362 a->width = MIN_WIDTH;
363 if (a->length < MIN_LENGTH)
364 a->length = MIN_LENGTH;
368 reallocate_lines (a);
374 ascii_destroy (struct output_driver *driver)
376 struct ascii_driver *a = ascii_driver_cast (driver);
380 ascii_close_page (a);
383 fn_close (a->file_name, a->file);
384 free (a->command_name);
388 free (a->chart_file_name);
389 for (i = 0; i < a->allocated_lines; i++)
390 u8_line_destroy (&a->lines[i]);
396 ascii_flush (struct output_driver *driver)
398 struct ascii_driver *a = ascii_driver_cast (driver);
401 ascii_close_page (a);
403 if (fn_close (a->file_name, a->file) != 0)
404 msg_error (errno, _("ascii: closing output file `%s'"), a->file_name);
410 ascii_init_caption_cell (const char *caption, struct table_cell *cell)
412 cell->inline_contents.options = TAB_LEFT;
413 cell->inline_contents.text = CONST_CAST (char *, caption);
414 cell->inline_contents.table = NULL;
415 cell->contents = &cell->inline_contents;
416 cell->n_contents = 1;
417 cell->destructor = NULL;
421 ascii_output_table_item (struct ascii_driver *a,
422 const struct table_item *table_item)
424 const char *caption = table_item_get_caption (table_item);
425 struct render_params params;
426 struct render_page *page;
427 struct render_break x_break;
431 update_page_size (a, false);
435 /* XXX doesn't do well with very large captions */
436 struct table_cell cell;
437 ascii_init_caption_cell (caption, &cell);
438 caption_height = ascii_measure_cell_height (a, &cell, a->width);
443 params.draw_line = ascii_draw_line;
444 params.measure_cell_width = ascii_measure_cell_width;
445 params.measure_cell_height = ascii_measure_cell_height;
446 params.adjust_break = NULL;
447 params.draw_cell = ascii_draw_cell;
449 params.size[H] = a->width;
450 params.size[V] = a->length - caption_height;
451 params.font_size[H] = 1;
452 params.font_size[V] = 1;
453 for (i = 0; i < RENDER_N_LINES; i++)
455 int width = i == RENDER_LINE_NONE ? 0 : 1;
456 params.line_widths[H][i] = width;
457 params.line_widths[V][i] = width;
459 for (i = 0; i < TABLE_N_AXES; i++)
460 params.min_break[i] = a->min_break[i];
462 if (a->file == NULL && !ascii_open_page (a))
465 page = render_page_create (¶ms, table_item_get_table (table_item));
466 for (render_break_init (&x_break, page, H);
467 render_break_has_next (&x_break); )
469 struct render_page *x_slice;
470 struct render_break y_break;
472 x_slice = render_break_next (&x_break, a->width);
473 for (render_break_init (&y_break, x_slice, V);
474 render_break_has_next (&y_break); )
476 struct render_page *y_slice;
482 space = a->length - a->y - caption_height;
483 if (render_break_next_size (&y_break) > space)
486 ascii_close_page (a);
487 if (!ascii_open_page (a))
492 y_slice = render_break_next (&y_break, space);
495 struct table_cell cell;
496 int bb[TABLE_N_AXES][2];
498 ascii_init_caption_cell (caption, &cell);
502 bb[V][1] = caption_height;
503 ascii_draw_cell (a, &cell, bb, bb);
504 a->y += caption_height;
507 render_page_draw (y_slice);
508 a->y += render_page_get_size (y_slice, V);
509 render_page_unref (y_slice);
511 render_break_destroy (&y_break);
513 render_break_destroy (&x_break);
517 ascii_output_text (struct ascii_driver *a, const char *text)
519 struct table_item *table_item;
521 table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
522 ascii_output_table_item (a, table_item);
523 table_item_unref (table_item);
527 ascii_submit (struct output_driver *driver,
528 const struct output_item *output_item)
530 struct ascii_driver *a = ascii_driver_cast (driver);
532 output_driver_track_current_command (output_item, &a->command_name);
537 if (is_table_item (output_item))
538 ascii_output_table_item (a, to_table_item (output_item));
540 else if (is_chart_item (output_item) && a->chart_file_name != NULL)
542 struct chart_item *chart_item = to_chart_item (output_item);
545 file_name = xr_draw_png_chart (chart_item, a->chart_file_name,
549 if (file_name != NULL)
551 struct text_item *text_item;
553 text_item = text_item_create_format (
554 TEXT_ITEM_PARAGRAPH, _("See %s for a chart."), file_name);
556 ascii_submit (driver, &text_item->output_item);
557 text_item_unref (text_item);
561 #endif /* HAVE_CAIRO */
562 else if (is_text_item (output_item))
564 const struct text_item *text_item = to_text_item (output_item);
565 enum text_item_type type = text_item_get_type (text_item);
566 const char *text = text_item_get_text (text_item);
570 case TEXT_ITEM_TITLE:
572 a->title = xstrdup (text);
575 case TEXT_ITEM_SUBTITLE:
577 a->subtitle = xstrdup (text);
580 case TEXT_ITEM_COMMAND_OPEN:
581 case TEXT_ITEM_COMMAND_CLOSE:
584 case TEXT_ITEM_BLANK_LINE:
589 case TEXT_ITEM_EJECT_PAGE:
591 ascii_close_page (a);
595 ascii_output_text (a, text);
599 else if (is_message_item (output_item))
601 const struct message_item *message_item = to_message_item (output_item);
602 const struct msg *msg = message_item_get_msg (message_item);
603 char *s = msg_to_string (msg, a->command_name);
604 ascii_output_text (a, s);
609 const struct output_driver_factory txt_driver_factory =
610 { "txt", "-", ascii_create };
611 const struct output_driver_factory list_driver_factory =
612 { "list", "-", ascii_create };
614 static const struct output_driver_class ascii_driver_class =
622 static char *ascii_reserve (struct ascii_driver *, int y, int x0, int x1,
624 static void ascii_layout_cell (struct ascii_driver *,
625 const struct table_cell *,
626 int bb[TABLE_N_AXES][2],
627 int clip[TABLE_N_AXES][2],
628 int *width, int *height);
631 ascii_draw_line (void *a_, int bb[TABLE_N_AXES][2],
632 enum render_line_style styles[TABLE_N_AXES][2])
634 struct ascii_driver *a = a_;
641 /* Clip to the page. */
642 x0 = MAX (bb[H][0] + a->x, 0);
643 y0 = MAX (bb[V][0] + a->y, 0);
644 x1 = MIN (bb[H][1] + a->x, a->width);
645 y1 = MIN (bb[V][1] + a->y, a->length);
646 if (x1 <= 0 || y1 <= 0 || x0 >= a->width || y0 >= a->length)
650 uc = a->box[make_box_index (styles[V][0], styles[V][1],
651 styles[H][0], styles[H][1])];
652 mblen = u8_uctomb (CHAR_CAST (uint8_t *, mbchar), uc, 6);
653 for (y = y0; y < y1; y++)
655 char *p = ascii_reserve (a, y, x0, x1, mblen * (x1 - x0));
656 for (x = x0; x < x1; x++)
658 memcpy (p, mbchar, mblen);
665 ascii_measure_cell_width (void *a_, const struct table_cell *cell,
666 int *min_width, int *max_width)
668 struct ascii_driver *a = a_;
669 int bb[TABLE_N_AXES][2];
670 int clip[TABLE_N_AXES][2];
677 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
678 ascii_layout_cell (a, cell, bb, clip, max_width, &h);
680 if (cell->n_contents != 1
681 || cell->contents[0].table
682 || strchr (cell->contents[0].text, ' '))
685 ascii_layout_cell (a, cell, bb, clip, min_width, &h);
688 *min_width = *max_width;
692 ascii_measure_cell_height (void *a_, const struct table_cell *cell, int width)
694 struct ascii_driver *a = a_;
695 int bb[TABLE_N_AXES][2];
696 int clip[TABLE_N_AXES][2];
703 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
704 ascii_layout_cell (a, cell, bb, clip, &w, &h);
709 ascii_draw_cell (void *a_, const struct table_cell *cell,
710 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
712 struct ascii_driver *a = a_;
715 ascii_layout_cell (a, cell, bb, clip, &w, &h);
719 ascii_reserve (struct ascii_driver *a, int y, int x0, int x1, int n)
721 assert (y < a->allocated_lines);
722 return u8_line_reserve (&a->lines[y], x0, x1, n);
726 text_draw (struct ascii_driver *a, unsigned int options,
727 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
728 int y, const uint8_t *string, int n, size_t width)
730 int x0 = MAX (0, clip[H][0] + a->x);
731 int y0 = MAX (0, clip[V][0] + a->y);
732 int x1 = MIN (a->width, clip[H][1] + a->x);
733 int y1 = MIN (a->length, clip[V][1] + a->y);
737 if (y < y0 || y >= y1)
740 switch (options & TAB_ALIGNMENT)
746 x = (bb[H][0] + bb[H][1] - width + 1) / 2;
749 x = bb[H][1] - width;
766 mblen = u8_mbtouc (&uc, string, n);
771 w = uc_width (uc, "UTF-8");
786 for (ofs = 0; ofs < n; )
792 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
794 w = uc_width (uc, "UTF-8");
797 if (width + w > x1 - x)
808 if (!(options & TAB_EMPH) || a->emphasis == EMPH_NONE)
809 memcpy (ascii_reserve (a, y, x, x + width, n), string, n);
817 /* First figure out how many bytes need to be inserted. */
819 for (ofs = 0; ofs < n; ofs += mblen)
824 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
825 w = uc_width (uc, "UTF-8");
828 n_out += a->emphasis == EMPH_UNDERLINE ? 2 : 1 + mblen;
831 /* Then insert them. */
832 out = ascii_reserve (a, y, x, x + width, n_out);
833 for (ofs = 0; ofs < n; ofs += mblen)
838 mblen = u8_mbtouc (&uc, string + ofs, n - ofs);
839 w = uc_width (uc, "UTF-8");
843 if (a->emphasis == EMPH_UNDERLINE)
846 out = mempcpy (out, string + ofs, mblen);
849 out = mempcpy (out, string + ofs, mblen);
855 ascii_layout_cell_text (struct ascii_driver *a,
856 const struct cell_contents *contents,
857 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
860 size_t length = strlen (contents->text);
870 breaks = xmalloc (length + 1);
871 u8_possible_linebreaks (CHAR_CAST (const uint8_t *, contents->text), length,
873 breaks[length] = (breaks[length - 1] == UC_BREAK_MANDATORY
874 ? UC_BREAK_PROHIBITED : UC_BREAK_POSSIBLE);
877 bb_width = bb[H][1] - bb[H][0];
878 for (y = bb[V][0]; y < bb[V][1] && pos < length; y++)
880 const uint8_t *line = CHAR_CAST (const uint8_t *, contents->text + pos);
881 const char *b = breaks + pos;
882 size_t n = length - pos;
884 size_t last_break_ofs = 0;
885 int last_break_width = 0;
890 for (ofs = 0; ofs < n; )
896 mblen = u8_mbtouc (&uc, line + ofs, n - ofs);
897 if (b[ofs] == UC_BREAK_MANDATORY)
899 else if (b[ofs] == UC_BREAK_POSSIBLE)
901 last_break_ofs = ofs;
902 last_break_width = width;
905 w = uc_width (uc, "UTF-8");
908 if (width + w > bb_width)
910 if (isspace (line[ofs]))
912 else if (last_break_ofs != 0)
914 ofs = last_break_ofs;
915 width = last_break_width;
924 /* Trim any trailing spaces off the end of the text to be drawn. */
925 for (graph_ofs = ofs; graph_ofs > 0; graph_ofs--)
926 if (!isspace (line[graph_ofs - 1]))
928 width -= ofs - graph_ofs;
931 text_draw (a, contents->options, bb, clip, y, line, graph_ofs, width);
933 /* If a new-line ended the line, just skip the new-line. Otherwise, skip
934 past any spaces past the end of the line (but not past a new-line). */
935 if (b[ofs] == UC_BREAK_MANDATORY)
938 while (ofs < n && isspace (line[ofs]) && b[ofs] != UC_BREAK_MANDATORY)
952 ascii_layout_subtable (struct ascii_driver *a,
953 const struct cell_contents *contents,
954 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2] UNUSED,
957 const struct table *table = contents->table;
958 struct render_params params;
959 struct render_page *page;
960 int r[TABLE_N_AXES][2];
964 params.draw_line = ascii_draw_line;
965 params.measure_cell_width = ascii_measure_cell_width;
966 params.measure_cell_height = ascii_measure_cell_height;
967 params.adjust_break = NULL;
968 params.draw_cell = ascii_draw_cell,
970 params.size[H] = bb[TABLE_HORZ][1] - bb[TABLE_HORZ][0];
971 params.size[V] = bb[TABLE_VERT][1] - bb[TABLE_VERT][0];
972 params.font_size[H] = 1;
973 params.font_size[V] = 1;
974 for (i = 0; i < RENDER_N_LINES; i++)
976 int width = i == RENDER_LINE_NONE ? 0 : 1;
977 params.line_widths[H][i] = width;
978 params.line_widths[V][i] = width;
981 page = render_page_create (¶ms, table);
982 width = render_page_get_size (page, TABLE_HORZ);
983 height = render_page_get_size (page, TABLE_VERT);
985 /* r = intersect(bb, clip) - bb. */
986 for (i = 0; i < TABLE_N_AXES; i++)
988 r[i][0] = MAX (bb[i][0], clip[i][0]) - bb[i][0];
989 r[i][1] = MIN (bb[i][1], clip[i][1]) - bb[i][0];
992 if (r[H][0] < r[H][1] && r[V][0] < r[V][1])
994 unsigned int alignment = contents->options & TAB_ALIGNMENT;
997 a->x += bb[TABLE_HORZ][0];
998 if (alignment == TAB_RIGHT)
999 a->x += params.size[H] - width;
1000 else if (alignment == TAB_CENTER)
1001 a->x += (params.size[H] - width) / 2;
1002 a->y += bb[TABLE_VERT][0];
1003 render_page_draw (page);
1004 a->y -= bb[TABLE_VERT][0];
1007 render_page_unref (page);
1009 if (width > *widthp)
1011 return bb[V][0] + height;
1015 ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
1016 int bb_[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1017 int *widthp, int *heightp)
1019 int bb[TABLE_N_AXES][2];
1025 memcpy (bb, bb_, sizeof bb);
1026 for (i = 0; i < cell->n_contents && bb[V][0] < bb[V][1]; i++)
1028 const struct cell_contents *contents = &cell->contents[i];
1030 /* Put a blank line between contents. */
1034 if (bb[V][0] >= bb[V][1])
1039 bb[V][0] = ascii_layout_cell_text (a, contents, bb, clip, widthp);
1041 bb[V][0] = ascii_layout_subtable (a, contents, bb, clip, widthp);
1043 *heightp = bb[V][0] - bb_[V][0];
1047 ascii_test_write (struct output_driver *driver,
1048 const char *s, int x, int y, unsigned int options)
1050 struct ascii_driver *a = ascii_driver_cast (driver);
1051 struct cell_contents contents;
1052 struct table_cell cell;
1053 int bb[TABLE_N_AXES][2];
1056 if (a->file == NULL && !ascii_open_page (a))
1060 contents.options = options | TAB_LEFT;
1061 contents.text = CONST_CAST (char *, s);
1062 contents.table = NULL;
1064 memset (&cell, 0, sizeof cell);
1065 cell.contents = &contents;
1066 cell.n_contents = 1;
1068 bb[TABLE_HORZ][0] = x;
1069 bb[TABLE_HORZ][1] = a->width;
1070 bb[TABLE_VERT][0] = y;
1071 bb[TABLE_VERT][1] = a->length;
1073 ascii_layout_cell (a, &cell, bb, bb, &width, &height);
1079 ascii_test_set_length (struct output_driver *driver, int y, int length)
1081 struct ascii_driver *a = ascii_driver_cast (driver);
1083 if (a->file == NULL && !ascii_open_page (a))
1086 if (y < 0 || y >= a->length)
1088 u8_line_set_length (&a->lines[y], length);
1091 /* ascii_close_page () and support routines. */
1093 #if HAVE_DECL_SIGWINCH
1094 static struct ascii_driver *the_driver;
1097 winch_handler (int signum UNUSED)
1099 update_page_size (the_driver, false);
1104 ascii_open_page (struct ascii_driver *a)
1111 if (a->file == NULL)
1113 a->file = fn_open (a->file_name, a->append ? "a" : "w");
1114 if (a->file != NULL)
1116 if ( isatty (fileno (a->file)))
1118 #if HAVE_DECL_SIGWINCH
1119 struct sigaction action;
1120 sigemptyset (&action.sa_mask);
1121 action.sa_flags = 0;
1122 action.sa_handler = winch_handler;
1124 sigaction (SIGWINCH, &action, NULL);
1126 a->auto_width = true;
1127 a->auto_length = true;
1132 msg_error (errno, _("ascii: opening output file `%s'"),
1141 reallocate_lines (a);
1143 for (i = 0; i < a->length; i++)
1144 u8_line_clear (&a->lines[i]);
1150 output_title_line (FILE *out, int width, const char *left, const char *right)
1152 struct string s = DS_EMPTY_INITIALIZER;
1153 ds_put_byte_multiple (&s, ' ', width);
1156 size_t length = MIN (strlen (left), width);
1157 memcpy (ds_end (&s) - width, left, length);
1161 size_t length = MIN (strlen (right), width);
1162 memcpy (ds_end (&s) - length, right, length);
1164 ds_put_byte (&s, '\n');
1165 fputs (ds_cstr (&s), out);
1170 ascii_close_page (struct ascii_driver *a)
1176 if (a->file == NULL)
1179 if (!a->top_margin && !a->bottom_margin && a->squeeze_blank_lines
1180 && !a->paginate && a->page_number > 1)
1181 putc ('\n', a->file);
1183 for (i = 0; i < a->top_margin; i++)
1184 putc ('\n', a->file);
1189 r1 = xasprintf (_("%s - Page %d"), get_start_date (), a->page_number);
1190 r2 = xasprintf ("%s - %s" , version, host_system);
1192 output_title_line (a->file, a->width, a->title, r1);
1193 output_title_line (a->file, a->width, a->subtitle, r2);
1194 putc ('\n', a->file);
1201 for (y = 0; y < a->allocated_lines; y++)
1203 struct u8_line *line = &a->lines[y];
1205 if (a->squeeze_blank_lines && y > 0 && line->width == 0)
1211 putc ('\n', a->file);
1215 while (ds_chomp_byte (&line->s, ' '))
1217 fwrite (ds_data (&line->s), 1, ds_length (&line->s), a->file);
1218 putc ('\n', a->file);
1221 if (!a->squeeze_blank_lines)
1222 for (y = a->allocated_lines; y < a->length; y++)
1223 putc ('\n', a->file);
1225 for (i = 0; i < a->bottom_margin; i++)
1226 putc ('\n', a->file);
1228 putc ('\f', a->file);