/* * Copyright © 2016 Adrian Johnson * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Author: Adrian Johnson */ /* Compilation: * Build cairo with -DDEBUG_SVG_RENDER * gcc -o svg-render svg-render.c `pkg-config --cflags --libs cairo librsvg` */ /* svg-render - render SVG files using both the cairo glyph renderer and librsvg. * * This allows testing the cairo SVG test cases before assembling them into an SVG font. * Usage: * svg-render [-b] [-s] [-g ] [-e ] input.svg * * Output is written to input.cairo.png and input.rsvg.png. * * -b print bounding box. * -s Use standard SVG viewport. See below. * -g render glyph with id * -e set the EM size. The default is 1000. * * SVG Glyphs are assumed to be wholely within the view port. */ #include #include #include #include typedef enum { CAIRO_SVG, LIBRSVG } svg_renderer_t; /* output image size */ #define WIDTH 1000 #define HEIGHT 1000 static cairo_bool_t bbox = FALSE; static cairo_bool_t standard_svg = FALSE; static const char *glyph_id = NULL; static int em_size = 1000; cairo_bool_t _cairo_debug_svg_render (cairo_t *cr, const char *svg_document, const char *element, double units_per_em, int debug_level); static void cairo_render (const char *svg_document, cairo_t *cr) { if (!_cairo_debug_svg_render (cr, svg_document, glyph_id, em_size, 2)) printf("_cairo_debug_svg_render() failed\n"); } static void librsvg_render (const char *svg_document, cairo_t *cr) { gboolean has_width; gboolean has_height; gboolean has_viewbox; RsvgLength svg_width; RsvgLength svg_height; RsvgRectangle svg_viewbox; RsvgRectangle viewport; double width, height; GError *error = NULL; RsvgHandle *handle = rsvg_handle_new_from_data ((guint8 *)svg_document, strlen(svg_document), &error); if (!handle) { printf ("Could not load: %s", error->message); return; } /* Default width if not specified is EM Square */ width = em_size; height = em_size; /* Get width/height if specified. */ rsvg_handle_get_intrinsic_dimensions(handle, &has_width, &svg_width, &has_height, &svg_height, &has_viewbox, &svg_viewbox); if (has_width) width = svg_width.length; if (has_height) height = svg_height.length; /* We set the viewport for the call rsvg_handle_render_layer() to * width/height. That way if either dimension is not specified in * the SVG it will be inherited from the viewport we provide. * * As this scales up the rendered dimensions by width/height we * need to undo this scaling to get a unit square scale that * matches the cairo SVG renderer scale. The OpenType SVG spec * does not say what to do if width != height. In this case we * will just use a uniform scale that ensures the viewport fits in * the unit square and also center it. */ if (width > height) { cairo_scale (cr, 1.0/width, 1.0/width); cairo_translate (cr, 0, (width - height)/2.0); } else { cairo_scale (cr, 1.0/height, 1.0/height); cairo_translate (cr, (height - width)/2.0, 0); } viewport.x = 0; viewport.y = 0; viewport.width = width; viewport.height = height; if (!rsvg_handle_render_layer (handle, cr, glyph_id, &viewport, &error)) { printf ("librsvg render failed: %s", error->message); return; } } static void render_svg (const char *svg_document, svg_renderer_t renderer, const char* out_file) { double x, y, w, h; cairo_surface_t *recording = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL); cairo_t *cr = cairo_create (recording); /* Scale up to image size when recording to reduce rounding errors * in cairo_recording_surface_ink_extents() */ cairo_scale (cr, WIDTH, HEIGHT); if (renderer == CAIRO_SVG) { cairo_render (svg_document, cr); } else { librsvg_render (svg_document, cr); } cairo_destroy (cr); if (bbox) { cairo_recording_surface_ink_extents (recording, &x, &y, &w, &h); if (renderer == CAIRO_SVG) printf("cairo "); else printf("librsvg"); printf(" bbox left: %d right: %d top: %d bottom: %d\n", (int)floor(x), (int)ceil(x + w), (int)floor(y), (int)ceil(y + h)); } cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, WIDTH, HEIGHT); cr = cairo_create (surface); /* If rendering a glyph need to translate base line to bottom of image */ if (standard_svg) cairo_set_source_surface (cr, recording, 0, 0); else cairo_set_source_surface (cr, recording, 0, HEIGHT); cairo_paint (cr); cairo_surface_write_to_png (surface, out_file); cairo_surface_destroy (surface); } static char * create_output_name (const char *svg_file, svg_renderer_t renderer) { char buf[1000]; int len; strcpy (buf, svg_file); len = strlen (buf); if (strlen (buf) > 5 && strcmp (buf + len - 4, ".svg") == 0) buf[len - 4] = 0; if (renderer == CAIRO_SVG) strcat (buf, ".cairo.png"); else strcat (buf, ".rsvg.png"); return strdup (buf); } static char * read_file(const char *filename) { FILE *fp; int len; char *data; fp = fopen (filename, "r"); if (fp == NULL) return NULL; fseek (fp, 0, SEEK_END); len = ftell(fp); rewind (fp); data = malloc (len + 1); if (fread(data, len, 1, fp) != 1) return NULL; data[len] = 0; fclose(fp); return data; } static void usage_and_exit() { printf ("svg-render [-b] [-s] [-g ] [-e ] input.svg\n"); exit (1); } int main(int argc, char **argv) { const char *input_file = NULL; char *svg_document; char *output_file; argc--; argv++; while (argc > 0) { if (strcmp (argv[0], "-b") == 0) { bbox = TRUE; argc--; argv++; } else if (strcmp (argv[0], "-s") == 0) { standard_svg = TRUE; argc--; argv++; } else if (strcmp (argv[0], "-g") == 0) { if (argc > 1) { glyph_id = argv[1]; argc -= 2; argv += 2; } else { usage_and_exit(); } } else if (strcmp (argv[0], "-e") == 0) { if (argc > 1) { em_size = atoi (argv[1]); if (em_size <= 0) { usage_and_exit(); } argc -= 2; argv += 2; } else { usage_and_exit(); } } else { input_file = argv[0]; argc--; argv++; } } if (!input_file) usage_and_exit(); svg_document = read_file (input_file); if (!svg_document) { printf("error reading file %s\n", input_file); exit (1); } output_file = create_output_name (input_file, CAIRO_SVG); render_svg (svg_document, CAIRO_SVG, output_file); free (output_file); output_file = create_output_name (input_file, LIBRSVG); render_svg (svg_document, LIBRSVG, output_file); free (output_file); free (svg_document); return 0; }