margarettrias.org

Scalable Vector Graphics (SVG)

November 30, 2011

This post assumes you have some familiarity with PHP. If that is not the case, I'd suggest looking at the php.net tutorial or the w3schools tutorial. SVG is an XML based format, so you may want to learn about XML at w3schools.com. Most modern browsers can display SVG images without trouble. If you are using a version of Internet Explorer prior to IE 9, you will not be able to view SVG. Stop now and download Chrome or Firefox!

Scalable Vector Graphics (SVG) are not like the most common image formats (things like JPEG and GIF) which are Raster Graphics. A raster image contains color information for each individual pixel, making for much larger files. In contrast, SVG files contain only the structural information necessary to recreate an image. For example, a simple circle could be described with only a few words about its position, radius, and color. So one of the benefits of SVG is image files are much smaller than the equivalent JPEG.

The name "Scalable Vector Graphic" implies another benefit: the ability to scale or zoom in without changing the image. For instance, if you blow up a JPEG image of a circle, the edges will become jagged. Anti-aliasing is an imperfect strategy which works only if you aren't changing the size of an object too much. But a circle in SVG is described only by its position and radius, so you can zoom in forever and always see a sharp edge (limited only by the resolution of your computer screen or printer).

SVG files can be created either with a simple text editor, or a graphical editor such as Inkscape. This post will focus on creating images in a text file using the primitive SVG commands. Full documentation of the latest version of SVG can be found at w3.org. Without further ado, let's get to an example.

Example 1.0 -- Rings



Here's some code to get you started writing a "Hello World!" Scalable Vector Graphic. It doesn't really spell out "Hello World!" (although SVG can contain text, that's not very exciting). Creating the images above required only slightly more sophistication, and some PHP, but I'll get to that.

  1. <?xml version="1.0" standalone="no"?>
  2. <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
  3. "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
  4. <svg width="850" height="850"
  5. xmlns="http://www.w3.org/2000/svg" version="1.1">
  6.  
  7. <!-- Definitions are for graphical objects that may be used multiple times.
  8. Examples of objects that are always referenced to a definition are:
  9. cursor, filter, linearGradient, marker, radialGradient, etc. -->
  10. <defs>
  11. <radialGradient id="mycircle" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
  12. <stop offset="0%" stop-color="rgb(146,85,143)" stop-opacity="0.04" />
  13. <stop offset="100%" stop-color="rgb(156,76,142)" stop-opacity="1" />
  14. </radialGradient>
  15. </defs>
  16.  
  17. <!-- Any graphics object can be rotated or translated. In this case,
  18. we rotate the ellipse by 67 degrees about the coordinate point
  19. (x=400, y=690). The <g></g> tag defines a group of objects. -->
  20. <g transform="rotate(67 400 690)">
  21. <!-- (cx, cy) is the coordinate of the center of the ellipse
  22. (rx, ry) is the radius of the ellipse in the horizontal and vertical
  23. directions. #mycircle references the gradient defined above. -->
  24. <ellipse cx="400" cy="690" rx="120" ry="200" fill="url(#mycircle)" />
  25. </g>
  26. </svg>

So that looks simple enough (maybe?), but I wanted to have loads of circles all positioned randomly, and I didn't want to have to figure out a color scheme by hand. This is where PHP comes in real handy. Here's the code that I developed to produce the lovely images shown above:

  1. <?php
  2. // This allows the browser to recognize the content and render it properly
  3. header('Content-Type: image/svg+xml');
  4.  
  5. // Writing text to the file requires 'echo' statements in php.
  6. // Remember to escape all of your quotation marks!
  7. echo "<?xml version=\"1.0\" standalone=\"no\"?> \n";
  8.  
  9. echo "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\"
  10. \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\"> \n";
  11.  
  12. echo "<svg width=\"850\" height=\"850\"
  13. xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\"> \n";
  14.  
  15. // CREATE DEFINITIONS
  16. echo "<defs> \n";
  17.  
  18. // number of circles
  19. $circles = 30;
  20.  
  21. // get some random opacities for each ring
  22. $opi = rand(1,25) * 0.01;
  23. $opf = rand(1,25) * 0.04;
  24.  
  25. // color scheme
  26. $rr = rand(0,255);
  27. $gg = rand(0,255);
  28. $bb = rand(0,255);
  29.  
  30. for($i=0; $i<$circles; $i++) {
  31.  
  32. // each bubble is made up of four rings, each ring of a slightly
  33. // different color (the 60 can be changed if you want to deviate further
  34. // from the main theme.The abs( ... ) is there to make sure you're
  35. // always between 0 and 255.
  36. $ri = 255 - abs(255 - ($rr + rand(0,60)));
  37. $gi = 255 - abs(255 - ($gg + rand(0,60)));
  38. $bi = 255 - abs(255 - ($bb + rand(0,60)));
  39.  
  40. $rf = 255 - abs(255 - ($rr + rand(0,60)));
  41. $gf = 255 - abs(255 - ($gg + rand(0,60)));
  42. $bf = 255 - abs(255 - ($bb + rand(0,60)));
  43.  
  44. // the width of each ring is set by offset="xx%". cx and cy indicate the
  45. // center of the gradient. fx and fy change the focus point.
  46. echo "<radialGradient id=\"basecolor" . $i . "\" cx=\"50%\"
  47. cy=\"50%\" r=\"50%\" fx=\"50%\" fy=\"50%\"> \n";
  48. echo " <stop offset=\"0%\" stop-color=\"rgb(" . $ri . "," .
  49. $gi . "," . $bi . ")\" stop-opacity=\"" . $opi . "\" /> \n";
  50. echo " <stop offset=\"100%\" stop-color=\"rgb(" . $rf . "," .
  51. $gf . "," . $bf . ")\" stop-opacity=\"" . $opf . "\" /> \n";
  52. echo "</radialGradient> \n";
  53. }
  54. echo "</defs> \n";
  55.  
  56. // PLACE CIRCLES
  57. for ($i=0; $i<$circles; $i++) {
  58.  
  59. // pick a random position and radius for the ellipses
  60. $cx = rand(0,850);
  61. $cy = rand(0,850);
  62. $rx = rand(10,200);
  63. $ry = rand(10,200);
  64.  
  65. // rotate the ellipses randomly
  66. $rot = rand(0,90);
  67.  
  68. echo " <g transform=\"rotate(" . $rot . " " . $cx . " " . $cy . ")\"> ";
  69. echo " <ellipse cx=\"" . $cx . "\" cy=\"" .
  70. $cy . "\" rx=\"" . $rx . "\" ry=\"" . $ry . "\"
  71. fill=\"url(#basecolor" . $i . ")\" /> ";
  72. echo "</g> \n \n";
  73. }
  74. echo "</svg>"

So that's only 74 lines of code (I never know whether to count comments as code?) for a lovely little design. One thing I discovered in this process: having a single color theme is far more aethetically pleasing than picking truly random colors. There's a lesson in here that it's always a good idea to play around with what goes inside and outside of your for loops!