This is a card in Dave's Virtual Box of Cards.

SVG Sourcecode Shapes (A Little Ruby Script that can make a self-portrait in SVG)

Page created: 2025-06-11

Back to SVG.

This was created as a deeply nested item in a stack of projects. I wanted to visually represent the "shape" of the source code in a project.

Here’s a self-portrait of shape.rb as drawn by itself (how cute!):


Original SVG "self-portrait" generated by shape.rb (2.2Kb)

SVG colorized in Inkscape (8.0Kb)

PNG (3.8Kb)

16-color PNG (1.1Kb)

I’m not 100% sure why the images exported by Inkscape are a little larger. I’m guessing it has something to with the SVG viewbox?

Here’s how the "self portrait" was made:

$ ./shape.rb shape.rb > shape.svg

Anyway you can see for yourself that this image does, indeed, look like the source of shape.rb below.

shape.rb

#!/usr/bin/env ruby

# Usage: Send me a list of files

# Settings
px_per_char = 1

stuff = []

# We'll keep incrementing Y as we draw stuff...
rect_y = 10
y = 20
x = 10
longest_longest = 0

svg = ""
ARGV.each do |a|
  # start group and write filename at the top
  svg.concat %Q(<text x="#{x}" y="#{y}" style="fill: #000; stroke: none">#{a}</text>\n)

  # start group after the name because i'll definitely need to move
  # them around separately
  svg.concat %Q(<g style="fill:none; stroke: #000; stroke-width: 2;">\n)
  y += 15
  rect_y = y
  y += 3

  longest_line = 0

  # draw lines of text as...lines
  File.readlines(a).each do |line|

    if line.length > 1 # a newline still makes 1
      indent = line =~ /\S/ # find first non-space char
      x1 = x + indent

      x2 = x + line.length
      svg.concat %Q(  <line x1="#{x1}" y1="#{y}" x2="#{x2}" y2="#{y}" />\n)

      if line.length > longest_line
        longest_line = line.length
      end
    end

    y += 3
  end

  if longest_line > longest_longest
    longest_longest = longest_line
  end

  # draw page rect and end group
  h = y - rect_y
  svg.concat %Q(<rect x="#{x-3}" y="#{rect_y}" width="#{longest_line+6}" height="#{h}"></rect>)
  svg.concat '</g>'

  y += 30 # move down for next file
end

# final image size
w = longest_longest + (x * 2) + 20
h = y + 20

puts %Q(<svg width="#{w}" height="#{h}" xmlns="http://www.w3.org/2000/svg">\n#{svg}</svg>)