asciidoctor-diagram 1.5.19 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.adoc +10 -0
  3. data/README.adoc +22 -9
  4. data/examples/features.adoc +2 -2
  5. data/lib/asciidoctor-diagram.rb +1 -0
  6. data/lib/asciidoctor-diagram/a2s/converter.rb +55 -0
  7. data/lib/asciidoctor-diagram/a2s/extension.rb +6 -52
  8. data/lib/asciidoctor-diagram/blockdiag/converter.rb +37 -0
  9. data/lib/asciidoctor-diagram/blockdiag/extension.rb +9 -116
  10. data/lib/asciidoctor-diagram/bpmn.rb +7 -0
  11. data/lib/asciidoctor-diagram/bpmn/converter.rb +62 -0
  12. data/lib/asciidoctor-diagram/bpmn/extension.rb +14 -0
  13. data/lib/asciidoctor-diagram/diagram_converter.rb +19 -0
  14. data/lib/asciidoctor-diagram/diagram_processor.rb +320 -0
  15. data/lib/asciidoctor-diagram/diagram_source.rb +275 -0
  16. data/lib/asciidoctor-diagram/ditaa/converter.rb +86 -0
  17. data/lib/asciidoctor-diagram/ditaa/extension.rb +6 -71
  18. data/lib/asciidoctor-diagram/erd/converter.rb +31 -0
  19. data/lib/asciidoctor-diagram/erd/extension.rb +6 -35
  20. data/lib/asciidoctor-diagram/gnuplot/converter.rb +63 -0
  21. data/lib/asciidoctor-diagram/gnuplot/extension.rb +6 -62
  22. data/lib/asciidoctor-diagram/graphviz/converter.rb +32 -0
  23. data/lib/asciidoctor-diagram/graphviz/extension.rb +6 -35
  24. data/lib/asciidoctor-diagram/http/server.rb +127 -0
  25. data/lib/asciidoctor-diagram/lilypond/converter.rb +54 -0
  26. data/lib/asciidoctor-diagram/lilypond/extension.rb +6 -53
  27. data/lib/asciidoctor-diagram/meme/converter.rb +122 -0
  28. data/lib/asciidoctor-diagram/meme/extension.rb +5 -107
  29. data/lib/asciidoctor-diagram/mermaid/converter.rb +178 -0
  30. data/lib/asciidoctor-diagram/mermaid/extension.rb +6 -159
  31. data/lib/asciidoctor-diagram/msc/converter.rb +35 -0
  32. data/lib/asciidoctor-diagram/msc/extension.rb +6 -36
  33. data/lib/asciidoctor-diagram/nomnoml/converter.rb +25 -0
  34. data/lib/asciidoctor-diagram/nomnoml/extension.rb +6 -28
  35. data/lib/asciidoctor-diagram/plantuml/converter.rb +115 -0
  36. data/lib/asciidoctor-diagram/plantuml/extension.rb +10 -119
  37. data/lib/asciidoctor-diagram/shaape/converter.rb +25 -0
  38. data/lib/asciidoctor-diagram/shaape/extension.rb +6 -28
  39. data/lib/asciidoctor-diagram/smcat/converter.rb +44 -0
  40. data/lib/asciidoctor-diagram/smcat/extension.rb +6 -42
  41. data/lib/asciidoctor-diagram/svgbob/converter.rb +25 -0
  42. data/lib/asciidoctor-diagram/svgbob/extension.rb +6 -28
  43. data/lib/asciidoctor-diagram/syntrax/converter.rb +55 -0
  44. data/lib/asciidoctor-diagram/syntrax/extension.rb +6 -51
  45. data/lib/asciidoctor-diagram/tikz/converter.rb +56 -0
  46. data/lib/asciidoctor-diagram/tikz/extension.rb +6 -60
  47. data/lib/asciidoctor-diagram/umlet/converter.rb +24 -0
  48. data/lib/asciidoctor-diagram/umlet/extension.rb +6 -28
  49. data/lib/asciidoctor-diagram/util/java.rb +1 -1
  50. data/lib/asciidoctor-diagram/util/java_socket.rb +7 -9
  51. data/lib/asciidoctor-diagram/util/which.rb +0 -29
  52. data/lib/asciidoctor-diagram/vega/converter.rb +47 -0
  53. data/lib/asciidoctor-diagram/vega/extension.rb +6 -44
  54. data/lib/asciidoctor-diagram/version.rb +1 -1
  55. data/lib/asciidoctor-diagram/wavedrom/converter.rb +50 -0
  56. data/lib/asciidoctor-diagram/wavedrom/extension.rb +6 -54
  57. data/lib/ditaa-1.3.14.jar +0 -0
  58. data/lib/plantuml-1.3.14.jar +0 -0
  59. data/lib/plantuml.jar +0 -0
  60. data/lib/server-1.3.14.jar +0 -0
  61. data/spec/bpmn-example.xml +44 -0
  62. data/spec/bpmn_spec.rb +96 -0
  63. data/spec/mermaid_spec.rb +33 -1
  64. data/spec/plantuml_spec.rb +89 -0
  65. metadata +37 -8
  66. data/lib/asciidoctor-diagram/extensions.rb +0 -568
  67. data/lib/ditaa-1.3.13.jar +0 -0
  68. data/lib/plantuml-1.3.13.jar +0 -0
  69. data/lib/server-1.3.13.jar +0 -0
