[cairo] redesigning the arguments for svg2png

Jason Dorje Short jdorje at users.sf.net
Fri Jul 8 20:27:20 PDT 2005


Jason Dorje Short wrote:

> Thus I suggest:
> 
>   --width=$WIDTH (-w)
>   --height=$HEIGHT (-h)
>   --xscale=$X_SCALE (-x)
>   --yscale=$Y_SCALE (-y)
>   --flipx (-X maybe?)
>   --flipy (-Y maybe?)
>   --scale=$SCALE (-s)
>   --dpi=$DPI (-d)
>   --pad [1]
>   --stretch

Here is a complete patch that implements this (except --dpi which isn't
possible).  I also updated the manpage.

Note that --xscale is ignored if --width or --height is given.  I'm not
positive this is the best behavior.  For instance you might want to say
--xscale=0.5 with --height=50 to change the aspect ratio while also
setting the height, but as it is you cannot do this.  You'd instead have
to do --xscale=0.42 and --yscale=0.84 (or whatever) to get the exact
right size and aspect ratio you want.  (Of course this behavior wasn't
possible before either.)

Another thing I changed is that svg2png no longer defaults to using
stdin and stdout; you have to tell it explicitly.  This means if you
just run "svg2png" you'll get the more reasonable behavior of a usage
summary.

In summary this patch increases the range of possible behaviors.  The
existing behavior is still possible but is no longer the default.  The
new defaults corresponds to what rsvg and inkscape do, and the available
options are more-or-less compatible with those programs (but see my
previous mail for more on this).

Also this fixes certain bugs where "svg2png -x" and "svg2png -w 0" would
give wrong behavior.  Of course there may be new bugs introduced too.

-jason
-------------- next part --------------
? src/foo.png
? src/galicia.svg
Index: doc/svg2png.1
===================================================================
RCS file: /cvs/cairo/svg2png/doc/svg2png.1,v
retrieving revision 1.2
diff -p -u -r1.2 svg2png.1
--- doc/svg2png.1	11 Jun 2004 12:49:58 -0000	1.2
+++ doc/svg2png.1	9 Jul 2005 03:23:00 -0000
@@ -19,24 +19,35 @@ Renders the given SVG file to the given 
 .SH OPTIONS
 .TP
 .B \-w, \-\-width=\fIWIDTH\fR
-Scale the image so that the output has a width of \fIWIDTH\fR pixels. The original aspect ratio is preserved.
+Scale the image so that the output has a width of \fIWIDTH\fR pixels. The original aspect ratio is preserved unless --height is also given.
 .TP
 .B \-h, \-\-height=\fIHEIGHT\fR
-Scale the image so that the output has a height of \fIHEIGHT\fR pixels. The original aspect ratio is preserved.
+Scale the image so that the output has a height of \fIHEIGHT\fR pixels. The original aspect ratio is preserved unless --width is also given.
 .TP
-If both the --width and --height options are provided, the image will be scaled to the smaller dimension and will be centered within any extra space.
+If both the --width and --height options are provided, the behavior is controlled by the presence or absence of --pad and --stretch.
 .TP
 .B \-s, \-\-scale=\fIFACTOR\fR
 Scale image by \fIFACTOR\fR. This option is ignored if either of the --width or --height options are provided.
 .TP
-.B \-x, \-\-flipx
+.B \-x, \-\-xscale=\fIFACTOR\fR
+Scale image by \fIFACTOR\fR in the X direction. This option is ignored if either of the --width or --height options are provided.  This option will override the --scale option.
+.TP
+.B \-y, \-\-yscale=\fIFACTOR\fR
+Scale image by \fIFACTOR\fR in the Y direction. This option is ignored if either of the --width or --height options are provided.  This option will override the --scale option.
+.TP
+.B \-X, \-\-flipx
 Flip the output X coordinates.
 .TP
-.B \-y, \-\-flipy
+.B \-Y, \-\-flipy
 Flip the output Y coordinates.
 .TP
-.B \-\-usage
-Give a short usage message.
+.B \-p, \-\-pad
+If both --width and --height are given, then draw the image at the correct aspect ratio, centered within a PNG file of given width and height.
+.TP
+.B \-t, \-\-stretch
+If both --width and --height are given, then change the image's aspect ratio and stretch it to fill the full PNG file of the given width and height.  --stretch will supercede --pad.
+.TP
+If but --width and --height but neither --pad or --stretch is given then the aspect ratio is preserved and the image is sized to fit within the given width and height, and the PNG file will have the same size as the image.
 .TP
 .B \-?, \-\-help
 Give a longer help list.
