asciidoctor-diagram 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +77 -1
  3. data/README.adoc +25 -483
  4. data/Rakefile +1 -1
  5. data/docs/antora.yml +3 -0
  6. data/{images → docs/modules/ROOT/images}/asciidoctor-diagram-classes.png +0 -0
  7. data/docs/modules/ROOT/images/asciidoctor-diagram-process.png +0 -0
  8. data/docs/modules/ROOT/pages/index.adoc +21 -0
  9. data/docs/modules/ROOT/partials/advanced.adoc +303 -0
  10. data/docs/modules/ROOT/partials/create_diagram.adoc +131 -0
  11. data/docs/modules/ROOT/partials/generate.adoc +15 -0
  12. data/docs/modules/ROOT/partials/installation.adoc +19 -0
  13. data/docs/modules/ROOT/partials/uris.adoc +38 -0
  14. data/examples/features.adoc +1 -1
  15. data/lib/asciidoctor-diagram.rb +4 -0
  16. data/lib/asciidoctor-diagram/a2s/converter.rb +10 -6
  17. data/lib/asciidoctor-diagram/blockdiag/converter.rb +1 -1
  18. data/lib/asciidoctor-diagram/bpmn/converter.rb +4 -4
  19. data/lib/asciidoctor-diagram/bytefield.rb +7 -0
  20. data/lib/asciidoctor-diagram/bytefield/converter.rb +26 -0
  21. data/lib/asciidoctor-diagram/bytefield/extension.rb +14 -0
  22. data/lib/asciidoctor-diagram/diagram_converter.rb +5 -1
  23. data/lib/asciidoctor-diagram/diagram_processor.rb +106 -51
  24. data/lib/asciidoctor-diagram/diagram_source.rb +66 -19
  25. data/lib/asciidoctor-diagram/ditaa/converter.rb +20 -7
  26. data/lib/asciidoctor-diagram/ditaa/ditaa-1.3.17.jar +0 -0
  27. data/lib/asciidoctor-diagram/dpic.rb +7 -0
  28. data/lib/asciidoctor-diagram/dpic/converter.rb +30 -0
  29. data/lib/asciidoctor-diagram/dpic/extension.rb +14 -0
  30. data/lib/asciidoctor-diagram/gnuplot/converter.rb +8 -8
  31. data/lib/asciidoctor-diagram/graphviz/converter.rb +2 -2
  32. data/lib/asciidoctor-diagram/http/converter.rb +93 -0
  33. data/lib/asciidoctor-diagram/http/server.rb +10 -5
  34. data/lib/asciidoctor-diagram/lilypond/converter.rb +13 -3
  35. data/lib/asciidoctor-diagram/meme/converter.rb +24 -18
  36. data/lib/asciidoctor-diagram/mermaid/converter.rb +45 -14
  37. data/lib/asciidoctor-diagram/msc/converter.rb +2 -2
  38. data/lib/asciidoctor-diagram/pikchr.rb +7 -0
  39. data/lib/asciidoctor-diagram/pikchr/converter.rb +33 -0
  40. data/lib/asciidoctor-diagram/pikchr/extension.rb +14 -0
  41. data/lib/asciidoctor-diagram/plantuml/converter.rb +22 -14
  42. data/lib/asciidoctor-diagram/plantuml/plantuml-1.3.17.jar +0 -0
  43. data/lib/asciidoctor-diagram/smcat/converter.rb +3 -3
  44. data/lib/asciidoctor-diagram/svgbob/converter.rb +29 -1
  45. data/lib/asciidoctor-diagram/symbolator.rb +7 -0
  46. data/lib/asciidoctor-diagram/symbolator/converter.rb +23 -0
  47. data/lib/asciidoctor-diagram/symbolator/extension.rb +14 -0
  48. data/lib/asciidoctor-diagram/syntrax/converter.rb +9 -6
  49. data/lib/asciidoctor-diagram/tikz/converter.rb +20 -1
  50. data/lib/asciidoctor-diagram/umlet/converter.rb +11 -2
  51. data/lib/asciidoctor-diagram/util/cli.rb +29 -3
  52. data/lib/asciidoctor-diagram/util/cli_generator.rb +22 -2
  53. data/lib/asciidoctor-diagram/util/gif.rb +2 -2
  54. data/lib/asciidoctor-diagram/util/java.rb +118 -3
  55. data/lib/asciidoctor-diagram/util/java_jruby.rb +4 -1
  56. data/lib/asciidoctor-diagram/util/java_socket.rb +9 -110
  57. data/lib/asciidoctor-diagram/util/pdf.rb +2 -2
  58. data/lib/asciidoctor-diagram/util/png.rb +2 -2
  59. data/lib/{server-1.3.14.jar → asciidoctor-diagram/util/server-1.3.17.jar} +0 -0
  60. data/lib/asciidoctor-diagram/util/svg.rb +66 -18
  61. data/lib/asciidoctor-diagram/vega/converter.rb +2 -2
  62. data/lib/asciidoctor-diagram/version.rb +1 -1
  63. data/spec/a2s_spec.rb +5 -143
  64. data/spec/blockdiag_spec.rb +5 -203
  65. data/spec/bpmn_spec.rb +52 -92
  66. data/spec/bytefield_spec.rb +92 -0
  67. data/spec/ditaa_spec.rb +37 -143
  68. data/spec/dpic_spec.rb +19 -0
  69. data/spec/erd_spec.rb +5 -202
  70. data/spec/gnuplot_spec.rb +2 -255
  71. data/spec/graphviz_spec.rb +6 -145
  72. data/spec/lilypond_spec.rb +5 -143
  73. data/spec/mermaid_spec.rb +68 -210
  74. data/spec/msc_spec.rb +2 -199
  75. data/spec/nomnoml_spec.rb +4 -142
  76. data/spec/pikchr_spec.rb +69 -0
  77. data/spec/plantuml_spec.rb +6 -578
  78. data/spec/shaape_spec.rb +12 -224
  79. data/spec/shared_examples.rb +603 -0
  80. data/spec/smcat_spec.rb +2 -140
  81. data/spec/svgbob_spec.rb +5 -143
  82. data/spec/symbolator_spec.rb +23 -0
  83. data/spec/syntrax_spec.rb +7 -217
  84. data/spec/test_helper.rb +4 -28
  85. data/spec/tikz_spec.rb +68 -18
  86. data/spec/umlet_spec.rb +2 -58
  87. data/spec/vega_spec.rb +4 -117
  88. data/spec/wavedrom_spec.rb +2 -199
  89. metadata +80 -15
  90. data/README_zh-CN.adoc +0 -336
  91. data/images/asciidoctor-diagram-process.png +0 -0
  92. data/lib/batik-all-1.10.jar +0 -0
  93. data/lib/ditaa-1.3.14.jar +0 -0
  94. data/lib/ditaamini-0.12.jar +0 -0
  95. data/lib/jlatexmath-minimal-1.0.5.jar +0 -0
  96. data/lib/plantuml-1.3.14.jar +0 -0
  97. data/lib/plantuml.jar +0 -0
  98. data/spec/bpmn-example.xml +0 -44
