metanorma-standoc 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +4 -0
  3. data/.gitignore +11 -0
  4. data/.hound.yml +3 -0
  5. data/.oss-guides.rubocop.yml +1077 -0
  6. data/.rubocop.ribose.yml +66 -0
  7. data/.rubocop.tb.yml +650 -0
  8. data/.rubocop.yml +15 -0
  9. data/.travis.yml +21 -0
  10. data/CODE_OF_CONDUCT.md +46 -0
  11. data/Gemfile +7 -0
  12. data/LICENSE +25 -0
  13. data/Makefile +39 -0
  14. data/README.adoc +9 -0
  15. data/Rakefile +6 -0
  16. data/bin/rspec +18 -0
  17. data/docs/customisation.adoc +178 -0
  18. data/docs/guidance.adoc +436 -0
  19. data/docs/htmloutput.adoc +115 -0
  20. data/docs/quickstart.adoc +375 -0
  21. data/lib/asciidoctor/standoc/base.rb +198 -0
  22. data/lib/asciidoctor/standoc/biblio.rng +836 -0
  23. data/lib/asciidoctor/standoc/blocks.rb +190 -0
  24. data/lib/asciidoctor/standoc/cleanup.rb +247 -0
  25. data/lib/asciidoctor/standoc/cleanup_block.rb +193 -0
  26. data/lib/asciidoctor/standoc/cleanup_footnotes.rb +78 -0
  27. data/lib/asciidoctor/standoc/cleanup_ref.rb +125 -0
  28. data/lib/asciidoctor/standoc/converter.rb +55 -0
  29. data/lib/asciidoctor/standoc/front.rb +121 -0
  30. data/lib/asciidoctor/standoc/inline.rb +134 -0
  31. data/lib/asciidoctor/standoc/isodoc.rng +1059 -0
  32. data/lib/asciidoctor/standoc/lists.rb +87 -0
  33. data/lib/asciidoctor/standoc/macros.rb +95 -0
  34. data/lib/asciidoctor/standoc/ref.rb +187 -0
  35. data/lib/asciidoctor/standoc/section.rb +159 -0
  36. data/lib/asciidoctor/standoc/table.rb +61 -0
  37. data/lib/asciidoctor/standoc/utils.rb +121 -0
  38. data/lib/asciidoctor/standoc/validate.rb +65 -0
  39. data/lib/asciidoctor/standoc/validate_section.rb +42 -0
  40. data/lib/asciidoctor/standoc/version.rb +5 -0
  41. data/lib/metanorma-standoc.rb +9 -0
  42. data/lib/metanorma/standoc.rb +7 -0
  43. data/lib/metanorma/standoc/processor.rb +40 -0
  44. data/metanorma-standoc.gemspec +47 -0
  45. data/spec/asciidoctor-standoc/base_spec.rb +271 -0
  46. data/spec/asciidoctor-standoc/blocks_spec.rb +469 -0
  47. data/spec/asciidoctor-standoc/cleanup_spec.rb +760 -0
  48. data/spec/asciidoctor-standoc/inline_spec.rb +162 -0
  49. data/spec/asciidoctor-standoc/isobib_cache_spec.rb +332 -0
  50. data/spec/asciidoctor-standoc/lists_spec.rb +190 -0
  51. data/spec/asciidoctor-standoc/macros_spec.rb +111 -0
  52. data/spec/asciidoctor-standoc/refs_spec.rb +606 -0
  53. data/spec/asciidoctor-standoc/section_spec.rb +310 -0
  54. data/spec/asciidoctor-standoc/table_spec.rb +307 -0
  55. data/spec/asciidoctor-standoc/validate_spec.rb +133 -0
  56. data/spec/assets/header.html +7 -0
  57. data/spec/assets/html.css +2 -0
  58. data/spec/assets/htmlcover.html +4 -0
  59. data/spec/assets/htmlintro.html +5 -0
  60. data/spec/assets/i18n.yaml +2 -0
  61. data/spec/assets/iso.headless.html +33 -0
  62. data/spec/assets/iso.xml +8 -0
  63. data/spec/assets/rice_image1.png +0 -0
  64. data/spec/assets/scripts.html +3 -0
  65. data/spec/assets/std.css +2 -0
  66. data/spec/assets/word.css +2 -0
  67. data/spec/assets/wordcover.html +3 -0
  68. data/spec/assets/wordintro.html +4 -0
  69. data/spec/examples/103_01_02.html +247 -0
  70. data/spec/examples/english.yaml +69 -0
  71. data/spec/examples/iso_123_.xml +45 -0
  72. data/spec/examples/iso_123_all_parts.xml +45 -0
  73. data/spec/examples/iso_123_no_year_note.xml +46 -0
  74. data/spec/examples/iso_124_.xml +41 -0
  75. data/spec/examples/iso_216_.xml +47 -0
  76. data/spec/examples/iso_iec_12382_.xml +48 -0
  77. data/spec/examples/rice.adoc +715 -0
  78. data/spec/examples/rice.preview.html +1877 -0
  79. data/spec/examples/rice.sh +4 -0
  80. data/spec/examples/rice_images/rice_image1.png +0 -0
  81. data/spec/examples/rice_images/rice_image2.png +0 -0
  82. data/spec/examples/rice_images/rice_image3_1.png +0 -0
  83. data/spec/examples/rice_images/rice_image3_2.png +0 -0
  84. data/spec/examples/rice_images/rice_image3_3.png +0 -0
  85. data/spec/metanorma/processor_spec.rb +70 -0
  86. data/spec/spec_helper.rb +198 -0
  87. metadata +370 -0
