Search code examples
csvggtk3cairo

Possible to export GtkWidget cairo_t drawing to vector image format (svg)?


I have found an application (ufsm/ufsm-compose), which uses Cairo internally, to allow for vector drawing in the application GUI canvas.

I'd like to try to export the canvas drawing as a vector image - primarily SVG - with minimal changes to the program, however, I'm not sure whether it is possible.

This application uses gtk_drawing_area_new to instantiate a GtkWidget (ufsmm_canvas_new in ufsm-compose/controller.c), and then a draw_cb callback is made to run on draw event - similar to the approach here:

Then, draw_cb "automagically" receives a reference to cairo_t, and uses that in calls to rendering functions, that use typical cairo_rectangle etc draw commands (see ufsmm_canvas_render in ufsm-compose/render.c).

However, I'm not really sure whether I can export these drawings somehow in a vector image (SVG). For instance, on this page:

... I can see that for SVG, one should call cairo_svg_surface_create - however, the ufsm-compose application does not use this command (in fact, there is no mention of the word "surface" anywhere in the ufsm-compose code -- which, otherwise, figures also in say cairo_image_surface_create (https://www.cairographics.org/tutorial/) which is used for bitmap images).

So, what are my options in exporting this drawing as an SVG (or other vector format)? Could I get away with instantiating a cairo_svg_surface_create upon export command, then somehow copying the application canvas' cairo_t to this SVG, and then finally save the SVG? If so - how exactly do I do this - can a full example be found on the Internet?


Solution

  • Could I get away with instantiating a cairo_svg_surface_create upon export command, then somehow copying the application canvas' cairo_t to this SVG, and then finally save the SVG? If so - how exactly do I do this - can a full example be found on the Internet?

    Looking at the code of the draw_cb, one finds:

        struct ufsmm_canvas *priv =
                        g_object_get_data(G_OBJECT(widget), "canvas private");
    
        gint width, height;
        GtkAllocation allocation;
    
        gtk_widget_get_allocation(widget, &allocation);
    
        width = allocation.width;
        height = allocation.height;
    
        priv->cr = cr;
        //priv->menu->cr = cr;
        priv->window_width = width;
        priv->window_height = height;
    
        ufsmm_canvas_render(priv, width, height);
        ufsmm_nav_render(priv, width, height);
        //menu_render(priv->menu, priv->theme, priv->selection, width, height);
        uc_status_render(priv, width, height);
    

    So, apparently, the state of the application is kept in a struct ufsmm_canvas. When you have such a canvas, you have to decide on a size of your drawing and then there are just three functions to call to do the drawing.

    So, to export the drawing, one could do (completely untested):

    void export_drawing(struct ufsmm_canvas *priv, int width, int height, const char* filename) {
        cairo_surface_t *surface = cairo_svg_surface_create(filename, width, height);
        cairo_t *cr = cairo_create(surface);
    
        priv->cr = cr;
        priv->window_width = width;
        priv->window_height = height;
    
        ufsmm_canvas_render(priv, width, height);
        ufsmm_nav_render(priv, width, height);
        //menu_render(priv->menu, priv->theme, priv->selection, width, height);
        uc_status_render(priv, width, height);
    
        cairo_destroy(cr);
        cairo_destroy(surface);
    } 
    

    EDIT: here is a tested version, along with a quick-n-dirty hack, so svg image always gets exported upon a save command:

    #include <cairo-svg.h>
    ...
    void export_drawing(struct ufsmm_canvas *priv, int width, int height) {
        printf("export_drawing ufsm_out.svg ...\n");
        cairo_surface_t *surface = cairo_svg_surface_create("ufsm_out.svg", width, height);
        cairo_t *cr = cairo_create(surface);
    
        cairo_t *old_cr = priv->cr;
        priv->cr = cr;
    
        ufsmm_canvas_render(priv, width, height);
    
        cairo_destroy(cr);
        cairo_surface_destroy(surface);
        priv->cr = old_cr;
        printf("export_drawing DONE\n");
    }
    
    void canvas_save(void *context)
    {
        struct ufsmm_canvas *priv = (struct ufsmm_canvas *) context;
    
        if (priv->model->filename == NULL) {
            canvas_save_as(context);
        } else {
            L_DEBUG("%s: writing to '%s'", __func__, priv->model->filename);
            remove_dangling_guard_refs(priv);
            ufsmm_model_write(priv->model->filename, priv->model);
            uc_rstatus_set(false);
        }
        export_drawing(priv, priv->window_width, priv->window_height);
    }