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.
- checksums.yaml +4 -4
- data/isodoc.gemspec +1 -1
- data/lib/isodoc/base_style/reset.scss +1 -1
- data/lib/isodoc/css.rb +6 -8
- data/lib/isodoc/css_border_parser.rb +13 -5
- data/lib/isodoc/function/blocks.rb +9 -3
- data/lib/isodoc/function/cleanup.rb +8 -5
- data/lib/isodoc/function/lists.rb +13 -3
- data/lib/isodoc/function/section_titles.rb +3 -1
- data/lib/isodoc/function/utils.rb +35 -51
- data/lib/isodoc/function/utils_img.rb +55 -0
- data/lib/isodoc/gem_tasks.rb +9 -9
- data/lib/isodoc/html_function/postprocess.rb +2 -2
- data/lib/isodoc/html_function/postprocess_cover.rb +12 -9
- data/lib/isodoc/presentation_function/annex.rb +97 -0
- data/lib/isodoc/presentation_function/autonum.rb +7 -6
- data/lib/isodoc/presentation_function/block.rb +26 -11
- data/lib/isodoc/presentation_function/footnotes.rb +1 -1
- data/lib/isodoc/presentation_function/image.rb +1 -1
- data/lib/isodoc/presentation_function/list_to_table.rb +250 -0
- data/lib/isodoc/presentation_function/math.rb +10 -32
- data/lib/isodoc/presentation_function/section.rb +7 -32
- data/lib/isodoc/presentation_function/sourcecode.rb +4 -1
- data/lib/isodoc/presentation_function/xrefs.rb +1 -1
- data/lib/isodoc/presentation_xml_convert.rb +7 -4
- data/lib/isodoc/version.rb +1 -1
- data/lib/isodoc/word_function/table.rb +7 -4
- data/lib/isodoc/xref/xref_counter.rb +36 -3
- data/lib/isodoc/xref/xref_gen.rb +23 -14
- data/lib/isodoc/xref/xref_sect_gen.rb +21 -7
- metadata +19 -16
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
module IsoDoc
|
|
2
2
|
class PresentationXMLConvert < ::IsoDoc::Convert
|
|
3
|
-
def prefix_name(node, delims, label, elem)
|
|
3
|
+
def prefix_name(node, delims, label, elem, fmt_xref_label: true)
|
|
4
4
|
sem_xml_descendant?(node) and return
|
|
5
5
|
label, delims = prefix_name_defaults(node, delims, label, elem)
|
|
6
6
|
name, ins, ids, number = prefix_name_prep(node, elem)
|
|
7
|
-
ins.next = fmt_xref_label(label, number, ids)
|
|
7
|
+
fmt_xref_label and ins.next = fmt_xref_label(label, number, ids)
|
|
8
8
|
# autonum can be empty, e.g single note in clause: "NOTE []"
|
|
9
9
|
number and node["autonum"] = number.gsub(/<[^>]+>/, "")
|
|
10
10
|
!node.at(ns("./fmt-#{elem}")) &&
|
|
@@ -40,6 +40,7 @@ module IsoDoc
|
|
|
40
40
|
node.at(ns("./sentinel"))&.remove
|
|
41
41
|
strip_duplicate_ids(node, node.at(ns("./#{elem}")),
|
|
42
42
|
node.at(ns("./fmt-#{elem}")))
|
|
43
|
+
node.at(ns("./fmt-#{elem}"))
|
|
43
44
|
end
|
|
44
45
|
|
|
45
46
|
def transfer_id(old, new)
|
|
@@ -67,7 +68,7 @@ module IsoDoc
|
|
|
67
68
|
# remove ids duplicated between sem_title and pres_title
|
|
68
69
|
# index terms are assumed transferred to pres_title from sem_title
|
|
69
70
|
def strip_duplicate_ids(_node, sem_title, pres_title)
|
|
70
|
-
sem_title && pres_title or return
|
|
71
|
+
(sem_title && pres_title) or return
|
|
71
72
|
ids = gather_all_ids(pres_title)
|
|
72
73
|
sem_title.xpath(".//*[@id]").each do |x|
|
|
73
74
|
ids.include?(x["id"]) or next
|
|
@@ -78,7 +79,7 @@ module IsoDoc
|
|
|
78
79
|
end
|
|
79
80
|
|
|
80
81
|
def semx(node, label, element = "autonum")
|
|
81
|
-
id = node["id"] || node[:id] ||
|
|
82
|
+
id = node["id"] || node[:id] || node["original-id"]
|
|
82
83
|
/<semx element='[^']+' source='#{id}'/.match?(label) and return label
|
|
83
84
|
l = stripsemx(label)
|
|
84
85
|
%(<semx element='#{element}' source='#{id}'>#{l}</semx>)
|
|
@@ -202,7 +203,7 @@ module IsoDoc
|
|
|
202
203
|
def sem_xml_descendant_inline?(_node, ancestor_names)
|
|
203
204
|
%w[xref eref origin link name title newcontent]
|
|
204
205
|
.any? do |name|
|
|
205
|
-
|
|
206
|
+
ancestor_names.include?(name)
|
|
206
207
|
end and return true
|
|
207
208
|
end
|
|
208
209
|
|
|
@@ -213,7 +214,7 @@ module IsoDoc
|
|
|
213
214
|
block?(node) and return false
|
|
214
215
|
%w[preferred admitted deprecated related definition source]
|
|
215
216
|
.any? do |name|
|
|
216
|
-
|
|
217
|
+
ancestor_names.include?(name)
|
|
217
218
|
end
|
|
218
219
|
end
|
|
219
220
|
|
|
@@ -107,11 +107,16 @@ module IsoDoc
|
|
|
107
107
|
docxml.xpath(ns("//table")).each { |f| table1(f) }
|
|
108
108
|
end
|
|
109
109
|
|
|
110
|
+
def table1_caption?(elem)
|
|
111
|
+
!labelled_ancestor(elem) &&
|
|
112
|
+
!(elem["unnumbered"] && !elem.at(ns("./name"))) &&
|
|
113
|
+
!%w(fmt-ol fmt-ul).include?(elem.parent.name)
|
|
114
|
+
end
|
|
115
|
+
|
|
110
116
|
def table1(elem)
|
|
111
117
|
table_fn(elem)
|
|
112
118
|
table_css(elem)
|
|
113
|
-
|
|
114
|
-
elem["unnumbered"] && !elem.at(ns("./name")) and return
|
|
119
|
+
table1_caption?(elem) or return
|
|
115
120
|
n = @xrefs.anchor(elem["id"] || elem["original-id"], :label, false)
|
|
116
121
|
lbl = labelled_autonum(lower2cap(@i18n.table),
|
|
117
122
|
elem["id"] || elem["original-id"], n)
|
|
@@ -153,32 +158,42 @@ module IsoDoc
|
|
|
153
158
|
"./classification | ./contributor | ./fmt-name | " \
|
|
154
159
|
"./fmt-xref-label")).each(&:remove)
|
|
155
160
|
amend_newcontent(ret)
|
|
156
|
-
ret.xpath(ns("./newcontent")).each { |a| a.name = "quote" }
|
|
157
161
|
ret.xpath(ns("./description")).each { |a| a.replace(a.children) }
|
|
158
162
|
elem.next = ret
|
|
159
163
|
end
|
|
160
164
|
|
|
161
165
|
def amend_newcontent(elem)
|
|
162
|
-
elem.xpath(ns("
|
|
166
|
+
elem.xpath(ns(".//newcontent")).each do |a|
|
|
163
167
|
a.name = "quote"
|
|
164
|
-
a
|
|
168
|
+
a["type"] = "newcontent"
|
|
169
|
+
a.xpath(ns("./clause")).reverse_each do |c|
|
|
165
170
|
amend_subclause(c, 1)
|
|
166
171
|
a.next = c
|
|
167
172
|
end
|
|
168
173
|
end
|
|
174
|
+
elem.xpath(ns("./quote[not(node())]")).each(&:remove)
|
|
169
175
|
end
|
|
170
176
|
|
|
171
177
|
def amend_subclause(clause, depth)
|
|
172
|
-
clause
|
|
173
|
-
# t.name = "floating-title"
|
|
174
|
-
# t["depth"] ||= depth || "1"
|
|
175
|
-
t.name = "p"
|
|
176
|
-
t["type"] = "floating-title"
|
|
177
|
-
end
|
|
178
|
+
amend_subclause_title(clause, depth)
|
|
178
179
|
clause.name = depth == 1 ? "quote" : "quote" # "div"
|
|
180
|
+
clause["type"] = "newcontent"
|
|
179
181
|
clause.xpath(ns("./clause")).each { |c| amend_subclause(c, depth + 1) }
|
|
180
182
|
end
|
|
181
183
|
|
|
184
|
+
def amend_subclause_title(clause, _depth)
|
|
185
|
+
if clause["type"] == "annex"
|
|
186
|
+
annex1(clause)
|
|
187
|
+
else
|
|
188
|
+
clause1(clause) # insert title prefix
|
|
189
|
+
end
|
|
190
|
+
clause.xpath(ns("./title | ./variant-title")).each(&:remove)
|
|
191
|
+
t = clause.at(ns("./fmt-title")) or return
|
|
192
|
+
t.name = "p"
|
|
193
|
+
t["type"] = "floating-title"
|
|
194
|
+
# t["depth"] ||= depth || "1"
|
|
195
|
+
end
|
|
196
|
+
|
|
182
197
|
def quote(docxml)
|
|
183
198
|
docxml.xpath(ns("//quote")).each { |f| quote1(f) }
|
|
184
199
|
end
|
|
@@ -157,7 +157,7 @@ module IsoDoc
|
|
|
157
157
|
elem.at(ns(".//dl/name"))&.next ||
|
|
158
158
|
elem.at(ns(".//dl"))&.children&.first ||
|
|
159
159
|
elem.add_child("<key><dl> </dl></key>")
|
|
160
|
-
|
|
160
|
+
.first.elements.first.children.first
|
|
161
161
|
end
|
|
162
162
|
|
|
163
163
|
def comments(docxml)
|
|
@@ -48,7 +48,7 @@ module IsoDoc
|
|
|
48
48
|
|
|
49
49
|
svg = Base64.strict_decode64(elem["src"]
|
|
50
50
|
.sub(%r{^data:image/svg\+xml;(charset=[^;]+;)?base64,}, ""))
|
|
51
|
-
x = Nokogiri::XML.fragment(svg.sub(
|
|
51
|
+
x = Nokogiri::XML.fragment(svg.sub(/\A<\?xml[^<>]*>\n?/, ""), &:huge)
|
|
52
52
|
elem["src"] = ""
|
|
53
53
|
elem.children = x
|
|
54
54
|
end
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
module IsoDoc
|
|
2
|
+
class PresentationXMLConvert < ::IsoDoc::Convert
|
|
3
|
+
# Entry point: find all top-level (ol|ul)[@display='table'] and convert each
|
|
4
|
+
def list_to_table(docxml)
|
|
5
|
+
docxml.xpath(ns("//ol[@display='table'] | //ul[@display='table']"))
|
|
6
|
+
.each do |elem|
|
|
7
|
+
list_table_convert(elem)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Convert a single display="table" list: build table, insert under fmt-ol
|
|
12
|
+
def list_table_convert(elem)
|
|
13
|
+
n = list_table_depth(elem)
|
|
14
|
+
table = list_table_build(elem, n)
|
|
15
|
+
elem << "<fmt-#{elem.name}></fmt-#{elem.name}>"
|
|
16
|
+
elem.elements.last << table
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Maximum depth of nested ol/ul: elem itself = depth 1
|
|
20
|
+
def list_table_depth(elem)
|
|
21
|
+
depths = [1]
|
|
22
|
+
elem.xpath(ns(".//ol | .//ul")).each do |sub|
|
|
23
|
+
d = sub.ancestors.take_while { |a| a != elem }
|
|
24
|
+
.count { |a| %w[ol ul].include?(a.name) } + 2
|
|
25
|
+
depths << d
|
|
26
|
+
end
|
|
27
|
+
depths.max
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Build the full table as a Nokogiri node
|
|
31
|
+
def list_table_build(elem, cellcount)
|
|
32
|
+
xml = "<table>"
|
|
33
|
+
xml += list_table_name(elem)
|
|
34
|
+
xml += list_table_colgroup(elem, cellcount)
|
|
35
|
+
xml += list_table_header(elem, cellcount)
|
|
36
|
+
xml += list_table_body(elem, cellcount)
|
|
37
|
+
xml += "</table>"
|
|
38
|
+
Nokogiri::XML(xml).root
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# global table title if there are no nested titled in the list:
|
|
42
|
+
# move list/fmt-name to ol/fmt-ol/table/fmt-name: ol/fmt-ol is all we render
|
|
43
|
+
def list_table_name(elem)
|
|
44
|
+
list_only_one_title(elem) or return ""
|
|
45
|
+
ret = elem.at(ns("./fmt-name")) or return ""
|
|
46
|
+
to_xml(ret.remove)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def list_table_colgroup(elem, cellcount)
|
|
50
|
+
n = elem["display-directives"] or return ""
|
|
51
|
+
attrs = csv_attribute_extract(n)
|
|
52
|
+
attrs[:colgroup] or return ""
|
|
53
|
+
vals = attrs[:colgroup].split(",").map(&:to_f)
|
|
54
|
+
vals = list_table_normalise_colgroup(vals, cellcount)
|
|
55
|
+
ret = vals.map { |n| "<col width='#{n}%'/>" }.join
|
|
56
|
+
"<colgroup>#{ret}</colgroup>"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def list_table_normalise_colgroup(vals, cellcount)
|
|
60
|
+
vals.size > cellcount and vals = vals[0...cellcount]
|
|
61
|
+
if vals.size < cellcount
|
|
62
|
+
(vals.size...cellcount).each do |_i|
|
|
63
|
+
vals << 10.0
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
sum = vals.sum
|
|
67
|
+
vals.map { |i| 100 * i / sum }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Build <thead><tr> with n <th> cells, one per depth level
|
|
71
|
+
def list_table_header(elem, cellcount)
|
|
72
|
+
list_only_one_title(elem) and return ""
|
|
73
|
+
ths = (1..cellcount).map { |i| list_table_th(elem, i) }.join
|
|
74
|
+
"<thead><tr>#{ths}</tr></thead>"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def list_only_one_title(elem)
|
|
78
|
+
(!elem.at(ns(".//ol//name")) && !elem.at(ns(".//ul//name"))) or return nil
|
|
79
|
+
elem.at(ns("./name"))
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def list_table_th(elem, depth)
|
|
83
|
+
name = list_table_col_name(elem, depth)
|
|
84
|
+
name or return "<th/>"
|
|
85
|
+
add_id(name)
|
|
86
|
+
src = name["original-id"] || name["id"]
|
|
87
|
+
children = to_xml(name.children)
|
|
88
|
+
<<~XML
|
|
89
|
+
<th><fmt-name><semx element='name' source='#{src}'>#{children}</semx></fmt-name></th>
|
|
90
|
+
XML
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Find the <name> element of the first ol/ul at the given depth within elem
|
|
94
|
+
def list_table_col_name(elem, depth)
|
|
95
|
+
list_at_depth(elem, depth)&.at(ns("./name"))
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Return the first ol/ul at the given depth (depth 1 = elem itself)
|
|
99
|
+
def list_at_depth(elem, target_depth)
|
|
100
|
+
target_depth == 1 and return elem
|
|
101
|
+
elem.xpath(ns(".//ol | .//ul")).find do |sub|
|
|
102
|
+
d = sub.ancestors.take_while { |a| a != elem }
|
|
103
|
+
.count { |a| %w[ol ul].include?(a.name) } + 2
|
|
104
|
+
d == target_depth
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Build <tbody> with one <tr> per leaf (terminal sublist) path
|
|
109
|
+
def list_table_body(elem, cellcount)
|
|
110
|
+
paths = list_table_leaf_paths(elem, 1)
|
|
111
|
+
emitted = {} # li object_id => true when already emitted under a rowspan
|
|
112
|
+
rows = paths.map do |path|
|
|
113
|
+
list_table_row(path, cellcount, emitted)
|
|
114
|
+
end.join
|
|
115
|
+
"<tbody>#{rows}</tbody>"
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def list_table_row(path, cellcount, emitted)
|
|
119
|
+
cells = path.map do |step|
|
|
120
|
+
if step[:terminal]
|
|
121
|
+
if step[:li]
|
|
122
|
+
# Degenerate: single li with no child sublist —
|
|
123
|
+
# render just that li with colspan
|
|
124
|
+
list_table_degen_terminal_td(step[:list], step[:li], step[:li_idx],
|
|
125
|
+
step[:depth], cellcount)
|
|
126
|
+
else
|
|
127
|
+
list_table_terminal_td(step[:list], step[:depth], cellcount)
|
|
128
|
+
end
|
|
129
|
+
else
|
|
130
|
+
li = step[:li]
|
|
131
|
+
emitted[li.object_id] and next
|
|
132
|
+
rowspan = list_table_count_terminals(li)
|
|
133
|
+
emitted[li.object_id] = true
|
|
134
|
+
list_table_nonterminal_td(step[:list], li, step[:li_idx], rowspan,
|
|
135
|
+
step[:depth])
|
|
136
|
+
end
|
|
137
|
+
end.compact.join
|
|
138
|
+
"<tr>#{cells}</tr>"
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Recursively collect all leaf paths from xl downward.
|
|
142
|
+
# Each path is an array of step hashes; the last step has terminal: true.
|
|
143
|
+
# Non-terminal step: { list:, li:, depth:, li_idx: }
|
|
144
|
+
# Terminal step: { list:, depth:, terminal: true }
|
|
145
|
+
def list_table_leaf_paths(xl, depth)
|
|
146
|
+
paths = []
|
|
147
|
+
xl.xpath(ns("./li")).each_with_index do |li, idx|
|
|
148
|
+
li_idx = idx + 1
|
|
149
|
+
sub_xls = li.children.select { |c| %w[ol ul].include?(c.name) }
|
|
150
|
+
if sub_xls.empty?
|
|
151
|
+
# Degenerate: li with no child sublist — treat as its own terminal row
|
|
152
|
+
paths << [{ list: xl, li:, depth:, li_idx:, terminal: true }]
|
|
153
|
+
else
|
|
154
|
+
sub_xls.each do |sub_xl|
|
|
155
|
+
step = { list: xl, li: li, depth: depth, li_idx: li_idx }
|
|
156
|
+
if (sub_xl.xpath(ns(".//ol")) + sub_xl.xpath(ns(".//ul"))).empty?
|
|
157
|
+
paths << [step,
|
|
158
|
+
{ list: sub_xl, depth: depth + 1, terminal: true }]
|
|
159
|
+
else
|
|
160
|
+
list_table_leaf_paths(sub_xl, depth + 1).each do |sub_path|
|
|
161
|
+
paths << ([step] + sub_path)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
paths
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Count terminal sublists reachable from a li element (used for rowspan)
|
|
171
|
+
def list_table_count_terminals(listitem)
|
|
172
|
+
sub_xls = listitem.children.select { |c| %w[ol ul].include?(c.name) }
|
|
173
|
+
sub_xls.empty? and return 1
|
|
174
|
+
count = 0
|
|
175
|
+
sub_xls.each do |sub_xl|
|
|
176
|
+
count += list_table_count_terminals_recurse(sub_xl)
|
|
177
|
+
end
|
|
178
|
+
[count, 1].max
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def list_table_count_terminals_recurse(sub_xl)
|
|
182
|
+
ret = 0
|
|
183
|
+
(sub_xl.xpath(ns(".//ol")) + sub_xl.xpath(ns(".//ul"))).empty? and
|
|
184
|
+
return 1
|
|
185
|
+
sub_xl.xpath(ns("./li")).each do |sub_li|
|
|
186
|
+
ret += list_table_count_terminals(sub_li)
|
|
187
|
+
end
|
|
188
|
+
ret
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Build a nonterminal <td>: wraps a single li (minus nested sublists)
|
|
192
|
+
# in an ol/ul with appropriate start, type, and rowspan
|
|
193
|
+
def list_table_nonterminal_td(list, listitem, li_idx, rowspan, depth)
|
|
194
|
+
rowspan_attr = rowspan > 1 ? " rowspan='#{rowspan}'" : ""
|
|
195
|
+
li_content = list_table_li_content(listitem)
|
|
196
|
+
if list.name == "ol"
|
|
197
|
+
start = list_table_calc_start(list, li_idx)
|
|
198
|
+
type = @counter.ol_type(list, depth).to_s
|
|
199
|
+
"<td#{rowspan_attr}><ol start='#{start}' type='#{type}'>" \
|
|
200
|
+
"<li>#{li_content}</li></ol></td>"
|
|
201
|
+
else
|
|
202
|
+
"<td#{rowspan_attr}><ul><li>#{li_content}</li></ul></td>"
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Serialize a li's content, excluding any direct ol/ul children
|
|
207
|
+
def list_table_li_content(listitem)
|
|
208
|
+
listitem.children.reject { |c| %w[ol ul].include?(c.name) }
|
|
209
|
+
.map { |c| to_xml(c) }.join
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Build a terminal <td> for a degenerate li (no child sublist),
|
|
213
|
+
# wrapping just that single li with the correct colspan
|
|
214
|
+
def list_table_degen_terminal_td(list, listitem, li_idx, depth, cellcount)
|
|
215
|
+
colspan = cellcount - depth + 1
|
|
216
|
+
colspan_attr = colspan > 1 ? " colspan='#{colspan}'" : ""
|
|
217
|
+
li_content = list_table_li_content(listitem)
|
|
218
|
+
if list.name == "ol"
|
|
219
|
+
start = list_table_calc_start(list, li_idx)
|
|
220
|
+
type = @counter.ol_type(list, depth).to_s
|
|
221
|
+
"<td#{colspan_attr}><ol start='#{start}' type='#{type}'>" \
|
|
222
|
+
"<li>#{li_content}</li></ol></td>"
|
|
223
|
+
else
|
|
224
|
+
"<td#{colspan_attr}><ul><li>#{li_content}</li></ul></td>"
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Build a terminal <td>: contains the whole sublist,
|
|
229
|
+
# with colspan if depth < cellcount
|
|
230
|
+
def list_table_terminal_td(xl, depth, cellcount)
|
|
231
|
+
colspan = cellcount - depth + 1
|
|
232
|
+
colspan_attr = colspan > 1 ? " colspan='#{colspan}'" : ""
|
|
233
|
+
xl_dup = xl.dup
|
|
234
|
+
# Remove <name> from the copy (names go in thead, not in the cell body)
|
|
235
|
+
xl_dup.children.each { |c| c.name == "name" and c.remove }
|
|
236
|
+
if xl.name == "ol"
|
|
237
|
+
type = @counter.ol_type(xl, depth).to_s
|
|
238
|
+
xl_dup["type"] = type
|
|
239
|
+
end
|
|
240
|
+
"<td#{colspan_attr}>#{to_xml(xl_dup)}</td>"
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Calculate the start number for a wrapped ol cell
|
|
244
|
+
# (original ol start) + (0-based position of this li) = start
|
|
245
|
+
# for this li's number
|
|
246
|
+
def list_table_calc_start(list, li_idx)
|
|
247
|
+
(list["start"] || 1).to_i + li_idx - 1
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
end
|
|
@@ -50,32 +50,6 @@ module IsoDoc
|
|
|
50
50
|
precision: num_precision(num.text))
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
-
COMMA_PLACEHOLDER = "##COMMA##".freeze
|
|
54
|
-
|
|
55
|
-
# Temporarily replace commas inside quotes with a placeholder
|
|
56
|
-
def comma_placeholder(options)
|
|
57
|
-
processed = ""
|
|
58
|
-
in_quotes = false
|
|
59
|
-
options.each_char do |c|
|
|
60
|
-
c == "'" and in_quotes = !in_quotes
|
|
61
|
-
processed << if c == "," && in_quotes
|
|
62
|
-
COMMA_PLACEHOLDER
|
|
63
|
-
else c end
|
|
64
|
-
end
|
|
65
|
-
processed
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def numberformat_extract(options)
|
|
69
|
-
options.gsub!(/([a-z_]+)='/, %('\\1=))
|
|
70
|
-
processed = comma_placeholder(options)
|
|
71
|
-
CSV.parse_line(processed,
|
|
72
|
-
quote_char: "'").each_with_object({}) do |x, acc|
|
|
73
|
-
x.gsub!(COMMA_PLACEHOLDER, ",")
|
|
74
|
-
m = /^(.+?)=(.+)?$/.match(x) or next
|
|
75
|
-
acc[m[1].to_sym] = m[2].sub(/^(["'])(.+)\1$/, "\\2")
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
|
|
79
53
|
def numberformat_type(ret)
|
|
80
54
|
%i(precision significant digit_count group_digits fraction_group_digits)
|
|
81
55
|
.each do |i|
|
|
@@ -88,7 +62,7 @@ module IsoDoc
|
|
|
88
62
|
end
|
|
89
63
|
|
|
90
64
|
def explicit_number_formatter(num, locale, options)
|
|
91
|
-
ret = numberformat_type(
|
|
65
|
+
ret = numberformat_type(csv_attribute_extract(options))
|
|
92
66
|
l = ret[:locale] || locale
|
|
93
67
|
precision, symbols, significant = explicit_number_formatter_cfg(num, ret)
|
|
94
68
|
n = normalise_number(num.text)
|
|
@@ -137,7 +111,9 @@ module IsoDoc
|
|
|
137
111
|
precision = nil
|
|
138
112
|
/\.(?!\d+e)/.match?(num) and
|
|
139
113
|
precision = twitter_cldr_localiser_symbols[:precision] ||
|
|
140
|
-
|
|
114
|
+
# [^.]* excludes the delimiter itself, preventing polynomial
|
|
115
|
+
# backtracking on strings with multiple dots.
|
|
116
|
+
num.sub(/\A[^.]*\./, "").size
|
|
141
117
|
precision
|
|
142
118
|
end
|
|
143
119
|
|
|
@@ -145,7 +121,9 @@ module IsoDoc
|
|
|
145
121
|
totaldigits = nil
|
|
146
122
|
/\.(?=\d+e)/.match?(num) and
|
|
147
123
|
totaldigits = twitter_cldr_localiser_symbols[:significant] ||
|
|
148
|
-
|
|
124
|
+
# [^.]* and [^e]* exclude their respective delimiters,
|
|
125
|
+
# preventing polynomial backtracking.
|
|
126
|
+
num.sub(/\A0\./, ".").sub(/\A[^.]*\./, "").sub(/e[^e]*\z/, "").size
|
|
149
127
|
totaldigits
|
|
150
128
|
end
|
|
151
129
|
|
|
@@ -155,9 +133,9 @@ module IsoDoc
|
|
|
155
133
|
|
|
156
134
|
def asciimath_dup(node)
|
|
157
135
|
@suppressasciimathdup || node.parent.at(ns("./asciimath")) and return
|
|
158
|
-
math = node.to_xml.gsub(/ xmlns=["'][^"']
|
|
159
|
-
.gsub(%r{<[^:/>]
|
|
160
|
-
.gsub(%r{ data-metanorma-numberformat="[^"]
|
|
136
|
+
math = node.to_xml.gsub(/ xmlns=["'][^"']*["']/, "")
|
|
137
|
+
.gsub(%r{<[^:/>]*:}, "<").gsub(%r{</[^:/>]*:}, "</")
|
|
138
|
+
.gsub(%r{ data-metanorma-numberformat="[^"]*"}, "")
|
|
161
139
|
ret = Plurimath::Math.parse(math, "mathml").to_asciimath
|
|
162
140
|
node.next = "<asciimath>#{@c.encode(ret, :basic)}</asciimath>"
|
|
163
141
|
rescue StandardError => e
|
|
@@ -33,15 +33,12 @@ module IsoDoc
|
|
|
33
33
|
|
|
34
34
|
def clausedelim
|
|
35
35
|
ret = super
|
|
36
|
-
ret && !ret.empty? or return ret
|
|
36
|
+
(ret && !ret.empty?) or return ret
|
|
37
37
|
"<span class='fmt-autonum-delim'>#{ret}</span>"
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def clause1(elem)
|
|
41
|
-
level =
|
|
42
|
-
(elem.ancestors("clause, annex").size + 1)
|
|
43
|
-
is_unnumbered = unnumbered_clause?(elem)
|
|
44
|
-
lbl = @xrefs.anchor(elem["id"], :label, false)
|
|
41
|
+
level, is_unnumbered, lbl = clause1_prep(elem)
|
|
45
42
|
if is_unnumbered || !lbl
|
|
46
43
|
prefix_name(elem, {}, nil, "title")
|
|
47
44
|
else
|
|
@@ -51,34 +48,12 @@ module IsoDoc
|
|
|
51
48
|
t = elem.at(ns("./fmt-title")) and t["depth"] = level
|
|
52
49
|
end
|
|
53
50
|
|
|
54
|
-
def
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
@xrefs.klass.single_term_clause?(f) and single_term_clause_unnest(f)
|
|
59
|
-
end
|
|
60
|
-
@xrefs.parse_inclusions(clauses: true).parse(docxml)
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def annex1(elem)
|
|
51
|
+
def clause1_prep(elem)
|
|
52
|
+
level = @xrefs.anchor(elem["id"], :level, false) ||
|
|
53
|
+
(elem.ancestors("clause, annex").size + 1)
|
|
54
|
+
is_unnumbered = unnumbered_clause?(elem)
|
|
64
55
|
lbl = @xrefs.anchor(elem["id"], :label, false)
|
|
65
|
-
|
|
66
|
-
t = elem.at(ns("./title")) and
|
|
67
|
-
t.children = "<strong>#{to_xml(t.children)}</strong>"
|
|
68
|
-
if unnumbered_clause?(elem)
|
|
69
|
-
prefix_name(elem, {}, nil, "title")
|
|
70
|
-
else
|
|
71
|
-
prefix_name(elem, { caption: annex_delim_override(elem) }, lbl, "title")
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def annex_delim_override(elem)
|
|
76
|
-
m = elem.document.root.at(ns("//presentation-metadata/annex-delim"))
|
|
77
|
-
m ? to_xml(m.children) : annex_delim(elem)
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def annex_delim(_elem)
|
|
81
|
-
"<br/><br/>"
|
|
56
|
+
[level, is_unnumbered, lbl]
|
|
82
57
|
end
|
|
83
58
|
|
|
84
59
|
def single_term_clause_retitle(elem)
|
|
@@ -153,6 +153,9 @@ module IsoDoc
|
|
|
153
153
|
pre.name = "sourcecode"
|
|
154
154
|
pre.children = to_xml(pre.children).sub(/\s+$/, "")
|
|
155
155
|
end
|
|
156
|
+
r.xpath(".//tr[@id]").each do |tr| # disambig Rouge ids
|
|
157
|
+
tr["id"] = "#{elem['source']}_#{tr['id']}"
|
|
158
|
+
end
|
|
156
159
|
r
|
|
157
160
|
end
|
|
158
161
|
|
|
@@ -160,7 +163,7 @@ module IsoDoc
|
|
|
160
163
|
lexer = Rouge::Lexer.find(lang || "plaintext") ||
|
|
161
164
|
Rouge::Lexer.find("plaintext")
|
|
162
165
|
l = Rouge::Lexers::Escape.new(start: "{^^{", end: "}^^}", lang: lexer)
|
|
163
|
-
source = to_xml(elem.children).gsub(
|
|
166
|
+
source = to_xml(elem.children).gsub("<", "{^^{<").gsub(">", ">}^^}")
|
|
164
167
|
l.lang.reset!
|
|
165
168
|
l.lex(@c.decode(source))
|
|
166
169
|
end
|
|
@@ -194,7 +194,7 @@ module IsoDoc
|
|
|
194
194
|
end
|
|
195
195
|
|
|
196
196
|
def capitalise_xref(node, linkend, label)
|
|
197
|
-
linktext = linkend.gsub(/<[^<>]
|
|
197
|
+
linktext = linkend.gsub(/<[^<>]*>/, "")
|
|
198
198
|
(label && !label.empty? && /^#{Regexp.escape(label)}/.match?(linktext)) ||
|
|
199
199
|
linktext[0, 1].match?(/\p{Upper}/) and return linkend
|
|
200
200
|
node["case"] and
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require_relative "presentation_function/block"
|
|
2
2
|
require_relative "presentation_function/source"
|
|
3
3
|
require_relative "presentation_function/list"
|
|
4
|
+
require_relative "presentation_function/list_to_table"
|
|
4
5
|
require_relative "presentation_function/reqt"
|
|
5
6
|
require_relative "presentation_function/concepts"
|
|
6
7
|
require_relative "presentation_function/designations"
|
|
@@ -11,6 +12,7 @@ require_relative "presentation_function/inline"
|
|
|
11
12
|
require_relative "presentation_function/math"
|
|
12
13
|
require_relative "presentation_function/section"
|
|
13
14
|
require_relative "presentation_function/section_refs"
|
|
15
|
+
require_relative "presentation_function/annex"
|
|
14
16
|
require_relative "presentation_function/title"
|
|
15
17
|
require_relative "presentation_function/refs"
|
|
16
18
|
require_relative "presentation_function/docid"
|
|
@@ -60,8 +62,8 @@ module IsoDoc
|
|
|
60
62
|
def bibitem_lookup(docxml)
|
|
61
63
|
@bibitem_lookup ||= docxml.xpath(ns("//references/bibitem"))
|
|
62
64
|
.each_with_object({}) do |b, m|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
+
m[b["id"]] = b
|
|
66
|
+
m[b["anchor"]] = b
|
|
65
67
|
end
|
|
66
68
|
end
|
|
67
69
|
|
|
@@ -106,8 +108,9 @@ module IsoDoc
|
|
|
106
108
|
note docxml
|
|
107
109
|
admonition docxml
|
|
108
110
|
source docxml
|
|
109
|
-
ul docxml
|
|
110
|
-
ol docxml
|
|
111
|
+
ul docxml # feeds list_to_table
|
|
112
|
+
ol docxml # feeds list_to_table
|
|
113
|
+
list_to_table docxml
|
|
111
114
|
dl docxml
|
|
112
115
|
quote docxml
|
|
113
116
|
permission docxml
|
data/lib/isodoc/version.rb
CHANGED
|
@@ -2,9 +2,12 @@ module IsoDoc
|
|
|
2
2
|
module WordFunction
|
|
3
3
|
module Body
|
|
4
4
|
def remove_bottom_border(cell)
|
|
5
|
+
# [^;]* (not +): the preceding property name is the unambiguous
|
|
6
|
+
# delimiter, so zero-or-more is equivalent and avoids polynomial
|
|
7
|
+
# backtracking on the value portion.
|
|
5
8
|
cell["style"] =
|
|
6
|
-
cell["style"].gsub(/border-bottom:[^;]
|
|
7
|
-
.gsub(/mso-border-bottom-alt:[^;]
|
|
9
|
+
cell["style"].gsub(/border-bottom:[^;]*;/, "border-bottom:0pt;")
|
|
10
|
+
.gsub(/mso-border-bottom-alt:[^;]*;/, "mso-border-bottom-alt:0pt;")
|
|
8
11
|
end
|
|
9
12
|
|
|
10
13
|
SW1 = "solid windowtext".freeze
|
|
@@ -40,7 +43,7 @@ module IsoDoc
|
|
|
40
43
|
border-top:#{top}mso-border-top-alt:#{top}
|
|
41
44
|
border-bottom:#{bottom}mso-border-bottom-alt:#{bottom}
|
|
42
45
|
STYLE
|
|
43
|
-
opt[:bordered] && !cell["style"] or ret = ""
|
|
46
|
+
(opt[:bordered] && !cell["style"]) or ret = ""
|
|
44
47
|
pb = keep_rows_together(cell, rowmax, totalrows, opt) ? "avoid" : "auto"
|
|
45
48
|
"#{ret}page-break-after:#{pb};"
|
|
46
49
|
end
|
|
@@ -76,7 +79,7 @@ module IsoDoc
|
|
|
76
79
|
ret = { summary: node["summary"], width: node["width"],
|
|
77
80
|
class: table_class(node),
|
|
78
81
|
style: "mso-table-anchor-horizontal:column;" \
|
|
79
|
-
|
|
82
|
+
"mso-table-overlap:never;#{style}#{keep_style(node)}" }
|
|
80
83
|
style or ret.delete(:class)
|
|
81
84
|
super.merge(attr_code(ret))
|
|
82
85
|
end
|