isodoc 1.2.3 → 1.2.8

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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake.yml +69 -0
  3. data/README.adoc +1 -3
  4. data/isodoc.gemspec +3 -1
  5. data/lib/isodoc-yaml/i18n-en.yaml +1 -0
  6. data/lib/isodoc-yaml/i18n-fr.yaml +9 -8
  7. data/lib/isodoc-yaml/i18n-zh-Hans.yaml +1 -0
  8. data/lib/isodoc/base_style/all.css +5 -1
  9. data/lib/isodoc/base_style/metanorma_word.css +6 -0
  10. data/lib/isodoc/base_style/metanorma_word.scss +6 -0
  11. data/lib/isodoc/base_style/reset.css +5 -1
  12. data/lib/isodoc/base_style/reset.scss +6 -1
  13. data/lib/isodoc/convert.rb +1 -14
  14. data/lib/isodoc/function/blocks.rb +1 -0
  15. data/lib/isodoc/function/inline.rb +0 -33
  16. data/lib/isodoc/function/references.rb +5 -3
  17. data/lib/isodoc/function/table.rb +1 -0
  18. data/lib/isodoc/function/to_word_html.rb +3 -2
  19. data/lib/isodoc/function/utils.rb +1 -1
  20. data/lib/isodoc/html_function/postprocess.rb +1 -0
  21. data/lib/isodoc/i18n.rb +23 -52
  22. data/lib/isodoc/metadata.rb +44 -111
  23. data/lib/isodoc/metadata_contributor.rb +90 -0
  24. data/lib/isodoc/metadata_date.rb +11 -0
  25. data/lib/isodoc/presentation_function/bibdata.rb +96 -0
  26. data/lib/isodoc/presentation_function/block.rb +28 -9
  27. data/lib/isodoc/presentation_function/inline.rb +149 -34
  28. data/lib/isodoc/presentation_xml_convert.rb +7 -0
  29. data/lib/isodoc/version.rb +1 -1
  30. data/lib/isodoc/word_function/body.rb +12 -0
  31. data/lib/isodoc/word_function/postprocess.rb +2 -2
  32. data/lib/isodoc/word_function/table.rb +3 -2
  33. data/lib/isodoc/xref.rb +1 -0
  34. data/lib/isodoc/xref/xref_anchor.rb +8 -3
  35. data/lib/isodoc/xref/xref_counter.rb +21 -7
  36. data/lib/isodoc/xref/xref_gen.rb +29 -6
  37. data/lib/isodoc/xref/xref_sect_gen.rb +1 -1
  38. data/lib/isodoc/xslfo_convert.rb +6 -1
  39. data/spec/assets/i18n.yaml +22 -5
  40. data/spec/isodoc/blocks_spec.rb +331 -215
  41. data/spec/isodoc/footnotes_spec.rb +4 -5
  42. data/spec/isodoc/i18n_spec.rb +89 -16
  43. data/spec/isodoc/inline_spec.rb +185 -163
  44. data/spec/isodoc/metadata_spec.rb +69 -19
  45. data/spec/isodoc/postproc_spec.rb +40 -3
  46. data/spec/isodoc/presentation_xml_spec.rb +584 -1
  47. data/spec/isodoc/ref_spec.rb +5 -5
  48. data/spec/isodoc/section_spec.rb +9 -9
  49. data/spec/isodoc/table_spec.rb +306 -207
  50. data/spec/isodoc/terms_spec.rb +1 -1
  51. data/spec/isodoc/xref_spec.rb +46 -18
  52. data/spec/spec_helper.rb +6 -0
  53. metadata +35 -7
  54. data/.github/workflows/macos.yml +0 -42
  55. data/.github/workflows/ubuntu.yml +0 -62
  56. data/.github/workflows/windows.yml +0 -44
@@ -1,3 +1,5 @@
1
+ require "twitter_cldr"
2
+
1
3
  module IsoDoc
2
4
  class PresentationXMLConvert < ::IsoDoc::Convert
3
5
  def prefix_container(container, linkend, _target)
