diff options
Diffstat (limited to 'test/svg/svg-render.c')
-rw-r--r-- | test/svg/svg-render.c | 308 |
1 files changed, 308 insertions, 0 deletions
diff --git a/test/svg/svg-render.c b/test/svg/svg-render.c new file mode 100644 index 000000000..8f67ebdc2 --- /dev/null +++ b/test/svg/svg-render.c @@ -0,0 +1,308 @@ +/* + * 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 <ajohnson@redneon.com> + */ + +/* 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 <id>] [-e <em_size> ] 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 <id> + * -e set the EM size. The default is 1000. + * + * SVG Glyphs are assumed to be wholely within the view port. + */ + +#include <stdio.h> +#include <math.h> +#include <cairo.h> +#include <librsvg/rsvg.h> + +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 <id>] [-e <em_size> ] 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; +} |