MRdotB

MRdotB

Resvg - SVG Rendering in Elixir with Rust

Greetings Elixir community!

Today, I’m thrilled to present you with resvg_nif, an open-source project that provides Elixir bindings for the Rust-based SVG rendering library resvg. After initially exploring resvg via a port, I decided to take a plunge into creating native bindings for it.

SVG to PNG conversion:

:ok = Resvg.svg_to_png("input.svg", "output.png")

Resvg also allows direct handling of SVG content as strings:

svg_string = """
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
  <path stroke-linecap="round" stroke-linejoin="round" d="M2.25 15a4.5 4.5 0 004.5 4.5H18a3.75 3.75 0 001.332-7.257 3 3 0 00-3.758-3.848 5.25 5.25 0 00-10.233 2.33A4.502 4.502 0 002.25 15z" />
</svg>
"""
:ok = Resvg.svg_string_to_png(svg_string, "output.png", resources_dir: "/tmp")

To see Resvg in action, you can try the Livebook !

This NIF originated from my endeavor to create a system for generating YouTube thumbnails. After experimenting with various approaches using ImageMagick and libvips, I discovered that utilizing SVGs for conversion yielded the best results, simplifying testing in the process. The impressive support for fonts and SVG images offered by Resvg is strikingly similar to the rendering you get in a browser, making adjustments and testing much easier.

One thumbnail generated from my svg template.

First 10 of 23 Posts Switch mode

tmbb

tmbb

Something which is useful for complex drawings is to be able to determine the dimensions of text boxes. Resvg can be used to query the dimensions of SVG elements (in particular text elements). Maybe might be interesting to expose the query functionality? I won’t be usint that for my own projects as ExTypst fits my bill better, but it’s something to think about.

kip

kip

ex_cldr Core Team

Cool library and great to see some bindings for the current go-to SVG renderer.

I’m curious about what issues you had with libvips (I maintain image which is ultimately built on libvips). I’ve not had any special issues with SVG rendering (although it currently binds with libsvg with plans to move to resvg).

aiwaiwa

aiwaiwa

Very cool! Are you planning to add Windows support at some point? Livebook crashes node after installing NIF:

MRdotB

MRdotB OP

I did not try --query-all before.

$ resvg drawing.svg --query-all
layer1,19.063,40.228,172.675,123.088
text236,19.063,40.228,172.675,20.681
rect398,41.69,93.372,144.021,69.943

It should be simple to expose in a function like the list_fonts/1
Not sure about the api to make and how to format the returned data.

MRdotB

MRdotB OP

Hello Kip,

Your work with image, and specifically the use of Operation.svgload_buffer(svg), was a significant source of inspiration while I was developing this library.

I encountered several issues with libvips / vix that led me to try alternative:

  1. Windows Support: One of my primary requirements was compatibility with Windows. Unfortunately, as of the time of development, vix did not support it.

  2. Font Loading with librsvg: I was unable to successfully get libvips to load my specific font.

  3. SVG local file resolution: Another challenge I faced pertained to the svg element <image> in librsvg when resolving local files.

I found resvg to be more compatible with my requirements. Resvg allowed you to specify the :resources_dir, facilitating the resolution of images from this path:

path = "/project/assets/"
svg_string = """
  <svg xmlns="http://www.w3.org/2000/svg" width="400" height="200"
    xmlns:xlink="http://www.w3.org/1999/xlink"
  >
    <image xlink:href="mdn-logo.png" width="200" height="200" />
    <image xlink:href="mdn-logo.png" x="200" width="200" height="200" />
  </svg>
"""
Resvg.svg_string_to_png_buffer(svg_string, resources_dir: path)

Moreover, I found that resvg provided results that closely matched those from the browser. This aspect was particularly important to me as I heavily rely on the browser to create the svg templates and making adjustments.

MRdotB

MRdotB OP

The :rustler_precompiled nif does not work on windows but the self compiled work. I’m new to rustler and friends. I will try to fix it asap.

kip

kip

ex_cldr Core Team

@MRdotB, Thanks very much for the thoughtful response, really appreciate it. I agree with all three of your challenges with libsvg (under libvips) although I would like to try and debug a little further the font loading issues you had. Are you able to share your .ttf file for the font that wouldn’t load?

On the image loading part, typically Image would encourage composing images to achieve the result, but given your templating approach I can see why you want everything to be in an <svg> tag.

Also great to see more bindings for some good Rust libs too.

MRdotB

MRdotB OP

I have published resvg version 0.3.0

I added a query_all function which return the pos and size of nodes with id.

Resvg.query_all("rustacean.svg")
[
  %Resvg.Native.Node{
    id: "Layer-1",
    x: -63.99300003051758,
    y: 90.14399719238281,
    width: 1304.344970703125,
    height: 613.6170043945312
  }
]

The precompiled nif is now working on windows system.

aiwaiwa

aiwaiwa

Amazing thank you @MRdotB!
The Livebook example needed {:resvg, "~> 0.3"}, to succeed and it got the NIF like charm! Congrats, I’m imagining Windows is not a top priority for the majority of image processors, but you managed to figure it out! Thank you!
:slight_smile:

ostap

ostap

@MRdotB I wonder if it’s possible to return the PNG directly instead of having to save it into the file? My use case is generating those images “on the fly” and sending them as a response in Phoenix Framework.

Great library, thank you! The only issue I noticed is that the text wasn’t loading on Linux unless I specified the font-family, but that’s related to resvg itself and not the Elixir library.

Where Next?

Trending in Announcing Top

bluzky
You may know https://ui.shadcn.com/, a UI component library for React. I really love it’s design style and components. I’ve built some co...
387 14960 120
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
shahryarjb
The Chelekom project is a library of Phoenix and LiveView components generated via Mix tasks to fit developer needs seamlessly. One of i...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New
wintermeyer
There are three potential reasons for members of this forum to have a look at https://vutuv.de You are tired or annoyed of LinkedIn. Yo...
New

Other Trending Topics Top

type1fool
I just stumbled on a newly redesigned elixir-lang.org. :tada: It looks like @Software_Mansion did the work, and I think it is generally a...
New
akoutmos
@hugobarauna and I (Alex Koutmos) have been hard at work on writing a book on Nerves that takes you from simply blinking LEDs to building...
New
juhalehtonen
There has been a thread to discuss the Stack Overflow Developer Survey on this forum every year since 2018, so here’s yet another one for...
New
bjorng
We want to introduce a new native datatype to Erlang: native records. Although replacing all tuple records with native records is not our...
New
spammy
I’m looking to build a personal workflow to quickly deploy web applications written in elixir/phoenix, for local consumption (ie not on t...
New
alexslade
Fly’s CEO posted this recently - Turn And Face The Strange · The Fly Blog It says that Fly is going all-in on sprites, which is a worry ...
New

We're in Beta

About us Mission Statement