asciidoctor-diagram 1.5.18 → 2.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +75 -0
  3. data/README.adoc +98 -23
  4. data/examples/features.adoc +2 -2
  5. data/lib/asciidoctor-diagram.rb +8 -0
  6. data/lib/asciidoctor-diagram/a2s/converter.rb +59 -0
  7. data/lib/asciidoctor-diagram/a2s/extension.rb +6 -52
  8. data/lib/asciidoctor-diagram/blockdiag/converter.rb +37 -0
  9. data/lib/asciidoctor-diagram/blockdiag/extension.rb +9 -116
  10. data/lib/asciidoctor-diagram/bpmn.rb +7 -0
  11. data/lib/asciidoctor-diagram/bpmn/converter.rb +62 -0
  12. data/lib/asciidoctor-diagram/bpmn/extension.rb +14 -0
  13. data/lib/asciidoctor-diagram/bytefield.rb +7 -0
  14. data/lib/asciidoctor-diagram/bytefield/converter.rb +26 -0
  15. data/lib/asciidoctor-diagram/bytefield/extension.rb +14 -0
  16. data/lib/asciidoctor-diagram/diagram_converter.rb +23 -0
  17. data/lib/asciidoctor-diagram/diagram_processor.rb +357 -0
  18. data/lib/asciidoctor-diagram/diagram_source.rb +322 -0
  19. data/lib/asciidoctor-diagram/ditaa/converter.rb +90 -0
  20. data/lib/asciidoctor-diagram/ditaa/extension.rb +6 -71
  21. data/lib/asciidoctor-diagram/dpic.rb +7 -0
  22. data/lib/asciidoctor-diagram/dpic/converter.rb +30 -0
  23. data/lib/asciidoctor-diagram/dpic/extension.rb +14 -0
  24. data/lib/asciidoctor-diagram/erd/converter.rb +31 -0
  25. data/lib/asciidoctor-diagram/erd/extension.rb +6 -35
  26. data/lib/asciidoctor-diagram/gnuplot.rb +7 -0
  27. data/lib/asciidoctor-diagram/gnuplot/converter.rb +63 -0
  28. data/lib/asciidoctor-diagram/gnuplot/extension.rb +14 -0
  29. data/lib/asciidoctor-diagram/graphviz/converter.rb +32 -0
  30. data/lib/asciidoctor-diagram/graphviz/extension.rb +6 -35
  31. data/lib/asciidoctor-diagram/http/converter.rb +99 -0
  32. data/lib/asciidoctor-diagram/http/server.rb +132 -0
  33. data/lib/asciidoctor-diagram/lilypond.rb +7 -0
  34. data/lib/asciidoctor-diagram/lilypond/converter.rb +54 -0
  35. data/lib/asciidoctor-diagram/lilypond/extension.rb +14 -0
  36. data/lib/asciidoctor-diagram/meme/converter.rb +122 -0
  37. data/lib/asciidoctor-diagram/meme/extension.rb +5 -107
  38. data/lib/asciidoctor-diagram/mermaid/converter.rb +179 -0
  39. data/lib/asciidoctor-diagram/mermaid/extension.rb +6 -159
  40. data/lib/asciidoctor-diagram/msc/converter.rb +35 -0
  41. data/lib/asciidoctor-diagram/msc/extension.rb +6 -36
  42. data/lib/asciidoctor-diagram/nomnoml/converter.rb +25 -0
  43. data/lib/asciidoctor-diagram/nomnoml/extension.rb +6 -28
  44. data/lib/asciidoctor-diagram/pikchr.rb +7 -0
  45. data/lib/asciidoctor-diagram/pikchr/converter.rb +26 -0
  46. data/lib/asciidoctor-diagram/pikchr/extension.rb +14 -0
  47. data/lib/asciidoctor-diagram/plantuml/converter.rb +117 -0
  48. data/lib/asciidoctor-diagram/plantuml/extension.rb +10 -119
  49. data/lib/asciidoctor-diagram/shaape/converter.rb +25 -0
  50. data/lib/asciidoctor-diagram/shaape/extension.rb +6 -28
  51. data/lib/asciidoctor-diagram/smcat.rb +7 -0
  52. data/lib/asciidoctor-diagram/smcat/converter.rb +44 -0
  53. data/lib/asciidoctor-diagram/smcat/extension.rb +14 -0
  54. data/lib/asciidoctor-diagram/svgbob/converter.rb +49 -0
  55. data/lib/asciidoctor-diagram/svgbob/extension.rb +6 -28
  56. data/lib/asciidoctor-diagram/symbolator.rb +7 -0
  57. data/lib/asciidoctor-diagram/symbolator/converter.rb +23 -0
  58. data/lib/asciidoctor-diagram/symbolator/extension.rb +14 -0
  59. data/lib/asciidoctor-diagram/syntrax/converter.rb +58 -0
  60. data/lib/asciidoctor-diagram/syntrax/extension.rb +6 -51
  61. data/lib/asciidoctor-diagram/tikz/converter.rb +56 -0
  62. data/lib/asciidoctor-diagram/tikz/extension.rb +6 -60
  63. data/lib/asciidoctor-diagram/umlet/converter.rb +24 -0
  64. data/lib/asciidoctor-diagram/umlet/extension.rb +6 -28
  65. data/lib/asciidoctor-diagram/util/cli.rb +14 -3
  66. data/lib/asciidoctor-diagram/util/cli_generator.rb +19 -1
  67. data/lib/asciidoctor-diagram/util/gif.rb +2 -2
  68. data/lib/asciidoctor-diagram/util/java.rb +1 -1
  69. data/lib/asciidoctor-diagram/util/java_socket.rb +7 -9
  70. data/lib/asciidoctor-diagram/util/pdf.rb +2 -2
  71. data/lib/asciidoctor-diagram/util/png.rb +2 -2
  72. data/lib/asciidoctor-diagram/util/svg.rb +46 -19
  73. data/lib/asciidoctor-diagram/util/which.rb +0 -29
  74. data/lib/asciidoctor-diagram/vega/converter.rb +47 -0
  75. data/lib/asciidoctor-diagram/vega/extension.rb +6 -44
  76. data/lib/asciidoctor-diagram/version.rb +1 -1
  77. data/lib/asciidoctor-diagram/wavedrom/converter.rb +50 -0
  78. data/lib/asciidoctor-diagram/wavedrom/extension.rb +6 -46
  79. data/lib/ditaa-1.3.15.jar +0 -0
  80. data/lib/ditaamini-0.12.jar +0 -0
  81. data/lib/plantuml-1.3.15.jar +0 -0
  82. data/lib/plantuml.jar +0 -0
  83. data/lib/server-1.3.15.jar +0 -0
  84. data/spec/a2s_spec.rb +2 -140
  85. data/spec/blockdiag_spec.rb +2 -200
  86. data/spec/bpmn_spec.rb +56 -0
  87. data/spec/bytefield_spec.rb +92 -0
  88. data/spec/ditaa_spec.rb +37 -143
  89. data/spec/dpic_spec.rb +19 -0
  90. data/spec/erd_spec.rb +2 -199
  91. data/spec/gnuplot_spec.rb +225 -0
  92. data/spec/graphviz_spec.rb +6 -145
  93. data/spec/lilypond_spec.rb +13 -0
  94. data/spec/mermaid_spec.rb +35 -200
  95. data/spec/msc_spec.rb +2 -199
  96. data/spec/nomnoml_spec.rb +4 -142
  97. data/spec/pikchr_spec.rb +51 -0
  98. data/spec/plantuml_spec.rb +24 -507
  99. data/spec/shaape_spec.rb +9 -221
  100. data/spec/shared_examples.rb +552 -0
  101. data/spec/smcat_spec.rb +26 -0
  102. data/spec/svgbob_spec.rb +2 -140
  103. data/spec/symbolator_spec.rb +23 -0
  104. data/spec/syntrax_spec.rb +5 -215
  105. data/spec/test_helper.rb +1 -18
  106. data/spec/tikz_spec.rb +4 -24
  107. data/spec/umlet_spec.rb +2 -58
  108. data/spec/vega_spec.rb +4 -117
  109. data/spec/wavedrom_spec.rb +2 -199
  110. metadata +73 -11
  111. data/lib/asciidoctor-diagram/extensions.rb +0 -568
  112. data/lib/ditaa-1.3.13.jar +0 -0
  113. data/lib/ditaamini-0.11.jar +0 -0
  114. data/lib/plantuml-1.3.13.jar +0 -0
  115. data/lib/server-1.3.13.jar +0 -0
