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.
This commit is contained in:
parent
40842f67af
commit
1fdbeffe3a
4 changed files with 105 additions and 15 deletions
|
@ -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.
|
||||
|
|
9
etc/NEWS
9
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
|
||||
|
||||
+++
|
||||
|
|
|
@ -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'. */
|
||||
|
|
84
src/image.c
84
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. */
|
||||
|
|
Loading…
Add table
Reference in a new issue