@@ -1,3 +1,4 @@
1
+ require 'asciidoctor/logging'
1
2
  require_relative 'util/which'
2
3
 
3
4
  module Asciidoctor
@@ -5,6 +6,12 @@ module Asciidoctor
5
6
  # This module describes the duck-typed interface that diagram sources must implement. Implementations
6
7
  # may include this module but it is not required.
7
8
  module DiagramSource
9
+ include Asciidoctor::Logging
10
+
11
+ def diagram_type
12
+ raise NotImplementedError.new
13
+ end
14
+
8
15
  def image_name
9
16
  raise NotImplementedError.new
10
17
  end
@@ -15,20 +22,24 @@ module Asciidoctor
15
22
  raise NotImplementedError.new
16
23
  end
17
24
 
25
+ def global_attr(name, default_value = nil)
26
+ attr(name) || attr(name, default_value, 'diagram')
27
+ end
28
+
18
29
  # Get the value for the specified attribute. First look in the attributes on
19
30
  # this document and return the value of the attribute if found. Otherwise, if
20
31
  # this document is a child of the Document document, look in the attributes of the
21
32
  # Document document and return the value of the attribute if found. Otherwise,
22
33
  # return the default value, which defaults to nil.
23
34
  #
24
- # @param name [String, Symbol] the name of the attribute to lookup
35
+ # @param name [String, Symbol, Array] the name(s) of the attribute to lookup
25
36
  # @param default_value [Object] the value to return if the attribute is not found
