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

Time:08-20

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?

CodePudding user response:

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);
}
  • Related