asciidoctor-diagram 1.2.1-java → 1.3.0.preview.1-java
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.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +10 -5
- data/README.adoc +27 -3
- data/lib/asciidoctor-diagram/blockdiag.rb +2 -6
- data/lib/asciidoctor-diagram/blockdiag/extension.rb +84 -3
- data/lib/asciidoctor-diagram/ditaa.rb +2 -2
- data/lib/asciidoctor-diagram/ditaa/extension.rb +16 -7
- data/lib/asciidoctor-diagram/extensions.rb +325 -0
- data/lib/asciidoctor-diagram/graphviz.rb +2 -2
- data/lib/asciidoctor-diagram/graphviz/extension.rb +17 -6
- data/lib/asciidoctor-diagram/plantuml.rb +2 -2
- data/lib/asciidoctor-diagram/plantuml/extension.rb +59 -11
- data/lib/asciidoctor-diagram/shaape.rb +2 -2
- data/lib/asciidoctor-diagram/shaape/extension.rb +17 -6
- data/lib/asciidoctor-diagram/util/binaryio.rb +2 -0
- data/lib/asciidoctor-diagram/util/cli_generator.rb +2 -1
- data/lib/asciidoctor-diagram/util/java_jruby.rb +1 -0
- data/lib/asciidoctor-diagram/util/java_rjb.rb +1 -0
- data/lib/asciidoctor-diagram/util/png.rb +1 -0
- data/lib/asciidoctor-diagram/util/svg.rb +1 -0
- data/lib/asciidoctor-diagram/util/which.rb +1 -0
- data/lib/asciidoctor-diagram/version.rb +1 -1
- data/lib/plantuml.jar +0 -0
- data/spec/blockdiag_spec.rb +2 -2
- data/spec/ditaa_spec.rb +2 -2
- data/spec/graphviz_spec.rb +2 -2
- data/spec/plantuml_spec.rb +2 -2
- data/spec/shaape_spec.rb +2 -2
- metadata +4 -5
- data/lib/asciidoctor-diagram/plantuml/generator.rb +0 -46
- data/lib/asciidoctor-diagram/util/diagram.rb +0 -220
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e1cbd019a0247c43f55f350f3897a689b3af5d83
|
4
|
+
data.tar.gz: 3d85e72167829121d471faa6f20586cc2eb1eab4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 24de85845930e2d4b87458dbf5e79308d09134975e61bc8ec8f34f354c3b8f2c317f8687a18e8a58f3deaee85c6ac7e183da3b9b332f67dbfa2f352667ac5810
|
7
|
+
data.tar.gz: ff54af102f2a7d21eefa591f4db10985826c97f2eff4044bb305a91f2c0cc888f5d965fddea4bba4f75ad96ed03baa2f5bcca8edf6e952d664c5cd8663183cd7
|
data/CHANGELOG.adoc
CHANGED
@@ -1,10 +1,14 @@
|
|
1
1
|
= Asciidoctor-diagram Changelog
|
2
2
|
|
3
|
-
== 1.
|
3
|
+
== 1.3.0
|
4
4
|
|
5
5
|
Enhancements::
|
6
6
|
|
7
|
-
*
|
7
|
+
* Introduces a documented, public extension API
|
8
|
+
* Add support for Actdiag diagrams (requires Actdiag to be installed separately)
|
9
|
+
* Add support for Seqdiag diagrams (requires Seqdiag to be installed separately)
|
10
|
+
* Add support for Nwdiag diagrams (requires Nwdiag to be installed separately)
|
11
|
+
* Updated PlantUML to revision 8004 (23/08/2014)
|
8
12
|
|
9
13
|
== 1.2.0
|
10
14
|
|
@@ -16,11 +20,12 @@ Enhancements::
|
|
16
20
|
|
17
21
|
Enhancements::
|
18
22
|
|
19
|
-
* Updated PlantUML to revision 8002 (23/
|
20
|
-
* Add support for Shaape diagrams (requires Shaape
|
21
|
-
* Add support for Blockdiag diagrams (requires Blockdiag
|
23
|
+
* Updated PlantUML to revision 8002 (23/07/2014)
|
24
|
+
* Add support for Shaape diagrams (requires Shaape to be installed separately)
|
25
|
+
* Add support for Blockdiag diagrams (requires Blockdiag to be installed separately)
|
22
26
|
|
23
27
|
Bug Fixes::
|
28
|
+
|
24
29
|
* Issue #38: Resolved Graphviz syntax errors with certain diagrams
|
25
30
|
|
26
31
|
== 1.1.5
|
data/README.adoc
CHANGED
@@ -2,7 +2,14 @@
|
|
2
2
|
Pepijn Van_Eeckhoudt
|
3
3
|
|
4
4
|
Asciidoctor Diagram is a set of extensions for http://asciidoctor.org[Asciidoctor], the Ruby-based AsciiDoc processor.
|
5
|
-
These extensions allow you to embed diagrams
|
5
|
+
These extensions allow you to embed plain text diagrams inside your AsciiDoc documents using one of the following syntaxes:
|
6
|
+
|
7
|
+
- http://plantuml.sourceforge.net[PlantUML]
|
8
|
+
- http://ditaa.sourceforge.net[Ditaa].
|
9
|
+
- https://github.com/christiangoltz/shaape[Shaape]
|
10
|
+
- http://blockdiag.com[BlockDiag, SeqDiag, ActDiag, NwDiag]
|
11
|
+
- http://www.graphviz.org/content/dot-language[GraphViz DOT]
|
12
|
+
|
6
13
|
The extension takes care of running the diagram processor to generate the images from the input text and insert them into the rendered document.
|
7
14
|
|
8
15
|
This gem was inspired by the https://code.google.com/p/asciidoc-plantuml/[AsciiDoc PlantUML filter] for AsciiDoc Python.
|
@@ -23,6 +30,14 @@ Or install it yourself as:
|
|
23
30
|
|
24
31
|
$ gem install asciidoctor-diagram
|
25
32
|
|
33
|
+
=== Additional Requirements
|
34
|
+
|
35
|
+
Certain diagram types require other tools to be installed seperately.
|
36
|
+
|
37
|
+
- PlantUML and Graphviz: the Graphviz `dot` tool.
|
38
|
+
- Shaape: the Shaape Python package
|
39
|
+
- Block/Seq/Act/Nw diag: the block/seq/act/nw diag Python packages
|
40
|
+
|
26
41
|
== Usage
|
27
42
|
|
28
43
|
=== Enable the extensions
|
@@ -32,9 +47,11 @@ In order to use extensions you should need to invoke Asciidoctor via the http://
|
|
32
47
|
In your script you can then either require one or more of the following files:
|
33
48
|
|
34
49
|
. `asciidoctor-diagram`: to enable all the diagramming extensions
|
50
|
+
. `asciidoctor-diagram/blockdiag`: to enable the block/act/seq/nw diag extension
|
35
51
|
. `asciidoctor-diagram/ditaa`: to enable the ditaa extension
|
36
52
|
. `asciidoctor-diagram/graphviz`: to enable the graphviz extension
|
37
53
|
. `asciidoctor-diagram/plantuml`: to enable the plantuml extension
|
54
|
+
. `asciidoctor-diagram/shaape`: to enable the shaape extension
|
38
55
|
|
39
56
|
Requiring one or more of these files will automatically register the extensions for all processed documents.
|
40
57
|
If you need more fine grained control over when the extensions are enabled or not, `asciidoctor-diagram/ditaa/extension`, `asciidoctor-diagram/graphviz/extension` and `asciidoctor-diagram/plantuml/extension` can be used instead.
|
@@ -43,7 +60,14 @@ You should then register the extensions yourself at the appropriate time using t
|
|
43
60
|
|
44
61
|
=== Using the extensions
|
45
62
|
|
46
|
-
Once the extensions are enabled
|
63
|
+
Once the extensions are enabled the following block types becomes available for your documents:
|
64
|
+
|
65
|
+
- `blockdiag`, `actdiag`, `seqdiag`, `nwdiag`, `rackdiag` and `packetdiag`
|
66
|
+
- `ditaa`
|
67
|
+
- `graphviz`
|
68
|
+
- `plantuml`
|
69
|
+
- `shaape`
|
70
|
+
|
47
71
|
Detailed descriptions of the supported syntax inside these blocks is available on the http://plantuml.sourceforge.net/[PlantUML], http://www.graphviz.org/content/dot-language[Graphviz] and http://ditaa.sourceforge.net/[ditaa] websites.
|
48
72
|
|
49
73
|
At this point you can start adding diagrams to your application, like the following example:
|
@@ -65,7 +89,7 @@ DiagramBlock <|-- PlantUmlBlock
|
|
65
89
|
The diagram blocks support the following attributes:
|
66
90
|
|
67
91
|
. `target` (or 2nd position): the basename of the file to generate. If not specified an auto-generated name will be used.
|
68
|
-
. `format` (or 3rd position): the output format. PlantUML blocks support `png`, `svg` and `txt`. Graphviz
|
92
|
+
. `format` (or 3rd position): the output format. PlantUML blocks support `png`, `svg` and `txt`. Graphviz, Shaape and BlockDiag support `png` and `svg`. Ditaa only supports `png`.
|
69
93
|
|
70
94
|
== Contributing
|
71
95
|
|
@@ -5,11 +5,7 @@ Asciidoctor::Extensions.register do
|
|
5
5
|
require_relative 'blockdiag/extension'
|
6
6
|
|
7
7
|
['BlockDiag', 'SeqDiag', 'ActDiag', 'NwDiag', 'RackDiag', 'PacketDiag'].each do |tool|
|
8
|
-
|
9
|
-
|
10
|
-
block_macro = Asciidoctor::Diagram.const_get("#{tool}BlockMacro")
|
11
|
-
|
12
|
-
block block, name
|
13
|
-
block_macro block_macro, name
|
8
|
+
block ::Asciidoctor::Diagram.const_get("#{tool}BlockProcessor"), tool.downcase.to_sym
|
9
|
+
block_macro ::Asciidoctor::Diagram.const_get("#{tool}BlockMacroProcessor"), tool.downcase.to_sym
|
14
10
|
end
|
15
11
|
end
|
@@ -1,13 +1,94 @@
|
|
1
|
+
require_relative '../extensions'
|
1
2
|
require_relative '../util/cli_generator'
|
2
|
-
require_relative '../util/diagram'
|
3
3
|
|
4
4
|
module Asciidoctor
|
5
5
|
module Diagram
|
6
|
+
# @private
|
7
|
+
module BlockDiag
|
8
|
+
def self.define_processors(name, &init)
|
9
|
+
block = Class.new(Extensions::DiagramBlockProcessor) do
|
10
|
+
self.instance_eval &init
|
11
|
+
end
|
12
|
+
::Asciidoctor::Diagram.const_set("#{name}BlockProcessor", block)
|
13
|
+
|
14
|
+
block_macro = Class.new(Extensions::DiagramBlockMacroProcessor) do
|
15
|
+
self.instance_eval &init
|
16
|
+
end
|
17
|
+
|
18
|
+
::Asciidoctor::Diagram.const_set("#{name}BlockMacroProcessor", block_macro)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# @!parse
|
23
|
+
# # Block processor converts blockdiag code into images.
|
24
|
+
# #
|
25
|
+
# # Supports PNG and SVG output.
|
26
|
+
# class BlockDiagBlockProcessor < API::DiagramBlockProcessor; end
|
27
|
+
#
|
28
|
+
# # Block macro processor converts blockdiag source files into images.
|
29
|
+
# #
|
30
|
+
# # Supports PNG and SVG output.
|
31
|
+
# class BlockDiagBlockMacroProcessor < DiagramBlockMacroProcessor; end
|
32
|
+
|
33
|
+
# @!parse
|
34
|
+
# # Block processor converts seqdiag code into images.
|
35
|
+
# #
|
36
|
+
# # Supports PNG and SVG output.
|
37
|
+
# class SeqDiagBlockProcessor < API::DiagramBlockProcessor; end
|
38
|
+
#
|
39
|
+
# # Block macro processor converts seqdiag source files into images.
|
40
|
+
# #
|
41
|
+
# # Supports PNG and SVG output.
|
42
|
+
# class SeqDiagBlockMacroProcessor < API::DiagramBlockMacroProcessor; end
|
43
|
+
|
44
|
+
# @!parse
|
45
|
+
# # Block processor converts actdiag code into images.
|
46
|
+
# #
|
47
|
+
# # Supports PNG and SVG output.
|
48
|
+
# class ActDiagBlockProcessor < API::DiagramBlockProcessor; end
|
49
|
+
#
|
50
|
+
# # Block macro processor converts actdiag source files into images.
|
51
|
+
# #
|
52
|
+
# # Supports PNG and SVG output.
|
53
|
+
# class ActDiagBlockMacroProcessor < API::DiagramBlockMacroProcessor; end
|
54
|
+
|
55
|
+
# @!parse
|
56
|
+
# # Block processor converts nwdiag code into images.
|
57
|
+
# #
|
58
|
+
# # Supports PNG and SVG output.
|
59
|
+
# class NwDiagBlockProcessor < API::DiagramBlockProcessor; end
|
60
|
+
#
|
61
|
+
# # Block macro processor converts nwdiag source files into images.
|
62
|
+
# #
|
63
|
+
# # Supports PNG and SVG output.
|
64
|
+
# class NwDiagBlockMacroProcessor < API::DiagramBlockMacroProcessor; end
|
65
|
+
|
66
|
+
# @!parse
|
67
|
+
# # Block processor converts rackdiag code into images.
|
68
|
+
# #
|
69
|
+
# # Supports PNG and SVG output.
|
70
|
+
# class RackDiagBlockProcessor < API::DiagramBlockProcessor; end
|
71
|
+
#
|
72
|
+
# # Block macro processor converts rackdiag source files into images.
|
73
|
+
# #
|
74
|
+
# # Supports PNG and SVG output.
|
75
|
+
# class RackDiagBlockMacroProcessor < API::DiagramBlockMacroProcessor; end
|
76
|
+
|
77
|
+
# @!parse
|
78
|
+
# # Block processor converts packetdiag code into images.
|
79
|
+
# #
|
80
|
+
# # Supports PNG and SVG output.
|
81
|
+
# class PacketDiagBlockProcessor < API::DiagramBlockProcessor; end
|
82
|
+
#
|
83
|
+
# # Block macro processor converts packetdiag source files into images.
|
84
|
+
# #
|
85
|
+
# # Supports PNG and SVG output.
|
86
|
+
# class PacketDiagBlockMacroProcessor < API::DiagramBlockMacroProcessor; end
|
6
87
|
['BlockDiag', 'SeqDiag', 'ActDiag', 'NwDiag', 'RackDiag', 'PacketDiag'].each do |tool|
|
7
|
-
define_processors(tool) do
|
88
|
+
BlockDiag.define_processors(tool) do
|
8
89
|
[:png, :svg].each do |f|
|
9
90
|
register_format(f, :image) do |c, p|
|
10
|
-
CliGenerator.generate(tool.downcase, p, c) do |tool_path, output_path|
|
91
|
+
CliGenerator.generate(tool.downcase, p, c.to_s) do |tool_path, output_path|
|
11
92
|
[tool_path, '-o', output_path, "-T#{f.to_s}", '-']
|
12
93
|
end
|
13
94
|
end
|
@@ -3,6 +3,6 @@ require_relative 'version'
|
|
3
3
|
|
4
4
|
Asciidoctor::Extensions.register do
|
5
5
|
require_relative 'ditaa/extension'
|
6
|
-
block Asciidoctor::Diagram::
|
7
|
-
block_macro Asciidoctor::Diagram::
|
6
|
+
block Asciidoctor::Diagram::DitaaBlockProcessor, :ditaa
|
7
|
+
block_macro Asciidoctor::Diagram::DitaaBlockMacroProcessor, :ditaa
|
8
8
|
end
|
@@ -1,13 +1,14 @@
|
|
1
|
-
require_relative '../
|
1
|
+
require_relative '../extensions'
|
2
2
|
require_relative '../util/java'
|
3
3
|
|
4
4
|
module Asciidoctor
|
5
5
|
module Diagram
|
6
|
-
|
6
|
+
# @private
|
7
|
+
module Ditaa
|
7
8
|
DITAA_JAR_PATH = File.expand_path File.join('../..', 'ditaamini0_9.jar'), File.dirname(__FILE__)
|
8
9
|
Java.classpath << DITAA_JAR_PATH
|
9
10
|
|
10
|
-
def
|
11
|
+
def ditaa(code)
|
11
12
|
Java.load
|
12
13
|
|
13
14
|
args = ['-e', 'UTF-8']
|
@@ -25,12 +26,20 @@ module Asciidoctor
|
|
25
26
|
|
26
27
|
result
|
27
28
|
end
|
28
|
-
end
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
30
|
+
def self.included(mod)
|
31
|
+
mod.register_format(:png, :image) do |c|
|
32
|
+
ditaa(c.to_s)
|
33
|
+
end
|
33
34
|
end
|
34
35
|
end
|
36
|
+
|
37
|
+
class DitaaBlockProcessor < Extensions::DiagramBlockProcessor
|
38
|
+
include Ditaa
|
39
|
+
end
|
40
|
+
|
41
|
+
class DitaaBlockMacroProcessor < Extensions::DiagramBlockMacroProcessor
|
42
|
+
include Ditaa
|
43
|
+
end
|
35
44
|
end
|
36
45
|
end
|
@@ -0,0 +1,325 @@
|
|
1
|
+
require 'asciidoctor/extensions'
|
2
|
+
require 'digest'
|
3
|
+
require 'json'
|
4
|
+
require 'fileutils'
|
5
|
+
require_relative 'util/java'
|
6
|
+
require_relative 'util/png'
|
7
|
+
require_relative 'util/svg'
|
8
|
+
|
9
|
+
module Asciidoctor
|
10
|
+
module Diagram
|
11
|
+
module Extensions
|
12
|
+
# Provides the means for diagram processors to register supported output formats and image
|
13
|
+
# generation routines
|
14
|
+
module FormatRegistry
|
15
|
+
# Registers a supported format. The first registered format becomes the default format for the block
|
16
|
+
# processor.
|
17
|
+
#
|
18
|
+
# @param [Symbol] format the format name
|
19
|
+
# @param [Symbol] type a symbol indicating the type of block that should be generated; either :image or :literal
|
20
|
+
# @yieldparam parent [Asciidoctor::AbstractNode] the asciidoc block that is being processed
|
21
|
+
# @yieldparam source [DiagramSource] the source object
|
22
|
+
# @yieldreturn [String] the generated diagram
|
23
|
+
#
|
24
|
+
# Examples
|
25
|
+
#
|
26
|
+
# register_format(:png, :image ) do |parent, source|
|
27
|
+
# File.read(source.to_s)
|
28
|
+
# end
|
29
|
+
def register_format(format, type, &block)
|
30
|
+
unless @default_format
|
31
|
+
@default_format = format
|
32
|
+
end
|
33
|
+
|
34
|
+
formats[format] = {
|
35
|
+
:type => type,
|
36
|
+
:generator => block
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns the registered formats
|
41
|
+
#
|
42
|
+
# @return [Hash]
|
43
|
+
# @api private
|
44
|
+
def formats
|
45
|
+
@formats ||= {}
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns the default format
|
49
|
+
#
|
50
|
+
# @return [Symbol] the default format
|
51
|
+
# @api private
|
52
|
+
def default_format
|
53
|
+
@default_format
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Mixin that provides the basic machinery for image generation.
|
58
|
+
# When this module is included it will include the FormatRegistry into the singleton class of the target class.
|
59
|
+
module DiagramProcessor
|
60
|
+
IMAGE_PARAMS = {
|
61
|
+
:svg => {
|
62
|
+
:encoding => Encoding::UTF_8,
|
63
|
+
:decoder => SVG
|
64
|
+
},
|
65
|
+
:png => {
|
66
|
+
:encoding => Encoding::ASCII_8BIT,
|
67
|
+
:decoder => PNG
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
def self.included(mod)
|
72
|
+
class << mod
|
73
|
+
include FormatRegistry
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Processes the diagram block or block macro by converting it into an image or literal block.
|
78
|
+
#
|
79
|
+
# @param parent [Asciidoctor::AbstractBlock] the parent asciidoc block of the block or block macro being processed
|
80
|
+
# @param reader_or_target [Asciidoctor::Reader, String] a reader that provides the contents of a block or the
|
81
|
+
# target value of a block macro
|
82
|
+
# @param attributes [Hash] the attributes of the block or block macro
|
83
|
+
# @return [Asciidoctor::AbstractBlock] a new block that replaces the original block or block macro
|
84
|
+
def process(parent, reader_or_target, attributes)
|
85
|
+
source = create_source(parent, reader_or_target, attributes)
|
86
|
+
|
87
|
+
format = attributes.delete('format') || self.class.default_format
|
88
|
+
format = format.to_sym if format.respond_to?(:to_sym)
|
89
|
+
|
90
|
+
raise "Format undefined" unless format
|
91
|
+
|
92
|
+
generator_info = self.class.formats[format]
|
93
|
+
|
94
|
+
raise "#{self.class.name} does not support output format #{format}" unless generator_info
|
95
|
+
|
96
|
+
case generator_info[:type]
|
97
|
+
when :image
|
98
|
+
create_image_block(parent, source, attributes, format, generator_info)
|
99
|
+
when :literal
|
100
|
+
create_literal_block(parent, source, attributes, generator_info)
|
101
|
+
else
|
102
|
+
raise "Unsupported output format: #{format}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
protected
|
107
|
+
|
108
|
+
# Creates a DiagramSource object for the block or block macro being processed. Classes using this
|
109
|
+
# mixin must implement this method.
|
110
|
+
#
|
111
|
+
# @param parent [Asciidoctor::AbstractBlock] the parent asciidoc block of the block or block macro being processed
|
112
|
+
# @param reader_or_target [Asciidoctor::Reader, String] a reader that provides the contents of a block or the
|
113
|
+
# target value of a block macro
|
114
|
+
# @param attributes [Hash] the attributes of the block or block macro
|
115
|
+
#
|
116
|
+
# @return [DiagramSource] an object that implements the interface described by DiagramSource
|
117
|
+
#
|
118
|
+
# @abstract
|
119
|
+
def create_source(parent, reader_or_target, attributes)
|
120
|
+
raise NotImplementedError.new
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
def create_image_block(parent, source, attributes, format, generator_info)
|
125
|
+
image_name = "#{source.image_name}.#{format}"
|
126
|
+
outdir = parent.document.attr('imagesoutdir') || parent.document.attr('outdir')
|
127
|
+
image_dir = parent.normalize_system_path parent.document.attr 'imagesdir', outdir
|
128
|
+
image_file = parent.normalize_system_path image_name, image_dir
|
129
|
+
metadata_file = parent.normalize_system_path "#{image_name}.cache", image_dir
|
130
|
+
|
131
|
+
if File.exists? metadata_file
|
132
|
+
metadata = File.open(metadata_file, 'r') { |f| JSON.load f }
|
133
|
+
else
|
134
|
+
metadata = {}
|
135
|
+
end
|
136
|
+
|
137
|
+
if !File.exists?(image_file) || source.should_process?(image_file, metadata)
|
138
|
+
params = IMAGE_PARAMS[format]
|
139
|
+
|
140
|
+
result = instance_exec(source, parent, &generator_info[:generator])
|
141
|
+
|
142
|
+
result.force_encoding(params[:encoding])
|
143
|
+
|
144
|
+
metadata = source.create_image_metadata
|
145
|
+
metadata['width'], metadata['height'] = params[:decoder].get_image_size(result)
|
146
|
+
|
147
|
+
FileUtils.mkdir_p(image_dir) unless Dir.exists?(image_dir)
|
148
|
+
File.open(image_file, 'wb') { |f| f.write result }
|
149
|
+
File.open(metadata_file, 'w') { |f| JSON.dump(metadata, f) }
|
150
|
+
end
|
151
|
+
|
152
|
+
image_attributes = attributes.dup
|
153
|
+
image_attributes['target'] = image_name
|
154
|
+
if /html/i =~ parent.document.attributes['backend']
|
155
|
+
image_attributes['width'] ||= metadata['width'] if metadata['width']
|
156
|
+
image_attributes['height'] ||= metadata['height'] if metadata['height']
|
157
|
+
end
|
158
|
+
image_attributes['alt'] ||= if title_text = attributes['title']
|
159
|
+
title_text
|
160
|
+
elsif target = attributes['target']
|
161
|
+
(File.basename target, (File.extname target) || '').tr '_-', ' '
|
162
|
+
else
|
163
|
+
'Diagram'
|
164
|
+
end
|
165
|
+
|
166
|
+
Asciidoctor::Block.new parent, :image, :content_model => :empty, :attributes => image_attributes
|
167
|
+
end
|
168
|
+
|
169
|
+
def create_literal_block(parent, source, attributes, generator_info)
|
170
|
+
literal_attributes = attributes.dup
|
171
|
+
literal_attributes.delete('target')
|
172
|
+
|
173
|
+
result = instance_exec(source, parent, &generator_info[:generator])
|
174
|
+
|
175
|
+
result.force_encoding(Encoding::UTF_8)
|
176
|
+
Asciidoctor::Block.new parent, :literal, :source => result, :attributes => literal_attributes
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# Base class for diagram block processors.
|
181
|
+
class DiagramBlockProcessor < Asciidoctor::Extensions::BlockProcessor
|
182
|
+
include DiagramProcessor
|
183
|
+
|
184
|
+
def self.inherited(subclass)
|
185
|
+
subclass.option :pos_attrs, ['target', 'format']
|
186
|
+
subclass.option :contexts, [:listing, :literal, :open]
|
187
|
+
subclass.option :content_model, :simple
|
188
|
+
end
|
189
|
+
|
190
|
+
# Creates a ReaderSource from the given reader.
|
191
|
+
#
|
192
|
+
# @return [ReaderSource] a ReaderSource
|
193
|
+
def create_source(parent, reader, attributes)
|
194
|
+
ReaderSource.new(reader, attributes)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# Base class for diagram block macro processors.
|
199
|
+
class DiagramBlockMacroProcessor < Asciidoctor::Extensions::BlockMacroProcessor
|
200
|
+
include DiagramProcessor
|
201
|
+
|
202
|
+
def self.inherited(subclass)
|
203
|
+
subclass.option :pos_attrs, ['target', 'format']
|
204
|
+
end
|
205
|
+
|
206
|
+
# Creates a FileSource using target as the file name.
|
207
|
+
#
|
208
|
+
# @return [FileSource] a FileSource
|
209
|
+
def create_source(parent, target, attributes)
|
210
|
+
FileSource.new(File.expand_path(target, parent.document.attributes['docdir']), attributes)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# This module describes the duck-typed interface that diagram sources must implement. Implementations
|
215
|
+
# may include this module but it is not required.
|
216
|
+
module DiagramSource
|
217
|
+
# @return [String] the base name for the image file that will be produced
|
218
|
+
# @abstract
|
219
|
+
def image_name
|
220
|
+
raise NotImplementedError.new
|
221
|
+
end
|
222
|
+
|
223
|
+
# @return [String] the String representation of the source code for the diagram
|
224
|
+
# @abstract
|
225
|
+
def code
|
226
|
+
raise NotImplementedError.new
|
227
|
+
end
|
228
|
+
|
229
|
+
# Alias for code
|
230
|
+
def to_s
|
231
|
+
code
|
232
|
+
end
|
233
|
+
|
234
|
+
# Determines if the diagram should be regenerated or not. The default implementation of this method simply
|
235
|
+
# returns true.
|
236
|
+
#
|
237
|
+
# @param image_file [String] the path to the previously generated version of the image
|
238
|
+
# @param image_metadata [Hash] the image metadata Hash that was stored during the previous diagram generation pass
|
239
|
+
# @return [Boolean] true if the diagram should be regenerated; false otherwise
|
240
|
+
def should_process?(image_file, image_metadata)
|
241
|
+
true
|
242
|
+
end
|
243
|
+
|
244
|
+
# Creates an image metadata Hash that will be stored to disk alongside the generated image file. The contents
|
245
|
+
# of this Hash are reread during subsequent document processing and then passed to the should_process? method
|
246
|
+
# where it can be used to determine if the diagram should be regenerated or not.
|
247
|
+
# The default implementation returns an empty Hash.
|
248
|
+
# @return [Hash] a Hash containing metadata
|
249
|
+
def create_image_metadata
|
250
|
+
{}
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
# Base class for diagram source implementations that uses an md5 checksum of the source code of a diagram to
|
255
|
+
# determine if it has been updated or not.
|
256
|
+
class BasicSource
|
257
|
+
include DiagramSource
|
258
|
+
|
259
|
+
def initialize(attributes)
|
260
|
+
@attributes = attributes
|
261
|
+
end
|
262
|
+
|
263
|
+
def image_name
|
264
|
+
@attributes['target'] || ('diag-' + checksum)
|
265
|
+
end
|
266
|
+
|
267
|
+
def should_process?(image_file, image_metadata)
|
268
|
+
image_metadata['checksum'] != checksum
|
269
|
+
end
|
270
|
+
|
271
|
+
def create_image_metadata
|
272
|
+
{'checksum' => checksum}
|
273
|
+
end
|
274
|
+
|
275
|
+
def checksum
|
276
|
+
@checksum ||= compute_checksum(code)
|
277
|
+
end
|
278
|
+
|
279
|
+
def compute_checksum(code)
|
280
|
+
md5 = Digest::MD5.new
|
281
|
+
md5 << code
|
282
|
+
md5.hexdigest
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
# A diagram source that retrieves the code for the diagram from the contents of a block.
|
287
|
+
class ReaderSource < BasicSource
|
288
|
+
include DiagramSource
|
289
|
+
|
290
|
+
def initialize(reader, attributes)
|
291
|
+
super(attributes)
|
292
|
+
@reader = reader
|
293
|
+
end
|
294
|
+
|
295
|
+
def code
|
296
|
+
@code ||= @reader.lines.join("\n")
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
# A diagram source that retrieves the code for a diagram from an external source file.
|
301
|
+
class FileSource < BasicSource
|
302
|
+
def initialize(file_name, attributes)
|
303
|
+
super(attributes)
|
304
|
+
@file_name = file_name
|
305
|
+
end
|
306
|
+
|
307
|
+
def image_name
|
308
|
+
if @attributes['target']
|
309
|
+
super
|
310
|
+
else
|
311
|
+
File.basename(@file_name, File.extname(@file_name))
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
def should_process?(image_file, image_metadata)
|
316
|
+
File.mtime(@file_name) > File.mtime(image_file) || super
|
317
|
+
end
|
318
|
+
|
319
|
+
def code
|
320
|
+
@code ||= File.read(@file_name)
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
@@ -3,6 +3,6 @@ require_relative 'version'
|
|
3
3
|
|
4
4
|
Asciidoctor::Extensions.register do
|
5
5
|
require_relative 'graphviz/extension'
|
6
|
-
block Asciidoctor::Diagram::
|
7
|
-
block_macro Asciidoctor::Diagram::
|
6
|
+
block Asciidoctor::Diagram::GraphvizBlockProcessor, :graphviz
|
7
|
+
block_macro Asciidoctor::Diagram::GraphvizBlockMacroProcessor, :graphviz
|
8
8
|
end
|
@@ -1,16 +1,27 @@
|
|
1
|
+
require_relative '../extensions'
|
1
2
|
require_relative '../util/cli_generator'
|
2
|
-
require_relative '../util/diagram'
|
3
3
|
|
4
4
|
module Asciidoctor
|
5
5
|
module Diagram
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
# @private
|
7
|
+
module Graphviz
|
8
|
+
def self.included(mod)
|
9
|
+
[:png, :svg].each do |f|
|
10
|
+
mod.register_format(f, :image) do |c, p|
|
11
|
+
CliGenerator.generate('dot', p, c.to_s) do |tool_path, output_path|
|
12
|
+
[tool_path, "-o#{output_path}", "-T#{f.to_s}"]
|
13
|
+
end
|
11
14
|
end
|
12
15
|
end
|
13
16
|
end
|
14
17
|
end
|
18
|
+
|
19
|
+
class GraphvizBlockProcessor < Extensions::DiagramBlockProcessor
|
20
|
+
include Graphviz
|
21
|
+
end
|
22
|
+
|
23
|
+
class GraphvizBlockMacroProcessor < Extensions::DiagramBlockMacroProcessor
|
24
|
+
include Graphviz
|
25
|
+
end
|
15
26
|
end
|
16
27
|
end
|
@@ -4,6 +4,6 @@ require_relative 'version'
|
|
4
4
|
Asciidoctor::Extensions.register do
|
5
5
|
require_relative 'plantuml/extension'
|
6
6
|
|
7
|
-
block Asciidoctor::Diagram::
|
8
|
-
block_macro Asciidoctor::Diagram::
|
7
|
+
block Asciidoctor::Diagram::PlantUmlBlockProcessor, :plantuml
|
8
|
+
block_macro Asciidoctor::Diagram::PlantUmlBlockMacroProcessor, :plantuml
|
9
9
|
end
|
@@ -1,9 +1,47 @@
|
|
1
|
-
require_relative '../
|
2
|
-
require_relative 'generator'
|
1
|
+
require_relative '../extensions'
|
3
2
|
|
4
3
|
module Asciidoctor
|
5
4
|
module Diagram
|
6
|
-
|
5
|
+
# @private
|
6
|
+
module PlantUml
|
7
|
+
private
|
8
|
+
|
9
|
+
PLANTUML_JAR_PATH = File.expand_path File.join('../..', 'plantuml.jar'), File.dirname(__FILE__)
|
10
|
+
Java.classpath << PLANTUML_JAR_PATH
|
11
|
+
|
12
|
+
def plantuml(parent, code, tag, *flags)
|
13
|
+
unless @graphvizdot
|
14
|
+
@graphvizdot = parent.document.attributes['dot']
|
15
|
+
@graphvizdot = ::Asciidoctor::Diagram.which('dot') unless @graphvizdot && File.executable?(@graphvizdot)
|
16
|
+
raise "Could not find the Graphviz 'dot' executable in PATH; add it to the PATH or specify its location using the 'dot' document attribute" unless @graphvizdot
|
17
|
+
end
|
18
|
+
|
19
|
+
Java.load
|
20
|
+
|
21
|
+
code = "@start#{tag}\n#{code}\n@end#{tag}" unless code.index "@start#{tag}"
|
22
|
+
|
23
|
+
flags += ['-charset', 'UTF-8', '-failonerror', '-graphvizdot', @graphvizdot]
|
24
|
+
|
25
|
+
config_file = parent.document.attributes['plantumlconfig']
|
26
|
+
if config_file
|
27
|
+
flags += ['-config', File.expand_path(config_file, parent.document.attributes['docdir'])]
|
28
|
+
end
|
29
|
+
|
30
|
+
option = Java.new_object( Java.net.sourceforge.plantuml.Option, '[Ljava.lang.String;', Java.array_to_java_array(flags, :string) )
|
31
|
+
source_reader = Java.new_object( Java.net.sourceforge.plantuml.SourceStringReader,
|
32
|
+
'Lnet.sourceforge.plantuml.preproc.Defines;Ljava.lang.String;Ljava.util.List;',
|
33
|
+
Java.new_object( Java.net.sourceforge.plantuml.preproc.Defines ),
|
34
|
+
code,
|
35
|
+
option.getConfig()
|
36
|
+
)
|
37
|
+
|
38
|
+
bos = Java.new_object( Java.java.io.ByteArrayOutputStream )
|
39
|
+
ps = Java.new_object( Java.java.io.PrintStream, 'Ljava.io.OutputStream;', bos )
|
40
|
+
source_reader.generateImage(ps, 0, option.getFileFormatOption())
|
41
|
+
ps.close
|
42
|
+
Java.string_from_java_bytes(bos.toByteArray)
|
43
|
+
end
|
44
|
+
|
7
45
|
def config_args(parent)
|
8
46
|
config_args = []
|
9
47
|
config = parent.document.attributes['plantumlconfig']
|
@@ -14,15 +52,25 @@ module Asciidoctor
|
|
14
52
|
config_args
|
15
53
|
end
|
16
54
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
55
|
+
def self.included(mod)
|
56
|
+
mod.register_format(:png, :image) do |c, p|
|
57
|
+
plantuml(p, c.to_s, 'uml', *config_args(p))
|
58
|
+
end
|
59
|
+
mod.register_format(:svg, :image) do |c, p|
|
60
|
+
plantuml(p, c.to_s, 'uml', '-tsvg', *config_args(p))
|
61
|
+
end
|
62
|
+
mod.register_format(:txt, :literal) do |c, p|
|
63
|
+
plantuml(p, c.to_s, 'uml', '-tutxt', *config_args(p))
|
64
|
+
end
|
25
65
|
end
|
26
66
|
end
|
67
|
+
|
68
|
+
class PlantUmlBlockProcessor < Extensions::DiagramBlockProcessor
|
69
|
+
include PlantUml
|
70
|
+
end
|
71
|
+
|
72
|
+
class PlantUmlBlockMacroProcessor < Extensions::DiagramBlockMacroProcessor
|
73
|
+
include PlantUml
|
74
|
+
end
|
27
75
|
end
|
28
76
|
end
|
@@ -4,6 +4,6 @@ require_relative 'version'
|
|
4
4
|
Asciidoctor::Extensions.register do
|
5
5
|
require_relative 'shaape/extension'
|
6
6
|
|
7
|
-
block Asciidoctor::Diagram::
|
8
|
-
block_macro Asciidoctor::Diagram::
|
7
|
+
block Asciidoctor::Diagram::ShaapeBlockProcessor, :shaape
|
8
|
+
block_macro Asciidoctor::Diagram::ShaapeBlockMacroProcessor, :shaape
|
9
9
|
end
|
@@ -1,16 +1,27 @@
|
|
1
|
+
require_relative '../extensions'
|
1
2
|
require_relative '../util/cli_generator'
|
2
|
-
require_relative '../util/diagram'
|
3
3
|
|
4
4
|
module Asciidoctor
|
5
5
|
module Diagram
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
# @private
|
7
|
+
module Shaape
|
8
|
+
def self.included(mod)
|
9
|
+
[:png, :svg].each do |f|
|
10
|
+
mod.register_format(f, :image) do |c, p|
|
11
|
+
CliGenerator.generate('shaape', p, c.to_s) do |tool_path, output_path|
|
12
|
+
[tool_path, '-o', output_path, '-t', f.to_s, '-']
|
13
|
+
end
|
11
14
|
end
|
12
15
|
end
|
13
16
|
end
|
14
17
|
end
|
18
|
+
|
19
|
+
class ShaapeBlockProcessor < Extensions::DiagramBlockProcessor
|
20
|
+
include Shaape
|
21
|
+
end
|
22
|
+
|
23
|
+
class ShaapeBlockMacroProcessor < Extensions::DiagramBlockMacroProcessor
|
24
|
+
include Shaape
|
25
|
+
end
|
15
26
|
end
|
16
27
|
end
|
@@ -5,6 +5,7 @@ require_relative '../util/which'
|
|
5
5
|
|
6
6
|
module Asciidoctor
|
7
7
|
module Diagram
|
8
|
+
# @private
|
8
9
|
module CliGenerator
|
9
10
|
def self.generate(tool, parent, code)
|
10
11
|
tool_var = '@' + tool
|
@@ -13,7 +14,7 @@ module Asciidoctor
|
|
13
14
|
unless tool_path
|
14
15
|
tool_path = parent.document.attributes[tool]
|
15
16
|
tool_path = ::Asciidoctor::Diagram.which(tool) unless tool_path && File.executable?(tool_path)
|
16
|
-
raise "Could not find the '#{tool}' executable in PATH; add it to the PATH or specify its location using the '
|
17
|
+
raise "Could not find the '#{tool}' executable in PATH; add it to the PATH or specify its location using the '#{tool}' document attribute" unless tool_path
|
17
18
|
instance_variable_set(tool_var, tool_path)
|
18
19
|
end
|
19
20
|
|
data/lib/plantuml.jar
CHANGED
Binary file
|
data/spec/blockdiag_spec.rb
CHANGED
@@ -7,7 +7,7 @@ blockdiag {
|
|
7
7
|
}
|
8
8
|
eos
|
9
9
|
|
10
|
-
describe Asciidoctor::Diagram::
|
10
|
+
describe Asciidoctor::Diagram::BlockDiagBlockMacroProcessor do
|
11
11
|
it "should generate PNG images when format is set to 'png'" do
|
12
12
|
File.write('blockdiag.txt', code)
|
13
13
|
|
@@ -38,7 +38,7 @@ blockdiag::blockdiag.txt[format="png"]
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
-
describe Asciidoctor::Diagram::
|
41
|
+
describe Asciidoctor::Diagram::BlockDiagBlockProcessor do
|
42
42
|
it "should generate PNG images when format is set to 'png'" do
|
43
43
|
doc = <<-eos
|
44
44
|
= Hello, BlockDiag!
|
data/spec/ditaa_spec.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require_relative 'test_helper'
|
2
2
|
|
3
|
-
describe Asciidoctor::Diagram::
|
3
|
+
describe Asciidoctor::Diagram::DitaaBlockMacroProcessor do
|
4
4
|
it "should generate PNG images when format is set to 'png'" do
|
5
5
|
code = <<-eos
|
6
6
|
+--------+ +-------+ +-------+
|
@@ -43,7 +43,7 @@ ditaa::ditaa.txt[format="png"]
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
describe Asciidoctor::Diagram::
|
46
|
+
describe Asciidoctor::Diagram::DitaaBlockProcessor do
|
47
47
|
it "should generate PNG images when format is set to 'png'" do
|
48
48
|
doc = <<-eos
|
49
49
|
= Hello, ditaa!
|
data/spec/graphviz_spec.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require_relative 'test_helper'
|
2
2
|
|
3
|
-
describe Asciidoctor::Diagram::
|
3
|
+
describe Asciidoctor::Diagram::GraphvizBlockMacroProcessor do
|
4
4
|
it "should generate PNG images when format is set to 'png'" do
|
5
5
|
code = <<-eos
|
6
6
|
digraph foo {
|
@@ -42,7 +42,7 @@ graphviz::graphviz.txt[format="png"]
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
-
describe Asciidoctor::Diagram::
|
45
|
+
describe Asciidoctor::Diagram::GraphvizBlockProcessor do
|
46
46
|
it "should generate PNG images when format is set to 'png'" do
|
47
47
|
doc = <<-eos
|
48
48
|
= Hello, graphviz!
|
data/spec/plantuml_spec.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require_relative 'test_helper'
|
2
2
|
|
3
|
-
describe Asciidoctor::Diagram::
|
3
|
+
describe Asciidoctor::Diagram::PlantUmlBlockMacroProcessor do
|
4
4
|
it "should generate PNG images when format is set to 'png'" do
|
5
5
|
code = <<-eos
|
6
6
|
User -> (Start)
|
@@ -38,7 +38,7 @@ plantuml::plantuml.txt[format="png"]
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
-
describe Asciidoctor::Diagram::
|
41
|
+
describe Asciidoctor::Diagram::PlantUmlBlockProcessor do
|
42
42
|
it "should generate PNG images when format is set to 'png'" do
|
43
43
|
doc = <<-eos
|
44
44
|
= Hello, PlantUML!
|
data/spec/shaape_spec.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require_relative 'test_helper'
|
2
2
|
|
3
|
-
describe Asciidoctor::Diagram::
|
3
|
+
describe Asciidoctor::Diagram::ShaapeBlockMacroProcessor do
|
4
4
|
it "should generate PNG images when format is set to 'png'" do
|
5
5
|
code = <<-eos
|
6
6
|
+--------+ +-------------+
|
@@ -40,7 +40,7 @@ shaape::shaape.txt[format="png"]
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
describe Asciidoctor::Diagram::
|
43
|
+
describe Asciidoctor::Diagram::ShaapeBlockProcessor do
|
44
44
|
it "should generate PNG images when format is set to 'png'" do
|
45
45
|
doc = <<-eos
|
46
46
|
= Hello, Shaape!
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: asciidoctor-diagram
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0.preview.1
|
5
5
|
platform: java
|
6
6
|
authors:
|
7
7
|
- Pepijn Van Eeckhoudt
|
@@ -86,16 +86,15 @@ files:
|
|
86
86
|
- lib/asciidoctor-diagram/blockdiag/extension.rb
|
87
87
|
- lib/asciidoctor-diagram/ditaa.rb
|
88
88
|
- lib/asciidoctor-diagram/ditaa/extension.rb
|
89
|
+
- lib/asciidoctor-diagram/extensions.rb
|
89
90
|
- lib/asciidoctor-diagram/graphviz.rb
|
90
91
|
- lib/asciidoctor-diagram/graphviz/extension.rb
|
91
92
|
- lib/asciidoctor-diagram/plantuml.rb
|
92
93
|
- lib/asciidoctor-diagram/plantuml/extension.rb
|
93
|
-
- lib/asciidoctor-diagram/plantuml/generator.rb
|
94
94
|
- lib/asciidoctor-diagram/shaape.rb
|
95
95
|
- lib/asciidoctor-diagram/shaape/extension.rb
|
96
96
|
- lib/asciidoctor-diagram/util/binaryio.rb
|
97
97
|
- lib/asciidoctor-diagram/util/cli_generator.rb
|
98
|
-
- lib/asciidoctor-diagram/util/diagram.rb
|
99
98
|
- lib/asciidoctor-diagram/util/java.rb
|
100
99
|
- lib/asciidoctor-diagram/util/java_jruby.rb
|
101
100
|
- lib/asciidoctor-diagram/util/java_rjb.rb
|
@@ -126,9 +125,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
126
125
|
version: '0'
|
127
126
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
127
|
requirements:
|
129
|
-
- - '
|
128
|
+
- - '>'
|
130
129
|
- !ruby/object:Gem::Version
|
131
|
-
version:
|
130
|
+
version: 1.3.1
|
132
131
|
requirements: []
|
133
132
|
rubyforge_project:
|
134
133
|
rubygems_version: 2.0.14
|
@@ -1,46 +0,0 @@
|
|
1
|
-
require_relative '../util/java'
|
2
|
-
require_relative '../util/which'
|
3
|
-
|
4
|
-
module Asciidoctor
|
5
|
-
module Diagram
|
6
|
-
module PlantUmlGenerator
|
7
|
-
private
|
8
|
-
|
9
|
-
PLANTUML_JAR_PATH = File.expand_path File.join('../..', 'plantuml.jar'), File.dirname(__FILE__)
|
10
|
-
Java.classpath << PLANTUML_JAR_PATH
|
11
|
-
|
12
|
-
def self.plantuml(parent, code, tag, *flags)
|
13
|
-
unless @graphvizdot
|
14
|
-
@graphvizdot = parent.document.attributes['graphvizdot']
|
15
|
-
@graphvizdot = ::Asciidoctor::Diagram.which('dot') unless @graphvizdot && File.executable?(@graphvizdot)
|
16
|
-
raise "Could not find the Graphviz 'dot' executable in PATH; add it to the PATH or specify its location using the 'graphvizdot' document attribute" unless @graphvizdot
|
17
|
-
end
|
18
|
-
|
19
|
-
Java.load
|
20
|
-
|
21
|
-
code = "@start#{tag}\n#{code}\n@end#{tag}" unless code.index "@start#{tag}"
|
22
|
-
|
23
|
-
flags += ['-charset', 'UTF-8', '-failonerror', '-graphvizdot', @graphvizdot]
|
24
|
-
|
25
|
-
config_file = parent.document.attributes['plantumlconfig']
|
26
|
-
if config_file
|
27
|
-
flags += ['-config', File.expand_path(config_file, parent.document.attributes['docdir'])]
|
28
|
-
end
|
29
|
-
|
30
|
-
option = Java.new_object( Java.net.sourceforge.plantuml.Option, '[Ljava.lang.String;', Java.array_to_java_array(flags, :string) )
|
31
|
-
source_reader = Java.new_object( Java.net.sourceforge.plantuml.SourceStringReader,
|
32
|
-
'Lnet.sourceforge.plantuml.preproc.Defines;Ljava.lang.String;Ljava.util.List;',
|
33
|
-
Java.new_object( Java.net.sourceforge.plantuml.preproc.Defines ),
|
34
|
-
code,
|
35
|
-
option.getConfig()
|
36
|
-
)
|
37
|
-
|
38
|
-
bos = Java.new_object( Java.java.io.ByteArrayOutputStream )
|
39
|
-
ps = Java.new_object( Java.java.io.PrintStream, 'Ljava.io.OutputStream;', bos )
|
40
|
-
source_reader.generateImage(ps, 0, option.getFileFormatOption())
|
41
|
-
ps.close
|
42
|
-
Java.string_from_java_bytes(bos.toByteArray)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
@@ -1,220 +0,0 @@
|
|
1
|
-
require 'asciidoctor/extensions'
|
2
|
-
require 'digest'
|
3
|
-
require 'json'
|
4
|
-
require 'fileutils'
|
5
|
-
require_relative 'java'
|
6
|
-
require_relative 'png'
|
7
|
-
require_relative 'svg'
|
8
|
-
|
9
|
-
module Asciidoctor
|
10
|
-
module Diagram
|
11
|
-
def self.define_processors(name, &init)
|
12
|
-
block = Class.new(Asciidoctor::Extensions::BlockProcessor) do
|
13
|
-
class << self
|
14
|
-
include FormatRegistry
|
15
|
-
end
|
16
|
-
include DiagramProcessor
|
17
|
-
|
18
|
-
option :pos_attrs, ['target', 'format']
|
19
|
-
option :contexts, [:listing, :literal, :open]
|
20
|
-
option :content_model, :simple
|
21
|
-
|
22
|
-
def process(parent, reader, attributes)
|
23
|
-
generate_block(parent, ReaderSource.new(reader), attributes)
|
24
|
-
end
|
25
|
-
|
26
|
-
self.instance_eval &init
|
27
|
-
end
|
28
|
-
|
29
|
-
block_macro = Class.new(Asciidoctor::Extensions::BlockMacroProcessor) do
|
30
|
-
class << self
|
31
|
-
include FormatRegistry
|
32
|
-
end
|
33
|
-
include DiagramProcessor
|
34
|
-
|
35
|
-
option :pos_attrs, ['target', 'format']
|
36
|
-
|
37
|
-
def process(parent, target, attributes)
|
38
|
-
source = FileSource.new(File.expand_path(target, parent.document.attributes['docdir']))
|
39
|
-
attributes['target'] ||= File.basename(target, File.extname(target))
|
40
|
-
|
41
|
-
generate_block(parent, source, attributes)
|
42
|
-
end
|
43
|
-
|
44
|
-
self.instance_eval &init
|
45
|
-
end
|
46
|
-
|
47
|
-
Asciidoctor::Diagram.const_set("#{name}Block", block)
|
48
|
-
Asciidoctor::Diagram.const_set("#{name}BlockMacro", block_macro)
|
49
|
-
end
|
50
|
-
|
51
|
-
module FormatRegistry
|
52
|
-
#
|
53
|
-
# Registers a supported format. The first registered format becomes the default format for the block processor.
|
54
|
-
#
|
55
|
-
# +format+ is a symbol with the format name
|
56
|
-
# +type+ is a symbol and should be either :image or :literal
|
57
|
-
# +block+ is a block that produces the diagrams from code. The block receives the parent asciidoc block and the diagram code as arguments
|
58
|
-
#
|
59
|
-
def register_format(format, type, &block)
|
60
|
-
unless @default_format
|
61
|
-
@default_format = format
|
62
|
-
end
|
63
|
-
|
64
|
-
formats[format] = {
|
65
|
-
:type => type,
|
66
|
-
:generator => block
|
67
|
-
}
|
68
|
-
end
|
69
|
-
|
70
|
-
def formats
|
71
|
-
@formats ||= {}
|
72
|
-
end
|
73
|
-
|
74
|
-
def default_format
|
75
|
-
@default_format
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
module DiagramProcessor
|
80
|
-
IMAGE_PARAMS = {
|
81
|
-
:svg => {
|
82
|
-
:encoding => Encoding::UTF_8,
|
83
|
-
:decoder => SVG
|
84
|
-
},
|
85
|
-
:png => {
|
86
|
-
:encoding => Encoding::ASCII_8BIT,
|
87
|
-
:decoder => PNG
|
88
|
-
}
|
89
|
-
}
|
90
|
-
|
91
|
-
private
|
92
|
-
|
93
|
-
def generate_block(parent, source, attributes)
|
94
|
-
format = attributes.delete('format') || self.class.default_format
|
95
|
-
format = format.to_sym if format.respond_to?(:to_sym)
|
96
|
-
|
97
|
-
raise "Format undefined" unless format
|
98
|
-
|
99
|
-
generator_info = self.class.formats[format]
|
100
|
-
|
101
|
-
raise "#{self.class.name} does not support output format #{format}" unless generator_info
|
102
|
-
|
103
|
-
case generator_info[:type]
|
104
|
-
when :image
|
105
|
-
create_image_block(parent, source, attributes, format, generator_info)
|
106
|
-
when :literal
|
107
|
-
create_literal_block(parent, source, attributes, generator_info)
|
108
|
-
else
|
109
|
-
raise "Unsupported output format: #{format}"
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
def create_image_block(parent, source, attributes, format, generator_info)
|
114
|
-
target = attributes.delete('target')
|
115
|
-
|
116
|
-
image_name = "#{target || ('diag-' + source.checksum)}.#{format}"
|
117
|
-
image_dir = File.expand_path(parent.document.attributes['imagesdir'] || '', parent.document.attributes['outdir'] || parent.document.attributes['docdir'])
|
118
|
-
image_file = File.expand_path(image_name, image_dir)
|
119
|
-
metadata_file = File.expand_path("#{image_name}.cache", image_dir)
|
120
|
-
|
121
|
-
if File.exists? metadata_file
|
122
|
-
metadata = File.open(metadata_file, 'r') { |f| JSON.load f }
|
123
|
-
else
|
124
|
-
metadata = {}
|
125
|
-
end
|
126
|
-
|
127
|
-
if source.should_process?(image_file, metadata['checksum'])
|
128
|
-
params = IMAGE_PARAMS[format]
|
129
|
-
|
130
|
-
result = generator_info[:generator].call(source.code, parent)
|
131
|
-
|
132
|
-
result.force_encoding(params[:encoding])
|
133
|
-
|
134
|
-
metadata = {'checksum' => source.checksum}
|
135
|
-
metadata['width'], metadata['height'] = params[:decoder].get_image_size(result)
|
136
|
-
|
137
|
-
FileUtils.mkdir_p(image_dir) unless Dir.exists?(image_dir)
|
138
|
-
File.open(image_file, 'wb') { |f| f.write result }
|
139
|
-
File.open(metadata_file, 'w') { |f| JSON.dump(metadata, f) }
|
140
|
-
end
|
141
|
-
|
142
|
-
attributes['target'] = image_name
|
143
|
-
if /html/i =~ parent.document.attributes['backend']
|
144
|
-
attributes['width'] ||= metadata['width'] if metadata['width']
|
145
|
-
attributes['height'] ||= metadata['height'] if metadata['height']
|
146
|
-
end
|
147
|
-
attributes['alt'] ||= if title_text = attributes['title']
|
148
|
-
title_text
|
149
|
-
elsif target
|
150
|
-
(File.basename target, (File.extname target) || '').tr '_-', ' '
|
151
|
-
else
|
152
|
-
'Diagram'
|
153
|
-
end
|
154
|
-
|
155
|
-
Asciidoctor::Block.new parent, :image, :content_model => :empty, :attributes => attributes
|
156
|
-
end
|
157
|
-
|
158
|
-
def create_literal_block(parent, source, attributes, generator_info)
|
159
|
-
attributes.delete('target')
|
160
|
-
|
161
|
-
result = generator_info[:generator].call(source.code, parent)
|
162
|
-
|
163
|
-
result.force_encoding(Encoding::UTF_8)
|
164
|
-
Asciidoctor::Block.new parent, :literal, :code => result, :attributes => attributes
|
165
|
-
end
|
166
|
-
|
167
|
-
def code_checksum(code)
|
168
|
-
md5 = Digest::MD5.new
|
169
|
-
md5 << code
|
170
|
-
md5.hexdigest
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
class Source
|
175
|
-
def checksum
|
176
|
-
@checksum ||= compute_checksum(code)
|
177
|
-
end
|
178
|
-
|
179
|
-
def should_process?(image_file, old_checksum)
|
180
|
-
!File.exists?(image_file) || (newer_than?(image_file) && old_checksum != checksum)
|
181
|
-
end
|
182
|
-
|
183
|
-
private
|
184
|
-
|
185
|
-
def compute_checksum(code)
|
186
|
-
md5 = Digest::MD5.new
|
187
|
-
md5 << code
|
188
|
-
md5.hexdigest
|
189
|
-
end
|
190
|
-
end
|
191
|
-
|
192
|
-
class ReaderSource < Source
|
193
|
-
def initialize(reader)
|
194
|
-
@reader = reader
|
195
|
-
end
|
196
|
-
|
197
|
-
def newer_than?(image_file)
|
198
|
-
true
|
199
|
-
end
|
200
|
-
|
201
|
-
def code
|
202
|
-
@code ||= @reader.lines.join("\n")
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
class FileSource < Source
|
207
|
-
def initialize(file_name)
|
208
|
-
@file_name = file_name
|
209
|
-
end
|
210
|
-
|
211
|
-
def newer_than?(image_file)
|
212
|
-
File.mtime(@file_name) > File.mtime(image_file)
|
213
|
-
end
|
214
|
-
|
215
|
-
def code
|
216
|
-
@code ||= File.read(@file_name)
|
217
|
-
end
|
218
|
-
end
|
219
|
-
end
|
220
|
-
end
|