[cairo] [PATCH] Enable links to image files in SVG backend

Alexander Shulgin alex.shulgin at gmail.com
Wed Jan 20 02:56:44 PST 2010


On Wed, Jan 20, 2010 at 12:18, Alexander Shulgin <alex.shulgin at gmail.com> wrote:
> On Wed, Jan 20, 2010 at 10:20, Carl Worth <cworth at cworth.org> wrote:
>>
>> Finally, after you've created one, (or more), commits, you can format
>> these as mail messages to be submitted with:
>>
>>      git format-patch origin/master
>>
>> Which will create one file for each commit you've created since the
>> origin/master commit.
>
> Oh, thanks, this seems to work better.  I've tried something like
> this, but not exactly, and it produced no output at all.

OK, now I've got this:

---
 doc/public/cairo-sections.txt               |    2 +
 doc/public/tmpl/cairo-surface.sgml          |   14 +++
 src/Makefile.am                             |    2 +-
 src/cairo-surface.c                         |   17 +++-
 src/cairo-svg-surface.c                     |  136 ++++++++++++++++++++++++++-
 src/cairo.h                                 |    2 +
 src/generate-uri-allowed-set-lookup-table.c |   27 ++++++
 src/uri-allowed-set-lookup-table.inc        |   35 +++++++
 8 files changed, 229 insertions(+), 6 deletions(-)
 create mode 100644 src/generate-uri-allowed-set-lookup-table.c
 create mode 100644 src/uri-allowed-set-lookup-table.inc

diff --git a/doc/public/cairo-sections.txt b/doc/public/cairo-sections.txt
index ff89db7..1e45c59 100644
--- a/doc/public/cairo-sections.txt
+++ b/doc/public/cairo-sections.txt
@@ -176,6 +176,8 @@ cairo_svg_version_to_string
 CAIRO_MIME_TYPE_JP2
 CAIRO_MIME_TYPE_JPEG
 CAIRO_MIME_TYPE_PNG
+CAIRO_MIME_TYPE_XURI
+CAIRO_MIME_TYPE_XURI_ENCODED
 cairo_surface_t
 cairo_content_t
 cairo_surface_create_similar
diff --git a/doc/public/tmpl/cairo-surface.sgml
b/doc/public/tmpl/cairo-surface.sgml
index 4503e40..3d0596e 100644
--- a/doc/public/tmpl/cairo-surface.sgml
+++ b/doc/public/tmpl/cairo-surface.sgml
@@ -48,6 +48,20 @@ The Portable Network Graphics image file format
(ISO/IEC 15948). Since 1.10



+<!-- ##### MACRO CAIRO_MIME_TYPE_XURI ##### -->
+<para>
+URI for an image file (unofficial MIME type). Since 1.10
+</para>
+
+
+
+<!-- ##### MACRO CAIRO_MIME_TYPE_XURI_ENCODED ##### -->
+<para>
+Percent-encoded URI for an image file (unofficial MIME type). Since 1.10
+</para>
+
+
+
 <!-- ##### TYPEDEF cairo_surface_t ##### -->
 <para>

diff --git a/src/Makefile.am b/src/Makefile.am
index b6f8c57..0da6cc0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -6,7 +6,7 @@ include $(srcdir)/Makefile.am.features
 EXTRA_DIST += Makefile.win32      Makefile.win32.features
 #MAINTAINERCLEANFILES += $(srcdir)/Makefile.win32.features

-EXTRA_DIST += glew/LICENSE.txt
+EXTRA_DIST += glew/LICENSE.txt  uri-allowed-set-lookup-table.inc

 AM_CPPFLAGS = -I$(srcdir) $(CAIRO_CFLAGS)
 AM_LDFLAGS = $(CAIRO_LDFLAGS)
