asciidoctor-iso 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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