26
37
  # @inherit [Boolean, String] indicates whether to check for the attribute on the AsciiDoctor::Document if not found on this document.
27
38
  # When a non-nil String is given the an attribute name "#{inherit}-#{name}" is looked for on the document.
28
39
  #
29
40
  # @return the value of the attribute or the default value if the attribute is not found in the attributes of this node or the document node
30
41
  # @abstract
31
- def attr(name, default_value = nil, inherit = nil)
42
+ def attr(name, default_value = nil, inherit = diagram_type)
32
43
  raise NotImplementedError.new
33
44
  end
34
45
 
@@ -75,14 +86,42 @@ module Asciidoctor
75
86
  if config.key? cmd_var
76
87
  cmd_path = config[cmd_var]
77
88
  else
78
- cmd_path = attr_names.map { |attr_name| attr(attr_name, nil, true) }.find { |attr| !attr.nil? }
79
-
80
- unless cmd_path && File.executable?(cmd_path)
81
- cmd_paths = cmd_names.map do |c|
82
- ::Asciidoctor::Diagram::Which.which(c, :path => options[:path])
83
- end
84
-
85
- cmd_path = cmd_paths.reject { |c| c.nil? }.first
89
+ logger.debug "Finding '#{cmd}' in attributes"
90
+ cmd_path = attr_names.map { |attr_name|
91
+ attr = attr(attr_name, nil, true)
92
+ if logger.debug? && attr
93
+ logger.debug "Found value '#{attr}' in attribute '#{attr_name}'" if attr
94
+ end
95
+ attr
96
+ }
97
+ .reject { |attr| attr.nil? }
98
+ .map { |attr|
99
+ expanded = File.expand_path(attr)
100
+ if logger.debug? && attr != expanded
101
+ logger.debug "Expanded '#{attr}' to '#{expanded}'"
102
+ end
103
+ expanded
104
+ }
105
+ .select { |path|
106
+ executable = File.executable?(path)
107
+ if logger.debug?
108
+ logger.debug "Is '#{path}' executable? #{executable}"
109
+ end
110
+ executable
111
+ }
112
+ .first
113
+
114
+ unless cmd_path
115
+ logger.debug "Finding '#{cmd}' in environment"
116
+ cmd_path = cmd_names.map { |c|
117
+ path = ::Asciidoctor::Diagram::Which.which(c, :path => options[:path])
118
+ if logger.debug? && path
119
+ logger.debug "Found '#{path}' in environment"
120
+ end
121
+ path
122
+ }
123
+ .reject { |path| path.nil? }
124
+ .first
86
125
  end
87
126
 
88
127
  config[cmd_var] = cmd_path
@@ -113,6 +152,10 @@ module Asciidoctor
113
152
  @attributes = attributes
114
153
  end
115
154
 
155
+ def diagram_type
156
+ @block_processor.name.downcase
157
+ end
158
+
116
159
  def resolve_path target, start = base_dir
117
160
  @parent_block.normalize_system_path(target, start)
118
161
  end
@@ -125,29 +168,33 @@ module Asciidoctor
125
168
  attr('target', 'diag-' + checksum)
126
169
  end
127
170
 
128
- def attr(name, default_value = nil, inherit = nil)
171
+ def attr(name, default_value = nil, inherit = diagram_type)
129
172
  name = name.to_s if ::Symbol === name
173
+ name = [name] unless name.is_a?(Enumerable)
130
174
 
131
- value = @attributes[name]
175
+ value = name.lazy.map { |n| @attributes[n] }.reject { |v| v.nil? }.first
132
176
 
133
177
  if value.nil? && inherit
