isodoc 1.2.2 → 1.2.7

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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake.yml +64 -0
  3. data/isodoc.gemspec +3 -1
  4. data/lib/isodoc-yaml/i18n-fr.yaml +1 -1
  5. data/lib/isodoc/base_style/all.css +5 -1
  6. data/lib/isodoc/base_style/metanorma_word.css +6 -0
  7. data/lib/isodoc/base_style/metanorma_word.scss +6 -0
  8. data/lib/isodoc/base_style/reset.css +5 -1
  9. data/lib/isodoc/base_style/reset.scss +6 -1
  10. data/lib/isodoc/convert.rb +1 -14
  11. data/lib/isodoc/function/blocks.rb +1 -0
  12. data/lib/isodoc/function/cleanup.rb +1 -1
  13. data/lib/isodoc/function/references.rb +4 -2
  14. data/lib/isodoc/function/section.rb +13 -1
  15. data/lib/isodoc/function/table.rb +1 -0
  16. data/lib/isodoc/function/to_word_html.rb +4 -0
  17. data/lib/isodoc/function/utils.rb +1 -1
  18. data/lib/isodoc/html_function/html.rb +1 -0
  19. data/lib/isodoc/i18n.rb +8 -50
  20. data/lib/isodoc/metadata.rb +44 -111
  21. data/lib/isodoc/metadata_contributor.rb +90 -0
  22. data/lib/isodoc/metadata_date.rb +11 -0
  23. data/lib/isodoc/presentation_function/bibdata.rb +96 -0
  24. data/lib/isodoc/presentation_function/block.rb +14 -0
  25. data/lib/isodoc/presentation_function/inline.rb +27 -14
  26. data/lib/isodoc/presentation_xml_convert.rb +4 -0
  27. data/lib/isodoc/version.rb +1 -1
  28. data/lib/isodoc/word_function/body.rb +1 -0
  29. data/lib/isodoc/word_function/postprocess.rb +2 -2
  30. data/lib/isodoc/word_function/table.rb +3 -2
  31. data/lib/isodoc/xref.rb +1 -0
  32. data/lib/isodoc/xref/xref_anchor.rb +8 -3
  33. data/lib/isodoc/xref/xref_counter.rb +21 -7
  34. data/lib/isodoc/xref/xref_gen.rb +29 -6
  35. data/lib/isodoc/xref/xref_sect_gen.rb +1 -1
  36. data/lib/isodoc/xslfo_convert.rb +6 -1
  37. data/spec/assets/i18n.yaml +16 -0
  38. data/spec/isodoc/blocks_spec.rb +330 -215
  39. data/spec/isodoc/cleanup_spec.rb +3 -1
  40. data/spec/isodoc/i18n_spec.rb +68 -16
  41. data/spec/isodoc/inline_spec.rb +47 -5
  42. data/spec/isodoc/metadata_spec.rb +69 -19
  43. data/spec/isodoc/postproc_spec.rb +39 -3
  44. data/spec/isodoc/ref_spec.rb +3 -3
  45. data/spec/isodoc/section_spec.rb +134 -10
  46. data/spec/isodoc/table_spec.rb +306 -207
  47. data/spec/isodoc/terms_spec.rb +1 -1
  48. data/spec/isodoc/xref_spec.rb +46 -18
  49. data/spec/spec_helper.rb +6 -0
  50. metadata +35 -7
  51. data/.github/workflows/macos.yml +0 -42
  52. data/.github/workflows/ubuntu.yml +0 -62
  53. data/.github/workflows/windows.yml +0 -44
