dbuenzli / vg

Declarative 2D vector graphics for OCaml
http://erratique.ch/software/vg
ISC License
88 stars 12 forks source link

Cant get SVG rendering to work #3

Closed andrewray closed 10 years ago

andrewray commented 10 years ago

I have tried to follow the tutorials on the website but get no output when rendering in the web-browser, or with the (Ubuntu 13.10 64bit) gnome image viewer.

The only time I have seen anything at all is with the pie-ambiguity database image. You can just about see the text numbers but that's all.

This all being said the images in your demo website work fine in the browser until I download them.

Any idea what I am doing wrong? The fact your website works suggest it's something at my end.

Cheers, Andy

dbuenzli commented 10 years ago

It's difficult in your description to understand what doesn't work.

Did you try the minimal svg example ? This should output you an SVG file on stdout that you should be able to visualise with an SVG viewer (browsers included).

andrewray commented 10 years ago

Yes I did - here's a test case;

andyman@woofer:~/dev/test$ more svg_test.ml
#use "topfind"
#require "vg.svg"

open Gg
open Vg

(* 1. Define your image *)

let aspect = 1.618  
let size = Size2.v (aspect *. 100.) 100. (* mm *)
let view = Box2.v P2.o (Size2.v aspect 1.)
let image = I.const (Color.v_srgb 0.314 0.784 0.471)

(* 2. Render *)