Index: src/args.c
===================================================================
RCS file: /cvs/cairo/svg2png/src/args.c,v
retrieving revision 1.4
diff -p -u -r1.4 args.c
--- src/args.c	26 Apr 2005 20:03:33 -0000	1.4
+++ src/args.c	9 Jul 2005 03:23:00 -0000
@@ -41,24 +41,39 @@ static const char ARGS_PROGRAM_VERSION[]
 static const char ARGS_PROGRAM_DESCRIPTION[] = "svg2png - Render an SVG image to a PNG image";
 static const char ARGS_PROGRAM_BUG_ADDRESS[] = "<cworth at isi.edu>";
 
-static const char ARGS_PROGRAM_ARGDOC[] = "[<SVG_file> [<PNG_file>]]";
+static const char ARGS_PROGRAM_ARGDOC[] = "<SVG_file> <PNG_file>";
 
-enum {
-    ARGS_VAL_HELP = 256,
-    ARGS_VAL_FLIPX,
-    ARGS_VAL_FLIPY
+enum args_val {
+    ARGS_VAL_HEIGHT = 'h',
+    ARGS_VAL_WIDTH = 'w',
+    ARGS_VAL_SCALE = 's',
+    ARGS_VAL_XSCALE = 'x',
+    ARGS_VAL_YSCALE = 'y',
+    ARGS_VAL_FLIPX = 'X',
+    ARGS_VAL_FLIPY = 'Y',
+    ARGS_VAL_PAD = 'p',
+    ARGS_VAL_STRETCH = 't',
+    ARGS_VAL_HELP = '?',
+    ARGS_VAL_VERSION = 'V'
 };
 
-static const char args_optstring[] = "h:s:w:xyV";
+static const char args_optstring[] = "h:w:s:x:y:XYpt?V";
 static struct option args_options[] = {
     /* name,		has_arg,	flag,	val */
-    {"height",		1,		0,	'h'},
-    {"scale",		1,		0,	's'},
-    {"width",		1,		0,	'w'},
+    {"height",		1,		0,	ARGS_VAL_HEIGHT},
+    {"width",		1,		0,	ARGS_VAL_WIDTH},
+    {"scale",		1,		0,	ARGS_VAL_SCALE},
+    {"xscale",          1,              0,      ARGS_VAL_XSCALE},
+    {"yscale",          1,              0,      ARGS_VAL_YSCALE},
+
     {"flipx",		0,		0,	ARGS_VAL_FLIPX},
     {"flipy",		0,		0,	ARGS_VAL_FLIPY},
+
+    {"pad",             0,              0,      ARGS_VAL_PAD},
+    {"stretch",         0,              0,      ARGS_VAL_STRETCH},
+
     {"help",		0,		0,	ARGS_VAL_HELP},
-    {"version",		0,		0,	'V'},
+    {"version",		0,		0,	ARGS_VAL_VERSION},
     { 0 }
 };
 
@@ -71,14 +86,40 @@ args_help (const char *argv0)
     printf ("Usage: %s [OPTIONS] %s\n", argv0_base, ARGS_PROGRAM_ARGDOC);
     printf ("%s - %s\n", argv0_base, ARGS_PROGRAM_DESCRIPTION);
     puts ("");
+    printf ("<SVG_file> may be - to read from standard input.\n");
+    printf ("<PNG_file> may be - to write to standard output.\n");
+    puts ("");
+#if 0
+    printf ("By default the image will be rendered at the SVG file's default\n");
+    printf ("size. This can be controlled by the following options:\n");
+#endif
     printf ("  -w, --width=WIDTH\tWidth of output image in pixels\n");
     printf ("  -h, --height=HEIGHT\tHeight of output image in pixels\n");
     printf ("  -s, --scale=FACTOR\tScale image by FACTOR\n");
+    printf ("  -x, --xscale=FACTOR\tScale image width by FACTOR\n");
+    printf ("  -y, --yscale=FACTOR\tScale image height by FACTOR\n");
+    puts ("");
+#if 0
+    printf ("Options to flip the image:\n");
+#endif
+    printf ("  -X, --flipx\t\tFlip X coordinates of image\n");
+    printf ("  -Y, --flipy\t\tFlip Y coordinates of image\n");
     puts ("");
-    printf ("  --flipx\t\tFlip X coordinates of image\n");
-    printf ("  --flipy\t\tFlip Y coordinates of image\n");
+#if 0
+    printf ("These options control the behavior when given a non-default aspect\n");
+    printf ("ratio.  The default is to render within the given width/height\n");
+    printf ("without any padding and preserving the aspect ratio.  The pad\n");
+    printf ("option will pad the image to the width/height while still preserving\n");
+    printf ("the aspect ratio.  The stretch option will stretch the image and change\n");
+    printf ("the aspect ratio.  These two options are incompatible.\n");
+#endif
+    printf ("  -p, --pad\t\tPad image to width/height (default off)\n");
+    printf ("  -t, --stretch\t\tStretch image to width/height (default off)\n");
     puts ("");
-    printf ("  --help\t\tGive this help list\n");
+#if 0
+    printf ("Program information options:\n");
+#endif
+    printf ("  -?, --help\t\tGive this help list\n");
     printf ("  -V, --version\t\tPrint program version\n");
 
     free (argv0_copy);
@@ -101,33 +142,42 @@ args_parse (args_t *args, int argc, char
 {
     int c;
 
-    args->svg_filename = "-";
-    args->png_filename = "-";
+    args->svg_filename = "";
+    args->png_filename = "";
+
     args->scale = 1.0;
+    args->xscale = 1.0;
+    args->yscale = 1.0;
 
     args->width = -1;
     args->height = -1;
+
     args->flipx = 0;
     args->flipy = 0;
 
+    args->pad = 0;
+    args->stretch = 0;
+
     while (1) {
 	c = getopt_long (argc, argv, args_optstring, args_options, NULL);
 	if (c == -1)
 	    break;
 
-	switch (c) {
-	case 'h':
+	switch ((enum args_val)c) {
+	case ARGS_VAL_HEIGHT:
 	    args->height = atoi (optarg);
 	    break;
-	case 's':
+	case ARGS_VAL_WIDTH:
+	    args->width = atoi (optarg);
+	    break;
+	case ARGS_VAL_SCALE:
 	    args->scale = atof (optarg);
 	    break;
-	case 'w':
-	    args->width = atoi (optarg);
+	case ARGS_VAL_XSCALE:
+	    args->xscale = atof (optarg);
 	    break;
-	case 'V':
-	    printf ("%s\n", ARGS_PROGRAM_VERSION);
-	    exit (0);
+	case ARGS_VAL_YSCALE:
+	    args->yscale = atof(optarg);
 	    break;
 	case ARGS_VAL_FLIPX:
 	    args->flipx = 1;
@@ -135,21 +185,27 @@ args_parse (args_t *args, int argc, char
 	case ARGS_VAL_FLIPY:
 	    args->flipy = 1;
 	    break;
+	case ARGS_VAL_PAD:
+	    args->pad = 1;
+	    break;
+	case ARGS_VAL_STRETCH:
+	    args->stretch = 1;
+	    break;
 	case ARGS_VAL_HELP:
 	    args_help (argv[0]);
 	    exit (0);
 	    break;
-	case '?':
-	    args_help (argv[0]);
-	    exit (1);
-	    break;
-	default:
-	    fprintf (stderr, "Unhandled option: %d\n", c);
-	    exit (1);
+	case ARGS_VAL_VERSION:
+	    printf ("%s\n", ARGS_PROGRAM_VERSION);
+	    exit (0);
 	    break;
 	}
     }
-	
+
+    if (argc - optind < 2) {
+        args_help (argv[0]);
+        exit(1);
+    }
     if (argc - optind >= 1) {
 	args->svg_filename = argv[optind++];
 	if (argc - optind >= 1) {
Index: src/args.h
===================================================================
RCS file: /cvs/cairo/svg2png/src/args.h,v
retrieving revision 1.1.1.1
diff -p -u -r1.1.1.1 args.h
--- src/args.h	27 Apr 2004 02:10:02 -0000	1.1.1.1
+++ src/args.h	9 Jul 2005 03:23:00 -0000
@@ -35,10 +35,18 @@ typedef struct args
     char *png_filename;
 
     double scale;
+    double xscale;
+    double yscale;
+
+    double dpi;
+
     int width;
     int height;
+
     int flipx;
     int flipy;
+    int pad;
+    int stretch;
 } args_t;
 
 int
Index: src/svg2png.c
===================================================================
RCS file: /cvs/cairo/svg2png/src/svg2png.c,v
retrieving revision 1.9
diff -p -u -r1.9 svg2png.c
--- src/svg2png.c	25 May 2005 16:07:35 -0000	1.9
+++ src/svg2png.c	9 Jul 2005 03:23:01 -0000
@@ -40,7 +40,7 @@
 #define MIN(a, b)     (((a) < (b)) ? (a) : (b))
 
 static svg_cairo_status_t
-render_to_png (FILE *svg_file, FILE *png_file, double scale, int width, int height);
+render_to_png (FILE *svg_file, FILE *png_file, const args_t *args);
 
 int 
 main (int argc, char **argv)
@@ -72,7 +72,7 @@ main (int argc, char **argv)
 	}
     }
 	       
-    status = render_to_png (svg_file, png_file, args.scale, args.width, args.height);
+    status = render_to_png (svg_file, png_file, &args);
     if (status) {
 	fprintf (stderr, "%s: failed to render %s\n", argv[0], args.svg_filename);
 	return 1;
@@ -114,9 +114,11 @@ write_surface_to_png_file (cairo_surface
 }
 
 static svg_cairo_status_t
-render_to_png (FILE *svg_file, FILE *png_file, double scale, int width, int height)
+render_to_png (FILE *svg_file, FILE *png_file, const args_t *args)
 {
     unsigned int svg_width, svg_height;
+    unsigned int png_width, png_height;
+    double scale, xscale, yscale;
 
     svg_cairo_status_t status;
     cairo_t *cr;
@@ -136,23 +138,71 @@ render_to_png (FILE *svg_file, FILE *png
 
     svg_cairo_get_size (svgc, &svg_width, &svg_height);
 
-    if (width < 0 && height < 0) {
-	width = (svg_width * scale + 0.5);
-	height = (svg_height * scale + 0.5);
-    } else if (width < 0) {
-	scale = (double) height / (double) svg_height;
-	width = (svg_width * scale + 0.5);
-    } else if (height < 0) {
-	scale = (double) width / (double) svg_width;
-	height = (svg_height * scale + 0.5);
+    /* xscale, yscale, or scale <= 0 means no option */
+    scale = args->scale > 0 ? args->scale : 1.0;
+    xscale = args->xscale > 0 ? args->xscale : args->scale;
+    yscale = args->yscale > 0 ? args->yscale : args->scale;
+
+    if (args->width <= 0 && args->height <= 0) {
+        /* Neither width or height are given.  This case is easy; we just
+	 * take the dimensions from the scale (or 1.0) and ignore stretch
+	 * and pad. */
+	png_width = (svg_width * xscale + 0.5);
+	png_height = (svg_height * yscale + 0.5);
+    } else if (args->width <= 0) {
+        /* Height is given but not width.  In this case we ignore the
+	 * scale values and preserve the aspect ratio. */
+        png_height = args->height;
+	xscale = yscale = (double) args->height / (double) svg_height;
+	png_width = (svg_width * xscale + 0.5);
+    } else if (args->height <= 0) {
+        /* Here width is given but not height.  This simply mirrors the 
+	 * case above. */
+        png_width = args->width;
+	xscale = yscale = (double) args->width / (double) svg_width;
+	png_height = (svg_height * yscale + 0.5);
     } else {
-	scale = MIN ((double) width / (double) svg_width, (double) height / (double) svg_height);
-	/* Center the resulting image */
-	dx = (width - (int) (svg_width * scale + 0.5)) / 2;
-	dy = (height - (int) (svg_height * scale + 0.5)) / 2;
+        /* Both width and height are given.  Now we ignore the xscale and
+         * yscale entirely!  But now pad and stretch must be considered. */
+        if (args->stretch) {
+	    /* Stretch the image to meet the given width and height. */
+	    png_width = args->width;
+	    png_height = args->height;
+	    xscale = (double) png_width / (double) svg_width;
+	    yscale = (double) png_height / (double) svg_height;
+	} else if (args->pad) {
+	    /* Make the PNG file of the given width and height, while the
+	     * image is centered within the PNG and keeps the same aspect
+	     * ratio. */
+	    png_width = args->width;
+	    png_height = args->height;
+
+	    xscale = yscale = MIN ((double) png_width / (double) svg_width,
+				   (double) png_height / (double) svg_height);
+
+	    /* Center the resulting image */
+	    dx = (png_width - (int) (svg_width * xscale + 0.5)) / 2.0;
+	    dy = (png_height - (int) (svg_height * yscale + 0.5)) / 2.0;
+	} else {
+	    /* Neither stretch nor pad.  Now the aspect ratio is preserved
+	     * and width/height are taken as the max possible dimensions. */
+	    xscale = yscale = MIN ((double) args->width / (double) svg_width,
+				   (double) args->height / (double) svg_height);
+	    png_width = (svg_width * xscale + 0.5);
+	    png_height = (svg_height * yscale + 0.5);
+	}
+    }
+    if (args->flipx) {
+      xscale = -1.0 * xscale;
+      dx = png_width - dx;
+    }
+    if (args->flipy) {
+      yscale = -1.0 * yscale;
+      dy = png_height - dy;
     }
 
-    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+					  png_width, png_height);
     cr = cairo_create (surface);
     
     cairo_save (cr);
@@ -161,7 +211,7 @@ render_to_png (FILE *svg_file, FILE *png
     cairo_restore (cr);
 
     cairo_translate (cr, dx, dy);
-    cairo_scale (cr, scale, scale);
+    cairo_scale (cr, xscale, yscale);
 
     /* XXX: This probably doesn't need to be here (eventually) */
     cairo_set_source_rgb (cr, 1, 1, 1);


More information about the cairo mailing list