emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master 1fdbeff 3/4: Set CSS for SVG files


From: Alan Third
Subject: master 1fdbeff 3/4: Set CSS for SVG files
Date: Sat, 3 Apr 2021 18:27:54 -0400 (EDT)

branch: master
commit 1fdbeffe3a65cb23abb43a4ea59df9553c9246f9
Author: Alan Third <alan@idiocy.org>
Commit: Alan Third <alan@idiocy.org>

    Set CSS for SVG files
    
    * src/dispextern.h (struct image): Add font details required for the CSS.
    * src/image.c (free_image): Free the font family string.
    (search_image_cache):
    (uncache_image): Make image caching understand the font details.
    (lookup_image): Handle the font details when generating the image and
    looking up the cache.
    (svg_css_length_to_pixels): Handle 'em' when we know the font size.
    (svg_load_image): Generate the CSS and apply it to the SVG.
    (enum svg_keyword_index):
    (svg_format):
    (syms_of_image): Add ':css' attribute.
    * doc/lispref/display.texi (SVG Images): Add details of new svg image
    attributes.
---
 doc/lispref/display.texi | 22 +++++++++++++
 etc/NEWS                 |  9 ++++++
 src/dispextern.h         |  5 +++
 src/image.c              | 84 +++++++++++++++++++++++++++++++++++++++---------
 4 files changed, 105 insertions(+), 15 deletions(-)

diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index 2e1b4a6..68d7e82 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -5751,6 +5751,28 @@ Cropping is performed after scaling but before rotation.
 @cindex SVG images
 
 SVG (Scalable Vector Graphics) is an XML format for specifying images.
+SVG images support the following additional image descriptor
+properties:
+
+@table @code
+@item :foreground @var{foreground}
+@var{foreground}, if non-@code{nil}, should be a string specifying a
+color, which is used as the image's foreground color.  If the value is
+@code{nil}, it defaults to the faces's foreground color.
+
+@item :background @var{background}
+@var{background}, if non-@code{nil}, should be a string specifying a
+color, which is used as the image's background color if the image
+supports transparency.  If the value is @code{nil}, it defaults to the
+faces's background color.
+
+@item :css @var{css}
+@var{css}, if non-@code{nil}, should be a string specifying the CSS to
+override the default CSS used when generating the image.
+@end table
+
+@subsubheading SVG library
+
 If your Emacs build has SVG support, you can create and manipulate
 these images with the following functions from the @file{svg.el}
 library.
diff --git a/etc/NEWS b/etc/NEWS
index 2d66a93..c93e2e7 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1511,6 +1511,15 @@ This controls whether to use smoothing or not for an 
image.  Values
 include nil (no smoothing), t (do smoothing) or a predicate function
 that's called with the image object and should return nil/t.
 
++++
+*** SVG images now support user stylesheets.
+The ':css' image attribute can be used to override the default CSS
+stylesheet for an image.  The default sets 'font-family' and
+'font-size' to match the current face, so an image with 'height="1em"'
+will match the font size in use where it is embedded.
+
+This feature relies on librsvg 2.48 or above being available.
+
 ** EWW
 
 +++
diff --git a/src/dispextern.h b/src/dispextern.h
index f4e8726..a2ebd04 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -3066,6 +3066,11 @@ struct image
      is created.  */
   unsigned long face_foreground, face_background;
 
+  /* Details of the font, only really relevant for types like SVG that
+     allow us to draw text. */
+  int face_font_size;
+  char *face_font_family;
+
   /* True if this image has a `transparent' background -- that is, is
      uses an image mask.  The accessor macro for this is
      `IMAGE_BACKGROUND_TRANSPARENT'.  */
diff --git a/src/image.c b/src/image.c
index 774b7e1..13bd503 100644
--- a/src/image.c
+++ b/src/image.c
@@ -1199,6 +1199,7 @@ free_image (struct frame *f, struct image *img)
 
       /* Free resources, then free IMG.  */
       img->type->free_img (f, img);
+      xfree (img->face_font_family);
       xfree (img);
     }
 }
@@ -1597,7 +1598,7 @@ make_image_cache (void)
 static struct image *
 search_image_cache (struct frame *f, Lisp_Object spec, EMACS_UINT hash,
                     unsigned long foreground, unsigned long background,
-                    bool ignore_colors)
+                    int font_size, char *font_family, bool ignore_colors)
 {
   struct image *img;
   struct image_cache *c = FRAME_IMAGE_CACHE (f);
@@ -1621,7 +1622,10 @@ search_image_cache (struct frame *f, Lisp_Object spec, 
EMACS_UINT hash,
     if (img->hash == hash
        && !NILP (Fequal (img->spec, spec))
        && (ignore_colors || (img->face_foreground == foreground
-                              && img->face_background == background)))
+                              && img->face_background == background
+                             && img->face_font_size == font_size
+                             && (font_family
+                                 &&!strcmp (font_family, 
img->face_font_family)))))
       break;
   return img;
 }