@@ -314,6 +314,77 @@ User --> (Use the application) : Label
314
314
  expect(b.attributes['height']).to_not be_nil
315
315
  end
316
316
 
317
+ it "should respect the svg-type attribute when format is set to 'svg'" do
318
+ doc = <<-eos
319
+ = Hello, PlantUML!
320
+ Doc Writer <doc@example.com>
321
+
322
+ == First Section
323
+
324
+ [plantuml, format="svg", svg-type="inline"]
325
+ ----
326
+ User -> (Start)
327
+ User --> (Use the application) : Label
328
+
329
+ :Main Admin: ---> (Use the application) : Another label
330
+ ----
331
+ eos
332
+
333
+ d = load_asciidoc doc
334
+ expect(d).to_not be_nil
335
+
336
+ b = d.find { |bl| bl.context == :image }
337
+ expect(b).to_not be_nil
338
+
339
+ expect(b.content_model).to eq :empty
340
+
341
+ target = b.attributes['target']
342
+ expect(target).to_not be_nil
343
+ expect(target).to match(/\.svg/)
344
+ expect(File.exist?(target)).to be true
345
+
346
+ expect(b.attributes['opts']).to eq('inline')
347
+
348
+ expect(b.attributes['width']).to_not be_nil
349
+ expect(b.attributes['height']).to_not be_nil
350
+ end
351
+
352
+ it "should respect the diagram-svg-type attribute when format is set to 'svg'" do
353
+ doc = <<-eos
354
+ = Hello, PlantUML!
355
+ :diagram-svg-type: inline
356
+ Doc Writer <doc@example.com>
357
+
358
+ == First Section
359
+
360
+ [plantuml, format="svg"]
361
+ ----
362
+ User -> (Start)
363
+ User --> (Use the application) : Label
364
+
365
+ :Main Admin: ---> (Use the application) : Another label
366
+ ----
367
+ eos
368
+
369
+ d = load_asciidoc doc
370
+ expect(d).to_not be_nil
371
+
372
+ b = d.find { |bl| bl.context == :image }
373
+ expect(b).to_not be_nil
374
+
375
+ expect(b.content_model).to eq :empty
376
+
377
+ target = b.attributes['target']
378
+ expect(target).to_not be_nil
379
+ expect(target).to match(/\.svg/)
380
+ expect(File.exist?(target)).to be true
381
+
382
+ expect(b.attributes['opts']).to eq('inline')
383
+
384
+ expect(b.attributes['width']).to_not be_nil
385
+ expect(b.attributes['height']).to_not be_nil
386
+ end
387
+
317
388
  it "should generate literal blocks when format is set to 'txt'" do
318
389
  doc = <<-eos
319
390
  = Hello, PlantUML!
@@ -1074,4 +1145,22 @@ Doc Writer <doc@example.com>
1074
1145
  expect(b.attributes['width']).to_not be_nil
1075
1146
  expect(b.attributes['height']).to_not be_nil
1076
1147
  end
1148
+
1149
+ it "should report syntax errors" do
1150
+ doc = <<-eos
1151
+ = Hello, PlantUML!
1152
+ Doc Writer <doc@example.com>
1153
+
1154
+ == First Section
1155
+
1156
+ [plantuml,format="svg"]
1157
+ ----
1158
+ Bob; Alice; foo
1159
+ ----
1160
+ eos
1161
+
1162
+ expect {
1163
+ load_asciidoc doc
1164
+ }.to raise_error(/syntax error/i)
1165
+ end
1077
1166
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: asciidoctor-diagram
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.19
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pepijn Van Eeckhoudt
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-10-12 00:00:00.000000000 Z
11
+ date: 2019-12-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -92,42 +92,66 @@ files:
92
92
  - images/asciidoctor-diagram-process.png