134
- case inherit
135
- when String, Symbol
136
- value = @parent_block.attr("#{inherit.to_s}-#{name}", default_value, true)
137
- else
138
- value = @parent_block.attr(name, default_value, inherit)
178
+ inherited_values = name.lazy.map do |n|
179
+ case inherit
180
+ when String, Symbol
181
+ @parent_block.attr("#{inherit.to_s}-#{n}", default_value, true)
182
+ else
183
+ @parent_block.attr(n, default_value, inherit)
184
+ end
139
185
  end
186
+ value = inherited_values.reject { |v| v.nil? }.first
140
187
  end
141
188
 
142
189
  value || default_value
143
190
  end
144
191
 
145
192
  def should_process?(image_file, image_metadata)
146
- image_metadata['checksum'] != checksum
193
+ image_metadata[:checksum] != checksum
147
194
  end
148
195
 
149
196
  def create_image_metadata
150
- {'checksum' => checksum}
197
+ {:checksum => checksum}
151
198
  end
152
199
 
153
200
  def checksum
@@ -23,27 +23,40 @@ module Asciidoctor
23
23
  :transparent => lambda { |o, v| o << '--transparent' if v == 'true'}
24
24
  }
25
25
 
26
- JARS = ['ditaa-1.3.14.jar', 'ditaamini-0.12.jar'].map do |jar|
27
- File.expand_path File.join('../..', jar), File.dirname(__FILE__)
28
- end
29
- Java.classpath.concat JARS
30
-
26
+ CLASSPATH_ENV = 'DIAGRAM_DITAA_CLASSPATH'
27
+ DITAA_JARS = if ENV.has_key?(CLASSPATH_ENV)
28
+ ENV[CLASSPATH_ENV].split(File::PATH_SEPARATOR)
29
+ else
30
+ begin
31
+ require 'asciidoctor-diagram/ditaa/classpath'
32
+ ::Asciidoctor::Diagram::DitaaClasspath::JAR_FILES
33
+ rescue LoadError
34
+ raise "Could not load PlantUML. Eiter require 'asciidoctor-diagram-ditaamini' or specify the location of the PlantUML JAR(s) using the 'DIAGRAM_DITAA_CLASSPATH' environment variable."
35
+ end
36
+ end
37
+
38
+ Java.classpath.concat Dir[File.join(File.dirname(__FILE__), '*.jar')]
39
+ Java.classpath.concat DITAA_JARS
31
40
 
32
41
  def supported_formats
33
42
  [:png, :svg]
34
43
  end
35
44
 
36
- def collect_options(source, name)
45
+ def collect_options(source)
37
46
  options = {}
38
47
 
39
48
  OPTIONS.keys.each do |option|
40
49
  attr_name = option.to_s.tr('_', '-')
41
- options[option] = source.attr(attr_name, nil, name) || source.attr(attr_name, nil, 'ditaa-option')
50
+ options[option] = source.attr(attr_name) || source.attr(attr_name, nil, 'ditaa-option')
42
51
  end
43
52
 
44
53
  options
45
54
  end
46
55
 
56
+ def native_scaling?
57
+ true
58
+ end
59
+
47
60
  def convert(source, format, options)
48
61
  Java.load
49
62
 
