isodoc 3.7.0 → 3.7.2
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/.rubocop.yml +9 -0
- data/lib/isodoc/base_style/all.css +9 -0
- data/lib/isodoc/base_style/reset.css +4 -0
- data/lib/isodoc/base_style/reset.scss +12 -0
- data/lib/isodoc/function/section_titles.rb +12 -1
- data/lib/isodoc/function/table.rb +5 -1
- data/lib/isodoc/init.rb +3 -0
- data/lib/isodoc/metadata_bibdata.rb +50 -2
- data/lib/isodoc/presentation_function/annex.rb +1 -1
- data/lib/isodoc/presentation_function/block.rb +7 -5
- data/lib/isodoc/presentation_function/concepts.rb +3 -0
- data/lib/isodoc/presentation_function/designations.rb +5 -0
- data/lib/isodoc/presentation_function/image.rb +2 -3
- data/lib/isodoc/presentation_function/index.rb +68 -24
- data/lib/isodoc/presentation_function/list.rb +3 -0
- data/lib/isodoc/presentation_function/section.rb +1 -1
- data/lib/isodoc/presentation_function/sourcecode.rb +32 -2
- data/lib/isodoc/presentation_function/terms.rb +1 -1
- data/lib/isodoc/version.rb +1 -1
- data/lib/isodoc/xref/xref_anchor.rb +10 -0
- data/lib/isodoc-yaml/i18n-en.yaml +4 -0
- data/lib/isodoc-yaml/i18n-fr.yaml +4 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3f336d204ef62fe18fa00d602cd7281ef876b14cc63c216e718eb8bf60be0f5d
|
|
4
|
+
data.tar.gz: 354606fcf812e4d4378a36dac7aa771e875b947fa21ade182faec0b52cff63f9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e29dd8f0a3e9181226c4df3dccc334497a313052b841671bfbf9b342fe577f4778578c3018eee82409a664f7a11cc937f7ed539dec6d74d2d2d8485a9d3f64cf
|
|
7
|
+
data.tar.gz: 2e1de040cfb8293321f989e31250f31b7b3c8a33167eb0846e34e08ae40d329f9e69342d6010b05b23aae615260457b3023d4f02baf084af5c6429dbe94e115d
|
data/.rubocop.yml
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
# See https://github.com/metanorma/cimas
|
|
3
3
|
inherit_from:
|
|
4
4
|
- https://raw.githubusercontent.com/riboseinc/oss-guides/main/ci/rubocop.yml
|
|
5
|
+
# .rubocop_todo.yml MUST be the last entry. inherit_from is last-wins:
|
|
6
|
+
# if listed before oss-guides, the shared config's stricter Metrics/
|
|
7
|
+
# (MethodLength, BlockLength, etc.) rules override the todo's per-file
|
|
8
|
+
# grandfathering, and the todo becomes inert on those cops. Empirically
|
|
9
|
+
# verified on suma 2026-07-06: with todo listed first, 34 Metrics/* offenses
|
|
10
|
+
# remained; moved last, cleared. Every gem in the metanorma-org fleet
|
|
11
|
+
# already ships a `.rubocop_todo.yml` on its live tree (audited 2026-07-06,
|
|
12
|
+
# 54/54 have it), so this reference is safe to emit unconditionally.
|
|
13
|
+
- .rubocop_todo.yml
|
|
5
14
|
|
|
6
15
|
# Rubocop plugins enabled centrally so every metanorma-org gem picks them up
|
|
7
16
|
# on cimas sync — best practice belongs at the shared-template layer, not
|
|
@@ -130,6 +130,10 @@ dl dd {
|
|
|
130
130
|
grid-column-start: 2;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
dl:has(dt .stem) {
|
|
134
|
+
grid-template-columns: fit-content(8em) auto;
|
|
135
|
+
}
|
|
136
|
+
|
|
133
137
|
b, strong {
|
|
134
138
|
font-weight: bold;
|
|
135
139
|
}
|
|
@@ -143,6 +147,11 @@ a.FootnoteRef, span.FootnoteRef {
|
|
|
143
147
|
vertical-align: super;
|
|
144
148
|
}
|
|
145
149
|
|
|
150
|
+
a.TableFootnoteRef, a.FootnoteRef, a.footnote-number,
|
|
151
|
+
sup a, a:has(> sup) {
|
|
152
|
+
vertical-align: baseline;
|
|
153
|
+
}
|
|
154
|
+
|
|
146
155
|
.addition {
|
|
147
156
|
color: blue;
|
|
148
157
|
}
|
|
@@ -137,6 +137,18 @@ dl {
|
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
// metanorma-taste#150: a symbols definition list holds the AsciiMath *source*
|
|
141
|
+
// string in its dt (HTML renders math client-side via async MathJax, which never
|
|
142
|
+
// reflows the grid), so a long source blows out the max-content term column and
|
|
143
|
+
// pushes every definition far right. Cap the term column of stem-bearing dls so
|
|
144
|
+
// the term-to-definition gap stays bounded; plain/abbreviation dls keep
|
|
145
|
+
// max-content, which is correct for them. 8em is a crude, tunable ceiling.
|
|
146
|
+
// STOPGAP: remove this rule and restore plain max-content once HTML emits
|
|
147
|
+
// server-side MathML (the term column will then size to the rendered symbol).
|
|
148
|
+
dl:has(dt .stem) {
|
|
149
|
+
grid-template-columns: fit-content(8em) auto;
|
|
150
|
+
}
|
|
151
|
+
|
|
140
152
|
b, strong {
|
|
141
153
|
font-weight: bold;
|
|
142
154
|
}
|
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
module IsoDoc
|
|
2
2
|
module Function
|
|
3
3
|
module Section
|
|
4
|
+
# Optional per-caption-type delimiter override from i18n: a
|
|
5
|
+
# +<type>_delimiter+ key, falling back to a shared +caption_delimiter+
|
|
6
|
+
# key, falling back to the hardcoded default. Uses +@i18n.get+ (hash
|
|
7
|
+
# lookup, nil for absent keys) rather than +@i18n.<key>+ (which is only
|
|
8
|
+
# defined for present keys). The keys are absent from the base i18n YAML,
|
|
9
|
+
# so default behaviour is unchanged unless a flavour or document sets them.
|
|
10
|
+
def i18n_delim(type, default)
|
|
11
|
+
@i18n&.get&.dig("#{type}_delimiter") ||
|
|
12
|
+
@i18n&.get&.dig("caption_delimiter") || default
|
|
13
|
+
end
|
|
14
|
+
|
|
4
15
|
def clausedelim
|
|
5
|
-
"."
|
|
16
|
+
i18n_delim("clause", ".")
|
|
6
17
|
end
|
|
7
18
|
|
|
8
19
|
def clausedelimspace(_node, out)
|
|
@@ -173,7 +173,11 @@ module IsoDoc
|
|
|
173
173
|
true
|
|
174
174
|
end
|
|
175
175
|
|
|
176
|
-
|
|
176
|
+
# Defaults cover the bare-tr dispatch in ToWordHtml#parse, which had
|
|
177
|
+
# called this with two arguments (unsatisfiable) since 2018: a tr
|
|
178
|
+
# outside tbody/thead context renders as an unbordered plain row
|
|
179
|
+
# instead of crashing (metanorma-pdfa#84)
|
|
180
|
+
def tr_parse(node, out, ord = 0, totalrows = 1, header = false)
|
|
177
181
|
out.tr(**attr_code(style: node["style"])) do |r|
|
|
178
182
|
node.elements.each do |td|
|
|
179
183
|
attrs = make_tr_attr(td, ord, totalrows - 1, header,
|
data/lib/isodoc/init.rb
CHANGED
|
@@ -35,6 +35,9 @@ module IsoDoc
|
|
|
35
35
|
toc_init(xml)
|
|
36
36
|
@details_open = xml.at(ns("//metanorma-extension/presentation-metadata/" \
|
|
37
37
|
"html-details-open"))&.text != "false"
|
|
38
|
+
@index_fold_case =
|
|
39
|
+
xml.at(ns("//metanorma-extension/presentation-metadata/" \
|
|
40
|
+
"index-case-sensitive"))&.text != "true"
|
|
38
41
|
end
|
|
39
42
|
|
|
40
43
|
def doctype_init(docxml)
|
|
@@ -63,11 +63,59 @@ module IsoDoc
|
|
|
63
63
|
bib = BibdataConfig.from_xml(
|
|
64
64
|
"<metanorma>#{stripped.root.to_xml}</metanorma>",
|
|
65
65
|
).bibdata or return nil
|
|
66
|
-
YAML.safe_load(bib.to_yaml, permitted_classes: [Date, Symbol],
|
|
67
|
-
|
|
66
|
+
hash = YAML.safe_load(bib.to_yaml, permitted_classes: [Date, Symbol],
|
|
67
|
+
symbolize_names: true)
|
|
68
|
+
hash and fold_in_bibdata_ext(hash, stripped)
|
|
69
|
+
hash
|
|
68
70
|
rescue StandardError => e
|
|
69
71
|
warn "Failed to parse bibdata for Liquid template use: #{e.message}"
|
|
70
72
|
nil
|
|
71
73
|
end
|
|
74
|
+
|
|
75
|
+
# The Liquid `bibdata` object is built by round-tripping //bibdata through
|
|
76
|
+
# the Relaton object model, which represents bibliographic-item fields but
|
|
77
|
+
# not the Metanorma-document extension metadata under bibdata/ext
|
|
78
|
+
# (coverpage-image, innercoverpage-image, ...). Relaton drops those, so
|
|
79
|
+
# fold the raw ext subtree back in, using the same serialisation convention
|
|
80
|
+
# Relaton uses: element text -> :content, attributes -> sibling keys,
|
|
81
|
+
# repeated elements -> arrays, hyphens -> underscores (as in
|
|
82
|
+
# schema-version -> schema_version). Relaton-modelled keys win on collision;
|
|
83
|
+
# :ext is only populated when <ext> is present.
|
|
84
|
+
def fold_in_bibdata_ext(hash, stripped)
|
|
85
|
+
ext = stripped.at("//bibdata/ext") or return
|
|
86
|
+
raw = xml_element_hash(ext)
|
|
87
|
+
raw.is_a?(Hash) or return
|
|
88
|
+
modelled = hash[:ext].is_a?(Hash) ? hash[:ext] : {}
|
|
89
|
+
hash[:ext] = raw.merge(modelled)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Generic Nokogiri element -> nested hash in Relaton's serialisation shape.
|
|
93
|
+
def xml_element_hash(node)
|
|
94
|
+
hash = {}
|
|
95
|
+
node.attribute_nodes.each { |a| hash[ext_hash_key(a.node_name)] = a.value }
|
|
96
|
+
children = node.element_children
|
|
97
|
+
if children.empty?
|
|
98
|
+
text = node.text.strip
|
|
99
|
+
hash[:content] = text unless text.empty?
|
|
100
|
+
else
|
|
101
|
+
children.each do |c|
|
|
102
|
+
add_ext_child(hash, ext_hash_key(c.node_name), xml_element_hash(c))
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
hash
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def add_ext_child(hash, key, value)
|
|
109
|
+
if hash.key?(key)
|
|
110
|
+
hash[key] = [hash[key]] unless hash[key].is_a?(Array)
|
|
111
|
+
hash[key] << value
|
|
112
|
+
else
|
|
113
|
+
hash[key] = value
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def ext_hash_key(name)
|
|
118
|
+
name.tr("-", "_").to_sym
|
|
119
|
+
end
|
|
72
120
|
end
|
|
73
121
|
end
|
|
@@ -46,7 +46,8 @@ module IsoDoc
|
|
|
46
46
|
n = @xrefs.get[elem["id"]] || @xrefs.get[elem["original-id"]]
|
|
47
47
|
lbl = labelled_autonum(@i18n.example, elem["id"] || elem["original-id"],
|
|
48
48
|
n&.dig(:label))
|
|
49
|
-
prefix_name(elem, { caption: block_delim }, lbl,
|
|
49
|
+
prefix_name(elem, { caption: i18n_delim("example", block_delim) }, lbl,
|
|
50
|
+
"name")
|
|
50
51
|
end
|
|
51
52
|
|
|
52
53
|
def note(docxml)
|
|
@@ -54,7 +55,7 @@ module IsoDoc
|
|
|
54
55
|
end
|
|
55
56
|
|
|
56
57
|
def note_delim(_elem)
|
|
57
|
-
"<tab/>"
|
|
58
|
+
i18n_delim("note", "<tab/>")
|
|
58
59
|
end
|
|
59
60
|
|
|
60
61
|
def note1(elem)
|
|
@@ -88,7 +89,8 @@ module IsoDoc
|
|
|
88
89
|
|
|
89
90
|
def admonition_options(elem)
|
|
90
91
|
options = { label: admonition_delim(elem) }
|
|
91
|
-
elem["type"] == "box" and
|
|
92
|
+
elem["type"] == "box" and
|
|
93
|
+
options = { caption: i18n_delim("admonition", block_delim) }
|
|
92
94
|
options
|
|
93
95
|
end
|
|
94
96
|
|
|
@@ -119,7 +121,7 @@ module IsoDoc
|
|
|
119
121
|
|
|
120
122
|
def admonition_delim(elem)
|
|
121
123
|
if elem["type"] == "box"
|
|
122
|
-
block_delim
|
|
124
|
+
i18n_delim("admonition", block_delim)
|
|
123
125
|
else
|
|
124
126
|
""
|
|
125
127
|
end
|
|
@@ -157,7 +159,7 @@ module IsoDoc
|
|
|
157
159
|
end
|
|
158
160
|
|
|
159
161
|
def table_delim
|
|
160
|
-
block_delim
|
|
162
|
+
i18n_delim("table", block_delim)
|
|
161
163
|
end
|
|
162
164
|
|
|
163
165
|
def table_long_strings_cleanup(docxml)
|
|
@@ -21,6 +21,9 @@ module IsoDoc
|
|
|
21
21
|
|
|
22
22
|
def concept_render(node, defaults)
|
|
23
23
|
opts, render, ref, ret = concept_render_init(node, defaults)
|
|
24
|
+
# generator-emitted term-resolution failure reports
|
|
25
|
+
# (metanorma-model-iso#144) render in boldface
|
|
26
|
+
ret&.xpath(ns(".//errormsg"))&.each { |e| e.name = "strong" }
|
|
24
27
|
ret&.at(ns("./refterm"))&.remove
|
|
25
28
|
ref && opts[:ref] != "false" and render&.next = " "
|
|
26
29
|
concept1_linkmention(ref, render, opts)
|
|
@@ -8,12 +8,17 @@ module IsoDoc
|
|
|
8
8
|
def related1(node)
|
|
9
9
|
p, ref, orig = related1_prep(node)
|
|
10
10
|
label = @i18n.relatedterms[orig["type"]].upcase
|
|
11
|
+
err = node.at(ns("./errormsg"))
|
|
11
12
|
ret = if p && ref
|
|
12
13
|
if p.text == ref.text
|
|
13
14
|
"<em>#{Common::to_xml(ref)}</em>"
|
|
14
15
|
else
|
|
15
16
|
"<em>#{to_xml(p)}</em> (#{Common::to_xml(ref)})"
|
|
16
17
|
end
|
|
18
|
+
elsif err
|
|
19
|
+
# generator-emitted term-resolution failure reports
|
|
20
|
+
# (metanorma-model-iso#144) render in boldface
|
|
21
|
+
"<strong>#{to_xml(err.children)}</strong>"
|
|
17
22
|
else "<strong>**RELATED TERM NOT FOUND**</strong>"
|
|
18
23
|
end
|
|
19
24
|
node.children = (l10n("<p><strong>#{label}:</strong> #{ret}</p>"))
|
|
@@ -54,12 +54,11 @@ module IsoDoc
|
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
def subfigure_delim
|
|
57
|
-
""
|
|
58
|
-
# "<span class='fmt-label-delim'>)</span>"
|
|
57
|
+
i18n_delim("subfigure", "")
|
|
59
58
|
end
|
|
60
59
|
|
|
61
60
|
def figure_delim(_elem)
|
|
62
|
-
block_delim
|
|
61
|
+
i18n_delim("figure", block_delim)
|
|
63
62
|
end
|
|
64
63
|
|
|
65
64
|
def figure_name(elem)
|
|
@@ -22,16 +22,24 @@ module IsoDoc
|
|
|
22
22
|
def index1(docxml, indexsect, index)
|
|
23
23
|
c = indexsect.add_child("<ul></ul>").first
|
|
24
24
|
index.keys.sort.each do |k|
|
|
25
|
-
words = index[k].keys
|
|
26
|
-
v[sortable(w).downcase] = w
|
|
27
|
-
end
|
|
25
|
+
words = index_sort_buckets(index[k].keys)
|
|
28
26
|
words.keys.localize(@lang.to_sym).sort.to_a.each do |w|
|
|
29
|
-
c.add_child index_entries(
|
|
27
|
+
words[w].each { |w1| c.add_child index_entries(w1, index[k]) }
|
|
30
28
|
end
|
|
31
29
|
end
|
|
32
30
|
index1_cleanup(docxml)
|
|
33
31
|
end
|
|
34
32
|
|
|
33
|
+
# Sorting is case-insensitive, so entries differing only in case (or
|
|
34
|
+
# only in markup, which the sort key strips) share a sort key; they
|
|
35
|
+
# are kept as distinct entries in a bucket per key, sorted adjacently
|
|
36
|
+
# (metanorma/metanorma#298: overwriting them lost all but one entry)
|
|
37
|
+
def index_sort_buckets(keys)
|
|
38
|
+
keys.compact.each_with_object({}) do |w, v|
|
|
39
|
+
(v[sortable(w).downcase] ||= []) << w
|
|
40
|
+
end.transform_values(&:sort)
|
|
41
|
+
end
|
|
42
|
+
|
|
35
43
|
def index1_cleanup(docxml)
|
|
36
44
|
docxml.xpath(ns("//indexsect//xref")).each do |x|
|
|
37
45
|
x.children.remove
|
|
@@ -48,34 +56,31 @@ module IsoDoc
|
|
|
48
56
|
{ xref_lbl: ", ", see_lbl: ", #{see_lbl}", also_lbl: ", #{also_lbl}" }
|
|
49
57
|
end
|
|
50
58
|
|
|
51
|
-
def index_entries(
|
|
52
|
-
ret = index_entries_head(
|
|
53
|
-
index.dig(words[primary], nil, nil),
|
|
59
|
+
def index_entries(primary, index)
|
|
60
|
+
ret = index_entries_head(primary, index.dig(primary, nil, nil),
|
|
54
61
|
index_entries_opt)
|
|
55
|
-
words2 = index[
|
|
56
|
-
&.each_with_object({}) { |w, v| v[w.downcase] = w }
|
|
62
|
+
words2 = index_sort_buckets(index[primary].keys)
|
|
57
63
|
unless words2.empty?
|
|
58
64
|
ret += "<ul>"
|
|
59
65
|
words2.keys.localize(@lang.to_sym).sort.to_a.each do |w|
|
|
60
|
-
ret += index_entries2(
|
|
66
|
+
words2[w].each { |w1| ret += index_entries2(w1, index[primary]) }
|
|
61
67
|
end
|
|
62
68
|
ret += "</ul>"
|
|
63
69
|
end
|
|
64
70
|
"#{ret}</li>"
|
|
65
71
|
end
|
|
66
72
|
|
|
67
|
-
def index_entries2(
|
|
68
|
-
ret = index_entries_head(
|
|
69
|
-
index.dig(words[secondary], nil),
|
|
73
|
+
def index_entries2(secondary, index)
|
|
74
|
+
ret = index_entries_head(secondary, index.dig(secondary, nil),
|
|
70
75
|
index_entries_opt)
|
|
71
|
-
words3 = index[
|
|
72
|
-
&.each_with_object({}) { |w, v| v[w.downcase] = w }
|
|
76
|
+
words3 = index_sort_buckets(index[secondary].keys)
|
|
73
77
|
unless words3.empty?
|
|
74
78
|
ret += "<ul>"
|
|
75
79
|
words3.keys.localize(@lang.to_sym).sort.to_a.each do |w|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
80
|
+
words3[w].each do |w1|
|
|
81
|
+
ret += "#{index_entries_head(w1, index[secondary][w1],
|
|
82
|
+
index_entries_opt)}</li>"
|
|
83
|
+
end
|
|
79
84
|
end
|
|
80
85
|
ret += "</ul>"
|
|
81
86
|
end
|
|
@@ -95,12 +100,9 @@ module IsoDoc
|
|
|
95
100
|
|
|
96
101
|
def index_entries_see(entries, label)
|
|
97
102
|
see_sort = entries&.dig(label) or return nil
|
|
98
|
-
x = see_sort
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
x.keys.localize(@lang.to_sym).sort.to_a.map do |k|
|
|
102
|
-
# see_sort[k]
|
|
103
|
-
x[k]
|
|
103
|
+
x = index_sort_buckets(see_sort)
|
|
104
|
+
x.keys.localize(@lang.to_sym).sort.to_a.flat_map do |k|
|
|
105
|
+
@index_fold_case ? [index_fold_head(x[k])] : x[k].uniq
|
|
104
106
|
end.join(", ")
|
|
105
107
|
end
|
|
106
108
|
|
|
@@ -116,12 +118,54 @@ module IsoDoc
|
|
|
116
118
|
index = extract_indexterms(terms)
|
|
117
119
|
index = extract_indexsee(index, see, :see)
|
|
118
120
|
index = extract_indexsee(index, also, :also)
|
|
121
|
+
@index_fold_case and index_fold_case(index)
|
|
119
122
|
index.keys.sort.each_with_object({}) do |k, v|
|
|
120
123
|
v[sortable(k)[0].upcase.transliterate] ||= {}
|
|
121
124
|
v[sortable(k)[0].upcase.transliterate][k] = index[k]
|
|
122
125
|
end
|
|
123
126
|
end
|
|
124
127
|
|
|
128
|
+
# Entries differing only in case merge into a single headword, the
|
|
129
|
+
# lowercase spelling, with the union of their subentries and locators:
|
|
130
|
+
# ((activity)) mid-sentence and ((Activity)) sentence-initially are
|
|
131
|
+
# the same entry. Suppressed by the :index-case-sensitive: document
|
|
132
|
+
# attribute for identifier-heavy documents, where Activity and
|
|
133
|
+
# activity are distinct case-bearing names (metanorma/metanorma#298).
|
|
134
|
+
# Applied per level: primaries, then secondaries, then tertiaries.
|
|
135
|
+
def index_fold_case(index)
|
|
136
|
+
index_fold_level(index)
|
|
137
|
+
index.each_value do |sec|
|
|
138
|
+
index_fold_level(sec)
|
|
139
|
+
sec.each_value { |ter| index_fold_level(ter) }
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def index_fold_level(hash)
|
|
144
|
+
hash.keys.grep(String).group_by { |w| sortable(w).downcase }
|
|
145
|
+
.each_value { |ws| ws.size > 1 and index_fold_bucket(hash, ws) }
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def index_fold_bucket(hash, words)
|
|
149
|
+
head = index_fold_head(words)
|
|
150
|
+
(words - [head]).each do |w|
|
|
151
|
+
index_merge(hash[head], hash[w])
|
|
152
|
+
hash.delete(w)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def index_fold_head(words)
|
|
157
|
+
words.find { |w| sortable(w) == sortable(w).downcase } || words.min
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def index_merge(target, other)
|
|
161
|
+
other.each do |k, v|
|
|
162
|
+
if !target.key?(k) then target[k] = v
|
|
163
|
+
elsif v.is_a?(Array) then target[k].concat(v)
|
|
164
|
+
else index_merge(target[k], v)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
125
169
|
def extract_indexsee(val, terms, label)
|
|
126
170
|
terms.each_with_object(val) do |t, v|
|
|
127
171
|
term, term2, term3 = extract_indexterms_init(t)
|
|
@@ -68,6 +68,9 @@ module IsoDoc
|
|
|
68
68
|
|
|
69
69
|
def ol_label_format(semx, elem)
|
|
70
70
|
template = ol_label_template(elem)[elem.parent["type"].to_sym]
|
|
71
|
+
# types outside the template set are not decorated, e.g. xml2rfc v2
|
|
72
|
+
# format strings passed through as ol/@type by metanorma-ietf
|
|
73
|
+
template or return semx
|
|
71
74
|
template.sub("%", semx)
|
|
72
75
|
end
|
|
73
76
|
|
|
@@ -51,7 +51,19 @@ module IsoDoc
|
|
|
51
51
|
|
|
52
52
|
def sourcecode1(elem)
|
|
53
53
|
ret1 = semx_fmt_dup(elem)
|
|
54
|
-
b = ret1.at(ns(".//body"))
|
|
54
|
+
if (b = ret1.at(ns(".//body")))
|
|
55
|
+
# Remove formatting whitespace *preceding* the body (e.g. the newlines
|
|
56
|
+
# standoc emits around a <name> with inline markup). Once the body is
|
|
57
|
+
# unwrapped and the name removed, such stray whitespace would be lexed
|
|
58
|
+
# as leading blank lines of code. Whitespace after the body and the
|
|
59
|
+
# verbatim body content itself are untouched, so genuine code
|
|
60
|
+
# indentation is preserved.
|
|
61
|
+
b.parent.children.each do |c|
|
|
62
|
+
c == b and break
|
|
63
|
+
c.text? && c.text.strip.empty? and c.remove
|
|
64
|
+
end
|
|
65
|
+
b.replace(b.children)
|
|
66
|
+
end
|
|
55
67
|
source_label(elem)
|
|
56
68
|
source_highlight(ret1, elem["linenums"] == "true", elem["lang"])
|
|
57
69
|
callouts(elem)
|
|
@@ -163,17 +175,35 @@ module IsoDoc
|
|
|
163
175
|
lexer = Rouge::Lexer.find(lang || "plaintext") ||
|
|
164
176
|
Rouge::Lexer.find("plaintext")
|
|
165
177
|
l = Rouge::Lexers::Escape.new(start: "{^^{", end: "}^^}", lang: lexer)
|
|
178
|
+
source_collapse_markup_newlines(elem)
|
|
166
179
|
source = to_xml(elem.children).gsub("<", "{^^{<").gsub(">", ">}^^}")
|
|
167
180
|
l.lang.reset!
|
|
168
181
|
l.lex(@c.decode(source))
|
|
169
182
|
end
|
|
170
183
|
|
|
184
|
+
# Embedded markup serialised across multiple lines (e.g. pretty-printed
|
|
185
|
+
# MathML from stem) would be split across line cells by the line-table
|
|
186
|
+
# formatter, leaving each cell with unbalanced tags: the string reparse
|
|
187
|
+
# of the table then cascades mis-nested rows, and rendering crashes on
|
|
188
|
+
# the orphaned rows (metanorma-pdfa#84). Collapse whitespace inside
|
|
189
|
+
# embedded elements so each occupies a single code line; code text
|
|
190
|
+
# outside the elements is untouched.
|
|
191
|
+
def source_collapse_markup_newlines(elem)
|
|
192
|
+
elem.elements.each do |e|
|
|
193
|
+
e.xpath(".//text()").each do |t|
|
|
194
|
+
/[\n\r]/.match?(t.content) and
|
|
195
|
+
t.content = t.content.gsub(/\s*[\n\r]+\s*/, " ")
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
171
200
|
def source_label(elem)
|
|
172
201
|
if !labelled_ancestor(elem) && # do not number if labelled_ancestor
|
|
173
202
|
lbl = @xrefs.anchor(elem["id"], :label, false)
|
|
174
203
|
s = labelled_autonum(lower2cap(@i18n.figure), elem["id"], lbl)&.strip
|
|
175
204
|
end
|
|
176
|
-
prefix_name(elem, { caption: block_delim }, s,
|
|
205
|
+
prefix_name(elem, { caption: i18n_delim("sourcecode", block_delim) }, s,
|
|
206
|
+
"name")
|
|
177
207
|
end
|
|
178
208
|
end
|
|
179
209
|
end
|
data/lib/isodoc/version.rb
CHANGED
|
@@ -70,9 +70,19 @@ module IsoDoc
|
|
|
70
70
|
elem_name)
|
|
71
71
|
ret[:container] = @klass.get_clause_id(node) if opt[:container]
|
|
72
72
|
ret[:value] = stripsemx(lbl)
|
|
73
|
+
t = asset_title(node) and ret[:title] = t
|
|
73
74
|
ret
|
|
74
75
|
end
|
|
75
76
|
|
|
77
|
+
# Caption of a numbered asset (its <name>, or a modspec requirement's
|
|
78
|
+
# <title>), semx-wrapped, so that xrefstyle=full can render
|
|
79
|
+
# "Table 3, caption" the way clauses render "Clause 6, Title". Returns nil
|
|
80
|
+
# when the asset has no caption, so anchor_xref_full falls back to short.
|
|
81
|
+
def asset_title(node)
|
|
82
|
+
n = node.at(ns("./name")) || node.at(ns("./title")) or return nil
|
|
83
|
+
semx(node, n.children.to_xml, n.name)
|
|
84
|
+
end
|
|
85
|
+
|
|
76
86
|
include ::IsoDoc::XrefGen::Util
|
|
77
87
|
end
|
|
78
88
|
end
|
|
@@ -135,6 +135,7 @@ title_prefixes:
|
|
|
135
135
|
locality: {
|
|
136
136
|
section: Section,
|
|
137
137
|
clause: Clause,
|
|
138
|
+
definition: Definition,
|
|
138
139
|
part: Part,
|
|
139
140
|
paragraph: Paragraph,
|
|
140
141
|
chapter: Chapter,
|
|
@@ -174,6 +175,9 @@ inflection:
|
|
|
174
175
|
Clause:
|
|
175
176
|
sg: Clause
|
|
176
177
|
pl: Clauses
|
|
178
|
+
Definition:
|
|
179
|
+
sg: Definition
|
|
180
|
+
pl: Definitions
|
|
177
181
|
Annex:
|
|
178
182
|
sg: Annex
|
|
179
183
|
pl: Annexes
|
|
@@ -131,6 +131,7 @@ title_prefixes:
|
|
|
131
131
|
locality: {
|
|
132
132
|
section: Section,
|
|
133
133
|
clause: Article,
|
|
134
|
+
definition: Définition,
|
|
134
135
|
part: Partie,
|
|
135
136
|
paragraph: Alinéa,
|
|
136
137
|
chapter: Chapitre,
|
|
@@ -170,6 +171,9 @@ inflection:
|
|
|
170
171
|
Clause:
|
|
171
172
|
sg: Article
|
|
172
173
|
pl: Articles
|
|
174
|
+
Definition:
|
|
175
|
+
sg: Définition
|
|
176
|
+
pl: Définitions
|
|
173
177
|
Annex:
|
|
174
178
|
sg: Annexe
|
|
175
179
|
pl: Annexes
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: isodoc
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.7.
|
|
4
|
+
version: 3.7.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -351,7 +351,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
351
351
|
- !ruby/object:Gem::Version
|
|
352
352
|
version: '0'
|
|
353
353
|
requirements: []
|
|
354
|
-
rubygems_version: 4.0.
|
|
354
|
+
rubygems_version: 4.0.16
|
|
355
355
|
specification_version: 4
|
|
356
356
|
summary: Convert documents in IsoDoc into Word and HTML in AsciiDoc.
|
|
357
357
|
test_files: []
|