asciidoctor-iso 0.0.1

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.
@@ -0,0 +1,53 @@
1
+ require "asciidoctor"
2
+
3
+ require "asciidoctor/iso/version"
4
+ require "asciidoctor/iso/base"
5
+ require "asciidoctor/iso/front"
6
+ require "asciidoctor/iso/lists"
7
+ require "asciidoctor/iso/inline_anchor"
8
+ require "asciidoctor/iso/blocks"
9
+ require "asciidoctor/iso/table"
10
+ require "asciidoctor/iso/validate"
11
+ require "asciidoctor/iso/utils"
12
+
13
+ module Asciidoctor
14
+ module ISO
15
+ # A {Converter} implementation that generates ISO output, and a document
16
+ # schema encapsulation of the document for validation
17
+ class Converter
18
+ include ::Asciidoctor::Converter
19
+ include ::Asciidoctor::Writer
20
+
21
+ include ::Asciidoctor::ISO::Base
22
+ include ::Asciidoctor::ISO::Front
23
+ include ::Asciidoctor::ISO::Lists
24
+ include ::Asciidoctor::ISO::InlineAnchor
25
+ include ::Asciidoctor::ISO::Blocks
26
+ include ::Asciidoctor::ISO::Table
27
+ include ::Asciidoctor::ISO::Utils
28
+ include ::Asciidoctor::ISO::Validate
29
+
30
+ register_for "iso"
31
+
32
+ $xreftext = {}
33
+
34
+ def initialize(backend, opts)
35
+ super
36
+ basebackend "html"
37
+ outfilesuffix ".xml"
38
+ end
39
+
40
+ # alias_method :pass, :content
41
+ alias_method :embedded, :content
42
+ alias_method :verse, :content
43
+ alias_method :literal, :content
44
+ alias_method :audio, :skip
45
+ alias_method :thematic_break, :skip
46
+ alias_method :video, :skip
47
+ alias_method :inline_button, :skip
48
+ alias_method :inline_kbd, :skip
49
+ alias_method :inline_menu, :skip
50
+ alias_method :inline_image, :skip
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,80 @@
1
+ require "date"
2
+ require "nokogiri"
3
+ require "htmlentities"
4
+ require "json"
5
+ require "pathname"
6
+ require "open-uri"
7
+ require "pp"
8
+
9
+ module Asciidoctor
10
+ module ISO
11
+ module Front
12
+ def metadata_id(node, xml)
13
+ xml.id do |i|
14
+ i.documentnumber node.attr("docnumber"),
15
+ **attr_code(partnumber: node.attr("partnumber"))
16
+ if node.attr("tc-docnumber")
17
+ i.tc_documentnumber node.attr("tc-docnumber")
18
+ end
19
+ if node.attr("ref-docnumber")
20
+ i.ref_documentnumber node.attr("ref-docnumber")
21
+ end
22
+ end
23
+ end
24
+
25
+ def metadata_version(node, xml)
26
+ xml.version do |v|
27
+ v.edition node.attr("edition") if node.attr("edition")
28
+ v.revdate node.attr("revdate") if node.attr("revdate")
29
+ if node.attr("copyright-year")
30
+ v.copyright_year node.attr("copyright-year")
31
+ end
32
+ end
33
+ end
34
+
35
+ def metadata_author(node, xml)
36
+ xml.author do |a|
37
+ a.technical_committee node.attr("technical-committee"),
38
+ **attr_code(number: node.attr("technical-committee-number"))
39
+ if node.attr("subcommittee")
40
+ a.subcommittee node.attr("subcommittee"),
41
+ **attr_code(number: node.attr("subcommittee-number"))
42
+ end
43
+ if node.attr("workgroup")
44
+ a.workgroup node.attr("workgroup"),
45
+ **attr_code(number: node.attr("workgroup-number"))
46
+ end
47
+ a.secretariat node.attr("secretariat") if node.attr("secretariat")
48
+ end
49
+ end
50
+
51
+ def metadata(node, xml)
52
+ xml.documenttype node.attr("doctype")
53
+ xml.documentstatus do |s|
54
+ s.stage node.attr("docstage")
55
+ s.substage node.attr("docsubstage") if node.attr("docsubstage")
56
+ end
57
+ metadata_id(node, xml)
58
+ xml.language node.attr("language")
59
+ metadata_version(node, xml)
60
+ metadata_author(node, xml)
61
+ end
62
+
63
+ def title(node, xml)
64
+ xml.title do |t0|
65
+ ["en", "fr"].each do |lang|
66
+ t0.send lang do |t|
67
+ if node.attr("title-intro-#{lang}")
68
+ t.title_intro { |t1| t1 << node.attr("title-intro-#{lang}") }
69
+ end
70
+ t.title_main { |t1| t1 << node.attr("title-main-#{lang}") }
71
+ if node.attr("title-part-#{lang}")
72
+ t.title_part node.attr("title-part-#{lang}")
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,65 @@
1
+ module Asciidoctor
2
+ module ISO
3
+ module InlineAnchor
4
+ def inline_anchor(node)
5
+ case node.type
6
+ when :xref
7
+ inline_anchor_xref node
8
+ when :link
9
+ inline_anchor_link node
10
+ when :bibref
11
+ inline_anchor_bibref node
12
+ else
13
+ warn %(asciidoctor: WARNING (#{current_location(node)}): unknown anchor type: #{node.type.inspect})
14
+ end
15
+ end
16
+
17
+ def inline_anchor_xref(node)
18
+ matched = /^fn(: (?<text>.*))?$/.match node.text
19
+ if matched.nil?
20
+ format = "inline"
21
+ xref_contents = node.text
22
+ else
23
+ format = "footnote"
24
+ xref_contents = matched[:text]
25
+ end
26
+ xref_attributes = {
27
+ target: node.target.gsub(/^#/, "").gsub(/(.)(\.xml)?#.*$/, "\\1"),
28
+ format: format,
29
+ }
30
+
31
+ noko do |xml|
32
+ xml.xref xref_contents, **attr_code(xref_attributes)
33
+ end.join
34
+ end
35
+
36
+ def inline_anchor_link(node)
37
+ eref_contents = node.target == node.text ? nil : node.text
38
+ eref_attributes = {
39
+ target: node.target,
40
+ }
41
+
42
+ noko do |xml|
43
+ xml.eref eref_contents, **attr_code(eref_attributes)
44
+ end.join
45
+ end
46
+
47
+ def inline_anchor_bibref(node)
48
+ eref_contents = node.target == node.text ? nil : node.text
49
+ eref_attributes = {
50
+ anchor: node.target,
51
+ }
52
+
53
+ noko do |xml|
54
+ xml.ref eref_contents, **attr_code(eref_attributes)
55
+ end.join
56
+ end
57
+
58
+ def inline_callout(node)
59
+ noko do |xml|
60
+ xml.ref node.text
61
+ end.join
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,158 @@
1
+ require "pp"
2
+ module Asciidoctor
3
+ module ISO
4
+ module Lists
5
+ def ulist(node)
6
+ return norm_ref(node) if $norm_ref
7
+ return biblio_ref(node) if $biblio
8
+ noko do |xml|
9
+ xml.ul **attr_code(anchor: node.id) do |xml_ul|
10
+ node.items.each do |item|
11
+ xml_ul.li **attr_code(anchor: item.id) do |xml_li|
12
+ if item.blocks?
13
+ xml_li.p { |t| t << item.text }
14
+ xml_li << item.content
15
+ else
16
+ xml_li.p { |p| p << item.text }
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end.join
22
+ end
23
+
24
+ def isorefmatches(xml, matched)
25
+ ref_attributes = {
26
+ anchor: matched[:anchor],
27
+ }
28
+ xml.iso_ref_title **attr_code(ref_attributes) do |t|
29
+ t.isocode matched[:code]
30
+ t.isodate matched[:year] if matched[:year]
31
+ t.isotitle { |i| i << ref_normalise(matched[:text]) }
32
+ end
33
+ end
34
+
35
+ def isorefmatches2(xml, matched2)
36
+ ref_attributes = {
37
+ anchor: matched2[:anchor],
38
+ }
39
+ xml.iso_ref_title **attr_code(ref_attributes) do |t|
40
+ t.isocode matched2[:code]
41
+ t.isodate "--"
42
+ t.date_footnote matched2[:fn]
43
+ t.isotitle { |i| i << ref_normalise(matched2[:text]) }
44
+ end
45
+ end
46
+
47
+ def ref_normalise(ref)
48
+ ref.gsub(/&#8201;&#8212;&#8201;/, " -- ").
49
+ gsub(/&amp;amp;/, "&amp;")
50
+ end
51
+
52
+ def norm_ref(node)
53
+ noko do |xml|
54
+ node.items.each do |item|
55
+ matched = %r{^<ref\sanchor="(?<anchor>[^"]+)">
56
+ \[ISO\s(?<code>[0-9-]+)(:(?<year>[0-9]+))?\]</ref>,?\s
57
+ (?<text>.*)$}x.match item.text
58
+ matched2 = %r{^<ref\sanchor="(?<anchor>[^"]+)">
59
+ \[ISO\s(?<code>[0-9-]+):--\]</ref>,?\s?
60
+ <fn>(?<fn>[^\]]+)</fn>,?\s?(?<text>.*)$}x.match item.text
61
+ if matched2.nil?
62
+ if matched.nil?
63
+ warn %(asciidoctor: WARNING (#{current_location(node)}): normative reference not in expected format: #{item.text})
64
+ else
65
+ isorefmatches(xml, matched)
66
+ end
67
+ else
68
+ isorefmatches2(xml, matched2)
69
+ end
70
+ end
71
+ end.join
72
+ end
73
+
74
+ def biblio_ref(node)
75
+ noko do |xml|
76
+ node.items.each do |item|
77
+ matched = %r{^<ref\sanchor="(?<anchor>[^"]+)">
78
+ \[ISO\s(?<code>[0-9-]+)(:(?<year>[0-9]+))?\]</ref>,?\s
79
+ (?<text>.*)$}.match item.text
80
+ matched2 = %r{^<ref\sanchor="(?<anchor>[^"]+)">
81
+ \[ISO\s(?<code>[0-9-]+):--\]</ref>,?\s?
82
+ <fn>(?<fn>[^\]]+)</fn>,?\s?(?<text>.*)$}.match item.text
83
+ if matched2.nil?
84
+ if matched.nil?
85
+ xml.reference do |t|
86
+ t.p { |p| p << ref_normalise(item.text) }
87
+ end
88
+ else
89
+ isorefmatches(xml, matched)
90
+ end
91
+ else
92
+ isorefmatches2(xml, matched2)
93
+ end
94
+ end
95
+ end.join
96
+ end
97
+
98
+ def olist(node)
99
+ noko do |xml|
100
+ xml.ol **attr_code(anchor: node.id, type: node.style) do |xml_ol|
101
+ node.items.each do |item|
102
+ xml_ol.li **attr_code(anchor: item.id) do |xml_li|
103
+ if item.blocks?
104
+ xml_li.p { |t| t << item.text }
105
+ xml_li << item.content
106
+ else
107
+ xml_li.p { |p| p << item.text }
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end.join
113
+ end
114
+
115
+ def dlist(node)
116
+ noko do |xml|
117
+ xml.dl **attr_code(anchor: node.id) do |xml_dl|
118
+ node.items.each do |terms, dd|
119
+ terms.each_with_index do |dt, idx|
120
+ xml_dl.dt { |xml_dt| xml_dt << dt.text }
121
+ if idx < terms.size - 1
122
+ xml_dl.dd
123
+ end
124
+ end
125
+
126
+ if dd.nil?
127
+ xml_dl.dd
128
+ else
129
+ xml_dl.dd do |xml_dd|
130
+ if dd.blocks?
131
+ if dd.text?
132
+ xml_dd.p { |t| t << dd.text }
133
+ end
134
+ xml_dd << dd.content
135
+ else
136
+ xml_dd.p { |t| t << dd.text }
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end.join
143
+ end
144
+
145
+ def colist(node)
146
+ noko do |xml|
147
+ xml.colist **attr_code(anchor: node.id) do |xml_ul|
148
+ node.items.each_with_index do |item, i|
149
+ xml_ul.annotation **attr_code(id: i + 1) do |xml_li|
150
+ xml_li << item.text
151
+ end
152
+ end
153
+ end
154
+ end.join
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,53 @@
1
+ module Asciidoctor
2
+ module ISO
3
+ module Table
4
+ def table(node)
5
+ noko do |xml|
6
+ has_body = false
7
+ xml.table **attr_code(anchor: node.id) do |xml_table|
8
+ %i(head body foot).reject do |tblsec|
9
+ node.rows[tblsec].empty?
10
+ end.each do |tblsec|
11
+ has_body = true if tblsec == :body
12
+ end
13
+ xml_table.name node.title if node.title?
14
+ table_head_body_and_foot node, xml_table
15
+ end
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def table_head_body_and_foot(node, xml)
22
+ %i(head body foot).reject do |tblsec|
23
+ node.rows[tblsec].empty?
24
+ end.each do |tblsec|
25
+ tblsec_tag = "t#{tblsec}"
26
+ # "anchor" attribute from tblsec.id not supported
27
+ xml.send tblsec_tag do |xml_tblsec|
28
+ node.rows[tblsec].each_with_index do |row, i|
29
+ xml_tblsec.tr do |xml_tr|
30
+ rowlength = 0
31
+ row.each do |cell|
32
+ cell_attributes = {
33
+ anchor: cell.id,
34
+ colspan: cell.colspan,
35
+ rowspan: cell.rowspan,
36
+ align: cell.attr("halign"),
37
+ }
38
+
39
+ cell_tag = "td"
40
+ cell_tag = "th" if tblsec == :head || cell.style == :header
41
+ rowlength += cell.text.size
42
+ xml_tr.send cell_tag, **attr_code(cell_attributes) do |thd|
43
+ thd << (cell.style == :asciidoc ? cell.content : cell.text)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,224 @@
1
+ require "date"
2
+ require "nokogiri"
3
+ require "htmlentities"
4
+ require "json"
5
+ require "pathname"
6
+ require "open-uri"
7
+ require "pp"
8
+
9
+ module Asciidoctor
10
+ module ISO
11
+ module Utils
12
+ def convert(node, transform = nil, opts = {})
13
+ transform ||= node.node_name
14
+ opts.empty? ? (send transform, node) : (send transform, node, opts)
15
+ end
16
+
17
+ def document_ns_attributes(_doc)
18
+ # ' xmlns="http://riboseinc.com/isoxml"'
19
+ nil
20
+ end
21
+
22
+ def cleanup(xmldoc)
23
+ intro_cleanup(xmldoc)
24
+ termdef_cleanup(xmldoc)
25
+ isotitle_cleanup(xmldoc)
26
+ tablenote_cleanup(xmldoc)
27
+ formula_cleanup(xmldoc)
28
+ figure_cleanup(xmldoc)
29
+ back_cleanup(xmldoc)
30
+ ref_cleanup(xmldoc)
31
+ end
32
+
33
+ def intro_cleanup(xmldoc)
34
+ intro = xmldoc.at("//introduction")
35
+ foreword = xmldoc.at("//foreword")
36
+ front = xmldoc.at("//front")
37
+ unless foreword.nil? || front.nil?
38
+ foreword.remove
39
+ front << foreword
40
+ end
41
+ unless intro.nil? || front.nil?
42
+ intro.remove
43
+ front << intro
44
+ end
45
+ end
46
+
47
+ def termdef_cleanup(xmldoc)
48
+ # release termdef tags from surrounding paras
49
+ nodes = xmldoc.xpath("//p/admitted_term | //p/termsymbol |
50
+ //p/deprecated_term")
51
+ while !nodes.empty?
52
+ nodes[0].parent.replace(nodes[0].parent.children)
53
+ nodes = xmldoc.xpath("//p/admitted_term | //p/termsymbol |
54
+ //p/deprecated_term")
55
+ end
56
+ xmldoc.xpath("//termdef/p/stem").each do |a|
57
+ if a.parent.elements.size == 1
58
+ # para containing just a stem expression
59
+ t = Nokogiri::XML::Element.new("termsymbol", xmldoc)
60
+ parent = a.parent
61
+ a.remove
62
+ t.children = a
63
+ parent.replace(t)
64
+ end
65
+ end
66
+ xmldoc.xpath("//p/termdomain").each do |a|
67
+ prev = a.parent.previous
68
+ a.remove
69
+ prev.next = a
70
+ end
71
+ end
72
+
73
+ def isotitle_cleanup(xmldoc)
74
+ # Remove italicised ISO titles
75
+ xmldoc.xpath("//isotitle").each do |a|
76
+ if a.elements.size == 1 && a.elements[0].name == "em"
77
+ a.children = a.elements[0].children
78
+ end
79
+ end
80
+ end
81
+
82
+ def tablenote_cleanup(xmldoc)
83
+ # move notes after table footer
84
+ xmldoc.xpath("//tfoot/tr/td/note | //tfoot/tr/th/note").each do |n|
85
+ target = n.parent.parent.parent.parent
86
+ n.remove
87
+ target << n
88
+ end
89
+ end
90
+
91
+ def formula_cleanup(xmldoc)
92
+ # include where definition list inside stem block
93
+ xmldoc.xpath("//formula").each do |s|
94
+ if !s.next_element.nil? && s.next_element.name == "p" &&
95
+ s.next_element.content == "where" &&
96
+ !s.next_element.next_element.nil? &&
97
+ s.next_element.next_element.name == "dl"
98
+ dl = s.next_element.next_element.remove
99
+ s.next_element.remove
100
+ s << dl
101
+ end
102
+ end
103
+ end
104
+
105
+ def figure_cleanup(xmldoc)
106
+ # include key definition list inside figure
107
+ xmldoc.xpath("//figure").each do |s|
108
+ if !s.next_element.nil? && s.next_element.name == "p" &&
109
+ s.next_element.content =~ /^\s*Key\s*$/m &&
110
+ !s.next_element.next_element.nil? &&
111
+ s.next_element.next_element.name == "dl"
112
+ dl = s.next_element.next_element.remove
113
+ s.next_element.remove
114
+ s << dl
115
+ end
116
+ end
117
+
118
+ # examples containing only figures become subfigures of figures
119
+ nodes = xmldoc.xpath("//example/figure")
120
+ while !nodes.empty?
121
+ nodes[0].parent.name = "figure"
122
+ nodes = xmldoc.xpath("//example/figure")
123
+ end
124
+ end
125
+
126
+ def back_cleanup(xmldoc)
127
+ # move annex/bibliography to back
128
+ if !xmldoc.xpath("//annex | //bibliography").empty?
129
+ b = Nokogiri::XML::Element.new("back", xmldoc)
130
+ xmldoc.root << b
131
+ xmldoc.xpath("//annex").each do |e|
132
+ e.remove
133
+ b << e
134
+ end
135
+ xmldoc.xpath("//bibliography").each do |e|
136
+ e.remove
137
+ b << e
138
+ end
139
+ end
140
+ end
141
+
142
+ def ref_cleanup(xmldoc)
143
+ # move ref before p
144
+ xmldoc.xpath("//p/ref").each do |r|
145
+ parent = r.parent
146
+ r.remove
147
+ parent.previous = r
148
+ end
149
+
150
+ xmldoc
151
+ end
152
+
153
+ # block for processing XML document fragments as XHTML,
154
+ # to allow for HTMLentities
155
+ def noko(&block)
156
+ # fragment = ::Nokogiri::XML::DocumentFragment.parse("")
157
+ # fragment.doc.create_internal_subset("xml", nil, "xhtml.dtd")
158
+ head = <<HERE
159
+ <!DOCTYPE html SYSTEM
160
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
161
+ <html xmlns="http://www.w3.org/1999/xhtml">
162
+ <head> <title></title> <meta charset="UTF-8" /> </head>
163
+ <body> </body> </html>
164
+ HERE
165
+ doc = ::Nokogiri::XML.parse(head)
166
+ fragment = doc.fragment("")
167
+ ::Nokogiri::XML::Builder.with fragment, &block
168
+ fragment.to_xml(encoding: "US-ASCII").lines.map do |l|
169
+ l.gsub(/\s*\n/, "")
170
+ end
171
+ end
172
+
173
+ def attr_code(attributes)
174
+ attributes = attributes.reject { |_, val| val.nil? }.map
175
+ attributes.map do |k, v|
176
+ [k, (v.is_a? String) ? HTMLEntities.new.decode(v) : v]
177
+ end.to_h
178
+ end
179
+
180
+ def current_location(node)
181
+ if node.respond_to?(:lineno) && !node.lineno.nil? &&
182
+ !node.lineno.empty?
183
+ return "Line #{node.lineno}"
184
+ end
185
+ if node.respond_to?(:id) && !node.id.nil?
186
+ return "ID #{node.id}"
187
+ end
188
+ while !node.nil? && (!node.respond_to?(:level) ||
189
+ node.level.positive?) && node.context != :section
190
+ node = node.parent
191
+ if !node.nil? && node.context == :section
192
+ return "Section: #{node.title}"
193
+ end
194
+ end
195
+ "??"
196
+ end
197
+
198
+ # if node contains blocks, flatten them into a single line;
199
+ # and extract only raw text
200
+ def flatten_rawtext(node)
201
+ result = []
202
+ if node.respond_to?(:blocks) && node.blocks?
203
+ node.blocks.each { |b| result << flatten_rawtext(b) }
204
+ elsif node.respond_to?(:lines)
205
+ node.lines.each do |x|
206
+ if node.respond_to?(:context) && (node.context == :literal ||
207
+ node.context == :listing)
208
+ result << x.gsub(/</, "&lt;").gsub(/>/, "&gt;")
209
+ else
210
+ # strip not only HTML tags <tag>,
211
+ # but also Asciidoc crossreferences <<xref>>
212
+ result << x.gsub(/<[^>]*>+/, "")
213
+ end
214
+ end
215
+ elsif node.respond_to?(:text)
216
+ result << node.text.gsub(/<[^>]*>+/, "")
217
+ else
218
+ result << node.content.gsub(/<[^>]*>+/, "")
219
+ end
220
+ result.reject(&:empty?)
221
+ end
222
+ end
223
+ end
224
+ end