isodoc 3.4.7 → 3.4.9

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.
@@ -18,6 +18,7 @@ module IsoDoc
18
18
 
19
19
  class Counter
20
20
  include OlTypeProvider
21
+
21
22
  attr_accessor :prefix_override
22
23
 
23
24
  def initialize(num = 0, opts = { numerals: :arabic })
@@ -57,7 +58,7 @@ module IsoDoc
57
58
  end
58
59
 
59
60
  def new_subseq_increment1(node)
60
- /^(?<b>.*?)(?<n>\d*)(?<a>[a-zA-Z]*)$/ =~ node["number"]
61
+ b, n, a = parse_number_suffix(node["number"])
61
62
  if !n.empty? || !a.empty?
62
63
  @letter_override = @letter = a unless a.empty?
63
64
  @number_override = @num = n.to_i unless n.empty?
@@ -73,7 +74,10 @@ module IsoDoc
73
74
  @prefix_override = node["branch-number"]
74
75
  elsif node["number"]
75
76
  @base = @letter_override = @number_override = ""
76
- /^(?<b>.*?)(?<n>\d+)$/ =~ node["number"]
77
+ b, n, a = parse_number_suffix(node["number"])
78
+ # Original required digits at the absolute end (no trailing letters).
79
+ # If there are trailing letters, treat as a no-digit-at-end match.
80
+ n = "" unless a.empty?
77
81
  if blank?(n)
78
82
  @num = nil
79
83
  @base = node["number"][0..-2]
@@ -92,7 +96,16 @@ module IsoDoc
92
96
 
93
97
  @base = ""
94
98
  @letter_override = node["number"]
95
- /^(?<b>.*?)(?<n>\d*)(?<a>[a-zA-Z])$/ =~ node["number"]
99
+ # Replace polynomial /^(?<b>.*?)(?<n>\d*)(?<a>[a-zA-Z])$/ with
100
+ # sequential parsing from the right: last char must be a single letter;
101
+ # before it are optional digits; before that is the base prefix b.
102
+ a = /[a-zA-Z]/.match?(node["number"][-1]) ? node["number"][-1] : nil
103
+ if a
104
+ rest = node["number"][..-2]
105
+ j = rest.rindex(/[^\d]/)
106
+ n = j.nil? ? rest : rest[(j + 1)..]
107
+ b = j.nil? ? "" : rest[..j]
108
+ end
96
109
  if blank?(a) then subsequence_increment_no_letter(node)
97
110
  else
98
111
  @letter_override = @letter = a
@@ -174,6 +187,26 @@ module IsoDoc
174
187
  "#{prefix}#{@base}#{out}#{@letter_override || @letter}"
175
188
  end
176
189
 
190
+ # Decompose a counter number string into [prefix, digits, letters]
191
+ # by scanning from the right. Examples:
192
+ # "A1b" => ["A", "1", "b" ]
193
+ # "prefix-3" => ["prefix-", "3", "" ]
194
+ # "ABC" => ["", "", "ABC" ]
195
+ # "42" => ["", "42", "" ]
196
+ # Replaces polynomial regexes like /^(?<b>.*?)(?<n>\d*)(?<a>[a-zA-Z]*)$/
197
+ # with O(n) rindex operations that cannot backtrack.
198
+ def parse_number_suffix(str)
199
+ # Step 1: split off all trailing letters
200
+ i = str.rindex(/[^a-zA-Z]/) # index of last non-letter char
201
+ a = i.nil? ? str : str[(i + 1)..]
202
+ rest = i.nil? ? "" : str[..i]
203
+ # Step 2: split off all trailing digits from the remaining prefix
204
+ j = rest.rindex(/[^\d]/) # index of last non-digit char
205
+ n = j.nil? ? rest : rest[(j + 1)..]
206
+ b = j.nil? ? "" : rest[..j]
207
+ [b, n, a]
208
+ end
209
+
177
210
  def listlabel(list, depth)
178
211
  case ol_type(list, depth)
179
212
  when :arabic then @num.to_s
@@ -11,34 +11,43 @@ module IsoDoc
11
11
  end
12
12
  end
13
13
 
14
- def amend_preprocess1(amend, subclause: false)
15
- autonum = amend_autonums(amend)
14
+ def amend_preprocess1(amend, subclause: false, autonum: {})
15
+ newautonum = amend_autonums(amend).merge(autonum)
16
16
  NUMBERED_BLOCKS.each do |b|
17
- amend_blocks(amend, autonum, b, subclause)
17
+ amend_blocks(amend, newautonum, autonum, b, subclause)
18
18
  end
19
- amend.xpath(ns(".#{amend_newcontent(subclause)}/clause")).each do |c|
20
- amend_preprocess1(c, subclause: true)
19
+ amend.xpath(ns(".#{amend_newcontent(subclause)}/clause"))
20
+ .each_with_index do |c, i|
21
+ amend_autonumber(c, "clause", newautonum, autonum, i)
22
+ amend_preprocess1(c, subclause: true, autonum: newautonum)
21
23
  end
