isodoc 1.2.2 → 1.2.7

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