@@ -0,0 +1,7 @@
1
+ require 'asciidoctor/extensions'
2
+ require_relative 'dpic/extension'
3
+
4
+ Asciidoctor::Extensions.register do
5
+ block Asciidoctor::Diagram::DpicBlockProcessor, :dpic
6
+ block_macro Asciidoctor::Diagram::DpicBlockMacroProcessor, :dpic
7
+ end
@@ -0,0 +1,30 @@
1
+ require_relative '../diagram_converter'
2
+ require_relative '../util/cli_generator'
3
+ require_relative '../util/platform'
4
+
5
+ module Asciidoctor
6
+ module Diagram
7
+ # @private
8
+ class DpicConverter
9
+ include DiagramConverter
10
+ include CliGenerator
11
+
12
+
13
+ def supported_formats
14
+ [:svg]
15
+ end
16
+
17
+ def convert(source, format, options)
18
+ dpic_path = source.find_command('dpic')
19
+
20
+ code = source.to_s
21
+ code.prepend ".PS\n" unless code.start_with?(".PS\n")
22
+ code << "\n.PE" unless code.start_with?("\n.PE")
23
+
24
+ generate_file_stdout(dpic_path, format.to_s, code) do |tool_path, input_path|
25
+ [tool_path, "-v", "-z", input_path]
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,14 @@
1
+ require_relative 'converter'
2
+ require_relative '../diagram_processor'
3
+
4
+ module Asciidoctor
5
+ module Diagram
6
+ class DpicBlockProcessor < DiagramBlockProcessor
7
+ use_converter DpicConverter
8
+ end
9
+
10
+ class DpicBlockMacroProcessor < DiagramBlockMacroProcessor
11
+ use_converter DpicConverter
12
+ end
13
+ end
14
+ end
@@ -14,15 +14,15 @@ module Asciidoctor
14
14
  [:png, :svg, :gif, :txt, :literal]
15
15
  end
16
16
 
17
- def collect_options(source, name)
17
+ def collect_options(source)
18
18
  {
19
- :width => source.attr('width', nil, name),
20
- :height => source.attr('height', nil, name),
21
- :transparent => source.attr('transparent', nil, name),
22
- :crop => source.attr('crop', nil, name),
23
- :font => source.attr('font', nil, name),
24
- :fontscale => source.attr('fontscale', nil, name),
25
- :background => source.attr('background', nil, name),
19
+ :width => source.attr('width'),
20
+ :height => source.attr('height'),
21
+ :transparent => source.attr('transparent'),
22
+ :crop => source.attr('crop'),
23
+ :font => source.attr('font'),
24
+ :fontscale => source.attr('fontscale'),
25
+ :background => source.attr('background'),
26
26
  }
27
27
  end
28
28
 
@@ -13,8 +13,8 @@ module Asciidoctor
13
13
  [:png, :pdf, :svg]
14
14
  end
15
15
 
16
- def collect_options(source, name)
17
- {:layout => source.attr('layout', nil, name)}
16
+ def collect_options(source)
17
+ {:layout => source.attr('layout')}
18
18
  end
19
19
 
20
20
  def convert(source, format, options)
@@ -0,0 +1,93 @@
1
+ require_relative '../diagram_converter'
2
+ require_relative '../util/cli_generator'
3
+ require_relative '../util/platform'
4
+
5
+ require 'base64'
6
+ require 'net/http'
7
+ require 'uri'
8
+ require 'zlib'
9
+
10
+ module Asciidoctor
11
+ module Diagram
12
+ # @private
13
+ class HttpConverter
14
+ include DiagramConverter
15
+
16
+ def initialize(base_uri, type, converter)
17
+ @base_uri = base_uri
18
+ @type = type
19
+ @converter = converter
20
+ end
21
+
22
+ def supported_formats
23
+ @converter.supported_formats
24
+ end
25
+
26
+ def convert(source, format, options)
27
+ code = source.code
28
+
29
+ uri = URI(@base_uri)
30
+
31
+ case @type
32
+ when :plantuml
33
+ deflate = Zlib::Deflate.new(Zlib::BEST_COMPRESSION,
34
+ -Zlib::MAX_WBITS,
35
+ Zlib::MAX_MEM_LEVEL,
36
+ Zlib::DEFAULT_STRATEGY)
37
+
38
+ compressed = deflate.deflate(code, Zlib::FINISH)
39
+ deflate.close
40
+
41
+ encoded = Base64.urlsafe_encode64(compressed)
42
+ data = '0A' + encoded
43
+
44
+ path = uri.path
45
+ path << '/' unless path.end_with? '/'
46
+ path << format.to_s
47
+ path << '/' << data
48
+ uri.path = path
49
+ when :kroki_io
50
+ compressed = Zlib.deflate(code, Zlib::BEST_COMPRESSION)
51
+ data = Base64.urlsafe_encode64(compressed)
52
+
53
+ path = uri.path
54
+ path << '/' unless path.end_with? '/'
55
+ path << source.diagram_type.to_s
56
+ path << '/' << format.to_s
57
+ path << '/' << data
58
+ uri.path = path
59
+ else
60
+ raise "Unsupported server type: " + @type
61
+ end
62
+
63
+ get_uri(uri)
64
+ end
65
+
66
+ private
67
+
68
+ def get_uri(uri, attempt = 1)
69
+ if attempt >= 10
70
+ raise "Too many redirects"
71
+ end
72
+
73
+ Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme.downcase == 'https') do |http|
74
+ response = http.request_get uri.path
75
+ case response
76
+ when Net::HTTPSuccess
77
+ response.body
78
+ when Net::HTTPRedirection then
79
+ location = response['Location']
80
+ new_uri = URI.parse(location)
81
+ if new_uri.relative?
82
+ get_uri(uri + location, attempt + 1)
83
+ else
84
+ get_uri(new_uri, attempt + 1)
85
+ end
86
+ else
87
+ response.value
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -67,15 +67,15 @@ module Asciidoctor
67
67
  end
