metanorma-standoc 3.1.2 → 3.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fe69b54cc4d7ab2744446c02c80d3e4bcfd5af8b098ab4f709f71269bfeed28e
4
- data.tar.gz: d16ff11d49036801eb8d7a14b277d6d57b6079435682f4b4a07bf5b0140702d7
3
+ metadata.gz: 07dd4263f3139ccb6d6f742e52e7ccf48a4d5f585dc442fd4fe22019f68a2173
4
+ data.tar.gz: 013cd7e28bb591c2b09b138de8debe85831bc30460ed6192aae2dd9d4434de89
5
5
  SHA512:
6
- metadata.gz: f6f68dceb7b3ab96e34b1b09f91b616b2b53e627ee6383cab0246ca1d429b975dee17f90118bae28a60ece153310c0fb78697c1ea863731c965d556b81fcab0f
7
- data.tar.gz: 2613d30ba2e9bef9812787c5fc247205ca2c14929e5f50ba83138049c9cae7c1f0835bcc104127016516db61dc1477c911fb55f8161b9271c18251290f5684e2
6
+ metadata.gz: 4a856c20cb73fb75982c1e473d5d959e1aeed8cd1354eb53af33d612d8769e03b4f85229bfd93adaafb0840e1a4071eca7c99e49e4ed5ae0dd2211e55d6ec8fa
7
+ data.tar.gz: bcc1fa89fe0ddd3b33c0bf9d0bd7696a1668a9d123b289aff61d4f82969bcdc54c7dc0088e7debb3d3256221eefbc5609b8e0ac3d074c62dfcbde826e5b995e7
data/README.adoc CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  image:https://img.shields.io/gem/v/metanorma-standoc.svg["Gem Version", link="https://rubygems.org/gems/metanorma-standoc"]
4
4
  image:https://github.com/metanorma/metanorma-standoc/workflows/rake/badge.svg["Build Status", link="https://github.com/metanorma/metanorma-standoc/actions?workflow=rake"]
5
- image:https://codeclimate.com/github/metanorma/metanorma-standoc/badges/gpa.svg["Code Climate", link="https://codeclimate.com/github/metanorma/metanorma-standoc"]
5
+ // image:https://codeclimate.com/github/metanorma/metanorma-standoc/badges/gpa.svg["Code Climate", link="https://codeclimate.com/github/metanorma/metanorma-standoc"]
6
6
  image:https://img.shields.io/github/issues-pr-raw/metanorma/metanorma-standoc.svg["Pull Requests", link="https://github.com/metanorma/metanorma-standoc/pulls"]
7
7
  image:https://img.shields.io/github/commits-since/metanorma/metanorma-standoc/latest.svg["Commits since latest",link="https://github.com/metanorma/metanorma-standoc/releases"]
8
8
 
@@ -139,7 +139,7 @@ module Metanorma
139
139
  %w(presentation semantic).each do |t|
140
140
  /^#{t}-metadata-/.match?(k) or next
141
141
  k = k.sub(/^#{t}-metadata-/, "")
142
- quoted_csv_split(v)&.each do |c|
142
+ quoted_csv_split(v.gsub("&#", "&#"))&.each do |c|
143
143
  ret << "<#{t}-metadata><#{k}>#{c}</#{k}></#{t}-metadata>"
144
144
  end
145
145
  end
@@ -1291,13 +1291,13 @@ Restricted recursively to contain only other such inline elements with no identi
1291
1291
  <ref name="pure_strike"/>
1292
1292
  <ref name="pure_smallcap"/>
1293
1293
  <ref name="br"/>
1294
+ <ref name="stem"/>
1294
1295
  </choice>
1295
1296
  </define>
1296
1297
  <define name="NestedTextElement">
1297
1298
  <a:documentation>Contents of TextElement tags: leaves out tags that should occur only at top level of block: bookmark image hr pagebreak</a:documentation>
1298
1299
  <choice>
1299
1300
  <ref name="PureTextElement"/>
1300
- <ref name="stem"/>
1301
1301
  <ref name="eref"/>
1302
1302
  <ref name="xref"/>
1303
1303
  <ref name="hyperlink"/>
@@ -76,12 +76,6 @@ from other documents in the same doctype</a:documentation>
76
76
  <ref name="flavor">
77
77
  <a:documentation>Flavour of Metanorma used to process this document</a:documentation>
78
78
  </ref>
79
- <optional>
80
- <ref name="editorialgroup">
81
- <a:documentation>Groups associated with the production of the standards document, typically within
82
- a standards definition organization</a:documentation>
83
- </ref>
84
- </optional>
85
79
  <zeroOrMore>
86
80
  <ref name="ics">
87
81
  <a:documentation>Classification of the document contents taken from the International Classification of Standards</a:documentation>
@@ -130,49 +124,6 @@ a standards definition organization</a:documentation>
130
124
  However we prefer not to hardcode it, given ongoing extension.</a:documentation>
131
125
  <text/>
132
126
  </define>
133
- <define name="editorialgroup">
134
- <a:documentation>A group associated with the production of the standards document, typically within
135
- a standards definition organization</a:documentation>
136
- <element name="editorialgroup">
137
- <oneOrMore>
138
- <ref name="technical-committee">
139
- <a:documentation>A technical committee associated with the production of the standards document</a:documentation>
140
- </ref>
141
- </oneOrMore>
142
- </element>
143
- </define>
144
- <define name="technical-committee">
145
- <a:documentation>Technical committee associated with the production of a standards document</a:documentation>
146
- <element name="technical-committee">
147
- <ref name="IsoWorkgroup"/>
148
- </element>
149
- </define>
150
- <define name="IsoWorkgroup">
151
- <optional>
152
- <attribute name="number">
153
- <a:documentation>Numeric identifier of the technical committee</a:documentation>
154
- </attribute>
155
- </optional>
156
- <optional>
157
- <attribute name="type">
158
- <a:documentation>Type of the technical committee, used in identifying the technical committee</a:documentation>
159
- </attribute>
160
- </optional>
161
- <optional>
162
- <attribute name="identifier">
163
- <a:documentation>Non-numeric, complete identifier of the technical committee</a:documentation>
164
- </attribute>
165
- </optional>
166
- <optional>
167
- <attribute name="prefix">
168
- <a:documentation>Disambiguating prefix added to number to form the identifier of the technical committee,
169
- typically indicating its type</a:documentation>
170
- </attribute>
171
- </optional>
172
- <text>
173
- <a:documentation>Name of the technical committee</a:documentation>
174
- </text>
175
- </define>
176
127
  <define name="ics">
177
128
  <a:documentation>Classification taken from the International Classification of Standards.
178
129
  ICS is defined by ISO here -- https://www.iso.org/publication/PUB100033.html</a:documentation>
@@ -242,6 +242,8 @@ Detailed in https://www.relaton.org/model/creator/</a:documentation>
242
242
  </choice>
243
243
  </define>
244
244
  <define name="roledescription">
245
+ <a:documentation>A more detailed description of the role of the contributor
246
+ Some descriptions are reserved words; in particular, the editorial committee of a standard has the role description "committee"</a:documentation>
245
247
  <element name="description">
246
248
  <ref name="LocalizedMarkedUpString"/>
247
249
  </element>
@@ -443,7 +445,10 @@ real names (if the person is named with a pseudonym or user name); previous lega
443
445
  </oneOrMore>
444
446
  <zeroOrMore>
445
447
  <ref name="subdivision">
446
- <a:documentation>The subdivision of the organization directly involved with the production of the bibliographic item</a:documentation>
448
+ <a:documentation>The subdivision of the organization directly involved with the production of the bibliographic item.
449
+ Multiple subdivisions can be specified for an organization, with no implication of hierarchical
450
+ relation between them
451
+ Editorial and advisory groups are represented as consecutive subdivisions of the SDO</a:documentation>
447
452
  </ref>
448
453
  </zeroOrMore>
449
454
  <optional>
@@ -485,6 +490,11 @@ real names (if the person is named with a pseudonym or user name); previous lega
485
490
  <a:documentation>The type of subdivision</a:documentation>
486
491
  </attribute>
487
492
  </optional>
493
+ <optional>
494
+ <attribute name="subtype">
495
+ <a:documentation>The subtype of subdivision; e.g. different types of technical committee</a:documentation>
496
+ </attribute>
497
+ </optional>
488
498
  <ref name="OrganizationType">
489
499
  <a:documentation>The subdivision, modelled recursively as an organization</a:documentation>
490
500
  </ref>
@@ -161,12 +161,15 @@ module Metanorma
161
161
  type: node.attr("type"))))
162
162
  end
163
163
 
164
- # term sources occasionally turning up as "source source"?
164
+ # TODO: term sources occasionally turning up as "source source"?
165
165
  def paragraph(node)
166
166
  node.role&.sub(/ .*$/, "") == "source" and return termsource(node)
167
+ content = node.content
168
+ content.start_with?("TODO: ") and return todo_prefixed_para(node)
169
+ content.start_with?("EDITOR: ") and return editor_prefixed_para(node)
167
170
  noko do |xml|
168
171
  xml.p **para_attrs(node) do |xml_t|
169
- xml_t << node.content
172
+ xml_t << content
170
173
  end
171
174
  end
172
175
  end
@@ -6,6 +6,7 @@ module Metanorma
6
6
  .merge(
7
7
  "keep-separate": node.attr("keep-separate"),
8
8
  keepasterm: node.option?("termnote") ? "true" : nil,
9
+ type: node.attr("type")
9
10
  )))
10
11
  end
11
12
 
@@ -46,6 +47,20 @@ module Metanorma
46
47
  end
47
48
  end
48
49
 
50
+ # -TO-DO :
51
+ def todo_prefixed_para(node)
52
+ node.lines[0].sub!(/^TODO: /, "")
53
+ todo(node)
54
+ end
55
+
56
+ # EDITOR:
57
+ def editor_prefixed_para(node)
58
+ node.lines[0].sub!(/^EDITOR: /, "")
59
+ node.set_attr("type", "editorial")
60
+ node.assign_caption "EDITOR"
61
+ admonition(node)
62
+ end
63
+
49
64
  def termnote(node)
50
65
  noko do |xml|
51
66
  xml.termnote **termnote_attrs(node) do |ex|
@@ -33,7 +33,8 @@ module Metanorma
33
33
  passthrough_cleanup(xmldoc) # feeds: smartquotes_cleanup
34
34
  unnumbered_blocks_cleanup(xmldoc)
35
35
  termdocsource_cleanup(xmldoc) # feeds: metadata_cleanup
36
- metadata_cleanup(xmldoc) # feeds: boilerplate_cleanup
36
+ metadata_cleanup(xmldoc) # feeds: boilerplate_cleanup, bibdata_cleanup,
37
+ # docidentifier_cleanup (in generic: template)
37
38
  misccontainer_cleanup(xmldoc)
38
39
  sections_cleanup(xmldoc) # feeds: obligations_cleanup, toc_cleanup,
39
40
  # floatingtitle_cleanup
@@ -202,6 +203,8 @@ module Metanorma
202
203
  end
203
204
 
204
205
  def metadata_cleanup(xmldoc)
206
+ bibdata_published(xmldoc) # feeds: bibdata_cleanup,
207
+ # docidentifier_cleanup (in generic: template)
205
208
  (@metadata_attrs.nil? || @metadata_attrs.empty?) and return
206
209
  ins = add_misc_container(xmldoc)
207
210
  ins << @metadata_attrs
@@ -59,7 +59,7 @@ module Metanorma
59
59
  end
60
60
  end
61
61
 
62
- def indirect_eref_to_xref(eref, ident, id_map=nil)
62
+ def indirect_eref_to_xref(eref, ident, id_map = nil)
63
63
  loc = eref.at("./localityStack[locality[@type = 'anchor']]") ||
64
64
  eref.at("./locality[@type = 'anchor']")
65
65
  loc = loc&.remove&.text || ident
@@ -72,6 +72,7 @@ module Metanorma
72
72
  else
73
73
  eref.document.at("//*[@anchor = '#{loc}']") and return
74
74
  end
75
+
75
76
  eref.children = %(** Missing target #{loc})
76
77
  eref["target"] = ident
77
78
  end
@@ -181,6 +182,20 @@ module Metanorma
181
182
  ins.next = c
182
183
  end
183
184
  end
185
+
186
+ def bibdata_published(xmldoc)
187
+ ins = add_misc_container(xmldoc)
188
+ ins ||= add_misc_container(xmldoc)
189
+ ins.at("./semantic-metadata/stage-published") and return
190
+ p = published?(xmldoc.at("bibdata/status/stage")&.text, xmldoc)
191
+ ins << <<~XML
192
+ <semantic-metadata><stage-published>#{p}</stage-published></semantic-metadata>
193
+ XML
194
+ end
195
+
196
+ def published?(stage, _xmldoc)
197
+ stage.casecmp("published").zero?
198
+ end
184
199
  end
185
200
  end
186
201
  end
@@ -6,6 +6,9 @@ module Metanorma
6
6
  ret = new_bibitem_from_formattedref_spans(b)
7
7
  merge_bibitem_from_formattedref_spans(b, ret)
8
8
  end
9
+ xmldoc.xpath("//bibitem[@amend]").each do |b|
10
+ b.delete("amend")
11
+ end
9
12
  end
10
13
 
11
14
  def new_bibitem_from_formattedref_spans(bib)
@@ -18,9 +21,12 @@ module Metanorma
18
21
 
19
22
  def merge_bibitem_from_formattedref_spans(bib, new)
20
23
  new["type"] and bib["type"] = new["type"]
21
- if bib.at("./title") # there already is a fetched record here: merge
24
+ if bib.at("./title") && bib["amend"]
25
+ # there already is a fetched record here: merge
22
26
  bib.children = MergeBibitems
23
27
  .new(bib.to_xml, new.to_xml).merge.to_noko.children
28
+ elsif bib.at("./title") # replace record
29
+ bib.children = new.children.to_xml
24
30
  else bib << new.children.to_xml
25
31
  end
26
32
  end
@@ -162,25 +162,64 @@ module Metanorma
162
162
  end
163
163
  end
164
164
 
165
- # Asciidoc macro, e.g. span:publisher[...]
166
- # May contain one or more {{ }} in target, with spaces in them
167
- # Does not end in \]
168
- ADOC_MACRO_START =
169
- '\S+:(?:[^\[\] ]+|\{\{[^{}]+\}\})*\[.*?(?<!\\\\)\]'.freeze
170
-
171
- # Replace {{ ... }} with {{ pass:[...]}} to preserve any XML markup
172
- # use pass:[...\] if {{}} is already inside an Asciidoc macro
173
- # Do not use pass: if this is a macro target: mailto:{{x}}[]
174
- # or body: mailto:[{{x}}]
165
+ # The boilerplate file is in Liquid AsciiDoc format
166
+ # (technically, `boilerplate.adoc.liquid`).
167
+ #
168
+ # This file is processed separately from the main Metanorma document and
169
+ # therefore is oblivious of the `{{ concept-mention }}` syntax.
170
+ #
171
+ # Due to historic reasons, the Liquid objects being evaluated in the
172
+ # boilerplate document are XML strings. Notably these are the document
173
+ # metadata, that are extracted from the already generated Metanorma XML.
174
+ #
175
+ # These XML strings are then passed into the AsciiDoc macros such as
176
+ # `span:publisher[...]`.
177
+ #
178
+ # Here, we need to interpolate the XML strings into the AsciiDoc macros
179
+ # without breaking the AsciiDoc syntax.
180
+ #
181
+ # EXAMPLE 1: `mailto:{{ pub_email }}[]`, we need to
182
+ # convert it to:
183
+ # `mailto:{{ pass-format:metanorma[++pub_email_xml++] }}[]`
184
+ #
185
+ # EXAMPLE 2: `link:{{ pub_uri}}[{{ pub_address }}, {{ pub_uri }}]`
186
+ # We need to convert it to:
187
+ # `link:{{ pass-format:metanorma[++pub_uri_xml++] }}[{{
188
+ # pass-format:metanorma[++pub_address_xml++] }}, {{
189
+ # pass-format:metanorma[++pub_uri_xml++] }}]`
190
+ #
191
+ # NOTE: The boilerplate may use macros that contain one or more
192
+ # `{{ ... }}` in the target, and can contain spaces in them.
193
+ #
194
+ # NOTE: The routine needs to handle cases where the content
195
+ # contains an escaped closing bracket `\]`.
196
+
197
+ ADOC_MACRO_PATTERN = /\S+:[^\[\n]*\[[^\]\\]*(?:\\.[^\]\\]*)*\]/
198
+
199
+ # Replace {{ ... }} with {{ pass-format:metanorma:[...] }} to preserve any
200
+ # XML markup provided by Metanorma XML Metadata content, through the
201
+ # `pass-format:metanorma` command.
202
+ #
203
+ # * If `{{ ... }}` is inside an Asciidoc macro, we have to wrap with
204
+ # pass-format:metanorma:[...\].
205
+ # * If this is a macro target (e.g. `mailto:{{x}}[]`, body: mailto:[{{x}}])
206
+ # then do not use pass-format:metanorma.
207
+
175
208
  def boilerplate_read(file)
176
209
  ret = File.read(file, encoding: "UTF-8")
