asciidoctor-diagram 1.5.18 → 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +75 -0
- data/README.adoc +98 -23
- data/examples/features.adoc +2 -2
- data/lib/asciidoctor-diagram.rb +8 -0
- data/lib/asciidoctor-diagram/a2s/converter.rb +59 -0
- data/lib/asciidoctor-diagram/a2s/extension.rb +6 -52
- data/lib/asciidoctor-diagram/blockdiag/converter.rb +37 -0
- data/lib/asciidoctor-diagram/blockdiag/extension.rb +9 -116
- data/lib/asciidoctor-diagram/bpmn.rb +7 -0
- data/lib/asciidoctor-diagram/bpmn/converter.rb +62 -0
- data/lib/asciidoctor-diagram/bpmn/extension.rb +14 -0
- data/lib/asciidoctor-diagram/bytefield.rb +7 -0
- data/lib/asciidoctor-diagram/bytefield/converter.rb +26 -0
- data/lib/asciidoctor-diagram/bytefield/extension.rb +14 -0
- data/lib/asciidoctor-diagram/diagram_converter.rb +23 -0
- data/lib/asciidoctor-diagram/diagram_processor.rb +357 -0
- data/lib/asciidoctor-diagram/diagram_source.rb +322 -0
- data/lib/asciidoctor-diagram/ditaa/converter.rb +90 -0
- data/lib/asciidoctor-diagram/ditaa/extension.rb +6 -71
- data/lib/asciidoctor-diagram/dpic.rb +7 -0
- data/lib/asciidoctor-diagram/dpic/converter.rb +30 -0
- data/lib/asciidoctor-diagram/dpic/extension.rb +14 -0
- data/lib/asciidoctor-diagram/erd/converter.rb +31 -0
- data/lib/asciidoctor-diagram/erd/extension.rb +6 -35
- data/lib/asciidoctor-diagram/gnuplot.rb +7 -0
- data/lib/asciidoctor-diagram/gnuplot/converter.rb +63 -0
- data/lib/asciidoctor-diagram/gnuplot/extension.rb +14 -0
- data/lib/asciidoctor-diagram/graphviz/converter.rb +32 -0
- data/lib/asciidoctor-diagram/graphviz/extension.rb +6 -35
- data/lib/asciidoctor-diagram/http/converter.rb +99 -0
- data/lib/asciidoctor-diagram/http/server.rb +132 -0
- data/lib/asciidoctor-diagram/lilypond.rb +7 -0
- data/lib/asciidoctor-diagram/lilypond/converter.rb +54 -0
- data/lib/asciidoctor-diagram/lilypond/extension.rb +14 -0
- data/lib/asciidoctor-diagram/meme/converter.rb +122 -0
- data/lib/asciidoctor-diagram/meme/extension.rb +5 -107
- data/lib/asciidoctor-diagram/mermaid/converter.rb +179 -0
- data/lib/asciidoctor-diagram/mermaid/extension.rb +6 -159
- data/lib/asciidoctor-diagram/msc/converter.rb +35 -0
- data/lib/asciidoctor-diagram/msc/extension.rb +6 -36
- data/lib/asciidoctor-diagram/nomnoml/converter.rb +25 -0
- data/lib/asciidoctor-diagram/nomnoml/extension.rb +6 -28
- data/lib/asciidoctor-diagram/pikchr.rb +7 -0
- data/lib/asciidoctor-diagram/pikchr/converter.rb +26 -0
- data/lib/asciidoctor-diagram/pikchr/extension.rb +14 -0
- data/lib/asciidoctor-diagram/plantuml/converter.rb +117 -0
- data/lib/asciidoctor-diagram/plantuml/extension.rb +10 -119
- data/lib/asciidoctor-diagram/shaape/converter.rb +25 -0
- data/lib/asciidoctor-diagram/shaape/extension.rb +6 -28
- data/lib/asciidoctor-diagram/smcat.rb +7 -0
- data/lib/asciidoctor-diagram/smcat/converter.rb +44 -0
- data/lib/asciidoctor-diagram/smcat/extension.rb +14 -0
- data/lib/asciidoctor-diagram/svgbob/converter.rb +49 -0
- data/lib/asciidoctor-diagram/svgbob/extension.rb +6 -28
- data/lib/asciidoctor-diagram/symbolator.rb +7 -0
- data/lib/asciidoctor-diagram/symbolator/converter.rb +23 -0
- data/lib/asciidoctor-diagram/symbolator/extension.rb +14 -0
- data/lib/asciidoctor-diagram/syntrax/converter.rb +58 -0
- data/lib/asciidoctor-diagram/syntrax/extension.rb +6 -51
- data/lib/asciidoctor-diagram/tikz/converter.rb +56 -0
- data/lib/asciidoctor-diagram/tikz/extension.rb +6 -60
- data/lib/asciidoctor-diagram/umlet/converter.rb +24 -0
- data/lib/asciidoctor-diagram/umlet/extension.rb +6 -28
- data/lib/asciidoctor-diagram/util/cli.rb +14 -3
- data/lib/asciidoctor-diagram/util/cli_generator.rb +19 -1
- data/lib/asciidoctor-diagram/util/gif.rb +2 -2
- data/lib/asciidoctor-diagram/util/java.rb +1 -1
- data/lib/asciidoctor-diagram/util/java_socket.rb +7 -9
- data/lib/asciidoctor-diagram/util/pdf.rb +2 -2
- data/lib/asciidoctor-diagram/util/png.rb +2 -2
- data/lib/asciidoctor-diagram/util/svg.rb +46 -19
- data/lib/asciidoctor-diagram/util/which.rb +0 -29
- data/lib/asciidoctor-diagram/vega/converter.rb +47 -0
- data/lib/asciidoctor-diagram/vega/extension.rb +6 -44
- data/lib/asciidoctor-diagram/version.rb +1 -1
- data/lib/asciidoctor-diagram/wavedrom/converter.rb +50 -0
- data/lib/asciidoctor-diagram/wavedrom/extension.rb +6 -46
- data/lib/ditaa-1.3.15.jar +0 -0
- data/lib/ditaamini-0.12.jar +0 -0
- data/lib/plantuml-1.3.15.jar +0 -0
- data/lib/plantuml.jar +0 -0
- data/lib/server-1.3.15.jar +0 -0
- data/spec/a2s_spec.rb +2 -140
- data/spec/blockdiag_spec.rb +2 -200
- data/spec/bpmn_spec.rb +56 -0
- data/spec/bytefield_spec.rb +92 -0
- data/spec/ditaa_spec.rb +37 -143
- data/spec/dpic_spec.rb +19 -0
- data/spec/erd_spec.rb +2 -199
- data/spec/gnuplot_spec.rb +225 -0
- data/spec/graphviz_spec.rb +6 -145
- data/spec/lilypond_spec.rb +13 -0
- data/spec/mermaid_spec.rb +35 -200
- data/spec/msc_spec.rb +2 -199
- data/spec/nomnoml_spec.rb +4 -142
- data/spec/pikchr_spec.rb +51 -0
- data/spec/plantuml_spec.rb +24 -507
- data/spec/shaape_spec.rb +9 -221
- data/spec/shared_examples.rb +552 -0
- data/spec/smcat_spec.rb +26 -0
- data/spec/svgbob_spec.rb +2 -140
- data/spec/symbolator_spec.rb +23 -0
- data/spec/syntrax_spec.rb +5 -215
- data/spec/test_helper.rb +1 -18
- data/spec/tikz_spec.rb +4 -24
- data/spec/umlet_spec.rb +2 -58
- data/spec/vega_spec.rb +4 -117
- data/spec/wavedrom_spec.rb +2 -199
- metadata +73 -11
- data/lib/asciidoctor-diagram/extensions.rb +0 -568
- data/lib/ditaa-1.3.13.jar +0 -0
- data/lib/ditaamini-0.11.jar +0 -0
- data/lib/plantuml-1.3.13.jar +0 -0
- data/lib/server-1.3.13.jar +0 -0
@@ -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
|