svgconvert

Name

svgconvert - Critcl code to convert svg images into png images or pdf files

Description

This is a short example on how to use the Cairo library with Critcl. The C code is wrapped in a namespace and only used internally by the Tcl procedures svg2pdf and svg2png.

# file svgconvert.tcl
package require critcl
package provide svgconvert 1.0


if {![critcl::compiling]} {
    puts stderr "This extension cannot be compiled without critcl enabled"
    exit 1
}
namespace eval svgconvert {
    # how to get all the include directories otherwise?
    set options [regsub -all -- -I [exec pkg-config --cflags --libs cairo --libs librsvg-2.0] "I "]
    set dirs [list]
    foreach {i dir} $options {
        if {$i eq "I"} {
            lappend dirs $dir
        }
    }
    critcl::clibraries -lrsvg-2 -lm -lgio-2.0 -lgdk_pixbuf-2.0 -lgobject-2.0 -lglib-2.0 -lcairo -pthread
    critcl::config I $dirs
    critcl::ccode {
        #include <string.h>
        #include <stdio.h>
        #include <cairo.h>
        #include <cairo/cairo-pdf.h>
        #include <librsvg/rsvg.h>
    }
    critcl::cproc svgconvert {char* svgfile  char* outfile double scalex double scaley} void {
        char *ext = ".pdf";
        char *pdf = strstr(outfile, ext);
        
        RsvgHandle *handle;
        RsvgDimensionData dimension_data;
        
        GError* err = NULL;
        handle = rsvg_handle_new_from_file(svgfile, &err);
        
        if (err != NULL) {
            fprintf(stderr, "libsvgconv: Failed to load svg: '%s'; %s\n", svgfile, (char*) err->message);
            g_error_free(err);
            err = NULL;
        }
        
        cairo_surface_t *surface;
        cairo_t *ctx;
        
        rsvg_handle_get_dimensions(handle, &dimension_data);
        double resx = ((double) dimension_data.width) * scalex;
        double resy = ((double) dimension_data.height) * scaley;
        if (pdf) {
            surface = cairo_pdf_surface_create(outfile, (int) resx, (int) resy); 
        } else {
            surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, (int) resx, (int) resy);
        }
        ctx = cairo_create(surface);
        
        cairo_set_source_rgba(ctx, 255, 255, 255, 0);
        cairo_scale(ctx, scalex, scaley);
        
        rsvg_handle_render_cairo(handle, ctx);
        cairo_paint(ctx);
        cairo_show_page(ctx);
        cairo_destroy(ctx);
        // Destroying cairo context
        cairo_surface_flush(surface);
        if (pdf) {
            // Destroying PDF surface
            cairo_surface_destroy(surface);
        } else {
            cairo_surface_write_to_png(surface, outfile);
            cairo_surface_destroy(surface);
        } 
    }
    proc svg2pdf {svgfile pdffile {scalex 1.0} {scaley 1.0}} {
        if {$scalex != $scaley} {
            set scaley $scalex
        }
        if {![file exists $svgfile]} {
            error "Error: File $svgfile does not exist!"
        }
        if {[file extension $pdffile] ne ".pdf"} {
            error "Error: File extension for $pdffile is not .pdf!"
        }
        svgconvert::svgconvert $svgfile $pdffile $scalex $scaley
    }
    proc svg2png {svgfile pngfile {scalex 1.0} {scaley 1.0}} {
        if {$scalex != $scaley} {
            set scaley $scalex
        }
        if {![file exists $svgfile]} {
            error "Error: File $svgfile does not exist!"
        }
        if {[file extension $pngfile] ne ".png"} {
            error "Error: File extension for $pngfile is not .png!"
        }
        svgconvert::svgconvert $svgfile $pngfile $scalex $scaley
    }
    namespace export svg2pdf svg2png

}

Test

source svgconvert.tcl
namespace import svgconvert::*
svg2pdf infile.svg outfile.pdf
svg2png infile.svg outfile.png

Todos/Ideas

  • Parsing command line arguments to make it an convert application
  • Postscript output (?)
  • width and height in pixels
  • embedding into [tsvg9 as alternative to cairosvg if this is not available
  • using cairo xrender to a Tk widget(?)
  • svg widget (displays actually the converted png in an label widget)

See also


Discussion

Please discuss here.

DDG - 2021-12-01 - Initial release, ideas welcome.