class GraphVizML

Attributes

css[RW]
fill[RW]
g[R]
stroke[RW]
text_color[RW]

Public Class Methods

new(obj=nil, debug: false, fill: 'transparent', stroke: ' click to toggle source
# File lib/graphvizml.rb, line 32
def initialize(obj=nil, debug: false, fill: 'transparent', stroke: '#000', text_color: '#000')
  
  @debug = debug
  @fill, @stroke, @text_color = fill, stroke, text_color
  
  if obj then
  
          
    xml = if obj.is_a? Rexle or obj.is_a? Rexle::Element
    
      obj.xml
    
    else 
      
      s = RXFHelper.read(obj).first
    
      if  s =~ /<\?graphvizml\b/ then
        
        import_string s        
        
      else
      
        if @debug then
          #File.write '/tmp/graphviz.xml', xml
          puts('graphvizml xml: ' + s.inspect) 
        end        
        
        s
      
      end
      
    end
    
    @g = build_from_nodes Domle.new(xml)
    
  end
  
  @css = "
    .node ellipse {stroke: #{stroke}; fill: #{fill}}
    .node text {fill: #{text_color}}
    .edge path {stroke: #{stroke}}
    .edge polygon {stroke: #{stroke}; fill: #{stroke}}
  "

end

Public Instance Methods

import(obj) click to toggle source
# File lib/graphvizml.rb, line 78
def import(obj)
  
  s = RXFHelper.read(obj).first
  xml = import_string s
  puts('graphvizml/import xml: ' + xml.inspect).debug if @debug
  @g = build_from_nodes Domle.new(xml)
  self
  
end
to_dot() click to toggle source
# File lib/graphvizml.rb, line 89
def to_dot()
  @g.to_dot
end
to_svg() click to toggle source

returns an SVG blob

# File lib/graphvizml.rb, line 96
def to_svg()
  f = Tempfile.new('graphvizml')
  Graphviz::output(@g, format: 'svg', path: f.path)
  s = File.read f.path
  #s.sub!('xmlns:xlink="http://www.w3.org/1999/xlink"','')
  #s.sub!('xlink:','') # not yet implemented because of a local namespace issue
  s.lines.insert(8, css_code()).join    
end

Private Instance Methods

build_from_nodes(doc) click to toggle source
# File lib/graphvizml.rb, line 109
def build_from_nodes(doc)

  puts 'inside build_from_nodes'.info if @debug

  g = Graphviz::Graph.new format_summary_attributes(doc.root.attributes) 
  
  # add the nodes

  nodes = doc.root.xpath('//node').inject({}) do |r,e|
    
    r.merge fetch_node(e)

  end
  
  if @debug then
    puts 'nodes: ' + nodes.inspect 
    puts 'doc: ' + doc.root.xml.inspect
  end

  a = doc.root.element('style/text()').to_s.strip.split(/}/).map do |entry|
    
    puts 'entry: ' + entry.inspect if @debug

    raw_selector,raw_styles = entry.split(/{/,2)

    h = raw_styles.strip.split(/;/).inject({}) do |r, x| 
      k, v = x.split(/:/,2).map(&:strip)
      r.merge(k.to_sym => v)
    end

    [raw_selector.split(/,\s*/).map(&:strip), h]
  end
  
  puts ' a: ' + a.inspect if @debug
  

  edge_style = a.any? ? a.find {|x| x[0].grep(/edge/).any?}.last : []

  
  
  # add the edges

  id_1 = nodes.first[0]
  nodes[id_1][-1] = g.add_node(nodes[id_1][0])    


  doc.root.each_recursive do |node|

    next unless node.name == 'node'

    node.xpath('a/node | node').each do |child|
      
      id1, id2 = node.object_id, child.object_id

      label = child.attributes[:connection].to_s
      puts('nodes[id1]: ' + nodes[id1].inspect) if @debug
      puts('nodes[id1].last: ' + nodes[id1].last.inspect) if @debug
      nodes[id2][-1] ||= nodes[id1].last.add_node(nodes[id2][0])
      attributes = child.style.merge({label: label})

      conn = nodes[id1][-1].connections.last
      conn.attributes[:label] = label
      
      edge_style.each {|key,val| conn.attributes[key] = m(val) }    
            
    end
  end    
  
  format_attributes nodes    
  
  g
  
end
css_code() click to toggle source
# File lib/graphvizml.rb, line 183
  def css_code()
<<EOF    
        <defs>
                <style type='text/css'><![CDATA[
      #{@css}
                ]]></style>
        </defs>    
        
EOF
  end
fetch_node(node) click to toggle source
# File lib/graphvizml.rb, line 194
def fetch_node(node)

  puts 'inside fetch_node'.info if @debug
  
  h = node.attributes
  #puts 'h: ' + h.inspect
  puts('graphvizml/fetch_node h: ' + h.inspect) if @debug
  
  if node.parent.name == 'a' then
                  
    url = node.parent.attributes[:href]
    h[:url] = url if url
    
  end    

  id = h[:gid] || node.object_id
  label = node.text('label')
  
  # shape options:  box, ellipse, record, diamond, circle, polygon, point
  
  style = {}

  style[:shape] = h[:shape] if h[:shape] and !h[:shape].empty?
  style[:URL] = h[:url] if h[:url]

  #puts "adding node id: %s label: %s" % [id, label]
  
  # the nil is replaced by the Graphviz node object
  {id => [label, node.style.merge(style), nil]}
  
end
format_attributes(nodes) click to toggle source
# File lib/graphvizml.rb, line 226
def format_attributes(nodes)
  
  nodes.each do |id, x|

    _, attributes, obj = x
    next unless obj
    attributes.each {|key, val| obj.attributes[key] = m(val) }
                                                    
  end    

end
format_summary_attributes(h) click to toggle source
# File lib/graphvizml.rb, line 238
def format_summary_attributes(h)
  
  type = (h.has_key?(:type) ? h[:type].to_sym : :digraph)    

  h[:type] = type.to_s
  h[:rankdir] = h.has_key?(:direction) ? h[:direction].to_s.upcase : 'LR'
  
  %i(recordx_type format_mask schema direction).each do |x| 
    h.delete x; h.delete x.to_s
  end
  
  # remove any entries with an empty value
  h.each {|key, value| h.delete key if value.empty?}    
  
  h
end
import_string(s) click to toggle source
# File lib/graphvizml.rb, line 256
def import_string(s)

  
  s2 = s.slice!(/<\?graphvizml\b[^>]*\?>/)

  if s2 then
    
    attributes = %w(id fill stroke text_color).inject({}) do |r, keyword|
      found = s2[/(?<=#{keyword}=['"])[^'"]+/]
      found ? r.merge(keyword.to_sym => found) : r
    end
    
  fill, stroke, text_color = %i(fill stroke text_color).map do |x|
    attributes[x] ? attributes[x] : method(x).call
  end
  
  @css = "
    .node ellipse {stroke: #{stroke}; fill: #{fill}}
    .node text {fill: #{text_color}}
    .edge path {stroke: #{stroke}}
    .edge polygon {stroke: #{stroke}; fill: #{stroke}}
  "      
    
  end 
  
  xml = LineTree.new(s, root: 'nodes', debug: @debug).to_xml
  
end
m(s) click to toggle source

modify the value if it matches the following criteria

# File lib/graphvizml.rb, line 287
def m(s)
  
  # is it a shorthand hexcode? e.g. #fff
  s.gsub(/^#([\da-f]{3})$/)\
      { '#' + ($1).chars.inject([]) {|r,x| r << x + x}.join}
end