@@ -7,17 +9,25 @@ module IsoDoc
7
9
  def anchor_linkend(node, linkend)
8
10
  if node["citeas"].nil? && node["bibitemid"]
9
11
  return @xrefs.anchor(node["bibitemid"] ,:xref) || "???"
12
+ elsif node["target"] && node["droploc"]
13
+ return @xrefs.anchor(node["target"], :value) ||
14
+ @xrefs.anchor(node["target"], :label) ||
15
+ @xrefs.anchor(node["target"], :xref) || "???"
10
16
  elsif node["target"] && !/.#./.match(node["target"])
11
- linkend = @xrefs.anchor(node["target"], :xref)
12
- container = @xrefs.anchor(node["target"], :container, false)
13
- (container && get_note_container_id(node) != container &&
14
- @xrefs.get[node["target"]]) &&
15
- linkend = prefix_container(container, linkend, node["target"])
16
- linkend = capitalise_xref(node, linkend)
17
+ linkend = anchor_linkend1(node)
17
18
  end
18
19
  linkend || "???"
19
20
  end
20
21
 
22
+ def anchor_linkend1(node)
23
+ linkend = @xrefs.anchor(node["target"], :xref)
24
+ container = @xrefs.anchor(node["target"], :container, false)
25
+ (container && get_note_container_id(node) != container &&
26
+ @xrefs.get[node["target"]]) &&
27
+ linkend = prefix_container(container, linkend, node["target"])
28
+ capitalise_xref(node, linkend)
29
+ end
30
+
21
31
  def capitalise_xref(node, linkend)
22
32
  return linkend unless %w(Latn Cyrl Grek).include? @script
23
33
  return linkend&.capitalize if node["case"] == "capital"
@@ -30,7 +40,7 @@ module IsoDoc
30
40
  end
31
41
 
32
42
  def nearest_block_parent(node)
