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.

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()

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…