@@ -0,0 +1,90 @@
1
+ module IsoDoc
2
+ class Metadata
3
+ def extract_person_names(authors)
4
+ authors.reduce([]) do |ret, a|
5
+ if a.at(ns('./name/completename'))
6
+ ret << a.at(ns('./name/completename')).text
7
+ else
8
+ fn = []
9
+ forenames = a.xpath(ns('./name/forename'))
10
+ forenames.each { |f| fn << f.text }
11
+ surname = a&.at(ns('./name/surname'))&.text
12
+ ret << fn.join(' ') + ' ' + surname
13
+ end
14
+ end
15
+ end
16
+
17
+ def extract_person_affiliations(authors)
18
+ authors.reduce([]) do |m, a|
19
+ name = a&.at(ns('./affiliation/organization/name'))&.text
20
+ location = a&.at(ns('./affiliation/organization/address/'\
21
+ 'formattedAddress'))&.text
22
+ m << (!name.nil? && !location.nil? ? "#{name}, #{location}" :
23
+ (name || location || ''))
24
+ m
25
+ end
26
+ end
27
+
28
+ def extract_person_names_affiliations(authors)
29
+ names = extract_person_names(authors)
30
+ affils = extract_person_affiliations(authors)
31
+ ret = {}
32
+ affils.each_with_index do |a, i|
33
+ ret[a] ||= []
34
+ ret[a] << names[i]
35
+ end
36
+ ret
37
+ end
38
+
39
+ def personal_authors(isoxml)
40
+ authors = isoxml.xpath(ns("//bibdata/contributor[role/@type = 'author' "\
41
+ "or xmlns:role/@type = 'editor']/person"))
42
+ set(:authors, extract_person_names(authors))
43
+ set(:authors_affiliations, extract_person_names_affiliations(authors))
44
+ end
45
+
46
+ def author(xml, _out)
47
+ personal_authors(xml)
48
+ agency(xml)
49
+ end
50
+
51
+ def iso?(org)
52
+ name = org&.at(ns('./name'))&.text
53
+ abbrev = org&.at(ns('./abbreviation'))&.text
54
+ (abbrev == 'ISO' ||
55
+ name == 'International Organization for Standardization')
56
+ end
57
+
58
+ def agency1(xml)
59
+ agency = ''
60
+ publisher = []
61
+ xml.xpath(ns("//bibdata/contributor[xmlns:role/@type = 'publisher']/"\
62
+ 'organization')).each do |org|
63
+ name = org&.at(ns('./name'))&.text
64
+ agency1 = org&.at(ns('./abbreviation'))&.text || name
65
+ publisher << name if name
66
+ agency = iso?(org) ? "ISO/#{agency}" : "#{agency}#{agency1}/"
67
+ end
68
+ [agency, publisher]
69
+ end
70
+
71
+ def agency(xml)
72
+ agency, publisher = agency1(xml)
73
+ set(:agency, agency.sub(%r{/$}, ''))
74
+ set(:publisher, @i18n.multiple_and(publisher, @labels['and']))
75
+ agency_addr(xml)
76
+ end
77
+
78
+ def agency_addr(xml)
79
+ a = xml.at(ns("//bibdata/contributor[xmlns:role/@type = 'publisher'][1]/"\
80
+ "organization")) or return
81
+ n = a.at(ns("./subdivision")) and set(:subdivision, n.text)
82
+ n = a.at(ns("./address/formattedAddress")) and
83
+ set(:pub_address, n.children.to_xml)
84
+ n = a.at(ns("./phone[not(@type = 'fax')]")) and set(:pub_phone, n.text)
85
+ n = a.at(ns("./phone[@type = 'fax']")) and set(:pub_fax, n.text)
86
+ n = a.at(ns("./email")) and set(:pub_email, n.text)
87
+ n = a.at(ns("./uri")) and set(:pub_uri, n.text)
88
+ end
89
+ end
90
+ end
@@ -1,5 +1,10 @@
1
1
  module IsoDoc
2
2
  class Metadata
3
+ DATETYPES = %w{published accessed created implemented obsoleted confirmed
4
+ updated issued received transmitted copied unchanged
5
+ circulated vote-started
6
+ vote-ended}.freeze
7
+
3
8
  def months
