From b37e251848408f32a9f648047e072de74c24c376 Mon Sep 17 00:00:00 2001 From: Christian Persch Date: Thu, 16 Aug 2018 22:47:44 +0200 Subject: [PATCH] all: Add API to create surface from file descriptor There already is API to create a surface for a filename, but not for a file descriptor. This could be done by using cairo_*_surface_create_for_stream(), but that's unnecessarily complicated; just exposing a cairo_*_surface_create_for_fd() makes this simpler. --- doc/public/cairo-docs.xml | 3 ++ doc/public/cairo-sections.txt | 6 +++ src/cairo-output-stream-private.h | 11 ++++ src/cairo-output-stream.c | 34 +++++++++++++ src/cairo-pdf-surface.c | 40 +++++++++++++++ src/cairo-pdf.h | 5 ++ src/cairo-png.c | 104 ++++++++++++++++++++++++++++++++++++++ src/cairo-ps-surface.c | 45 +++++++++++++++++ src/cairo-ps.h | 5 ++ src/cairo-script-surface.c | 32 ++++++++++++ src/cairo-script.h | 3 ++ src/cairo-svg-surface.c | 57 +++++++++++++++++++++ src/cairo-svg.h | 5 ++ src/cairo-xml-surface.c | 21 ++++++++ src/cairo-xml.h | 3 ++ src/cairo.h | 7 +++ 16 files changed, 381 insertions(+) diff --git a/doc/public/cairo-docs.xml b/doc/public/cairo-docs.xml index 9b92bfa..d6d152d 100644 --- a/doc/public/cairo-docs.xml +++ b/doc/public/cairo-docs.xml @@ -80,5 +80,8 @@ Index of new symbols in 1.14 + + Index of new symbols in 1.16 + diff --git a/doc/public/cairo-sections.txt b/doc/public/cairo-sections.txt index a735869..ac912c0 100644 --- a/doc/public/cairo-sections.txt +++ b/doc/public/cairo-sections.txt @@ -72,6 +72,7 @@ CAIRO_PDF_OUTLINE_ROOT cairo_pdf_outline_flags_t cairo_pdf_metadata_t cairo_pdf_surface_create +cairo_pdf_surface_create_for_fd cairo_pdf_surface_create_for_stream cairo_pdf_surface_restrict_to_version cairo_pdf_version_t @@ -89,8 +90,10 @@ cairo_pdf_surface_set_thumbnail_size CAIRO_HAS_PNG_FUNCTIONS cairo_image_surface_create_from_png cairo_read_func_t +cairo_image_surface_create_from_png_fd cairo_image_surface_create_from_png_stream cairo_surface_write_to_png +cairo_surface_write_to_png_fd cairo_write_func_t cairo_surface_write_to_png_stream @@ -99,6 +102,7 @@ cairo_surface_write_to_png_stream cairo-ps CAIRO_HAS_PS_SURFACE cairo_ps_surface_create +cairo_ps_surface_create_for_fd cairo_ps_surface_create_for_stream cairo_ps_surface_restrict_to_level cairo_ps_level_t @@ -208,6 +212,7 @@ cairo_xcb_device_debug_set_precision cairo-svg CAIRO_HAS_SVG_SURFACE cairo_svg_surface_create +cairo_svg_surface_create_for_fd cairo_svg_surface_create_for_stream cairo_svg_surface_restrict_to_version cairo_svg_version_t @@ -740,6 +745,7 @@ cairo_atsui_font_face_create_for_atsu_font_id cairo-script CAIRO_HAS_SCRIPT_SURFACE cairo_script_create +cairo_script_create_for_fd cairo_script_create_for_stream cairo_script_from_recording_surface cairo_script_get_mode diff --git a/src/cairo-output-stream-private.h b/src/cairo-output-stream-private.h index 2542646..dcc1646 100644 --- a/src/cairo-output-stream-private.h +++ b/src/cairo-output-stream-private.h @@ -157,6 +157,17 @@ _cairo_output_stream_get_status (cairo_output_stream_t *stream); cairo_private cairo_output_stream_t * _cairo_output_stream_create_for_filename (const char *filename); +/* This function never returns %NULL. If an error occurs (NO_MEMORY or + * WRITE_ERROR) while trying to create the output stream this function + * returns a valid pointer to a nil output stream. + * + * Note: Even if a nil surface is returned, the caller should still + * call _cairo_output_stream_destroy (or _cairo_output_stream_close at + * least) in order to ensure that everything is properly cleaned up. + */ +cairo_private cairo_output_stream_t * +_cairo_output_stream_create_for_fd (int fd); + /* This function never returns %NULL. If an error occurs (NO_MEMORY or * WRITE_ERROR) while trying to create the output stream this function * returns a valid pointer to a nil output stream. diff --git a/src/cairo-output-stream.c b/src/cairo-output-stream.c index 72b8152..4744e9e 100644 --- a/src/cairo-output-stream.c +++ b/src/cairo-output-stream.c @@ -692,6 +692,40 @@ _cairo_output_stream_create_for_filename (const char *filename) return &stream->base; } +cairo_output_stream_t * +_cairo_output_stream_create_for_fd (int fd) +{ + stdio_stream_t *stream; + FILE *file; + + if (fd == -1) + return _cairo_null_stream_create (); + + file = fdopen (fd, "wb"); + if (file == NULL) { + switch (errno) { + case ENOMEM: + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_output_stream_t *) &_cairo_output_stream_nil; + default: + _cairo_error_throw (CAIRO_STATUS_WRITE_ERROR); + return (cairo_output_stream_t *) &_cairo_output_stream_nil_write_error; + } + } + + stream = _cairo_malloc (sizeof *stream); + if (unlikely (stream == NULL)) { + fclose (file); + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_output_stream_t *) &_cairo_output_stream_nil; + } + + _cairo_output_stream_init (&stream->base, + stdio_write, stdio_flush, stdio_close); + stream->file = file; + + return &stream->base; +} typedef struct _memory_stream { cairo_output_stream_t base; diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c index ab67813..261f264 100644 --- a/src/cairo-pdf-surface.c +++ b/src/cairo-pdf-surface.c @@ -611,6 +611,46 @@ cairo_pdf_surface_create (const char *filename, height_in_points); } +/** + * cairo_pdf_surface_create_for_fd: + * @fd: a file descriptor for the PDF output (must be writable), -1 may be + * used to specify no output. This will generate a PDF surface that + * may be queried and used as a source, without generating a + * temporary file. + * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) + * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) + * + * Creates a PDF surface of the specified size in points to be written + * to @fd. + * + * Note that this function takes ownership of @fd. + * + * Return value: a pointer to the newly created surface. The caller + * owns the surface and should call cairo_surface_destroy() when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" surface if an error such as out of memory + * occurs. You can use cairo_surface_status() to check for this. + * + * Since: 1.16 + **/ +cairo_surface_t * +cairo_pdf_surface_create_for_fd (int fd, + double width_in_points, + double height_in_points) +{ + cairo_output_stream_t *output; + + output = _cairo_output_stream_create_for_fd (fd); + if (_cairo_output_stream_get_status (output)) + return _cairo_surface_create_in_error (_cairo_output_stream_destroy (output)); + + return _cairo_pdf_surface_create_for_stream_internal (output, + width_in_points, + height_in_points); +} + static cairo_bool_t _cairo_surface_is_pdf (cairo_surface_t *surface) { diff --git a/src/cairo-pdf.h b/src/cairo-pdf.h index 5be0b3f..2412626 100644 --- a/src/cairo-pdf.h +++ b/src/cairo-pdf.h @@ -63,6 +63,11 @@ cairo_pdf_surface_create (const char *filename, double width_in_points, double height_in_points); +cairo_public cairo_surface_t * +cairo_pdf_surface_create_for_fd (int fd, + double width_in_points, + double height_in_points); + cairo_public cairo_surface_t * cairo_pdf_surface_create_for_stream (cairo_write_func_t write_func, void *closure, diff --git a/src/cairo-png.c b/src/cairo-png.c index ab0b9d0..6954179 100644 --- a/src/cairo-png.c +++ b/src/cairo-png.c @@ -390,6 +390,57 @@ cairo_surface_write_to_png (cairo_surface_t *surface, return status; } +/** + * cairo_surface_write_to_png_fd: + * @surface: a #cairo_surface_t with pixel contents + * @fd: a file descriptor of a file to write to + * + * Writes the contents of @surface to a file descriptor @fd as a PNG + * image. + * + * Note that this function takes ownership of @fd. + * + * Return value: %CAIRO_STATUS_SUCCESS if the PNG file was written + * successfully. Otherwise, %CAIRO_STATUS_NO_MEMORY if memory could not + * be allocated for the operation or + * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface does not have + * pixel contents, or %CAIRO_STATUS_WRITE_ERROR if an I/O error occurs + * while attempting to write the file, or %CAIRO_STATUS_PNG_ERROR if libpng + * returned an error. + * + * Since: 1.16 + **/ +cairo_status_t +cairo_surface_write_to_png_fd (cairo_surface_t *surface, + int fd) +{ + FILE *fp; + cairo_status_t status; + + if (surface->status) + return surface->status; + + if (surface->finished) + return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); + + fp = fdopen (fd, "wb"); + if (fp == NULL) { + switch (errno) { + case ENOMEM: + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + default: + return _cairo_error (CAIRO_STATUS_WRITE_ERROR); + } + } + + status = write_png (surface, stdio_write_func, fp); + + if (fclose (fp) && status == CAIRO_STATUS_SUCCESS) + status = _cairo_error (CAIRO_STATUS_WRITE_ERROR); + + return status; +} + struct png_write_closure_t { cairo_write_func_t write_func; void *closure; @@ -796,6 +847,59 @@ cairo_image_surface_create_from_png (const char *filename) return surface; } +/** + * cairo_image_surface_create_from_png: + * @fd: a file descriptor of PNG file to load + * + * Creates a new image surface and initializes the contents to the + * given PNG file. + * + * Note that this function takes ownership of @fd. + * + * Return value: a new #cairo_surface_t initialized with the contents + * of the PNG file, or a "nil" surface if any error occurred. A nil + * surface can be checked for with cairo_surface_status(surface) which + * may return one of the following values: + * + * %CAIRO_STATUS_NO_MEMORY + * %CAIRO_STATUS_READ_ERROR + * %CAIRO_STATUS_PNG_ERROR + * + * Alternatively, you can allow errors to propagate through the drawing + * operations and check the status on the context upon completion + * using cairo_status(). + * + * Since: 1.16 + **/ +cairo_surface_t * +cairo_image_surface_create_from_png_fd (int fd) +{ + struct png_read_closure_t png_closure; + cairo_surface_t *surface; + cairo_status_t status; + + png_closure.closure = fdopen (fd, "rb"); + if (png_closure.closure == NULL) { + switch (errno) { + case ENOMEM: + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + break; + default: + status = _cairo_error (CAIRO_STATUS_READ_ERROR); + break; + } + return _cairo_surface_create_in_error (status); + } + + png_closure.read_func = stdio_read_func; + + surface = read_png (&png_closure); + + fclose (png_closure.closure); + + return surface; +} + /** * cairo_image_surface_create_from_png_stream: * @read_func: function called to read the data of the file diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c index 599eff4..5294a6b 100644 --- a/src/cairo-ps-surface.c +++ b/src/cairo-ps-surface.c @@ -1295,6 +1295,51 @@ cairo_ps_surface_create (const char *filename, height_in_points); } +/** + * cairo_ps_surface_create_for_fd: + * @fd: a file descriptor for the PS output (must be writable), -1 may be + * used to specify no output. This will generate a PS surface that + * may be queried and used as a source, without generating a + * temporary file. + * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) + * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) + * + * Creates a PostScript surface of the specified size in points to be + * written to @fd. See cairo_ps_surface_create_for_stream() for + * a more flexible mechanism for handling the PostScript output than + * simply writing it to a named file or file descriptor. + * + * Note that the size of individual pages of the PostScript output can + * vary. See cairo_ps_surface_set_size(). + * + * Note that this function takes ownership of @fd. + * + * Return value: a pointer to the newly created surface. The caller + * owns the surface and should call cairo_surface_destroy() when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" surface if an error such as out of memory + * occurs. You can use cairo_surface_status() to check for this. + * + * Since: 1.16 + **/ +cairo_surface_t * +cairo_ps_surface_create_for_fd (int fd, + double width_in_points, + double height_in_points) +{ + cairo_output_stream_t *stream; + + stream = _cairo_output_stream_create_for_fd (fd); + if (_cairo_output_stream_get_status (stream)) + return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream)); + + return _cairo_ps_surface_create_for_stream_internal (stream, + width_in_points, + height_in_points); +} + /** * cairo_ps_surface_create_for_stream: * @write_func: a #cairo_write_func_t to accept the output data, may be %NULL diff --git a/src/cairo-ps.h b/src/cairo-ps.h index 33d0e0d..d662efc 100644 --- a/src/cairo-ps.h +++ b/src/cairo-ps.h @@ -68,6 +68,11 @@ cairo_ps_surface_create (const char *filename, double width_in_points, double height_in_points); +cairo_public cairo_surface_t * +cairo_ps_surface_create_for_fd (int fd, + double width_in_points, + double height_in_points); + cairo_public cairo_surface_t * cairo_ps_surface_create_for_stream (cairo_write_func_t write_func, void *closure, diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c index 7db7dc5..ed18824 100644 --- a/src/cairo-script-surface.c +++ b/src/cairo-script-surface.c @@ -3769,6 +3769,38 @@ cairo_script_create (const char *filename) return _cairo_script_context_create (stream); } +/** + * cairo_script_create_for_fd: + * @fd: a file descriptor for the script output + * + * Creates a output device for emitting the script, used when + * creating the individual surfaces. + * + * Note that this function takes ownership of @fd. + * + * Return value: a pointer to the newly created device. The caller + * owns the surface and should call cairo_device_destroy() when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" device if an error such as out of memory + * occurs. You can use cairo_device_status() to check for this. + * + * Since: 1.16 + **/ +cairo_device_t * +cairo_script_create_for_fd (int fd) +{ + cairo_output_stream_t *stream; + cairo_status_t status; + + stream = _cairo_output_stream_create_for_fd (fd); + if ((status = _cairo_output_stream_get_status (stream))) + return _cairo_device_create_in_error (status); + + return _cairo_script_context_create (stream); +} + /** * cairo_script_create_for_stream: * @write_func: callback function passed the bytes written to the script diff --git a/src/cairo-script.h b/src/cairo-script.h index b5a8cf3..20cdfc5 100644 --- a/src/cairo-script.h +++ b/src/cairo-script.h @@ -59,6 +59,9 @@ typedef enum { cairo_public cairo_device_t * cairo_script_create (const char *filename); +cairo_public cairo_device_t * +cairo_script_create_for_fd (int fd); + cairo_public cairo_device_t * cairo_script_create_for_stream (cairo_write_func_t write_func, void *closure); diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c index dd37198..bc962a6 100644 --- a/src/cairo-svg-surface.c +++ b/src/cairo-svg-surface.c @@ -297,6 +297,63 @@ cairo_svg_surface_create (const char *filename, return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1); } +/** + * cairo_svg_surface_create_for_fd: + * @filename: a file descriptor for the SVG output (must be writable), -1 may be + * used to specify no output. This will generate a SVG surface that + * may be queried and used as a source, without generating a + * temporary file. + * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) + * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) + * + * Creates a SVG surface of the specified size in points to be written + * to @fd. + * + * The SVG surface backend recognizes the following MIME types for the + * data attached to a surface (see cairo_surface_set_mime_data()) when + * it is used as a source pattern for drawing on this surface: + * %CAIRO_MIME_TYPE_JPEG, %CAIRO_MIME_TYPE_PNG, + * %CAIRO_MIME_TYPE_URI. If any of them is specified, the SVG backend + * emits a href with the content of MIME data instead of a surface + * snapshot (PNG, Base64-encoded) in the corresponding image tag. + * + * The unofficial MIME type %CAIRO_MIME_TYPE_URI is examined + * first. If present, the URI is emitted as is: assuring the + * correctness of URI is left to the client code. + * + * If %CAIRO_MIME_TYPE_URI is not present, but %CAIRO_MIME_TYPE_JPEG + * or %CAIRO_MIME_TYPE_PNG is specified, the corresponding data is + * Base64-encoded and emitted. + * + * If %CAIRO_MIME_TYPE_UNIQUE_ID is present, all surfaces with the same + * unique identifier will only be embedded once. + * + * Note that this function takes ownership of @fd. + * + * Return value: a pointer to the newly created surface. The caller + * owns the surface and should call cairo_surface_destroy() when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" surface if an error such as out of memory + * occurs. You can use cairo_surface_status() to check for this. + * + * Since: 1.16 + **/ +cairo_surface_t * +cairo_svg_surface_create_for_fd (int fd, + double width, + double height) +{ + cairo_output_stream_t *stream; + + stream = _cairo_output_stream_create_for_fd (fd); + if (_cairo_output_stream_get_status (stream)) + return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream)); + + return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1); +} + static cairo_bool_t _cairo_surface_is_svg (cairo_surface_t *surface) { diff --git a/src/cairo-svg.h b/src/cairo-svg.h index 4d24857..3dd80a3 100644 --- a/src/cairo-svg.h +++ b/src/cairo-svg.h @@ -98,6 +98,11 @@ cairo_svg_surface_create (const char *filename, double width_in_points, double height_in_points); +cairo_public cairo_surface_t * +cairo_svg_surface_create_for_fd (int fd, + double width_in_points, + double height_in_points); + cairo_public cairo_surface_t * cairo_svg_surface_create_for_stream (cairo_write_func_t write_func, void *closure, diff --git a/src/cairo-xml-surface.c b/src/cairo-xml-surface.c index 35773a2..8c41f02 100644 --- a/src/cairo-xml-surface.c +++ b/src/cairo-xml-surface.c @@ -1127,6 +1127,27 @@ cairo_xml_create (const char *filename) return _cairo_xml_create_internal (stream); } +/** + * cairo_xml_create_for_fd: + * @fd: + * + * Note that this function takes ownership of @fd. + * + * Since: 1.16 + */ +cairo_device_t * +cairo_xml_create_for_fd (int fd) +{ + cairo_output_stream_t *stream; + cairo_status_t status; + + stream = _cairo_output_stream_create_for_fd (fd); + if ((status = _cairo_output_stream_get_status (stream))) + return _cairo_device_create_in_error (status); + + return _cairo_xml_create_internal (stream); +} + cairo_device_t * cairo_xml_create_for_stream (cairo_write_func_t write_func, void *closure) diff --git a/src/cairo-xml.h b/src/cairo-xml.h index 9ae76e9..0644e97 100644 --- a/src/cairo-xml.h +++ b/src/cairo-xml.h @@ -45,6 +45,9 @@ CAIRO_BEGIN_DECLS cairo_public cairo_device_t * cairo_xml_create (const char *filename); +cairo_public cairo_device_t * +cairo_xml_create_for_fd (int fd); + cairo_public cairo_device_t * cairo_xml_create_for_stream (cairo_write_func_t write_func, void *closure); diff --git a/src/cairo.h b/src/cairo.h index b2386af..dcb35fe 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -2435,6 +2435,10 @@ cairo_public cairo_status_t cairo_surface_write_to_png (cairo_surface_t *surface, const char *filename); +cairo_public cairo_status_t +cairo_surface_write_to_png_fd (cairo_surface_t *surface, + int fd); + cairo_public cairo_status_t cairo_surface_write_to_png_stream (cairo_surface_t *surface, cairo_write_func_t write_func, @@ -2577,6 +2581,9 @@ cairo_image_surface_get_stride (cairo_surface_t *surface); cairo_public cairo_surface_t * cairo_image_surface_create_from_png (const char *filename); +cairo_public cairo_surface_t * +cairo_image_surface_create_from_png_fd (int fd); + cairo_public cairo_surface_t * cairo_image_surface_create_from_png_stream (cairo_read_func_t read_func, void *closure); -- 2.9.4