/
graphviz.rb
121 lines (98 loc) · 3.25 KB
/
graphviz.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
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
# taken from https://raw.githubusercontent.com/kui/octopress-graphviz/master/graphviz_block.rb
require 'open3'
module Jekyll
class GraphvizBlock < Liquid::Block
DIV_CLASS_ATTR = 'graphviz-wrapper'
DEFAULT_GRAPH_NAME = 'Graphviz'
DOT_OPTS = '-Tsvg'
DOT_EXEC = 'dot'
DOT_EXTS = (ENV['PATHEXT'] || '.exe;.bat;.com').split(';')
DOT_EXTS.unshift ''
DOT_PATH = ENV['PATH'].split(File::PATH_SEPARATOR)
.map { |a| File.join a, DOT_EXEC }
.map { |a| DOT_EXTS.map { |ex| a+ex } }.flatten
.find { |c| File.executable_real? c }
raise "not found a executable file: #{DOT_EXEC}" if DOT_PATH.nil?
DOT_CMD = "#{DOT_PATH} #{DOT_OPTS}"
def initialize(tag_name, markup, tokens)
super
@tag_name = tag_name
@title = markup or ''
@title.strip!
@src = ''
end
def render(context)
code = super
title = if @title.empty? then
DEFAULT_GRAPH_NAME
else
@title
end
case @tag_name
when 'graphviz' then
render_graphviz code
when 'graph' then
render_graph 'graph', title, code
when 'digraph' then
render_graph 'digraph', title, code
else
raise "unknown liquid tag name: #{@tag_name}"
end
end
def render_graphviz(code)
@src = code
svg = generate_svg code
filter_for_inline_svg svg
end
def filter_for_inline_svg(code)
code = remove_declarations code
code = remove_xmlns_attrs code
code = add_desc_attrs code
code = insert_desc_elements code
code = wrap_with_div code
code = code.gsub /<polygon fill="white" stroke="none"/, '<polygon fill="transparent" stroke="none"'
code
end
def generate_svg code
Open3.popen3(DOT_CMD) do |stdin, stdout, stderr|
stdout.binmode
stdin.print code
stdin.close
err = stderr.read
if not (err.nil? || err.strip.empty?)
raise "Error from #{DOT_CMD}:\n#{err}"
end
svg = stdout.read
svg.force_encoding 'UTF-8'
return svg
end
end
def remove_declarations(svg)
svg.sub(/<!DOCTYPE .+?>/im, '').sub(/<\?xml .+?\?>/im, '')
end
def remove_xmlns_attrs(svg)
svg.sub(%[xmlns="http://www.w3.org/2000/svg"], '')
.sub(%[xmlns:xlink="http://www.w3.org/1999/xlink"], '')
end
def add_desc_attrs(svg)
svg.sub!('<svg', %[<svg aria-label="#{CGI::escapeHTML @title}"])
svg.sub!('<svg', %[<svg role="img"])
return svg
end
def insert_desc_elements(svg)
inserted_elements = %[<title>#{CGI::escapeHTML @title}</title>\n]
inserted_elements << %[<desc>#{CGI::escapeHTML @src}</desc>\n]
svg.sub!(/(<svg [^>]*>)/, "\\1\n#{inserted_elements}")
return svg
end
def wrap_with_div(svg)
%[<div class="#{DIV_CLASS_ATTR}">#{svg}</div>]
end
def render_graph(type, title, code)
render_graphviz %[#{type} "#{title}" { #{code} }]
end
end
end
Liquid::Template.register_tag('graphviz', Jekyll::GraphvizBlock)
Liquid::Template.register_tag('graph', Jekyll::GraphvizBlock)
Liquid::Template.register_tag('digraph', Jekyll::GraphvizBlock)