From: Ben Pfaff Date: Wed, 26 Dec 2018 00:32:38 +0000 (-0800) Subject: output: New page-setup-item. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?p=pspp;a=commitdiff_plain;h=a1b7813a6993b8a8822914a83982a52e98c2afe5 output: New page-setup-item. --- diff --git a/src/language/utilities/echo.c b/src/language/utilities/echo.c index 88ece636ad..3f31e335f6 100644 --- a/src/language/utilities/echo.c +++ b/src/language/utilities/echo.c @@ -31,7 +31,7 @@ cmd_echo (struct lexer *lexer, struct dataset *ds UNUSED) if (!lex_force_string (lexer)) return CMD_FAILURE; - text_item_submit (text_item_create (TEXT_ITEM_ECHO, lex_tokcstr (lexer))); + text_item_submit (text_item_create (TEXT_ITEM_LOG, lex_tokcstr (lexer))); lex_get (lexer); return CMD_SUCCESS; diff --git a/src/output/automake.mk b/src/output/automake.mk index df4a743f73..5753d59a0f 100644 --- a/src/output/automake.mk +++ b/src/output/automake.mk @@ -65,6 +65,8 @@ src_output_liboutput_la_SOURCES = \ src/output/output-item-provider.h \ src/output/output-item.c \ src/output/output-item.h \ + src/output/page-setup-item.c \ + src/output/page-setup-item.h \ src/output/render.c \ src/output/render.h \ src/output/tab.c \ diff --git a/src/output/cairo.c b/src/output/cairo.c index c96384ae0a..1ff7c2a214 100644 --- a/src/output/cairo.c +++ b/src/output/cairo.c @@ -42,6 +42,7 @@ #include "output/group-item.h" #include "output/message-item.h" #include "output/options.h" +#include "output/page-setup-item.h" #include "output/render.h" #include "output/tab.h" #include "output/table-item.h" @@ -150,10 +151,16 @@ struct xr_driver int line_width; /* Width of lines. */ int min_break[TABLE_N_AXES]; /* Min cell size to break across pages. */ + int object_spacing; /* Space between output objects. */ struct xr_color bg; /* Background color */ struct xr_color fg; /* Foreground color */ + int initial_page_number; + + struct page_heading headings[2]; /* Top and bottom headings. */ + int headings_height[2]; + /* Internal state. */ struct render_params *params; int char_width, char_height; @@ -161,10 +168,12 @@ struct xr_driver char *title; char *subtitle; cairo_t *cairo; + cairo_surface_t *surface; int page_number; /* Current page number. */ int x, y; struct xr_render_fsm *fsm; int nest; + struct string_map heading_vars; }; static const struct output_driver_class cairo_driver_class; @@ -316,8 +325,6 @@ apply_options (struct xr_driver *xr, struct string_map *o) xr->fonts[XR_FONT_EMPHASIS].desc = parse_font_option ( d, o, "emph-font", "sans serif", font_size, false, true); - xr->page_number = 0; - parse_color (d, o, "background-color", "#FFFFFFFFFFFF", &xr->bg); parse_color (d, o, "foreground-color", "#000000000000", &xr->fg); @@ -331,6 +338,9 @@ apply_options (struct xr_driver *xr, struct string_map *o) min_break[H] = parse_dimension (opt (d, o, "min-hbreak", NULL)) * scale; min_break[V] = parse_dimension (opt (d, o, "min-vbreak", NULL)) * scale; + int object_spacing = (parse_dimension (opt (d, o, "object-spacing", NULL)) + * scale); + /* Convert to inch/(XR_POINT * 72). */ xr->left_margin = left_margin * scale; xr->right_margin = right_margin * scale; @@ -340,6 +350,9 @@ apply_options (struct xr_driver *xr, struct string_map *o) xr->length = (paper_length - top_margin - bottom_margin) * scale; xr->min_break[H] = min_break[H] >= 0 ? min_break[H] : xr->width / 2; xr->min_break[V] = min_break[V] >= 0 ? min_break[V] : xr->length / 2; + xr->object_spacing = object_spacing >= 0 ? object_spacing : XR_POINT * 12; + + /* There are no headings so headings_height can stay 0. */ } static struct xr_driver * @@ -350,6 +363,8 @@ xr_allocate (const char *name, int device_type, struct string_map *o) output_driver_init (d, &cairo_driver_class, name, device_type); + string_map_init (&xr->heading_vars); + apply_options (xr, o); return xr; @@ -371,29 +386,139 @@ xr_to_pango (int xr) : xr); } +static void +xr_measure_fonts (cairo_t *cairo, const struct xr_font fonts[XR_N_FONTS], + int *char_width, int *char_height) +{ + *char_width = 0; + *char_height = 0; + for (int i = 0; i < XR_N_FONTS; i++) + { + PangoLayout *layout = pango_cairo_create_layout (cairo); + pango_layout_set_font_description (layout, fonts[i].desc); + + pango_layout_set_text (layout, "0", 1); + + int cw, ch; + pango_layout_get_size (layout, &cw, &ch); + *char_width = MAX (*char_width, pango_to_xr (cw)); + *char_height = MAX (*char_height, pango_to_xr (ch)); + + g_object_unref (G_OBJECT (layout)); + } +} + +static int +xr_render_page_heading (cairo_t *cairo, const PangoFontDescription *font, + const struct page_heading *ph, int page_number, + int width, bool draw, int base_y) +{ + PangoLayout *layout = pango_cairo_create_layout (cairo); + pango_layout_set_font_description (layout, font); + + int y = 0; + for (size_t i = 0; i < ph->n; i++) + { + const struct page_paragraph *pp = &ph->paragraphs[i]; + + char *markup = output_driver_substitute_heading_vars (pp->markup, + page_number); + pango_layout_set_markup (layout, markup, -1); + free (markup); + + pango_layout_set_alignment (layout, + (pp->halign == TAB_RIGHT ? PANGO_ALIGN_RIGHT + : pp->halign == TAB_LEFT ? PANGO_ALIGN_LEFT + : PANGO_ALIGN_CENTER)); + pango_layout_set_width (layout, xr_to_pango (width)); + if (draw) + { + cairo_save (cairo); + cairo_translate (cairo, 0, xr_to_pt (y + base_y)); + pango_cairo_show_layout (cairo, layout); + cairo_restore (cairo); + } + + int w, h; + pango_layout_get_size (layout, &w, &h); + y += pango_to_xr (h); + } + + g_object_unref (G_OBJECT (layout)); + + return y; +} + +static int +xr_measure_headings (cairo_surface_t *surface, + const PangoFontDescription *font, + const struct page_heading headings[2], + int width, int object_spacing, int height[2]) +{ + cairo_t *cairo = cairo_create (surface); + int total = 0; + for (int i = 0; i < 2; i++) + { + int h = xr_render_page_heading (cairo, font, &headings[i], -1, + width, false, 0); + + /* If the top heading is nonempty, add some space below it. */ + if (h && i == 0) + h += object_spacing; + + if (height) + height[i] = h; + total += h; + } + cairo_destroy (cairo); + return total; +} + static bool -xr_set_cairo (struct xr_driver *xr, cairo_t *cairo) +xr_check_fonts (cairo_surface_t *surface, + const struct xr_font fonts[XR_N_FONTS], + int usable_width, int usable_length) { - int i; + cairo_t *cairo = cairo_create (surface); + int char_width, char_height; + xr_measure_fonts (cairo, fonts, &char_width, &char_height); + cairo_destroy (cairo); + bool ok = true; + enum { MIN_WIDTH = 3, MIN_LENGTH = 3 }; + if (usable_width / char_width < MIN_WIDTH) + { + msg (ME, _("The defined page is not wide enough to hold at least %d " + "characters in the default font. In fact, there's only " + "room for %d characters."), + MIN_WIDTH, usable_width / char_width); + ok = false; + } + if (usable_length / char_height < MIN_LENGTH) + { + msg (ME, _("The defined page is not long enough to hold at least %d " + "lines in the default font. In fact, there's only " + "room for %d lines."), + MIN_LENGTH, usable_length / char_height); + ok = false; + } + return ok; +} + +static void +xr_set_cairo (struct xr_driver *xr, cairo_t *cairo) +{ xr->cairo = cairo; cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH)); - xr->char_width = 0; - xr->char_height = 0; - for (i = 0; i < XR_N_FONTS; i++) + xr_measure_fonts (xr->cairo, xr->fonts, &xr->char_width, &xr->char_height); + + for (int i = 0; i < XR_N_FONTS; i++) { struct xr_font *font = &xr->fonts[i]; - int char_width, char_height; - font->layout = pango_cairo_create_layout (cairo); pango_layout_set_font_description (font->layout, font->desc); - - pango_layout_set_text (font->layout, "0", 1); - pango_layout_get_size (font->layout, &char_width, &char_height); - xr->char_width = MAX (xr->char_width, pango_to_xr (char_width)); - xr->char_height = MAX (xr->char_height, pango_to_xr (char_height)); } if (xr->params == NULL) @@ -412,7 +537,7 @@ xr_set_cairo (struct xr_driver *xr, cairo_t *cairo) int lw = XR_LINE_WIDTH; int ls = XR_LINE_SPACE; - for (i = 0; i < TABLE_N_AXES; i++) + for (int i = 0; i < TABLE_N_AXES; i++) { xr->params->line_widths[i][RENDER_LINE_NONE] = 0; xr->params->line_widths[i][RENDER_LINE_SINGLE] = lw; @@ -422,24 +547,20 @@ xr_set_cairo (struct xr_driver *xr, cairo_t *cairo) xr->params->line_widths[i][RENDER_LINE_DOUBLE] = 2 * lw + ls; } - for (i = 0; i < TABLE_N_AXES; i++) + for (int i = 0; i < TABLE_N_AXES; i++) xr->params->min_break[i] = xr->min_break[i]; xr->params->supports_margins = true; xr->params->rtl = render_direction_rtl (); } cairo_set_source_rgb (xr->cairo, xr->fg.red, xr->fg.green, xr->fg.blue); - - return true; } static struct output_driver * xr_create (const char *file_name, enum settings_output_devices device_type, struct string_map *o, enum xr_output_type file_type) { - enum { MIN_WIDTH = 3, MIN_LENGTH = 3 }; struct xr_driver *xr; - cairo_surface_t *surface; cairo_status_t status; double width_pt, length_pt; @@ -448,52 +569,25 @@ xr_create (const char *file_name, enum settings_output_devices device_type, width_pt = xr_to_pt (xr->width + xr->left_margin + xr->right_margin); length_pt = xr_to_pt (xr->length + xr->top_margin + xr->bottom_margin); if (file_type == XR_PDF) - surface = cairo_pdf_surface_create (file_name, width_pt, length_pt); + xr->surface = cairo_pdf_surface_create (file_name, width_pt, length_pt); else if (file_type == XR_PS) - surface = cairo_ps_surface_create (file_name, width_pt, length_pt); + xr->surface = cairo_ps_surface_create (file_name, width_pt, length_pt); else if (file_type == XR_SVG) - surface = cairo_svg_surface_create (file_name, width_pt, length_pt); + xr->surface = cairo_svg_surface_create (file_name, width_pt, length_pt); else NOT_REACHED (); - status = cairo_surface_status (surface); + status = cairo_surface_status (xr->surface); if (status != CAIRO_STATUS_SUCCESS) { msg (ME, _("error opening output file `%s': %s"), file_name, cairo_status_to_string (status)); - cairo_surface_destroy (surface); goto error; } - xr->cairo = cairo_create (surface); - cairo_surface_destroy (surface); - - if (!xr_set_cairo (xr, xr->cairo)) + if (!xr_check_fonts (xr->surface, xr->fonts, xr->width, xr->length)) goto error; - cairo_save (xr->cairo); - xr_driver_next_page (xr, xr->cairo); - - if (xr->width / xr->char_width < MIN_WIDTH) - { - msg (ME, _("The defined page is not wide enough to hold at least %d " - "characters in the default font. In fact, there's only " - "room for %d characters."), - MIN_WIDTH, - xr->width / xr->char_width); - goto error; - } - - if (xr->length / xr->char_height < MIN_LENGTH) - { - msg (ME, _("The defined page is not long enough to hold at least %d " - "lines in the default font. In fact, there's only " - "room for %d lines."), - MIN_LENGTH, - xr->length / xr->char_height); - goto error; - } - return &xr->driver; error: @@ -538,14 +632,14 @@ xr_destroy (struct output_driver *driver) if (xr->cairo != NULL) { - cairo_status_t status; - - cairo_surface_finish (cairo_get_target (xr->cairo)); - status = cairo_status (xr->cairo); + cairo_surface_finish (xr->surface); + cairo_status_t status = cairo_status (xr->cairo); if (status != CAIRO_STATUS_SUCCESS) msg (ME, _("error drawing output for %s driver: %s"), output_driver_get_name (driver), cairo_status_to_string (status)); + cairo_surface_destroy (xr->surface); + cairo_destroy (xr->cairo); } @@ -571,11 +665,69 @@ xr_flush (struct output_driver *driver) cairo_surface_flush (cairo_get_target (xr->cairo)); } +static void +xr_update_page_setup (struct output_driver *driver, + const struct page_setup *ps) +{ + struct xr_driver *xr = xr_driver_cast (driver); + + xr->initial_page_number = ps->initial_page_number; + xr->object_spacing = ps->object_spacing * 72 * XR_POINT; + + if (xr->cairo) + return; + + int usable[TABLE_N_AXES]; + for (int i = 0; i < 2; i++) + usable[i] = (ps->paper[i] + - (ps->margins[i][0] + ps->margins[i][1])) * 72 * XR_POINT; + + int headings_height[2]; + usable[V] -= xr_measure_headings ( + xr->surface, xr->fonts[XR_FONT_PROPORTIONAL].desc, ps->headings, + usable[H], xr->object_spacing, headings_height); + + enum table_axis h = ps->orientation == PAGE_LANDSCAPE; + enum table_axis v = !h; + if (!xr_check_fonts (xr->surface, xr->fonts, usable[h], usable[v])) + return; + + for (int i = 0; i < 2; i++) + { + page_heading_uninit (&xr->headings[i]); + page_heading_copy (&xr->headings[i], &ps->headings[i]); + xr->headings_height[i] = headings_height[i]; + } + xr->width = usable[h]; + xr->length = usable[v]; + xr->left_margin = ps->margins[h][0] * 72 * XR_POINT; + xr->right_margin = ps->margins[h][1] * 72 * XR_POINT; + xr->top_margin = ps->margins[v][0] * 72 * XR_POINT; + xr->bottom_margin = ps->margins[v][1] * 72 * XR_POINT; + cairo_pdf_surface_set_size (xr->surface, + ps->paper[h] * 72.0, ps->paper[v] * 72.0); +} + static void xr_submit (struct output_driver *driver, const struct output_item *output_item) { struct xr_driver *xr = xr_driver_cast (driver); + if (is_page_setup_item (output_item)) + { + xr_update_page_setup (driver, + to_page_setup_item (output_item)->page_setup); + return; + } + + if (!xr->cairo) + { + xr->page_number = xr->initial_page_number - 1; + xr_set_cairo (xr, cairo_create (xr->surface)); + cairo_save (xr->cairo); + xr_driver_next_page (xr, xr->cairo); + } + xr_driver_output_item (xr, output_item); while (xr_driver_need_new_page (xr)) { @@ -606,11 +758,19 @@ xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo) cairo_translate (cairo, xr_to_pt (xr->left_margin), - xr_to_pt (xr->top_margin)); + xr_to_pt (xr->top_margin + xr->headings_height[0])); xr->page_number++; xr->cairo = cairo; xr->x = xr->y = 0; + + xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL].desc, + &xr->headings[0], xr->page_number, xr->width, true, + -xr->headings_height[0]); + xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL].desc, + &xr->headings[1], xr->page_number, xr->width, true, + xr->length); + xr_driver_run_fsm (xr); } @@ -1274,7 +1434,8 @@ xr_layout_cell_text (struct xr_driver *xr, if (best && !xr->nest) dump_line (xr, -xr->left_margin, best, xr->width + xr->right_margin, best, - RENDER_LINE_SINGLE, &CELL_COLOR (0, 255, 0)); + RENDER_LINE_SINGLE, + &(struct cell_color) CELL_COLOR (0, 255, 0)); } } @@ -1377,11 +1538,7 @@ struct xr_driver * xr_driver_create (cairo_t *cairo, struct string_map *options) { struct xr_driver *xr = xr_allocate ("cairo", 0, options); - if (!xr_set_cairo (xr, cairo)) - { - output_driver_destroy (&xr->driver); - return NULL; - } + xr_set_cairo (xr, cairo); return xr; } @@ -1714,10 +1871,12 @@ static struct xr_render_fsm * xr_render_text (struct xr_driver *xr, const struct text_item *text_item) { enum text_item_type type = text_item_get_type (text_item); + const char *text = text_item_get_text (text_item); switch (type) { case TEXT_ITEM_PAGE_TITLE: + string_map_replace (&xr->heading_vars, "PageTitle", text); break; case TEXT_ITEM_BLANK_LINE: diff --git a/src/output/driver-provider.h b/src/output/driver-provider.h index bfeaca8368..c44a2e303b 100644 --- a/src/output/driver-provider.h +++ b/src/output/driver-provider.h @@ -43,6 +43,8 @@ void output_driver_destroy (struct output_driver *); const char *output_driver_get_name (const struct output_driver *); +char *output_driver_substitute_heading_vars (const char *, int page_number); + /* One kind of output driver. Output driver implementations must not call msg() to report errors. This diff --git a/src/output/driver.c b/src/output/driver.c index b9e4b78529..c9c54ca0e6 100644 --- a/src/output/driver.c +++ b/src/output/driver.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "data/file-handle-def.h" #include "data/settings.h" @@ -62,6 +63,8 @@ struct output_engine char **groups; /* Command names of nested sections. */ size_t n_groups; size_t allocated_groups; + + struct string_map heading_vars; }; static const struct output_driver_factory *factories[]; @@ -77,6 +80,18 @@ engine_stack_top (void) return &engine_stack[n_stack - 1]; } +static void +put_strftime (const char *key, const char *format, + const struct tm *tm, struct string_map *vars) +{ + if (!string_map_find (vars, key)) + { + char value[128]; + strftime (value, sizeof value, format, tm); + string_map_insert (vars, key, value); + } +} + void output_engine_push (void) { @@ -90,8 +105,13 @@ output_engine_push (void) memset (e, 0, sizeof *e); llx_init (&e->drivers); ds_init_empty (&e->deferred_syntax); - e->title = NULL; - e->subtitle = NULL; + + string_map_init (&e->heading_vars); + + time_t t = time (NULL); + const struct tm *tm = localtime (&t); + put_strftime ("Date", "%x", tm, &e->heading_vars); + put_strftime ("Time", "%X", tm, &e->heading_vars); } void @@ -113,6 +133,7 @@ output_engine_pop (void) for (size_t i = 0; i < e->n_groups; i++) free (e->groups[i]); free (e->groups); + string_map_destroy (&e->heading_vars); } void @@ -216,7 +237,29 @@ output_submit (struct output_item *item) size_t idx = --e->n_groups; free (e->groups[idx]); + if (idx >= 1 && idx <= 4) + { + char key[6]; + snprintf (key, sizeof key, "Head%d", idx); + string_map_find_and_delete (&e->heading_vars, key); + } + } + else if (is_text_item (item)) + { + const struct text_item *text_item = to_text_item (item); + enum text_item_type type = text_item_get_type (text_item); + const char *text = text_item_get_text (text_item); + if (type == TEXT_ITEM_TITLE + && e->n_groups >= 1 && e->n_groups <= 4) + { + char key[6]; + snprintf (key, sizeof key, "Head%d", e->n_groups); + string_map_replace (&e->heading_vars, key, text); + } + else if (type == TEXT_ITEM_PAGE_TITLE) + string_map_replace (&e->heading_vars, "PageTitle", text); } + output_submit__ (e, item); } @@ -283,6 +326,14 @@ output_set_subtitle (const char *subtitle) output_set_title__ (e, &e->subtitle, subtitle); } +void +output_set_filename (const char *filename) +{ + struct output_engine *e = engine_stack_top (); + + string_map_replace (&e->heading_vars, "Filename", filename); +} + size_t output_get_group_level (void) { @@ -512,3 +563,39 @@ output_get_text_from_markup (const char *markup) return content; } + +char * +output_driver_substitute_heading_vars (const char *src, int page_number) +{ + struct output_engine *e = engine_stack_top (); + struct string dst = DS_EMPTY_INITIALIZER; + ds_extend (&dst, strlen (src)); + for (const char *p = src; *p; ) + { + if (!strncmp (p, "&[", 6)) + { + if (page_number != INT_MIN) + { + const char *start = p + 6; + const char *end = strchr (start, ']'); + if (end) + { + const char *value = string_map_find__ (&e->heading_vars, + start, end - start); + if (value) + ds_put_cstr (&dst, value); + else if (ss_equals (ss_buffer (start, end - start), + ss_cstr ("Page"))) + ds_put_format (&dst, "%d", page_number); + p = end + 1; + continue; + } + } + ds_put_cstr (&dst, "&"); + p += 5; + } + else + ds_put_byte (&dst, *p++); + } + return ds_steal_cstr (&dst); +} diff --git a/src/output/driver.h b/src/output/driver.h index 09281449df..dfefa3f69d 100644 --- a/src/output/driver.h +++ b/src/output/driver.h @@ -34,6 +34,8 @@ void output_flush (void); void output_set_title (const char *); void output_set_subtitle (const char *); +void output_set_filename (const char *); + const char *output_get_command_name (void); size_t output_get_group_level (void); diff --git a/src/output/html.c b/src/output/html.c index 84843cbd64..f5639cc0ab 100644 --- a/src/output/html.c +++ b/src/output/html.c @@ -40,7 +40,8 @@ #include "output/table-item.h" #include "output/text-item.h" -#include "xalloc.h" +#include "gl/minmax.h" +#include "gl/xalloc.h" #include "gettext.h" #define _(msgid) gettext (msgid) @@ -199,7 +200,7 @@ print_title_tag (FILE *file, const char *name, const char *content) { if (content != NULL) { - fprintf (file, "<%s>", name); + fprintf (file, "<%s>", name); escape_string (file, content, strlen (content), " ", " - "); fprintf (file, "\n", name); } @@ -264,8 +265,12 @@ html_submit (struct output_driver *driver, case TEXT_ITEM_PAGE_TITLE: break; - case TEXT_ITEM_SUBHEAD: - print_title_tag (html->file, "H4", s); + case TEXT_ITEM_TITLE: + { + int level = MIN (5, output_get_group_level ()) + 1; + char tag[3] = { 'H', level + '1', '\0' }; + print_title_tag (html->file, tag, s); + } break; case TEXT_ITEM_SYNTAX: @@ -278,7 +283,7 @@ html_submit (struct output_driver *driver, print_title_tag (html->file, "P", s); break; - case TEXT_ITEM_MONOSPACE: + case TEXT_ITEM_LOG: print_title_tag (html->file, "PRE", s); /* should be