33
- until %w(p title td th name formula
43
+ until %w(p title td th name formula
34
44
  li dt dd sourcecode pre).include?(node.name)
35
45
  node = node.parent
36
46
  end
@@ -43,13 +53,13 @@ module IsoDoc
43
53
  end
44
54
  end
45
55
 
46
- def get_linkend(node)
47
- contents = non_locality_elems(node).select { |c| !c.text? || /\S/.match(c) }
56
+ def get_linkend(n)
57
+ contents = non_locality_elems(n).select { |c| !c.text? || /\S/.match(c) }
48
58
  return unless contents.empty?
49
- link = anchor_linkend(node, docid_l10n(node["target"] || node["citeas"]))
50
- link += eref_localities(node.xpath(ns("./locality | ./localityStack")), link)
51
- non_locality_elems(node).each { |n| n.remove }
52
- node.add_child(link)
59
+ link = anchor_linkend(n, docid_l10n(n["target"] || n["citeas"]))
60
+ link += eref_localities(n.xpath(ns("./locality | ./localityStack")), link)
61
+ non_locality_elems(n).each { |n| n.remove }
62
+ n.add_child(link)
53
63
  end
54
64
  # so not <origin bibitemid="ISO7301" citeas="ISO 7301">
55
65
  # <locality type="section"><reference>3.1</reference></locality></origin>
@@ -59,14 +69,19 @@ module IsoDoc
59
69
  refs.each_with_index do |r, i|
60
70
  delim = ","
61
71
  delim = ";" if r.name == "localityStack" && i>0
62
- if r.name == "localityStack"
63
- r.elements.each_with_index do |rr, j|
64
- ret += eref_localities0(rr, j, target, delim)
65
- delim = ","
66
- end
67
- else
68
- ret += eref_localities0(r, i, target, delim)
72
+ ret = eref_locality_stack(r, i, target, delim, ret)
73
+ end
74
+ ret
75
+ end
76
+
77
+ def eref_locality_stack(r, i, target, delim, ret)
78
+ if r.name == "localityStack"
79
+ r.elements.each_with_index do |rr, j|
80
+ ret += eref_localities0(rr, j, target, delim)
81
+ delim = ","
69
82
  end
83
+ else
84
+ ret += eref_localities0(r, i, target, delim)
70
85
  end
71
86
  ret
72
87
  end
@@ -79,7 +94,7 @@ module IsoDoc
79
94
  end
80
95
  end
81
96
 
82
- # TODO: move to localization file
97
+ # TODO: move to localization file
83
98
  def eref_localities1_zh(target, type, from, to, delim)
84
99
  ret = "#{delim} 第#{from.text}" if from
85
100
  ret += "&ndash;#{to.text}" if to
@@ -91,7 +106,8 @@ module IsoDoc
91
106
  # TODO: move to localization file
92
107
  def eref_localities1(target, type, from, to, delim, lang = "en")
93
108
  return "" if type == "anchor"
94
- return l10n(eref_localities1_zh(target, type, from, to, delim)) if lang == "zh"
109
+ lang == "zh" and
110
+ return l10n(eref_localities1_zh(target, type, from, to, delim))
95
111
  ret = delim
96
112
  loc = @i18n.locality[type] || type.sub(/^locality:/, "").capitalize
97
113
  ret += " #{loc}"
@@ -101,31 +117,130 @@ module IsoDoc
101
117
  end
102
118
 
103
119
  def xref(docxml)
104
- docxml.xpath(ns("//xref")).each do |f|
105
- xref1(f)
106
- end
120
+ docxml.xpath(ns("//xref")).each { |f| xref1(f) }
107
121
  end
108
122
 
109
123
  def eref(docxml)
110
- docxml.xpath(ns("//eref")).each do |f|
111
- xref1(f)
112
- end
124
+ docxml.xpath(ns("//eref")).each { |f| xref1(f) }
113
125
  end
114
126
 
115
127
  def origin(docxml)
116
- docxml.xpath(ns("//origin[not(termref)]")).each do |f|
117
- xref1(f)
118
- end
128
+ docxml.xpath(ns("//origin[not(termref)]")).each { |f| xref1(f) }
119
129
  end
120
130
 
121
131
  def quotesource(docxml)
122
- docxml.xpath(ns("//quote/source")).each do |f|
123
- xref1(f)
124
- end
132
+ docxml.xpath(ns("//quote/source")).each { |f| xref1(f) }
125
133
  end
126
134
 
127
135
  def xref1(f)
128
136
  get_linkend(f)
129
137
  end
138
+
139
+ def concept(docxml)
140
+ docxml.xpath(ns("//concept")).each { |f| concept1(f) }
141
+ end
142
+
143
+ def concept1(node)
144
+ content = node.first_element_child.children.select do |c|
145
+ !%w{locality localityStack}.include? c.name
146
+ end.select { |c| !c.text? || /\S/.match(c) }
147
+ node.replace content.empty? ?
148
+ @i18n.term_defined_in.sub(/%/, node.first_element_child.to_xml) :
149
+ "<em>#{node.children.to_xml}</em>"
150
+ end
151
+
152
+
153
+ MATHML = { "m" => "http://www.w3.org/1998/Math/MathML" }.freeze
154
+
155
+ def mathml(docxml)
156
+ locale = twitter_cldr_localiser()
157
+ docxml.xpath("//m:math", MATHML).each do |f|
158
+ mathml1(f, locale)
159
+ end
160
+ end
161
+
162
+ # symbols is merged into
163
+ # TwitterCldr::DataReaders::NumberDataReader.new(locale).symbols
164
+ def localize_maths(f, locale)
165
+ f.xpath(".//m:mn", MATHML).each do |x|
166
+ num = /\./.match(x.text) ? x.text.to_f : x.text.to_i
167
+ x.children = localized_number(num, locale)
168
+ end
169
+ end
170
+
171
+ # By itself twiiter cldr does not support fraction part digits grouping
172
+ # and custom delimeter, will decorate fraction part manually
173
+ def localized_number(num, locale)
174
+ localized = num.localize(locale).to_s
175
+ twitter_cldr_reader_symbols = twitter_cldr_reader(locale)
176
+ return localized unless twitter_cldr_reader_symbols[:decimal]
177
+ integer, fraction = localized.split(twitter_cldr_reader_symbols[:decimal])
178
+ return localized if fraction.nil? || fraction.length.zero?
179
+ [integer, decorate_fraction_part(fraction, locale)].
180
+ join(twitter_cldr_reader_symbols[:decimal])
181
+ end
182
+
183
+ def decorate_fraction_part(fract, locale)
184
+ result = []
185
+ twitter_cldr_reader_symbols = twitter_cldr_reader(locale)
186
+ fract = fract.slice(0..(twitter_cldr_reader_symbols[:precision] || -1))
187
+ fr_group_digits = twitter_cldr_reader_symbols[:fraction_group_digits] || 1
188
+ until fract.empty?
189
+ result.push(fract.slice!(0, fr_group_digits))
190
+ end
191
+ result.join(twitter_cldr_reader_symbols[:fraction_group].to_s)
192
+ end
193
+
194
+ def twitter_cldr_localiser_symbols
195
+ {}
196
+ end
197
+
198
+ def twitter_cldr_reader(locale)
199
+ num = TwitterCldr::DataReaders::NumberDataReader.new(locale)
200
+ num.symbols.merge!(twitter_cldr_localiser_symbols)
201
+ end
202
+
203
+ def twitter_cldr_localiser()
204
+ locale = TwitterCldr.supported_locale?(@lang.to_sym) ? @lang.to_sym : :en
205
+ twitter_cldr_reader(locale)
206
+ locale
207
+ end
208
+
209
+ def mathml1(f, locale)
210
+ localize_maths(f, locale)
211
+ return unless f.elements.size == 1 && f.elements.first.name == "mn"
212
+ f.replace(f.at("./m:mn", MATHML).children)
213
+ end
214
+
215
+ def variant(docxml)
216
+ docxml.xpath(ns("//variant")).each { |f| variant1(f) }
217
+ docxml.xpath(ns("//variant[@remove = 'true']")).each { |f| f.remove }
218
+ docxml.xpath(ns("//variant")).each do |v|
219
+ next unless v&.next&.name == "variant"
220
+ v.next = "/"
221
+ end
222
+ docxml.xpath(ns("//variant")).each { |f| f.replace(f.children) }
223
+ end
224
+
225
+ def variant1(node)
226
+ if (!node["lang"] || node["lang"] == @lang) &&
227
+ (!node["script"] || node["script"] == @script)
228
+ elsif found_matching_variant_sibling(node)
229
+ node["remove"] = "true"
230
+ else
231
+ #return unless !node.at("./preceding-sibling::xmlns:variant")
232
+ end
233
+ end
234
+
235
+ def found_matching_variant_sibling(node)
236
+ prev = node.xpath("./preceding-sibling::xmlns:variant")
237
+ foll = node.xpath("./following-sibling::xmlns:variant")
238
+ found = false
239
+ (prev + foll).each do |n|
240
+ found = true if n["lang"] == @lang &&
241
+ (!n["script"] || n["script"] == @script)
242
+ end
243
+ found
244
+ end
130
245
  end
131
246
  end
@@ -1,6 +1,7 @@
1
1
  require_relative "presentation_function/block"
2
2
  require_relative "presentation_function/inline"
3
3
  require_relative "presentation_function/section"
4
+ require_relative "presentation_function/bibdata"
4
5
 
5
6
  module IsoDoc
6
7
  class PresentationXMLConvert < ::IsoDoc::Convert
@@ -14,10 +15,12 @@ module IsoDoc
14
15
  @xrefs.parse docxml
15
16
  info docxml, nil
16
17
  conversions(docxml)
18
+ docxml.root["type"] = "presentation"
17
19
  docxml.to_xml
18
20
  end
19
21
 
20
22
  def conversions(docxml)
23
+ bibdata docxml
21
24
  section docxml
22
25
  block docxml
23
26
  inline docxml
@@ -30,6 +33,7 @@ module IsoDoc
30
33
  end
31
34
 
32
35
  def block(docxml)
36
+ amend docxml
33
37
  table docxml
34
38
  figure docxml
35
39
  sourcecode docxml
@@ -47,7 +51,10 @@ module IsoDoc
47
51
  xref docxml
48
52
  eref docxml
49
53
  origin docxml
54
+ concept docxml
50
55
  quotesource docxml
56
+ mathml docxml
57
+ variant docxml
51
58
  end
52
59
 
53
60
  def postprocess(result, filename, dir)
@@ -1,3 +1,3 @@
1
1
  module IsoDoc
2
- VERSION = "1.2.3".freeze
2
+ VERSION = "1.2.8".freeze
3
3
  end
@@ -196,6 +196,18 @@ module IsoDoc::WordFunction
196
196
  out.parent.at("./table")["class"] = "formula_dl"
197
197
  end
198
198
 
199
+ def formula_parse1(node, out)
200
+ out.div **attr_code(class: "formula") do |div|
201
+ div.p do |p|
202
+ parse(node.at(ns("./stem")), div)
203
+ insert_tab(div, 1)
204
+ if lbl = node&.at(ns("./name"))&.text
205
+ div << "(#{lbl})"
206
+ end
207
+ end
208
+ end
209
+ end
210
+
199
211
  def li_parse(node, out)
200
212
  out.li **attr_code(id: node["id"]) do |li|
201
213
  if node["uncheckedcheckbox"] == "true"
@@ -127,7 +127,7 @@ xmlns:m="http://schemas.microsoft.com/office/2004/12/omml">
127
127
  def list_add(xpath, lvl)
128
128
  xpath.each do |list|
129
129
  (list.xpath(".//li") - list.xpath(".//ol//li | .//ul//li")).each do |l|
130
- l.xpath("./p | ./div").each_with_index do |p, i|
130
+ l.xpath("./p | ./div | ./table").each_with_index do |p, i|
131
131
  next if i == 0
132
132
  p.wrap(%{<div class="ListContLevel#{lvl}"/>})
133
133
  end
@@ -183,7 +183,7 @@ xmlns:m="http://schemas.microsoft.com/office/2004/12/omml">
183
183
  def generate_header(filename, _dir)
184
184
  return nil unless @header
185
185
  template = IsoDoc::Common.liquid(File.read(@header, encoding: "UTF-8"))
186
- meta = @meta.get
186
+ meta = @meta.get.merge(@labels || {}).merge(@meta.labels || {})
187
187
  meta[:filename] = filename
188
188
  params = meta.map { |k, v| [k.to_s, v] }.to_h
189
189
  Tempfile.open(%w(header html), :encoding => "utf-8") do |f|
@@ -29,7 +29,7 @@ module IsoDoc::WordFunction
29
29
  border-bottom:#{SW1} #{rowmax == totalrows ? '1.5' : '1.0'}pt;
30
30
  mso-border-bottom-alt:#{SW1} #{rowmax == totalrows ? '1.5' : '1.0'}pt;
31
31
  STYLE
32
- { rowspan: td["rowspan"], colspan: td["colspan"],
32
+ { rowspan: td["rowspan"], colspan: td["colspan"], valign: td["valign"],
33
33
  align: td["align"], style: style.gsub(/\n/, "") }
34
34
  end
35
35
 
@@ -38,7 +38,8 @@ module IsoDoc::WordFunction
38
38
  summary: node["summary"],
39
39
  width: node["width"],
40
40
  style: "mso-table-anchor-horizontal:column;"\
41
- "mso-table-overlap:never;border-spacing:0;border-width:1px;#{keep_style(node)}"
41
+ "mso-table-overlap:never;border-spacing:0;border-width:1px;#{keep_style(node)}",
42
+ class: (node.text.length > 4000 ? "MsoISOTableBig" : "MsoISOTable")
42
43
  }))
