Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for inline SVG #75

Closed
barryvan opened this issue Apr 24, 2013 · 20 comments
Closed

Add support for inline SVG #75

barryvan opened this issue Apr 24, 2013 · 20 comments
Labels
feature New feature that should be supported

Comments

@barryvan
Copy link

At the moment, to use SVG within WeasyPrint, you need to embed it (generally via an tag). It'd be really useful if WeasyPrint could handle inline SVG as well.

In other words, one should be able to do the following:

<!doctype html>
<svg style="width: 100px; height: 100px;">
  <rect width="100px" height="100px" style="fill: #c00"/>
</svg>

and get a red square.

In our particular scenario, we're generating HTML for WeasyPrint by performing an XSLT transformation on an XML document -- and in some scenarios, we're hoping to generate SVG graphics (bar charts and the like) dynamically as part of this.

@SimonSapin
Copy link
Member

Yes, I’d say this is a missing feature. I wonder if you could get away with WeasyPrint giving a parsed subtree to CairoSVG, but it gets more tricky when document stylesheets apply to inline SVG elements.

In the meantime as a work-around, you could either use data: URLs, or use a custom "URL fetcher" that returns dynamically generated SVG based on an identifier or other parameters in the URL. Flask-WeasyPrint builds on the latter idea.

@barryvan
Copy link
Author

Hmm, yes, I hadn't thought of document styles... The data: URI works (I just tested it), and will allow us to embed static SVG images directly quite easily. Dynamic, XSLT-generated SVGs would still be a problem in that case -- albeit a problem outside the scope of WeasyPrint!

To be honest, I'd probably be super-happy if WeasyPrint passed the SVG subtree to CairoSVG for rendering, ignoring document styles -- it wouldn't necessarily be conformant, but it'd be a huge step forward in any case. I suspect that inline styles are the norm for SVG images at the moment anyway.

@graingert
Copy link
Contributor

Yes, I’d say this is a missing feature. I wonder if you could get away with WeasyPrint giving a parsed subtree to CairoSVG, but it gets more tricky when document stylesheets apply to inline SVG elements.

Would it not be possible to pre-caclulate and flatten the CSS onto element style attributes of the inline SVG?

@SimonSapin
Copy link
Member

@graingert maybe. This only reinforces my opinion that CairoSVG and WeasyPrint should be one and the same code base :)

@graingert
Copy link
Contributor

Although absolute and fixed positioning would be broken. It does look like CairoSVG and WeasyPrint should be one and the same.

@bitdivine
Copy link

The data URL approach definitely works and is indeed trivial. In Django the code is:

Define a template filter:

from base64 import b64encode
@register.filter
def svg2data_url(value):
        return "data:image/svg+xml;charset=utf-8;base64,"+b64encode(value)

Use it in a template:

<img src="{{context.svg|svg2data_url}}">

@diethardsteiner
Copy link

Please add inline SVG support. This will make things easier for people who do not have any Python experience and hence do not want to add just another new technology to their stack.

@graingert
Copy link
Contributor

@SimonSapin would it be possible to use premailer to flatten all the CSS directly onto attributes of the SVGs?

@SimonSapin
Copy link
Member

@diethardsteiner There is no disagreement that this feature is desired. It’s mostly a matter of "someone" doing the work.

@graingert I don’t see why premailer wouldn’t work, but I don’t see either how it helps at all with inline SVG not being supported by WeasyPrint. Note that "inline" means different things in the context of premailer (style HTML attributes instead of stylesheets with selectors) and in the context of this issue (<svg> elements in an HTML document instead of <img> or <object> elements referencing an external SVG document).

@graingert
Copy link
Contributor

This only fixes the issue that using passing a subtree would cause.

@SimonSapin
Copy link
Member

Oh, I see. Yes, I suppose we could extract a subtree, "flatten" sytelsheets into style attributes, and give that to CairoSVG. Though I’d rather have the "flattening" done by re-using WeasyPrint’s existing CSS parsing and Selector matching code than using Premailer.

@diethardsteiner
Copy link

@SimonSapin Ok thanks for your feedback

@graingert
Copy link
Contributor

@SimonSapin where in the code is both the html tree and the cario context available?

@SimonSapin
Copy link
Member

@graingert The cairo context for the document (as opposed to contexts for intermediate surfaces used at various points) is created in the write_* methods (such as write_pdf) of the Document class in weayprint/document.py. But by the time that code is called, layout is entirely done and the HTML tree is not kept around anymore. We could pass it through so that it’s still available there, but modifying it at that point wouldn’t affect already-done layout.

@graingert
Copy link
Contributor

@SimonSapin is it possible to just keep hold of the SVG fragments during the layout processing? So we can at least get it working by pretending the SVG is just an embedded image?

@SimonSapin
Copy link
Member

I don’t have an answer to that right now, sorry. (I’m not as involved in WeasyPrint as I used to be, so I won’t be spending a lot of time to work on this or think about how it could be done.)

What I remember is that I thought that the CSS rendering and SVG rendering really should share their style system. (The thing that takes stylesheets and a DOM-like tree, matches selectors, runs the cascade, and gives one computed values for every property for every element.) Right now they’re completely separate (and somewhat duplicated) in WeasyPrint and CairoSVG.

But last I talked about merging the code bases, @liZe didn’t like the idea :)

@lutostag
Copy link

This is the code we are using for our specific use-case to inline the svgs generated by angular-nvd3.

(The selector for svgs was trickier due to namespacing, but I'm sure someone could figure it out). The issue is that it doesnt account for styling information, but got us to what we needed.

+from base64 import b64encode
+from lxml import etree
+
+    def svg_embed(html):
+        """For the child of nvd3 nodes (svg) munge them into b64encoded data
+        as a workaround for https://github.com/Kozea/WeasyPrint/issues/75"""
+        root = html.root_element
+        svgs = root.findall('.//nvd3')
+        for svg in svgs:
+            child = svg.getchildren()[0]
+            encoded = b64encode(etree.tostring(child)).decode()
+            encoded_data = "data:image/svg+xml;charset=utf-8;base64," + encoded
+            encoded_child = etree.fromstring('<img src="%s"/>' % encoded_data)
+            svg.replace(child, encoded_child)
+        return html
+
-    rendered = HTML(string=html, base_url=base_url).render()
+    rendered = svg_embed(HTML(string=html, base_url=base_url)).render()

@mr-katsini
Copy link

@liZe is there any possibility for this?

@liZe
Copy link
Member

liZe commented Mar 28, 2019

@liZe is there any possibility for this?

Finding svg tags and rendering them as if they were embedded images seems to be somehow possible. But that's just a hack as it wouldn't handle CSS rules correctly (i.e. you would have to link stylesheets in the SVG file, not in the HTML file) and would probably introduce a lot of subtle problems that we can't imagine yet.

If anyone is interested in implementing that, I can give some advice, it's a good way to start hacking WeasyPrint.

@liZe
Copy link
Member

liZe commented Aug 17, 2021

The feature has been added in version 53.0. There are lots of limitations and bugs related to this initial support, but things will improve with time.

Please try version 53, and open new issues with the specific problems you may meet!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New feature that should be supported
Projects
None yet
Development

No branches or pull requests

8 participants