Downloading svg files created in the browser

I am trying to create an application that lets you create geometric images within the browser. I am mostly creating this application as a development environment for creating my art. I need to be able to download the image that was created as an SVG file type. I have been able to learn through most of the technical issues up to this point, all except downloading the created image.

I have been using the ianmackenzie/elm-geometry-svg library to create and display the content within the browser. The geometry utilities within the elm-geometry and elm-geometry-svg libraries are paramount to the style of art that I create. This package is one of the reasons I was so excited to create my stack within the elm environment.

Everything was going well until I tried to implement the download functionality. I was looking at the elm/file library to download the image. They have an example

import File.Download as Download

download : String -> Cmd msg
download svgContent =
  Download.string "floorplan.svg" "image/svg+xml" svgContent

I was able to download some string output but where Iā€™m getting stuck is that I need to convert the SVG content into the String representation to send to this example download function. The elm/svg library doesnā€™t provide any Svg.toString function to do this conversion. With more research, I was able to find the PaackEng/elm-svg-string and zwilias/elm-html-string packages which try to help and solve this problem.

Unfortunately, there isnā€™t a simple single-function insertion from either of these packages. This would be a wholesale replacement of all the Html and Svg function calls with the Html.String and Svg.string variants from these packages. It appears like the Svg.String uses strings as an internal representation instead of the VirtualDom. I would be alright just swapping to those libraries and taking what I assume would be a performance hit because of the different representation. However, this switch would have other propagating consequences. Since I am using the ianmackenzie/elm-geometry-svg, this library also has dependencies on the core libraries.

The only solution I see at the moment it to now fork ianmackenzie/elm-geometry-svg library and rework it to use the Svg.String library as the backend. Not only would this likely be a lot of work, but it also seems like more of a hacky solution and has consequences for any packages that use these core libraries as dependencies. I would also have to then manually update that fork with all the new updates from that library.

What is the best way forward to solve this problem? What other pathways forward do I have to be able to download these SVG files that are created on the fly? Thanks

I think a port can work here. you can render your solution to the dom, then use a port and some javascript to extract the html content and send that as a string to elm. Be sure to wrap your javascript code in a requestAnimationFrame to make sure you only extract the content once itā€™s rendered.

Itā€™s not a great solution, but because you want to download the file I think itā€™s workable. You need a Msg type and update anyway for the downloads.

If its just for you personal art work you might consider right-clicking on the image and doing a ā€œSave Image asā€¦ā€? This should work.

Nice to have a button that can trigger downloading as a file, if the application is going to be more polished for other users though.

Unfortunately, that doesnā€™t work. Right click save as saves the entire Html regardless of which element is selected apparently. I donā€™t know if this is a result of me also using the elm-community/elm-ui library or not. I did forget to mention this part but I didnā€™t think it was particularly relevant to the main issue at hand.

I can copy the SVG from the Html via the F12 dev window into a text file to get it out but that is fairly un-ergonomic but doable approach. I wouldnā€™t want this to be my main workflow for what I am doing though.

I will look into using ports. I havenā€™t gotten in that deep into elm yet but it would probably be a good experience for me to explore some of the boundaries of what the language is and is not at the moment. Hopefully this leads to something that is a bit more robust and less ad-hoc than copy and paste into .svg files.

Sorry, my bad. I thought it did work because I successfully exported SVG before, but I just tried it and you are right, so I must have used dev tools to cut and paste the SVG element directly.

BTW another thing to be aware of is that SVG in the browser is not necessarily standards compliant. I think I used a fill color like rgba(ā€¦) which was valid CSS so worked in the browser, but the exported SVG did not look right in Inkscape. Just something to be aware of.

Just use ports. Like so: https://ellie-app.com/7XMGG66xhLRa1
(I have adapted the logo example from the library)

Later Edit: you can also convert the received SVG into a base64 encoded string and then create an a element with download set to whatever you want the file to be named and put the base64 encoded value as a dataURI inside thehref. Like so: https://ellie-app.com/7XN53BSQBnga1
(you have to click GetSvg for the download link to appear)

1 Like

Wonderful examples @pdamoc, thank you. You got me up and running. Iā€™ve been lucky enough to get by with just elm reactor up to this point, with the main pain point being the lack of live reload. I was going to ask what the best recommended build management would be, but after a little bit of searching, it looks like you already answered that one as well. For those reading this now, What is the build manager best suited for bundling Elm with JS? In short, the answer was webpack for more complicated requirements like using ports.

I also have a follow up question. I feel like I may have understated my requirements at the beginning. I was trying to avoid the XY problem but I need to back up one more step to get there. I am looking to create these SVG files so that I can then print them afterwords. This means that I would like to have proper sizing of the output file (Letter 8ā€™ā€™ x 11.5ā€™ā€™ or A3 210mm x 297mm). I can fix this in post processing since the aspect ratio will be the same. Again, for the sake of ergonomics and to help reduce the number of mistakes I may make in my process, how can I ensure that I download the SVG with the proper size specified by some input values? It is unlikely that the browser version will be accurate due to the different unit representation and viewing requirements.

Are you currently specifying width and height values in the SVG? Without these SVGs donā€™t usually have a specific ā€œsizeā€ - theyā€™ll scale as needed.

That post is old now. I would recommend parcel now but webpack should work fine too.

For SVG apps, I typically request the viewport size at application start up, then set the SVG viewbox to match it.

    svg
        [ preserveAspectRatio (Align ScaleMid ScaleMid) Meet
        , viewBox (round frame.x |> toFloat)
            (round frame.y |> toFloat)
            (round frame.w |> toFloat)
            (round frame.h |> toFloat)
        , svgNamespace
        , shapeRendering RenderGeometricPrecision
        ]

I think the width and height parameters are what Inkscape or other SVG drawing software would interpret as being the image size in pixels.

Also note, these donā€™t have to match the on-screen viewport, they are the ā€˜user-spaceā€™ size of the image. I tend to match them up exactly to the screen size, to ensure pixels are 1:1 for a sharp image - also not shown above if the web device has a pixel density <> 1, those numbers need to be scaled appropriately to get back to 1:1 pixels vs user-space coordinates.

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.