43
44
  end
44
45
 
@@ -41,6 +41,7 @@ module IsoDoc
41
41
 
42
42
  # extract names for all anchors, xref and label
43
43
  def parse(docxml)
44
+ amend_preprocess(docxml)
44
45
  initial_anchor_names(docxml)
45
46
  back_anchor_names(docxml)
46
47
  # preempt clause notes with all other types of note (ISO default)
@@ -35,11 +35,15 @@ module IsoDoc::XrefGen
35
35
  end
36
36
 
37
37
  def anchor_struct_xref(lbl, elem)
38
+ l10n("#{elem} #{anchor_struct_value(lbl, elem)}")
39
+ end
40
+
41
+ def anchor_struct_value(lbl, elem)
38
42
  case elem
39
- when @labels["formula"] then l10n("#{elem} (#{lbl})")
40
- when @labels["inequality"] then l10n("#{elem} (#{lbl})")
43
+ when @labels["formula"] then "(#{lbl})"
44
+ when @labels["inequality"] then "(#{lbl})"
41
45
  else
42
- l10n("#{elem} #{lbl}")
46
+ lbl
43
47
  end
44
48
  end
45
49
 
@@ -50,6 +54,7 @@ module IsoDoc::XrefGen
50
54
  ret[:xref].gsub!(/ $/, "")
