As an exercise to learn genx, Tim Bray's new C library for generating XML, I used it the generate SVG pictures. (I suggested the name of the library, so I guess I should try to use it :)
I used this same model to exercise a Java SVG generation library I wrote a while back and it was interesting to compare approaches of Java vs. C.
The general flow of the programs is to randomly generate graphic elements with varying parameters like size and position. The pictures and SVG code you see here were inspired by some Ruby scripts posted to the SVG Yahoo group.
The pictures, which remind me a bit of Piet Mondrian's work, are JPEG files rendered at 0.75 quality, with the Batik SVG rasterizer.
Note that these programs lack proper error checking, but they show the basic patterns.
The first attempt: circle.c -- creates circles on a 500x500 canvas, with randomly generated size, color (rgb triples, 0-255), and position.
/*
* generate random "bubbles" in SVG
*
* Anthony Starks
* Licensed under the Creative Commons
* Attribution-NonCommercial-ShareAlike license
*/
#include "genx.h"
int main(int argc, char *argv[])
{
genxWriter w;
int n;
char cy[10], cx[10], cr[10], rgb[20];
srandomdev();
if (argc > 1)
n = atoi(argv[1]);
else
n = 250;
w = genxNew(NULL, NULL, NULL);
genxStartDocFile(w, stdout);
genxStartElementLiteral(w, NULL, "svg");
genxAddAttributeLiteral(w, NULL, "width", "500");
genxAddAttributeLiteral(w, NULL, "height", "500");
genxStartElementLiteral(w, NULL, "rect");
genxAddAttributeLiteral(w, NULL, "x", "0");
genxAddAttributeLiteral(w, NULL, "y", "0");
genxAddAttributeLiteral(w, NULL, "width", "500");
genxAddAttributeLiteral(w, NULL, "height", "500");
genxAddAttributeLiteral(w, NULL, "fill", "black");
genxEndElement(w);
while (n--) {
snprintf(cx, 10, "%ld", random() % 500);
snprintf(cy, 10, "%ld", random() % 500);
snprintf(cr, 10, "%ld", random() % 50);
snprintf(rgb, 20, "rgb(%03ld,%03ld,%03ld)",
random() % 255, random() % 255, random() % 255);
genxStartElementLiteral(w, NULL, "circle");
genxAddAttributeLiteral(w, NULL, "cx", cx);
genxAddAttributeLiteral(w, NULL, "cy", cy);
genxAddAttributeLiteral(w, NULL, "r", cr);
genxAddAttributeLiteral(w, NULL, "fill", rgb);
genxAddAttributeLiteral(w, NULL, "stroke", "black");
genxAddAttributeLiteral(w, NULL, "stroke-width", "3");
genxEndElement(w);
}
genxEndElement(w);
genxEndDocument(w);
}
The next refactoring, added the additional random selections of square or circle.
The next iteration uses predeclared items and additionally makes the shapes either solid or translucent (opacity = 0.5).
/*
* grand -- generate random graphics in SVG
*
* Anthony Starks
* Licensed under the Creative Commons
* Attribution-NonCommercial-ShareAlike license
*/
#include "genx.h"
int main(int argc, char *argv[])
{
genxWriter w;
genxStatus status;
int n, shapeflag, opflag;
char chx[10], chy[10], chr[10], rgb[20], opval[20];
genxElement circle, rect;
genxAttribute cx, cy, rad, x, y, width, height, fill, stroke, opacity;
srandomdev();
if (argc > 1)
n = atoi(argv[1]);
else
n = 250;
w = genxNew(NULL, NULL, NULL);
if (!(circle = genxDeclareElement(w, NULL, "circle", &status))) gerr(w);
if (!(cx = genxDeclareAttribute(w, NULL, "cx", &status))) gerr(w);
if (!(cy = genxDeclareAttribute(w, NULL, "cy", &status))) gerr(w);
if (!(rad = genxDeclareAttribute(w, NULL, "r", &status))) gerr(w);
if (!(fill = genxDeclareAttribute(w, NULL, "fill", &status))) gerr(w);
if (!(stroke = genxDeclareAttribute(w, NULL, "stroke", &status))) gerr(w);
if (!(opacity = genxDeclareAttribute(w, NULL, "opacity", &status))) gerr(w);
if (!(rect = genxDeclareElement(w, NULL, "rect", &status))) gerr(w);
if (!(x = genxDeclareAttribute(w, NULL, "x", &status))) gerr(w);
if (!(y = genxDeclareAttribute(w, NULL, "y", &status))) gerr(w);
if (!(width = genxDeclareAttribute(w, NULL, "width", &status))) gerr(w);
if (!(height = genxDeclareAttribute(w, NULL, "height", &status))) gerr(w);
genxStartDocFile(w, stdout);
svgstart(w, 500, 500);
genxStartElement(rect);
genxAddAttribute(width, "500");
genxAddAttribute(height, "500");
genxAddAttribute(fill, "white");
genxEndElement(w);
genxStartElementLiteral(w, NULL, "g");
genxAddAttributeLiteral(w, NULL, "style", "stroke: black; stroke-width: 2");
while (n--) {
shapeflag = (int)random() % 100;
opflag = (int)random() % 100;
snprintf(chx, 10, "%ld", random() % 500);
snprintf(chy, 10, "%ld", random() % 500);
snprintf(chr, 10, "%ld", random() % 50);
snprintf(rgb, 20, "rgb(%03d,%03d,%03d)",
(int)random() % 255, (int)random() % 255, (int)random() % 255);
snprintf(opval, 10, "%.1f", opflag <= 50 ? 1.0 : 0.5);
if (shapeflag >= 50 ) {
genxStartElement(circle);
genxAddAttribute(cx, chx);
genxAddAttribute(cy, chy);
genxAddAttribute(rad, chr);
genxAddAttribute(fill, rgb);
genxAddAttribute(opacity, opval);
genxEndElement(w);
}
else {
genxStartElement(rect);
genxAddAttribute(x, chx);
genxAddAttribute(y, chy);
genxAddAttribute(width, chr);
genxAddAttribute(height, chr);
genxAddAttribute(fill, rgb);
genxAddAttribute(opacity, opval);
genxEndElement(w);
}
}
genxEndElement(w);
genxEndElement(w);
genxEndDocument(w);
}
svgstart(genxWriter w, int width, int height) {
genxStartElementLiteral(w, NULL, "svg");
genxAddAttributeLiteral(w, NULL, "width", "500");
genxAddAttributeLiteral(w, NULL, "height", "500");
}
gerr(genxWriter w) {
fprintf(stderr, "genx error: %s\n", genxLastErrorMessage(w));
exit(1);
}
The command line:
cc grand.c libgenx.a && ./a.out
| tidy -i -xml >f.xml
builds the program, and pretty prints the output via the tidy program; this way I can preview the generated xml in my editor window, and also inspect the graphic in the Batik SVG browser. It's fun playing with these; every picture is different and you can tell by quick inspection if the desired effect is working.
It's interesting to compare the sizes of the various representations. Here are the sizes of files that contain 100 circles and squares in varying colors and opacities on a 500x500 canvas:
| Format | Size (bytes) | File |
| SVG | 8,612 | 100.svg |
| SVG (pretty-printed) | 9,523 | 100pp.svg |
| SVG (gzipped) | 1,948 | 100.svg.gz |
| 92,374 | 100.pdf | |
| PNG | 69,027 | 100.png |
| JPG (no compression) | 110,303 | 100.jpg |
| JPG (0.75 quality) | 33,042 | 100.75.jpg |
| JPG (0.50 quality) | 24,488 | 100.50.jpg |
Draw your own conclusions.
Final note: these programs are very fast (with no user-perceptible difference between generation via Literal vs. Pre-Declared items). On my 667 Mhz PowerBook:
./grand 1000 >1000.svg
runs in 0.131 seconds of real time.