4
9
  {
5
10
  "01": @labels["month_january"],
@@ -35,5 +40,11 @@ module IsoDoc
35
40
  Date.parse(isodate).strftime("%B %d, %Y")
36
41
  end
37
42
  end
43
+
44
+ def bibdate(isoxml, _out)
45
+ isoxml.xpath(ns('//bibdata/date')).each do |d|
46
+ set("#{d['type'].gsub(/-/, '_')}date".to_sym, Common::date_range(d))
47
+ end
48
+ end
38
49
  end
39
50
  end
@@ -0,0 +1,96 @@
1
+ module IsoDoc
2
+ class PresentationXMLConvert < ::IsoDoc::Convert
3
+ def bibdata(docxml)
4
+ a = bibdata_current(docxml) or return
5
+ bibdata_i18n(a)
6
+ a.next =
7
+ "<localized-strings>#{i8n_name(trim_hash(@i18n.get), "").join("")}"\
8
+ "</localized-strings>"
9
+ end
10
+
11
+ def bibdata_current(docxml)
12
+ a = docxml.at(ns("//bibdata")) or return
13
+ a.xpath(ns("./language")).each do |l|
14
+ l.text == @lang and l["current"] = "true"
15
+ end
16
+ a.xpath(ns("./script")).each do |l|
17
+ l.text == @script and l["current"] = "true"
18
+ end
19
+ a
20
+ end
21
+
22
+ def bibdata_i18n(b)
23
+ hash_translate(b, @i18n.get["doctype_dict"], "./ext/doctype")
24
+ hash_translate(b, @i18n.get["stage_dict"], "./status/stage")
25
+ hash_translate(b, @i18n.get["substage_dict"], "./status/substage")
26
+ end
27
+
28
+ def hash_translate(bibdata, hash, xpath)
29
+ x = bibdata.at(ns(xpath)) or return
30
+ x["language"] = ""
31
+ hash.is_a? Hash or return
32
+ hash[x.text] or return
33
+ x.next = x.dup
34
+ x.next["language"] = @lang
35
+ x.next.children = hash[x.text]
36
+ end
37
+
38
+ def i18n_tag(k, v)
39
+ "<localized-string key='#{k}' language='#{@lang}'>#{v}</localized-string>"
40
+ end
41
+
42
+ def i18n_safe(k)
43
+ k.gsub(/\s|\./, "_")
44
+ end
45
+
46
+ def i8n_name(h, pref)
47
+ if h.is_a? Hash then i8n_name1(h, pref)
48
+ elsif h.is_a? Array
49
+ h.reject { |a| blank?(a) }.each_with_object([]).
50
+ with_index do |(v1, g), i|
51
+ i8n_name(v1, "#{i18n_safe(k)}.#{i}").each { |x| g << x }
52
+ end
53
+ else [i18n_tag(pref, h)]
54
+ end
55
+ end
56
+
57
+ def i8n_name1(h, pref)
58
+ h.reject { |k, v| blank?(v) }.each_with_object([]) do |(k, v), g|
59
+ if v.is_a? Hash then i8n_name(v, i18n_safe(k)).each { |x| g << x }
60
+ elsif v.is_a? Array
61
+ v.reject { |a| blank?(a) }.each_with_index do |v1, i|
62
+ i8n_name(v1, "#{i18n_safe(k)}.#{i}").each { |x| g << x }
63
+ end
64
+ else
65
+ g << i18n_tag("#{pref}#{pref.empty? ? "" : "."}#{i18n_safe(k)}", v)
66
+ end
67
+ end
68
+ end
69
+
70
+ #https://stackoverflow.com/a/31822406
71
+ def blank?(v)
72
+ v.nil? || v.respond_to?(:empty?) && v.empty?
73
+ end
74
+
75
+ def trim_hash(h)
76
+ loop do
77
+ h_new = trim_hash1(h)
78
+ break h if h==h_new
79
+ h = h_new
80
+ end
81
+ end
82
+
83
+ def trim_hash1(h)
84
+ return h unless h.is_a? Hash
85
+ h.each_with_object({}) do |(k,v), g|
86
+ next if blank?(v)
87
+ g[k] = if v.is_a? Hash then trim_hash1(h[k])
88
+ elsif v.is_a? Array
89
+ h[k].map { |a| trim_hash1(a) }.reject { |a| blank?(a) }
90
+ else
91
+ v
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -134,5 +134,19 @@ module IsoDoc
134
134
  n = @xrefs.anchor(f['id'], :label, false)
135
135
  prefix_name(f, "&nbsp;&mdash; ", l10n("#{@i18n.table} #{n}"), "name")
136
136
  end
137
+
138
+ # we use this to eliminate the semantic amend blocks from rendering
139
+ def amend(docxml)
140
+ docxml.xpath(ns("//amend")).each do |f|
141
+ amend1(f)
142
+ end
143
+ end
144
+
145
+ def amend1(f)
146
+ f.xpath(ns("./autonumber")).each { |a| a.remove }
147
+ f.xpath(ns("./newcontent")).each { |a| a.name = "quote" }
148
+ f.xpath(ns("./description")).each { |a| a.replace(a.children) }
149
+ f.replace(f.children)
150
+ end
137
151
  end
138
152
  end
@@ -7,17 +7,25 @@ module IsoDoc
7
7
  def anchor_linkend(node, linkend)
8
8
  if node["citeas"].nil? && node["bibitemid"]
9
9
  return @xrefs.anchor(node["bibitemid"] ,:xref) || "???"
10
+ elsif node["target"] && node["droploc"]
11
+ return @xrefs.anchor(node["target"], :value) ||
12
+ @xrefs.anchor(node["target"], :label) ||
13
+ @xrefs.anchor(node["target"], :xref) || "???"
10
14
  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)
