Re: Inline SVG images and the need to prefix identifiers
Christian von Schultz <[email protected]>
| Newsgroups | gmane.comp.lib.cairo |
|---|---|
| Message-ID | <[email protected]> |
On Fri, 2015-10-23 at 01:09 -0700, Bryce Harrington wrote: > Sounds reasonable, I'd definitely entertain a patch to add this to > cairo if you'd like to submit one. OK, here is a patch that does the job for me. /Christian -- cairo mailing list [email protected] http://lists.cairographics.org/mailman/listinfo/cairo
0001-cairo-svg-Added-cairo_svg_surface_set_id_attribute_p.patch
(text/x-patch, 14.6 KB)
From 3b20b17533883d7f8e9181ae336e6103948d7293 Mon Sep 17 00:00:00 2001 From: Christian von Schultz <[email protected]> Date: Fri, 23 Oct 2015 15:05:29 +0200 Subject: [PATCH] cairo-svg: Added cairo_svg_surface_set_id_attribute_prefix() and related code. Inserts document->idprefix in every id attribute of the generated SVG file. This is useful if several SVG files will be merged in a single XML document (e.g. XHTML with inline SVG): You can prevent otherwise inevitable id clashes by giving the different SVG files different idprefix values. --- src/cairo-svg-surface.c | 134 ++++++++++++++++++++++++++++++++++++------------ src/cairo-svg.h | 4 ++ 2 files changed, 106 insertions(+), 32 deletions(-) diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c index 2e023b3..06a5248 100644 --- a/src/cairo-svg-surface.c +++ b/src/cairo-svg-surface.c @@ -139,6 +139,8 @@ struct cairo_svg_document { cairo_output_stream_t *xml_node_defs; cairo_output_stream_t *xml_node_glyphs; + char *idprefix; + unsigned int linear_pattern_id; unsigned int radial_pattern_id; unsigned int pattern_id; @@ -330,6 +332,36 @@ _extract_svg_surface (cairo_surface_t *surface, } /** + * cairo_svg_surface_set_id_attribute_prefix: + * @surface: a SVG #cairo_surface_t + * @prefix: a char* + * + * Inserts @prefix in every id attribute of the generated SVG file. + * This is useful if several SVG files will be merged in a single + * XML document (e.g. XHTML with inline SVG): You can prevent otherwise + * inevitable id clashes by giving the different SVG files different + * @prefix values. + **/ +cairo_status_t +cairo_svg_surface_set_id_attribute_prefix (cairo_surface_t *abstract_surface, + const char *prefix) +{ + cairo_svg_surface_t *surface = NULL; /* hide compiler warning */ + size_t size; + + if (! _extract_svg_surface (abstract_surface, &surface)) + return CAIRO_STATUS_SURFACE_TYPE_MISMATCH; + + size = strlen(prefix) + 1; + surface->document->idprefix = realloc (surface->document->idprefix, size); + if (unlikely (surface->document->idprefix == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + strncpy(surface->document->idprefix, prefix, size); + + return CAIRO_STATUS_SUCCESS; +} + +/** * cairo_svg_surface_restrict_to_version: * @surface: a SVG #cairo_surface_t * @version: SVG version @@ -444,8 +476,9 @@ _cairo_svg_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper return CAIRO_STATUS_SUCCESS; _cairo_output_stream_printf (document->xml_node_defs, - "<clipPath id=\"clip%d\">\n" + "<clipPath id=\"%sclip%d\">\n" " <path ", + document->idprefix, document->clip_id); _cairo_svg_surface_emit_path (document->xml_node_defs, path, NULL); @@ -454,8 +487,9 @@ _cairo_svg_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper "</clipPath>\n"); _cairo_output_stream_printf (surface->xml_node, - "<g clip-path=\"url(#clip%d)\" " + "<g clip-path=\"url(#%sclip%d)\" " "clip-rule=\"%s\">\n", + document->idprefix, document->clip_id, fill_rule == CAIRO_FILL_RULE_EVEN_ODD ? "evenodd" : "nonzero"); @@ -840,7 +874,8 @@ _cairo_svg_document_emit_glyph (cairo_svg_document_t *document, cairo_int_status_t status; _cairo_output_stream_printf (document->xml_node_glyphs, - "<symbol overflow=\"visible\" id=\"glyph%d-%d\">\n", + "<symbol overflow=\"visible\" id=\"%sglyph%d-%d\">\n", + document->idprefix, font_id, subset_glyph_index); @@ -1026,14 +1061,15 @@ _cairo_svg_surface_emit_alpha_filter (cairo_svg_document_t *document) return; _cairo_output_stream_printf (document->xml_node_defs, - "<filter id=\"alpha\" " + "<filter id=\"%salpha\" " "filterUnits=\"objectBoundingBox\" " "x=\"0%%\" y=\"0%%\" " "width=\"100%%\" height=\"100%%\">\n" " <feColorMatrix type=\"matrix\" " "in=\"SourceGraphic\" " "values=\"0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0\"/>\n" - "</filter>\n"); + "</filter>\n", + document->idprefix); document->alpha_filter = TRUE; } @@ -1300,7 +1336,8 @@ _cairo_svg_surface_emit_surface (cairo_svg_document_t *document, assert (is_bounded); _cairo_output_stream_printf (document->xml_node_defs, - "<image id=\"image%d\" width=\"%d\" height=\"%d\"", + "<image id=\"%simage%d\" width=\"%d\" height=\"%d\"", + document->idprefix, surface->unique_id, extents.width, extents.height); @@ -1356,9 +1393,10 @@ _cairo_svg_surface_emit_composite_surface_pattern (cairo_output_stream_t *outp assert (is_bounded); _cairo_output_stream_printf (output, - "<pattern id=\"pattern%d\" " + "<pattern id=\"%spattern%d\" " "patternUnits=\"userSpaceOnUse\" " "width=\"%d\" height=\"%d\" ", + svg_surface->document->idprefix, pattern_id, extents.width, extents.height); _cairo_svg_surface_emit_transform (output, @@ -1368,7 +1406,8 @@ _cairo_svg_surface_emit_composite_surface_pattern (cairo_output_stream_t *outp } _cairo_output_stream_printf (output, - "<use xlink:href=\"#image%d\"", + "<use xlink:href=\"#%simage%d\"", + svg_surface->document->idprefix, pattern->surface->unique_id); if (extra_attributes) _cairo_output_stream_printf (output, " %s", extra_attributes); @@ -1437,9 +1476,10 @@ _cairo_svg_surface_emit_recording_surface (cairo_svg_document_t *document, if (! svg_surface->is_base_clip_emitted) { svg_surface->is_base_clip_emitted = TRUE; _cairo_output_stream_printf (document->xml_node_defs, - "<clipPath id=\"clip%d\">\n" + "<clipPath id=\"%sclip%d\">\n" " <rect width=\"%f\" height=\"%f\"/>\n" "</clipPath>\n", + document->idprefix, svg_surface->base_clip, svg_surface->width, svg_surface->height); @@ -1448,16 +1488,21 @@ _cairo_svg_surface_emit_recording_surface (cairo_svg_document_t *document, if (source->base.content == CAIRO_CONTENT_ALPHA) { _cairo_svg_surface_emit_alpha_filter (document); _cairo_output_stream_printf (document->xml_node_defs, - "<g id=\"surface%d\" " - "clip-path=\"url(#clip%d)\" " - "filter=\"url(#alpha)\">\n", + "<g id=\"%ssurface%d\" " + "clip-path=\"url(#%sclip%d)\" " + "filter=\"url(#%salpha)\">\n", + document->idprefix, source->base.unique_id, - svg_surface->base_clip); + document->idprefix, + svg_surface->base_clip, + document->idprefix); } else { _cairo_output_stream_printf (document->xml_node_defs, - "<g id=\"surface%d\" " - "clip-path=\"url(#clip%d)\">\n", + "<g id=\"%ssurface%d\" " + "clip-path=\"url(#%sclip%d)\">\n", + document->idprefix, source->base.unique_id, + document->idprefix, svg_surface->base_clip); } @@ -1529,9 +1574,10 @@ _cairo_svg_surface_emit_composite_recording_pattern (cairo_output_stream_t *outp if (pattern_id != invalid_pattern_id) { _cairo_output_stream_printf (output, - "<pattern id=\"pattern%d\" " + "<pattern id=\"%spattern%d\" " "patternUnits=\"userSpaceOnUse\" " "width=\"%d\" height=\"%d\"", + document->idprefix, pattern_id, recording_surface->extents.width, recording_surface->extents.height); @@ -1540,7 +1586,8 @@ _cairo_svg_surface_emit_composite_recording_pattern (cairo_output_stream_t *outp } _cairo_output_stream_printf (output, - "<use xlink:href=\"#surface%d\"", + "<use xlink:href=\"#%ssurface%d\"", + document->idprefix, recording_surface->base.unique_id); if (pattern_id == invalid_pattern_id) { @@ -1620,8 +1667,9 @@ _cairo_svg_surface_emit_surface_pattern (cairo_svg_surface_t *surface, return status; _cairo_output_stream_printf (style, - "%s:url(#pattern%d);", + "%s:url(#%spattern%d);", is_stroke ? "stroke" : "fill", + document->idprefix, pattern_id); return CAIRO_STATUS_SUCCESS; @@ -1824,9 +1872,10 @@ _cairo_svg_surface_emit_linear_pattern (cairo_svg_surface_t *surface, assert (status == CAIRO_STATUS_SUCCESS); _cairo_output_stream_printf (document->xml_node_defs, - "<linearGradient id=\"linear%d\" " + "<linearGradient id=\"%slinear%d\" " "gradientUnits=\"userSpaceOnUse\" " "x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" ", + document->idprefix, document->linear_pattern_id, pattern->pd1.x, pattern->pd1.y, pattern->pd2.x, pattern->pd2.y); @@ -1845,8 +1894,9 @@ _cairo_svg_surface_emit_linear_pattern (cairo_svg_surface_t *surface, "</linearGradient>\n"); _cairo_output_stream_printf (style, - "%s:url(#linear%d);", + "%s:url(#%slinear%d);", is_stroke ? "stroke" : "fill", + document->idprefix, document->linear_pattern_id); document->linear_pattern_id++; @@ -1898,10 +1948,11 @@ _cairo_svg_surface_emit_radial_pattern (cairo_svg_surface_t *surface, unsigned int n_stops = pattern->base.n_stops; _cairo_output_stream_printf (document->xml_node_defs, - "<radialGradient id=\"radial%d\" " + "<radialGradient id=\"%sradial%d\" " "gradientUnits=\"userSpaceOnUse\" " "cx=\"%f\" cy=\"%f\" " "fx=\"%f\" fy=\"%f\" r=\"%f\" ", + document->idprefix, document->radial_pattern_id, x1, y1, x1, y1, r1); @@ -1979,10 +2030,11 @@ _cairo_svg_surface_emit_radial_pattern (cairo_svg_surface_t *surface, } _cairo_output_stream_printf (document->xml_node_defs, - "<radialGradient id=\"radial%d\" " + "<radialGradient id=\"%sradial%d\" " "gradientUnits=\"userSpaceOnUse\" " "cx=\"%f\" cy=\"%f\" " "fx=\"%f\" fy=\"%f\" r=\"%f\" ", + document->idprefix, document->radial_pattern_id, x1, y1, fx, fy, r1); @@ -2027,8 +2079,9 @@ _cairo_svg_surface_emit_radial_pattern (cairo_svg_surface_t *surface, "</radialGradient>\n"); _cairo_output_stream_printf (style, - "%s:url(#radial%d);", + "%s:url(#%sradial%d);", is_stroke ? "stroke" : "fill", + document->idprefix, document->radial_pattern_id); document->radial_pattern_id++; @@ -2427,10 +2480,14 @@ _cairo_svg_surface_mask (void *abstract_surface, mask_id = _cairo_svg_document_allocate_mask_id (document); _cairo_output_stream_printf (mask_stream, - "<mask id=\"mask%d\">\n" - "%s", - mask_id, - discard_filter ? "" : " <g filter=\"url(#alpha)\">\n"); + "<mask id=\"%smask%d\">\n", + document->idprefix, + mask_id); + if (!discard_filter) { + _cairo_output_stream_printf (mask_stream, + " <g filter=\"url(#%salpha)\">\n", + document->idprefix); + } status = _cairo_svg_surface_emit_paint (mask_stream, surface, CAIRO_OPERATOR_OVER, mask, source, NULL); if (unlikely (status)) { cairo_status_t ignore = _cairo_output_stream_destroy (mask_stream); @@ -2448,7 +2505,8 @@ _cairo_svg_surface_mask (void *abstract_surface, if (unlikely (status)) return status; - snprintf (buffer, sizeof buffer, "mask=\"url(#mask%d)\"", + snprintf (buffer, sizeof buffer, "mask=\"url(#%smask%d)\"", + document->idprefix, mask_id); status = _cairo_svg_surface_emit_paint (surface->xml_node, surface, op, source, 0, buffer); if (unlikely (status)) @@ -2558,8 +2616,9 @@ _cairo_svg_surface_show_glyphs (void *abstract_surface, return status; _cairo_output_stream_printf (surface->xml_node, - " <use xlink:href=\"#glyph%d-%d\" " + " <use xlink:href=\"#%sglyph%d-%d\" " "x=\"%f\" y=\"%f\"/>\n", + document->idprefix, subset_glyph.font_id, subset_glyph.subset_glyph_index, glyphs[i].x, glyphs[i].y); @@ -2663,11 +2722,17 @@ _cairo_svg_document_create (cairo_output_stream_t *output_stream, if (unlikely (document == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); + document->idprefix = strdup(""); + if (unlikely (document->idprefix == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_DOCUMENT; + } + /* The use of defs for font glyphs imposes no per-subset limit. */ document->font_subsets = _cairo_scaled_font_subsets_create_scaled (); if (unlikely (document->font_subsets == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_DOCUMENT; + goto CLEANUP_IDPREFIX; } document->output_stream = output_stream; @@ -2706,6 +2771,8 @@ _cairo_svg_document_create (cairo_output_stream_t *output_stream, CLEANUP_NODE_DEFS: status_ignored = _cairo_output_stream_destroy (document->xml_node_defs); _cairo_scaled_font_subsets_destroy (document->font_subsets); + CLEANUP_IDPREFIX: + free (document->idprefix); CLEANUP_DOCUMENT: free (document); return status; @@ -2736,6 +2803,7 @@ _cairo_svg_document_destroy (cairo_svg_document_t *document) status = _cairo_svg_document_finish (document); + free (document->idprefix); free (document); return status; @@ -2817,7 +2885,8 @@ _cairo_svg_document_finish (cairo_svg_document_t *document) page = _cairo_array_index (&surface->page_set, i); _cairo_output_stream_printf (output, "<page>\n"); _cairo_output_stream_printf (output, - "<g id=\"surface%d\">\n", + "<g id=\"%ssurface%d\">\n", + document->idprefix, page->surface_id); _cairo_memory_stream_copy (page->xml_node, output); _cairo_output_stream_printf (output, "</g>\n</page>\n"); @@ -2826,7 +2895,8 @@ _cairo_svg_document_finish (cairo_svg_document_t *document) } else if (surface->page_set.num_elements > 0) { page = _cairo_array_index (&surface->page_set, surface->page_set.num_elements - 1); _cairo_output_stream_printf (output, - "<g id=\"surface%d\">\n", + "<g id=\"%ssurface%d\">\n", + document->idprefix, page->surface_id); _cairo_memory_stream_copy (page->xml_node, output); _cairo_output_stream_printf (output, "</g>\n"); diff --git a/src/cairo-svg.h b/src/cairo-svg.h index 592c645..71ac4f5 100644 --- a/src/cairo-svg.h +++ b/src/cairo-svg.h @@ -64,6 +64,10 @@ cairo_svg_surface_create_for_stream (cairo_write_func_t write_func, double width_in_points, double height_in_points); +cairo_public cairo_status_t +cairo_svg_surface_set_id_attribute_prefix (cairo_surface_t *abstract_surface, + const char *prefix); + cairo_public void cairo_svg_surface_restrict_to_version (cairo_surface_t *surface, cairo_svg_version_t version); -- 2.4.10