93
93
  - lib/asciidoctor-diagram.rb
94
94
  - lib/asciidoctor-diagram/a2s.rb
95
+ - lib/asciidoctor-diagram/a2s/converter.rb
95
96
  - lib/asciidoctor-diagram/a2s/extension.rb
96
97
  - lib/asciidoctor-diagram/blockdiag.rb
98
+ - lib/asciidoctor-diagram/blockdiag/converter.rb
97
99
  - lib/asciidoctor-diagram/blockdiag/extension.rb
100
+ - lib/asciidoctor-diagram/bpmn.rb
101
+ - lib/asciidoctor-diagram/bpmn/converter.rb
102
+ - lib/asciidoctor-diagram/bpmn/extension.rb
103
+ - lib/asciidoctor-diagram/diagram_converter.rb
104
+ - lib/asciidoctor-diagram/diagram_processor.rb
105
+ - lib/asciidoctor-diagram/diagram_source.rb
98
106
  - lib/asciidoctor-diagram/ditaa.rb
107
+ - lib/asciidoctor-diagram/ditaa/converter.rb
99
108
  - lib/asciidoctor-diagram/ditaa/extension.rb
100
109
  - lib/asciidoctor-diagram/erd.rb
110
+ - lib/asciidoctor-diagram/erd/converter.rb
101
111
  - lib/asciidoctor-diagram/erd/extension.rb
102
- - lib/asciidoctor-diagram/extensions.rb
103
112
  - lib/asciidoctor-diagram/gnuplot.rb
113
+ - lib/asciidoctor-diagram/gnuplot/converter.rb
104
114
  - lib/asciidoctor-diagram/gnuplot/extension.rb
105
115
  - lib/asciidoctor-diagram/graphviz.rb
116
+ - lib/asciidoctor-diagram/graphviz/converter.rb
106
117
  - lib/asciidoctor-diagram/graphviz/extension.rb
118
+ - lib/asciidoctor-diagram/http/server.rb
107
119
  - lib/asciidoctor-diagram/lilypond.rb
120
+ - lib/asciidoctor-diagram/lilypond/converter.rb
108
121
  - lib/asciidoctor-diagram/lilypond/extension.rb
109
122
  - lib/asciidoctor-diagram/meme.rb
123
+ - lib/asciidoctor-diagram/meme/converter.rb
110
124
  - lib/asciidoctor-diagram/meme/extension.rb
111
125
  - lib/asciidoctor-diagram/mermaid.rb
126
+ - lib/asciidoctor-diagram/mermaid/converter.rb
112
127
  - lib/asciidoctor-diagram/mermaid/extension.rb
113
128
  - lib/asciidoctor-diagram/msc.rb
129
+ - lib/asciidoctor-diagram/msc/converter.rb
114
130
  - lib/asciidoctor-diagram/msc/extension.rb
115
131
  - lib/asciidoctor-diagram/nomnoml.rb
132
+ - lib/asciidoctor-diagram/nomnoml/converter.rb
116
133
  - lib/asciidoctor-diagram/nomnoml/extension.rb
117
134
  - lib/asciidoctor-diagram/plantuml.rb
135
+ - lib/asciidoctor-diagram/plantuml/converter.rb
118
136
  - lib/asciidoctor-diagram/plantuml/extension.rb
119
137
  - lib/asciidoctor-diagram/salt.rb
120
138
  - lib/asciidoctor-diagram/shaape.rb
139
+ - lib/asciidoctor-diagram/shaape/converter.rb
121
140
  - lib/asciidoctor-diagram/shaape/extension.rb
122
141
  - lib/asciidoctor-diagram/smcat.rb
142
+ - lib/asciidoctor-diagram/smcat/converter.rb
123
143
  - lib/asciidoctor-diagram/smcat/extension.rb
124
144
  - lib/asciidoctor-diagram/svgbob.rb
145
+ - lib/asciidoctor-diagram/svgbob/converter.rb
125
146
  - lib/asciidoctor-diagram/svgbob/extension.rb
126
147
  - lib/asciidoctor-diagram/syntrax.rb
148
+ - lib/asciidoctor-diagram/syntrax/converter.rb
127
149
  - lib/asciidoctor-diagram/syntrax/extension.rb
128
150
  - lib/asciidoctor-diagram/tikz.rb
151
+ - lib/asciidoctor-diagram/tikz/converter.rb
129
152
  - lib/asciidoctor-diagram/tikz/extension.rb
130
153
  - lib/asciidoctor-diagram/umlet.rb