@@ -0,0 +1,190 @@
1
+ require "htmlentities"
2
+ require "uri"
3
+
4
+ module Asciidoctor
5
+ module Standoc
6
+ module Blocks
7
+ def id_attr(node = nil)
8
+ { id: Utils::anchor_or_uuid(node) }
9
+ end
10
+
11
+ # open block is a container of multiple blocks,
12
+ # treated as a single block.
13
+ # We append each contained block to its parent
14
+ def open(node)
15
+ result = []
16
+ node.blocks.each do |b|
17
+ result << send(b.context, b)
18
+ end
19
+ result
20
+ end
21
+
22
+ def literal(node)
23
+ paragraph(node)
24
+ end
25
+
26
+ # NOTE: html escaping is performed by Nokogiri
27
+ def stem(node)
28
+ stem_content = node.lines.join("\n")
29
+ noko do |xml|
30
+ xml.formula **id_attr(node) do |s|
31
+ stem_parse(stem_content, s)
32
+ end
33
+ end
34
+ end
35
+
36
+ def sidebar_attrs(node)
37
+ date = node.attr("date") || Date.today.iso8601.gsub(/\+.*$/, "")
38
+ date += "T00:00:00Z" unless /T/.match date
39
+ {
40
+ reviewer: node.attr("reviewer") || node.attr("source") || "(Unknown)",
41
+ id: Utils::anchor_or_uuid(node),
42
+ date: date,
43
+ from: node.attr("from"),
44
+ to: node.attr("to") || node.attr("from"),
45
+ }
46
+ end
47
+
48
+ def sidebar(node)
49
+ return unless draft?
50
+ noko do |xml|
51
+ xml.review **attr_code(sidebar_attrs(node)) do |r|
52
+ wrap_in_para(node, r)
53
+ end
54
+ end
55
+ end
56
+
57
+ def termnote(n)
58
+ noko do |xml|
59
+ xml.termnote **id_attr(n) do |ex|
60
+ wrap_in_para(n, ex)
61
+ end
62
+ end.join("\n")
63
+ end
64
+
65
+ def note(n)
66
+ noko do |xml|
67
+ xml.note **id_attr(n) do |c|
68
+ wrap_in_para(n, c)
69
+ end
70
+ end.join("\n")
71
+ end
72
+
73
+ def admonition_attrs(node)
74
+ name = node.attr("name")
75
+ if type = node.attr("type")
76
+ ["danger", "safety precautions"].each do |t|
77
+ name = t if type.casecmp(t).zero?
78
+ end
79
+ end
80
+ { id: Utils::anchor_or_uuid(node), type: name }
81
+ end
82
+
83
+ def admonition(node)
84
+ return termnote(node) if in_terms?
85
+ return note(node) if node.attr("name") == "note"
86
+ noko do |xml|
87
+ xml.admonition **admonition_attrs(node) do |a|
88
+ wrap_in_para(node, a)
89
+ end
90
+ end.join("\n")
91
+ end
92
+
93
+ def term_example(node)
94
+ noko do |xml|
95
+ xml.termexample **id_attr(node) do |ex|
96
+ wrap_in_para(node, ex)
97
+ end
98
+ end.join("\n")
99
+ end
100
+
101
+ def example(node)
102
+ return term_example(node) if in_terms?
103
+ noko do |xml|
104
+ xml.example **id_attr(node) do |ex|
105
+ content = node.content
106
+ ex << content
107
+ end
108
+ end.join("\n")
109
+ end
110
+
111
+ def preamble(node)
112
+ noko do |xml|
113
+ xml.foreword do |xml_abstract|
114
+ xml_abstract.title { |t| t << "Foreword" }
115
+ content = node.content
116
+ xml_abstract << content
117
+ end
118
+ end.join("\n")
119
+ end
120
+
121
+ def image_attributes(node)
122
+ uri = node.image_uri node.attr("target")
123
+ types = MIME::Types.type_for(uri)
124
+ { src: uri,
125
+ id: Utils::anchor_or_uuid,
126
+ imagetype: types.first.sub_type.upcase,
127
+ height: node.attr("height") || "auto",
128
+ width: node.attr("width") || "auto" }
129
+ end
130
+
131
+ def figure_title(node, f)
132
+ unless node.title.nil?
133
+ f.name { |name| name << node.title }
134
+ end
135
+ end
136
+
137
+ def image(node)
138
+ noko do |xml|
139
+ xml.figure **id_attr(node) do |f|
140
+ figure_title(node, f)
141
+ f.image **attr_code(image_attributes(node))
142
+ end
143
+ end
144
+ end
145
+
146
+ def paragraph(node)
147
+ return termsource(node) if node.role == "source"
148
+ attrs = { align: node.attr("align"),
149
+ id: Utils::anchor_or_uuid(node) }
150
+ noko do |xml|
151
+ xml.p **attr_code(attrs) do |xml_t|
152
+ xml_t << node.content
153
+ end
154
+ end.join("\n")
155
+ end
156
+
157
+ def quote_attrs(node)
158
+ { id: Utils::anchor_or_uuid(node), align: node.attr("align") }
159
+ end
160
+
161
+ def quote_attribution(node, out)
162
+ if node.attr("citetitle")
163
+ m = /^(?<cite>[^,]+)(,(?<text>.*$))?$/m.match node.attr("citetitle")
164
+ out.source m[:text],
165
+ **attr_code(target: m[:cite], type: "inline")
166
+ end
167
+ if node.attr("attribution")
168
+ out.author { |a| a << node.attr("attribution") }
169
+ end
170
+ end
171
+
172
+ def quote(node)
173
+ noko do |xml|
174
+ xml.quote **attr_code(quote_attrs(node)) do |q|
175
+ quote_attribution(node, q)
176
+ wrap_in_para(node, q)
177
+ end
178
+ end
179
+ end
180
+
181
+ def listing(node)
182
+ # NOTE: html escaping is performed by Nokogiri
183
+ noko do |xml|
184
+ xml.sourcecode(**id_attr(node)) { |s| s << node.content }
185
+ # xml.sourcecode(**id_attr(node)) { |s| s << node.lines.join("\n") }
186
+ end
187
+ end
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,247 @@
1
+ require "date"
2
+ require "nokogiri"
3
+ require "pathname"
4
+ require "open-uri"
5
+ require "pp"
6
+ require_relative "./cleanup_block.rb"
7
+ require_relative "./cleanup_footnotes.rb"
8
+ require_relative "./cleanup_ref.rb"
9
+
10
+ module Asciidoctor
11
+ module Standoc
12
+ module Cleanup
13
+ def textcleanup(text)
14
+ text.gsub(/\s+<fn /, "<fn ")
15
+ end
16
+
17
+ def cleanup(xmldoc)
18
+ termdef_cleanup(xmldoc)
19
+ sections_cleanup(xmldoc)
20
+ obligations_cleanup(xmldoc)
21
+ table_cleanup(xmldoc)
22
+ formula_cleanup(xmldoc)
23
+ figure_cleanup(xmldoc)
24
+ ref_cleanup(xmldoc)
25
+ note_cleanup(xmldoc)
26
+ normref_cleanup(xmldoc)
27
+ biblio_cleanup(xmldoc)
28
+ reference_names(xmldoc)
29
+ xref_cleanup(xmldoc)
30
+ bpart_cleanup(xmldoc)
31
+ quotesource_cleanup(xmldoc)
32
+ para_cleanup(xmldoc)
33
+ callout_cleanup(xmldoc)
34
+ origin_cleanup(xmldoc)
35
+ element_name_cleanup(xmldoc)
36
+ footnote_renumber(xmldoc)
37
+ empty_element_cleanup(xmldoc)
38
+ mathml_cleanup(xmldoc)
39
+ script_cleanup(xmldoc)
40
+ docidentifier_cleanup(xmldoc)
41
+ bookmark_cleanup(xmldoc)
42
+ xmldoc
43
+ end
44
+
45
+ # ISO as a prefix goes first
46
+ def docidentifier_cleanup(xmldoc)
47
+ id = xmldoc.at("//bibdata/docidentifier/project-number")
48
+ return unless id
49
+
50
+ prefix = []
51
+ xmldoc.xpath("//bibdata/contributor[role/@type = 'publisher']"\
52
+ "/organization").each do |x|
53
+ x1 = x.at("abbreviation")&.text || x.at("name")&.text
54
+ x1 == "ISO" and prefix.unshift("ISO") or prefix << x1
55
+ end
56
+
57
+ id.content = prefix.join("/") + " " + id.text
58
+ end
59
+
60
+ TEXT_ELEMS =
61
+ %w{status language script version author name callout phone
62
+ email street city state country postcode identifier referenceFrom
63
+ referenceTo docidentifier prefix initial addition surname forename
64
+ title draft secretariat title-main title-intro title-part}.freeze
65
+
66
+ # it seems Nokogiri::XML is treating the content of <script> as cdata,
67
+ # because of its use in HTML. Bad nokogiri. Undoing that, since we use
68
+ # script as a normal tag
69
+ def script_cleanup(xmldoc)
70
+ xmldoc.xpath("//script").each do |x|
71
+ x.content = x.to_str
72
+ end
73
+ end
74
+
75
+ def empty_element_cleanup(xmldoc)
76
+ xmldoc.xpath("//" + TEXT_ELEMS.join(" | //")).each do |x|
77
+ x.remove if x.children.empty?
78
+ end
79
+ end
80
+
81
+ def element_name_cleanup(xmldoc)
82
+ xmldoc.traverse { |n| n.name = n.name.gsub(/_/, "-") }
83
+ end
84
+
85
+ def link_callouts_to_annotations(callouts, annotations)
86
+ callouts.each_with_index do |c, i|
87
+ c["target"] = "_" + UUIDTools::UUID.random_create
88
+ annotations[i]["id"] = c["target"]
89
+ end
90
+ end
91
+
92
+ def align_callouts_to_annotations(xmldoc)
93
+ xmldoc.xpath("//sourcecode").each do |x|
94
+ callouts = x.elements.select { |e| e.name == "callout" }
95
+ annotations = x.elements.select { |e| e.name == "annotation" }
96
+ if callouts.size == annotations.size
97
+ link_callouts_to_annotations(callouts, annotations)
98
+ end
99
+ end
100
+ end
101
+
102
+ def merge_annotations_into_sourcecode(xmldoc)
103
+ xmldoc.xpath("//sourcecode").each do |x|
104
+ while x&.next_element&.name == "annotation"
105
+ x.next_element.parent = x
106
+ end
107
+ end
108
+ end
109
+
110
+ def callout_cleanup(xmldoc)
111
+ merge_annotations_into_sourcecode(xmldoc)
112
+ align_callouts_to_annotations(xmldoc)
113
+ end
114
+
115
+ def termdef_stem_cleanup(xmldoc)
116
+ xmldoc.xpath("//term/p/stem").each do |a|
117
+ if a.parent.elements.size == 1
118
+ # para containing just a stem expression
119
+ t = Nokogiri::XML::Element.new("admitted", xmldoc)
120
+ parent = a.parent
121
+ t.children = a.remove
122
+ parent.replace(t)
123
+ end
124
+ end
125
+ end
126
+
127
+ def termdomain_cleanup(xmldoc)
128
+ xmldoc.xpath("//p/domain").each do |a|
129
+ prev = a.parent.previous
130
+ prev.next = a.remove
131
+ end
132
+ end
133
+
134
+ def termdefinition_cleanup(xmldoc)
135
+ xmldoc.xpath("//term").each do |d|
136
+ first_child = d.at("./p | ./figure | ./formula") || return
137
+ t = Nokogiri::XML::Element.new("definition", xmldoc)
138
+ first_child.replace(t)
139
+ t << first_child.remove
140
+ d.xpath("./p | ./figure | ./formula").each { |n| t << n.remove }
141
+ end
142
+ end
143
+
144
+ def termdef_unnest_cleanup(xmldoc)
145
+ # release termdef tags from surrounding paras
146
+ nodes = xmldoc.xpath("//p/admitted | //p/deprecates")
147
+ while !nodes.empty?
148
+ nodes[0].parent.replace(nodes[0].parent.children)
149
+ nodes = xmldoc.xpath("//p/admitted | //p/deprecates")
150
+ end
151
+ end
152
+
153
+ def termdef_boilerplate_cleanup(xmldoc)
154
+ xmldoc.xpath("//terms/p | //terms/ul").each(&:remove)
155
+ end
156
+
157
+ def termdef_subclause_cleanup(xmldoc)
158
+ xmldoc.xpath("//terms[terms]").each do |t|
159
+ t.name = "clause"
160
+ end
161
+ end
162
+
163
+ def termdocsource_cleanup(xmldoc)
164
+ f = xmldoc.at("//preface | //sections")
165
+ xmldoc.xpath("//terms/termdocsource | "\
166
+ "//clause/termdocsource").each do |s|
167
+ f.previous = s.remove
168
+ end
169
+ end
170
+
171
+ def termdef_cleanup(xmldoc)
172
+ termdef_unnest_cleanup(xmldoc)
173
+ termdef_stem_cleanup(xmldoc)
174
+ termdomain_cleanup(xmldoc)
175
+ termdefinition_cleanup(xmldoc)
176
+ termdef_boilerplate_cleanup(xmldoc)
177
+ termdef_subclause_cleanup(xmldoc)
178
+ termdocsource_cleanup(xmldoc)
179
+ end
180
+
181
+ def biblio_cleanup(xmldoc)
182
+ xmldoc.xpath("//references[references]").each do |t|
183
+ t.name = "clause"
184
+ end
185
+ end
186
+
187
+ ELEMS_ALLOW_NOTES =
188
+ # %w[p formula quote sourcecode example admonition ul ol dl figure]
189
+ %w[p formula ul ol dl figure].freeze
190
+
191
+ # if a note is at the end of a section, it is left alone
192
+ # if a note is followed by a non-note block,
193
+ # it is moved inside its preceding block if it is not delimited
194
+ # (so there was no way of making that block include the note)
195
+ def note_cleanup(xmldoc)
196
+ q = "//note[following-sibling::*[not(local-name() = 'note')]]"
197
+ xmldoc.xpath(q).each do |n|
198
+ next unless n.ancestors("table").empty?
199
+ prev = n.previous_element || next
200
+ n.parent = prev if ELEMS_ALLOW_NOTES.include? prev.name
201
+ end
202
+ end
203
+
204
+ def empty_text_before_first_element(x)
205
+ x.children.each do |c|
206
+ if c.text?
207
+ return false if /\S/.match(c.text)
208
+ end
209
+ return true if c.element?
210
+ end
211
+ true
212
+ end
213
+
214
+ def strip_initial_space(x)
215
+ if x.children[0].text?
216
+ if !/\S/.match(x.children[0].text)
217
+ x.children[0].remove
218
+ else
219
+ x.children[0].content = x.children[0].text.gsub(/^ /, "")
220
+ end
221
+ end
222
+ end
223
+
224
+ def bookmark_cleanup(xmldoc)
225
+ xmldoc.xpath("//li[descendant::bookmark]").each do |x|
226
+ if x&.elements&.first&.name == "p" &&
227
+ x&.elements&.first&.elements&.first&.name == "bookmark"
228
+ if empty_text_before_first_element(x.elements[0])
229
+ x["id"] = x.elements[0].elements[0].remove["id"]
230
+ strip_initial_space(x.elements[0])
231
+ end
232
+ end
233
+ end
234
+ end
235
+
236
+ def mathml_cleanup(xmldoc)
237
+ xmldoc.xpath("//stem[@type = 'MathML']").each do |x|
238
+ math = x.text.gsub(/&lt;/, "<").gsub(/&gt;/, ">").gsub(/&quot;/, '"').
239
+ gsub(/&amp;/, "&").gsub(/<[^:\/]+:/, "<").gsub(/<\/[^:]+:/, "</").
240
+ gsub(/ xmlns[^>]+/, "").
241
+ gsub(/<math>/, '<math xmlns="http://www.w3.org/1998/Math/MathML">')
242
+ x.children = math
243
+ end
244
+ end
245
+ end
246
+ end
247
+ end
@@ -0,0 +1,193 @@
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 Standoc
11
+ module Cleanup
12
+ def para_cleanup(xmldoc)
13
+ xmldoc.xpath("//p[not(@id)]").each do |x|
14
+ x["id"] = Utils::anchor_or_uuid
15
+ end
16
+ xmldoc.xpath("//note[not(@id)][not(ancestor::bibitem)]"\
17
+ "[not(ancestor::table)]").each do |x|
18
+ x["id"] = Utils::anchor_or_uuid
19
+ end
20
+ end
21
+
22
+ # move Key dl after table footer
23
+ def dl_table_cleanup(xmldoc)
24
+ q = "//table/following-sibling::*[1]"\
25
+ "[self::p and normalize-space() = 'Key']"
26
+ xmldoc.xpath(q).each do |s|
27
+ if !s.next_element.nil? && s.next_element.name == "dl"
28
+ s.previous_element << s.next_element.remove
29
+ s.remove
30
+ end
31
+ end
32
+ end
33
+
34
+ def insert_thead(s)
35
+ thead = s.at("./thead")
36
+ return thead unless thead.nil?
37
+ if tname = s.at("./name")
38
+ thead = tname.add_next_sibling("<thead/>").first
39
+ return thead
40
+ end
41
+ s.children.first.add_previous_sibling("<thead/>").first
42
+ end
43
+
44
+ def header_rows_cleanup(xmldoc)
45
+ xmldoc.xpath("//table[@headerrows]").each do |s|
46
+ thead = insert_thead(s)
47
+ (thead.xpath("./tr").size...s["headerrows"].to_i).each do
48
+ row = s.at("./tbody/tr")
49
+ row.parent = thead
50
+ end
51
+ s.delete("headerrows")
52
+ end
53
+ end
54
+
55
+ def table_cleanup(xmldoc)
56
+ dl_table_cleanup(xmldoc)
57
+ notes_table_cleanup(xmldoc)
58
+ header_rows_cleanup(xmldoc)
59
+ end
60
+
61
+ # move notes into table
62
+ def notes_table_cleanup(xmldoc)
63
+ nomatches = false
64
+ until nomatches
65
+ q = "//table/following-sibling::*[1][self::note]"
66
+ nomatches = true
67
+ xmldoc.xpath(q).each do |n|
68
+ n.previous_element << n.remove
69
+ nomatches = false
70
+ end
71
+ end
72
+ end
73
+
74
+ # include where definition list inside stem block
75
+ def formula_cleanup(x)
76
+ q = "//formula/following-sibling::*[1]"\
77
+ "[self::p and text() = 'where']"
78
+ x.xpath(q).each do |s|
79
+ if !s.next_element.nil? && s.next_element.name == "dl"
80
+ s.previous_element << s.next_element.remove
81
+ s.remove
82
+ end
83
+ end
84
+ end
85
+
86
+ # include key definition list inside figure
87
+ def figure_dl_cleanup(xmldoc)
88
+ q = "//figure/following-sibling::*"\
89
+ "[self::p and normalize-space() = 'Key']"
90
+ xmldoc.xpath(q).each do |s|
91
+ if !s.next_element.nil? && s.next_element.name == "dl"
92
+ s.previous_element << s.next_element.remove
93
+ s.remove
94
+ end
95
+ end
96
+ end
97
+
98
+ # examples containing only figures become subfigures of figures
99
+ def subfigure_cleanup(xmldoc)
100
+ nodes = xmldoc.xpath("//example/figure")
101
+ while !nodes.empty?
102
+ nodes[0].parent.name = "figure"
103
+ nodes = xmldoc.xpath("//example/figure")
104
+ end
105
+ end
106
+
107
+ def figure_cleanup(xmldoc)
108
+ figure_footnote_cleanup(xmldoc)
109
+ figure_dl_cleanup(xmldoc)
110
+ subfigure_cleanup(xmldoc)
111
+ end
112
+
113
+ def make_preface(x, s)
114
+ if x.at("//foreword | //introduction")
115
+ preface = s.add_previous_sibling("<preface/>").first
116
+ foreword = x.at("//foreword")
117
+ preface.add_child foreword.remove if foreword
118
+ introduction = x.at("//introduction")
119
+ preface.add_child introduction.remove if introduction
120
+ end
121
+ end
122
+
123
+ def make_bibliography(x, s)
124
+ if x.at("//sections/references")
125
+ biblio = s.add_next_sibling("<bibliography/>").first
126
+ x.xpath("//sections/references").each do |r|
127
+ biblio.add_child r.remove
128
+ end
129
+ end
130
+ end
131
+
132
+ def sections_order_cleanup(x)
133
+ s = x.at("//sections")
134
+ make_preface(x, s)
135
+ make_bibliography(x, s)
136
+ x.xpath("//sections/annex").reverse_each { |r| s.next = r.remove }
137
+ end
138
+
139
+ def maxlevel(x)
140
+ max = 5
141
+ x.xpath("//clause[@level]").each do |c|
142
+ max = c["level"].to_i if max < c["level"].to_i
143
+ end
144
+ max
145
+ end
146
+
147
+ def sections_level_cleanup(x)
148
+ m = maxlevel(x)
149
+ return if m < 6
150
+ m.downto(6).each do |l|
151
+ x.xpath("//clause[@level = '#{l}']").each do |c|
152
+ c.delete("level")
153
+ c.previous_element << c.remove
154
+ end
155
+ end
156
+ end
157
+
158
+ def sections_cleanup(x)
159
+ sections_order_cleanup(x)
160
+ sections_level_cleanup(x)
161
+ end
162
+
163
+ def obligations_cleanup(x)
164
+ obligations_cleanup_info(x)
165
+ obligations_cleanup_norm(x)
166
+ obligations_cleanup_inherit(x)
167
+ end
168
+
169
+ def obligations_cleanup_info(x)
170
+ (s = x.at("//foreword")) && s["obligation"] = "informative"
171
+ (s = x.at("//introduction")) && s["obligation"] = "informative"
172
+ x.xpath("//references").each { |r| r["obligation"] = "informative" }
173
+ end
174
+
175
+ def obligations_cleanup_norm(x)
176
+ (s = x.at("//clause[title = 'Scope']")) && s["obligation"] = "normative"
177
+ (s = x.at("//clause[title = 'Symbols and Abbreviated Terms']")) &&
178
+ s["obligation"] = "normative"
179
+ x.xpath("//terms").each { |r| r["obligation"] = "normative" }
180
+ x.xpath("//symbols-abbrevs").each { |r| r["obligation"] = "normative" }
181
+ end
182
+
183
+ def obligations_cleanup_inherit(x)
184
+ x.xpath("//annex | //clause").each do |r|
185
+ r["obligation"] = "normative" unless r["obligation"]
186
+ end
187
+ x.xpath(Utils::SUBCLAUSE_XPATH).each do |r|
188
+ r["obligation"] = r.at("./ancestor::*/@obligation").text
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end