diff options
| author | Alan Third | 2021-03-28 17:52:57 +0100 |
|---|---|---|
| committer | Alan Third | 2021-04-03 23:07:01 +0100 |
| commit | 1fdbeffe3a65cb23abb43a4ea59df9553c9246f9 (patch) | |
| tree | e0bf007c9aa559aa184c05ee795e8abe6d787db7 /src | |
| parent | 40842f67afb2931de6789237a49570f99b70404f (diff) | |
| download | emacs-1fdbeffe3a65cb23abb43a4ea59df9553c9246f9.tar.gz emacs-1fdbeffe3a65cb23abb43a4ea59df9553c9246f9.zip | |
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.
Diffstat (limited to 'src')
| -rw-r--r-- | src/dispextern.h | 5 | ||||
| -rw-r--r-- | src/image.c | 84 |
2 files changed, 74 insertions, 15 deletions
diff --git a/src/dispextern.h b/src/dispextern.h index f4e872644db..a2ebd04f235 100644 --- a/src/dispextern.h +++ b/src/dispextern.h | |||
| @@ -3066,6 +3066,11 @@ struct image | |||
| 3066 | is created. */ | 3066 | is created. */ |
| 3067 | unsigned long face_foreground, face_background; | 3067 | unsigned long face_foreground, face_background; |
| 3068 | 3068 | ||
| 3069 | /* Details of the font, only really relevant for types like SVG that | ||
| 3070 | allow us to draw text. */ | ||
| 3071 | int face_font_size; | ||
| 3072 | char *face_font_family; | ||
| 3073 | |||
| 3069 | /* True if this image has a `transparent' background -- that is, is | 3074 | /* True if this image has a `transparent' background -- that is, is |
| 3070 | uses an image mask. The accessor macro for this is | 3075 | uses an image mask. The accessor macro for this is |
| 3071 | `IMAGE_BACKGROUND_TRANSPARENT'. */ | 3076 | `IMAGE_BACKGROUND_TRANSPARENT'. */ |
diff --git a/src/image.c b/src/image.c index 774b7e14ea9..13bd503e535 100644 --- a/src/image.c +++ b/src/image.c | |||
| @@ -1199,6 +1199,7 @@ free_image (struct frame *f, struct image *img) | |||
| 1199 | 1199 | ||
| 1200 | /* Free resources, then free IMG. */ | 1200 | /* Free resources, then free IMG. */ |
| 1201 | img->type->free_img (f, img); | 1201 | img->type->free_img (f, img); |
| 1202 | xfree (img->face_font_family); | ||
| 1202 | xfree (img); | 1203 | xfree (img); |
| 1203 | } | 1204 | } |
| 1204 | } | 1205 | } |
| @@ -1597,7 +1598,7 @@ make_image_cache (void) | |||
| 1597 | static struct image * | 1598 | static struct image * |
| 1598 | search_image_cache (struct frame *f, Lisp_Object spec, EMACS_UINT hash, | 1599 | search_image_cache (struct frame *f, Lisp_Object spec, EMACS_UINT hash, |
| 1599 | unsigned long foreground, unsigned long background, | 1600 | unsigned long foreground, unsigned long background, |
| 1600 | bool ignore_colors) | 1601 | int font_size, char *font_family, bool ignore_colors) |
| 1601 | { | 1602 | { |
| 1602 | struct image *img; | 1603 | struct image *img; |
| 1603 | struct image_cache *c = FRAME_IMAGE_CACHE (f); | 1604 | struct image_cache *c = FRAME_IMAGE_CACHE (f); |
| @@ -1621,7 +1622,10 @@ search_image_cache (struct frame *f, Lisp_Object spec, EMACS_UINT hash, | |||
| 1621 | if (img->hash == hash | 1622 | if (img->hash == hash |
| 1622 | && !NILP (Fequal (img->spec, spec)) | 1623 | && !NILP (Fequal (img->spec, spec)) |
| 1623 | && (ignore_colors || (img->face_foreground == foreground | 1624 | && (ignore_colors || (img->face_foreground == foreground |
| 1624 | && img->face_background == background))) | 1625 | && img->face_background == background |
| 1626 | && img->face_font_size == font_size | ||
| 1627 | && (font_family | ||
| 1628 | &&!strcmp (font_family, img->face_font_family))))) | ||
| 1625 | break; | 1629 | break; |
| 1626 | return img; | 1630 | return img; |
| 1627 | } | 1631 | } |
| @@ -1639,7 +1643,7 @@ uncache_image (struct frame *f, Lisp_Object spec) | |||
| 1639 | can have multiple copies of an image with the same spec. We want | 1643 | can have multiple copies of an image with the same spec. We want |
| 1640 | to remove them all to ensure the user doesn't see an old version | 1644 | to remove them all to ensure the user doesn't see an old version |
| 1641 | of the image when the face changes. */ | 1645 | of the image when the face changes. */ |
| 1642 | while ((img = search_image_cache (f, spec, hash, 0, 0, true))) | 1646 | while ((img = search_image_cache (f, spec, hash, 0, 0, 0, NULL, true))) |
| 1643 | { | 1647 | { |
| 1644 | free_image (f, img); | 1648 | free_image (f, img); |
| 1645 | /* As display glyphs may still be referring to the image ID, we | 1649 | /* 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) | |||
| 2411 | struct face *face = FACE_FROM_ID (f, face_id); | 2415 | struct face *face = FACE_FROM_ID (f, face_id); |
| 2412 | unsigned long foreground = FACE_COLOR_TO_PIXEL (face->foreground, f); | 2416 | unsigned long foreground = FACE_COLOR_TO_PIXEL (face->foreground, f); |
| 2413 | unsigned long background = FACE_COLOR_TO_PIXEL (face->background, f); | 2417 | unsigned long background = FACE_COLOR_TO_PIXEL (face->background, f); |
| 2418 | int font_size = face->font->pixel_size; | ||
| 2419 | char *font_family = SSDATA (face->lface[LFACE_FAMILY_INDEX]); | ||
| 2414 | 2420 | ||
| 2415 | /* F must be a window-system frame, and SPEC must be a valid image | 2421 | /* F must be a window-system frame, and SPEC must be a valid image |
| 2416 | specification. */ | 2422 | specification. */ |
| @@ -2419,7 +2425,8 @@ lookup_image (struct frame *f, Lisp_Object spec, int face_id) | |||
| 2419 | 2425 | ||
| 2420 | /* Look up SPEC in the hash table of the image cache. */ | 2426 | /* Look up SPEC in the hash table of the image cache. */ |
| 2421 | hash = sxhash (spec); | 2427 | hash = sxhash (spec); |
| 2422 | img = search_image_cache (f, spec, hash, foreground, background, false); | 2428 | img = search_image_cache (f, spec, hash, foreground, background, |
| 2429 | font_size, font_family, false); | ||
| 2423 | if (img && img->load_failed_p) | 2430 | if (img && img->load_failed_p) |
| 2424 | { | 2431 | { |
| 2425 | free_image (f, img); | 2432 | free_image (f, img); |
| @@ -2434,6 +2441,9 @@ lookup_image (struct frame *f, Lisp_Object spec, int face_id) | |||
| 2434 | cache_image (f, img); | 2441 | cache_image (f, img); |
| 2435 | img->face_foreground = foreground; | 2442 | img->face_foreground = foreground; |
| 2436 | img->face_background = background; | 2443 | img->face_background = background; |
| 2444 | img->face_font_size = font_size; | ||
| 2445 | img->face_font_family = malloc (strlen (font_family) + 1); | ||
| 2446 | strcpy (img->face_font_family, font_family); | ||
| 2437 | img->load_failed_p = ! img->type->load_img (f, img); | 2447 | img->load_failed_p = ! img->type->load_img (f, img); |
| 2438 | 2448 | ||
| 2439 | /* If we can't load the image, and we don't have a width and | 2449 | /* If we can't load the image, and we don't have a width and |
| @@ -9532,6 +9542,7 @@ enum svg_keyword_index | |||
| 9532 | SVG_DATA, | 9542 | SVG_DATA, |
| 9533 | SVG_FILE, | 9543 | SVG_FILE, |
| 9534 | SVG_BASE_URI, | 9544 | SVG_BASE_URI, |
| 9545 | SVG_CSS, | ||
| 9535 | SVG_ASCENT, | 9546 | SVG_ASCENT, |
| 9536 | SVG_MARGIN, | 9547 | SVG_MARGIN, |
| 9537 | SVG_RELIEF, | 9548 | SVG_RELIEF, |
| @@ -9552,6 +9563,7 @@ static const struct image_keyword svg_format[SVG_LAST] = | |||
| 9552 | {":data", IMAGE_STRING_VALUE, 0}, | 9563 | {":data", IMAGE_STRING_VALUE, 0}, |
| 9553 | {":file", IMAGE_STRING_VALUE, 0}, | 9564 | {":file", IMAGE_STRING_VALUE, 0}, |
| 9554 | {":base-uri", IMAGE_STRING_VALUE, 0}, | 9565 | {":base-uri", IMAGE_STRING_VALUE, 0}, |
| 9566 | {":css", IMAGE_STRING_VALUE, 0}, | ||
| 9555 | {":ascent", IMAGE_ASCENT_VALUE, 0}, | 9567 | {":ascent", IMAGE_ASCENT_VALUE, 0}, |
| 9556 | {":margin", IMAGE_NON_NEGATIVE_INTEGER_VALUE_OR_PAIR, 0}, | 9568 | {":margin", IMAGE_NON_NEGATIVE_INTEGER_VALUE_OR_PAIR, 0}, |
| 9557 | {":relief", IMAGE_INTEGER_VALUE, 0}, | 9569 | {":relief", IMAGE_INTEGER_VALUE, 0}, |
| @@ -9838,7 +9850,7 @@ svg_load (struct frame *f, struct image *img) | |||
| 9838 | 9850 | ||
| 9839 | #if LIBRSVG_CHECK_VERSION (2, 46, 0) | 9851 | #if LIBRSVG_CHECK_VERSION (2, 46, 0) |
| 9840 | static double | 9852 | static double |
| 9841 | svg_css_length_to_pixels (RsvgLength length, double dpi) | 9853 | svg_css_length_to_pixels (RsvgLength length, double dpi, int font_size) |
| 9842 | { | 9854 | { |
| 9843 | double value = length.length; | 9855 | double value = length.length; |
| 9844 | 9856 | ||
| @@ -9866,9 +9878,16 @@ svg_css_length_to_pixels (RsvgLength length, double dpi) | |||
| 9866 | case RSVG_UNIT_IN: | 9878 | case RSVG_UNIT_IN: |
| 9867 | value *= dpi; | 9879 | value *= dpi; |
| 9868 | break; | 9880 | break; |
| 9881 | #if LIBRSVG_CHECK_VERSION (2, 48, 0) | ||
| 9882 | /* We don't know exactly what font size is used on older librsvg | ||
| 9883 | versions. */ | ||
| 9884 | case RSVG_UNIT_EM: | ||
| 9885 | value *= font_size; | ||
| 9886 | break; | ||
| 9887 | #endif | ||
| 9869 | default: | 9888 | default: |
| 9870 | /* Probably one of em, ex, or %. We can't know what the pixel | 9889 | /* Probably ex or %. We can't know what the pixel value is |
| 9871 | value is without more information. */ | 9890 | without more information. */ |
| 9872 | value = 0; | 9891 | value = 0; |
| 9873 | } | 9892 | } |
| 9874 | 9893 | ||
| @@ -9923,6 +9942,27 @@ svg_load_image (struct frame *f, struct image *img, char *contents, | |||
| 9923 | 9942 | ||
| 9924 | rsvg_handle_set_dpi_x_y (rsvg_handle, FRAME_DISPLAY_INFO (f)->resx, | 9943 | rsvg_handle_set_dpi_x_y (rsvg_handle, FRAME_DISPLAY_INFO (f)->resx, |
| 9925 | FRAME_DISPLAY_INFO (f)->resy); | 9944 | FRAME_DISPLAY_INFO (f)->resy); |
| 9945 | |||
| 9946 | #if LIBRSVG_CHECK_VERSION (2, 48, 0) | ||
| 9947 | char *css; | ||
| 9948 | Lisp_Object lcss = image_spec_value (img->spec, QCcss, NULL); | ||
| 9949 | if (!STRINGP (lcss)) | ||
| 9950 | { | ||
| 9951 | /* Generate the CSS for the SVG image. */ | ||
| 9952 | char *css_spec = "svg{font-family:\"%s\";font-size:%4dpx}"; | ||
| 9953 | int css_len = strlen (css_spec) + strlen (img->face_font_family); | ||
| 9954 | css = xmalloc (css_len); | ||
| 9955 | snprintf (css, css_len, css_spec, img->face_font_family, img->face_font_size); | ||
| 9956 | rsvg_handle_set_stylesheet (rsvg_handle, css, strlen (css), NULL); | ||
| 9957 | } | ||
| 9958 | else | ||
| 9959 | { | ||
| 9960 | css = xmalloc (SBYTES (lcss) + 1); | ||
| 9961 | strncpy (css, SSDATA (lcss), SBYTES (lcss)); | ||
| 9962 | *(css + SBYTES (lcss) + 1) = 0; | ||
| 9963 | } | ||
| 9964 | #endif | ||
| 9965 | |||
| 9926 | #else | 9966 | #else |
| 9927 | /* Make a handle to a new rsvg object. */ | 9967 | /* Make a handle to a new rsvg object. */ |
| 9928 | rsvg_handle = rsvg_handle_new (); | 9968 | rsvg_handle = rsvg_handle_new (); |
| @@ -9965,20 +10005,20 @@ svg_load_image (struct frame *f, struct image *img, char *contents, | |||
| 9965 | if (has_width && has_height) | 10005 | if (has_width && has_height) |
| 9966 | { | 10006 | { |
| 9967 | /* Success! We can use these values directly. */ | 10007 | /* Success! We can use these values directly. */ |
| 9968 | viewbox_width = svg_css_length_to_pixels (iwidth, dpi); | 10008 | viewbox_width = svg_css_length_to_pixels (iwidth, dpi, img->face_font_size); |
| 9969 | viewbox_height = svg_css_length_to_pixels (iheight, dpi); | 10009 | viewbox_height = svg_css_length_to_pixels (iheight, dpi, img->face_font_size); |
| 9970 | } | 10010 | } |
| 9971 | else if (has_width && has_viewbox) | 10011 | else if (has_width && has_viewbox) |
| 9972 | { | 10012 | { |
| 9973 | viewbox_width = svg_css_length_to_pixels (iwidth, dpi); | 10013 | viewbox_width = svg_css_length_to_pixels (iwidth, dpi, img->face_font_size); |
| 9974 | viewbox_height = svg_css_length_to_pixels (iwidth, dpi) | 10014 | viewbox_height = svg_css_length_to_pixels (iwidth, dpi, img->face_font_size) |
| 9975 | * viewbox.width / viewbox.height; | 10015 | * viewbox.height / viewbox.width; |
| 9976 | } | 10016 | } |
| 9977 | else if (has_height && has_viewbox) | 10017 | else if (has_height && has_viewbox) |
| 9978 | { | 10018 | { |
| 9979 | viewbox_height = svg_css_length_to_pixels (iheight, dpi); | 10019 | viewbox_height = svg_css_length_to_pixels (iheight, dpi, img->face_font_size); |
| 9980 | viewbox_width = svg_css_length_to_pixels (iheight, dpi) | 10020 | viewbox_width = svg_css_length_to_pixels (iheight, dpi, img->face_font_size) |
| 9981 | * viewbox.height / viewbox.width; | 10021 | * viewbox.width / viewbox.height; |
| 9982 | } | 10022 | } |
| 9983 | else if (has_viewbox) | 10023 | else if (has_viewbox) |
| 9984 | { | 10024 | { |
| @@ -10099,6 +10139,10 @@ svg_load_image (struct frame *f, struct image *img, char *contents, | |||
| 10099 | 10139 | ||
| 10100 | rsvg_handle_set_dpi_x_y (rsvg_handle, FRAME_DISPLAY_INFO (f)->resx, | 10140 | rsvg_handle_set_dpi_x_y (rsvg_handle, FRAME_DISPLAY_INFO (f)->resx, |
| 10101 | FRAME_DISPLAY_INFO (f)->resy); | 10141 | FRAME_DISPLAY_INFO (f)->resy); |
| 10142 | |||
| 10143 | #if LIBRSVG_CHECK_VERSION (2, 48, 0) | ||
| 10144 | rsvg_handle_set_stylesheet (rsvg_handle, css, strlen (css), NULL); | ||
| 10145 | #endif | ||
| 10102 | #else | 10146 | #else |
| 10103 | /* Make a handle to a new rsvg object. */ | 10147 | /* Make a handle to a new rsvg object. */ |
| 10104 | rsvg_handle = rsvg_handle_new (); | 10148 | rsvg_handle = rsvg_handle_new (); |
| @@ -10132,6 +10176,11 @@ svg_load_image (struct frame *f, struct image *img, char *contents, | |||
| 10132 | g_object_unref (rsvg_handle); | 10176 | g_object_unref (rsvg_handle); |
| 10133 | xfree (wrapped_contents); | 10177 | xfree (wrapped_contents); |
| 10134 | 10178 | ||
| 10179 | #if LIBRSVG_CHECK_VERSION (2, 48, 0) | ||
| 10180 | if (!STRINGP (lcss)) | ||
| 10181 | xfree (css); | ||
| 10182 | #endif | ||
| 10183 | |||
| 10135 | /* Extract some meta data from the svg handle. */ | 10184 | /* Extract some meta data from the svg handle. */ |
| 10136 | width = gdk_pixbuf_get_width (pixbuf); | 10185 | width = gdk_pixbuf_get_width (pixbuf); |
| 10137 | height = gdk_pixbuf_get_height (pixbuf); | 10186 | height = gdk_pixbuf_get_height (pixbuf); |
| @@ -10202,6 +10251,10 @@ svg_load_image (struct frame *f, struct image *img, char *contents, | |||
| 10202 | g_object_unref (rsvg_handle); | 10251 | g_object_unref (rsvg_handle); |
| 10203 | if (wrapped_contents) | 10252 | if (wrapped_contents) |
| 10204 | xfree (wrapped_contents); | 10253 | xfree (wrapped_contents); |
| 10254 | #if LIBRSVG_CHECK_VERSION (2, 48, 0) | ||
| 10255 | if (css && !STRINGP (lcss)) | ||
| 10256 | xfree (css); | ||
| 10257 | #endif | ||
| 10205 | /* FIXME: Use error->message so the user knows what is the actual | 10258 | /* FIXME: Use error->message so the user knows what is the actual |
| 10206 | problem with the image. */ | 10259 | problem with the image. */ |
| 10207 | image_error ("Error parsing SVG image `%s'", img->spec); | 10260 | 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. */); | |||
| 10793 | #if defined (HAVE_RSVG) | 10846 | #if defined (HAVE_RSVG) |
| 10794 | DEFSYM (Qsvg, "svg"); | 10847 | DEFSYM (Qsvg, "svg"); |
| 10795 | DEFSYM (QCbase_uri, ":base-uri"); | 10848 | DEFSYM (QCbase_uri, ":base-uri"); |
| 10849 | DEFSYM (QCcss, ":css"); | ||
| 10796 | add_image_type (Qsvg); | 10850 | add_image_type (Qsvg); |
| 10797 | #ifdef HAVE_NTGUI | 10851 | #ifdef HAVE_NTGUI |
| 10798 | /* Other libraries used directly by svg code. */ | 10852 | /* Other libraries used directly by svg code. */ |