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.

Most Liked

MRdotB

MRdotB

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.

MRdotB

MRdotB

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

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.

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:

kip

kip

ex_cldr Core Team

If for some reason that’s not possible in revsg you can do it in image with:

"<svg> .....</svg>"
|> Image.from_svg!()
|> Image.write(conn)

Unfortunately the underlying vix isn’t supported on Windows so if that’s a requirement then this isn’t the solution for you. Also it uses librsvg as the svg renderer, not libresvg.

Where Next?

Popular in Announcing Top

zachdaniel
Hey folks! AshEvents Release We’ve just released the first version of AshEvents, an Event Sourcing tool for Ash Framework apps. Check o...
New
moxley
An Elixir implementation of the popular PostCSS library, providing CSS parsing, AST manipulation, and stringification capabilities. Feat...
New
hauleth
PhoenixBakery is library for Phoenix 1.6 (and later) that provides modules implementing Phoenix.Digester.Compressor. There are currently ...
New
fuelen
Ecto.DevLogger is an alternative logger of SQL queries in development It inlines bindings into the query, so it is easy to copy-paste lo...
New
pcharbon
This is a new extension for the Ash framework that lets you use the Commanded library in a more declarative manner and removes most of th...
New
type1fool
WebAuthnLiveComponent WebAuthnComponents See this post about renaming the package. Passwordless authentication for Phoenix LiveView app...
New
LostKobrakai
I’ve recently created a small library phoenix_vite integrating the vite build tooling with phoenix. It provides an igniter.installer t...
New
Asd
Hi, I am happy to release the Repatch library for mocking and patching implementation in tests and anywhere else. It brings new possibili...
New
brainlid
LangChain is short for Language Chain. An LLM, or Large Language Model, is the “Language” part. This library makes it easier for Elixir a...
New
billylanchantin
The Explorer library has been out for some time. But we just released the latest version (see below), and we thought we’d start posting u...
New

Other popular topics Top

belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
peerreynders
Manning 2016 Halloween weekend sale via Deal of the Day Friday, October 28 - Half off all MEAPs - code WM102816LT Saturday, October 29 ...
326 29600 154
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
sergio
I couldn’t find any guides that worked well with Phoenix 1.6.0 and esbuild. I hope this helps people test the waters and eases you into t...
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
Nvim
Elixir appears to be a superior language to Python. I don’t see any advantage of Python over Elixir. Are there any?
New

We're in Beta

About us Mission Statement