From b37e251848408f32a9f648047e072de74c24c376 Mon Sep 17 00:00:00 2001
From: Christian Persch <chpe@src.gnome.org>
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 id="index-1.14" role="1.14">
     <title>Index of new symbols in 1.14</title>
   </index>
+  <index id="index-1.16" role="1.16">
+    <title>Index of new symbols in 1.16</title>
+  </index>
   <xi:include href="language-bindings.xml"/>
 </book>
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
 </SECTION>
@@ -99,6 +102,7 @@ cairo_surface_write_to_png_stream
 <FILE>cairo-ps</FILE>
 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
 <FILE>cairo-svg</FILE>
 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
 <FILE>cairo-script</FILE>
 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

