Subject: bug#64908: 29.1; svg parse failure

From: Alan Third <[email-masked]>
Date: Sat, 5 Aug 2023 10:58:26 +0100

On Sat, Aug 05, 2023 at 10:30:38AM +0200, David Ponce wrote:
> Hello,
> 
> I attached a better patch, because it is possible to have svg image
> definition that combines viewBox, and percentage of width/height, like
> in this example:
> 
> (insert-image
>   '(image :type svg :data
>      "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 22 22\" width=\"110%\" height=\"80%\">
>   <defs id=\"defs3051\">
>     <style type=\"text/css\" id=\"current-color-scheme\">
>       .ColorScheme-Text {
>         color:#232629;
>       }
>       </style>
>   </defs>
>  <path
>     style=\"fill:currentColor;fill-opacity:1;stroke:none\"
>     d=\"m7.707031 3l-.707031.707031 6.125 6.125 1.167969 1.167969-1.167969 1.167969-6.125 6.125.707031.707031 6.125-6.125 1.875-1.875-1.875-1.875-6.125-6.125\"
>     class=\"ColorScheme-Text\"
>     />
> </svg>"
>     :scale 1))
> 
> I hope Alan will find a simpler implementation for all these cases!

Hi David, I did a bit of thinking about what a percentage in the width
or height attributes actually means and how librsvg handles it.

I think we should always ignore it.

A percentage is intended as a percentage of the enclosing box. this
means if we set an image width of 100 pixels in Emacs, and the width
in the SVG is 50%, the image should be 50 pixels wide.

However we requested a width of 100 pixels, and Emacs will give us
that no matter what, even scaling it back up.

Additionally, librsvg will scale the drawn image to a width of 50%
anyway.

What this means is if we set the image width to 100 pixels, librsvg
will scale its drawing down by 50% and it should come out the correct
size within the 100 pixel wide image.

I hope that made sense, it took me a bit of thinking about it before I
came to this conclusion. It was really the fact that librsvg scales
its drawing anyway that made me decide there's no point in us also
scaling it.

I've attached a patch, can you give it a go and let me know how it
works.

Or if you disagree with my analysis above, let me know, because I'm
not 100% sure of it.
-- 
Alan Third
>From 5f300dcfcb25e727ceb2da8c7175cbb331bb0546 Mon Sep 17 00:00:00 2001
From: Alan Third <[email-masked]>
Date: Sat, 5 Aug 2023 10:39:31 +0100
Subject: [PATCH] Fix percentage sizes in SVG display (bug#64908)

* src/image.c (svg_css_length_to_pixels): Make percent units always
return zero.
(svg_load_image): Don't rely on the width and height values from the
SVG actually having any useful data, even if they're explicitly set.
---
 src/image.c | 55 +++++++++++++++++++++++++++++++----------------------
 1 file changed, 32 insertions(+), 23 deletions(-)

diff --git a/src/image.c b/src/image.c
index c9420b48f4a..96e0f2a7225 100644
--- a/src/image.c
+++ b/src/image.c
@@ -11161,6 +11161,21 @@ svg_css_length_to_pixels (RsvgLength length, double dpi, int font_size)
     case RSVG_UNIT_IN:
       value *= dpi;
       break;
+    case RSVG_UNIT_PERCENT:
+      /* Percent is a ratio of the containing "viewport".  We don't
+	 have a viewport, as such, as we try to draw the image to it's
+	 own desired size rather than dictate the size as if we were
+	 drawing icons on a toolbar or similar.  This means that
+	 percent values are useless to us and we are best off just
+	 drawing the image according to whatever other sizes we can
+	 derive.
+
+	 If we do set explicit width and height values in the image
+	 spec, this will work out correctly as librsvg will still
+	 honour the percentage sizes in its final rendering no matter
+	 what size we make the image.  */
+      value = 0;
+      break;
 #if LIBRSVG_CHECK_VERSION (2, 48, 0)
       /* We don't know exactly what font size is used on older librsvg
 	 versions.  */
@@ -11169,7 +11184,7 @@ svg_css_length_to_pixels (RsvgLength length, double dpi, int font_size)
       break;
 #endif
     default:
-      /* Probably ex or %.  We can't know what the pixel value is
+      /* Probably ex.  We can't know what the pixel value is
          without more information.  */
       value = 0;
     }
@@ -11302,6 +11317,7 @@ svg_load_image (struct frame *f, struct image *img, char *contents,
   else
     {
       RsvgRectangle zero_rect, viewbox, out_logical_rect;
+      double explicit_width = 0, explicit_height = 0;
 
       /* Try the intrinsic dimensions first.  */
       gboolean has_width, has_height;
@@ -11313,34 +11329,27 @@ svg_load_image (struct frame *f, struct image *img, char *contents,
 					    &has_height, &iheight,
 					    &has_viewbox, &viewbox);
 
-      if (has_width && has_height)
-	{
-	  /* Success!  We can use these values directly.  */
-	  viewbox_width = svg_css_length_to_pixels (iwidth, dpi,
+      if (has_width)
+	explicit_width = svg_css_length_to_pixels (iwidth, dpi,
+						   img->face_font_size);
+      if (has_height)
+	explicit_height = svg_css_length_to_pixels (iheight, dpi,
 						    img->face_font_size);
-	  viewbox_height = svg_css_length_to_pixels (iheight, dpi,
-						     img->face_font_size);
 
-	  /* Here one dimension could be zero because in percent unit.
-	     So calculate this dimension with the other.  */
-	  if (! (0 < viewbox_width) && (iwidth.unit == RSVG_UNIT_PERCENT))
-	    viewbox_width = (viewbox_height * viewbox.width / viewbox.height)
-	      * iwidth.length;
-	  else if (! (0 < viewbox_height) && (iheight.unit == RSVG_UNIT_PERCENT))
-	    viewbox_height = (viewbox_width * viewbox.height / viewbox.width)
-	      * iheight.length;
+      if (explicit_width > 0 && explicit_height > 0)
+	{
+	  viewbox_width = explicit_width;
+	  viewbox_height = explicit_height;
 	}
-      else if (has_width && has_viewbox)
+      else if (explicit_width > 0 && has_viewbox)
 	{
-	  viewbox_width = svg_css_length_to_pixels (iwidth, dpi,
-						    img->face_font_size);
-	  viewbox_height = viewbox_width * viewbox.height / viewbox.width;
+	  viewbox_width = explicit_width;
+	  viewbox_height = explicit_width * viewbox.height / viewbox.width;
 	}
-      else if (has_height && has_viewbox)
+      else if (explicit_height > 0 && has_viewbox)
 	{
-	  viewbox_height = svg_css_length_to_pixels (iheight, dpi,
-						     img->face_font_size);
-	  viewbox_width = viewbox_height * viewbox.width / viewbox.height;
+	  viewbox_height = explicit_height;
+	  viewbox_width = explicit_height * viewbox.width / viewbox.height;
 	}
       else if (has_viewbox)
 	{
-- 
2.40.1