isodoc 1.2.3 → 1.2.8

Sign up to get free protection for your applications and to get access to all the features.
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)