51
55
  ret[:container] = @klass.get_clause_id(container) unless container.nil?
52
56
  ret[:type] = type
57
+ ret[:value] = anchor_struct_value(lbl, elem)
53
58
  ret
54
59
  end
55
60
  end
@@ -8,17 +8,20 @@ module IsoDoc::XrefGen
8
8
  @subseq = ""
9
9
  @letter_override = nil
10
10
  @number_override = nil
11
+ @base = ""
11
12
  end
12
13
 
13
14
  def new_subseq_increment(node)
14
15
  @subseq = node["subsequence"]
15
16
  @num += 1
16
17
  @letter = node["subsequence"] ? "a" : ""
18
+ @base = ""
17
19
  if node["number"]
18
- /^(?<n>\d*)(?<a>[a-z]*)$/ =~ node["number"]
19
- if n || a
20
- @letter_override = @letter = a if a
21
- @number_override = @num = n.to_i if n
20
+ /^(?<b>.*?)(?<n>\d*)(?<a>[a-z]*)$/ =~ node["number"]
21
+ if !n.empty? || !a.empty?
22
+ @letter_override = @letter = a unless a.empty?
23
+ @number_override = @num = n.to_i unless n.empty?
24
+ @base = b
22
25
  else
23
26
  @letter_override = node["number"]