*/ break; @@ -289,11 +294,6 @@ html_submit (struct output_driver *driver, case TEXT_ITEM_EJECT_PAGE: /* Nothing to do. */ break; - - case TEXT_ITEM_COMMENT: - case TEXT_ITEM_ECHO: - /* We print out syntax anyway, so nothing to do here either. */ - break; } } else if (is_message_item (output_item)) diff --git a/src/output/page-setup-item.c b/src/output/page-setup-item.c new file mode 100644 index 0000000000..c9cecb0daa --- /dev/null +++ b/src/output/page-setup-item.c @@ -0,0 +1,103 @@ +/* PSPP - a program for statistical analysis. + Copyright (C) 2018 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include + +#include "output/page-setup-item.h" + +#include + +#include "output/driver-provider.h" +#include "output/output-item-provider.h" + +#include "gl/xalloc.h" + +void +page_heading_copy (struct page_heading *dst, const struct page_heading *src) +{ + dst->n = src->n; + dst->paragraphs = xmalloc (dst->n * sizeof *dst->paragraphs); + for (size_t i = 0; i < dst->n; i++) + { + dst->paragraphs[i].markup = xstrdup (src->paragraphs[i].markup); + dst->paragraphs[i].halign = src->paragraphs[i].halign; + } +} + +void +page_heading_uninit (struct page_heading *ph) +{ + if (!ph) + return; + + for (size_t i = 0; i < ph->n; i++) + free (ph->paragraphs[i].markup); + free (ph->paragraphs); +} + +struct page_setup * +page_setup_clone (const struct page_setup *old) +{ + struct page_setup *new = xmalloc (sizeof *new); + *new = *old; + for (int i = 0; i < 2; i++) + page_heading_copy (&new->headings[i], &old->headings[i]); + if (new->file_name) + new->file_name = xstrdup (new->file_name); + return new; +} + +void +page_setup_destroy (struct page_setup *ps) +{ + if (ps) + { + for (int i = 0; i < 2; i++) + page_heading_uninit (&ps->headings[i]); + free (ps->file_name); + free (ps); + } +} + +struct page_setup_item * +page_setup_item_create (const struct page_setup *ps) +{ + struct page_setup_item *item = xmalloc (sizeof *item); + output_item_init (&item->output_item, &page_setup_item_class); + item->page_setup = page_setup_clone (ps); + return item; +} + +/* Submits ITEM to the configured output drivers, and transfers ownership to + the output subsystem. */ +void +page_setup_item_submit (struct page_setup_item *item) +{ + output_submit (&item->output_item); +} + +static void +page_setup_item_destroy (struct output_item *output_item) +{ + struct page_setup_item *item = to_page_setup_item (output_item); + page_setup_destroy (item->page_setup); + free (item); +} + +const struct output_item_class page_setup_item_class = + { + page_setup_item_destroy, + }; diff --git a/src/output/page-setup-item.h b/src/output/page-setup-item.h new file mode 100644 index 0000000000..a01b7763e2 --- /dev/null +++ b/src/output/page-setup-item.h @@ -0,0 +1,148 @@ +/* PSPP - a program for statistical analysis. + Copyright (C) 2009, 2010, 2011, 2018 Free Sonftware Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef OUTPUT_PAGE_SETUP_ITEM_H +#define OUTPUT_PAGE_SETUP_ITEM_H 1 + +/* Page setup items. + + A page setup item configures the paper size, margins, header and footer, + and other attributes used for printing. */ + +#include +#include "output/output-item.h" +#include "table.h" + +enum page_orientation + { + PAGE_PORTRAIT, + PAGE_LANDSCAPE + }; + +enum page_chart_size + { + PAGE_CHART_AS_IS, + PAGE_CHART_FULL_HEIGHT, + PAGE_CHART_HALF_HEIGHT, + PAGE_CHART_QUARTER_HEIGHT, + }; + +struct page_paragraph + { + char *markup; + int halign; /* TAB_LEFT, TAB_CENTER, TAB_RIGHT. */ + }; + +struct page_heading + { + struct page_paragraph *paragraphs; + size_t n; + }; + +void page_heading_copy (struct page_heading *, const struct page_heading *); +void page_heading_uninit (struct page_heading *); + +struct page_setup + { + int initial_page_number; + double paper[TABLE_N_AXES]; /* Paper size in inches. */ + double margins[TABLE_N_AXES][2]; /* In inches. */ + enum page_orientation orientation; + double object_spacing; /* Space between objects, in inches. */ + enum page_chart_size chart_size; + struct page_heading headings[2]; /* Header and footer. */ + char *file_name; + }; + +#define PAGE_SETUP_INITIALIZER \ + { \ + .initial_page_number = 1, \ + .paper = { [TABLE_HORZ] = 8.5, [TABLE_VERT] = 11.0 }, \ + .margins = { { 0.5, 0.5 }, { 0.5, 0.5 } }, \ + .orientation = PAGE_PORTRAIT, \ + .object_spacing = 12.0 / 72.0, \ + .chart_size = PAGE_CHART_AS_IS, \ + } + +struct page_setup *page_setup_clone (const struct page_setup *); +void page_setup_destroy (struct page_setup *); + +/* A page setup item. */ +struct page_setup_item + { + struct output_item output_item; + struct page_setup *page_setup; + }; + +struct page_setup_item *page_setup_item_create (const struct page_setup *); + +/* This boilerplate for page_setup_item, a subclass of output_item, was + autogenerated by mk-class-boilerplate. */ + +#include +#include "libpspp/cast.h" + +extern const struct output_item_class page_setup_item_class; + +/* Returns true if SUPER is a page_setup_item, otherwise false. */ +static inline bool +is_page_setup_item (const struct output_item *super) +{ + return super->class == &page_setup_item_class; +} + +/* Returns SUPER converted to page_setup_item. SUPER must be a page_setup_item, as + reported by is_page_setup_item. */ +static inline struct page_setup_item * +to_page_setup_item (const struct output_item *super) +{ + assert (is_page_setup_item (super)); + return UP_CAST (super, struct page_setup_item, output_item); +} + +/* Returns INSTANCE converted to output_item. */ +static inline struct output_item * +page_setup_item_super (const struct page_setup_item *instance) +{ + return CONST_CAST (struct output_item *, &instance->output_item); +} + +/* Increments INSTANCE's reference count and returns INSTANCE. */ +static inline struct page_setup_item * +page_setup_item_ref (const struct page_setup_item *instance) +{ + return to_page_setup_item (output_item_ref (&instance->output_item)); +} + +/* Decrements INSTANCE's reference count, then destroys INSTANCE if + the reference count is now zero. */ +static inline void +page_setup_item_unref (struct page_setup_item *instance) +{ + output_item_unref (&instance->output_item); +} + +/* Returns true if INSTANCE's reference count is greater than 1, + false otherwise. */ +static inline bool +page_setup_item_is_shared (const struct page_setup_item *instance) +{ + return output_item_is_shared (&instance->output_item); +} + +void page_setup_item_submit (struct page_setup_item *); + +#endif /* output/page-setup-item.h */ diff --git a/src/output/tab.c b/src/output/tab.c index 435fa609ce..a9487b375b 100644 --- a/src/output/tab.c +++ b/src/output/tab.c @@ -769,8 +769,8 @@ tab_next_row (struct tab_table *t) void tab_output_text (int options, const char *string) { - enum text_item_type type = (options & TAB_EMPH ? TEXT_ITEM_SUBHEAD - : options & TAB_FIX ? TEXT_ITEM_MONOSPACE + enum text_item_type type = (options & TAB_EMPH ? TEXT_ITEM_TITLE + : options & TAB_FIX ? TEXT_ITEM_LOG : TEXT_ITEM_PARAGRAPH); text_item_submit (text_item_create (type, string)); } diff --git a/src/output/text-item.c b/src/output/text-item.c index 9c3645bd3e..231a5c1fb7 100644 --- a/src/output/text-item.c +++ b/src/output/text-item.c @@ -112,6 +112,8 @@ text_item_to_table_item (struct text_item *text_item) int opts = TAB_LEFT; if (text_item->markup) opts |= TAB_MARKUP; + if (text_item->type == TEXT_ITEM_SYNTAX || text_item->type == TEXT_ITEM_LOG) + opts |= TAB_FIX; tab_text (tab, 0, 0, opts, text_item_get_text (text_item)); struct table_item *table_item = table_item_create (&tab->table, NULL, NULL); text_item_unref (text_item); diff --git a/src/output/text-item.h b/src/output/text-item.h index 9b8345ef82..659a28bd0f 100644 --- a/src/output/text-item.h +++ b/src/output/text-item.h @@ -30,18 +30,13 @@ enum text_item_type { - /* Headings. */ TEXT_ITEM_PAGE_TITLE, /* TITLE and SUBTITLE commands. */ - TEXT_ITEM_SUBHEAD, /* Heading within a command's output.*/ + TEXT_ITEM_TITLE, /* Title. */ + TEXT_ITEM_PARAGRAPH, /* Normal paragraph of text. */ - /* Syntax. */ + /* Log items. */ TEXT_ITEM_SYNTAX, /* A single line of PSPP syntax. */ - TEXT_ITEM_COMMENT, /* COMMENT command. */ - TEXT_ITEM_ECHO, /* ECHO command. */ - - /* Ordinary text. */ - TEXT_ITEM_PARAGRAPH, /* Normal paragraph of text. */ - TEXT_ITEM_MONOSPACE, /* Paragraph of monospaced text. */ + TEXT_ITEM_LOG, /* Other logging. */ /* Spacing. Some output drivers that are not based on lines and pages (e.g. CSV, HTML) may ignore these. */