154
+ - lib/asciidoctor-diagram/umlet/converter.rb
131
155
  - lib/asciidoctor-diagram/umlet/extension.rb
132
156
  - lib/asciidoctor-diagram/util/binaryio.rb
133
157
  - lib/asciidoctor-diagram/util/cli.rb
@@ -142,19 +166,23 @@ files:
142
166
  - lib/asciidoctor-diagram/util/svg.rb
143
167
  - lib/asciidoctor-diagram/util/which.rb
144
168
  - lib/asciidoctor-diagram/vega.rb
169
+ - lib/asciidoctor-diagram/vega/converter.rb
145
170
  - lib/asciidoctor-diagram/vega/extension.rb
146
171
  - lib/asciidoctor-diagram/version.rb
147
172
  - lib/asciidoctor-diagram/wavedrom.rb
173
+ - lib/asciidoctor-diagram/wavedrom/converter.rb
148
174
  - lib/asciidoctor-diagram/wavedrom/extension.rb
149
175
  - lib/batik-all-1.10.jar
150
- - lib/ditaa-1.3.13.jar
176
+ - lib/ditaa-1.3.14.jar
151
177
  - lib/ditaamini-0.12.jar
152
178
  - lib/jlatexmath-minimal-1.0.5.jar
153
- - lib/plantuml-1.3.13.jar
179
+ - lib/plantuml-1.3.14.jar
154
180
  - lib/plantuml.jar
155
- - lib/server-1.3.13.jar
181
+ - lib/server-1.3.14.jar
156
182
  - spec/a2s_spec.rb
157
183
  - spec/blockdiag_spec.rb
184
+ - spec/bpmn-example.xml
185
+ - spec/bpmn_spec.rb
158
186
  - spec/ditaa_spec.rb
159
187
  - spec/erd_spec.rb
160
188
  - spec/gnuplot_spec.rb
@@ -194,8 +222,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
194
222
  - !ruby/object:Gem::Version
195
223
  version: '0'
196
224
  requirements: []
197
- rubyforge_project:
198
- rubygems_version: 2.5.1
225
+ rubygems_version: 3.0.3
199
226
  signing_key:
200
227
  specification_version: 4
201
228
  summary: An extension for asciidoctor that adds support for UML diagram generation
@@ -203,6 +230,8 @@ summary: An extension for asciidoctor that adds support for UML diagram generati
203
230
  test_files:
204
231
  - spec/a2s_spec.rb
205
232
  - spec/blockdiag_spec.rb
233
+ - spec/bpmn-example.xml
234
+ - spec/bpmn_spec.rb
206
235
  - spec/ditaa_spec.rb
207
236
  - spec/erd_spec.rb
208
237
  - spec/gnuplot_spec.rb