24
27
  @letter = @letter_override if /^[a-z]$/.match(@letter_override)
@@ -28,8 +31,13 @@ module IsoDoc::XrefGen
28
31
 
29
32
  def sequence_increment(node)
30
33
  if node["number"]
34
+ @base = ""
31
35
  @number_override = node["number"]
32
- @num = @number_override.to_i if /^\d+$/.match(@number_override)
36
+ /^(?<b>.*?)(?<n>\d+)$/ =~ node["number"]
37
+ unless n.nil? || n.empty?
38
+ @num = n.to_i
39
+ @base = b
40
+ end
33
41
  else
34
42
  @num += 1
35
43
  end
@@ -37,8 +45,14 @@ module IsoDoc::XrefGen
37
45
 
38
46
  def subsequence_increment(node)
39
47
  if node["number"]
48
+ @base = ""
40
49
  @letter_override = node["number"]
41
- @letter = @letter_override if /^[a-z]$/.match(@letter_override)
50
+ /^(?<b>.*?)(?<n>\d*)(?<a>[a-z]+)$/ =~ node["number"]
51
+ unless a.empty?
52
+ @letter = a
53
+ @base = b
54
+ @number_override = @num = n.to_i unless n.empty?
55
+ end
42
56
  else
43
57
  @letter = (@letter.ord + 1).chr.to_s
44
58
  end
@@ -59,7 +73,7 @@ module IsoDoc::XrefGen
59
73
  end
60
74
 
61
75
  def print
62
- "#{@number_override || @num}#{@letter_override || @letter}"
76
+ "#{@base}#{@number_override || @num}#{@letter_override || @letter}"
63
77
  end
64
78
 
65
79
  def listlabel(depth)