22
24
  end
23
25
 
24
26
  def amend_autonums(amend)
25
- autonum = {}
26
- amend.xpath(ns("./autonumber")).each do |n|
27
- autonum[n["type"]] = n.text
27
+ amend.xpath(ns("./autonumber")).each_with_object({}) do |n, m|
28
+ m[n["type"]] = n.text
28
29
  end
29
- autonum
30
30
  end
31
31
 
32
- def amend_blocks(amend, autonum, blocktype, subclause)
32
+ def amend_blocks(amend, autonum, old_autonum, blocktype, subclause)
33
33
  newc = amend_newcontent(subclause)
34
34
  (amend.xpath(ns(".#{newc}//#{blocktype}")) -
35
35
  amend.xpath(ns(".#{newc}/clause//#{blocktype}")))
36
36
  .each_with_index do |e, i|
37
- autonum[blocktype] && i.zero? and e["number"] = autonum[blocktype]
38
- !autonum[blocktype] and e["unnumbered"] = "true"
37
+ amend_autonumber(e, blocktype, autonum, old_autonum, i)
39
38
  end
40
39
  end
41
40
 
41
+ # only autonumber first instance of an asset in a subclause,
42
+ # if the autonumbering for that asset isn't a continuation of
43
+ # its ancestor clause
44
+ def amend_autonumber(node, blocktype, autonum, old_autonum, idx)
45
+ autonum[blocktype] && idx.zero? &&
46
+ autonum[blocktype] != old_autonum[blocktype] and
47
+ node["number"] = autonum[blocktype]
48
+ !autonum[blocktype] and node["unnumbered"] = "true"
49
+ end
50
+
42
51
  def amend_newcontent(subclause)
43
52
  subclause ? "" : "/newcontent"
44
53
  end
@@ -110,7 +119,7 @@ module IsoDoc
110
119
  sections.each do |s|
111
120
  notes = s.xpath(child_asset_path("note")) -
112
121
  s.xpath(ns(".//figure//note | .//table//note | //permission//note | " \
113
- "//recommendation//note | //requirement//note"))
122
+ "//recommendation//note | //requirement//note"))
114
123
  note_anchor_names1(notes, Counter.new)
115
124
  note_anchor_names(s.xpath(ns(child_sections)))
116
125
  end
@@ -159,7 +168,7 @@ module IsoDoc
159
168
  sections.each do |s|
160
169
  notes = s.xpath(child_asset_path("example")) -
161
170
  s.xpath(ns("//permission//note | " \
162
- "//recommendation//note | //requirement//note"))
171
+ "//recommendation//note | //requirement//note"))
163
172
  example_anchor_names1(notes, Counter.new)
164
173
  example_anchor_names(s.xpath(ns(child_sections)))
165
174
  end
@@ -37,6 +37,7 @@ module IsoDoc
37
37
  if @parse_settings.empty? || @parse_settings[:clauses]
38
38
  preface_anchor_names(xml)
39
39
  main_anchor_names(xml)
40
+ amend_subclause_names(xml)
40
41
  end
41
42
  end
42
43
 
@@ -81,6 +82,16 @@ module IsoDoc
81
82
  )
82
83
  end
83
84
 
85
+ def amend_subclause_names(xml)
86
+ n = clause_counter
87
+ xml.xpath(ns("//amend/newcontent/clause")).each do |c|
88
+ if c["type"] == "annex"
89
+ annex_names(c, n.increment(c).print)
90
+ else section_names(c, n, 1)
91
+ end
92
+ end
93
+ end
94
+
84
95
  def unnumbered_names(clause)
85
96
  clause.nil? and return
86
97
  title = clause_title(clause, use_elem_name: true)
@@ -88,7 +99,7 @@ module IsoDoc
88
99
  clause.xpath(ns(subclauses)).each_with_index do |c, i|
89
100
  t = c.at(ns("./title"))
90
101
  tt = "#{semx(clause, title, clause.name)}" \
91
- "<span class='fmt-comma'>,</span> #{semx(c, i + 1)}"
102
+ "<span class='fmt-comma'>,</span> #{semx(c, i + 1)}"
92
103
  preface_names1(c, t ? semx(c, t.text, c.name) : nil, tt, 2)
93
104
  end
94
105
  end
@@ -99,14 +110,12 @@ module IsoDoc
99
110
  clause.xpath(ns(subclauses)).each_with_index do |c, i|
100
111
  t = c.at(ns("./title"))
101
112
  preface_names1(c, t ? semx(c, t.text, c.name) : nil,
102
- "#{label} #{semx(c, i + 1)}",
103
- level + 1)
113
+ "#{label} #{semx(c, i + 1)}", level + 1)
104
114
  end
