How to base64 encode a rasterized image for embedding it into SVG?

Below I try to do this using just the Base64 module.

MWE

using Base64
using Downloads
using FileIO

path = Downloads.download("https://github.com/MakieOrg/Makie.jl/raw/master/assets/doge.png")
img = load(path)

# reduce quality a bit, otherwise firefox complains
img = img[1:4:end,1:4:end]
w, h = size(img)

encoded_img = base64encode(img)

svg = """
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<image width="$w" height="$h" xlink:href="data:image/png;base64,$encoded_img"/>
</svg>
"""

open("/tmp/mwe.svg", "w") do f
  write(f, svg)
 end

Unfortunately, my Firefox says that this image contains errors.
I also tried converting the image to RGBf first and then encode it, but without success.
I also looked into Images.jl’s docs but nowhere is something mentioned about base64 encoding.

Does anyone know how to do this?

PS: The svg’s xml structure itself seems to be correct, because when I copy the encoded string from e.g. this svg here File:Cabezo de Alcalá aerial view-fr.svg - Wikimedia Commons then it renders fine in Firefox.

But is encoded_img still a PNG ‘document’, or an array of RGBs?

Yeah, good question.

img itself is an array of RGAfs, encoded_img is then a string of gibberish.

Maybe I need to add something like a header in front of the gibberish?
Also tried image/jpeg but that did not work either …

i think the data string must be encoded PNG or JPEG file data, rather than nice arrays of plain RGB.

1 Like

You were right. Just encoding the raw content of the file is all that it needed:

using Base64
using Downloads
using FileIO

path = Downloads.download("https://github.com/MakieOrg/Makie.jl/raw/master/assets/doge.png")
img = read(path)
encoded_img = base64encode(img)

w, h = 100, 100

svg = """
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<image width="$w" height="$h" xlink:href="data:image/png;base64,$encoded_img"/>
</svg>
"""

write("/tmp/mwe.svg", svg)

Thank you!

Cool, glad it’s working.

Another approach, just for fun:

using Luxor
using Downloads
using FileIO
path = Downloads.download("https://github.com/MakieOrg/Makie.jl/raw/master/assets/doge.png")
img = load(path)
img = img[1:3:end, end:-3:1]
Drawing(200, 200, "/tmp/doge.svg")
origin()
placeimage(img, boxtopleft())
fontsize(20)
sethue("black")
text("One Doge", O + (0, 40), halign=:center)
finish()
preview()

1 Like

Oh nice. Really need to check out Luxor.jl at some point.

One thing: Your picture shows some rendering artifacts similar to what we observed with CairoMakie.jl recently: Artificial colored edges in CairoMakie when saving an image to PDF. · Issue #2162 · MakieOrg/Makie.jl · GitHub
Here is the fix: use premultiplied alpha for cairo argb surfaces by jkrumbiegel · Pull Request #2304 · MakieOrg/Makie.jl · GitHub
Maybe that could be applied to Luxor too?

Oh, I thought that was just the resampling … :joy:

I remember some encounters with premultiplied alpha a few years ago. I’ll investigate, on some rainy day soon…