asciidoctor-diagram 1.5.19 → 2.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.adoc +71 -0
- data/README.adoc +66 -20
- data/examples/features.adoc +2 -2
- data/lib/asciidoctor-diagram.rb +5 -0
- data/lib/asciidoctor-diagram/a2s/converter.rb +59 -0
- data/lib/asciidoctor-diagram/a2s/extension.rb +6 -52
- data/lib/asciidoctor-diagram/blockdiag/converter.rb +37 -0
- data/lib/asciidoctor-diagram/blockdiag/extension.rb +9 -116
- data/lib/asciidoctor-diagram/bpmn.rb +7 -0
- data/lib/asciidoctor-diagram/bpmn/converter.rb +62 -0
- data/lib/asciidoctor-diagram/bpmn/extension.rb +14 -0
- data/lib/asciidoctor-diagram/bytefield.rb +7 -0
- data/lib/asciidoctor-diagram/bytefield/converter.rb +26 -0
- data/lib/asciidoctor-diagram/bytefield/extension.rb +14 -0
- data/lib/asciidoctor-diagram/diagram_converter.rb +23 -0
- data/lib/asciidoctor-diagram/diagram_processor.rb +374 -0
- data/lib/asciidoctor-diagram/diagram_source.rb +322 -0
- data/lib/asciidoctor-diagram/ditaa/converter.rb +90 -0
- data/lib/asciidoctor-diagram/ditaa/extension.rb +6 -71
- data/lib/asciidoctor-diagram/dpic.rb +7 -0
- data/lib/asciidoctor-diagram/dpic/converter.rb +30 -0
- data/lib/asciidoctor-diagram/dpic/extension.rb +14 -0
- data/lib/asciidoctor-diagram/erd/converter.rb +31 -0
- data/lib/asciidoctor-diagram/erd/extension.rb +6 -35
- data/lib/asciidoctor-diagram/gnuplot/converter.rb +63 -0
- data/lib/asciidoctor-diagram/gnuplot/extension.rb +6 -62
- data/lib/asciidoctor-diagram/graphviz/converter.rb +32 -0
- data/lib/asciidoctor-diagram/graphviz/extension.rb +6 -35
- data/lib/asciidoctor-diagram/http/converter.rb +99 -0
- data/lib/asciidoctor-diagram/http/server.rb +132 -0
- data/lib/asciidoctor-diagram/lilypond/converter.rb +54 -0
- data/lib/asciidoctor-diagram/lilypond/extension.rb +6 -53
- data/lib/asciidoctor-diagram/meme/converter.rb +122 -0
- data/lib/asciidoctor-diagram/meme/extension.rb +5 -107
- data/lib/asciidoctor-diagram/mermaid/converter.rb +192 -0
- data/lib/asciidoctor-diagram/mermaid/extension.rb +6 -159
- data/lib/asciidoctor-diagram/msc/converter.rb +35 -0
- data/lib/asciidoctor-diagram/msc/extension.rb +6 -36
- data/lib/asciidoctor-diagram/nomnoml/converter.rb +25 -0
- data/lib/asciidoctor-diagram/nomnoml/extension.rb +6 -28
- data/lib/asciidoctor-diagram/pikchr.rb +7 -0
- data/lib/asciidoctor-diagram/pikchr/converter.rb +26 -0
- data/lib/asciidoctor-diagram/pikchr/extension.rb +14 -0
- data/lib/asciidoctor-diagram/plantuml/converter.rb +117 -0
- data/lib/asciidoctor-diagram/plantuml/extension.rb +10 -119
- data/lib/asciidoctor-diagram/shaape/converter.rb +25 -0
- data/lib/asciidoctor-diagram/shaape/extension.rb +6 -28
- data/lib/asciidoctor-diagram/smcat/converter.rb +44 -0
- data/lib/asciidoctor-diagram/smcat/extension.rb +6 -42
- data/lib/asciidoctor-diagram/svgbob/converter.rb +49 -0
- data/lib/asciidoctor-diagram/svgbob/extension.rb +6 -28
- data/lib/asciidoctor-diagram/symbolator.rb +7 -0
- data/lib/asciidoctor-diagram/symbolator/converter.rb +23 -0
- data/lib/asciidoctor-diagram/symbolator/extension.rb +14 -0
- data/lib/asciidoctor-diagram/syntrax/converter.rb +58 -0
- data/lib/asciidoctor-diagram/syntrax/extension.rb +6 -51
- data/lib/asciidoctor-diagram/tikz/converter.rb +75 -0
- data/lib/asciidoctor-diagram/tikz/extension.rb +6 -60
- data/lib/asciidoctor-diagram/umlet/converter.rb +33 -0
- data/lib/asciidoctor-diagram/umlet/extension.rb +6 -28
- data/lib/asciidoctor-diagram/util/cli.rb +14 -3
- data/lib/asciidoctor-diagram/util/cli_generator.rb +19 -1
- data/lib/asciidoctor-diagram/util/gif.rb +2 -2
- data/lib/asciidoctor-diagram/util/java.rb +114 -1
- data/lib/asciidoctor-diagram/util/java_socket.rb +8 -120
- data/lib/asciidoctor-diagram/util/pdf.rb +2 -2
- data/lib/asciidoctor-diagram/util/png.rb +2 -2
- data/lib/asciidoctor-diagram/util/svg.rb +46 -19
- data/lib/asciidoctor-diagram/util/which.rb +0 -29
- data/lib/asciidoctor-diagram/vega/converter.rb +47 -0
- data/lib/asciidoctor-diagram/vega/extension.rb +6 -44
- data/lib/asciidoctor-diagram/version.rb +1 -1
- data/lib/asciidoctor-diagram/wavedrom/converter.rb +50 -0
- data/lib/asciidoctor-diagram/wavedrom/extension.rb +6 -54
- data/lib/ditaa-1.3.15.jar +0 -0
- data/lib/plantuml-1.3.15.jar +0 -0
- data/lib/plantuml.jar +0 -0
- data/lib/server-1.3.15.jar +0 -0
- data/spec/a2s_spec.rb +2 -140
- data/spec/blockdiag_spec.rb +2 -200
- data/spec/bpmn_spec.rb +56 -0
- data/spec/bytefield_spec.rb +92 -0
- data/spec/ditaa_spec.rb +37 -143
- data/spec/dpic_spec.rb +19 -0
- data/spec/erd_spec.rb +2 -199
- data/spec/gnuplot_spec.rb +2 -255
- data/spec/graphviz_spec.rb +6 -145
- data/spec/lilypond_spec.rb +2 -140
- data/spec/mermaid_spec.rb +62 -209
- data/spec/msc_spec.rb +2 -199
- data/spec/nomnoml_spec.rb +4 -142
- data/spec/pikchr_spec.rb +51 -0
- data/spec/plantuml_spec.rb +24 -507
- data/spec/shaape_spec.rb +9 -221
- data/spec/shared_examples.rb +603 -0
- data/spec/smcat_spec.rb +2 -140
- data/spec/svgbob_spec.rb +2 -140
- data/spec/symbolator_spec.rb +23 -0
- data/spec/syntrax_spec.rb +5 -215
- data/spec/test_helper.rb +1 -21
- data/spec/tikz_spec.rb +65 -15
- data/spec/umlet_spec.rb +2 -58
- data/spec/vega_spec.rb +4 -117
- data/spec/wavedrom_spec.rb +2 -199
- metadata +58 -8
- data/lib/asciidoctor-diagram/extensions.rb +0 -568
- data/lib/ditaa-1.3.13.jar +0 -0
- data/lib/plantuml-1.3.13.jar +0 -0
- 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 '../
|
2
|
-
require_relative '
|
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
|
-
|
9
|
-
|
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
|
-
|
100
|
-
|
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
|
-
|
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,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,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,374 @@
|
|
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
|
+
|
148
|
+
image_file = parent.normalize_system_path(image_name, image_output_dir(parent))
|
149
|
+
metadata_file = parent.normalize_system_path("#{image_name}.cache", cache_dir(source, parent))
|
150
|
+
|
151
|
+
if File.exist? metadata_file
|
152
|
+
metadata = File.open(metadata_file, 'r') {|f| JSON.load(f, nil, :symbolize_names => true, :create_additions => false) }
|
153
|
+
else
|
154
|
+
metadata = {}
|
155
|
+
end
|
156
|
+
|
157
|
+
image_attributes = source.attributes
|
158
|
+
options = converter.collect_options(source)
|
159
|
+
|
160
|
+
if !File.exist?(image_file) || source.should_process?(image_file, metadata) || options != metadata[:options]
|
161
|
+
params = IMAGE_PARAMS[format]
|
162
|
+
|
163
|
+
server_url = source.global_attr('server-url')
|
164
|
+
if server_url
|
165
|
+
server_type = source.global_attr('server-type')
|
166
|
+
converter = HttpConverter.new(server_url, server_type.to_sym, converter)
|
167
|
+
end
|
168
|
+
|
169
|
+
options = converter.collect_options(source)
|
170
|
+
result = converter.convert(source, format, options)
|
171
|
+
|
172
|
+
result.force_encoding(params[:encoding])
|
173
|
+
|
174
|
+
metadata = source.create_image_metadata
|
175
|
+
metadata[:options] = options
|
176
|
+
|
177
|
+
result, metadata[:width], metadata[:height] = params[:decoder].post_process_image(result)
|
178
|
+
|
179
|
+
FileUtils.mkdir_p(File.dirname(image_file)) unless Dir.exist?(File.dirname(image_file))
|
180
|
+
File.open(image_file, 'wb') {|f| f.write result}
|
181
|
+
|
182
|
+
FileUtils.mkdir_p(File.dirname(metadata_file)) unless Dir.exist?(File.dirname(metadata_file))
|
183
|
+
File.open(metadata_file, 'w') {|f| JSON.dump(metadata, f)}
|
184
|
+
end
|
185
|
+
|
186
|
+
scale = image_attributes['scale']
|
187
|
+
if !converter.native_scaling? && scalematch = /([0-9]+(?:\.[0-9]+)?)/.match(scale)
|
188
|
+
scale_factor = scalematch[1].to_f
|
189
|
+
else
|
190
|
+
scale_factor = 1.0
|
191
|
+
end
|
192
|
+
|
193
|
+
if /html/i =~ parent.document.attributes['backend']
|
194
|
+
image_attributes.delete('scale')
|
195
|
+
if metadata[:width] && !image_attributes['width']
|
196
|
+
image_attributes['width'] = (metadata[:width] * scale_factor).to_i
|
197
|
+
end
|
198
|
+
if metadata[:height] && !image_attributes['height']
|
199
|
+
image_attributes['height'] = (metadata[:height] * scale_factor).to_i
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
parent.document.register(:images, image_name)
|
204
|
+
|
205
|
+
node = Asciidoctor::Block.new parent, :image, :content_model => :empty, :attributes => image_attributes
|
206
|
+
|
207
|
+
alt_text = node.attr('alt')
|
208
|
+
alt_text ||= if title_text = image_attributes['title']
|
209
|
+
title_text
|
210
|
+
elsif target = image_attributes['target']
|
211
|
+
(File.basename(target, File.extname(target)) || '').tr '_-', ' '
|
212
|
+
else
|
213
|
+
'Diagram'
|
214
|
+
end
|
215
|
+
alt_text = parent.sub_specialchars(alt_text)
|
216
|
+
|
217
|
+
node.set_attr('alt', alt_text)
|
218
|
+
|
219
|
+
if (scaledwidth = node.attr('scaledwidth'))
|
220
|
+
# append % to scaledwidth if ends in number (no units present)
|
221
|
+
if DIGIT_CHAR_RANGE.include?((scaledwidth[-1] || 0).ord)
|
222
|
+
node.set_attr('scaledwidth', %(#{scaledwidth}%))
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
use_absolute_path = source.attr('data-uri', nil, true)
|
227
|
+
|
228
|
+
if format == :svg
|
229
|
+
svg_type = source.global_attr('svg-type')
|
230
|
+
case svg_type
|
231
|
+
when nil, 'static'
|
232
|
+
when 'inline', 'interactive'
|
233
|
+
node.set_option(svg_type)
|
234
|
+
use_absolute_path = true
|
235
|
+
else
|
236
|
+
raise "Unsupported SVG type: #{svg_type}"
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
if use_absolute_path
|
241
|
+
node.set_attr('target', image_file)
|
242
|
+
else
|
243
|
+
node.set_attr('target', image_name)
|
244
|
+
|
245
|
+
if source.global_attr('autoimagesdir')
|
246
|
+
image_path = Pathname.new(image_file)
|
247
|
+
output_path = Pathname.new(parent.normalize_system_path(output_dir(parent)))
|
248
|
+
|
249
|
+
imagesdir = image_path.relative_path_from(output_path).dirname.to_s
|
250
|
+
node.set_attr('imagesdir', imagesdir)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
node
|
255
|
+
end
|
256
|
+
|
257
|
+
def scale(size, factor)
|
258
|
+
if match = /(\d+)(.*)/.match(size)
|
259
|
+
value = match[1].to_i
|
260
|
+
unit = match[2]
|
261
|
+
(value * factor).to_i.to_s + unit
|
262
|
+
else
|
263
|
+
size
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
# Returns the image output directory as an absolute path
|
268
|
+
def image_output_dir(parent)
|
269
|
+
images_out_dir = parent.attr('imagesoutdir', nil, true)
|
270
|
+
|
271
|
+
if images_out_dir
|
272
|
+
resolve_path(parent, images_out_dir)
|
273
|
+
else
|
274
|
+
images_dir = parent.attr('imagesdir', nil, true)
|
275
|
+
output_dir = output_dir(parent)
|
276
|
+
resolve_path(parent, images_dir, output_dir)
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
# Returns the cache directory as an absolute path
|
281
|
+
def cache_dir(source, parent)
|
282
|
+
cache_dir = source.global_attr('cachedir')
|
283
|
+
if cache_dir
|
284
|
+
resolve_path(parent, cache_dir)
|
285
|
+
else
|
286
|
+
output_dir = output_dir(parent)
|
287
|
+
resolve_path(parent, '.asciidoctor/diagram', output_dir)
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
# Returns the general output directory for Asciidoctor as an absolute path
|
292
|
+
def output_dir(parent)
|
293
|
+
resolve_path(parent, parent.attr('outdir', nil, true) || doc_option(parent.document, :to_dir))
|
294
|
+
end
|
295
|
+
|
296
|
+
def resolve_path(parent, path, base_dir = nil)
|
297
|
+
if path.nil?
|
298
|
+
# Resolve the base dir itself
|
299
|
+
parent.document.path_resolver.system_path(base_dir)
|
300
|
+
else
|
301
|
+
# Resolve the path with respect to the base dir
|
302
|
+
parent.document.path_resolver.system_path(path, base_dir)
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
def create_literal_block(parent, source, format, converter)
|
307
|
+
literal_attributes = source.attributes
|
308
|
+
literal_attributes.delete('target')
|
309
|
+
|
310
|
+
options = converter.collect_options(source)
|
311
|
+
result = converter.convert(source, format, options)
|
312
|
+
|
313
|
+
result.force_encoding(Encoding::UTF_8)
|
314
|
+
Asciidoctor::Block.new parent, :literal, :source => result, :attributes => literal_attributes
|
315
|
+
end
|
316
|
+
|
317
|
+
def doc_option(document, key)
|
318
|
+
if document.respond_to?(:options)
|
319
|
+
value = document.options[key]
|
320
|
+
else
|
321
|
+
value = nil
|
322
|
+
end
|
323
|
+
|
324
|
+
if document.nested? && value.nil?
|
325
|
+
doc_option(document.parent_document, key)
|
326
|
+
else
|
327
|
+
value
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
# Base class for diagram block processors.
|
333
|
+
class DiagramBlockProcessor < Asciidoctor::Extensions::BlockProcessor
|
334
|
+
include DiagramProcessor
|
335
|
+
|
336
|
+
def self.inherited(subclass)
|
337
|
+
subclass.name_positional_attributes ['target', 'format']
|
338
|
+
subclass.contexts [:listing, :literal, :open]
|
339
|
+
subclass.content_model :simple
|
340
|
+
end
|
341
|
+
|
342
|
+
# Creates a ReaderSource from the given reader.
|
343
|
+
#
|
344
|
+
# @return [ReaderSource] a ReaderSource
|
345
|
+
def create_source(parent_block, reader, attributes)
|
346
|
+
ReaderSource.new(self, parent_block, reader, attributes)
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
# Base class for diagram block macro processors.
|
351
|
+
class DiagramBlockMacroProcessor < Asciidoctor::Extensions::BlockMacroProcessor
|
352
|
+
include DiagramProcessor
|
353
|
+
|
354
|
+
def self.inherited(subclass)
|
355
|
+
subclass.name_positional_attributes ['format']
|
356
|
+
end
|
357
|
+
|
358
|
+
def apply_target_subs(parent, target)
|
359
|
+
if target
|
360
|
+
parent.normalize_system_path(parent.sub_attributes(target, :attribute_missing => 'warn'))
|
361
|
+
else
|
362
|
+
nil
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
# Creates a FileSource using target as the file name.
|
367
|
+
#
|
368
|
+
# @return [FileSource] a FileSource
|
369
|
+
def create_source(parent, target, attributes)
|
370
|
+
FileSource.new(self, parent, apply_target_subs(parent, target), attributes)
|
371
|
+
end
|
372
|
+
end
|
373
|
+
end
|
374
|
+
end
|