let () = 
let title = "Vgr_svg minimal example" in 
let description = "Emerald Color" in 
let xmp = Vgr.xmp ~title ~description () in
let warn w = Vgr.pp_warning Format.err_formatter w in
let r = Vgr.create ~warn (Vgr_svg.target ~xmp ()) (`Channel stdout) in
ignore (Vgr.render r (`Image (size, view, image))); 
ignore (Vgr.render r `End)

Then

andyman@woofer:~/dev/test$ ocaml ./svg_test.ml > test.svg

When loading the image with gnome image viewer (3.8.2) nothing is displayed (or rather a checker board image which I think is its default background). I note it takes a few seconds to think about it as well. The toolbar says "573x354 pixels 962 bytes 99%".

OCaml is 4.01.0, opam 1.1, Vg 0.8.0

dbuenzli commented 10 years ago

I tested in a linux vm and I confirm what you see. I suspect this to be a bug of eog (svg is an insane standard so it's difficult to implement correctly) as the file displays fine in firefox or inkscape in that vm.

andrewray commented 10 years ago

I can confirm that inkscape and loading the file directly into the browser works.

I guess my issue is more specific. I really want to make this work in the IOCaml notebook. I have copy/pasted the text output from the minimal example and tried to get it to render - it doesn't work. There is indeed a bunch of stuff going on behind the scenes to make this happen, though I still find it interesting that eog and iocaml seem to render the same thing for the pie-ambiguity example.

In summary, this doesn't work in the notbook;

x = '''<svg xmlns="http://www.w3.org/2000/svg" xmlns:l="http://www.w3.org/1999/xlink" version="1.1" width="161.8mm" height="100mm" viewBox="0 0 161.8 100" color-profile="auto" color-interpolation="linearRGB" color-interpolation-filters="linearRGB"><?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?><x:xmpmeta xmlns:x="adobe:ns:meta/"><r:RDF xmlns:r="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:d="http://purl.org/dc/elements/1.1/" xmlns:x="http://ns.adobe.com/xap/1.0/"><r:Description r:about=""><d:title><r:Alt><r:li xml:lang="x-default">Vgr_svg minimal example</r:li></r:Alt></d:title><d:description><r:Alt><r:li xml:lang="x-default">Emerald Color</r:li></r:Alt></d:description></r:Description></r:RDF></x:xmpmeta><?xpacket end="w"?><g fill="none" stroke-miterlimit="9.98123" transform="matrix(100 0 0 -100 -0 100)"><defs><path id="i1" d="M0 0L1.618 0L1.618 1L0 1Z"/></defs><use l:href="#i1" fill="#50C878"/></g></svg>
SVG(x)

While this example from the MSDN

x = '''<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="200px" height="200px">
      <rect x="0" y="0" width="100%" height="100%" fill="none" stroke="black"/>
      </svg>'''
SVG(x)

Does work. I'll look into this more - perhaps a XML version thing?

dbuenzli commented 10 years ago

Ok, so for eog it seems that the problem is that they use a non-conformant XML parser that looks for hard coded namespaces names, namely xlink. In our example you replace the declaration:

 xmlns:l="http://www.w3.org/1999/xlink" 

by

 xmlns:xlink="http://www.w3.org/1999/xlink" 

And replace the l: prefix accordingly in the document, it renders. This should be reported upstream.

I can have a look for IOCaml if you can point me to a web page somewhere for testing. This must be a different issue because if they inject the document directly in the DOM it should work (as it works in browsers when I do that in the Vg image database). Does maybe that SVG(x) maybe do some pre-processing before injecting it in the DOM ? Where is that SVG(x) defined ?

andrewray commented 10 years ago

Sorry I was being very confusing there. I put 2 code examples from the "python kernel for ipython" so the code is python. At that point I was testing with it to remove a possible point of failure.

Some equivalent ocaml code would be;

Iocaml.display "image/svg+xml" "<svg ...>..."

However, you would need to find a way to escape the svg string. There's an html example on-line here. You should be able to download the html page locally (some javascript stuff wont load but the svg declaration will be there).

Regarding the code it is writing the svg data to (mime : out_channel) then sending the svg to notebook with the 'send_mime' function.

Does maybe that SVG(x) maybe do some pre-processing before injecting it in the DOM ?

Yes, it certainly does some parsing as I have hacked away at some svg strings and got errors reported. I look further into that.

dbuenzli commented 10 years ago

The w3c validator has quite a few things to say about that webpage. I don't remember the exact rules for direct inclusion of svg (vs injecting it in the DOM via js) in an html5 documents, I need to have a look at that but it seems for example it doesn't like namespace declarations.

That being said something you can already do is to suppress the xml declarations and the xmp metadata packet which generate xml processing instructions that an html5 document won't like anyways. More precisely when you create create the rendering target, use xml_decl:false and don't specify the xmp argument.

andrewray commented 10 years ago

I have set xml_decl:false and updated the page. w3c validator is even less happy now!

dbuenzli commented 10 years ago

Shouldn't be can you provide the link to the new document.

andrewray commented 10 years ago

It should be at the same link. If its not showing up try adding ?create=1 to the end of the url.

dbuenzli commented 10 years ago

From what I read from here (the bullets at the end of the post) HTML5's svg tag seems to be hack: your xml shall have no namespace prefixes, make no usage of namespaces, and hard code the xlink namespace name to xlink.

So I think it's going hard to make it work with the current Vg. Here are a few possibilities if you want to make it work now:

  1. Switch to xhtml5.
  2. Keep html5 but output the svg to a file and embed it using an object tag (I don't know if this fits in the infrastructure provided by that notebook thing).
dbuenzli commented 10 years ago

The fact that the validator gives you more error is because it is now able to the parse the svg and gives you errors about these ones. Two things:

  1. There is still an xml declaration on that page.
  2. W.r.t. to my last comment, 2. would be better. The reason is that in xml documents id attributes have to be unique. Now since you repeatedetly output svg documents inline in the same document with different targets you get duplicated ids. That one of the reasons why there are so many warnings/errors reported by the validator. So these svg documents need to be properly isolated.
dbuenzli commented 10 years ago

Note that the id issue would also be an issue if Vg was able to generated the restricted form that html5 wants.

Of course we could give to the rendering target the first id it has to use and have a function to let us know the last one it used. But that would be a bloody hack and I won't do it as it wouldn't solve the problem in general for other svg (maybe non Vg generated) documents when you Iocaml.send_mime "image/svg+xml". If you really want to solve the problem in a clean way you have to do one of the following two things:

  1. Embed svg files send with Iocaml.send_mime "image/svg+xml" using an object tag.
  2. Parse the svg you get in Iocaml.send_mime "image/svg+xml" to make it html5 compatible. This means removing xmlns declarations, replacing any xmlns namespace name that points on xlink by xlink and uniquify all the ids (and of course modify their occurences) with respect to the current document being output.
andrewray commented 10 years ago

By hacking the svg output string data to use xlink it works as you suggest.

Switching to xhtml5 is not an option I have as I don't control the notebook interface code.

Generating external files is possible, however, they will not be embedded into the notebook so wouldn't work online. If there was a solution where I could encode the SVG data into the object tag itself that would be ideal but I am not sure it's possible.

Thanks for your help.

dbuenzli commented 10 years ago

In case 2. you also need to the following:

  1. Remove any processing instruction the xml may have (xml declarations are processing instructions so it will handle those aswell).
  2. Remove any markup that is not in the svg or xlink namespaces.

Note that this is not as hard you may think and should be quite easy to perform with xmlm.

dbuenzli commented 10 years ago

So I'm closing this, I hope you'll take path 2. it's the only clean and robust way to go. Feel free to continue the discussion in this issue if you need help.

andrewray commented 10 years ago

This seems to work the way I want;

Iocaml.display 
  "text/html" 
  ("<object data=\"data:image/svg+xml;base64," ^ 
      base64 (string_of_svg (Size2.v 30. 30., Box2.unit, gray)) ^ "\"> </object>")
dbuenzli commented 10 years ago

Interesting hack. Just note that the maximal size for data: URIs is unspecified and left to implementers (see "Length limitations" on this page). So I'd rather call this a workaround...

dbuenzli commented 10 years ago

Btw if the notebook interface has functions to inject SVG documents into a document (that SVG(x) function ?). What I have described above should be done by that function so maybe you should report the problem upstream.

andrewray commented 10 years ago

I think you're right - this is both a shocking hack and IPython itself should probably be doing a better job here. I will have a trawl though their forums and documentation to see what I can figure out.

In the mean time this seems like a good start. The latest version of the page has working svg images. I've also put in some code to pull apart the xml representation using ocaml-cow which I believe would provide a way to perform the transformations you were suggesting.