15
+ linkend = anchor_linkend1(node)
17
16
  end
18
17
  linkend || "???"
19
18
  end
20
19
 
20
+ def anchor_linkend1(node)
21
+ linkend = @xrefs.anchor(node["target"], :xref)
22
+ container = @xrefs.anchor(node["target"], :container, false)
23
+ (container && get_note_container_id(node) != container &&
24
+ @xrefs.get[node["target"]]) &&
25
+ linkend = prefix_container(container, linkend, node["target"])
26
+ capitalise_xref(node, linkend)
27
+ end
28
+
21
29
  def capitalise_xref(node, linkend)
22
30
  return linkend unless %w(Latn Cyrl Grek).include? @script
23
31
  return linkend&.capitalize if node["case"] == "capital"
@@ -59,14 +67,19 @@ module IsoDoc
59
67
  refs.each_with_index do |r, i|
60
68
  delim = ","
61
69
  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)
70
+ ret = eref_locality_stack(r, i, target, delim, ret)
71
+ end
72
+ ret
73
+ end
74
+
75
+ def eref_locality_stack(r, i, target, delim, ret)
76
+ if r.name == "localityStack"
77
+ r.elements.each_with_index do |rr, j|
78
+ ret += eref_localities0(rr, j, target, delim)
79
+ delim = ","
69
80
  end
81
+ else
82
+ ret += eref_localities0(r, i, target, delim)
70
83
  end
71
84
  ret
72
85
  end
@@ -79,7 +92,7 @@ module IsoDoc
79
92
  end
80
93
  end
81
94
 
82
- # TODO: move to localization file
95
+ # TODO: move to localization file
83
96
  def eref_localities1_zh(target, type, from, to, delim)
84
97
  ret = "#{delim} 第#{from.text}" if from
85
98
  ret += "&ndash;#{to.text}" if to
@@ -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
@@ -1,3 +1,3 @@
1
1
  module IsoDoc
2
- VERSION = "1.2.2".freeze
2
+ VERSION = "1.2.7".freeze
3
3
  end
@@ -27,6 +27,7 @@ module IsoDoc::WordFunction
27
27
  def make_body2(body, docxml)
28
28
  body.div **{ class: "WordSection2" } do |div2|
29
29
  boilerplate docxml, div2
30
+ preface_block docxml, div2
30
31
  abstract docxml, div2
31
32
  foreword docxml, div2
32
33
  introduction docxml, div2
@@ -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)