@@ -1639,7 +1643,7 @@ uncache_image (struct frame *f, Lisp_Object spec)
      can have multiple copies of an image with the same spec. We want
      to remove them all to ensure the user doesn't see an old version
      of the image when the face changes.  */
-  while ((img = search_image_cache (f, spec, hash, 0, 0, true)))
+  while ((img = search_image_cache (f, spec, hash, 0, 0, 0, NULL, true)))
     {
       free_image (f, img);
       /* As display glyphs may still be referring to the image ID, we
@@ -2411,6 +2415,8 @@ lookup_image (struct frame *f, Lisp_Object spec, int 
face_id)
   struct face *face = FACE_FROM_ID (f, face_id);
   unsigned long foreground = FACE_COLOR_TO_PIXEL (face->foreground, f);
   unsigned long background = FACE_COLOR_TO_PIXEL (face->background, f);
+  int font_size = face->font->pixel_size;
+  char *font_family = SSDATA (face->lface[LFACE_FAMILY_INDEX]);
 
   /* F must be a window-system frame, and SPEC must be a valid image
      specification.  */
@@ -2419,7 +2425,8 @@ lookup_image (struct frame *f, Lisp_Object spec, int 
face_id)
 
   /* Look up SPEC in the hash table of the image cache.  */
   hash = sxhash (spec);
-  img = search_image_cache (f, spec, hash, foreground, background, false);
+  img = search_image_cache (f, spec, hash, foreground, background,
+                           font_size, font_family, false);
   if (img && img->load_failed_p)
     {
       free_image (f, img);
@@ -2434,6 +2441,9 @@ lookup_image (struct frame *f, Lisp_Object spec, int 
face_id)
       cache_image (f, img);
       img->face_foreground = foreground;
       img->face_background = background;
+      img->face_font_size = font_size;
+      img->face_font_family = malloc (strlen (font_family) + 1);
+      strcpy (img->face_font_family, font_family);
       img->load_failed_p = ! img->type->load_img (f, img);
 
       /* If we can't load the image, and we don't have a width and
@@ -9532,6 +9542,7 @@ enum svg_keyword_index
   SVG_DATA,
   SVG_FILE,
   SVG_BASE_URI,
+  SVG_CSS,
   SVG_ASCENT,
   SVG_MARGIN,
   SVG_RELIEF,
@@ -9552,6 +9563,7 @@ static const struct image_keyword svg_format[SVG_LAST] =
   {":data",            IMAGE_STRING_VALUE,                     0},
   {":file",            IMAGE_STRING_VALUE,                     0},
   {":base-uri",                IMAGE_STRING_VALUE,                     0},
+  {":css",             IMAGE_STRING_VALUE,                     0},
   {":ascent",          IMAGE_ASCENT_VALUE,                     0},
   {":margin",          IMAGE_NON_NEGATIVE_INTEGER_VALUE_OR_PAIR, 0},
   {":relief",          IMAGE_INTEGER_VALUE,                    0},
@@ -9838,7 +9850,7 @@ svg_load (struct frame *f, struct image *img)
 
 #if LIBRSVG_CHECK_VERSION (2, 46, 0)
 static double
-svg_css_length_to_pixels (RsvgLength length, double dpi)
+svg_css_length_to_pixels (RsvgLength length, double dpi, int font_size)
 {
   double value = length.length;
 
@@ -9866,9 +9878,16 @@ svg_css_length_to_pixels (RsvgLength length, double dpi)
     case RSVG_UNIT_IN:
       value *= dpi;
       break;
+#if LIBRSVG_CHECK_VERSION (2, 48, 0)
+      /* We don't know exactly what font size is used on older librsvg
+        versions.  */
+    case RSVG_UNIT_EM:
+      value *= font_size;
+      break;
+#endif
     default:
-      /* Probably one of em, ex, or %.  We can't know what the pixel
-         value is without more information.  */
+      /* Probably ex or %.  We can't know what the pixel value is
+         without more information.  */
       value = 0;
     }
 
@@ -9923,6 +9942,27 @@ svg_load_image (struct frame *f, struct image *img, char 
*contents,
 
   rsvg_handle_set_dpi_x_y (rsvg_handle, FRAME_DISPLAY_INFO (f)->resx,
                            FRAME_DISPLAY_INFO (f)->resy);
+
+#if LIBRSVG_CHECK_VERSION (2, 48, 0)
+  char *css;
+  Lisp_Object lcss = image_spec_value (img->spec, QCcss, NULL);
+  if (!STRINGP (lcss))
+    {
+      /* Generate the CSS for the SVG image.  */
+      char *css_spec = "svg{font-family:\"%s\";font-size:%4dpx}";
+      int css_len = strlen (css_spec) + strlen (img->face_font_family);
+      css = xmalloc (css_len);
+      snprintf (css, css_len, css_spec, img->face_font_family, 
img->face_font_size);
+      rsvg_handle_set_stylesheet (rsvg_handle, css, strlen (css), NULL);
+    }
+  else
+    {
+      css = xmalloc (SBYTES (lcss) + 1);
+      strncpy (css, SSDATA (lcss), SBYTES (lcss));
+      *(css + SBYTES (lcss) + 1) = 0;
+    }
+#endif
+
 #else
   /* Make a handle to a new rsvg object.  */
   rsvg_handle = rsvg_handle_new ();
@@ -9965,20 +10005,20 @@ svg_load_image (struct frame *f, struct image *img, 
char *contents,
   if (has_width && has_height)
     {
       /* Success!  We can use these values directly.  */
-      viewbox_width = svg_css_length_to_pixels (iwidth, dpi);
-      viewbox_height = svg_css_length_to_pixels (iheight, dpi);
+      viewbox_width = svg_css_length_to_pixels (iwidth, dpi, 
img->face_font_size);
+      viewbox_height = svg_css_length_to_pixels (iheight, dpi, 
img->face_font_size);
     }
   else if (has_width && has_viewbox)
     {
-      viewbox_width = svg_css_length_to_pixels (iwidth, dpi);
-      viewbox_height = svg_css_length_to_pixels (iwidth, dpi)
-        * viewbox.width / viewbox.height;
+      viewbox_width = svg_css_length_to_pixels (iwidth, dpi, 
img->face_font_size);
+      viewbox_height = svg_css_length_to_pixels (iwidth, dpi, 
img->face_font_size)
+        * viewbox.height / viewbox.width;
     }
   else if (has_height && has_viewbox)
     {
-      viewbox_height = svg_css_length_to_pixels (iheight, dpi);
-      viewbox_width = svg_css_length_to_pixels (iheight, dpi)
-        * viewbox.height / viewbox.width;
+      viewbox_height = svg_css_length_to_pixels (iheight, dpi, 
img->face_font_size);
+      viewbox_width = svg_css_length_to_pixels (iheight, dpi, 
img->face_font_size)
+        * viewbox.width / viewbox.height;
     }
   else if (has_viewbox)
     {
@@ -10099,6 +10139,10 @@ svg_load_image (struct frame *f, struct image *img, 
char *contents,
 
   rsvg_handle_set_dpi_x_y (rsvg_handle, FRAME_DISPLAY_INFO (f)->resx,
                            FRAME_DISPLAY_INFO (f)->resy);
+
+#if LIBRSVG_CHECK_VERSION (2, 48, 0)
+  rsvg_handle_set_stylesheet (rsvg_handle, css, strlen (css), NULL);
+#endif
 #else
   /* Make a handle to a new rsvg object.  */
   rsvg_handle = rsvg_handle_new ();
@@ -10132,6 +10176,11 @@ svg_load_image (struct frame *f, struct image *img, 
char *contents,
   g_object_unref (rsvg_handle);
   xfree (wrapped_contents);
 
+#if LIBRSVG_CHECK_VERSION (2, 48, 0)
+  if (!STRINGP (lcss))
+    xfree (css);
+#endif
+
   /* Extract some meta data from the svg handle.  */
   width     = gdk_pixbuf_get_width (pixbuf);
   height    = gdk_pixbuf_get_height (pixbuf);
@@ -10202,6 +10251,10 @@ svg_load_image (struct frame *f, struct image *img, 
char *contents,
     g_object_unref (rsvg_handle);
   if (wrapped_contents)
     xfree (wrapped_contents);
+#if LIBRSVG_CHECK_VERSION (2, 48, 0)
+  if (css && !STRINGP (lcss))
+    xfree (css);
+#endif
   /* FIXME: Use error->message so the user knows what is the actual
      problem with the image.  */
   image_error ("Error parsing SVG image `%s'", img->spec);
@@ -10793,6 +10846,7 @@ non-numeric, there is no explicit limit on the size of 
images.  */);
 #if defined (HAVE_RSVG)
   DEFSYM (Qsvg, "svg");
   DEFSYM (QCbase_uri, ":base-uri");
+  DEFSYM (QCcss, ":css");
   add_image_type (Qsvg);
 #ifdef HAVE_NTGUI
   /* Other libraries used directly by svg code.  */



reply via email to

[Prev in Thread] Current Thread [Next in Thread]