@@ -0,0 +1,37 @@
1
+ require_relative '../diagram_converter'
2
+ require_relative '../util/cli_generator'
3
+ require_relative '../util/platform'
4
+
5
+ module Asciidoctor
6
+ module Diagram
7
+ ['BlockDiag', 'SeqDiag', 'ActDiag', 'NwDiag', 'RackDiag', 'PacketDiag'].each do |name|
8
+ converter = Class.new do
9
+ include DiagramConverter
10
+ include CliGenerator
11
+
12
+ def supported_formats
13
+ [:png, :pdf, :svg]
14
+ end
15
+
16
+ def convert(source, format, options)
17
+ # On Debian based systems the Python 3.x packages python3-(act|block|nw|seq)diag executables with
18
+ # a '3' suffix.
19
+ cmd_name = self.class.const_get(:TOOL)
20
+ alt_cmd_name = "#{cmd_name}3"
21
+
22
+ font_path = source.attr('fontpath')
23
+
24
+ generate_stdin(source.find_command(cmd_name, :alt_cmds => [alt_cmd_name]), format.to_s, source.to_s) do |tool_path, output_path|
25
+ args = [tool_path, '-a', '-o', Platform.native_path(output_path), "-T#{format.to_s}"]
26
+ args << "-f#{Platform.native_path(font_path)}" if font_path
27
+ args << '-'
28
+ args
29
+ end
30
+ end
31
+ end
32
+ converter.const_set(:TOOL, name.downcase)
33
+
34
+ ::Asciidoctor::Diagram.const_set("#{name}Converter", converter)
35
+ end
36
+ end
37
+ end
@@ -1,125 +1,18 @@
1
- require_relative '../extensions'
2
- require_relative '../util/cli_generator'
3
- require_relative '../util/platform'
4
- require_relative '../util/which'
1
+ require_relative '../diagram_processor'
2
+ require_relative 'converter'
5
3
 
