-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathgraphviz_block.rb
118 lines (95 loc) · 2.99 KB
/
graphviz_block.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
# -*- coding:utf-8; mode:ruby; -*-
['open3', 'win32/open3'].each do |lib|
begin
require lib
break
rescue LoadError
end
end
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
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)