68
68
  end
69
69
 
70
- def render_diagram(type, accepts, code, attributes)
70
+ def render_diagram(type, accepts, source, attributes)
71
71
  converter = get_converter(type.downcase.to_sym)
72
72
  return [500, "Unsupported diagram type #{type}"] unless converter
73
73
 
74
74
  format = converter.supported_formats.find {|f| accepts.call(f)}
75
75
  return [500, "Could not determine supported output format"] unless format
76
76
 
77
- source = ServerSource.new(code, attributes)
78
- options = converter.collect_options(source, type.downcase)
77
+ source = ServerSource.new(type.downcase, source, attributes)
78
+ options = converter.collect_options(source)
79
79
  diagram = converter.convert(source, format, options)
80
80
 
81
81
  content_type to_mime_type(format)
@@ -86,12 +86,17 @@ module Asciidoctor
86
86
  class ServerSource
87
87
  include Asciidoctor::Diagram::DiagramSource
88
88
 
89
- def initialize(source, attributes)
89
+ def initialize(name, source, attributes)
90
+ @name = name
90
91
  @source = source
91
92
  @attributes = attributes
92
93
  end
93
94
 
94
- def attr(name, default_value = nil, inherit = nil)
95
+ def diagram_type
96
+ @name
97
+ end
98
+
99
+ def attr(name, default_value = nil, inherit = diagram_type)
95
100
  @attributes[name] || default_value
96
101
  end
97
102
 
@@ -9,14 +9,24 @@ module Asciidoctor
9
9
  include DiagramConverter
10
10
  include CliGenerator
11
11
 
12
+ EXTRA_PATH = []
13
+
14
+ if ::Asciidoctor::Diagram::Platform.os == :macosx
15
+ lilypond_app = ::Asciidoctor::Diagram::Which.which('LilyPond.app', :path => ['/Applications'])
16
+ if lilypond_app
17
+ EXTRA_PATH << File.join(lilypond_app, 'Contents/Resources/bin')
18
+ end
19
+ end
20
+
21
+ EXTRA_PATH.freeze
12
22
 
13
23
  def supported_formats
14
24
  [:png, :pdf]
15
25
  end
16
26
 
17
- def collect_options(source, name)
27
+ def collect_options(source)
18
28
  {
19
- :resolution => source.attr('resolution', nil, name)
29
+ :resolution => source.attr('resolution')
20
30
  }
21
31
  end
22
32
 
@@ -34,7 +44,7 @@ module Asciidoctor
34
44
 
35
45
  resolution = options[:resolution]
36
46
 
37
- generate_stdin(source.find_command('lilypond'), format.to_s, code) do |tool_path, output_path|
47
+ generate_stdin(source.find_command('lilypond', :path => EXTRA_PATH), format.to_s, code) do |tool_path, output_path|
38
48
  args = [tool_path, '-daux-files=#f', '-dbackend=eps', '-dno-gs-load-fonts', '-dinclude-eps-fonts', '-o', Platform.native_path(output_path), '-f', format.to_s]
39
49
 
40
50
  args << '-dsafe'