6
4
  module Asciidoctor
7
5
  module Diagram
8
- # @!parse
9
- # # Block processor converts blockdiag code into images.
10
- # #
11
- # # Supports PNG and SVG output.
12
- # class BlockDiagBlockProcessor < API::DiagramBlockProcessor; end
13
- #
14
- # # Block macro processor converts blockdiag source files into images.
15
- # #
16
- # # Supports PNG and SVG output.
17
- # class BlockDiagBlockMacroProcessor < DiagramBlockMacroProcessor; end
18
-
19
- # @!parse
20
- # # Block processor converts seqdiag code into images.
21
- # #
22
- # # Supports PNG and SVG output.
23
- # class SeqDiagBlockProcessor < API::DiagramBlockProcessor; end
24
- #
25
- # # Block macro processor converts seqdiag source files into images.
26
- # #
27
- # # Supports PNG and SVG output.
28
- # class SeqDiagBlockMacroProcessor < API::DiagramBlockMacroProcessor; end
29
-
30
- # @!parse
31
- # # Block processor converts actdiag code into images.
32
- # #
33
- # # Supports PNG and SVG output.
34
- # class ActDiagBlockProcessor < API::DiagramBlockProcessor; end
35
- #
36
- # # Block macro processor converts actdiag source files into images.
37
- # #
38
- # # Supports PNG and SVG output.
39
- # class ActDiagBlockMacroProcessor < API::DiagramBlockMacroProcessor; end
40
-
41
- # @!parse
42
- # # Block processor converts nwdiag code into images.
43
- # #
44
- # # Supports PNG and SVG output.
45
- # class NwDiagBlockProcessor < API::DiagramBlockProcessor; end
46
- #
47
- # # Block macro processor converts nwdiag source files into images.
48
- # #
49
- # # Supports PNG and SVG output.
50
- # class NwDiagBlockMacroProcessor < API::DiagramBlockMacroProcessor; end
51
-
52
- # @!parse
53
- # # Block processor converts rackdiag code into images.
54
- # #
55
- # # Supports PNG and SVG output.
56
- # class RackDiagBlockProcessor < API::DiagramBlockProcessor; end
57
- #
58
- # # Block macro processor converts rackdiag source files into images.
59
- # #
60
- # # Supports PNG and SVG output.
61
- # class RackDiagBlockMacroProcessor < API::DiagramBlockMacroProcessor; end
62
-
63
- # @!parse
64
- # # Block processor converts packetdiag code into images.
65
- # #
66
- # # Supports PNG and SVG output.
67
- # class PacketDiagBlockProcessor < API::DiagramBlockProcessor; end
68
- #
69
- # # Block macro processor converts packetdiag source files into images.
70
- # #
71
- # # Supports PNG and SVG output.
72
- # class PacketDiagBlockMacroProcessor < API::DiagramBlockMacroProcessor; end
73
-
74
- # @private
75
- module BlockDiag
76
- def self.define_processors(name)
77
- init = Proc.new do
78
- include ::Asciidoctor::Diagram::BlockDiag
79
-
80
- [:png, :pdf, :svg].each do |f|
81
- register_format(f, :image) do |p, c|
82
- blockdiag(name, p, c, f)
83
- end
84
- end
85
- end
86
-
87
- block = Class.new(Extensions::DiagramBlockProcessor) do
88
- self.instance_eval(&init)
89
- end
90
- ::Asciidoctor::Diagram.const_set("#{name}BlockProcessor", block)
91
-
92
- block_macro = Class.new(Extensions::DiagramBlockMacroProcessor) do
93
- self.instance_eval(&init)
94
- end
95
-
96
- ::Asciidoctor::Diagram.const_set("#{name}BlockMacroProcessor", block_macro)
6
+ ['BlockDiag', 'SeqDiag', 'ActDiag', 'NwDiag', 'RackDiag', 'PacketDiag'].each do |tool|
7
+ block = Class.new(DiagramBlockProcessor) do
8
+ use_converter ::Asciidoctor::Diagram.const_get("#{tool}Converter")
97
9
  end
