From: gerard.vermeulen@posteo.net
To: Emacs orgmode <emacs-orgmode@gnu.org>
Subject: [PATCH] ox-html.el: add option to embed SVG for CSS support in SVG
Date: Mon, 05 Jun 2023 13:23:01 +0000 [thread overview]
Message-ID: <c1eef10be815748d2103cb81bce08708@posteo.net> (raw)
[-- Attachment #1: Type: text/plain, Size: 1793 bytes --]
Hi,
I have been trying to export SVG images having links to CSS from Org to
HTML
and I have found that the this CSS is not active in Firefox (only
browser I tried).
I have found that the CCS is only active under two conditions:
1. The HTML page should manage the CSS that the SVG image links to.
2. The SVG image should be embedded in the HTML page.
I wrote an ox-html derived export backend to embed SVG images:
https://forge.chapril.org/gav451/emacs.d/src/branch/main/site-lisp/ox-my-html/ox-my-html.el
and merged the SVG embedding functionality into ox-html.el leading to
the
attached patch.
I have included an MWE below and I attached mwe.org and mwe.html with
doc8.svg and style8.svg (showing a flower and chosen for the visual
impact).
You will get an M-non-WE after exporting mwe.org using ox-html on the
main branch.
# begin MWE
#+title: SVG and CSS MWE with patch or with ox-my-html backend
#+HTML_DOCTYPE: html5
#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="style8.css" />
#+OPTIONS: ^:{} toc:nil html-embed-svg:nil
# The next line overrules html-embed-svg:t
#+HTML_EMBED_SVG_EXCLUDES: ./doc8.svg
# The next line overrules html-embed-svg:nil
#+HTML_EMBED_SVG_INCLUDES: ./doc8.svg
[[https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/SVG_and_CSS][SVG
and CSS (MDN)]] is the origin of the files =doc8.svg= and
=style8.css=. Open =doc8.svg= and =mwe.html= in Firefox to compare
hovering over the flower. The flower is fully black in case of an
M-non-WE or in case of HTML export without patch or without ox-my-html
backend.
#+CAPTION: Is MWE with SVG embedding *and* with HTML_HEAD managing
#+CAPTION: the CSS. Becomes an M-non-WE without SVG embedding or
#+CAPTION: without HTML_HEAD managing the CSS.
[[./doc8.svg]]
# end MWE
Best regards -- Gerard
[-- Attachment #2: 0001-ox-html.el-add-option-to-embed-SVG-for-CSS-support-i.patch --]
[-- Type: application/octet-stream, Size: 6333 bytes --]
From a8d9e61016d455ebcbf878e3c9d6a4531dd21dc6 Mon Sep 17 00:00:00 2001
From: Gerard Vermeulen <gerard.vermeulen@posteo.net>
Date: Mon, 5 Jun 2023 11:13:36 +0200
Subject: [PATCH] ox-html.el: add option to embed SVG for CSS support in SVG
* lisp/ox-html (org-html-embed-svg, org-html-embed-svg-includes,
org-html-embed-html-excludes): add option to enable or disable
SVG embedding in HTML within emacs of file scope. Add options
to specify lists of SVG images to be included in or excluded
from embedding.
(org-html--embed-svg-p): apply the logic to each SVG image.
(org-html-svg-contents): try to return cleaned up SVG contents.
(org-html-link): add embedding SVG images before inlining images.
---
lisp/ox-html.el | 78 +++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 75 insertions(+), 3 deletions(-)
diff --git a/lisp/ox-html.el b/lisp/ox-html.el
index b519402b1..938b977ac 100644
--- a/lisp/ox-html.el
+++ b/lisp/ox-html.el
@@ -24,7 +24,7 @@
;;; Commentary:
-;; This library implements a HTML backend for Org generic exporter.
+;; This library implements an HTML backend for Org generic exporter.
;; See Org manual for more information.
;;; Code:
@@ -134,6 +134,10 @@
(:html-head "HTML_HEAD" nil org-html-head newline)
(:html-head-extra "HTML_HEAD_EXTRA" nil org-html-head-extra newline)
(:subtitle "SUBTITLE" nil nil parse)
+ (:html-embed-svg-excludes "HTML_EMBED_SVG_EXCLUDES" nil
+ org-html-embed-svg-excludes split)
+ (:html-embed-svg-includes "HTML_EMBED_SVG_INCLUDES" nil
+ org-html-embed-svg-includes split)
(:html-head-include-default-style
nil "html-style" org-html-head-include-default-style)
(:html-head-include-scripts nil "html-scripts" org-html-head-include-scripts)
@@ -187,6 +191,7 @@
(:html-klipse-js nil nil org-html-klipse-js)
(:html-klipse-selection-script nil nil org-html-klipse-selection-script)
(:infojs-opt "INFOJS_OPT" nil nil)
+ (:with-html-svg-embedding nil "html-embed-svg" org-html-embed-svg)
;; Redefine regular options.
(:creator "CREATOR" nil org-html-creator-string)
(:with-latex nil "tex" org-html-with-latex)
@@ -849,6 +854,43 @@ When nil, the links still point to the plain \".org\" file."
:group 'org-export-html
:type 'boolean)
+;;;; Links :: Embed SVG
+
+(defcustom org-html-embed-svg nil
+ "Non-nil means embed SVG images into exported HTML pages,
+otherwise link to SVG images from exported HTML pages.
+
+This option can also be set in Org files with
+#+OPTIONS: html-embed-svg:t
+or
+#+OPTIONS: html-embed-svg:nil
+to enable or disable SVG embedding in exported HTML."
+ :group 'org-export-html
+ :version "30.0"
+ :type 'boolean)
+
+(defcustom org-html-embed-svg-excludes nil
+ "List of SVG files to exclude from SVG embedding.
+
+This option overrules an `org-html-embed-svg' non-nil value.
+
+It can also be set with the HTML_EMBED_SVG_EXCLUDES keyword."
+ :group 'org-export-html
+ :version "30.0"
+ :type '(repeat string)
+ :safe (lambda (x) (and (listp x) (cl-every #'stringp x))))
+
+(defcustom org-html-embed-svg-includes nil
+ "List of SVG files to include in SVG embedding.
+
+This option overrules an `org-html-embed-svg' nil value.
+
+It can also be set with the HTML_EMBED_SVG_INCLUDES keyword."
+ :group 'org-export-html
+ :version "30.0"
+ :type '(repeat string)
+ :safe (lambda (x) (and (listp x) (cl-every #'stringp x))))
+
;;;; Links :: Inline images
(defcustom org-html-inline-images t
@@ -2279,7 +2321,7 @@ INFO is a plist used as a communication channel."
;;;; Anchor
(defun org-html--anchor (id desc attributes info)
- "Format a HTML anchor."
+ "Format an HTML anchor."
(let* ((name (and (plist-get info :html-allow-name-attribute-in-anchors) id))
(attributes (concat (and id (format " id=\"%s\"" id))
(and name (format " name=\"%s\"" name))
@@ -3122,6 +3164,33 @@ CONTENTS is nil. INFO is a plist holding contextual information."
;;;; Link
+(defun org-html--embed-svg-p (link path info)
+ "Check whether LINK and INFO specify to embed the SVG file named PATH.
+LINK must have no contents and link to an SVG file. INFO may contain
+lists of SVG files to include in and/or to exclude from embedding."
+ (and (not (org-element-contents link))
+ (let ((case-fold-search t))
+ (string-match-p ".svg\\'" (org-element-property :path link)))
+ (or (and (plist-get info :with-html-svg-embedding)
+ (not (member path (plist-get info :html-embed-svg-excludes))))
+ (and (not (plist-get info :with-html-svg-embedding))
+ (member path (plist-get info :html-embed-svg-includes))))))
+
+(defun org-html-svg-contents (path)
+ "Return the SVG contents of the file named PATH."
+ (with-temp-buffer
+ (insert-file-contents path)
+ ;; Delete text preceding something starting as an SVG root element.
+ ;; The intent is to remove XML declarations (and XML comments).
+ ;; This breaks in case of a preceding XML comment with <svg inside
+ ;; or a preceding XML element with an SVG element inside.
+ ;; See https://emacs.stackexchange.com/a/57433 for the original code.
+ (let ((case-fold-search t))
+ (unless (search-forward "<svg" nil 'noerror)
+ (user-error "Can't find a root SVG start tag in file `%s'." path)))
+ (delete-region (point-min) (match-beginning 0))
+ (buffer-string)))
+
(defun org-html-image-link-filter (data _backend info)
(org-export-insert-image-links data info org-html-inline-image-rules))
@@ -3266,6 +3335,9 @@ INFO is a plist holding contextual information. See
(cond
;; Link type is handled by a special function.
((org-export-custom-protocol-maybe link desc 'html info))
+ ;; Embed SVG.
+ ((org-html--embed-svg-p link path info)
+ (org-html-svg-contents path))
;; Image file.
((and (plist-get info :html-inline-images)
(org-export-inline-image-p
@@ -3972,7 +4044,7 @@ to convert it."
;;;###autoload
(defun org-html-export-to-html
(&optional async subtreep visible-only body-only ext-plist)
- "Export current buffer to a HTML file.
+ "Export current buffer to an HTML file.
If narrowing is active in the current buffer, only export its
narrowed part.
--
2.41.0
[-- Attachment #3: mwe.org --]
[-- Type: application/octet-stream, Size: 974 bytes --]
#+title: SVG and CSS MWE with patch or with ox-my-html backend
#+HTML_DOCTYPE: html5
#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="style8.css" />
#+OPTIONS: ^:{} toc:nil html-embed-svg:nil
# The next line overrules html-embed-svg:t
#+HTML_EMBED_SVG_EXCLUDES: ./doc8.svg
# The next line overrules html-embed-svg:nil
#+HTML_EMBED_SVG_INCLUDES: ./doc8.svg
[[https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/SVG_and_CSS][SVG and CSS (MDN)]] is the origin of the files =doc8.svg= and
=style8.css=. Open =doc8.svg= and =mwe.html= in Firefox to compare
hovering over the flower. The flower is fully black in case of an
M-non-WE or in case of HTML export without patch or without ox-my-html
backend.
#+CAPTION: Is MWE with SVG embedding *and* with HTML_HEAD managing
#+CAPTION: the CSS. Becomes an M-non-WE without SVG embedding or
#+CAPTION: without HTML_HEAD managing the CSS.
[[./doc8.svg]]
# Note: ./doc8.svg starts with ./ to resolve it when inlining.
[-- Attachment #4: mwe.html --]
[-- Type: text/html, Size: 18497 bytes --]
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #5: style8.css --]
[-- Type: text/css; name=style8.css, Size: 2391 bytes --]
/*** SVG demonstration ***/
/* page */
svg {
background-color: beige;
}
#heading {
font-size: 24px;
font-weight: bold;
}
#caption {
font-size: 12px;
}
/* flower */
#flower:hover {
cursor: crosshair;
}
/* gradient */
#fade-stop-1 {
stop-color: blue;
}
#fade-stop-2 {
stop-color: white;
}
/* petals */
.segment-fill {
fill: var(--segment-fill-fill);
stroke: var(--segment-fill-stroke);
stroke-width: var(--segment-fill-stroke-width);
}
.segment-fill:hover {
fill: var(--segment-fill-fill-hover);
stroke: var(--segment-fill-stroke-hover);
}
.segment-edge {
fill: var(--segment-edge-fill);
stroke: var(--segment-edge-stroke);
stroke-width: var(--segment-edge-stroke-width);
}
.segment-edge:hover {
stroke: var(--segment-edge-stroke-hover);
}
/* outer petals */
#outer-petals {
opacity: 0.75;
--segment-fill-fill: azure;
--segment-fill-stroke: lightsteelblue;
--segment-fill-stroke-width: 1;
--segment-edge-fill: none;
--segment-edge-stroke: deepskyblue;
--segment-edge-stroke-width: 3;
--segment-fill-fill-hover: plum;
--segment-fill-stroke-hover: none;
--segment-edge-stroke-hover: slateblue;
}
/*
Non-standard way of styling elements referenced via <use> elements,
supported by some older browsers
*/
#outer-petals .segment-fill {
fill: azure;
stroke: lightsteelblue;
stroke-width: 1;
}
#outer-petals .segment-edge {
fill: none;
stroke: deepskyblue;
stroke-width: 3;
}
#outer-petals .segment:hover > .segment-fill {
fill: plum;
stroke: none;
}
#outer-petals .segment:hover > .segment-edge {
stroke: slateblue;
}
/* inner petals */
#inner-petals {
--segment-fill-fill: yellow;
--segment-fill-stroke: yellow;
--segment-fill-stroke-width: 1;
--segment-edge-fill: none;
--segment-edge-stroke: yellowgreen;
--segment-edge-stroke-width: 9;
--segment-fill-fill-hover: darkseagreen;
--segment-fill-stroke-hover: none;
--segment-edge-stroke-hover: green;
}
/*
Non-standard way of styling elements referenced via <use> elements,
supported by some older browsers
*/
#inner-petals .segment-fill {
fill: yellow;
stroke: yellow;
stroke-width: 1;
}
#inner-petals .segment-edge {
fill: none;
stroke: yellowgreen;
stroke-width: 9;
}
#inner-petals .segment:hover > .segment-fill {
fill: darkseagreen;
stroke: none;
}
#inner-petals .segment:hover > .segment-edge {
stroke: green;
}
[-- Attachment #6: doc8.svg --]
[-- Type: image/svg+xml, Size: 9840 bytes --]
next reply other threads:[~2023-06-05 13:44 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-06-05 13:23 gerard.vermeulen [this message]
2023-06-06 7:49 ` [PATCH] ox-html.el: add option to embed SVG for CSS support in SVG Ihor Radchenko
2023-06-06 9:57 ` Christian Moe
2023-06-06 13:37 ` gerard.vermeulen
2023-06-06 18:54 ` Christian Moe
2023-06-07 8:43 ` Ihor Radchenko
2023-06-07 8:41 ` Ihor Radchenko
2023-06-07 14:30 ` Christian Moe
2023-06-07 17:57 ` Ihor Radchenko
2023-06-15 13:09 ` gerard.vermeulen
2023-06-15 14:55 ` Max Nikulin
2023-06-15 20:42 ` Ihor Radchenko
2023-06-15 20:51 ` Ihor Radchenko
2023-06-16 18:39 ` gerard.vermeulen
2023-06-17 12:38 ` Ihor Radchenko
2023-06-17 14:45 ` gerard.vermeulen
2023-06-21 15:52 ` Max Nikulin
2023-06-21 16:02 ` Ihor Radchenko
2023-06-21 16:27 ` Max Nikulin
2023-06-21 16:38 ` Ihor Radchenko
2023-06-22 16:25 ` Max Nikulin
2023-06-23 11:02 ` Ihor Radchenko
2023-06-06 12:58 ` gerard.vermeulen
2023-06-07 9:07 ` Ihor Radchenko
2023-06-06 14:14 ` Max Nikulin
2023-06-06 16:08 ` gerard.vermeulen
2023-06-07 9:10 ` Ihor Radchenko
2023-06-09 15:27 ` Max Nikulin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.orgmode.org/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=c1eef10be815748d2103cb81bce08708@posteo.net \
--to=gerard.vermeulen@posteo.net \
--cc=emacs-orgmode@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).