diff --git a/src/cairo-surface.c b/src/cairo-surface.c
index 9aa707f..7ce6235 100644
--- a/src/cairo-surface.c
+++ b/src/cairo-surface.c
@@ -770,7 +770,7 @@ _cairo_mime_data_destroy (void *ptr)
 /**
  * cairo_surface_set_mime_data:
  * @surface: a #cairo_surface_t
- * @mime_type: the mime type of the image data
+ * @mime_type: the MIME type of the image data
  * @data: the image data to attach to the surface
  * @length: the length of the image data
  * @destroy: a #cairo_destroy_func_t which will be called when the
@@ -782,6 +782,21 @@ _cairo_mime_data_destroy (void *ptr)
  * the data from a surface, call this function with same mime type
  * and %NULL for @data.
  *
+ * The attached image data can be later used by backends which support
+ * it (currently: PDF, PS, SVG and Win32 Printing surfaces) to emit
+ * this data instead of making a snapshot of the @surface.  This
+ * approach tends to be faster and requires less memory and disk
+ * space.
+ *
+ * The recognized MIME types are the following: #CAIRO_MIME_TYPE_JPEG,
+ * #CAIRO_MIME_TYPE_PNG, #CAIRO_MIME_TYPE_JP2, #CAIRO_MIME_TYPE_XURI,
+ * #CAIRO_MIME_TYPE_XURI_ENCODED.
+ *
+ * See corresponding backend surface docs for details about which MIME
+ * types it can handle. Caution: the associated MIME data might be
+ * totally unrelated to the actual content of the @surface, especially
+ * if you draw on the @surface afterwards. Use this function with care.
+ *
  * Since: 1.10
  *
  * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a
diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c
index fa25b82..7964aa3 100644
--- a/src/cairo-svg-surface.c
+++ b/src/cairo-svg-surface.c
@@ -202,6 +202,33 @@ cairo_svg_surface_create_for_stream
(cairo_write_func_t		 write_func,
  * Creates a SVG surface of the specified size in points to be written
  * to @filename.
  *
+ * 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_XURI,
+ * #CAIRO_MIME_TYPE_XURI_ENCODED. 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 #CAIRO_MIME_TYPE_XURI_ENCODED MIME type takes
+ * precedence over any other MIME type associated with a surface. This
+ * MIME type is most suitable for complex URIs which may occur in a
+ * server application. The URI is emitted as is: assuring the
+ * correctness of URI is left to the client code.
+ *
+ * The unofficial #CAIRO_MIME_TYPE_XURI MIME type is examined if
+ * #CAIRO_MIME_TYPE_XURI_ENCODED is not specified. This data is used
+ * the same way, however any non-printable characters are
+ * percent-encoded according to RFC 3986 before emitting. This MIME
+ * type is most suitable for simple URIs like files on disk: the
+ * percent-encoding takes care of filenames which do not form valid
+ * URIs as is.
+ *
+ * If none of the above is specified, but #CAIRO_MIME_TYPE_JPEG or
+ * #CAIRO_MIME_TYPE_PNG is specified, the corresponding data is
+ * Base64-encoded and emitted.
+ *
  * Return value: a pointer to the newly created surface. The caller
  * owns the surface and should call cairo_surface_destroy() when done
  * with it.
@@ -1168,6 +1195,81 @@ _cairo_svg_surface_emit_operator_for_style
(cairo_output_stream_t *output,
     }
 }

+/**
+ * _cairo_svg_surface_emit_attr_value:
+ *
+ * Write the value to output the stream as a sequence of characters,
+ * while escaping those which have special meaning in the XML
+ * attribute's value context: &amp; and &quot;.
+ **/
+static void
+_cairo_svg_surface_emit_attr_value (cairo_output_stream_t *stream,
+				    const unsigned char *value,
+				    unsigned int length)
+{
+    const unsigned char *p;
+    const unsigned char *q;
+    unsigned int i;
+
+    /* we'll accumulate non-special chars in [q, p) range */
+    p = value;
+    q = p;
+    for (i = 0; i < length; i++, p++) {
+	if (*p == '&' || *p == '"') {
+	    /* flush what's left before special char */
+	    if (p != q) {
+		_cairo_output_stream_write (stream, q, p - q);
+		q = p + 1;
+	    }
+
+	    if (*p == '&')
+		_cairo_output_stream_printf (stream, "&amp;");
+	    else // p == '"'
+		_cairo_output_stream_printf (stream, "&quot;");
+	}
+    }
+
+    /* flush the trailing chars if any */
+    if (p != q)
+	_cairo_output_stream_write (stream, q, p - q);
+}
+
+/* Generated file. See generate-uri-allowed-set-lookup-table.c */
+#include "uri-allowed-set-lookup-table.inc"
+
+/**
+ * _cairo_svg_surface_uri_encode:
+ *
+ * Percent-encode the URI string. Percent sign itself is not in
+ * allowed set so it gets encoded.
+ *
+ * RFC: http://tools.ietf.org/html/rfc3986
+ **/
+static cairo_status_t
+_cairo_svg_surface_uri_encode (const unsigned char *uri, unsigned int length,
+			       unsigned char **enc, unsigned int *enc_len)
+{
+    unsigned char *p;
+    int i;
+
+    *enc = malloc (length*3 + 1 /* to fit nul-byte from last snprintf */);
+    if (!*enc)
+	return CAIRO_STATUS_NO_MEMORY;
+
+    p = *enc;
+    for (i = 0; i < length; i++)
+	if (!uri_allowed_set[uri[i]]) {
+	    snprintf (p, 4 /* "%XX\0" */, "%%%2X", uri[i]);
+	    p += 3;
+	} else {
+	    *(p++) = uri[i];
+	}
+
+    *enc_len = p - *enc;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
 static cairo_status_t
 _cairo_svg_surface_emit_surface (cairo_svg_document_t *document,
 				 cairo_surface_t *surface)
@@ -1175,6 +1277,8 @@ _cairo_svg_surface_emit_surface
(cairo_svg_document_t *document,
     cairo_rectangle_int_t extents;
     cairo_bool_t is_bounded;
     cairo_status_t status;
+    const unsigned char *uri;
+    unsigned int uri_len;

     if (_cairo_user_data_array_get_data (&surface->user_data,
 					 (cairo_user_data_key_t *) document))
@@ -1192,10 +1296,34 @@ _cairo_svg_surface_emit_surface
(cairo_svg_document_t *document,

     _cairo_output_stream_printf (document->xml_node_defs, " xlink:href=\"");

-    status = _cairo_surface_base64_encode (surface,
-					   document->xml_node_defs);
-    if (unlikely (status))
-	return status;
+    cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_XURI_ENCODED,
+				 &uri, &uri_len);
+    if (uri)
+	_cairo_svg_surface_emit_attr_value (document->xml_node_defs,
+					    uri, uri_len);
+    else {
+	cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_XURI,
+				     &uri, &uri_len);
+	if (uri) {
+	    unsigned char *enc;
+	    unsigned int enc_len;
+
+	    status = _cairo_svg_surface_uri_encode (uri, uri_len,
+						    &enc, &enc_len);
+	    if (unlikely (status))
+		return status;
+
+	    _cairo_svg_surface_emit_attr_value (document->xml_node_defs,
+						enc, enc_len);
+
+	    free (enc);
+	} else {
+	    status = _cairo_surface_base64_encode (surface,
+						   document->xml_node_defs);
+	    if (unlikely (status))
+		return status;
+	}
+    }

     _cairo_output_stream_printf (document->xml_node_defs, "\"/>\n");

diff --git a/src/cairo.h b/src/cairo.h
index 8be8043..16cafaf 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -2042,6 +2042,8 @@ cairo_surface_set_user_data (cairo_surface_t		 *surface,
 #define CAIRO_MIME_TYPE_JPEG "image/jpeg"
 #define CAIRO_MIME_TYPE_PNG "image/png"
 #define CAIRO_MIME_TYPE_JP2 "image/jp2"
+#define CAIRO_MIME_TYPE_XURI "text/x-uri"
+#define CAIRO_MIME_TYPE_XURI_ENCODED "text/x-uri-encoded"

 cairo_public void
 cairo_surface_get_mime_data (cairo_surface_t		*surface,
diff --git a/src/generate-uri-allowed-set-lookup-table.c
b/src/generate-uri-allowed-set-lookup-table.c
new file mode 100644
index 0000000..702a973
--- /dev/null
+++ b/src/generate-uri-allowed-set-lookup-table.c
@@ -0,0 +1,27 @@
+/* vim: set sw=4 sts=4: -*- Mode: c; tab-width: 8; c-basic-offset: 4;
indent-tabs-mode: t; -*- */
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+static const char *uri_allowed_set =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&'()*+,;=";
+
+int
+main ()
+{
+    int i;
+
+    printf ("/* %s */\n", uri_allowed_set);
+    printf ("static const char uri_allowed_set[256] = {\n 0,");
+    for (i = 1; i < 256; i++) {
+	printf (strchr (uri_allowed_set, (char) i) ? " 1" : " 0");
+
+	if (isgraph (i))
+	    printf (" /*%c*/", i);
+
+	putchar(',');
+	if (i % 8 == 7)
+	    putchar('\n');
+    }
+    printf ("};\n");
+    return 0;
+}
diff --git a/src/uri-allowed-set-lookup-table.inc
b/src/uri-allowed-set-lookup-table.inc
new file mode 100644
index 0000000..a6caa9d
--- /dev/null
+++ b/src/uri-allowed-set-lookup-table.inc
@@ -0,0 +1,35 @@
+/* ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&'()*+,;=
*/
+static const char uri_allowed_set[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1 /*!*/, 0 /*"*/, 1 /*#*/, 1 /*$*/, 0 /*%*/, 1 /*&*/, 1 /*'*/,
+ 1 /*(*/, 1 /*)*/, 1 /***/, 1 /*+*/, 1 /*,*/, 1 /*-*/, 1 /*.*/, 1 /*/*/,
+ 1 /*0*/, 1 /*1*/, 1 /*2*/, 1 /*3*/, 1 /*4*/, 1 /*5*/, 1 /*6*/, 1 /*7*/,
+ 1 /*8*/, 1 /*9*/, 1 /*:*/, 1 /*;*/, 0 /*<*/, 1 /*=*/, 0 /*>*/, 1 /*?*/,
+ 1 /*@*/, 1 /*A*/, 1 /*B*/, 1 /*C*/, 1 /*D*/, 1 /*E*/, 1 /*F*/, 1 /*G*/,
+ 1 /*H*/, 1 /*I*/, 1 /*J*/, 1 /*K*/, 1 /*L*/, 1 /*M*/, 1 /*N*/, 1 /*O*/,
+ 1 /*P*/, 1 /*Q*/, 1 /*R*/, 1 /*S*/, 1 /*T*/, 1 /*U*/, 1 /*V*/, 1 /*W*/,
+ 1 /*X*/, 1 /*Y*/, 1 /*Z*/, 1 /*[*/, 0 /*\*/, 1 /*]*/, 0 /*^*/, 1 /*_*/,
+ 0 /*`*/, 1 /*a*/, 1 /*b*/, 1 /*c*/, 1 /*d*/, 1 /*e*/, 1 /*f*/, 1 /*g*/,
+ 1 /*h*/, 1 /*i*/, 1 /*j*/, 1 /*k*/, 1 /*l*/, 1 /*m*/, 1 /*n*/, 1 /*o*/,
+ 1 /*p*/, 1 /*q*/, 1 /*r*/, 1 /*s*/, 1 /*t*/, 1 /*u*/, 1 /*v*/, 1 /*w*/,
+ 1 /*x*/, 1 /*y*/, 1 /*z*/, 0 /*{*/, 0 /*|*/, 0 /*}*/, 1 /*~*/, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+};
-- 
1.6.3.3

Is this looking any good? :)

--
Regards,
Alex


More information about the cairo mailing list