10
+ ::Asciidoctor::Diagram.const_set("#{tool}BlockProcessor", block)
98
11
 
99
- include CliGenerator
100
- include Which
101
-
102
- def blockdiag(tool, parent, source, format)
103
- inherit_prefix = name
104
- cmd_name = tool.downcase
105
-
106
- # On Debian based systems the Python 3.x packages python3-(act|block|nw|seq)diag executables with
107
- # a '3' suffix.
108
- alt_cmd_name = "#{tool.downcase}3"
109
-
110
- font_path = source.attr('fontpath', nil, inherit_prefix)
111
-
112
- generate_stdin(which(parent, cmd_name, :alt_cmds => [alt_cmd_name]), format.to_s, source.to_s) do |tool_path, output_path|
113
- args = [tool_path, '-a', '-o', Platform.native_path(output_path), "-T#{format.to_s}"]
114
- args << "-f#{Platform.native_path(font_path)}" if font_path
115
- args << '-'
116
- args
117
- end
12
+ block_macro = Class.new(DiagramBlockMacroProcessor) do
13
+ use_converter ::Asciidoctor::Diagram.const_get("#{tool}Converter")
118
14
  end
119
- end
120
-
121
- ['BlockDiag', 'SeqDiag', 'ActDiag', 'NwDiag', 'RackDiag', 'PacketDiag'].each do |tool|
122
- BlockDiag.define_processors(tool)
15
+ ::Asciidoctor::Diagram.const_set("#{tool}BlockMacroProcessor", block_macro)
123
16
  end
124
17
  end
125
18
  end