@@ -1,568 +0,0 @@
1
- require 'asciidoctor' unless defined? ::Asciidoctor::VERSION
2
- require 'asciidoctor/extensions'
3
- require 'asciidoctor/logging'
4
- require 'digest'
5
- require 'json'
6
- require 'fileutils'
7
- require_relative 'version'
8
- require_relative 'util/java'
9
- require_relative 'util/gif'
10
- require_relative 'util/pdf'
11
- require_relative 'util/png'
12
- require_relative 'util/svg'
13
-
14
- module Asciidoctor
15
- module Diagram
16
- module Extensions
17
-
18
- # Provides the means for diagram processors to register supported output formats and image
19
- # generation routines
20
- module FormatRegistry
21
- # Registers a supported format. The first registered format becomes the default format for the block
22
- # processor.
23
- #
24
- # @param [Symbol] format the format name
25
- # @param [Symbol] type a symbol indicating the type of block that should be generated; either :image or :literal
26
- # @yieldparam parent [Asciidoctor::AbstractNode] the asciidoc block that is being processed
27
- # @yieldparam source [DiagramSource] the source object
28
- # @yieldreturn [String] the generated diagram
29
- #
30
- # Examples
31
- #
32
- # register_format(:png, :image ) do |parent_block, source|
33
- # File.read(source.to_s)
34
- # end
35
- def register_format(format, type, &block)
36
- raise "Unsupported output type: #{type}" unless type == :image || type == :literal
37
-
38
- unless defined?(@default_format)
39
- @default_format = format
40
- end
41
-
42
- formats[format] = {
43
- :type => type,
44
- :generator => block
45
- }
46
- end
47
-
48
- # Returns the registered formats
49
- #
50
- # @return [Hash]
51
- # @api private
52
- def formats
53
- @formats ||= {}
54
- end
55
-
56
- # Returns the default format
57
- #
58
- # @return [Symbol] the default format
59
- # @api private
60
- def default_format
61
- @default_format
62
- end
63
- end
64
-
65
- # Mixin that provides the basic machinery for image generation.
66
- # When this module is included it will include the FormatRegistry into the singleton class of the target class.
67
- module DiagramProcessor
68
- include Asciidoctor::Logging
69
-
70
- IMAGE_PARAMS = {
71
- :svg => {
72
- :encoding => Encoding::UTF_8,
73
- :decoder => SVG
74
- },
75
- :gif => {
76
- :encoding => Encoding::ASCII_8BIT,
77
- :decoder => GIF
78
- },
79
- :png => {
80
- :encoding => Encoding::ASCII_8BIT,
81
- :decoder => PNG
82
- },
83
- :pdf => {
84
- :encoding => Encoding::ASCII_8BIT,
85
- :decoder => PDF
86
- }
87
- }
88
-
89
- def self.included(mod)
90
- mod.use_dsl
91
- class << mod
92
- include FormatRegistry
93
- end
94
- end
95
-
96
- # Processes the diagram block or block macro by converting it into an image or literal block.
97
- #
98
- # @param parent [Asciidoctor::AbstractBlock] the parent asciidoc block of the block or block macro being processed
99
- # @param reader_or_target [Asciidoctor::Reader, String] a reader that provides the contents of a block or the
100
- # target value of a block macro
101
- # @param attributes [Hash] the attributes of the block or block macro
102
- # @return [Asciidoctor::AbstractBlock] a new block that replaces the original block or block macro
103
- def process(parent, reader_or_target, attributes)
104
- location = parent.document.reader.cursor_at_mark
105
-
106
- source = create_source(parent, reader_or_target, attributes.dup)
107
-
108
- begin
109
- format = source.attributes.delete('format') || source.attr('format', self.class.default_format, name)
110
- format = format.to_sym if format.respond_to?(:to_sym)
111
-
112
- raise "Format undefined" unless format
113
-
114
- generator_info = self.class.formats[format]
115
-
116
- raise "#{self.class.name} does not support output format #{format}" unless generator_info
117
-
118
- title = source.attributes.delete 'title'
119
- caption = source.attributes.delete 'caption'
120
-
121
- case generator_info[:type]
122
- when :literal
123
- block = create_literal_block(parent, source, generator_info)
124
- else
125
- block = create_image_block(parent, source, format, generator_info)
126
- end
127
-
128
- block.title = title
129
- block.assign_caption(caption, 'figure')
130
- block
131
- rescue => e
132
- case source.attr('on-error', 'log', 'diagram')
133
- when 'abort'
134
- raise e
135
- else
136
- text = "Failed to generate image: #{e.message}"
137
- warn_msg = text.dup
138
- if $VERBOSE
139
- warn_msg << "\n" << e.backtrace.join("\n")
140
- end
141
-
142
- logger.error message_with_context warn_msg, source_location: location
143
-
144
- text << "\n"
145
- text << source.code
146
- Asciidoctor::Block.new parent, :listing, :source => text, :attributes => attributes
147
- end
148
-
149
- end
150
- end
151
-
152
- protected
153
-
154
- # Creates a DiagramSource object for the block or block macro being processed. Classes using this
155
- # mixin must implement this method.
156
- #
157
- # @param parent_block [Asciidoctor::AbstractBlock] the parent asciidoc block of the block or block macro being processed
158
- # @param reader_or_target [Asciidoctor::Reader, String] a reader that provides the contents of a block or the
159
- # target value of a block macro
160
- # @param attributes [Hash] the attributes of the block or block macro
161
- #
162
- # @return [DiagramSource] an object that implements the interface described by DiagramSource
163
- #
164
- # @abstract
165
- def create_source(parent_block, reader_or_target, attributes)
166
- raise NotImplementedError.new
167
- end
168
-
169
- private
170
- DIGIT_CHAR_RANGE = ('0'.ord)..('9'.ord)
171
-
172
- def create_image_block(parent, source, format, generator_info)
173
- image_name = "#{source.image_name}.#{format}"
174
- image_dir = image_output_dir(parent)
175
- cache_dir = cache_dir(parent)
176
- image_file = parent.normalize_system_path image_name, image_dir
177
- metadata_file = parent.normalize_system_path "#{image_name}.cache", cache_dir
178
-
179
- if File.exist? metadata_file
180
- metadata = File.open(metadata_file, 'r') { |f| JSON.load f }
181
- else
182
- metadata = {}
183
- end
184
-
185
- image_attributes = source.attributes
186
-
187
- if !File.exist?(image_file) || source.should_process?(image_file, metadata)
188
- params = IMAGE_PARAMS[format]
189
-
190
- result = instance_exec(parent, source, &generator_info[:generator])
191
-
192
- result.force_encoding(params[:encoding])
193
-
194
- metadata = source.create_image_metadata
195
- metadata['width'], metadata['height'] = params[:decoder].get_image_size(result)
196
-
197
- FileUtils.mkdir_p(File.dirname(image_file)) unless Dir.exist?(File.dirname(image_file))
198
- File.open(image_file, 'wb') { |f| f.write result }
199
-
200
- FileUtils.mkdir_p(File.dirname(metadata_file)) unless Dir.exist?(File.dirname(metadata_file))
201
- File.open(metadata_file, 'w') { |f| JSON.dump(metadata, f) }
202
- end
203
-
204
- image_attributes['target'] = parent.attr('data-uri', nil, true) ? image_file : image_name
205
-
206
- scale = image_attributes['scale']
207
- if scalematch = /(\d+(?:\.\d+))/.match(scale)
208
- scale_factor = scalematch[1].to_f
209
- else
210
- scale_factor = 1.0
211
- end
212
-
213
- if /html/i =~ parent.document.attributes['backend']
214
- image_attributes.delete('scale')
215
- if metadata['width'] && !image_attributes['width']
216
- image_attributes['width'] = (metadata['width'] * scale_factor).to_i
217
- end
218
- if metadata['height'] && !image_attributes['height']
219
- image_attributes['height'] = (metadata['height'] * scale_factor).to_i
220
- end
221
- end
222
-
223
- image_attributes['alt'] ||= if title_text = image_attributes['title']
224
- title_text
225
- elsif target = image_attributes['target']
226
- (File.basename(target, File.extname(target)) || '').tr '_-', ' '
227
- else
228
- 'Diagram'
229
- end
230
-
231
- image_attributes['alt'] = parent.sub_specialchars image_attributes['alt']
232
-
233
- parent.document.register(:images, image_name)
234
- if (scaledwidth = image_attributes['scaledwidth'])
235
- # append % to scaledwidth if ends in number (no units present)
236
- if DIGIT_CHAR_RANGE.include?((scaledwidth[-1] || 0).ord)
237
- image_attributes['scaledwidth'] = %(#{scaledwidth}%)
238
- end
239
- end
240
-
241
- Asciidoctor::Block.new parent, :image, :content_model => :empty, :attributes => image_attributes
242
- end
243
-
244
- def scale(size, factor)
245
- if match = /(\d+)(.*)/.match(size)
246
- value = match[1].to_i
247
- unit = match[2]
248
- (value * factor).to_i.to_s + unit
249
- else
250
- size
251
- end
252
- end
253
-
254
- def image_output_dir(parent)
255
- document = parent.document
256
-
257
- images_dir = parent.attr('imagesoutdir', nil, true)
258
-
259
- if images_dir
260
- base_dir = nil
261
- else
262
- base_dir = parent.attr('outdir', nil, true) || doc_option(document, :to_dir)
263
- images_dir = parent.attr('imagesdir', nil, true)
264
- end
265
-
266
- parent.normalize_system_path(images_dir, base_dir)
267
- end
268
-
269
- def cache_dir(parent)
270
- document = parent.document
271
- cache_dir = '.asciidoctor/diagram'
272
- base_dir = parent.attr('outdir', nil, true) || doc_option(document, :to_dir)
273
- parent.normalize_system_path(cache_dir, base_dir)
274
- end
275
-
276
- def create_literal_block(parent, source, generator_info)
277
- literal_attributes = source.attributes
278
- literal_attributes.delete('target')
279
-
280
- result = instance_exec(parent, source, &generator_info[:generator])
281
-
282
- result.force_encoding(Encoding::UTF_8)
283
- Asciidoctor::Block.new parent, :literal, :source => result, :attributes => literal_attributes
284
- end
285
-
286
- def doc_option(document, key)
287
- if document.respond_to?(:options)
288
- value = document.options[key]
289
- else
290
- value = nil
291
- end
292
-
293
- if document.nested? && value.nil?
294
- doc_option(document.parent_document, key)
295
- else
296
- value
297
- end
298
- end
299
- end
300
-
301
- # Base class for diagram block processors.
302
- class DiagramBlockProcessor < Asciidoctor::Extensions::BlockProcessor
303
- include DiagramProcessor
304
-
305
- def self.inherited(subclass)
306
- subclass.name_positional_attributes ['target', 'format']
307
- subclass.contexts [:listing, :literal, :open]
308
- subclass.content_model :simple
309
- end
310
-
311
- # Creates a ReaderSource from the given reader.
312
- #
313
- # @return [ReaderSource] a ReaderSource
314
- def create_source(parent_block, reader, attributes)
315
- ReaderSource.new(parent_block, reader, attributes)
316
- end
317
- end
318
-
319
- # Base class for diagram block macro processors.
320
- class DiagramBlockMacroProcessor < Asciidoctor::Extensions::BlockMacroProcessor
321
- include DiagramProcessor
322
-
323
- def self.inherited(subclass)
324
- subclass.name_positional_attributes ['target', 'format']
325
- end
326
-
327
- def apply_target_subs(parent, target)
328
- if target
329
- parent.normalize_system_path(parent.sub_attributes(target, :attribute_missing => 'warn'))
330
- else
331
- nil
332
- end
333
- end
334
-
335
- # Creates a FileSource using target as the file name.
336
- #
337
- # @return [FileSource] a FileSource
338
- def create_source(parent, target, attributes)
339
- FileSource.new(parent, apply_target_subs(parent, target), attributes)
340
- end
341
- end
342
-
343
- # This module describes the duck-typed interface that diagram sources must implement. Implementations
344
- # may include this module but it is not required.
345
- module DiagramSource
346
- # @return [String] the base name for the image file that will be produced
347
- # @abstract
348
- def image_name
349
- raise NotImplementedError.new
350
- end
351
-
352
- # @return [String] the String representation of the source code for the diagram
353
- # @abstract
354
- def code
355
- raise NotImplementedError.new
356
- end
357
-
358
- # Get the value for the specified attribute. First look in the attributes on
359
- # this document and return the value of the attribute if found. Otherwise, if
360
- # this document is a child of the Document document, look in the attributes of the
361
- # Document document and return the value of the attribute if found. Otherwise,
362
- # return the default value, which defaults to nil.
363
- #
364
- # @param name [String, Symbol] the name of the attribute to lookup
365
- # @param default_value [Object] the value to return if the attribute is not found
366
- # @inherit [Boolean, String] indicates whether to check for the attribute on the AsciiDoctor::Document if not found on this document.
367
- # When a non-nil String is given the an attribute name "#{inherit}-#{name}" is looked for on the document.
368
- #
369
- # @return the value of the attribute or the default value if the attribute is not found in the attributes of this node or the document node
370
- # @abstract
371
- def attr(name, default_value = nil, inherit = nil)
372
- raise NotImplementedError.new
373
- end
374
-
375
- # @return [String] the base directory against which relative paths in this diagram should be resolved
376
- # @abstract
377
- def base_dir
378
- attr('docdir', nil, true)
379
- end
380
-
381
- # Alias for code
382
- def to_s
383
- code
384
- end
385
-
386
- # Determines if the diagram should be regenerated or not. The default implementation of this method simply
387
- # returns true.
388
- #
389
- # @param image_file [String] the path to the previously generated version of the image
390
- # @param image_metadata [Hash] the image metadata Hash that was stored during the previous diagram generation pass
391
- # @return [Boolean] true if the diagram should be regenerated; false otherwise
392
- def should_process?(image_file, image_metadata)
393
- true
394
- end
395
-
396
- # Creates an image metadata Hash that will be stored to disk alongside the generated image file. The contents
397
- # of this Hash are reread during subsequent document processing and then passed to the should_process? method
398
- # where it can be used to determine if the diagram should be regenerated or not.
399
- # The default implementation returns an empty Hash.
400
- # @return [Hash] a Hash containing metadata
401
- def create_image_metadata
402
- {}
403
- end
404
- end
405
-
406
- # Base class for diagram source implementations that uses an md5 checksum of the source code of a diagram to
407
- # determine if it has been updated or not.
408
- class BasicSource
409
- include DiagramSource
410
-
411
- attr_reader :attributes
412
-
413
- def initialize(parent_block, attributes)
414
- @parent_block = parent_block
415
- @attributes = attributes
416
- end
417
-
418
- def image_name
419
- attr('target', 'diag-' + checksum)
420
- end
421
-
422
- def attr(name, default_value=nil, inherit=nil)
423
- name = name.to_s if ::Symbol === name
424
-
425
- value = @attributes[name]
426
-
427
- if value.nil? && inherit
428
- case inherit
429
- when String, Symbol
430
- value = @parent_block.attr("#{inherit.to_s}-#{name}", default_value, true)
431
- else
432
- value = @parent_block.attr(name, default_value, true)
433
- end
434
- end
435
-
436
- value || default_value
437
- end
438
-
439
- def should_process?(image_file, image_metadata)
440
- image_metadata['checksum'] != checksum
441
- end
442
-
443
- def create_image_metadata
444
- {'checksum' => checksum}
445
- end
446
-
447
- def checksum
448
- @checksum ||= compute_checksum(code)
449
- end
450
-
451
- protected
452
- def resolve_diagram_subs
453
- if @attributes.key? 'subs'
454
- @parent_block.resolve_block_subs @attributes['subs'], nil, 'diagram'
455
- else
456
- []
457
- end
458
- end
459
-
460
- private
461
- def compute_checksum(code)
462
- md5 = Digest::MD5.new
463
- md5 << code
464
- @attributes.each do |k, v|
465
- md5 << k.to_s if k
466
- md5 << v.to_s if v
467
- end
468
- md5.hexdigest
469
- end
470
- end
471
-
472
- # A diagram source that retrieves the code for the diagram from the contents of a block.
473
- class ReaderSource < BasicSource
474
- include DiagramSource
475
-
476
- def initialize(parent_block, reader, attributes)
477
- super(parent_block, attributes)
478
- @reader = reader
479
- end
480
-
481
- def code
482
- @code ||= @parent_block.apply_subs(@reader.lines, resolve_diagram_subs).join("\n")
483
- end
484
- end
485
-
486
- # A diagram source that retrieves the code for a diagram from an external source file.
487
- class FileSource < BasicSource
488
- def initialize(parent_block, file_name, attributes)
489
- super(parent_block, attributes)
490
- @file_name = file_name
491
- end
492
-
493
- def base_dir
494
- if @file_name
495
- File.dirname(@file_name)
496
- else
497
- super
498
- end
499
- end
500
-
501
- def image_name
502
- if @attributes['target']
503
- super
504
- elsif @file_name
505
- File.basename(@file_name, File.extname(@file_name))
506
- else
507
- checksum
508
- end
509
- end
510
-
511
- def should_process?(image_file, image_metadata)
512
- (@file_name && File.mtime(@file_name) > File.mtime(image_file)) || super
513
- end
514
-
515
- def code
516
- @code ||= read_code
517
- end
518
-
519
- def read_code
520
- if @file_name
521
- lines = File.readlines(@file_name)
522
- lines = prepare_source_array(lines)
523
- @parent_block.apply_subs(lines, resolve_diagram_subs).join("\n")
524
- else
525
- ''
526
- end
527
- end
528
-
529
- private
530
-
531
- # Byte arrays for UTF-* Byte Order Marks
532
- BOM_BYTES_UTF_8 = [0xef, 0xbb, 0xbf]
533
- BOM_BYTES_UTF_16LE = [0xff, 0xfe]
534
- BOM_BYTES_UTF_16BE = [0xfe, 0xff]
535
-
536
- # Prepare the source data Array for parsing.
537
- #
538
- # Encodes the data to UTF-8, if necessary, and removes any trailing
539
- # whitespace from every line.
540
- #
541
- # If a BOM is found at the beginning of the data, a best attempt is made to
542
- # encode it to UTF-8 from the specified source encoding.
543
- #
544
- # data - the source data Array to prepare (no nil entries allowed)
545
- #
546
- # returns a String Array of prepared lines
547
- def prepare_source_array data
548
- return [] if data.empty?
549
- if (leading_2_bytes = (leading_bytes = (first = data[0]).unpack 'C3').slice 0, 2) == BOM_BYTES_UTF_16LE
550
- data[0] = first.byteslice 2, first.bytesize
551
- # NOTE you can't split a UTF-16LE string using .lines when encoding is UTF-8; doing so will cause this line to fail
552
- return data.map {|line| (line.encode ::Encoding::UTF_8, ::Encoding::UTF_16LE).rstrip }
553
- elsif leading_2_bytes == BOM_BYTES_UTF_16BE
554
- data[0] = first.byteslice 2, first.bytesize
555
- return data.map {|line| (line.encode ::Encoding::UTF_8, ::Encoding::UTF_16BE).rstrip }
556
- elsif leading_bytes == BOM_BYTES_UTF_8
557
- data[0] = first.byteslice 3, first.bytesize
558
- end
559
- if first.encoding == ::Encoding::UTF_8
560
- data.map {|line| line.rstrip }
561
- else
562
- data.map {|line| (line.encode ::Encoding::UTF_8).rstrip }
563
- end
564
- end
565
- end
566
- end
567
- end
568
- end