From: Ben Pfaff Date: Sat, 19 Dec 2020 08:12:28 +0000 (-0800) Subject: cairo: Add support for png and trim. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=13ca98079f4cb3cd80deb10b173813548b1f3ddc;p=pspp cairo: Add support for png and trim. This also allows dropping the librsvg dependency. --- diff --git a/INSTALL b/INSTALL index 66d9355fce..96191b9782 100644 --- a/INSTALL +++ b/INSTALL @@ -128,10 +128,6 @@ Other optional packages: PSPP to test the Perl module more thoroughly. It is not needed to build or use the Perl module. - * librsvg enables 300 dpi copy and paste operation. Without librsvg - the copy action will only provide images with default resolution - which is often 96dpi. This only affects bitmap image formats. - Basic Installation ================== diff --git a/NEWS b/NEWS index 204d773f17..98b24a4fef 100644 --- a/NEWS +++ b/NEWS @@ -9,8 +9,6 @@ Changes from 1.4.1 to 1.5.2: * An error in the displayed signficance of oneway anova contrasts tests has been corrected. - * PSPP can now write its results directly to a TeX source file. - * Added Drag-N-Drop in output view. * The Explore GUI dialog supports the "Plots" subdialog. Boxplots, Q-Q Plots @@ -20,7 +18,14 @@ Changes from 1.4.1 to 1.5.2: The new interface provides the user with a preview of the data to be imported and interactive methods to select the desired ranges. - * The html output driver has a new option "bare". + * Output driver changes: + + - New drivers for output to TeX source files and to PNG files. + + - New option "trim" to remove empty space from PDF, PostScript, SVG, + and PNG output files. + + - The HTML output driver has a new option "bare". * New features in pspp-output: diff --git a/configure.ac b/configure.ac index e62a77504d..d98631fcfc 100644 --- a/configure.ac +++ b/configure.ac @@ -142,10 +142,6 @@ if test "$with_cairo" != no && test "$with_gui" != "no"; then [PSPP_REQUIRED_PREREQ([spread-sheet-widget 0.7 (or use --without-gui)])]) PKG_CHECK_VAR([SPREAD_SHEET_WIDGET_LIBDIR], [spread-sheet-widget], [libdir]) - PKG_CHECK_MODULES([LIBRSVG], [librsvg-2.0 >= 2.44], - [AC_DEFINE([HAVE_RSVG], 1, [Define to 1 if librsvg is available])], - [PSPP_OPTIONAL_PREREQ([librsvg >= 2.44 required for high dpi Copy and Paste])]) - AC_ARG_VAR([GLIB_GENMARSHAL]) AC_CHECK_PROGS([GLIB_GENMARSHAL], [glib-genmarshal]) if test "x$GLIB_GENMARSHAL" = x; then diff --git a/doc/invoking.texi b/doc/invoking.texi index 016659377d..4800fc6997 100644 --- a/doc/invoking.texi +++ b/doc/invoking.texi @@ -22,7 +22,7 @@ interface. @menu * Main Options:: -* PDF PostScript and SVG Output Options:: +* PDF PostScript SVG and PNG Output Options:: * Plain Text Output Options:: * TeX Output Options:: * HTML Output Options:: @@ -205,25 +205,41 @@ Invoke heuristics to assist with testing @pspp{}. For use by @command{make check} and similar scripts. @end table -@node PDF PostScript and SVG Output Options -@section PDF, PostScript, and SVG Output Options +@node PDF PostScript SVG and PNG Output Options +@section PDF, PostScript, SVG, and PNG Output Options @cindex PDF @cindex Postscript @cindex SVG - -To produce output in PDF, PostScript, and SVG formats, specify -@option{-o @var{file}} on the @pspp{} command line, optionally followed -by any of the options shown in the table below to customize the output -format. - -PDF, PostScript, and SVG output is only available if your installation -of @pspp{} was compiled with the Cairo library. +@cindex PNG + +To produce output in PDF, PostScript, SVG, or PNG format, specify +@option{-o @var{file}} on the @pspp{} command line, optionally +followed by any of the options shown in the table below to customize +the output format. These output formats are only available if your +installation of @pspp{} was compiled with the Cairo library. + +PDF, PostScript, and SVG use real units: each dimension among the +options listed below may have a suffix @samp{mm} for millimeters, +@samp{in} for inches, or @samp{pt} for points. Lacking a suffix, +numbers below 50 are assumed to be in inches and those about 50 are +assumed to be in millimeters. + +PNG files are pixel-based, so dimensions in PNG output must ultimately +be measured in pixels. For output to these files, PSPP translates the +specified dimensions to pixels at 72 pixels per inch. For PNG output +only, fonts are by default rendered larger than this, at 96 pixels per +inch. + +An SVG or PNG file can only hold a single page. When PSPP outputs +more than one page to SVG or PNG, it creates multiple files. It +outputs the second page to a file named with a @code{-2} suffix, the +third with a @code{-3} suffix, and so on. @table @asis -@item @option{-O format=@{pdf|ps|svg@}} +@item @option{-O format=@{pdf|ps|svg|png@}} Specify the output format. This is only necessary if the file name -given on @option{-o} does not end in @file{.pdf}, @file{.ps}, or -@file{.svg}. +given on @option{-o} does not end in @file{.pdf}, @file{.ps}, +@file{.svg}, or @file{.png}. @item @option{-O paper-size=@var{paper-size}} Paper size, as a name (@i{e.g.}@: @code{a4}, @code{letter}) or @@ -238,11 +254,9 @@ the default paper size is read from it. As a last resort, A4 paper is assumed. @item @option{-O foreground-color=@var{color}} -@itemx @option{-O background-color=@var{color}} -Sets @var{color} as the color to be used for the background or foreground. -Color should be given in the format @code{#@var{RRRR}@var{GGGG}@var{BBBB}}, -where @var{RRRR}, @var{GGGG} and @var{BBBB} are 4 character hexadecimal -representations of the red, green and blue components respectively. +Sets @var{color} as the default color for lines and text. Use a CSS +color format (e.g.@: @code{#@var{rr}@var{gg}@var{bb}}) or name (e.g.@: +@code{black}) as @var{color}. @item @option{-O orientation=@var{orientation}} Either @code{portrait} or @code{landscape}. Default: @code{portrait}. @@ -267,24 +281,20 @@ Default: proportional font @code{serif}, fixed-pitch font @code{monospace}. Sets the size of the default fonts, in thousandths of a point. Default: 10000 (10 point). -@item @option{-O line-gutter=@var{dimension}} -Sets the width of white space on either side of lines that border text -or graphics objects. Default: @code{1pt}. - -@item @option{-O line-spacing=@var{dimension}} -Sets the spacing between the lines in a double line in a table. -Default: @code{1pt}. - -@item @option{-O line-width=@var{dimension}} -Sets the width of the lines used in tables. Default: @code{0.5pt}. +@item @option{-O trim=true} +This option makes PSPP trim empty space around each page of output, +before adding the margins. This can make the output easier to include +in other documents. + +@item @option{-O font-resolution=@var{dpi}} +Sets the resolution for font rendering, in dots per inch. For PDF, +PostScript, and SVG output, the default is 72 dpi, so that a 10-point +font is rendered with a height of 10 points. For PNG output, the +default is 96 dpi, so that a 10-point font is rendered with a height +of @math{10 / 72 * 96 = 13.3} pixels. Use a larger @var{dpi} to +enlarge text output, or a smaller @var{dpi} to shrink it. @end table -Each @var{dimension} value above may be specified in various units -based on its suffix: @samp{mm} for millimeters, @samp{in} for inches, -or @samp{pt} for points. Lacking a suffix, numbers below 50 are -assumed to be in inches and those about 50 are assumed to be in -millimeters. - @node Plain Text Output Options @section Plain Text Output Options diff --git a/src/output/cairo-fsm.c b/src/output/cairo-fsm.c index ec7e727880..f95ccd4c3f 100644 --- a/src/output/cairo-fsm.c +++ b/src/output/cairo-fsm.c @@ -105,7 +105,6 @@ xr_fsm_style_equals (const struct xr_fsm_style *a, || a->min_break[H] != b->min_break[H] || a->min_break[V] != b->min_break[V] || a->use_system_colors != b->use_system_colors - || a->transparent != b->transparent || a->font_resolution != b->font_resolution) return false; @@ -451,7 +450,8 @@ xrr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx, struct xr_fsm *xr = xr_; int w, h, brk; - if (!xr->style->transparent) + const struct cell_color *bg = &cell->style->font_style.bg[color_idx]; + if ((bg->r != 255 || bg->g != 255 || bg->b != 255) && bg->alpha) { cairo_save (xr->cairo); int bg_clip[TABLE_N_AXES][2]; @@ -466,7 +466,7 @@ xrr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx, bg_clip[axis][1] += spill[axis][1]; } xr_clip (xr, bg_clip); - xr_set_source_rgba (xr->cairo, &cell->style->font_style.bg[color_idx]); + xr_set_source_rgba (xr->cairo, bg); fill_rectangle (xr, bb[H][0] - spill[H][0], bb[V][0] - spill[V][0], diff --git a/src/output/cairo-fsm.h b/src/output/cairo-fsm.h index 37f4b8b40b..bf93783541 100644 --- a/src/output/cairo-fsm.h +++ b/src/output/cairo-fsm.h @@ -48,7 +48,6 @@ struct xr_fsm_style PangoFontDescription *fonts[XR_N_FONTS]; struct cell_color fg; bool use_system_colors; - bool transparent; /* Resolution, in units per inch, used for measuring font "points". If this is 72.0, for example, then 1pt = 1 device unit, which is diff --git a/src/output/cairo-pager.c b/src/output/cairo-pager.c index 36823bd945..f23ad5faa2 100644 --- a/src/output/cairo-pager.c +++ b/src/output/cairo-pager.c @@ -279,16 +279,6 @@ xr_pager_add_page (struct xr_pager *p, cairo_t *cr) const struct xr_fsm_style *fs = p->fsm_style; const struct xr_page_style *ps = p->page_style; - const struct cell_color *bg = &ps->bg; - if (bg->alpha) - { - cairo_save (cr); - cairo_set_source_rgba (cr, bg->r / 255.0, bg->g / 255.0, - bg->b / 255.0, bg->alpha / 255.0); - cairo_rectangle (cr, 0, 0, fs->size[H], fs->size[V]); - cairo_fill (cr); - cairo_restore (cr); - } cairo_translate (cr, xr_to_pt (ps->margins[H][0]), xr_to_pt (ps->margins[V][0])); diff --git a/src/output/cairo-pager.h b/src/output/cairo-pager.h index ab843ee30e..87dcd367f7 100644 --- a/src/output/cairo-pager.h +++ b/src/output/cairo-pager.h @@ -37,7 +37,6 @@ struct xr_page_style struct page_heading headings[2]; /* Top and bottom headings. */ - struct cell_color bg; /* Background color. */ int initial_page_number; int object_spacing; }; diff --git a/src/output/cairo.c b/src/output/cairo.c index 4ee386d25f..a3fb1b7d13 100644 --- a/src/output/cairo.c +++ b/src/output/cairo.c @@ -64,7 +64,8 @@ enum xr_output_type { XR_PDF, XR_PS, - XR_SVG + XR_SVG, + XR_PNG }; /* Cairo output driver. */ @@ -72,10 +73,47 @@ struct xr_driver { struct output_driver driver; + enum xr_output_type output_type; struct xr_fsm_style *fsm_style; struct xr_page_style *page_style; struct xr_pager *pager; - cairo_surface_t *surface; + bool trim; + + /* This is the surface where we're currently drawing. It is always + nonnull. + + If 'trim' is true, this is a special Cairo "recording surface" that we + are using to save output temporarily just to find out the bounding box, + then later replay it into the destination surface. + + If 'trim' is false: + + - For output to a PDF or PostScript file, it is the same pointer as + 'dest_surface'. + + - For output to a PNG file, it is an image surface. + + - For output to an SVG file, it is a recording surface. + */ + cairo_surface_t *drawing_surface; + + /* - For output to a PDF or PostScript file, this is the surface for the + PDF or PostScript file where the output is ultimately going. + + - For output to a PNG file, this is NULL, because Cairo has very + limited support for PNG. Cairo can't open a PNG file for writing as + a surface, it can only save an existing surface to a PNG file. + + - For output to a SVG file, this is NULL, because Cairo does not + permit resizing the SVG page size after creating the file, whereas + this driver needs to do that sometimes. Also, SVG is not multi-page + (according to https://wiki.inkscape.org/wiki/index.php/Multipage). + */ + cairo_surface_t *dest_surface; + + /* Used only in file names, for PNG and SVG output where we can only write + one page per file. */ + int page_number; }; static const struct output_driver_class cairo_driver_class; @@ -145,12 +183,14 @@ parse_font_option (struct output_driver *d, struct string_map *options, } static struct xr_driver * -xr_allocate (const char *name, int device_type, struct string_map *o) +xr_allocate (const char *name, int device_type, + enum xr_output_type output_type, struct string_map *o) { struct xr_driver *xr = xzalloc (sizeof *xr); struct output_driver *d = &xr->driver; output_driver_init (d, &cairo_driver_class, name, device_type); + xr->output_type = output_type; /* Scale factor from inch/72000 to inch/(72 * XR_POINT). */ const double scale = XR_POINT / 1000.; @@ -185,12 +225,6 @@ xr_allocate (const char *name, int device_type, struct string_map *o) struct cell_color fg = parse_color (opt (d, o, "foreground-color", "black")); - bool transparent = parse_boolean (opt (d, o, "transparent", "false")); - struct cell_color bg = (transparent - ? (struct cell_color) { .alpha = 0 } - : parse_color (opt (d, o, "background-color", - "white"))); - bool systemcolors = parse_boolean (opt (d, o, "systemcolors", "false")); int object_spacing @@ -198,6 +232,12 @@ xr_allocate (const char *name, int device_type, struct string_map *o) if (object_spacing <= 0) object_spacing = XR_POINT * 12; + const char *default_resolution = (output_type == XR_PNG ? "96" : "72"); + int font_resolution = parse_int (opt (d, o, "font-resolution", + default_resolution), 10, 1000); + + xr->trim = parse_boolean (opt (d, o, "trim", "false")); + xr->page_style = xmalloc (sizeof *xr->page_style); *xr->page_style = (struct xr_page_style) { .ref_cnt = 1, @@ -207,7 +247,6 @@ xr_allocate (const char *name, int device_type, struct string_map *o) [V] = { margins[V][0], margins[V][1], }, }, - .bg = bg, .initial_page_number = 1, .object_spacing = object_spacing, }; @@ -223,8 +262,7 @@ xr_allocate (const char *name, int device_type, struct string_map *o) }, .fg = fg, .use_system_colors = systemcolors, - .transparent = transparent, - .font_resolution = 72.0, + .font_resolution = font_resolution, }; return xr; @@ -232,32 +270,43 @@ xr_allocate (const char *name, int device_type, struct string_map *o) static struct output_driver * xr_create (struct file_handle *fh, enum settings_output_devices device_type, - struct string_map *o, enum xr_output_type file_type) + struct string_map *o, enum xr_output_type output_type) { const char *file_name = fh_get_file_name (fh); - struct xr_driver *xr = xr_allocate (file_name, device_type, o); + struct xr_driver *xr = xr_allocate (file_name, device_type, output_type, o); double paper[TABLE_N_AXES]; for (int a = 0; a < TABLE_N_AXES; a++) paper[a] = xr_to_pt (xr_page_style_paper_size (xr->page_style, xr->fsm_style, a)); - if (file_type == XR_PDF) - xr->surface = cairo_pdf_surface_create (file_name, paper[H], paper[V]); - else if (file_type == XR_PS) - xr->surface = cairo_ps_surface_create (file_name, paper[H], paper[V]); - else if (file_type == XR_SVG) - xr->surface = cairo_svg_surface_create (file_name, paper[H], paper[V]); - else - NOT_REACHED (); - cairo_status_t status = cairo_surface_status (xr->surface); - if (status != CAIRO_STATUS_SUCCESS) + xr->dest_surface + = (output_type == XR_PDF + ? cairo_pdf_surface_create (file_name, paper[H], paper[V]) + : output_type == XR_PS + ? cairo_ps_surface_create (file_name, paper[H], paper[V]) + : NULL); + if (xr->dest_surface) { - msg (ME, _("error opening output file `%s': %s"), - file_name, cairo_status_to_string (status)); - goto error; + cairo_status_t status = cairo_surface_status (xr->dest_surface); + if (status != CAIRO_STATUS_SUCCESS) + { + msg (ME, _("error opening output file `%s': %s"), + file_name, cairo_status_to_string (status)); + goto error; + } } + xr->drawing_surface + = (xr->trim || output_type == XR_SVG + ? cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, + &(cairo_rectangle_t) { + .width = paper[H], + .height = paper[V] }) + : output_type == XR_PNG + ? cairo_image_surface_create (CAIRO_FORMAT_ARGB32, paper[H], paper[V]) + : xr->dest_surface); + fh_unref (fh); return &xr->driver; @@ -288,20 +337,205 @@ xr_svg_create (struct file_handle *fh, enum settings_output_devices device_type, return xr_create (fh, device_type, o, XR_SVG); } +static struct output_driver * +xr_png_create (struct file_handle *fh, enum settings_output_devices device_type, + struct string_map *o) +{ + return xr_create (fh, device_type, o, XR_PNG); +} + +static void +xr_set_surface_size (cairo_surface_t *surface, enum xr_output_type output_type, + double width, double height) +{ + switch (output_type) + { + case XR_PDF: + cairo_pdf_surface_set_size (surface, width, height); + break; + + case XR_PS: + cairo_ps_surface_set_size (surface, width, height); + break; + + case XR_SVG: + case XR_PNG: + NOT_REACHED (); + } +} + +static void +xr_copy_surface (cairo_surface_t *dst, cairo_surface_t *src, + double x, double y) +{ + cairo_t *cr = cairo_create (dst); + cairo_set_source_surface (cr, src, x, y); + cairo_paint (cr); + cairo_destroy (cr); +} + +static void +clear_rectangle (cairo_surface_t *surface, + double x0, double y0, double x1, double y1) +{ + cairo_t *cr = cairo_create (surface); + cairo_set_source_rgb (cr, 1, 1, 1); + cairo_new_path (cr); + cairo_rectangle (cr, x0, y0, x1 - x0, y1 - y0); + cairo_fill (cr); + cairo_destroy (cr); +} + +static void +xr_report_error (cairo_status_t status, const char *file_name) +{ + if (status != CAIRO_STATUS_SUCCESS) + fprintf (stderr, "%s: %s", file_name, cairo_status_to_string (status)); +} + +static void +xr_finish_page (struct xr_driver *xr) +{ + /* For 'trim' true: + + - If the destination is PDF or PostScript, set the dest surface size, copy + ink extent, show_page. + + - If the destination is PNG, create image surface, copy ink extent, + cairo_surface_write_to_png(), destroy image surface. + + - If the destination is SVG, create svg surface, copy ink extent, close. + + then destroy drawing_surface and make a new one. + + For 'trim' false: + + - If the destination is PDF or PostScript, show_page. + + - If the destination is PNG, cairo_surface_write_to_png(), destroy image + surface, create new image surface. + + - If the destination is SVG, create svg surface, copy whole thing, close. + + */ + double paper[TABLE_N_AXES]; + for (int a = 0; a < TABLE_N_AXES; a++) + paper[a] = xr_to_pt (xr_page_style_paper_size ( + xr->page_style, xr->fsm_style, a)); + + xr->page_number++; + char *file_name = (xr->page_number > 1 + ? xasprintf ("%s-%d", xr->driver.name, xr->page_number) + : xr->driver.name); + + if (xr->trim) + { + /* Get the bounding box for the drawing surface. */ + double ofs[TABLE_N_AXES], size[TABLE_N_AXES]; + cairo_recording_surface_ink_extents (xr->drawing_surface, + &ofs[H], &ofs[V], + &size[H], &size[V]); + const int (*margins)[2] = xr->page_style->margins; + for (int a = 0; a < TABLE_N_AXES; a++) + { + double scale = XR_POINT; + size[a] += (margins[a][0] + margins[a][1]) / scale; + ofs[a] = -ofs[a] + margins[a][0] / scale; + } + + switch (xr->output_type) + { + case XR_PDF: + case XR_PS: + xr_set_surface_size (xr->dest_surface, xr->output_type, + size[H], size[V]); + xr_copy_surface (xr->dest_surface, xr->drawing_surface, + ofs[H], ofs[V]); + cairo_surface_show_page (xr->dest_surface); + break; + + case XR_SVG: + { + cairo_surface_t *svg = cairo_svg_surface_create ( + file_name, size[H], size[V]); + xr_copy_surface (svg, xr->drawing_surface, ofs[H], ofs[V]); + xr_report_error (cairo_surface_status (svg), file_name); + cairo_surface_destroy (svg); + } + break; + + case XR_PNG: + { + cairo_surface_t *png = cairo_image_surface_create ( + CAIRO_FORMAT_ARGB32, size[H], size[V]); + clear_rectangle (png, 0, 0, size[H], size[V]); + xr_copy_surface (png, xr->drawing_surface, ofs[H], ofs[V]); + xr_report_error (cairo_surface_write_to_png (png, file_name), + file_name); + cairo_surface_destroy (png); + } + break; + } + + /* Destroy the recording surface and create a fresh one of the same + size. */ + cairo_surface_destroy (xr->drawing_surface); + xr->drawing_surface = cairo_recording_surface_create ( + CAIRO_CONTENT_COLOR_ALPHA, + &(cairo_rectangle_t) { .width = paper[H], .height = paper[V] }); + } + else + { + switch (xr->output_type) + { + case XR_PDF: + case XR_PS: + cairo_surface_show_page (xr->dest_surface); + break; + + case XR_SVG: + { + cairo_surface_t *svg = cairo_svg_surface_create ( + file_name, paper[H], paper[V]); + xr_copy_surface (svg, xr->drawing_surface, 0.0, 0.0); + xr_report_error (cairo_surface_status (svg), file_name); + cairo_surface_destroy (svg); + } + break; + + case XR_PNG: + xr_report_error (cairo_surface_write_to_png (xr->drawing_surface, + file_name), file_name); + cairo_surface_destroy (xr->drawing_surface); + xr->drawing_surface = cairo_image_surface_create ( + CAIRO_FORMAT_ARGB32, paper[H], paper[V]); + break; + } + } + + if (file_name != xr->driver.name) + free (file_name); +} + static void xr_destroy (struct output_driver *driver) { struct xr_driver *xr = xr_driver_cast (driver); - if (xr->surface) + if (xr->pager) + xr_finish_page (xr); + + if (xr->drawing_surface && xr->drawing_surface != xr->dest_surface) + cairo_surface_destroy (xr->drawing_surface); + if (xr->dest_surface) { - cairo_surface_finish (xr->surface); - cairo_status_t status = cairo_surface_status (xr->surface); + cairo_surface_finish (xr->dest_surface); + cairo_status_t status = cairo_surface_status (xr->dest_surface); if (status != CAIRO_STATUS_SUCCESS) fprintf (stderr, _("error drawing output for %s driver: %s"), output_driver_get_name (driver), cairo_status_to_string (status)); - cairo_surface_destroy (xr->surface); + cairo_surface_destroy (xr->dest_surface); } xr_pager_destroy (xr->pager); @@ -310,15 +544,6 @@ xr_destroy (struct output_driver *driver) free (xr); } -static void -xr_flush (struct output_driver *driver) -{ - struct xr_driver *xr = xr_driver_cast (driver); - - if (xr->surface) - cairo_surface_flush (xr->surface); -} - static void xr_update_page_setup (struct output_driver *driver, const struct page_setup *ps) @@ -341,7 +566,6 @@ xr_update_page_setup (struct output_driver *driver, [V] = { ps->margins[v][0] * scale, ps->margins[v][1] * scale }, }, - .bg = xr->page_style->bg, .initial_page_number = ps->initial_page_number, .object_spacing = ps->object_spacing * 72 * XR_POINT, }; @@ -359,15 +583,14 @@ xr_update_page_setup (struct output_driver *driver, }, .fg = old_fs->fg, .use_system_colors = old_fs->use_system_colors, - .transparent = old_fs->transparent, - .font_resolution = 72.0, + .font_resolution = old_fs->font_resolution, }; for (size_t i = 0; i < XR_N_FONTS; i++) xr->fsm_style->fonts[i] = pango_font_description_copy (old_fs->fonts[i]); xr_fsm_style_unref (old_fs); - cairo_pdf_surface_set_size (xr->surface, ps->paper[H] * 72.0, - ps->paper[V] * 72.0); + xr_set_surface_size (xr->dest_surface, xr->output_type, ps->paper[H] * 72.0, + ps->paper[V] * 72.0); } static void @@ -386,14 +609,14 @@ xr_submit (struct output_driver *driver, const struct output_item *output_item) if (!xr->pager) { xr->pager = xr_pager_create (xr->page_style, xr->fsm_style); - xr_pager_add_page (xr->pager, cairo_create (xr->surface)); + xr_pager_add_page (xr->pager, cairo_create (xr->drawing_surface)); } xr_pager_add_item (xr->pager, output_item); while (xr_pager_needs_new_page (xr->pager)) { - cairo_surface_show_page (xr->surface); - xr_pager_add_page (xr->pager, cairo_create (xr->surface)); + xr_finish_page (xr); + xr_pager_add_page (xr->pager, cairo_create (xr->drawing_surface)); } } @@ -403,11 +626,13 @@ struct output_driver_factory ps_driver_factory = { "ps", "pspp.ps", xr_ps_create }; struct output_driver_factory svg_driver_factory = { "svg", "pspp.svg", xr_svg_create }; +struct output_driver_factory png_driver_factory = + { "png", "pspp.png", xr_png_create }; static const struct output_driver_class cairo_driver_class = { "cairo", xr_destroy, xr_submit, - xr_flush, + NULL, }; diff --git a/src/output/driver.c b/src/output/driver.c index 067bf831ae..cf0a535f3c 100644 --- a/src/output/driver.c +++ b/src/output/driver.c @@ -440,6 +440,7 @@ extern const struct output_driver_factory spv_driver_factory; extern const struct output_driver_factory pdf_driver_factory; extern const struct output_driver_factory ps_driver_factory; extern const struct output_driver_factory svg_driver_factory; +extern const struct output_driver_factory png_driver_factory; #endif extern const struct output_driver_factory tex_driver_factory; @@ -455,6 +456,7 @@ static const struct output_driver_factory *factories[] = &pdf_driver_factory, &ps_driver_factory, &svg_driver_factory, + &png_driver_factory, #endif &tex_driver_factory, NULL diff --git a/src/ui/gui/psppire-output-view.c b/src/ui/gui/psppire-output-view.c index f48b1ade4f..7e341797a1 100644 --- a/src/ui/gui/psppire-output-view.c +++ b/src/ui/gui/psppire-output-view.c @@ -22,9 +22,6 @@ #include #include -#if HAVE_RSVG -#include "librsvg/rsvg.h" -#endif #include "libpspp/assertion.h" #include "libpspp/string-map.h" #include "output/cairo-fsm.h" @@ -167,7 +164,6 @@ get_xr_fsm_style (struct psppire_output_view *view) [XR_FONT_FIXED] = ff, }, .use_system_colors = true, - .transparent = true, .font_resolution = 96.0, }; @@ -603,32 +599,16 @@ enum { SELECT_FMT_ODT }; -/* Returns a pixbuf from a svg file */ -/* You must unref the pixbuf after usage */ -static GdkPixbuf * -derive_pixbuf_from_svg (const char *filename) +static void +clear_rectangle (cairo_surface_t *surface, + double x0, double y0, double x1, double y1) { - GError *err = NULL; - GdkPixbuf *pixbuf = NULL; -#if HAVE_RSVG - RsvgHandle *handle = rsvg_handle_new_from_file (filename, &err); - if (err == NULL) - { - rsvg_handle_set_dpi (handle, 300.0); - pixbuf = rsvg_handle_get_pixbuf (handle); - g_object_unref (handle); - } -#else - pixbuf = gdk_pixbuf_new_from_file (filename, &err); -#endif - if (err != NULL) - { - msg (ME, _("Could not open file %s during copy operation: %s"), - filename, err->message); - g_error_free (err); - return NULL; - } - return pixbuf; + cairo_t *cr = cairo_create (surface); + cairo_set_source_rgb (cr, 1, 1, 1); + cairo_new_path (cr); + cairo_rectangle (cr, x0, y0, x1 - x0, y1 - y0); + cairo_fill (cr); + cairo_destroy (cr); } static void @@ -668,6 +648,7 @@ clipboard_get_cb (GtkClipboard *clipboard, case SELECT_FMT_TEXT: string_map_insert (&options, "format", "txt"); + string_map_insert (&options, "width", "1000"); break; case SELECT_FMT_HTML: @@ -708,16 +689,25 @@ clipboard_get_cb (GtkClipboard *clipboard, gdk_window_end_draw_frame (win, ctx); cairo_region_destroy (region); - cairo_surface_t *surface = cairo_svg_surface_create (filename, w, h); - if (surface) + cairo_surface_t *surface + = (info == SELECT_FMT_SVG + ? cairo_svg_surface_create (filename, w, h) + : cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h)); + clear_rectangle (surface, 0, 0, w, h); + cairo_t *cr2 = cairo_create (surface); + xr_fsm_draw_all (fsm, cr2); + cairo_destroy (cr2); + if (info == SELECT_FMT_IMG) { - cairo_t *cr = cairo_create (surface); - xr_fsm_draw_all (fsm, cr); - cairo_destroy (cr); - cairo_surface_destroy (surface); + GdkPixbuf *pixbuf = gdk_pixbuf_get_from_surface (surface, + 0, 0, w, h); + if (pixbuf) + { + gtk_selection_data_set_pixbuf (selection_data, pixbuf); + g_object_unref (pixbuf); + } } - else - g_error ("Could not create cairo svg surface with file %s", filename); + cairo_surface_destroy (surface); } else { @@ -736,19 +726,11 @@ clipboard_get_cb (GtkClipboard *clipboard, driver = NULL; } - if (info == SELECT_FMT_IMG) - { - GdkPixbuf *pixbuf = derive_pixbuf_from_svg (filename); - if (pixbuf) - { - gtk_selection_data_set_pixbuf (selection_data, pixbuf); - g_object_unref (pixbuf); - } - } - else if (g_file_get_contents (filename, &text, &length, NULL)) - gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data), - 8, - (const guchar *) text, length); + if (info != SELECT_FMT_IMG + && g_file_get_contents (filename, &text, &length, NULL)) + gtk_selection_data_set (selection_data, + gtk_selection_data_get_target (selection_data), + 8, (const guchar *) text, length); finish: @@ -1020,7 +1002,6 @@ create_xr_print_driver (GtkPrintContext *context, struct psppire_output_view *vi [H] = { margins[H][0], margins[H][1] }, [V] = { margins[V][0], margins[V][1] }, }, - .bg = { .alpha = 0 }, .initial_page_number = 1, .object_spacing = 12 * XR_POINT, }; @@ -1037,7 +1018,6 @@ create_xr_print_driver (GtkPrintContext *context, struct psppire_output_view *vi }, .fg = CELL_COLOR_BLACK, .use_system_colors = false, - .transparent = false, .font_resolution = 72.0 };