@@ -0,0 +1,7 @@
1
+ require 'asciidoctor/extensions'
2
+ require_relative 'bpmn/extension'
3
+
4
+ Asciidoctor::Extensions.register do
5
+ block Asciidoctor::Diagram::BpmnBlockProcessor, :bpmn
6
+ block_macro Asciidoctor::Diagram::BpmnBlockMacroProcessor, :bpmn
7
+ end
@@ -0,0 +1,62 @@
1
+ require_relative '../diagram_converter'
2
+ require_relative '../util/cli'
3
+ require_relative '../util/cli_generator'
4
+ require_relative '../util/platform'
5
+
6
+ module Asciidoctor
7
+ module Diagram
8
+ # @private
9
+ class BpmnConverter
10
+ include DiagramConverter
11
+ include CliGenerator
12
+
13
+
14
+ def supported_formats
15
+ [:png, :svg, :pdf, :jpeg]
16
+ end
17
+
18
+ def collect_options(source)
19
+ options = {}
20
+
21
+ options[:width] = source.attr('width')
22
+ options[:height] = source.attr('height')
23
+
24
+ options
25
+ end
26
+
27
+ def convert(source, format, options)
28
+ opts = {}
29
+
30
+ opts[:width] = options[:width]
31
+
32
+ bpmnjs = source.find_command('bpmn-js')
33
+ opts[:height] = options[:height]
34
+ opts[:theme] = options[:theme]
35
+ config = options[:config]
36
+ if config
37
+ opts[:config] = source.resolve_path(config)
38
+ end
39
+ run_bpmnjs(bpmnjs, source, format, opts)
40
+ end
41
+
42
+ private
43
+
44
+ def run_bpmnjs(bpmnjs, source, format, options = {})
45
+ generate_file(bpmnjs, 'bpmn', format.to_s, source.to_s) do |tool_path, input_path, output_path|
46
+ args = [tool_path, Platform.native_path(input_path), '-o', Platform.native_path(output_path), '-t', format.to_s]
47
+
48
+
49
+ if options[:width]
50
+ args << '--width' << options[:width]
51
+ end
52
+
53
+ if options[:height]
54
+ args << '--height' << options[:height]
55
+ end
56
+
57
+ args
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,14 @@
1
+ require_relative 'converter'
2
+ require_relative '../diagram_processor'
3
+
4
+ module Asciidoctor
5
+ module Diagram
6
+ class BpmnBlockProcessor < DiagramBlockProcessor
7
+ use_converter BpmnConverter
8
+ end
9
+
10
+ class BpmnBlockMacroProcessor < DiagramBlockMacroProcessor
11
+ use_converter BpmnConverter
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+ require 'asciidoctor/extensions'
2
+ require_relative 'bytefield/extension'
3
+
4
+ Asciidoctor::Extensions.register do
5
+ block Asciidoctor::Diagram::BytefieldBlockProcessor, :bytefield
6
+ block_macro Asciidoctor::Diagram::BytefieldBlockMacroProcessor, :bytefield
7
+ end
@@ -0,0 +1,26 @@
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 BytefieldConverter
9
+ include DiagramConverter
10
+ include CliGenerator
11
+
12
+
13
+ def supported_formats
14
+ [:svg]
15
+ end
16
+
17
+ def convert(source, format, options)
18
+ bytefield_path = source.find_command('bytefield-svg')
19
+
20
+ generate_stdin(bytefield_path, format.to_s, source.to_s) do |tool_path, output_path|
21
+ [tool_path, "--output", Platform.native_path(output_path)]
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,14 @@
1
+ require_relative 'converter'
2
+ require_relative '../diagram_processor'
3
+
4
+ module Asciidoctor
5
+ module Diagram
6
+ class BytefieldBlockProcessor < DiagramBlockProcessor
7
+ use_converter BytefieldConverter
8
+ end
9
+
10
+ class BytefieldBlockMacroProcessor < DiagramBlockMacroProcessor
11
+ use_converter BytefieldConverter
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ module Asciidoctor
2
+ module Diagram
3
+ # This module describes the duck-typed interface that diagram converters must implement. Implementations
4
+ # may include this module but it is not required.
5
+ module DiagramConverter
6
+ def supported_formats
7
+ raise NotImplementedError.new
8
+ end
9
+
10
+ def collect_options(source)
11
+ {}
12
+ end
13
+
14
+ def convert(source, format, options)
15
+ raise NotImplementedError.new
16
+ end
17
+
18
+ def native_scaling?
19
+ false
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,357 @@
1
+ require 'asciidoctor' unless defined? ::Asciidoctor::VERSION
2
+ require 'asciidoctor/extensions'
3
+ require 'digest'
4
+ require 'json'
5
+ require 'fileutils'
6
+ require 'pathname'
7
+ require_relative 'diagram_source.rb'
8
+ require_relative 'http/converter'
9
+ require_relative 'version'
10
+ require_relative 'util/java'
11
+ require_relative 'util/gif'
12
+ require_relative 'util/pdf'
13
+ require_relative 'util/png'
14
+ require_relative 'util/svg'
15
+
16
+ module Asciidoctor
17
+ module Diagram
18
+ # Mixin that provides the basic machinery for image generation.
19
+ # When this module is included it will include the FormatRegistry into the singleton class of the target class.
20
+ module DiagramProcessor
21
+ include Asciidoctor::Logging
22
+
23
+ module ClassMethods
24
+ def use_converter(converter_type)
25
+ config[:converter] = converter_type
26
+ end
27
+ end
28
+
29
+ def self.included(host_class)
30
+ host_class.use_dsl
31
+ host_class.extend(ClassMethods)
32
+ end
33
+
34
+ IMAGE_PARAMS = {
35
+ :svg => {
36
+ :encoding => Encoding::UTF_8,
37
+ :decoder => SVG
38
+ },
39
+ :gif => {
40
+ :encoding => Encoding::ASCII_8BIT,
41
+ :decoder => GIF
42
+ },
43
+ :png => {
44
+ :encoding => Encoding::ASCII_8BIT,
45
+ :decoder => PNG
46
+ },
47
+ :pdf => {
48
+ :encoding => Encoding::ASCII_8BIT,
49
+ :decoder => PDF
50
+ }
51
+ }
52
+
53
+ # Processes the diagram block or block macro by converting it into an image or literal block.
54
+ #
55
+ # @param parent [Asciidoctor::AbstractBlock] the parent asciidoc block of the block or block macro being processed
56
+ # @param reader_or_target [Asciidoctor::Reader, String] a reader that provides the contents of a block or the
57
+ # target value of a block macro
58
+ # @param attributes [Hash] the attributes of the block or block macro
59
+ # @return [Asciidoctor::AbstractBlock] a new block that replaces the original block or block macro
60
+ def process(parent, reader_or_target, attributes)
61
+ location = parent.document.reader.cursor_at_mark
62
+
63
+ normalised_attributes = attributes.inject({}) { |h, (k, v)| h[normalise_attribute_name(k)] = v; h }
64
+ source = create_source(parent, reader_or_target, normalised_attributes)
65
+
66
+ converter = config[:converter].new
67
+
68
+ supported_formats = converter.supported_formats
69
+
70
+ begin
71
+ format = source.attributes.delete('format') || source.global_attr('format', supported_formats[0])
72
+ format = format.to_sym if format.respond_to?(:to_sym)
73
+
74
+ raise "Format undefined" unless format
75
+
76
+ raise "#{self.class.name} does not support output format #{format}" unless supported_formats.include?(format)
77
+
78
+
79
+ title = source.attributes.delete 'title'
80
+ caption = source.attributes.delete 'caption'
81
+
82
+ case format
83
+ when :txt, :atxt, :utxt
84
+ block = create_literal_block(parent, source, format, converter)
85
+ else
86
+ block = create_image_block(parent, source, format, converter)
87
+ end
88
+
89
+ block.title = title
90
+ block.assign_caption(caption, 'figure')
91
+ block
92
+ rescue => e
93
+ case source.global_attr('on-error', 'log')
94
+ when 'abort'
95
+ raise e
96
+ else
97
+ text = "Failed to generate image: #{e.message}"
98
+ warn_msg = text.dup
99
+ if $VERBOSE
100
+ warn_msg << "\n" << e.backtrace.join("\n")
101
+ end
102
+
103
+ logger.error message_with_context warn_msg, source_location: location
104
+
105
+ text << "\n"
106
+ text << source.code
107
+ Asciidoctor::Block.new parent, :listing, :source => text, :attributes => attributes
108
+ end
109
+
110
+ end
111
+ end
112
+
113
+ protected
114
+
115
+ # Creates a DiagramSource object for the block or block macro being processed. Classes using this
116
+ # mixin must implement this method.
117
+ #
118
+ # @param parent_block [Asciidoctor::AbstractBlock] the parent asciidoc block of the block or block macro being processed
119
+ # @param reader_or_target [Asciidoctor::Reader, String] a reader that provides the contents of a block or the
120
+ # target value of a block macro
121
+ # @param attributes [Hash] the attributes of the block or block macro
122
+ #
123
+ # @return [DiagramSource] an object that implements the interface described by DiagramSource
124
+ #
125
+ # @abstract
126
+ def create_source(parent_block, reader_or_target, attributes)
127
+ raise NotImplementedError.new
128
+ end
129
+
130
+ private
131
+
132
+ def normalise_attribute_name(k)
133
+ case k
134
+ when String
135
+ k.downcase
136
+ when Symbol
137
+ k.to_s.downcase.to_sym
138
+ else
139
+ k
140
+ end
141
+ end
142
+
143
+ DIGIT_CHAR_RANGE = ('0'.ord)..('9'.ord)
144
+
145
+ def create_image_block(parent, source, format, converter)
146
+ image_name = "#{source.image_name}.#{format}"
147
+ image_dir = image_output_dir(parent)
148
+ cache_dir = cache_dir(source, parent)
149
+ image_file = parent.normalize_system_path image_name, image_dir
150
+ metadata_file = parent.normalize_system_path "#{image_name}.cache", cache_dir
151
+
152
+ if File.exist? metadata_file
153
+ metadata = File.open(metadata_file, 'r') {|f| JSON.load(f, nil, :symbolize_names => true, :create_additions => false) }
154
+ else
155
+ metadata = {}
156
+ end
157
+
158
+ image_attributes = source.attributes
159
+ options = converter.collect_options(source)
160
+
161
+ if !File.exist?(image_file) || source.should_process?(image_file, metadata) || options != metadata[:options]
162
+ params = IMAGE_PARAMS[format]
163
+
164
+ server_url = source.global_attr('server-url')
165
+ if server_url
166
+ server_type = source.global_attr('server-type')
167
+ converter = HttpConverter.new(server_url, server_type.to_sym, converter)
168
+ end
169
+
170
+ options = converter.collect_options(source)
171
+ result = converter.convert(source, format, options)
172
+
173
+ result.force_encoding(params[:encoding])
174
+
175
+ metadata = source.create_image_metadata
176
+ metadata[:options] = options
177
+
178
+ result, metadata[:width], metadata[:height] = params[:decoder].post_process_image(result)
179
+
180
+ FileUtils.mkdir_p(File.dirname(image_file)) unless Dir.exist?(File.dirname(image_file))
181
+ File.open(image_file, 'wb') {|f| f.write result}
182
+
183
+ FileUtils.mkdir_p(File.dirname(metadata_file)) unless Dir.exist?(File.dirname(metadata_file))
184
+ File.open(metadata_file, 'w') {|f| JSON.dump(metadata, f)}
185
+ end
186
+
187
+ scale = image_attributes['scale']
188
+ if !converter.native_scaling? && scalematch = /(\d+(?:\.\d+))/.match(scale)
189
+ scale_factor = scalematch[1].to_f
190
+ else
191
+ scale_factor = 1.0
192
+ end
193
+
194
+ if /html/i =~ parent.document.attributes['backend']
195
+ image_attributes.delete('scale')
196
+ if metadata[:width] && !image_attributes['width']
197
+ image_attributes['width'] = (metadata[:width] * scale_factor).to_i
198
+ end
199
+ if metadata[:height] && !image_attributes['height']
200
+ image_attributes['height'] = (metadata[:height] * scale_factor).to_i
201
+ end
202
+ end
203
+
204
+ parent.document.register(:images, image_name)
205
+
206
+ node = Asciidoctor::Block.new parent, :image, :content_model => :empty, :attributes => image_attributes
207
+
208
+ alt_text = node.attr('alt')
209
+ alt_text ||= if title_text = image_attributes['title']
210
+ title_text
211
+ elsif target = image_attributes['target']
212
+ (File.basename(target, File.extname(target)) || '').tr '_-', ' '
213
+ else
214
+ 'Diagram'
215
+ end
216
+ alt_text = parent.sub_specialchars(alt_text)
217
+
218
+ node.set_attr('alt', alt_text)
219
+
220
+ if (scaledwidth = node.attr('scaledwidth'))
221
+ # append % to scaledwidth if ends in number (no units present)
222
+ if DIGIT_CHAR_RANGE.include?((scaledwidth[-1] || 0).ord)
223
+ node.set_attr('scaledwidth', %(#{scaledwidth}%))
224
+ end
225
+ end
226
+
227
+ use_absolute_path = source.attr('data-uri', nil, true)
228
+
229
+ if format == :svg
230
+ svg_type = source.global_attr('svg-type')
231
+ case svg_type
232
+ when nil, 'static'
233
+ when 'inline', 'interactive'
234
+ node.set_option(svg_type)
235
+ use_absolute_path = true
236
+ else
237
+ raise "Unsupported SVG type: #{svg_type}"
238
+ end
239
+ end
240
+
241
+ if use_absolute_path
242
+ node.set_attr('target', image_file)
243
+ else
244
+ node.set_attr('target', image_name)
245
+
246
+ if source.global_attr('autoimagesdir')
247
+ output_base_dir = output_base_dir(parent)
248
+ if output_base_dir
249
+ imagesdir = Pathname.new(image_file).relative_path_from(output_base_dir).dirname.to_s
250
+ node.set_attr('imagesdir', imagesdir)
251
+ end
252
+ end
253
+ end
254
+
255
+ node
256
+ end
257
+
258
+ def scale(size, factor)
259
+ if match = /(\d+)(.*)/.match(size)
260
+ value = match[1].to_i
261
+ unit = match[2]
262
+ (value * factor).to_i.to_s + unit
263
+ else
264
+ size
265
+ end
266
+ end
267
+
268
+ def image_output_dir(parent)
269
+ images_dir = parent.attr('imagesoutdir', nil, true)
270
+
271
+ if images_dir
272
+ base_dir = nil
273
+ else
274
+ base_dir = output_base_dir(parent)
275
+ images_dir = parent.attr('imagesdir', nil, true)
276
+ end
277
+
278
+ parent.normalize_system_path(images_dir, base_dir)
279
+ end
280
+
281
+ def output_base_dir(parent)
282
+ parent.normalize_system_path(parent.attr('outdir', nil, true) || doc_option(parent.document, :to_dir))
283
+ end
284
+
285
+ def cache_dir(source, parent)
286
+ source.global_attr('cachedir') || parent.normalize_system_path('.asciidoctor/diagram', output_base_dir(parent))
287
+ end
288
+
289
+ def create_literal_block(parent, source, format, converter)
290
+ literal_attributes = source.attributes
291
+ literal_attributes.delete('target')
292
+
293
+ options = converter.collect_options(source)
294
+ result = converter.convert(source, format, options)
295
+
296
+ result.force_encoding(Encoding::UTF_8)
297
+ Asciidoctor::Block.new parent, :literal, :source => result, :attributes => literal_attributes
298
+ end
299
+
300
+ def doc_option(document, key)
301
+ if document.respond_to?(:options)
302
+ value = document.options[key]
303
+ else
304
+ value = nil
305
+ end
306
+
307
+ if document.nested? && value.nil?
308
+ doc_option(document.parent_document, key)
309
+ else
310
+ value
311
+ end
312
+ end
313
+ end
314
+
315
+ # Base class for diagram block processors.
316
+ class DiagramBlockProcessor < Asciidoctor::Extensions::BlockProcessor
317
+ include DiagramProcessor
318
+
319
+ def self.inherited(subclass)
320
+ subclass.name_positional_attributes ['target', 'format']
321
+ subclass.contexts [:listing, :literal, :open]
322
+ subclass.content_model :simple
323
+ end
324
+
325
+ # Creates a ReaderSource from the given reader.
326
+ #
327
+ # @return [ReaderSource] a ReaderSource
328
+ def create_source(parent_block, reader, attributes)
329
+ ReaderSource.new(self, parent_block, reader, attributes)
330
+ end
331
+ end
332
+
333
+ # Base class for diagram block macro processors.
334
+ class DiagramBlockMacroProcessor < Asciidoctor::Extensions::BlockMacroProcessor
335
+ include DiagramProcessor
336
+
337
+ def self.inherited(subclass)
338
+ subclass.name_positional_attributes ['target', 'format']
339
+ end
340
+
341
+ def apply_target_subs(parent, target)
342
+ if target
343
+ parent.normalize_system_path(parent.sub_attributes(target, :attribute_missing => 'warn'))
344
+ else
345
+ nil
346
+ end
347
+ end
348
+
349
+ # Creates a FileSource using target as the file name.
350
+ #
351
+ # @return [FileSource] a FileSource
352
+ def create_source(parent, target, attributes)
353
+ FileSource.new(self, parent, apply_target_subs(parent, target), attributes)
354
+ end
355
+ end
356
+ end
357
+ end