JuliaGraphics / Luxor.jl

Simple drawings using vector graphics; Cairo "for tourists!"
http://juliagraphics.github.io/Luxor.jl/
Other
590 stars 71 forks source link

placeimage and readsvg for inserting SVG graphics #130

Closed schneiderfelipe closed 3 years ago

schneiderfelipe commented 3 years ago

Is there a way to insert a SVG file in a drawing? Something along what is possible with PNG files,

svg = readsvg("/tmp/a.svg")
placeimage(svg, O; centered=true)

If no, could this be made available?

(There is no Cairo.read_from_svg - in contrast to the Cairo.read_from_png used in readpng; Cairo doesn't read SVG files, I suppose -, but Rsvg.jl is able to deliver a handle to Cairo.jl: a draft solution is available here.)

cormullion commented 3 years ago

Yes I think this is a good idea - Rsvg.jl is another (binary) dependency, but it might be worth the (minor) additional cost.

using Luxor, Rsvg

struct SVGimage
    im::RsvgHandle
    xpos::Float64
    ypos::Float64
    width::Float64
    height::Float64
end

function readsvg(fname)
    r = Rsvg.handle_new_from_file(fname)
    d = Rsvg.handle_get_dimensions(r)
    return SVGimage(r, d.em, d.ex, d.width, d.height)
end

function Luxor.placeimage(im::SVGimage, pos; 
        centered=false)
    if centered == true
        w, h = im.width, im.height
        pos = pos - ((w/2), (h/2))
    end
    @layer begin
        translate(pos)
        Rsvg.handle_render_cairo(Luxor.get_current_cr(), im.im)
    end
end

This gives you readsvg(filename) and placeimage(svgimg, pt, centered=true), consistent with existing PNG functions.

Then:

@png begin
    svgimg = readsvg("/tmp/svgimage.svg")
    pngimg = readpng("/tmp/pngimage.png")
    for (n, pt) in enumerate(first.(Tiler(600, 600, 5, 5)))
        @layer begin
            translate(pt)
            isodd(n) && placeimage(svgimg, O, centered=true)
            iseven(n) && placeimage(pngimg, O, centered=true)
        end 
    end
end 600 600 "/tmp/rsvg-test.png"
Screenshot 2021-01-08 at 11 57 17

If it works fairly reliably I'll add it to the next version.

Rsvg requires Julia 1.3, so this addition would obviously not be available for older versions of Julia. Perhaps when 1.6 is released this would be a good time to move on from supporting Julia 1.0 - 1.2.

schneiderfelipe commented 3 years ago

Awesome! In particular, that will enable embedding vectorized math equations (we talked about LaTeXStrings in #124; support for SVG rendering in Latexify is on its way, see korsbo/Latexify.jl#133).

hustf commented 3 years ago

I would also like to be able to include svg from other sources, especially latex. But is the dependency necessary? Couldn't the interface be a cairo context?

If a direct dependency, some say one would need to also distribute Luxor under a GPL license. If referring Rsvg.jl is left to the user, the user would inherit that obligation instead.

schneiderfelipe commented 3 years ago

If a direct dependency, some say one would need to also distribute Luxor under a GPL license. If referring Rsvg.jl is left to the user, the user would inherit that obligation instead.

Although it is true for binaries that link to GPL software statically, I'm not sure what it would mean for Julia code. First, that would be source code, not binaries. Second, JIT-compiled functions are dynamic (aren't they?). In essence, statically compiled binaries contain the dependencies, which is not the case for Julia packages. Or am I missing something?

cormullion commented 3 years ago

Rsvg.jl is licensed as "MIT "Expat"".. librsvg is LGPL, I think. So I can't release a version of Luxor.jl unless I'm sure that it's OK to do so license-wise.

hustf commented 3 years ago

Licensing is a rabbit hole for sure. I would encourage everybody to just keep programming, because the same issues would possibly apply to Cairo itself, and possibly still to Julia. I found this thread interesting, but still a time thief.

schneiderfelipe commented 3 years ago

LibSndFile.jl is licensed as MIT, even though libsndfile is both LGPL-2.1 and LGPL-3. According to them,

libsndfile is licensed under the LGPL, which is very permissive providing that libsndfile is dynamically linked. LibSndFile.jl is licensed under the MIT license, allowing you to statically compile the wrapper into your Julia application. Remember that you must still abide by the terms of the libsndfile license when using this wrapper, in terms of whether libsndfile is statically or dynamically linked.

The developers of libsndfile confirm this practice,

You can use libsndfile with Free Software, Open Source, proprietary, shareware or other closed source applications as long as libsndfile is used as a dynamically loaded library and you abide by a small number of other conditions (read the LGPL for more info). With applications released under the GNU GPL you can also use libsndfile statically linked to your application.

(Emphasis mine.)

So I think it is fine to depend on Rsvg.jl/librsvg as long as there is no static linking (i.e. wrappers are fine). But this is as far as I understand, I am not an attorney, and none of this should not be considered legal advice.

cormullion commented 3 years ago

I've added this to the current master branch (2.8.0) to as to give it some testing.

hustf commented 3 years ago

Briefly tested with 'Inkscape' svg files, works as a charm so far.

hustf commented 3 years ago

Please consider PR #133 for inclusion?

cormullion commented 3 years ago

Thanks!