asciidoctor-diagram 1.2.1-java → 1.3.0.preview.1-java

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1bd129d7f1a3bcb983c175ff81f7278736e8f84f
4
- data.tar.gz: fbd0f23226b3dd0df2b648e7b69a3c4721a3a4e0
3
+ metadata.gz: e1cbd019a0247c43f55f350f3897a689b3af5d83
4
+ data.tar.gz: 3d85e72167829121d471faa6f20586cc2eb1eab4
5
5
  SHA512:
6
- metadata.gz: c6b8951de8298832807be2187d14894194d92a0f61467dbc5d53ec2d9e6f2f7dd1f13bb00d5c63e66f82dc5a09eee65710f4619cb3acc93e9be97ad07330c2d0
7
- data.tar.gz: 3431ef61b193d3029a657bdec8955c9062ab7cf25f8c49533d40ca3006e338e90a85605d80177563ecaae0140c503791aac9f533b9a393bd2732a860d443ebbb
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.2.1
3
+ == 1.3.0
4
4
 
5
5
  Enhancements::
6
6
 
7
- * Add support for Actdiag, Seqdiag, Nwdiag, Packetdiag and Rackdiag diagrams (requires resp. Python packages to be installed separately)
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/07U/2014)
20
- * Add support for Shaape diagrams (requires Shaape Python package to be installed separately)
21
- * Add support for Blockdiag diagrams (requires Blockdiag Python package to be installed separately)
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 written using the http://plantuml.sourceforge.net[PlantUML] or Ditaa syntax inside your AsciiDoc documents.
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 support for `ditaa`, `graphviz` and `plantuml` blocks becomes available for your documents.
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 supports `png` and `svg`. Ditaa only supports `png`.
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
- name = tool.downcase.to_sym
9
- block = Asciidoctor::Diagram.const_get("#{tool}Block")
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::DitaaBlock, :ditaa
7
- block_macro Asciidoctor::Diagram::DitaaBlockMacro, :ditaa
6
+ block Asciidoctor::Diagram::DitaaBlockProcessor, :ditaa
7
+ block_macro Asciidoctor::Diagram::DitaaBlockMacroProcessor, :ditaa
8
8
  end
@@ -1,13 +1,14 @@
1
- require_relative '../util/diagram'
1
+ require_relative '../extensions'
2
2
  require_relative '../util/java'
3
3
 
4
4
  module Asciidoctor
5
5
  module Diagram
6
- module DitaaGenerator
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 self.ditaa(code)
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
- define_processors('Ditaa') do
31
- register_format(:png, :image) do |c|
32
- DitaaGenerator.ditaa(c)
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::GraphvizBlock, :graphviz
7
- block_macro Asciidoctor::Diagram::GraphvizBlockMacro, :graphviz
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
- define_processors('Graphviz') do
7
- [:png, :svg].each do |f|
8
- register_format(f, :image) do |c, p|
9
- CliGenerator.generate('dot', p, c) do |tool_path, output_path|
10
- [tool_path, "-o#{output_path}", "-T#{f.to_s}"]
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::PlantUmlBlock, :plantuml
8
- block_macro Asciidoctor::Diagram::PlantUmlBlockMacro, :plantuml
7
+ block Asciidoctor::Diagram::PlantUmlBlockProcessor, :plantuml
8
+ block_macro Asciidoctor::Diagram::PlantUmlBlockMacroProcessor, :plantuml
9
9
  end
@@ -1,9 +1,47 @@
1
- require_relative '../util/diagram'
2
- require_relative 'generator'
1
+ require_relative '../extensions'
3
2
 
4
3
  module Asciidoctor
5
4
  module Diagram
6
- define_processors('PlantUml') do
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
- register_format(:png, :image) do |c, p|
18
- PlantUmlGenerator.plantuml(p, c, 'uml', *config_args(p))
19
- end
20
- register_format(:svg, :image) do |c, p|
21
- PlantUmlGenerator.plantuml(p, c, 'uml', '-tsvg', *config_args(p))
22
- end
23
- register_format(:txt, :literal) do |c, p|
24
- PlantUmlGenerator.plantuml(p, c, 'uml', '-tutxt', *config_args(p))
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::ShaapeBlock, :shaape
8
- block_macro Asciidoctor::Diagram::ShaapeBlockMacro, :shaape
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
- define_processors('Shaape') do
7
- [:png, :svg].each do |f|
8
- register_format(f, :image) do |c, p|
9
- CliGenerator.generate('shaape', p, c) do |tool_path, output_path|
10
- [tool_path, '-o', output_path, '-t', f.to_s, '-']
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
@@ -1,5 +1,7 @@
1
1
  module Asciidoctor
2
2
  module Diagram
3
+ # Helper class to read binary values from a String
4
+ # @private
3
5
  class BinaryIO
4
6
  def initialize(string)
5
7
  @data = string
@@ -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 'shaape' document attribute" unless tool_path
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
 
@@ -2,6 +2,7 @@ require 'java'
2
2
 
3
3
  module Asciidoctor
4
4
  module Diagram
5
+ # @private
5
6
  module Java
6
7
  def self.classpath
7
8
  @classpath ||= []
@@ -2,6 +2,7 @@ require 'rjb'
2
2
 
3
3
  module Asciidoctor
4
4
  module Diagram
5
+ # @private
5
6
  module Java
6
7
  INITAWT_JAR_PATH = File.expand_path File.join('..', 'initawt.jar'), File.dirname(__FILE__)
7
8
 
@@ -2,6 +2,7 @@ require_relative 'binaryio'
2
2
 
3
3
  module Asciidoctor
4
4
  module Diagram
5
+ # @private
5
6
  module PNG
6
7
  PNG_SIGNATURE = [137, 80, 78, 71, 13, 10, 26, 10].pack('CCCCCCCC')
7
8
 
@@ -2,6 +2,7 @@ require_relative 'binaryio'
2
2
 
3
3
  module Asciidoctor
4
4
  module Diagram
5
+ # @private
5
6
  module SVG
6
7
  def self.get_image_size(data)
7
8
  if m = START_TAG_REGEX.match(data)
@@ -1,5 +1,6 @@
1
1
  module Asciidoctor
2
2
  module Diagram
3
+ # @private
3
4
  def self.which(cmd)
4
5
  exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
5
6
  ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
@@ -1,5 +1,5 @@
1
1
  module Asciidoctor
2
2
  module Diagram
3
- VERSION = "1.2.1"
3
+ VERSION = "1.3.0.preview.1"
4
4
  end
5
5
  end
data/lib/plantuml.jar CHANGED
Binary file
@@ -7,7 +7,7 @@ blockdiag {
7
7
  }
8
8
  eos
9
9
 
10
- describe Asciidoctor::Diagram::BlockDiagBlockMacro do
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::BlockDiagBlock do
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::DitaaBlockMacro do
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::DitaaBlock do
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!
@@ -1,6 +1,6 @@
1
1
  require_relative 'test_helper'
2
2
 
3
- describe Asciidoctor::Diagram::GraphvizBlockMacro do
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::GraphvizBlock do
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!
@@ -1,6 +1,6 @@
1
1
  require_relative 'test_helper'
2
2
 
3
- describe Asciidoctor::Diagram::PlantUmlBlockMacro do
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::PlantUmlBlock do
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::ShaapeBlockMacro do
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::ShaapeBlock do
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.2.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: '0'
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