Archive for April, 2007

SVG content with Rails 1.2.3

Saturday, April 21st, 2007

John Taber has been trying to use SVG in his Ruby on Rails app.
Unfortunately, Webrick and Rails have been uncooperative.

From railties/lib/webrick_server.rb we see that rails attempts to serve requests from the public directory before dispatching to controllers.

def service(req, res) #:nodoc:
    unless handle_file(req, res)
      begin
        REQUEST_MUTEX.lock unless ActionController::Base.allow_concurrency
        unless handle_dispatch(req, res)
          raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found."
        end
      ensure
        unless ActionController::Base.allow_concurrency
          REQUEST_MUTEX.unlock if REQUEST_MUTEX.locked?
        end
      end
    end
end

If you put a svg file in the public directory of a rails app, webrick serves it up, but with the wrong mime type.

$ wget localhost:3000/rect1.svg
--15:54:54--  http://localhost:3000/rect1.svg
           => `rect1.svg.1'
Resolving localhost... 127.0.0.1
Connecting to localhost|127.0.0.1|:3000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 333 [application/octet-stream]

100%[====================================>] 333           --.--K/s

15:54:54 (12.92 MB/s) - `rect1.svg.1' saved [333/333]

Firefox displays a friendly description based on file extension, but doesn’t display the svg because the mime type is wrong.

SVG download due to bad mime type.

So add a svg mime type entry to webrick

--- /usr/lib/ruby/1.8/webrick/httputils.rb.orig 2007-04-21 16:00:42.000000000 -0600
+++ /usr/lib/ruby/1.8/webrick/httputils.rb      2007-04-21 16:01:03.000000000 -0600
@@ -86,6 +86,7 @@
       "rtf"   => "application/rtf",
       "sgm"   => "text/sgml",
       "sgml"  => "text/sgml",
+      "svg"   => "image/svg+xml",
       "tif"   => "image/tiff",
       "tiff"  => "image/tiff",
       "txt"   => "text/plain",

And presto

$ wget localhost:3000/rect1.svg
--16:17:22--  http://localhost:3000/rect1.svg
           => `rect1.svg.2'
Resolving localhost... 127.0.0.1
Connecting to localhost|127.0.0.1|:3000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 333 [image/svg+xml]

100%[======================================>] 333           --.--K/s

16:17:22 (22.73 MB/s) - `rect1.svg.2' saved [333/333]

SVG Box

SVG content delivered through a controller works just fine, you just have to set the content disposition to inline.

def index
  data = <<END_D
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg width="100%" height="100%" version="1.1"
xmlns="http://www.w3.org/2000/svg">

<rect width="300" height="100"
style="fill:rgb(0,0,255);stroke-width:1;
stroke:rgb(0,0,0)"/>

</svg>
END_D
send_data data, :type => 'image/svg+xml', :disposition => 'inline'
end

Now a DSL for SVG creation would be cool, but that is a completely different project.