Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Distinguishing between CairoSVGSurface and CairoPDFSurface #316

Open
MaximeBouton opened this issue Mar 4, 2020 · 5 comments
Open

Distinguishing between CairoSVGSurface and CairoPDFSurface #316

MaximeBouton opened this issue Mar 4, 2020 · 5 comments

Comments

@MaximeBouton
Copy link

When trying to write a cairo surface to a file, it is not possible to distinguish whether the surface object is a pdf or svg surface.

MWE:

using Cairo 

function Base.write(filename::String, surface::Cairo.CairoSurfaceIOStream)
    finish(surface)
    seek(surface.stream, 0)
    open(filename, "w") do io
        write(io, read(surface.stream, String))
    end
    return
end

c = CairoSVGSurface(IOBuffer(), 1000, 600);
write("output.svg", c) # this works fine 
write("output.pdf", c) # this would also have worked but the output file would not really be a pdf

Is there a convenient way to enforce that the file extension matches the type of Cairo stream?
Right now both CairoSVGSurface and CairoPDFSurface outputs a CairoSurfaceIOStream which makes it not possible to dispatch between the two.

@lobingera
Copy link
Contributor

What are you trying to do? The logic of libcairo is, that you decide the type of surface before starting to draw and to render. If you are looking for a lazy/late decision, you might look at recordingSurfaces.
btw: the non-stream (with filename) surface constructors still exist (although libcairo documentation discourages the use).

@MaximeBouton
Copy link
Author

We have a visualization package that relies on Cairo: https://github.com/sisl/AutoViz.jl
The main usage is through a render function, that accepts a CairoSurface as keyword argument (default to CairoSVGSurface).

Now we would like users to be able to save the rendered scenes in different format using write.
If they want a PDF they would have to pass in a CairoPDFSurface to the function which is fine, but we were wondering if there was a way to warn user when they try to do something like this:

c = render([roadway, veh1, veh2], camera=CarFollowCamera(...))  # default to SVGSurface 
write("scene.pdf", c) 

This code will run fine but the user might think they have a pdf and instead they have an svg.

The correct way would have been to do:

c = render([roadway, veh1, veh2], camera=CarFollowCamera(...), surface=CairoPDFSurface(...))
write("scene.pdf", c) 

@lobingera
Copy link
Contributor

I see.
a) One could get the 'type' of a surface by looking via the surface type ptr into the c-struct of a cairo surface i.e.

struct _cairo_surface {
    const cairo_surface_backend_t *backend;
    cairo_device_t *device;

    /* We allow surfaces to override the backend->type by shoving something
     * else into surface->type. This is for "wrapper" surfaces that want to
     * hide their internal type from the user-level API. */
    cairo_surface_type_t type;

    cairo_content_t content;

    cairo_reference_count_t ref_count;
    cairo_status_t status;

b) workaround could be to look at the actual stream, as both PDF and SVG have headers (files need to start with a sequence of know bytes)

c) take the information along your 'c' output (type) of the render function.
d) ... as someone not believing in file extensions -> you cannot avoid users to do wrong.

@MaximeBouton
Copy link
Author

Thanks for the tips, I will give a shot to a)!

@MaximeBouton
Copy link
Author

this worked:

typ = ccall((:cairo_surface_get_type, Cairo.libcairo), Cint, (Ptr{Nothing},), c.ptr)
if typ == Cairo.CAIRO_SURFACE_TYPE_PDF 
    # do stuff 
end 

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants