metanorma-standoc 3.1.6 → 3.1.7

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: 9bc2bfaaaf9871ba417d1d455867a0d67b6df3f219886aa290a6195583eacafc
4
- data.tar.gz: 3f2a6511903492d4853334fc0780143a7b01d13e8d8f6d27630631208fc10b5d
3
+ metadata.gz: 154ca8c34101aef7d0dfb0cad7c1bf0cfafaf3df110a0fef56a7d524b17a7d5b
4
+ data.tar.gz: 7904a365e5bbaf50e159e164c5fc11e74d356f48a50de19889433f015eb9040d
5
5
  SHA512:
6
- metadata.gz: 3485d18dfbe8b3474770f3e97d80652225539cca579b06cf1f0ca9a2ebe414c8ba1663a9b099eabecdea30cd3c630c2bebc54dfc1eb7dd954ffe89155d9d1da4
7
- data.tar.gz: 8953152a143fbc28f9117e1f593b47e26f06b54eebd6498b71b98c5b5ea91a2240e41fae83af96d5f0a1ae618ebf0cb0d55b6f787db68d4ceb3509937493413c
6
+ metadata.gz: 5d50a9d2df00a760fe948b97d28f50652c09fe313942a48d2648018a68edf4e783d62288d965e9a34225b4804bdae4bf483f4c47317fcb2188590ecf4b4495e5
7
+ data.tar.gz: 616012d1b9ceaafbfd255aed0d477e66e201affece833b21413529c7ee2172c708400bc63422f3b29d460ca1d5b787a420f111b7cbfa7ec11deb46f103da893c
@@ -9,6 +9,7 @@ require "metanorma-utils"
9
9
  require_relative "render"
10
10
  require_relative "localbib"
11
11
  require_relative "init"
12
+ require_relative "isolated_converter"
12
13
  require "mn-requirements"
13
14
 
14
15
  module Asciidoctor
@@ -20,6 +21,8 @@ end
20
21
  module Metanorma
21
22
  module Standoc
22
23
  module Base
24
+ include IsolatedConverter
25
+
23
26
  # XML_ROOT_TAG = "standard-document".freeze
24
27
  # XML_NAMESPACE = "https://www.metanorma.org/ns/standoc".freeze
25
28
  FONTS_MANIFEST = "fonts-manifest".freeze
@@ -107,10 +110,14 @@ module Metanorma
107
110
  result = makexml1(node)
108
111
  ret1 = cleanup(Nokogiri::XML(insert_xml_cr(result)))
109
112
  ret1.root.add_namespace(nil, xml_namespace)
110
- validate(ret1) unless @novalid
113
+ validate(ret1) unless @novalid || in_isolated_conversion?
111
114
  ret1
112
115
  end
113
116
 
117
+ def in_isolated_conversion?
118
+ !@isolated_conversion_stack.empty?
119
+ end
120
+
114
121
  def draft?
115
122
  @draft
116
123
  end
@@ -134,7 +134,7 @@ module Metanorma
134
134
  verbal-definition non-verbal-representation}.freeze
135
135
 
136
136
  # it seems Nokogiri::XML is treating the content of <script> as cdata,
137
- # because of its use in HTML. Bad nokogiri. Undoing that, since we use
137
+ # because of its use in HTML. Bad Nokogiri. Undoing that, since we use
138
138
  # script as a normal tag
139
139
  def script_cleanup(xmldoc)
140
140
  xmldoc.xpath("//script").each { |x| x.content = x.to_str }
@@ -0,0 +1,71 @@
1
+ module Metanorma
2
+ module Standoc
3
+ module Cleanup
4
+ def attachment_cleanup(xmldoc)
5
+ xmldoc.xpath("//bibitem[uri/@type = 'attachment']").each do |b|
6
+ b["hidden"] = "true"
7
+ b.at("./docidentifier[@type = 'title']")&.remove
8
+ u = b.at("./uri[@type = 'attachment']")
9
+ c = b.at("./uri[@type = 'citation']") ||
10
+ u.after("<uri type='citation'/>")
11
+ uri = attachment_uri(u.text, b)
12
+ u.children = uri
13
+ c.children = uri
14
+ end
15
+ end
16
+
17
+ def attachment_uri(path, bib)
18
+ init_attachments
19
+ path = File.join(@localdir, path)
20
+ valid_attachment?(path, bib) or return ""
21
+ @datauriattachment or return attachment_location(path)
22
+ save_attachment(path, bib)
23
+ end
24
+
25
+ def save_attachment(path, bib)
26
+ init_attachments
27
+ f = File.basename(path)
28
+ File.exist?(File.join(@attachmentsdir, f)) and
29
+ f += "_#{UUIDTools::UUID.random_create}"
30
+ out_fld = File.join(@attachmentsdir, f)
31
+ FileUtils.cp(path, out_fld)
32
+ datauri_attachment(out_fld, bib.document)
33
+ end
34
+
35
+ def attachment_location(path)
36
+ f = path
37
+ @datauriattachment and
38
+ f = File.join(@attachmentsdir, File.basename(path))
39
+ Pathname.new(File.expand_path(f))
40
+ .relative_path_from(Pathname.new(File.expand_path(@output_dir))).to_s
41
+ end
42
+
43
+ def datauri_attachment(path, doc)
44
+ @datauriattachment or return
45
+ m = add_misc_container(doc)
46
+ f = attachment_location(path)
47
+ e = (m << "<attachment name='#{f}'/>").last_element_child
48
+ Vectory::Utils::datauri(path, @output_dir).scan(/.{1,60}/)
49
+ .each { |dd| e << "#{dd}\n" }
50
+ f
51
+ end
52
+
53
+ def valid_attachment?(path, bib)
54
+ File.exist?(path) and return true
55
+ p = Pathname.new(path).cleanpath
56
+ @log.add("Bibliography", bib, "Attachment #{p} does not exist",
57
+ severity: 0)
58
+ false
59
+ end
60
+
61
+ def init_attachments
62
+ @datauriattachment or return
63
+ @attachmentsdir and return
64
+ @attachmentsfld = "_#{@filename}_attachments"
65
+ @attachmentsdir = File.join(@output_dir, @attachmentsfld)
66
+ FileUtils.rm_rf(@attachmentsdir)
67
+ FileUtils.mkdir_p(@attachmentsdir)
68
+ end
69
+ end
70
+ end
71
+ end
@@ -119,9 +119,9 @@ module Metanorma
119
119
  end
120
120
 
121
121
  def hdr2bibitem(hdr)
122
- xml = Asciidoctor
123
- .convert(hdr[:text], backend: hdr2bibitem_type(hdr),
124
- header_footer: true)
122
+ xml = isolated_asciidoctor_convert(hdr[:text],
123
+ backend: hdr2bibitem_type(hdr),
124
+ header_footer: true)
125
125
  b = Nokogiri::XML(xml).at("//xmlns:bibdata")
126
126
  b.name = "bibitem"
127
127
  b.delete("type")
@@ -1,3 +1,5 @@
1
+ require_relative "cleanup_attachment"
2
+
1
3
  module Metanorma
2
4
  module Standoc
3
5
  module Cleanup
@@ -145,71 +147,6 @@ module Metanorma
145
147
  end
146
148
  end
147
149
 
148
- def attachment_cleanup(xmldoc)
149
- xmldoc.xpath("//bibitem[uri/@type = 'attachment']").each do |b|
150
- b["hidden"] = "true"
151
- u = b.at("./uri[@type = 'attachment']")
152
- c = b.at("./uri[@type = 'citation']") ||
153
- u.after("<uri type='citation'/>")
154
- uri = attachment_uri(u.text, b)
155
- u.children = uri
156
- c.children = uri
157
- end
158
- end
159
-
160
- def attachment_uri(path, bib)
161
- init_attachments
162
- path = File.join(@localdir, path)
163
- valid_attachment?(path, bib) or return ""
164
- @datauriattachment or return attachment_location(path)
165
- save_attachment(path, bib)
166
- end
167
-
168
- def save_attachment(path, bib)
169
- init_attachments
170
- f = File.basename(path)
171
- File.exist?(File.join(@attachmentsdir, f)) and
172
- f += "_#{UUIDTools::UUID.random_create}"
173
- out_fld = File.join(@attachmentsdir, f)
174
- FileUtils.cp(path, out_fld)
175
- datauri_attachment(out_fld, bib.document)
176
- end
177
-
178
- def attachment_location(path)
179
- f = path
180
- @datauriattachment and
181
- f = File.join(@attachmentsdir, File.basename(path))
182
- Pathname.new(File.expand_path(f))
183
- .relative_path_from(Pathname.new(File.expand_path(@output_dir))).to_s
184
- end
185
-
186
- def datauri_attachment(path, doc)
187
- @datauriattachment or return
188
- m = add_misc_container(doc)
189
- f = attachment_location(path)
190
- e = (m << "<attachment name='#{f}'/>").last_element_child
191
- Vectory::Utils::datauri(path, @output_dir).scan(/.{1,60}/)
192
- .each { |dd| e << "#{dd}\n" }
193
- f
194
- end
195
-
196
- def valid_attachment?(path, bib)
197
- File.exist?(path) and return true
198
- p = Pathname.new(path).cleanpath
199
- @log.add("Bibliography", bib, "Attachment #{p} does not exist",
200
- severity: 0)
201
- false
202
- end
203
-
204
- def init_attachments
205
- @datauriattachment or return
206
- @attachmentsdir and return
207
- @attachmentsfld = "_#{@filename}_attachments"
208
- @attachmentsdir = File.join(@output_dir, @attachmentsfld)
209
- FileUtils.rm_rf(@attachmentsdir)
210
- FileUtils.mkdir_p(@attachmentsdir)
211
- end
212
-
213
150
  # remove dupes if both same ID and same docid, in case dupes introduced
214
151
  # through termbases
215
152
  def remove_dup_bibtem_id(xmldoc)
@@ -229,12 +166,42 @@ module Metanorma
229
166
  end
230
167
  end
231
168
 
169
+ def remove_empty_docid(xmldoc)
170
+ xmldoc.xpath("//bibitem/docidentifier[normalize-space(.)='']")
171
+ .each(&:remove)
172
+ end
173
+
174
+ def empty_docid_to_title(xmldoc)
175
+ xmldoc.xpath("//references/bibitem").each do |b|
176
+ b.at("./docidentifier[not(@type = 'metanorma' or @type = 'DOI' or " \
177
+ "@type = 'metanorma-ordinal')]") and next
178
+ empty_docid_to_title?(b) or next
179
+ ins = b.at("./title[last()]") || b.at("./formattedref")
180
+ id = bibitem_title_to_id(b) or return
181
+ ins.next = <<~XML
182
+ <docidentifier type='title' primary='true'>#{id}</docidentifier>
183
+ XML
184
+ end
185
+ end
186
+
187
+ def bibitem_title_to_id(bibitem)
188
+ t = bibitem.at("./title") || bibitem.at("./formattedref") or return
189
+ t.text
190
+ end
191
+
192
+ # normative references only, biblio uses ordinal code instead
193
+ def empty_docid_to_title?(bibitem)
194
+ bibitem.parent["normative"] == "true"
195
+ end
196
+
232
197
  def bibitem_cleanup(xmldoc)
233
198
  bibitem_nested_id(xmldoc) # feeds remove_dup_bibtem_id
234
- remove_dup_bibtem_id(xmldoc)
235
199
  ref_dl_cleanup(xmldoc)
236
200
  formattedref_spans(xmldoc)
237
201
  fetch_local_bibitem(xmldoc)
202
+ remove_empty_docid(xmldoc)
203
+ empty_docid_to_title(xmldoc)
204
+ remove_dup_bibtem_id(xmldoc)
238
205
  attachment_cleanup(xmldoc)
239
206
  end
240
207
  end
@@ -163,7 +163,7 @@ module Metanorma
163
163
  .each_slice(4).map.with_object([]) do |a, acc|
164
164
  acc << safe_noko(a[0], node.document)
165
165
  a.size == 4 or next
166
- acc << Asciidoctor.convert(
166
+ acc << isolated_asciidoctor_convert(
167
167
  a[2], doctype: :inline, backend: backend&.to_sym || :standoc
168
168
  )
169
169
  end.join
@@ -7,7 +7,9 @@ module Metanorma
7
7
  def metadata_author(node, xml)
8
8
  org_author(node, xml)
9
9
  personal_author(node, xml)
10
- committee_contributors(node, xml, default_publisher, {})
10
+ corporate_author = node.attr("corporate-author") ||
11
+ node.attr("publisher") || default_publisher
12
+ committee_contributors(node, xml, corporate_author, {})
11
13
  end
12
14
 
13
15
  def org_author(node, xml)
@@ -49,6 +49,7 @@ module Metanorma
49
49
  @localdir = Metanorma::Utils::localdir(node)
50
50
  @xrefstyle = node.attr("xrefstyle")
51
51
  @novalid = node.attr("novalid")
52
+ @isolated_conversion_stack = []
52
53
  @smartquotes = node.attr("smartquotes") != "false"
53
54
  @sourcecode_markup_start = node.attr("sourcecode-markup-start") || "{{{"
54
55
  @sourcecode_markup_end = node.attr("sourcecode-markup-end") || "}}}"
@@ -0,0 +1,65 @@
1
+ module Metanorma
2
+ module Standoc
3
+ module IsolatedConverter
4
+ # Create an isolated Asciidoctor conversion that doesn't interfere with
5
+ # the current converter's instance variables
6
+ def isolated_asciidoctor_convert(content, options = {})
7
+ # Track that we're in an isolated conversion (for nested calls)
8
+ @isolated_conversion_stack << true
9
+
10
+ begin
11
+ # Ensure we get a completely fresh Document and conversion context
12
+ # Each call to Asciidoctor.convert creates a new Document with its own converter
13
+ # This naturally isolates the conversion from the current instance variables
14
+
15
+ # Save critical options that should be preserved from the current context
16
+ preserved_options = extract_preserved_options(options)
17
+
18
+ # Merge with isolated options to ensure clean state and skip validation
19
+ isolated_options = preserved_options.merge(options).merge(
20
+ attributes: (preserved_options[:attributes] || {}).merge(
21
+ 'novalid' => '' # Force no validation for isolated documents
22
+ )
23
+ )
24
+
25
+ # Perform the isolated conversion
26
+ Asciidoctor.convert(content, isolated_options)
27
+ ensure
28
+ # Always pop from stack, even if conversion fails
29
+ @isolated_conversion_stack.pop
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ # Extract options that should be preserved from the current conversion context
36
+ def extract_preserved_options(user_options)
37
+ options = {}
38
+
39
+ # Preserve safe mode to maintain security context
40
+ options[:safe] = user_options[:safe] if user_options.key?(:safe)
41
+
42
+ # Preserve local directory context if not explicitly overridden
43
+ options[:base_dir] = @localdir if @localdir && !user_options.key?(:base_dir)
44
+
45
+ # Preserve attributes that are safe to share
46
+ if user_options[:attributes].nil? && respond_to?(:safe_shared_attributes)
47
+ options[:attributes] = safe_shared_attributes
48
+ end
49
+
50
+ options
51
+ end
52
+
53
+ # Define attributes that are safe to share between converter instances
54
+ def safe_shared_attributes
55
+ # Only include read-only or configuration attributes
56
+ # Avoid any attributes that could cause state pollution
57
+ {
58
+ 'source-highlighter' => 'html-pipeline', # Use simple highlighter
59
+ 'nofooter' => '',
60
+ 'no-header-footer' => ''
61
+ }
62
+ end
63
+ end
64
+ end
65
+ end
@@ -71,11 +71,11 @@ module Metanorma
71
71
 
72
72
  # These regexes allow () inside usrlbl but not inside code
73
73
  NON_ISO_REF = %r{^<ref\sid="(?<anchor>[^"]+)">
74
- \[(?<usrlbl>\(.+\))?(?<code>.+?)\]</ref>,?\s*
74
+ \[(?<usrlbl>\(.+\))?(?<code>.+)\]</ref>,?\s*
75
75
  (?:<fn[^>]*>\s*<p>(?<fn>[^\]]+)</p>\s*</fn>)?(?<text>.*)$}xm
76
76
 
77
77
  NON_ISO_REF1 = %r{^<ref\sid="(?<anchor>[^"]+)">
78
- (?<usrlbl>\(.+\))?(?<code>.+?)</ref>,?\s*
78
+ (?<usrlbl>\(.+\))?(?<code>.+)</ref>,?\s*
79
79
  (?:<fn[^>]*>\s*<p>(?<fn>[^\]]+)</p>\s*</fn>\s*)?(?<text>.*)$}xm
80
80
  end
81
81
  end
@@ -16,11 +16,19 @@ module Metanorma
16
16
  @spans[:docid] = override_docids(ids[:docid], @spans[:docid])
17
17
  end
18
18
 
19
+ # override old values with new values if type is the same
20
+ # comparison is case-insensitive
21
+ # if types differ in case, use the old value's type, not the new
19
22
  def override_docids(old, new)
20
23
  ret = new
21
- keys = new.map { |a| a[:type] }
24
+ keys = new.map { |a| a[:type]&.upcase }
22
25
  old.each do |e|
23
- keys.include?(e[:type]) or ret << e
26
+ if keys.include?(e[:type]&.upcase)
27
+ ret.each do |a|
28
+ a[:type]&.upcase == e[:type]&.upcase and a[:type] = e[:type]
29
+ end
30
+ else ret << e
31
+ end
24
32
  end
25
33
  ret
26
34
  end
@@ -119,7 +119,7 @@ module Metanorma
119
119
  end
120
120
 
121
121
  def spans_preprocess_new_contrib?(span, contrib)
122
- contrib.empty? ||
122
+ contrib.empty? || contrib[-1][:entity] == "organization" ||
123
123
  (span[:key] == "surname" && contrib[-1][:surname]) ||
124
124
  contrib[-1][:role] != (span[:type] || "author")
125
125
  end
@@ -155,7 +155,7 @@ module Metanorma
155
155
 
156
156
  #{text}
157
157
  ADOC
158
- c = Asciidoctor.convert(doc, backend: flavour, header_footer: true)
158
+ c = isolated_asciidoctor_convert(doc, backend: flavour, header_footer: true)
159
159
  ret = Nokogiri::XML(c).at("//xmlns:sections")
160
160
  separate_numbering_footnotes(ret)
161
161
  end
@@ -19,6 +19,6 @@ module Metanorma
19
19
  end
20
20
 
21
21
  module Standoc
22
- VERSION = "3.1.6".freeze
22
+ VERSION = "3.1.7".freeze
23
23
  end
24
24
  end
@@ -32,7 +32,7 @@ Gem::Specification.new do |spec|
32
32
  spec.add_dependency "asciidoctor", "~> 2.0.0"
33
33
  spec.add_dependency "crass", "~> 1.0.0"
34
34
  spec.add_dependency "iev", "~> 0.3.5"
35
- spec.add_dependency "isodoc", "~> 3.2.0"
35
+ spec.add_dependency "isodoc", "~> 3.3.0"
36
36
  spec.add_dependency "metanorma", ">= 1.6.0"
37
37
  spec.add_dependency "metanorma-plugin-glossarist", "~> 0.2.3"
38
38
  spec.add_dependency "metanorma-plugin-lutaml", "~> 0.7.31"
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.6
4
+ version: 3.1.7
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-09-15 00:00:00.000000000 Z
11
+ date: 2025-09-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 3.2.0
75
+ version: 3.3.0
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 3.2.0
82
+ version: 3.3.0
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: metanorma
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -501,6 +501,7 @@ files:
501
501
  - lib/metanorma/standoc/cleanup.rb
502
502
  - lib/metanorma/standoc/cleanup_amend.rb
503
503
  - lib/metanorma/standoc/cleanup_asciibib.rb
504
+ - lib/metanorma/standoc/cleanup_attachment.rb
504
505
  - lib/metanorma/standoc/cleanup_bibdata.rb
505
506
  - lib/metanorma/standoc/cleanup_bibitem.rb
506
507
  - lib/metanorma/standoc/cleanup_block.rb
@@ -534,6 +535,7 @@ files:
534
535
  - lib/metanorma/standoc/inline.rb
535
536
  - lib/metanorma/standoc/isodoc-compile.rng
536
537
  - lib/metanorma/standoc/isodoc.rng
538
+ - lib/metanorma/standoc/isolated_converter.rb
537
539
  - lib/metanorma/standoc/lists.rb
538
540
  - lib/metanorma/standoc/localbib.rb
539
541
  - lib/metanorma/standoc/macros.rb