105
115
  end
106
116
 
107
117
  def preface_name_anchors(clause, level, title)
108
118
  xref = semx(clause, title, clause.name)
109
- #clause["id"] ||= "_#{UUIDTools::UUID.random_create}"
110
119
  @anchors[clause["id"]] =
111
120
  { label: nil, level:,
112
121
  xref:, title: nil,
@@ -171,7 +180,7 @@ module IsoDoc
171
180
  obl = "(#{@labels['inform_annex']})"
172
181
  clause["obligation"] == "normative" and
173
182
  obl = "(#{@labels['norm_annex']})"
174
- obl = "<span class='fmt-obligation'>#{l10n obl}</fmt>"
183
+ obl = "<span class='fmt-obligation'>#{l10n obl}</span>"
175
184
  title = Common::case_with_markup(@labels["annex"], "capital",
176
185
  @script)
177
186
  s = labelled_autonum(title, num)
@@ -180,7 +189,8 @@ module IsoDoc
180
189
 
181
190
  def annex_name_anchors(clause, num, level)
182
191
  label = num
183
- level == 1 && clause.name == "annex" and
192
+ (level == 1 && clause.name == "annex") ||
193
+ clause["type"] == "annex" and
184
194
  label = annex_name_lbl(clause, label)
185
195
  c = clause_title(clause) and title = semx(clause, c, "title")
186
196
  @anchors[clause["id"]] =
@@ -193,6 +203,11 @@ module IsoDoc
193
203
  appendix_names(clause, num)
194
204
  label = semx(clause, num)
195
205
  annex_name_anchors(clause, label, 1)
206
+ annex_name_recurse(clause, label, num)
207
+ hierarchical_asset_names(clause, label)
208
+ end
209
+
210
+ def annex_name_recurse(clause, label, num)
196
211
  if @klass.single_term_clause?(clause)
197
212
  annex_names1(clause.at(ns("./references | ./terms | ./definitions")),
198
213
  nil, num.to_s, 1)
@@ -202,7 +217,6 @@ module IsoDoc
202
217
  annex_names1(c, label, i.increment(c).print, 2)
203
218
  end
204
219
  end
205
- hierarchical_asset_names(clause, label)
206
220
  end
207
221
 
208
222
  def annex_names1(clause, parentnum, num, level)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: isodoc
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.4.7
4
+ version: 3.4.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-03-16 00:00:00.000000000 Z
11
+ date: 2026-03-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: 0.5.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '13.0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '13.0'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: relaton-render
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -234,20 +248,6 @@ dependencies:
234
248
  - - "~>"
235
249
  - !ruby/object:Gem::Version
236
250
  version: '4.7'
237
- - !ruby/object:Gem::Dependency
238
- name: rake
239
- requirement: !ruby/object:Gem::Requirement
240
- requirements:
241
- - - "~>"
242
- - !ruby/object:Gem::Version
243
- version: '13.0'
244
- type: :development
245
- prerelease: false
246
- version_requirements: !ruby/object:Gem::Requirement
247
- requirements:
248
- - - "~>"
249
- - !ruby/object:Gem::Version
250
- version: '13.0'
251
251
  - !ruby/object:Gem::Dependency
252
252
  name: rspec
253
253
  requirement: !ruby/object:Gem::Requirement
@@ -435,6 +435,7 @@ files:
435
435
  - lib/isodoc/function/terms.rb
436
436
  - lib/isodoc/function/to_word_html.rb
437
437
  - lib/isodoc/function/utils.rb
438
+ - lib/isodoc/function/utils_img.rb
438
439
  - lib/isodoc/gem_tasks.rb
439
440
  - lib/isodoc/headlesshtml_convert.rb
440
441
  - lib/isodoc/html_convert.rb
@@ -451,6 +452,7 @@ files:
451
452
  - lib/isodoc/metadata_contributor.rb
452
453
  - lib/isodoc/metadata_date.rb
453
454
  - lib/isodoc/pdf_convert.rb
455
+ - lib/isodoc/presentation_function/annex.rb
454
456
  - lib/isodoc/presentation_function/autonum.rb
455
457
  - lib/isodoc/presentation_function/bibdata.rb
456
458
  - lib/isodoc/presentation_function/block.rb
@@ -466,6 +468,7 @@ files:
466
468
  - lib/isodoc/presentation_function/index.rb
467
469
  - lib/isodoc/presentation_function/inline.rb
468
470
  - lib/isodoc/presentation_function/list.rb
471
+ - lib/isodoc/presentation_function/list_to_table.rb
469
472
  - lib/isodoc/presentation_function/math.rb
470
473
  - lib/isodoc/presentation_function/metadata.rb
471
474
  - lib/isodoc/presentation_function/refs.rb