Re: Inline SVG images and the need to prefix identifiers

Bryce Harrington <[email protected]>
Newsgroups gmane.comp.lib.cairo
Message-ID <[email protected]>
On Sat, Oct 24, 2015 at 10:36:33AM +0200, Christian von Schultz wrote:
> On Fri, 2015-10-23 at 19:59 +0200, Uli Schlachter wrote:
> > This needs a "Since: 1.16".
> 
> New patch attached.
> 
> Cheers,
> Christian

> From c8acd6b6f73a8604e9d45c94fb562495f608a683 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 | 136 ++++++++++++++++++++++++++++++++++++------------
>  src/cairo-svg.h         |   4 ++
>  2 files changed, 108 insertions(+), 32 deletions(-)

Sorry I'm tardy about a year in commenting on this, but this does seem
like it would be a handy feature for the generated SVG files, where
the contents will be merged into another document.  I'd love to see
other people's thoughts.

I did a cursory look through the code and didn't spot any errors, and
agree with the general concept so:

Acked-by: Bryce Harrington <[email protected]>


> diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c
> index 2e023b3..9f3c111 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,38 @@ _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.
> + *
> + * Since: 1.16
> + **/
> +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 +478,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 +489,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 +876,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 +1063,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 +1338,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 +1395,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 +1408,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 +1478,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 +1490,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 +1576,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 +1588,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 +1669,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 +1874,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 +1896,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 +1950,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 +2032,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 +2081,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 +2482,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 +2507,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 +2618,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 +2724,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 +2773,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 +2805,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 +2887,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 +2897,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
> 

> -- 
> cairo mailing list
> [email protected]
> http://lists.cairographics.org/mailman/listinfo/cairo

-- 
cairo mailing list
[email protected]
https://lists.cairographics.org/mailman/listinfo/cairo
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.