177
- /\.adoc$/.match?(file) or return ret
178
- ret.split(/(#{ADOC_MACRO_START}|\])/o).map do |r|
179
- if /^#{ADOC_MACRO_START}$/o.match?(r)
180
- r
210
+ /\.adoc(\.liquid)?$/.match?(file) or return ret
211
+
212
+ # Split content into macro and non-macro parts
213
+ parts = ret.split(/(#{ADOC_MACRO_PATTERN})/)
214
+
215
+ parts.map.with_index do |part, index|
216
+ if index.odd? && is_valid_macro?(part)
217
+ # This is a macro - leave unchanged
218
+ part
181
219
  else
182
- r.gsub(/(?<!\{)(\{\{[^{}]+\}\})(?!\})/,
183
- "pass-format:metanorma[++\\1++]")
220
+ # Not a macro - wrap {{ }} patterns
221
+ part.gsub(/(?<!\{)(\{\{[^{}]+\}\})(?!\})/,
222
+ "pass-format:metanorma[++\\1++]")
184
223
  end
185
224
  end.join
186
225
  end
@@ -199,6 +238,13 @@ module Metanorma
199
238
  ret
200
239
  end
201
240
 
241
+ private
242
+
243
+ def is_valid_macro?(text)
244
+ # Simple validation - does it look like a macro?
245
+ text.match?(/^\S+:[^\[]*\[.*\]$/)
246
+ end
247
+
202
248
  # remove Metanorma namespace, so generated doc containing boilerplate
203
249
  # can be queried consistently
204
250
  # _\d+ anchor is assigned to titleless clauses, will clash with main doc
@@ -96,7 +96,8 @@ module Metanorma
96
96
 
97
97
  def add_misc_container(xmldoc)
98
98
  unless ins = xmldoc.at("//metanorma-extension")
99
- a = xmldoc.xpath("//termdocsource")&.last || xmldoc.at("//bibdata")
99
+ a = xmldoc.xpath("//termdocsource")&.last || xmldoc.at("//bibdata") ||
100
+ xmldoc.root.children.first
100
101
  a.next = "<metanorma-extension/>"
101
102
  ins = xmldoc.at("//metanorma-extension")
102
103
  end
@@ -179,6 +180,7 @@ module Metanorma
179
180
  end
180
181
 
181
182
  def mathml_cleanup(xmldoc)
183
+ mathml_number_to_mathml(xmldoc)
182
184
  xmldoc.xpath("//stem[@type = 'MathML'][not(@validate = 'false')]")
183
185
  .each do |x|
184
186
  mathml_xml_cleanup(x)
@@ -189,6 +191,15 @@ module Metanorma
189
191
  mathml_unitsml(xmldoc)
190
192
  end
191
193
 
194
+ def mathml_number_to_mathml(xmldoc)
195
+ xmldoc.xpath("//mathml-number").each do |n|
196
+ n.name = "stem"
197
+ n["type"] = "MathML"
198
+ n.children =
199
+ "<math xmlns='#{MATHML_NS}'><mn>#{n.children.to_xml}</mn></math>"
200
+ end
201
+ end
202
+
192
203
  include ::Metanorma::Standoc::Regex
193
204
  end
194
205
  end
@@ -33,6 +33,7 @@ module Metanorma
33
33
  block_macro Metanorma::Plugin::Lutaml::LutamlGmlDictionaryBlockMacro
34
34
  block Metanorma::Plugin::Lutaml::LutamlGmlDictionaryBlock
35
35
  block_macro Metanorma::Plugin::Lutaml::LutamlKlassTableBlockMacro
36
+ block_macro Metanorma::Plugin::Lutaml::LutamlEnumTableBlockMacro
36
37
  preprocessor Metanorma::Standoc::EmbedIncludeProcessor
37
38
  preprocessor Metanorma::Standoc::LinkProtectPreprocessor
38
39
  preprocessor Metanorma::Standoc::PassProtectPreprocessor
@@ -80,8 +81,6 @@ module Metanorma
80
81
  inline_macro Metanorma::Standoc::SourceIncludeInlineMacro
81
82
  block Metanorma::Standoc::ToDoAdmonitionBlock
82
83
  block Metanorma::Standoc::EditorAdmonitionBlock
83
- treeprocessor Metanorma::Standoc::EditorInlineAdmonitionBlock
84
- treeprocessor Metanorma::Standoc::ToDoInlineAdmonitionBlock
85
84
  block Metanorma::Standoc::PlantUMLBlockMacro
86
85
  block Metanorma::Standoc::PseudocodeBlockMacro
87
86
  block_macro Metanorma::Standoc::ColumnBreakBlockMacro
@@ -53,13 +53,6 @@ module Metanorma
53
53
  end
54
54
  end
55
55
 
56
- def metadata_committee(node, xml)
57
- node.attr("technical-committee") or return
58
- xml.editorialgroup do |a|
59
- committee_component("technical-committee", node, a)
60
- end
61
- end
62
-
63
56
  def metadata_ics(node, xml)
64
57
  ics = node.attr("library-ics")
65
58
  ics&.split(/,\s*/)&.each do |i|
@@ -194,7 +187,6 @@ module Metanorma
194
187
  metadata_doctype(node, ext)
195
188
  metadata_subdoctype(node, ext)
196
189
  metadata_flavor(node, ext)
197
- metadata_committee(node, ext)
198
190
  metadata_ics(node, ext)
199
191
  structured_id(node, ext)
200
192
  metadata_coverpage_images(node, ext)
@@ -0,0 +1,138 @@
1
+ module Metanorma
2
+ module Standoc
3
+ module Front
4
+ def committee_number_or_name?(node, type, suffix)
5
+ node.attr("#{type}-number#{suffix}") || node.attr("#{type}#{suffix}")
6
+ end
7
+
8
+ def committee_contributors(node, xml, agency, _opts)
9
+ t = metadata_committee_types(node)
10
+ v = t.first
11
+ if committee_number_or_name?(node, v, "")
12
+ node.attr(v) or node.set_attr(v, "")
13
+ o = committee_contrib_org_prep(node, v, agency, _opts)
14
+ o[:groups] = t
15
+ org_contributor(node, xml, o)
16
+ end
17
+ end
18
+
19
+ def metadata_committee_types(_node)
20
+ %w(technical-committee)
21
+ end
22
+
23
+ def committee_contrib_org_prep(node, type, agency, _opts)
24
+ agency_arr, agency_abbrev =
25
+ committee_org_prep_agency(node, type, agency, [], [])
26
+ { source: [type], role: "author", desc: "committee",
27
+ default_org: false, committee: true,
28
+ agency: agency_arr, agency_abbrev:,
29
+ subdivtype: type.sub(/^approval-/, "").tr("-", " ").capitalize }.compact
30
+ end
31
+
32
+ def committee_org_prep_agency(node, type, agency, agency_arr, agency_abbr)
33
+ i = 1
34
+ suffix = ""
35
+ while committee_number_or_name?(node, type, suffix)
36
+ agency_arr << (node.attr("#{type}-agency#{suffix}") || agency)
37
+ agency_abbr << node.attr("#{type}-agency-abbr#{suffix}")
38
+ i += 1
39
+ suffix = "_#{i}"
40
+ end
41
+ [agency_arr, agency_abbr]
42
+ end
43
+
44
+ def contrib_committee_build(xml, agency, committee)
45
+ if name = org_abbrev.invert[agency]
46
+ committee[:agency_abbrev] = agency
47
+ agency = name
48
+ end
49
+ xml.name agency
50
+ s = committee
51
+ loop do
52
+ contrib_committee_subdiv(xml, s)
53
+ s = s[:subdiv] or break
54
+ end
55
+ abbr = committee[:agency_abbrev] and xml.abbreviation abbr
56
+ full_committee_id(xml.parent)
57
+ end
58
+
59
+ def contrib_committee_subdiv(xml, committee)
60
+ contributors_committees_filter_empty?(committee) and return
61
+ xml.subdivision **attr_code(type: committee[:subdivtype],
62
+ subtype: committee[:type]) do |o|
63
+ o.name committee[:name]
64
+ committee[:abbr] and o.abbreviation committee[:abbr]
65
+ committee[:ident] and o.identifier committee[:ident]
66
+ end
67
+ end
68
+
69
+ def full_committee_id(contrib)
70
+ ret = full_committee_agency_id(contrib)
71
+ ids = contrib.xpath("./subdivision").map do |x|
72
+ x.at("./identifier")&.text
73
+ end.compact
74
+ ins = contrib.at("./subdivision/identifier") and
75
+ ins.next = "<identifier type='full'>#{ret}#{ids.join('/')}</identifier>"
76
+ end
77
+
78
+ def full_committee_agency_id(contrib)
79
+ agency = contrib.at("./abbreviation")&.text
80
+ ret = agency == default_publisher ? "" : "#{agency} "
81
+ /^\s+/.match?(ret) and ret = ""
82
+ ret
83
+ end
84
+
85
+ def org_attrs_parse(node, opts)
86
+ opts_orig = opts.dup
87
+ ret = []
88
+ ret << org_attrs_parse_core(node, opts)&.map&.with_index do |x, i|
89
+ x.merge(agency: opts.dig(:agency, i), subdivtype: opts[:subdivtype],
90
+ agency_abbrev: opts.dig(:agency_abbrev, i), abbr: opts[:abbr],
91
+ committee: opts[:committee], default_org: opts[:default_org])
92
+ end
93
+ org_attrs_add_committees(node, ret, opts, opts_orig)
94
+ end
95
+
96
+ def org_attrs_add_committees(node, ret, opts, opts_orig)
97
+ opts_orig[:groups]&.each_with_index do |g, i|
98
+ i.zero? and next
99
+ contributors_committees_pad_multiples(ret.first, node, g)
100
+ opts = committee_contrib_org_prep(node, g, nil, opts_orig)
101
+ ret << org_attrs_parse_core(node, opts).map do |x|
102
+ x.merge(subdivtype: opts[:subdivtype])
103
+ end
104
+ end
105
+ contributors_committees_nest1(ret)
106
+ end
107
+
108
+ # ensure there is subcommittee, workgroup -number_2, -number_3 etc
109
+ # to parse multiple tech committees
110
+ def contributors_committees_pad_multiples(committees, node, group)
111
+ committees.each_with_index do |_r, j|
112
+ suffix = j.zero? ? "" : "_#{j + 1}"
113
+ node.attr("#{group}#{suffix}") or
114
+ node.set_attr("#{group}#{suffix}", "")
115
+ node.attr("#{group}-number#{suffix}") or
116
+ node.set_attr("#{group}-number#{suffix}", "")
117
+ end
118
+ end
119
+
120
+ def contributors_committees_filter_empty?(committee)
121
+ (committee[:name].nil? || committee[:name].empty?) &&
122
+ committee[:ident].nil?
123
+ end
124
+
125
+ def contributors_committees_nest1(committees)
126
+ committees.empty? and return committees
127
+ committees = committees.reverse
128
+ committees.each_with_index do |m, i|
129
+ i.zero? and next
130
+ m.each_with_index do |m1, j|
131
+ m1[:subdiv] = committees[i - 1][j]
132
+ end
133
+ end
134
+ committees[-1]
135
+ end
136
+ end
137
+ end
138
+ end
@@ -1,4 +1,5 @@
1
1
  require_relative "./front_organisation"
2
+ require_relative "./front_committee"
2
3
 
3
4
  module Metanorma
4
5
  module Standoc
@@ -8,6 +9,7 @@ module Metanorma
8
9
  { source: ["publisher", "pub"], role: "author",
9
10
  default: default_publisher })
10
11
  personal_author(node, xml)
12
+ committee_contributors(node, xml, default_publisher, {})
11
13
  end
12
14
 
13
15
  def personal_author(node, xml)
@@ -1,18 +1,6 @@
1
1
  module Metanorma
2
2
  module Standoc
3
3
  module Front
4
- def committee_component(compname, node, out)
5
- i = 1
6
- suffix = ""
7
- while node.attr(compname + suffix)
8
- out.send compname.gsub(/-/, "_"), node.attr(compname + suffix),
9
- **attr_code(number: node.attr("#{compname}-number#{suffix}"),
10
- type: node.attr("#{compname}-type#{suffix}"))
11
- i += 1
12
- suffix = "_#{i}"
13
- end
14
- end
15
-
16
4
  def organization(org, orgname, node = nil, default_org = nil, attrs = {})
17
5
  orgname, abbr = org_name_and_abbrev(attrs, orgname)
18
6
  org.name orgname
@@ -147,12 +135,16 @@ module Metanorma
147
135
  end
148
136
 
149
137
  def org_organization(node, xml, org)
150
- organization(xml, org[:name], node, !node.attr("publisher"), org)
151
- org_address(org, xml)
152
- org_logo(xml, org[:logo])
138
+ if org[:committee]
139
+ contrib_committee_build(xml, org[:agency], org)
140
+ else
141
+ organization(xml, org[:name], node, !node.attr("publisher"), org)
142
+ org_address(org, xml)
143
+ org_logo(xml, org[:logo])
144
+ end
153
145
  end
154
146
 
155
- def org_attrs_parse(node, opts)
147
+ def org_attrs_parse_core(node, opts)
156
148
  source = opts[:source]&.detect { |s| node.attr(s) }
157
149
  org_attrs_simple_parse(node, opts, source) ||
158
150
  org_attrs_complex_parse(node, opts, source)
@@ -178,7 +170,7 @@ module Metanorma
178
170
  i = 1
179
171
  suffix = ""
180
172
  ret = []
181
- while node.attr(source + suffix)
173
+ while committee_number_or_name?(node, source, suffix)
182
174
  ret << extract_org_attrs_complex(node, opts, source, suffix)
183
175
  i += 1
184
176
  suffix = "_#{i}"
@@ -187,14 +179,28 @@ module Metanorma
187
179
  end
188
180
 
189
181
  def extract_org_attrs_complex(node, opts, source, suffix)
190
- { name: node.attr(source + suffix),
182
+ n = node.attr("#{source}-number#{suffix}") # for committees
183
+ t = committee_ident(node.attr("#{source}-type#{suffix}"), n, source)
184
+ { name: node.attr(source + suffix), ident: t,
191
185
  abbrev: node.attr("#{source}_abbr#{suffix}"),
192
186
  role: opts[:role], desc: opts[:desc],
187
+ type: node.attr("#{source}-type#{suffix}"),
193
188
  subdiv: node.attr("#{source}_subdivision#{suffix}"),
194
189
  logo: node.attr("#{source}_logo#{suffix}") }.compact
195
190
  .merge(extract_org_attrs_address(node, opts, suffix))
196
191
  end
197
192
 
193
+ def committee_abbrevs
194
+ { "technical-committee" => "TC" }
195
+ end
196
+
197
+ def committee_ident(type, number, level)
198
+ number.nil? || number.empty? and return
199
+ type ||= committee_abbrevs[level]
200
+ type == "Other" and type = ""
201
+ "#{type} #{number}".strip
202
+ end
203
+
198
204
  def extract_org_attrs_address(node, opts, suffix)
199
205
  %w(address phone fax email uri).each_with_object({}) do |a, m|
200
206
  opts[:source]&.each do |s|
@@ -1,6 +1,6 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
2
  <grammar xmlns:a="http://relaxng.org/ns/compatibility/annotations/1.0" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
3
- <!-- VERSION v2.1.0 -->
3
+ <!-- VERSION v2.1.1 -->
4
4
 
5
5
  <!--
6
6
  ALERT: cannot have root comments, because of https://github.com/metanorma/metanorma/issues/437
@@ -42,36 +42,6 @@ All IdRefType instances point not to `@id` in Semantic XML, which is the Content
42
42
  but to `@anchor`, the user-supplied cross-reference</a:documentation>
43
43
  <text/>
44
44
  </define>
45
- <define name="index-primary">
46
- <element name="primary">
47
- <oneOrMore>
48
- <choice>
49
- <ref name="PureTextElement"/>
50
- <ref name="stem"/>
51
- </choice>
52
- </oneOrMore>
53
- </element>
54
- </define>
55
- <define name="index-secondary">
56
- <element name="secondary">
57
- <oneOrMore>
58
- <choice>
59
- <ref name="PureTextElement"/>
60
- <ref name="stem"/>
61
- </choice>
62
- </oneOrMore>
63
- </element>
64
- </define>
65
- <define name="index-tertiary">
66
- <element name="tertiary">
67
- <oneOrMore>
68
- <choice>
69
- <ref name="PureTextElement"/>
70
- <ref name="stem"/>
71
- </choice>
72
- </oneOrMore>
73
- </element>
74
- </define>
75
45
  <define name="review">
76
46
  <a:documentation>Generalise BasicDoc element from just review comments, to general annotations;
77
47
  the type attribute defaults to `review` for reviews</a:documentation>
@@ -592,28 +562,6 @@ normative or informative references, some split references into sections organiz
592
562
  </oneOrMore>
593
563
  </element>
594
564
  </define>
595
- <define name="sub">
596
- <a:documentation>Subscript text. Corresponds to HTML `sub</a:documentation>
597
- <element name="sub">
598
- <zeroOrMore>
599
- <choice>
600
- <ref name="PureTextElement"/>
601
- <ref name="stem"/>
602
- </choice>
603
- </zeroOrMore>
604
- </element>
605
- </define>
606
- <define name="sup">
607
- <a:documentation>Superscript text. Corresponds to HTML `sup`</a:documentation>
608
- <element name="sup">
609
- <zeroOrMore>
610
- <choice>
611
- <ref name="PureTextElement"/>
612
- <ref name="stem"/>
613
- </choice>
614
- </zeroOrMore>
615
- </element>
616
- </define>
617
565
  <define name="pagebreak">
618
566
  <a:documentation>Page break. Only applicable in paged layouts (e.g. PDF, Word), and not flow layouts (e.g. HTML)</a:documentation>
619
567
  <element name="pagebreak">
@@ -1094,7 +1042,6 @@ That concept may be defined as a term within the current document, or it may be
1094
1042
  <zeroOrMore>
1095
1043
  <choice>
1096
1044
  <ref name="PureTextElement"/>
1097
- <ref name="stem"/>
1098
1045
  <ref name="index"/>
1099
1046
  <ref name="index-xref"/>
1100
1047
  </choice>
@@ -1107,7 +1054,6 @@ That concept may be defined as a term within the current document, or it may be
1107
1054
  <zeroOrMore>
1108
1055
  <choice>
1109
1056
  <ref name="PureTextElement"/>
1110
- <ref name="stem"/>
1111
1057
  <ref name="index"/>
1112
1058
  <ref name="index-xref"/>
1113
1059
  </choice>
@@ -2025,10 +1971,7 @@ used in document amendments</a:documentation>
2025
1971
  <element name="name">
2026
1972
  <a:documentation>The symbolic form of the designation</a:documentation>
2027
1973
  <oneOrMore>
2028
- <choice>
2029
- <ref name="PureTextElement"/>
2030
- <ref name="stem"/>
2031
- </choice>
1974
+ <ref name="PureTextElement"/>
2032
1975
  </oneOrMore>
2033
1976
  </element>
2034
1977
  </element>
@@ -2081,7 +2024,6 @@ used in document amendments</a:documentation>
2081
2024
  <zeroOrMore>
2082
2025
  <choice>
2083
2026
  <ref name="PureTextElement"/>
2084
- <ref name="stem"/>
2085
2027
  <ref name="index"/>
2086
2028
  <ref name="index-xref"/>
2087
2029
  </choice>
@@ -2291,6 +2233,11 @@ used in document amendments</a:documentation>
2291
2233
  <ref name="RequiredId"/>
2292
2234
  <ref name="NumberingAttributes"/>
2293
2235
  <ref name="BlockAttributes"/>
2236
+ <optional>
2237
+ <attribute name="type">
2238
+ <a:documentation>Semantic classification of note</a:documentation>
2239
+ </attribute>
2240
+ </optional>
2294
2241
  <oneOrMore>
2295
2242
  <choice>
2296
2243
  <a:documentation>Content of the term note</a:documentation>
@@ -233,9 +233,7 @@ module Metanorma
233
233
  fmt = format(out, target)
234
234
  fmt.empty? and fmt = "default"
235
235
  fmt = %( number-format="#{fmt}")
236
- <<~OUTPUT.strip
237
- <stem type="MathML"#{fmt}><math xmlns='#{MATHML_NS}'><mn>#{number(target)}</mn></math></stem>
238
- OUTPUT
236
+ "<mathml-number#{fmt}>#{number(target)}</mathml-number>"
239
237
  end
240
238
  end
241
239
  end
@@ -14,20 +14,6 @@ module Metanorma
14
14
  end
15
15
  end
16
16
 
17
- class ToDoInlineAdmonitionBlock < Asciidoctor::Extensions::Treeprocessor
18
- def process(document)
19
- (document.find_by context: :paragraph).each do |para|
20
- next unless /^TODO: /.match? para.lines[0]
21
-
22
- para.set_attr("name", "todo")
23
- para.set_attr("caption", "TODO")
24
- para.lines[0].sub!(/^TODO: /, "")
25
- # these will be converted from admonition to annotation downstream
26
- para.context = :admonition
27
- end
28
- end
29
- end
30
-
31
17
  class FootnoteBlockInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
32
18
  use_dsl
33
19
  named :footnoteblock
@@ -130,10 +130,7 @@ module Metanorma
130
130
  end
131
131
 
132
132
  def refitem_render(xml, match, code)
133
- xml.bibitem **attr_code(
134
- anchor: match[:anchor], suppress_identifier: code[:dropid],
135
- hidden: code[:hidden], id: "_#{UUIDTools::UUID.random_create}"
136
- ) do |t|
133
+ xml.bibitem **refitem_render_attrs(match, code) do |t|
137
134
  refitem_render_formattedref(t, match[:text])
138
135
  yr_match = refitem1yr(code[:id])
139
136
  refitem_render1(match, code, t)
@@ -143,6 +140,12 @@ module Metanorma
143
140
  end
144
141
  end
145
142
 
143
+ def refitem_render_attrs(match, code)
144
+ attr_code(anchor: match[:anchor], suppress_identifier: code[:dropid],
145
+ amend: code[:amend], hidden: code[:hidden],
146
+ id: "_#{UUIDTools::UUID.random_create}")
147
+ end
148
+
146
149
  def refitem_render_formattedref(bibitem, title)
147
150
  (title.nil? || title.empty?) and title = @i18n.no_information_available
148
151
  bibitem.formattedref format: "application/x-isodoc+xml" do |i|
@@ -42,8 +42,9 @@ module Metanorma
42
42
  ref, i, doc = results.pop
43
43
  m[i.to_i] = { ref: }
44
44
  if doc.is_a?(RelatonBib::RequestError)
45
- @log.add("Bibliography", nil, "Could not retrieve #{ref[:code]}: " \
46
- "no access to online site", severity: 1)
45
+ @log.add("Bibliography", nil,
46
+ "Could not retrieve #{ref[:code]}: " \
47
+ "no access to online site", severity: 1)
47
48
  else m[i.to_i][:doc] = doc end
48
49
  end
49
50
  end
@@ -130,7 +131,7 @@ module Metanorma
130
131
 
131
132
  def fetch_ref(xml, code, year, **opts)
132
133
  opts[:no_year] and return nil
133
- code = code.sub(/^\([^)]+\)/, "")
134
+ _, code = extract_balanced_parentheses(code)
134
135
  hit = fetch_ref1(code, year, opts) or return nil
135
136
  xml.parent.add_child(smart_render_xml(hit, code, opts))
136
137
  xml
@@ -206,6 +207,7 @@ module Metanorma
206
207
  xml.parent.add_child(smart_render_xml(item[:doc], item[:ref][:code],
207
208
  item[:ref]))
208
209
  use_my_anchor(xml, item[:ref][:match][:anchor],
210
+ amend: item.dig(:ref, :analyse_code, :amend),
209
211
  hidden: item.dig(:ref, :analyse_code, :hidden),
210
212
  dropid: item.dig(:ref, :analyse_code, :dropid))
211
213
  end
@@ -32,16 +32,17 @@ module Metanorma
32
32
  end
33
33
 
34
34
  def use_my_anchor(ref, id, opt)
35
- ref.parent.elements.last["anchor"] = id
36
- add_id(ref.parent.elements.last)
37
- a = opt[:hidden] and ref.parent.elements.last["hidden"] = a
38
- a = opt[:dropid] and
39
- ref.parent.elements.last["suppress_identifier"] = a
35
+ elem = ref.parent.elements.last
36
+ elem["anchor"] = id
37
+ add_id(elem)
38
+ a = opt[:hidden] and elem["hidden"] = a
39
+ a = opt[:amend] and elem["amend"] = a
40
+ a = opt[:dropid] and elem["suppress_identifier"] = a
40
41
  ref
41
42
  end
42
43
 
43
44
  def docid(bib, code, codetype = nil)
44
- type, code1 = if /^\[\d+\]$|^\([^)]+\).*$/.match?(code)
45
+ type, code1 = if /^\[\d+\]$|^\(.+\).*$/.match?(code)
45
46
  ["metanorma", mn_code(code)]
46
47
  elsif %w(attachment repo path).include?(codetype)
47
48
  [nil, code]
@@ -61,45 +62,28 @@ module Metanorma
61
62
  end
62
63
 
63
64
  def mn_code(code)
64
- code.sub(/^\(/, "[").sub(/^([^)]+)\).*$/, "\\1]")
65
+ # Handle balanced parentheses at the start of the string
66
+ balance, remainder = extract_balanced_parentheses(code)
67
+ balance and return "[#{balance}]"
68
+ remainder
65
69
  .sub(/^dropid\((.+)\)$/, "\\1")
66
70
  .sub(/^hidden\((.+)\)$/, "\\1")
67
71
  .sub(/^nofetch\((.+)\)$/, "\\1")
68
72
  .sub(/^local-file\((.+)\)$/, "\\1")
69
- end
70
-
71
- def analyse_ref_localfile(ret)
72
- m = /^local-file\((?:(?<source>[^,)]+),\s*)?(?<id>[^)]+)\)$/
73
- .match(ret[:id])
74
- m or return ret
75
- ret.merge(id: m[:id], localfile: m[:source] || "default")
76
- end
77
-
78
- def analyse_ref_nofetch(ret)
79
- m = /^nofetch\((?<id>.+)\)$/.match(ret[:id]) or return ret
80
- ret.merge(id: m[:id], nofetch: true)
81
- end
82
-
83
- def analyse_ref_hidden(ret)
84
- m = /^hidden\((?<id>.+)\)$/.match(ret[:id]) or return ret
85
- ret.merge(id: m[:id], hidden: true)
86
- end
87
-
88
- def analyse_ref_dropid(ret)
89
- m = /^dropid\((?<id>.+)\)$/.match(ret[:id]) or return ret
90
- ret.merge(id: m[:id], dropid: true)
73
+ .sub(/^amend\((.+)\)$/, "\\1")
91
74
  end
92
75
 
93
76
  def analyse_ref_repo_path(ret)
94
- m = /^(?<type>repo|path|attachment):\((?<key>[^,)]+),?(?<id>[^)]*)\)$/
95
- .match(ret[:id]) or return ret
96
- id = if m[:id].empty?
97
- if m[:type] == "attachment"
98
- "(#{m[:key]})"
99
- else m[:key].sub(%r{^[^/]+/}, "")
100
- end
101
- else m[:id] end
102
- ret.merge(id:, type: m[:type], key: m[:key], nofetch: true)
77
+ %i(repo path attachment).each do |type|
78
+ ret[type] or next
79
+ id = if ret[:id].empty?
80
+ if type == :attachment then "(#{ret[type]})"
81
+ else ret[type].sub(%r{^[^/]+/}, "")
82
+ end
83
+ else ret[:id] end
84
+ ret.merge!(id: id, type: type.to_s, key: ret[type], nofetch: true)
85
+ end
86
+ ret
103
87
  end
104
88
 
105
89
  def analyse_ref_numeric(ret)
@@ -107,16 +91,6 @@ module Metanorma
107
91
  ret.merge(numeric: true)
108
92
  end
109
93
 
110
- def analyse_ref_dual(ret)
111
- m = /^(?<type>merge|dual)\((?<keys>.+)\)$/.match(ret[:id]) or
112
- return ret
113
- line = CSV.parse_line(m[:keys], liberal_parsing: true) or return ret
114
- line.size > 1 or return ret
115
- ret[:id] = line.first
116
- ret[m[:type].to_sym] = line[1..].map(&:strip)
117
- ret
118
- end
119
-
120
94
  def analyse_ref_code(code)
121
95
  ret = { id: code }
122
96
  code.nil? || code.empty? and return ret
@@ -166,23 +140,49 @@ module Metanorma
166
140
  end
167
141
  end
168
142
 
143
+ def analyse_ref_code_nested(ret)
144
+ opts, id = parse_ref_code_nested({}, ret[:id])
145
+ ret[:id] = id
146
+ ret.merge!(opts)
147
+ analyse_ref_numeric(ret)
148
+ analyse_ref_repo_path(ret)
149
+ ret
150
+ end
151
+
169
152
  # ref id = (usrlbl)code[:-]year
170
153
  # code = \[? number \]? | ident | nofetch(code) | hidden(code) |
171
- # dropid(code) | # (repo|path|attachment):(key,code) |
154
+ # dropid(code) | amend(code) | (repo|path|attachment):(key,code) |
172
155
  # local-file(source,? key) |
173
156
  # merge(code, code) | dual(code, code)
174
- def analyse_ref_code_nested(ret)
175
- analyse_ref_dual(
176
- analyse_ref_numeric(
177
- analyse_ref_repo_path(
178
- analyse_ref_dropid(
179
- analyse_ref_hidden(
180
- analyse_ref_nofetch(analyse_ref_localfile(ret)),
181
- ),
182
- ),
183
- ),
184
- ),
185
- )
157
+ def parse_ref_code_nested(ret, ident)
158
+ keys = %w(nofetch hidden dropid local-file repo path attachment merge
159
+ dual amend)
160
+ if (m = /^(?<key>[a-z-]+):?\((?<val>.*)\)$/.match(ident)) &&
161
+ keys.include?(m[:key])
162
+ case m[:key]
163
+ when "nofetch", "hidden", "dropid", "amend"
164
+ ret[m[:key].to_sym] = true
165
+ parse_ref_code_nested(ret, m[:val])
166
+ when "repo", "path", "attachment"
167
+ kv = m[:val].split(",", 2).map(&:strip)
168
+ ret[m[:key].to_sym] = kv[0]
169
+ parse_ref_code_nested(ret, kv.size == 1 ? "" : kv[1])
170
+ when "local-file"
171
+ kv = m[:val].split(",", 2).map(&:strip)
172
+ source = kv.size == 1 ? "default" : kv[0]
173
+ ret[:localfile] = source
174
+ parse_ref_code_nested(ret, kv[-1])
175
+ when "merge", "dual"
176
+ line = CSV.parse_line(m[:val],
177
+ liberal_parsing: true) or return [ret, ident]
178
+ line.size > 1 or return [ret, ident]
179
+ ret[:id] = line.first
180
+ ret[m[:key].to_sym] = line[1..].map(&:strip)
181
+ [ret, ret[:id]]
182
+ end
183
+
184
+ else [ret, ident]
185
+ end
186
186
  end
187
187
 
188
188
  # if no year is supplied, interpret as no_year reference
@@ -221,6 +221,28 @@ module Metanorma
221
221
  @type = 'DOI' or @type = 'doi' or @type = 'ISSN' or @type = 'issn' or @type = 'ISBN' or @type = 'isbn' or starts-with(@type, 'ISSN.') or starts-with(@type, 'ISBN.') or starts-with(@type, 'issn.') or starts-with(@type, 'isbn.')
222
222
  XPATH
223
223
  end
224
+
225
+ private
226
+
227
+ def extract_balanced_parentheses(code)
228
+ code.start_with?("(") or return [nil, code]
229
+ paren_count = 0
230
+ # Find the matching closing parenthesis
231
+ code.each_char.with_index do |char, index|
232
+ case char
233
+ when "(" then paren_count += 1
234
+ when ")"
235
+ paren_count -= 1
236
+ paren_count.zero? or next
237
+ # Found the matching closing parenthesis
238
+ content = code[1...index] # Extract content between parentheses
239
+ remaining = code[(index + 1)..] || "" # Get remaining string
240
+ return [content, remaining]
241
+ end
242
+ end
243
+ # If we get here, parentheses are unbalanced - return original
244
+ [nil, code]
245
+ end
224
246
  end
225
247
  end
226
248
  end
@@ -52,27 +52,28 @@ module Metanorma
52
52
 
53
53
  ISO_REF =
54
54
  %r{^<ref\sid="(?<anchor>[^"]+)">
55
- \[(?<usrlbl>\([^)]+\))?(?<code>(?:ISO|IEC)[^0-9]*\s[0-9-]+|IEV)
55
+ \[(?<usrlbl>\(.+\))?(?<code>(?:ISO|IEC)[^0-9]*\s[0-9-]+|IEV)
56
56
  (?::(?<year>[0-9][0-9-]+))?\]</ref>,?\s*(?<text>.*)$}xm
57
57
 
58
58
  ISO_REF_NO_YEAR =
59
59
  %r{^<ref\sid="(?<anchor>[^"]+)">
60
- \[(?<usrlbl>\([^)]+\))?(?<code>(?:ISO|IEC)[^0-9]*\s[0-9-]+):
60
+ \[(?<usrlbl>\(.+\))?(?<code>(?:ISO|IEC)[^0-9]*\s[0-9-]+):
61
61
  (?:--|–|—|&\#821[12];)\]</ref>,?\s*
62
62
  (?:<fn[^>]*>\s*<p>(?<fn>[^\]]+)</p>\s*</fn>)?,?\s?(?<text>.*)$}xm
63
63
 
64
64
  ISO_REF_ALL_PARTS =
65
65
  %r{^<ref\sid="(?<anchor>[^"]+)">
66
- \[(?<usrlbl>\([^)]+\))?(?<code>(?:ISO|IEC)[^0-9]*\s[0-9]+)
66
+ \[(?<usrlbl>\(.+\))?(?<code>(?:ISO|IEC)[^0-9]*\s[0-9]+)
67
67
  (?::(?<year>--|–|—|&\#821[12];|[0-9][0-9-]+))?\s
68
68
  \(all\sparts\)\]</ref>,?\s*
69
69
  (?:<fn[^>]*>\s*<p>(?<fn>[^\]]+)</p>\s*</fn>,?\s?)?(?<text>.*)$}xm
70
70
 
71
+ # These regexes allow () inside usrlbl but not inside code
71
72
  NON_ISO_REF = %r{^<ref\sid="(?<anchor>[^"]+)">
72
- \[(?<usrlbl>\([^)]+\))?(?<code>.+?)\]</ref>,?\s*(?<text>.*)$}xm
73
+ \[(?<usrlbl>\(.+\))?(?<code>.+?)\]</ref>,?\s*(?<text>.*)$}xm
73
74
 
74
75
  NON_ISO_REF1 = %r{^<ref\sid="(?<anchor>[^"]+)">
75
- (?<usrlbl>\([^)]+\))?(?<code>.+?)</ref>,?\s*(?<text>.*)$}xm
76
+ (?<usrlbl>\(.+\))?(?<code>.+?)</ref>,?\s*(?<text>.*)$}xm
76
77
  end
77
78
  end
78
79
  end
@@ -73,6 +73,7 @@ module Metanorma
73
73
  end
74
74
 
75
75
  def preface_main_filter(ret, node)
76
+ node.level > 1 and return ret
76
77
  start_main_section(ret, node)
77
78
  @preface && self.class::MAIN_CLAUSE_NAMES.include?(ret) and return nil
78
79
  !@preface && self.class::PREFACE_CLAUSE_NAMES.include?(ret) and
@@ -87,8 +87,10 @@ module Metanorma
87
87
  conv = presentation_xml_converter(EmptyAttr.new)
88
88
  i18n = conv.i18n_init(lang, script, locale, i18nyaml)
89
89
  conv.metadata_init(lang, script, locale, i18n)
90
+ conv.meta.localdir = @localdir
90
91
  conv.xref_init(lang, script, nil, i18n, {})
91
92
  conv.xrefs.klass.meta = conv.meta
93
+ conv.xrefs.klass.localdir = @localdir
92
94
  conv
93
95
  end
94
96
 
@@ -19,6 +19,6 @@ module Metanorma
19
19
  end
20
20
 
21
21
  module Standoc
22
- VERSION = "3.1.2".freeze
22
+ VERSION = "3.1.4".freeze
23
23
  end
24
24
  end
@@ -57,6 +57,6 @@ Gem::Specification.new do |spec|
57
57
  spec.add_development_dependency "timecop", "~> 0.9"
58
58
  spec.add_development_dependency "vcr", "~> 6.1.0"
59
59
  spec.add_development_dependency "webmock"
60
- spec.add_development_dependency "xml-c14n"
60
+ spec.add_development_dependency "canon"
61
61
  # spec.metadata["rubygems_mfa_required"] = "true"
62
62
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: metanorma-standoc
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.2
4
+ version: 3.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-07-21 00:00:00.000000000 Z
11
+ date: 2025-08-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -389,7 +389,7 @@ dependencies:
389
389
  - !ruby/object:Gem::Version
390
390
  version: '0'
391
391
  - !ruby/object:Gem::Dependency
392
- name: xml-c14n
392
+ name: canon
393
393
  requirement: !ruby/object:Gem::Requirement
394
394
  requirements:
395
395
  - - ">="
@@ -519,6 +519,7 @@ files:
519
519
  - lib/metanorma/standoc/datamodel/diagram_preprocessor.rb
520
520
  - lib/metanorma/standoc/datamodel/plantuml_renderer.rb
521
521
  - lib/metanorma/standoc/front.rb
522
+ - lib/metanorma/standoc/front_committee.rb
522
523
  - lib/metanorma/standoc/front_contributor.rb
523
524
  - lib/metanorma/standoc/front_organisation.rb
524
525
  - lib/metanorma/standoc/init.rb