asciidoctor-dita-map 0.9.4 → 0.9.6
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/bin/dita-map +4 -4
- data/lib/dita-map/catalog.rb +9 -7
- data/lib/dita-map/cli.rb +31 -203
- data/lib/dita-map/convert.rb +164 -0
- data/lib/dita-map/map.rb +52 -0
- data/lib/dita-map/topic.rb +60 -0
- data/lib/dita-map/version.rb +2 -1
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ecc37275d95dbc4dc7be898a49ea706ae9b44e479544e9c29d0c1108d1f8b13e
|
|
4
|
+
data.tar.gz: a3d26bff03b1660a599f3386099fd53232da8787309f12a6590c023b58e3c028
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 70f3c196109ddeb6cab52f5c96fb222a8336e521e43a7b3660bd9e65a703e1b7820a1e96f0e44d6d59c85666aacafb1640c1bf2028891698ce1f47140a206d62
|
|
7
|
+
data.tar.gz: 663059f4079fd1342808861209779e99656f011d5dbaf7764c698b7518903490193e67e6ffe56024ca240969adb39307f595c05da52a436580d6aa9aac5f25dd
|
data/bin/dita-map
CHANGED
|
@@ -26,11 +26,11 @@
|
|
|
26
26
|
require (cli = File.absolute_path '../lib/dita-map/cli.rb', __dir__) ? cli : 'dita-map/cli'
|
|
27
27
|
|
|
28
28
|
begin
|
|
29
|
-
|
|
30
|
-
converter = AsciidoctorDitaMap::Cli.new name, ARGV
|
|
29
|
+
converter = AsciidoctorDitaMap::Cli.new ARGV
|
|
31
30
|
converter.run
|
|
32
|
-
rescue OptionParser::InvalidArgument, OptionParser::InvalidOption,
|
|
33
|
-
|
|
31
|
+
rescue OptionParser::InvalidArgument, OptionParser::InvalidOption,
|
|
32
|
+
OptionParser::AmbiguousOption, OptionParser::MissingArgument => error
|
|
33
|
+
abort "#{AsciidoctorDitaMap::NAME}: #{error.message}"
|
|
34
34
|
rescue Interrupt
|
|
35
35
|
exit 130
|
|
36
36
|
end
|
data/lib/dita-map/catalog.rb
CHANGED
|
@@ -29,16 +29,18 @@ class CatalogIncludeDirectives < Asciidoctor::Extensions::IncludeProcessor
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def process doc, reader, target, attributes
|
|
32
|
-
offset
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
offset = attributes['leveloffset'].to_i
|
|
33
|
+
navtitle = attributes['navtitle'] or nil
|
|
34
|
+
chunk = attributes['chunk'] or nil
|
|
35
|
+
toc = attributes['toc'] or nil
|
|
35
36
|
|
|
36
37
|
doc.catalog[:include_files] = [] unless doc.catalog[:include_files]
|
|
37
38
|
doc.catalog[:include_files].append({
|
|
38
|
-
:target
|
|
39
|
-
:offset
|
|
40
|
-
:
|
|
41
|
-
:
|
|
39
|
+
:target => target,
|
|
40
|
+
:offset => offset,
|
|
41
|
+
:navtitle => navtitle,
|
|
42
|
+
:chunk => chunk,
|
|
43
|
+
:toc => toc
|
|
42
44
|
})
|
|
43
45
|
|
|
44
46
|
reader
|
data/lib/dita-map/cli.rb
CHANGED
|
@@ -23,44 +23,28 @@
|
|
|
23
23
|
|
|
24
24
|
require 'optparse'
|
|
25
25
|
require 'pathname'
|
|
26
|
-
|
|
27
|
-
require 'rexml/document'
|
|
28
|
-
require_relative 'catalog'
|
|
26
|
+
require_relative 'convert'
|
|
29
27
|
require_relative 'version'
|
|
30
28
|
|
|
31
29
|
module AsciidoctorDitaMap
|
|
32
30
|
class Cli
|
|
33
|
-
def initialize
|
|
34
|
-
@
|
|
35
|
-
@
|
|
36
|
-
|
|
37
|
-
:id => true,
|
|
38
|
-
:locktitle => true,
|
|
39
|
-
:navtitle => true,
|
|
40
|
-
:output => false,
|
|
41
|
-
:title => true,
|
|
42
|
-
:toc => true,
|
|
43
|
-
:type => true,
|
|
44
|
-
:self => false,
|
|
45
|
-
:verbose => false,
|
|
46
|
-
:zero_offset => false
|
|
47
|
-
}
|
|
48
|
-
@prep = []
|
|
49
|
-
@name = name
|
|
50
|
-
@args = self.parse_args argv
|
|
31
|
+
def initialize argv
|
|
32
|
+
@output = nil
|
|
33
|
+
@converter = Convert.new
|
|
34
|
+
@args = self.parse_args argv
|
|
51
35
|
end
|
|
52
36
|
|
|
53
37
|
def parse_args argv
|
|
54
38
|
parser = OptionParser.new do |opt|
|
|
55
|
-
opt.banner = "Usage: #{
|
|
56
|
-
opt.banner += " #{
|
|
39
|
+
opt.banner = "Usage: #{NAME} [OPTION...] [FILE...]\n"
|
|
40
|
+
opt.banner += " #{NAME} -h|-V\n\n"
|
|
57
41
|
|
|
58
42
|
opt.on('-o', '--out-file FILE', 'specify the output file; by default, the output file name is based on the input file') do |output|
|
|
59
|
-
@
|
|
43
|
+
@output = (output.strip == '-') ? $stdout : output
|
|
60
44
|
end
|
|
61
45
|
|
|
62
46
|
opt.on('-a', '--attribute ATTRIBUTE', 'set a document attribute in the form of name, name!, or name=value pair; can be supplied multiple times') do |value|
|
|
63
|
-
@attr.append value
|
|
47
|
+
@converter.attr.append value
|
|
64
48
|
end
|
|
65
49
|
|
|
66
50
|
opt.separator ''
|
|
@@ -69,51 +53,56 @@ module AsciidoctorDitaMap
|
|
|
69
53
|
raise OptionParser::InvalidArgument, "not a file: #{file}" unless File.exist? file and File.file? file
|
|
70
54
|
raise OptionParser::InvalidArgument, "file not readable: #{file}" unless File.readable? file
|
|
71
55
|
|
|
72
|
-
@prep.
|
|
56
|
+
@converter.prep << File.read(file)
|
|
57
|
+
@converter.prep << "\n"
|
|
73
58
|
end
|
|
74
59
|
|
|
75
60
|
opt.on('-i', '--include-self', 'make the supplied file the toplevel topicref') do
|
|
76
|
-
@opts[:self] = true
|
|
61
|
+
@converter.opts[:self] = true
|
|
77
62
|
end
|
|
78
63
|
|
|
79
64
|
opt.separator ''
|
|
80
65
|
|
|
81
66
|
opt.on('-I', '--no-id', 'do not generate the map id attribute') do
|
|
82
|
-
@opts[:id] = false
|
|
67
|
+
@converter.opts[:id] = false
|
|
83
68
|
end
|
|
84
69
|
|
|
85
70
|
opt.on('-M', '--no-maptitle', 'do not generate the map title') do
|
|
86
|
-
@opts[:title] = false
|
|
71
|
+
@converter.opts[:title] = false
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
opt.on('-A', '--no-assembly', 'do treat assemblies as maps') do
|
|
75
|
+
@converter.opts[:assembly] = false
|
|
87
76
|
end
|
|
88
77
|
|
|
89
78
|
opt.on('-C', '--no-chunk', 'do not generate the chunk attribute') do
|
|
90
|
-
@opts[:chunk] = false
|
|
79
|
+
@converter.opts[:chunk] = false
|
|
91
80
|
end
|
|
92
81
|
|
|
93
82
|
opt.on('-L', '--no-locktitle', 'do not generate the locktitle attribute') do
|
|
94
|
-
@opts[:locktitle] = false
|
|
83
|
+
@converter.opts[:locktitle] = false
|
|
95
84
|
end
|
|
96
85
|
|
|
97
86
|
opt.on('-N', '--no-navtitle', 'do not generate the navtitle attribute') do
|
|
98
|
-
@opts[:navtitle] = false
|
|
87
|
+
@converter.opts[:navtitle] = false
|
|
99
88
|
end
|
|
100
89
|
|
|
101
90
|
opt.on('-O', '--no-toc', 'do not generate the toc attribute') do
|
|
102
|
-
@opts[:toc] = false
|
|
91
|
+
@converter.opts[:toc] = false
|
|
103
92
|
end
|
|
104
93
|
|
|
105
94
|
opt.on('-T', '--no-type', 'do not generate the type attribute') do
|
|
106
|
-
@opts[:type] = false
|
|
95
|
+
@converter.opts[:type] = false
|
|
107
96
|
end
|
|
108
97
|
|
|
109
98
|
opt.separator ''
|
|
110
99
|
|
|
111
100
|
opt.on('-v', '--verbose', 'report additional problems in the supplied files') do
|
|
112
|
-
@opts[:verbose] = true
|
|
101
|
+
@converter.opts[:verbose] = true
|
|
113
102
|
end
|
|
114
103
|
|
|
115
104
|
opt.on('-z', '--zero-offset', 'allow include directives with zero leveloffset') do
|
|
116
|
-
@opts[:zero_offset] = true
|
|
105
|
+
@converter.opts[:zero_offset] = true
|
|
117
106
|
end
|
|
118
107
|
|
|
119
108
|
opt.separator ''
|
|
@@ -124,7 +113,7 @@ module AsciidoctorDitaMap
|
|
|
124
113
|
end
|
|
125
114
|
|
|
126
115
|
opt.on('-V', '--version', 'display version information and exit') do
|
|
127
|
-
puts "#{
|
|
116
|
+
puts "#{NAME} #{VERSION}"
|
|
128
117
|
exit
|
|
129
118
|
end
|
|
130
119
|
end
|
|
@@ -143,183 +132,22 @@ module AsciidoctorDitaMap
|
|
|
143
132
|
return args
|
|
144
133
|
end
|
|
145
134
|
|
|
146
|
-
def compose_mapref_attributes file_info, type
|
|
147
|
-
target_file = file_info[:target].sub(/\.adoc$/, '.ditamap')
|
|
148
|
-
attributes = { 'href' => target_file, 'format' => 'ditamap' }
|
|
149
|
-
attributes['type'] = type if @opts[:type]
|
|
150
|
-
attributes['chunk'] = file_info[:chunk] if @opts[:chunk] and file_info[:chunk]
|
|
151
|
-
attributes['toc'] = file_info[:toc] if @opts[:toc] and file_info[:toc]
|
|
152
|
-
|
|
153
|
-
return attributes
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
def compose_topicref_attributes file_info, title, type
|
|
157
|
-
target_file = file_info[:target].sub(/\.adoc$/, '.dita')
|
|
158
|
-
attributes = { 'href' => target_file }
|
|
159
|
-
attributes['navtitle'] = title if @opts[:navtitle] and title
|
|
160
|
-
attributes['locktitle'] = 'yes' if @opts[:locktitle] and attributes['navtitle']
|
|
161
|
-
attributes['type'] = type if @opts[:type] and type and ['concept', 'reference', 'task'].include? type
|
|
162
|
-
attributes['chunk'] = file_info[:chunk] if @opts[:chunk] and file_info[:chunk]
|
|
163
|
-
attributes['toc'] = file_info[:toc] if @opts[:toc] and file_info[:toc]
|
|
164
|
-
|
|
165
|
-
return attributes
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
def get_content_type attributes
|
|
169
|
-
document_type = attributes['_mod-docs-content-type'] ? attributes['_mod-docs-content-type'].downcase : nil
|
|
170
|
-
document_type = attributes['_content-type'] ? attributes['_content-type'].downcase : nil unless document_type
|
|
171
|
-
document_type = attributes['_module-type'] ? attributes['_module-type'].downcase : nil unless document_type
|
|
172
|
-
|
|
173
|
-
if document_type
|
|
174
|
-
document_type.sub!(/^assembly$/, 'concept')
|
|
175
|
-
document_type.sub!(/^procedure$/, 'task')
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
unless ['concept', 'reference', 'task', 'map', 'attributes', 'snippet'].include? document_type
|
|
179
|
-
return nil
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
return document_type
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
def parse_topic input
|
|
186
|
-
doc = Asciidoctor.load input, safe: :secure, attributes: @attr
|
|
187
|
-
|
|
188
|
-
document_title = doc.title ? doc.title.gsub(/<[^>]*>/, '') : nil
|
|
189
|
-
document_type = get_content_type doc.attributes
|
|
190
|
-
|
|
191
|
-
return document_title, document_type
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
def parse_map input, base_dir
|
|
195
|
-
Asciidoctor::Extensions.register do
|
|
196
|
-
include_processor CatalogIncludeDirectives
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
doc = Asciidoctor.load input, safe: :safe, catalog_assets: true, attributes: @attr, base_dir: base_dir
|
|
200
|
-
|
|
201
|
-
include_files = doc.catalog[:include_files] ? doc.catalog[:include_files] : []
|
|
202
|
-
map_id = doc.id ? doc.id.gsub(/["']/, '') : nil
|
|
203
|
-
map_title = doc.title ? doc.title.gsub(/<[^>]*>/, '') : nil
|
|
204
|
-
map_type = get_content_type doc.attributes
|
|
205
|
-
|
|
206
|
-
info = {
|
|
207
|
-
:id => map_id,
|
|
208
|
-
:title => map_title,
|
|
209
|
-
:type => map_type
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return include_files, info
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
def convert_map input, base_dir, prepended = '', file = nil
|
|
216
|
-
result = ''
|
|
217
|
-
|
|
218
|
-
include_files, map = parse_map prepended + input, base_dir
|
|
219
|
-
|
|
220
|
-
xml = REXML::Document.new
|
|
221
|
-
xml.context[:attribute_quote] = :quote
|
|
222
|
-
xml << REXML::XMLDecl.new('1.0', 'utf-8')
|
|
223
|
-
xml << REXML::DocType.new('map', 'PUBLIC "-//OASIS//DTD DITA Map//EN" "map.dtd"')
|
|
224
|
-
|
|
225
|
-
if map[:id] and @opts[:id]
|
|
226
|
-
xml_root = xml.add_element('map', { 'id' => map[:id] })
|
|
227
|
-
else
|
|
228
|
-
xml_root = xml.add_element('map')
|
|
229
|
-
end
|
|
230
|
-
|
|
231
|
-
if map[:title] and @opts[:title]
|
|
232
|
-
xml_title = xml_root.add_element('title')
|
|
233
|
-
xml_title.text = map[:title]
|
|
234
|
-
end
|
|
235
|
-
|
|
236
|
-
if @opts[:self] and file
|
|
237
|
-
attributes = compose_topicref_attributes({ :target => file }, map[:title], map[:type])
|
|
238
|
-
xml_self = xml_root.add_element('topicref', attributes)
|
|
239
|
-
stack = [{ :offset => 0, :element => xml_self }]
|
|
240
|
-
else
|
|
241
|
-
stack = [{ :offset => 0, :element => xml_root }]
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
include_files.each do |file_info|
|
|
245
|
-
target = file_info[:target]
|
|
246
|
-
offset = file_info[:offset]
|
|
247
|
-
last_offset = stack.last[:offset]
|
|
248
|
-
full_path = base_dir + target
|
|
249
|
-
|
|
250
|
-
if not File.exist? full_path and @opts[:verbose]
|
|
251
|
-
warn "#{@name}: warning: file not found: #{target}"
|
|
252
|
-
end
|
|
253
|
-
|
|
254
|
-
begin
|
|
255
|
-
include_title, include_type = parse_topic prepended + File.read(full_path)
|
|
256
|
-
next if ['attributes', 'snippet'].include? include_type
|
|
257
|
-
rescue
|
|
258
|
-
warn "#{@name}: warning: unable to read included file: #{target}"
|
|
259
|
-
include_title, include_type = nil, nil
|
|
260
|
-
end
|
|
261
|
-
|
|
262
|
-
if offset == 0
|
|
263
|
-
if @opts[:zero_offset]
|
|
264
|
-
offset = 0
|
|
265
|
-
else
|
|
266
|
-
warn "#{@name}: warning: invalid leveloffset - expected 1, got 0: #{target}"
|
|
267
|
-
offset = 1
|
|
268
|
-
end
|
|
269
|
-
elsif offset > last_offset and offset - last_offset > 1
|
|
270
|
-
expected_offset = last_offset + 1
|
|
271
|
-
warn "#{@name}: warning: invalid leveloffset - expected #{expected_offset}, got #{offset}: #{target}"
|
|
272
|
-
end
|
|
273
|
-
|
|
274
|
-
while stack.length > 1 and stack.last[:offset] >= offset
|
|
275
|
-
stack.pop
|
|
276
|
-
end
|
|
277
|
-
|
|
278
|
-
xml_parent = stack.last[:element]
|
|
279
|
-
|
|
280
|
-
if include_type == 'map'
|
|
281
|
-
attributes = compose_mapref_attributes file_info, include_type
|
|
282
|
-
xml_element = xml_parent.add_element('mapref', attributes)
|
|
283
|
-
else
|
|
284
|
-
attributes = compose_topicref_attributes file_info, include_title, include_type
|
|
285
|
-
xml_element = xml_parent.add_element('topicref', attributes)
|
|
286
|
-
end
|
|
287
|
-
|
|
288
|
-
stack.push ({ :offset => offset, :element => xml_element })
|
|
289
|
-
end
|
|
290
|
-
|
|
291
|
-
formatter = REXML::Formatters::Pretty.new(2, true)
|
|
292
|
-
formatter.compact = true
|
|
293
|
-
formatter.write(xml, result)
|
|
294
|
-
|
|
295
|
-
result << "\n"
|
|
296
|
-
|
|
297
|
-
return result
|
|
298
|
-
end
|
|
299
|
-
|
|
300
135
|
def run
|
|
301
|
-
prepended = ''
|
|
302
|
-
|
|
303
|
-
@prep.each do |file|
|
|
304
|
-
prepended << File.read(file)
|
|
305
|
-
prepended << "\n"
|
|
306
|
-
end
|
|
307
|
-
|
|
308
136
|
@args.each do |file|
|
|
309
137
|
if file == $stdin
|
|
310
138
|
base_dir = Pathname.new(Dir.pwd).expand_path
|
|
311
139
|
input = $stdin.read
|
|
312
|
-
output = @
|
|
140
|
+
output = @output ? @output : $stdout
|
|
313
141
|
else
|
|
314
142
|
base_dir = Pathname.new(file).dirname.expand_path
|
|
315
143
|
input = File.read(file)
|
|
316
|
-
output = @
|
|
144
|
+
output = @output ? @output : Pathname.new(file).sub_ext('.ditamap').to_s
|
|
317
145
|
end
|
|
318
146
|
|
|
319
|
-
if @opts[:self] and file != $stdin
|
|
320
|
-
result =
|
|
147
|
+
if @converter.opts[:self] and file != $stdin
|
|
148
|
+
result = @converter.run input, base_dir, file
|
|
321
149
|
else
|
|
322
|
-
result =
|
|
150
|
+
result = @converter.run input, base_dir
|
|
323
151
|
end
|
|
324
152
|
|
|
325
153
|
if output == $stdout
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# Copyright (C) 2026 Jaromir Hradilek
|
|
2
|
+
|
|
3
|
+
# MIT License
|
|
4
|
+
#
|
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
# a copy of this software and associated documentation files (the "Soft-
|
|
7
|
+
# ware"), to deal in the Software without restriction, including without
|
|
8
|
+
# limitation the rights to use, copy, modify, merge, publish, distribute,
|
|
9
|
+
# sublicense, and/or sell copies of the Software, and to permit persons to
|
|
10
|
+
# whom the Software is furnished to do so, subject to the following condi-
|
|
11
|
+
# tions:
|
|
12
|
+
#
|
|
13
|
+
# The above copyright notice and this permission notice shall be included
|
|
14
|
+
# in all copies or substantial portions of the Software.
|
|
15
|
+
#
|
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
17
|
+
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABI-
|
|
18
|
+
# LITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
|
19
|
+
# SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
|
20
|
+
# OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
21
|
+
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
22
|
+
# OTHER DEALINGS IN THE SOFTWARE.
|
|
23
|
+
|
|
24
|
+
require 'rexml/document'
|
|
25
|
+
require_relative 'topic'
|
|
26
|
+
require_relative 'map'
|
|
27
|
+
|
|
28
|
+
module AsciidoctorDitaMap
|
|
29
|
+
class Convert
|
|
30
|
+
attr_accessor :attr, :opts, :prep
|
|
31
|
+
|
|
32
|
+
def initialize
|
|
33
|
+
@attr = []
|
|
34
|
+
@opts = {
|
|
35
|
+
:chunk => true,
|
|
36
|
+
:id => true,
|
|
37
|
+
:assembly => true,
|
|
38
|
+
:locktitle => true,
|
|
39
|
+
:navtitle => true,
|
|
40
|
+
:title => true,
|
|
41
|
+
:toc => true,
|
|
42
|
+
:type => true,
|
|
43
|
+
:self => false,
|
|
44
|
+
:verbose => false,
|
|
45
|
+
:zero_offset => false
|
|
46
|
+
}
|
|
47
|
+
@prep = ''
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def compose_mapref_attributes element, file_info, type
|
|
51
|
+
target_file = file_info[:target].sub(/\.adoc$/, '.ditamap')
|
|
52
|
+
|
|
53
|
+
element.add_attribute 'href', target_file
|
|
54
|
+
element.add_attribute 'format', 'ditamap'
|
|
55
|
+
element.add_attribute 'type', type if @opts[:type]
|
|
56
|
+
element.add_attribute 'chunk', file_info[:chunk] if @opts[:chunk] and file_info[:chunk]
|
|
57
|
+
element.add_attribute 'toc', file_info[:toc] if @opts[:toc] and file_info[:toc]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def compose_topicref_attributes element, file_info, title, type
|
|
61
|
+
target_file = file_info[:target].sub(/\.adoc$/, '.dita')
|
|
62
|
+
|
|
63
|
+
element.add_attribute 'href', target_file
|
|
64
|
+
|
|
65
|
+
if file_info[:navtitle]
|
|
66
|
+
element.add_attribute REXML::Attribute.new('navtitle', file_info[:navtitle]) if @opts[:navtitle]
|
|
67
|
+
else
|
|
68
|
+
element.add_attribute REXML::Attribute.new('navtitle', title) if @opts[:navtitle] and title
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
element.add_attribute 'locktitle', 'yes' if @opts[:locktitle] and element['navtitle']
|
|
72
|
+
element.add_attribute 'type', type if @opts[:type] and type and ['concept', 'reference', 'task'].include? type
|
|
73
|
+
element.add_attribute 'chunk', file_info[:chunk] if @opts[:chunk] and file_info[:chunk]
|
|
74
|
+
element.add_attribute 'toc', file_info[:toc] if @opts[:toc] and file_info[:toc]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def run input, base_dir, file = nil
|
|
78
|
+
result = ''
|
|
79
|
+
|
|
80
|
+
map = Map.new @prep + input, base_dir, @attr
|
|
81
|
+
|
|
82
|
+
xml = REXML::Document.new
|
|
83
|
+
xml.context[:attribute_quote] = :quote
|
|
84
|
+
xml << REXML::XMLDecl.new('1.0', 'utf-8')
|
|
85
|
+
xml << REXML::DocType.new('map', 'PUBLIC "-//OASIS//DTD DITA Map//EN" "map.dtd"')
|
|
86
|
+
|
|
87
|
+
if map.id and @opts[:id]
|
|
88
|
+
xml_root = xml.add_element('map', { 'id' => map.id })
|
|
89
|
+
else
|
|
90
|
+
xml_root = xml.add_element('map')
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
if map.title and @opts[:title]
|
|
94
|
+
xml_title = xml_root.add_element('title')
|
|
95
|
+
xml_title.add REXML::Text.new(map.title, false, nil, true)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
if @opts[:self] and file
|
|
99
|
+
xml_self = xml_root.add_element('topicref')
|
|
100
|
+
compose_topicref_attributes(xml_self, { :target => file }, map.title, map.type)
|
|
101
|
+
stack = [{ :offset => 0, :element => xml_self }]
|
|
102
|
+
else
|
|
103
|
+
stack = [{ :offset => 0, :element => xml_root }]
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
map.includes.each do |file_info|
|
|
107
|
+
target = file_info[:target]
|
|
108
|
+
offset = file_info[:offset]
|
|
109
|
+
last_offset = stack.last[:offset]
|
|
110
|
+
full_path = base_dir + target
|
|
111
|
+
|
|
112
|
+
if not File.exist? full_path and @opts[:verbose]
|
|
113
|
+
warn "#{NAME}: warning: file not found: #{target}"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
begin
|
|
117
|
+
topic = Topic.new @prep + File.read(full_path), @attr
|
|
118
|
+
next if ['attributes', 'snippet'].include? topic.type
|
|
119
|
+
rescue
|
|
120
|
+
warn "#{NAME}: warning: unable to read included file: #{target}"
|
|
121
|
+
topic = Topic.new ''
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
if offset == 0
|
|
125
|
+
if @opts[:zero_offset]
|
|
126
|
+
offset = 0
|
|
127
|
+
else
|
|
128
|
+
warn "#{NAME}: warning: invalid leveloffset - expected 1, got 0: #{target}"
|
|
129
|
+
offset = 1
|
|
130
|
+
end
|
|
131
|
+
elsif offset > last_offset and offset - last_offset > 1
|
|
132
|
+
expected_offset = last_offset + 1
|
|
133
|
+
warn "#{NAME}: warning: invalid leveloffset - expected #{expected_offset}, got #{offset}: #{target}"
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
while stack.length > 1 and stack.last[:offset] >= offset
|
|
137
|
+
stack.pop
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
xml_parent = stack.last[:element]
|
|
141
|
+
|
|
142
|
+
topic.type.sub!(/^assembly$/, 'concept') if not @opts[:assembly]
|
|
143
|
+
|
|
144
|
+
if topic.type == 'map' or topic.type == 'assembly'
|
|
145
|
+
xml_element = xml_parent.add_element('mapref')
|
|
146
|
+
compose_mapref_attributes xml_element, file_info, 'map'
|
|
147
|
+
else
|
|
148
|
+
xml_element = xml_parent.add_element('topicref')
|
|
149
|
+
compose_topicref_attributes xml_element, file_info, topic.title, topic.type
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
stack.push ({ :offset => offset, :element => xml_element })
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
formatter = REXML::Formatters::Pretty.new(2, true)
|
|
156
|
+
formatter.compact = true
|
|
157
|
+
formatter.write(xml, result)
|
|
158
|
+
|
|
159
|
+
result << "\n"
|
|
160
|
+
|
|
161
|
+
return result
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
data/lib/dita-map/map.rb
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Copyright (C) 2026 Jaromir Hradilek
|
|
2
|
+
|
|
3
|
+
# MIT License
|
|
4
|
+
#
|
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
# a copy of this software and associated documentation files (the "Soft-
|
|
7
|
+
# ware"), to deal in the Software without restriction, including without
|
|
8
|
+
# limitation the rights to use, copy, modify, merge, publish, distribute,
|
|
9
|
+
# sublicense, and/or sell copies of the Software, and to permit persons to
|
|
10
|
+
# whom the Software is furnished to do so, subject to the following condi-
|
|
11
|
+
# tions:
|
|
12
|
+
#
|
|
13
|
+
# The above copyright notice and this permission notice shall be included
|
|
14
|
+
# in all copies or substantial portions of the Software.
|
|
15
|
+
#
|
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
17
|
+
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABI-
|
|
18
|
+
# LITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
|
19
|
+
# SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
|
20
|
+
# OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
21
|
+
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
22
|
+
# OTHER DEALINGS IN THE SOFTWARE.
|
|
23
|
+
|
|
24
|
+
require 'asciidoctor'
|
|
25
|
+
require_relative 'catalog'
|
|
26
|
+
require_relative 'topic'
|
|
27
|
+
|
|
28
|
+
module AsciidoctorDitaMap
|
|
29
|
+
class Map < Topic
|
|
30
|
+
attr_accessor :id, :includes
|
|
31
|
+
|
|
32
|
+
def initialize input, base_dir, attributes = []
|
|
33
|
+
if input.empty?
|
|
34
|
+
@id = nil
|
|
35
|
+
@title = nil
|
|
36
|
+
@type = nil
|
|
37
|
+
@includes = []
|
|
38
|
+
else
|
|
39
|
+
Asciidoctor::Extensions.register do
|
|
40
|
+
include_processor CatalogIncludeDirectives
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
doc = Asciidoctor.load input, safe: :safe, logger: false, catalog_assets: true, attributes: attributes, base_dir: base_dir
|
|
44
|
+
|
|
45
|
+
@includes = doc.catalog[:include_files] ? doc.catalog[:include_files] : []
|
|
46
|
+
@id = doc.id ? doc.id.gsub(/["']/, '') : nil
|
|
47
|
+
@title = doc.title ? doc.title.gsub(/<[^>]*>/, '') : nil
|
|
48
|
+
@type = get_content_type doc.attributes
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Copyright (C) 2026 Jaromir Hradilek
|
|
2
|
+
|
|
3
|
+
# MIT License
|
|
4
|
+
#
|
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
# a copy of this software and associated documentation files (the "Soft-
|
|
7
|
+
# ware"), to deal in the Software without restriction, including without
|
|
8
|
+
# limitation the rights to use, copy, modify, merge, publish, distribute,
|
|
9
|
+
# sublicense, and/or sell copies of the Software, and to permit persons to
|
|
10
|
+
# whom the Software is furnished to do so, subject to the following condi-
|
|
11
|
+
# tions:
|
|
12
|
+
#
|
|
13
|
+
# The above copyright notice and this permission notice shall be included
|
|
14
|
+
# in all copies or substantial portions of the Software.
|
|
15
|
+
#
|
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
17
|
+
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABI-
|
|
18
|
+
# LITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
|
19
|
+
# SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
|
20
|
+
# OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
21
|
+
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
22
|
+
# OTHER DEALINGS IN THE SOFTWARE.
|
|
23
|
+
|
|
24
|
+
require 'asciidoctor'
|
|
25
|
+
|
|
26
|
+
module AsciidoctorDitaMap
|
|
27
|
+
class Topic
|
|
28
|
+
attr_accessor :title, :type
|
|
29
|
+
|
|
30
|
+
def initialize input, attributes = []
|
|
31
|
+
if input.empty?
|
|
32
|
+
@title = nil
|
|
33
|
+
@type = nil
|
|
34
|
+
else
|
|
35
|
+
doc = Asciidoctor.load input, safe: :secure, logger: false, attributes: attributes
|
|
36
|
+
|
|
37
|
+
@title = doc.title ? doc.title.gsub(/<[^>]*>/, '') : nil
|
|
38
|
+
@type = get_content_type doc.attributes
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def get_content_type attributes
|
|
45
|
+
type = attributes['_mod-docs-content-type'] ? attributes['_mod-docs-content-type'].downcase : nil
|
|
46
|
+
type = attributes['_content-type'] ? attributes['_content-type'].downcase : nil unless type
|
|
47
|
+
type = attributes['_module-type'] ? attributes['_module-type'].downcase : nil unless type
|
|
48
|
+
|
|
49
|
+
if type
|
|
50
|
+
type.sub!(/^procedure$/, 'task')
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
unless ['assembly', 'concept', 'reference', 'task', 'map', 'attributes', 'snippet'].include? type
|
|
54
|
+
return nil
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
return type
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
data/lib/dita-map/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: asciidoctor-dita-map
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.9.
|
|
4
|
+
version: 0.9.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jaromir Hradilek
|
|
@@ -117,6 +117,9 @@ files:
|
|
|
117
117
|
- bin/dita-map
|
|
118
118
|
- lib/dita-map/catalog.rb
|
|
119
119
|
- lib/dita-map/cli.rb
|
|
120
|
+
- lib/dita-map/convert.rb
|
|
121
|
+
- lib/dita-map/map.rb
|
|
122
|
+
- lib/dita-map/topic.rb
|
|
120
123
|
- lib/dita-map/version.rb
|
|
121
124
|
homepage: https://github.com/jhradilek/asciidoctor-dita-map
|
|
122
125
|
licenses:
|