Class: Webby::Filters::Graphviz

Inherits:
Object
  • Object
show all
Defined in:
lib/webby/filters/graphviz.rb

Overview

The Graphviz filter processes DOT scripts in a webpage and replaces them with generated image files. A set of <graphviz>…</graphviz> tags is used to denote which section(s) of the page contains DOT scripts.

Options can be passed to the Graphviz filter using attributes in the <graphviz> tag.

<graphviz path="images" type="gif" cmd="dot">
digraph graph_1 {
  graph [URL="default.html"]
  a [URL="a.html"]
  b [URL="b.html"]
  c [URL="c.html"]
  a -> b -> c
  a -> c
}
</graphviz>

If the DOT script contains URL or href attributes on any of the nodes or edges, then an image map will be generated and the image will be “clikcable” in the webpage. If URL or href attributes do not appear in the DOT script, then a regular image will be inserted into the webpage.

The image is inserted into the page using an HTML <img /> tag. A corresponding <map>…</map> element will be inserted if needed.

The supported Graphviz options are the following:

path     : where generated images will be stored
           [default is "/"]
type     : the type of image to generate (png, jpeg, gif)
           [default is png]
cmd      : the Graphviz command to use when generating images
           (dot, neato, twopi, circo, fdp) [default is dot]

the following options are passed as-is to the generated <img /> tag
style    : CSS styles to apply to the <img />
class    : CSS class to apply to the <img />
id       : HTML identifier
alt      : alternate text for the <img />

Defined Under Namespace

Classes: Error

Instance Method Summary collapse

Constructor Details

#initialize(str, filters = nil) ⇒ Graphviz

call-seq:

Graphviz.new( string, filters = nil )

Creates a new Graphviz filter that will operate on the given string. The optional filters describe filters that will be applied to the output string returned by the Graphviz filter.



62
63
64
65
66
67
68
69
70
# File 'lib/webby/filters/graphviz.rb', line 62

def initialize( str, filters = nil )
  @str = str
  @filters = filters

  # create a temporary file for holding any error messages
  # from the graphviz program
  @err = Tempfile.new('graphviz_err')
  @err.close
end

Instance Method Details

#to_htmlObject

call-seq:

to_html    => string

Process the original text string passed to the filter when it was created and output HTML formatted text. Any text between <graphviz>…</graphviz> tags will have the contained DOT syntax converted into an image and then included into the resulting HTML text.



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/webby/filters/graphviz.rb', line 80

def to_html
  doc = Hpricot(@str)
  doc.search('//graphviz') do |gviz|
    
    path = gviz['path']
    cmd  = gviz['cmd'] || 'dot'
    type = gviz['type'] || 'png'
    text = gviz.inner_html.strip   # the DOT script to process

    # if we don't have a DOT script, then replace it with
    # the empty text and move on to the next graphviz tags
    if text.empty?
      gviz.swap text
      next
    end

    # ensure the requested graphviz command actually exists
    %x[#{cmd} -V 2>&1]
    unless 0 == $?.exitstatus
      raise NameError, "'#{cmd}' not found on the path"
    end

    # pull the name of the graph|digraph out of the DOT script
    name = text.match(%r/\A\s*(?:strict\s+)?(?:di)?graph\s+([A-Za-z_][A-Za-z0-9_]*)\s+\{/o)[1]

    # see if the user includes any URL or href attributes
    # if so, then we need to create an imagemap
    usemap = text.match(%r/(?:URL|href)\s*=/o) != nil

    # generate the image filename based on the path, graph name, and type
    # of image to generate
    image_fn = path.nil? ? name.dup : ::File.join(path, name)
    image_fn = ::File.join('', image_fn) << '.' << type

    # create the HTML img tag
    out = "<img src=\"#{image_fn}\""

    %w[class style id alt].each do |attr|
      next if gviz[attr].nil?
      out << " %s=\"%s\"" % [attr, gviz[attr]]
    end

    out << " usemap=\"\##{name}\"" if usemap
    out << " />\n"

    # generate the image map if needed
    if usemap
      IO.popen("#{cmd} -Tcmapx 2> #{@err.path}", 'r+') do |io|
        io.write text
        io.close_write
        out << io.read
      end
      error_check
    end

    # generate the image using graphviz -- but first ensure that the
    # path exists
    out_dir = ::Webby.site.output_dir
    out_file = ::File.join(out_dir, image_fn)
    FileUtils.mkpath(::File.join(out_dir, path)) unless path.nil?
    cmd = "#{cmd} -T#{type} -o #{out_file} 2> #{@err.path}"

    IO.popen(cmd, 'w') {|io| io.write text}
    error_check

    # see if we need to put some guards around the output
    # (specifically for textile)
    @filters.each do |f|
      case f
      when 'textile'
        out.insert 0, "<notextile>\n"
        out << "\n</notextile>"
      end
    end unless @filters.nil?

    gviz.swap out
  end

  doc.to_html
end