isodoc 2.0.2 → 2.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/lib/isodoc/convert.rb +36 -18
  3. data/lib/isodoc/function/inline.rb +24 -13
  4. data/lib/isodoc/function/references.rb +18 -6
  5. data/lib/isodoc/html_function/html.rb +6 -0
  6. data/lib/isodoc/i18n.rb +20 -7
  7. data/lib/isodoc/metadata.rb +1 -1
  8. data/lib/isodoc/metadata_contributor.rb +1 -1
  9. data/lib/isodoc/presentation_function/bibdata.rb +26 -9
  10. data/lib/isodoc/presentation_function/block.rb +24 -0
  11. data/lib/isodoc/presentation_function/image.rb +3 -4
  12. data/lib/isodoc/presentation_function/inline.rb +118 -76
  13. data/lib/isodoc/presentation_function/math.rb +1 -0
  14. data/lib/isodoc/presentation_function/xrefs.rb +100 -0
  15. data/lib/isodoc/presentation_xml_convert.rb +1 -0
  16. data/lib/isodoc/version.rb +1 -1
  17. data/lib/isodoc/word_function/body.rb +11 -1
  18. data/lib/isodoc/word_function/postprocess.rb +4 -6
  19. data/lib/isodoc/word_function/postprocess_cover.rb +82 -1
  20. data/lib/isodoc/xref/xref_anchor.rb +1 -0
  21. data/lib/isodoc/xref/xref_gen.rb +2 -1
  22. data/lib/isodoc/xref/xref_sect_gen.rb +14 -8
  23. data/lib/isodoc/xslfo_convert.rb +19 -19
  24. data/lib/isodoc-yaml/i18n-ar.yaml +11 -0
  25. data/lib/isodoc-yaml/i18n-de.yaml +11 -0
  26. data/lib/isodoc-yaml/i18n-en.yaml +11 -0
  27. data/lib/isodoc-yaml/i18n-es.yaml +11 -0
  28. data/lib/isodoc-yaml/i18n-fr.yaml +11 -0
  29. data/lib/isodoc-yaml/i18n-ru.yaml +11 -0
  30. data/lib/isodoc-yaml/i18n-zh-Hans.yaml +11 -0
  31. data/spec/isodoc/blocks_spec.rb +13 -77
  32. data/spec/isodoc/inline_spec.rb +565 -22
  33. data/spec/isodoc/metadata_spec.rb +1 -1
  34. data/spec/isodoc/postproc_spec.rb +466 -2
  35. data/spec/isodoc/presentation_xml_spec.rb +380 -78
  36. data/spec/isodoc/ref_spec.rb +96 -11
  37. data/spec/isodoc/section_spec.rb +1 -2
  38. data/spec/isodoc/table_spec.rb +1 -1
  39. data/spec/isodoc/terms_spec.rb +8 -8
  40. data/spec/isodoc/xref_spec.rb +71 -1190
  41. data/spec/isodoc/xslfo_convert_spec.rb +12 -7
  42. metadata +3 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 24d03a8703e3e07a41ac1344228f49be574f4902673eff50ec8423b9f313e02f
4
- data.tar.gz: a1b243c61998b9321e08e95d16f6239a5f14642203e9c2df8c4cec467e8ab10e
3
+ metadata.gz: e1cd1a1265557222ad7684ab50c08fd55d8cc92b46a3997a298f61c0e8b151bc
4
+ data.tar.gz: 394d232d1f1b9e9b4d138df36b555b090b923c96a3f848e7a88cd9d72a3e173a
5
5
  SHA512:
6
- metadata.gz: 51994dfea115e9442ce50fc4ad92f4c478708df0640946c9fdc08965f943cdcf374bc3173ad80abc522af5c45d6224e638411c6c250277b9b11f3037c9b4f548
7
- data.tar.gz: 1cb56a7760c7bec32a1f1fc144bb74c5d85eed4eddd4b856f0946e560423bece052efe4b7b2fe111d7a50838dbe4110f51fe9fff3577dbc866a0a85dfc6c477b
6
+ metadata.gz: 38809fca460021e23cdebe0beb21ba510e59120edba915a760ccbe5248cb73381b0694c0842cfd8bedc00f7d45298765281a36a84ad6bd4283e608f80424f67d
7
+ data.tar.gz: d2ea4b7bb2b0f461ff511da937a9e1ee359321bb228eba89c9ad2808b1e39c92b39af45100503be594c94f74fea0d0571641314f22e0cef645e91c7555e014b5
@@ -40,6 +40,9 @@ module IsoDoc
40
40
  # every 40-odd chars
41
41
  # sectionsplit: split up HTML output on sections
42
42
  # bare: do not insert any prefatory material (coverpage, boilerplate)
43
+ # tocfigures: add ToC for figures
44
+ # toctables: add ToC for tables
45
+ # tocrecommendations: add ToC for rcommendations
43
46
  def initialize(options)
44
47
  @libdir ||= File.dirname(__FILE__) # rubocop:disable Lint/DisjunctiveAssignmentInConstructor
45
48
  options.merge!(default_fonts(options)) do |_, old, new|
@@ -50,25 +53,14 @@ module IsoDoc
50
53
  @options = options
51
54
  @files_to_delete = []
52
55
  @tempfile_cache = []
53
- @htmlstylesheet_name = options[:htmlstylesheet]
54
- @wordstylesheet_name = options[:wordstylesheet]
55
- @htmlstylesheet_override_name = options[:htmlstylesheet_override]
56
- @wordstylesheet_override_name = options[:wordstylesheet_override]
57
- @standardstylesheet_name = options[:standardstylesheet]
58
56
  @sourcefilename = options[:sourcefilename]
59
- @header = options[:header]
60
- @htmlcoverpage = options[:htmlcoverpage]
61
- @wordcoverpage = options[:wordcoverpage]
62
- @htmlintropage = options[:htmlintropage]
63
- @wordintropage = options[:wordintropage]
57
+ init_stylesheets(options)
58
+ init_covers(options)
59
+ init_toc(options)
64
60
  @normalfontsize = options[:normalfontsize]
65
61
  @smallerfontsize = options[:smallerfontsize]
66
62
  @monospacefontsize = options[:monospacefontsize]
67
63
  @footnotefontsize = options[:footnotefontsize]
68
- @scripts = options[:scripts] ||
69
- File.join(File.dirname(__FILE__), "base_style", "scripts.html")
70
- @scripts_pdf = options[:scripts_pdf]
71
- @scripts_override = options[:scripts_override]
72
64
  @i18nyaml = options[:i18nyaml]
73
65
  @ulstyle = options[:ulstyle]
74
66
  @olstyle = options[:olstyle]
@@ -96,10 +88,6 @@ module IsoDoc
96
88
  @script = options[:script] || "Latn"
97
89
  @maxwidth = 1200
98
90
  @maxheight = 800
99
- @wordToClevels = options[:doctoclevels].to_i
100
- @wordToClevels = 2 if @wordToClevels.zero?
101
- @htmlToClevels = options[:htmltoclevels].to_i
102
- @htmlToClevels = 2 if @htmlToClevels.zero?
103
91
  @bookmarks_allocated = { "X" => true }
104
92
  @fn_bookmarks = {}
105
93
  @baseassetpath = options[:baseassetpath]
@@ -108,6 +96,36 @@ module IsoDoc
108
96
  @tmpfilesdir_suffix = tmpfilesdir_suffix
109
97
  end
110
98
 
99
+ def init_covers(options)
100
+ @header = options[:header]
101
+ @htmlcoverpage = options[:htmlcoverpage]
102
+ @wordcoverpage = options[:wordcoverpage]
103
+ @htmlintropage = options[:htmlintropage]
104
+ @wordintropage = options[:wordintropage]
105
+ @scripts = options[:scripts] ||
106
+ File.join(File.dirname(__FILE__), "base_style", "scripts.html")
107
+ @scripts_pdf = options[:scripts_pdf]
108
+ @scripts_override = options[:scripts_override]
109
+ end
110
+
111
+ def init_stylesheets(options)
112
+ @htmlstylesheet_name = options[:htmlstylesheet]
113
+ @wordstylesheet_name = options[:wordstylesheet]
114
+ @htmlstylesheet_override_name = options[:htmlstylesheet_override]
115
+ @wordstylesheet_override_name = options[:wordstylesheet_override]
116
+ @standardstylesheet_name = options[:standardstylesheet]
117
+ end
118
+
119
+ def init_toc(options)
120
+ @wordToClevels = options[:doctoclevels].to_i
121
+ @wordToClevels = 2 if @wordToClevels.zero?
122
+ @htmlToClevels = options[:htmltoclevels].to_i
123
+ @htmlToClevels = 2 if @htmlToClevels.zero?
124
+ @tocfigures = options[:tocfigures]
125
+ @toctables = options[:toctables]
126
+ @tocrecommendations = options[:tocrecommendations]
127
+ end
128
+
111
129
  def tmpimagedir_suffix
112
130
  "_#{SecureRandom.hex(8)}_images"
113
131
  end
@@ -34,30 +34,41 @@ module IsoDoc
34
34
  end
35
35
 
36
36
  def suffix_url(url)
37
- return url if %r{^https?://}.match?(url)
37
+ return url if url.nil? || %r{^https?://|^#}.match?(url)
38
38
  return url unless File.extname(url).empty?
39
39
 
40
40
  url.sub(/#{File.extname(url)}$/, ".html")
41
41
  end
42
42
 
43
43
  def eref_target(node)
44
- return "##{node['bibitemid']}" unless (!@bibitems.nil? &&
45
- url = @bibitems[node["bibitemid"]]&.at(ns("./uri[@type = 'citation']")))
44
+ url = suffix_url(eref_url(node["bibitemid"]))
45
+ anchor = node&.at(ns(".//locality[@type = 'anchor']"))
46
+ return url if url.nil? || /^#/.match?(url) || !anchor
46
47
 
47
- href = suffix_url(url.text)
48
- anchor = node&.at(ns(".//locality[@type = 'anchor']"))&.text&.strip
49
- anchor and href += "##{anchor}"
50
- href
48
+ "#{url}##{anchor.text.strip}"
49
+ end
50
+
51
+ def eref_url(bibitemid)
52
+ return nil if @bibitems.nil? || @bibitems[bibitemid].nil?
53
+
54
+ if url = @bibitems[bibitemid].at(ns("./uri[@type = 'citation']"))
55
+ url.text
56
+ elsif @bibitems[bibitemid]["hidden"] == "true"
57
+ @bibitems[bibitemid]&.at(ns("./uri"))&.text
58
+ else "##{bibitemid}"
59
+ end
51
60
  end
52
61
 
53
62
  def eref_parse(node, out)
54
- href = eref_target(node)
55
- if node["type"] == "footnote"
56
- out.sup do |s|
57
- s.a(**{ href: href }) { |l| no_locality_parse(node, l) }
63
+ if href = eref_target(node)
64
+ if node["type"] == "footnote"
65
+ out.sup do |s|
66
+ s.a(**{ href: href }) { |l| no_locality_parse(node, l) }
67
+ end
68
+ else
69
+ out.a(**{ href: href }) { |l| no_locality_parse(node, l) }
58
70
  end
59
- else
60
- out.a(**{ href: href }) { |l| no_locality_parse(node, l) }
71
+ else no_locality_parse(node, out)
61
72
  end
62
73
  end
63
74
 
@@ -49,10 +49,14 @@ module IsoDoc
49
49
  end
50
50
 
51
51
  def pref_ref_code(bib)
52
- bib.at(ns("./docidentifier[not(@type = 'DOI' or @type = 'metanorma' "\
53
- "or @type = 'metanorma-ordinal' or "\
54
- "@type = 'ISSN' or @type = 'ISBN' or "\
55
- "@type = 'rfc-anchor')]"))
52
+ ret = bib.xpath(ns("./docidentifier[@primary = 'true']"))
53
+ ret.empty? and
54
+ ret = bib.at(ns("./docidentifier[not(@type = 'DOI' or "\
55
+ "@type = 'metanorma' "\
56
+ "or @type = 'metanorma-ordinal' or "\
57
+ "@type = 'ISSN' or @type = 'ISBN' or "\
58
+ "@type = 'rfc-anchor')]"))
59
+ ret
56
60
  end
57
61
 
58
62
  # returns [metanorma, non-metanorma, DOI/ISSN/ISBN] identifiers
@@ -78,10 +82,18 @@ module IsoDoc
78
82
  num
79
83
  end
80
84
 
81
- def unbracket(ident)
85
+ def unbracket1(ident)
82
86
  ident&.text&.sub(/^\[/, "")&.sub(/\]$/, "")
83
87
  end
84
88
 
89
+ def unbracket(ident)
90
+ if ident.respond_to?(:size)
91
+ ident.map { |x| unbracket1(x) }.join(" / ")
92
+ else
93
+ unbracket1(ident)
94
+ end
95
+ end
96
+
85
97
  def render_identifier(ident)
86
98
  { metanorma: bracket_if_num(ident[0]),
87
99
  sdo: unbracket(ident[1]),
@@ -106,7 +118,7 @@ module IsoDoc
106
118
  date_note = bib.at(ns("./note[@type = 'Unpublished-Status']"))
107
119
  return if date_note.nil?
108
120
 
109
- date_note.children.first.replace("<p>#{date_note.content}</p>")
121
+ date_note.children = "<p>#{date_note.content}</p>"
110
122
  footnote_parse(date_note, ref)
111
123
  end
112
124
 
@@ -128,6 +128,12 @@ module IsoDoc
128
128
 
129
129
  def table_long_strings_cleanup(docxml); end
130
130
 
131
+ def table_attrs(node)
132
+ ret = super
133
+ node.at(ns("./colgroup")) and ret[:style] += "table-layout:fixed;"
134
+ ret
135
+ end
136
+
131
137
  def image_parse(node, out, caption)
132
138
  if svg = node.at("./m:svg", "m" => "http://www.w3.org/2000/svg")
133
139
  svg_parse(svg, out)
data/lib/isodoc/i18n.rb CHANGED
@@ -104,15 +104,28 @@ module IsoDoc
104
104
  xml.to_xml.gsub(/<b>/, "").gsub("</b>", "").gsub(/<\?[^>]+>/, "")
105
105
  end
106
106
 
107
- def multiple_and(names, andword)
108
- return "" if names.empty?
109
- return names[0] if names.length == 1
110
-
111
- (names.length == 2) &&
112
- (return l10n("#{names[0]} #{andword} #{names[1]}", @lang, @script))
113
- l10n(names[0..-2].join(", ") + " #{andword} #{names[-1]}", @lang, @script)
107
+ def boolean_conj(list, conn)
108
+ case list.size
109
+ when 0 then ""
110
+ when 1 then list.first
111
+ when 2 then @labels["binary_#{conn}"].sub(/%1/, list[0])
112
+ .sub(/%2/, list[1])
113
+ else
114
+ @labels["multiple_#{conn}"]
115
+ .sub(/%1/, l10n(list[0..-2].join(", "), @lang, @script))
116
+ .sub(/%2/, list[-1])
117
+ end
114
118
  end
115
119
 
120
+ # def multiple_and(names, andword)
121
+ # return "" if names.empty?
122
+ # return names[0] if names.length == 1
123
+ #
124
+ # (names.length == 2) &&
125
+ # (return l10n("#{names[0]} #{andword} #{names[1]}", @lang, @script))
126
+ # l10n(names[0..-2].join(", ") + " #{andword} #{names[-1]}", @lang, @script)
127
+ # end
128
+
116
129
  include Function::Utils
117
130
  # module_function :l10n
118
131
  end
@@ -69,7 +69,7 @@ module IsoDoc
69
69
  end
70
70
 
71
71
  def stage_abbr(docstatus)
72
- status_print(docstatus).split(/ /).map { |s| s[0].upcase }.join("")
72
+ status_print(docstatus).split(/ /).map { |s| s[0].upcase }.join
73
73
  end
74
74
 
75
75
  def unpublished(status)
@@ -75,7 +75,7 @@ module IsoDoc
75
75
  def agency(xml)
76
76
  agency, publisher = agency1(xml)
77
77
  set(:agency, agency.sub(%r{/$}, ""))
78
- set(:publisher, @i18n.multiple_and(publisher, @labels["and"]))
78
+ set(:publisher, @i18n.boolean_conj(publisher, "and"))
79
79
  agency_addr(xml)
80
80
  end
81
81
 
@@ -1,15 +1,30 @@
1
1
  module IsoDoc
2
2
  class PresentationXMLConvert < ::IsoDoc::Convert
3
3
  def bibdata(docxml)
4
+ toc_metadata(docxml)
4
5
  docid_prefixes(docxml)
5
6
  a = bibdata_current(docxml) or return
6
7
  address_precompose(a)
7
8
  bibdata_i18n(a)
8
9
  a.next =
9
- "<localized-strings>#{i8n_name(trim_hash(@i18n.get), '').join('')}"\
10
+ "<localized-strings>#{i8n_name(trim_hash(@i18n.get), '').join}"\
10
11
  "</localized-strings>"
11
12
  end
12
13
 
14
+ def toc_metadata(docxml)
15
+ return unless @tocfigures || @toctables || @tocrecommendations
16
+
17
+ ins = docxml.at(ns("//misc-container")) ||
18
+ docxml.at(ns("//bibdata")).after("<misc-container/>").next_element
19
+ @tocfigures and
20
+ ins << "<toc type='figure'><title>#{@i18n.toc_figures}</title></toc>"
21
+ @toctables and
22
+ ins << "<toc type='table'><title>#{@i18n.toc_tables}</title></toc>"
23
+ @tocfigures and
24
+ ins << "<toc type='recommendation'><title>#{@i18n.toc_recommendations}"\
25
+ "</title></toc>"
26
+ end
27
+
13
28
  def address_precompose(bib)
14
29
  bib.xpath(ns("//bibdata//address")).each do |b|
15
30
  next if b.at(ns("./formattedAddress"))
@@ -66,8 +81,9 @@ module IsoDoc
66
81
  end
67
82
 
68
83
  def i8n_name(hash, pref)
69
- if hash.is_a? Hash then i8n_name1(hash, pref)
70
- elsif hash.is_a? Array
84
+ case hash
85
+ when Hash then i8n_name1(hash, pref)
86
+ when Array
71
87
  hash.reject { |a| blank?(a) }.each_with_object([])
72
88
  .with_index do |(v1, g), i|
73
89
  i8n_name(v1, "#{i18n_safe(k)}.#{i}").each { |x| g << x }
@@ -78,8 +94,9 @@ module IsoDoc
78
94
 
79
95
  def i8n_name1(hash, pref)
80
96
  hash.reject { |_k, v| blank?(v) }.each_with_object([]) do |(k, v), g|
81
- if v.is_a? Hash then i8n_name(v, i18n_safe(k)).each { |x| g << x }
82
- elsif v.is_a? Array
97
+ case v
98
+ when Hash then i8n_name(v, i18n_safe(k)).each { |x| g << x }
99
+ when Array
83
100
  v.reject { |a| blank?(a) }.each_with_index do |v1, i|
84
101
  i8n_name(v1, "#{i18n_safe(k)}.#{i}").each { |x| g << x }
85
102
  end
@@ -109,11 +126,11 @@ module IsoDoc
109
126
  hash.each_with_object({}) do |(k, v), g|
110
127
  next if blank?(v)
111
128
 
112
- g[k] = if v.is_a? Hash then trim_hash1(hash[k])
113
- elsif v.is_a? Array
129
+ g[k] = case v
130
+ when Hash then trim_hash1(hash[k])
131
+ when Array
114
132
  hash[k].map { |a| trim_hash1(a) }.reject { |a| blank?(a) }
115
- else
116
- v
133
+ else v
117
134
  end
118
135
  end
119
136
  end
@@ -135,5 +135,29 @@ module IsoDoc
135
135
  elem.xpath(ns("./description")).each { |a| a.replace(a.children) }
136
136
  elem.replace(elem.children)
137
137
  end
138
+
139
+ def ol(docxml)
140
+ docxml.xpath(ns("//ol")).each do |f|
141
+ ol1(f)
142
+ end
143
+ @xrefs.list_anchor_names(docxml.xpath(ns(@xrefs.sections_xpath)))
144
+ end
145
+
146
+ # We don't really want users to specify type of ordered list;
147
+ # we will use by default a fixed hierarchy as practiced by ISO (though not
148
+ # fully spelled out): a) 1) i) A) I)
149
+ def ol_depth(node)
150
+ depth = node.ancestors("ul, ol").size + 1
151
+ type = :alphabet
152
+ type = :arabic if [2, 7].include? depth
153
+ type = :roman if [3, 8].include? depth
154
+ type = :alphabet_upper if [4, 9].include? depth
155
+ type = :roman_upper if [5, 10].include? depth
156
+ type
157
+ end
158
+
159
+ def ol1(elem)
160
+ elem["type"] ||= ol_depth(elem).to_s
161
+ end
138
162
  end
139
163
  end
@@ -65,15 +65,14 @@ module IsoDoc
65
65
  uri = svg_to_emf_uri(node)
66
66
  ret = svg_to_emf_filename(uri)
67
67
  File.exists?(ret) and return ret
68
- exe = inkscape_installed? or return nil
68
+ exe = inkscape_installed? or raise "Inkscape missing in PATH, unable" \
69
+ "to convert EMF to SVG. Aborting."
69
70
  uri = Metanorma::Utils::external_path uri
70
71
  exe = Metanorma::Utils::external_path exe
71
72
  system(%(#{exe} --export-type="emf" #{uri})) and
72
73
  return Metanorma::Utils::datauri(ret)
73
74
 
74
- warn %(Fail on #{exe} --export-type="emf" #{uri})
75
-
76
- nil
75
+ raise %(Fail on #{exe} --export-type="emf" #{uri})
77
76
  end
78
77
 
79
78
  def svg_to_emf_uri(node)
@@ -1,56 +1,8 @@
1
1
  require "metanorma-utils"
2
+ require_relative "xrefs"
2
3
 
3
4
  module IsoDoc
4
5
  class PresentationXMLConvert < ::IsoDoc::Convert
5
- def prefix_container(container, linkend, _target)
6
- l10n("#{@xrefs.anchor(container, :xref)}, #{linkend}")
7
- end
8
-
9
- def anchor_value(id)
10
- @xrefs.anchor(id, :value) || @xrefs.anchor(id, :label) ||
11
- @xrefs.anchor(id, :xref)
12
- end
13
-
14
- def anchor_linkend(node, linkend)
15
- if node["citeas"].nil? && node["bibitemid"]
16
- return @xrefs.anchor(node["bibitemid"], :xref) || "???"
17
- elsif node["target"] && node["droploc"]
18
- return anchor_value(node["target"]) || "???"
19
- elsif node["target"] && !/.#./.match(node["target"])
20
- linkend = anchor_linkend1(node)
21
- end
22
-
23
- linkend || "???"
24
- end
25
-
26
- def anchor_linkend1(node)
27
- linkend = @xrefs.anchor(node["target"], :xref)
28
- container = @xrefs.anchor(node["target"], :container, false)
29
- (container && get_note_container_id(node) != container &&
30
- @xrefs.get[node["target"]]) and
31
- linkend = prefix_container(container, linkend, node["target"])
32
- capitalise_xref(node, linkend, anchor_value(node["target"]))
33
- end
34
-
35
- def capitalise_xref(node, linkend, label)
36
- linktext = linkend.gsub(/<[^>]+>/, "")
37
- (label && !label.empty? && /^#{Regexp.escape(label)}/.match?(linktext)) ||
38
- linktext[0, 1].match?(/\p{Upper}/) and return linkend
39
- node["case"] and
40
- return Common::case_with_markup(linkend, node["case"], @script)
41
-
42
- capitalise_xref1(node, linkend)
43
- end
44
-
45
- def capitalise_xref1(node, linkend)
46
- prec = nearest_block_parent(node).xpath("./descendant-or-self::text()") &
47
- node.xpath("./preceding::text()")
48
- if prec.empty? || /(?!<[^.].)\.\s+$/.match(prec.map(&:text).join)
49
- Common::case_with_markup(linkend, "capital", @script)
50
- else linkend
51
- end
52
- end
53
-
54
6
  def nearest_block_parent(node)
55
7
  until %w(p title td th name formula li dt dd sourcecode pre)
56
8
  .include?(node.name)
@@ -61,7 +13,7 @@ module IsoDoc
61
13
 
62
14
  def non_locality_elems(node)
63
15
  node.children.reject do |c|
64
- %w{locality localityStack}.include? c.name
16
+ %w{locality localityStack location}.include? c.name
65
17
  end
66
18
  end
67
19
 
@@ -69,7 +21,8 @@ module IsoDoc
69
21
  c1 = non_locality_elems(node).select { |c| !c.text? || /\S/.match(c) }
70
22
  return unless c1.empty?
71
23
 
72
- link = anchor_linkend(node, docid_l10n(node["target"] || node["citeas"]))
24
+ link = anchor_linkend(node, docid_l10n(node["target"] ||
25
+ expand_citeas(node["citeas"])))
73
26
  link += eref_localities(node.xpath(ns("./locality | ./localityStack")),
74
27
  link, node)
75
28
  non_locality_elems(node).each(&:remove)
@@ -78,54 +31,143 @@ module IsoDoc
78
31
  # so not <origin bibitemid="ISO7301" citeas="ISO 7301">
79
32
  # <locality type="section"><reference>3.1</reference></locality></origin>
80
33
 
34
+ def expand_citeas(text)
35
+ text.nil? and return text
36
+ HTMLEntities.new.decode(text.gsub(/&amp;#x/, "&#"))
37
+ end
38
+
81
39
  def eref_localities(refs, target, node)
82
- ret = ""
83
- refs.each_with_index do |r, i|
84
- delim = ","
85
- delim = ";" if r.name == "localityStack" && i.positive?
86
- ret = eref_locality_stack(r, i, target, delim, ret, node)
40
+ if can_conflate_eref_rendering?(refs)
41
+ l10n(", #{eref_localities_conflated(refs, target, node)}")
42
+ else
43
+ ret = resolve_eref_connectives(eref_locality_stacks(refs, target, node))
44
+ l10n(ret.join)
87
45
  end
88
- ret
89
46
  end
90
47
 
91
- def eref_locality_stack(ref, idx, target, delim, ret, node)
48
+ def eref_localities_conflated(refs, target, node)
49
+ droploc = node["droploc"]
50
+ node["droploc"] = true
51
+ ret = resolve_eref_connectives(eref_locality_stacks(refs, target,
52
+ node))
53
+ node["droploc"] = droploc
54
+ eref_localities1(target,
55
+ refs.first.at(ns("./locality/@type")).text,
56
+ l10n(ret[1..-1].join), nil, node, @lang)
57
+ end
58
+
59
+ def can_conflate_eref_rendering?(refs)
60
+ (refs.size > 1 &&
61
+ refs.all? { |r| r.name == "localityStack" } &&
62
+ refs.all? { |r| r.xpath(ns("./locality")).size == 1 }) or return false
63
+
64
+ first = refs.first.at(ns("./locality/@type")).text
65
+ refs.all? do |r|
66
+ r.at(ns("./locality/@type")).text == first
67
+ end
68
+ end
69
+
70
+ def resolve_eref_connectives(locs)
71
+ locs = resolve_comma_connectives(locs)
72
+ locs = resolve_to_connectives(locs)
73
+ return locs if locs.size < 3
74
+
75
+ locs = locs.each_slice(2).with_object([]) do |a, m|
76
+ m << { conn: a[0], target: a[1] }
77
+ end
78
+ [", ", combine_conn(locs)]
79
+ end
80
+
81
+ def resolve_comma_connectives(locs)
82
+ locs1 = []
83
+ add = ""
84
+ until locs.empty?
85
+ if [", ", " "].include?(locs[1])
86
+ add += locs[0..2].join
87
+ locs.shift(3)
88
+ else
89
+ locs1 << add unless add.empty?
90
+ add = ""
91
+ locs1 << locs.shift
92
+ end
93
+ end
94
+ locs1 << add unless add.empty?
95
+ locs1
96
+ end
97
+
98
+ def resolve_to_connectives(locs)
99
+ locs1 = []
100
+ until locs.empty?
101
+ if locs[1] == "to"
102
+ locs1 << @i18n.chain_to.sub(/%1/, locs[0]).sub(/%2/, locs[2])
103
+ locs.shift(3)
104
+ else locs1 << locs.shift
105
+ end
106
+ end
107
+ locs1
108
+ end
109
+
110
+ def eref_locality_stacks(refs, target, node)
111
+ ret = refs.each_with_index.with_object([]) do |(r, i), m|
112
+ added = eref_locality_stack(r, i, target, node)
113
+ added.empty? and next
114
+ added.each { |a| m << a }
115
+ next if i == refs.size - 1
116
+
117
+ m << if r&.next_element&.name == "localityStack"
118
+ r.next_element["connective"]
119
+ else locality_delimiter(r)
120
+ end
121
+ end
122
+ ret.empty? ? ret : [", "] + ret
123
+ end
124
+
125
+ def eref_locality_stack(ref, idx, target, node)
126
+ ret = []
92
127
  if ref.name == "localityStack"
93
128
  ref.elements.each_with_index do |rr, j|
94
- ret += eref_localities0(rr, j, target, delim, node)
95
- delim = ","
129
+ l = eref_localities0(rr, j, target, node) or next
130
+
131
+ ret << l
132
+ ret << locality_delimiter(rr) unless j == ref.elements.size - 1
96
133
  end
97
- else ret += eref_localities0(ref, idx, target, delim, node)
134
+ else
135
+ l = eref_localities0(ref, idx, target, node) and ret << l
98
136
  end
99
137
  ret
100
138
  end
101
139
 
102
- def eref_localities0(ref, _idx, target, delim, node)
103
- if ref["type"] == "whole" then l10n("#{delim} #{@i18n.wholeoftext}")
140
+ def locality_delimiter(_loc)
141
+ ", "
142
+ end
143
+
144
+ def eref_localities0(ref, _idx, target, node)
145
+ if ref["type"] == "whole" then @i18n.wholeoftext
104
146
  else
105
- eref_localities1(target, ref["type"], ref.at(ns("./referenceFrom")),
106
- ref.at(ns("./referenceTo")), delim, node, @lang)
147
+ eref_localities1(target, ref["type"],
148
+ ref&.at(ns("./referenceFrom"))&.text,
149
+ ref&.at(ns("./referenceTo"))&.text, node, @lang)
107
150
  end
108
151
  end
109
152
 
110
153
  # TODO: move to localization file
111
- def eref_localities1_zh(_target, type, from, upto, node, delim)
112
- ret = "#{delim} 第#{from.text}" if from
113
- ret += "&ndash;#{upto.text}" if upto
114
- loc = (@i18n.locality[type] || type.sub(/^locality:/, "").capitalize)
154
+ def eref_localities1_zh(_target, type, from, upto, node)
155
+ ret = "第#{from}" if from
156
+ ret += "&ndash;#{upto}" if upto
157
+ loc = eref_locality_populate(type, node)
115
158
  ret += " #{loc}" unless node["droploc"] == "true"
116
159
  ret
117
160
  end
118
161
 
119
162
  # TODO: move to localization file
120
- def eref_localities1(target, type, from, upto, delim, node, lang = "en")
121
- return "" if type == "anchor"
163
+ def eref_localities1(target, type, from, upto, node, lang = "en")
164
+ return nil if type == "anchor"
122
165
 
123
166
  lang == "zh" and
124
- return l10n(eref_localities1_zh(target, type, from, upto, node, delim))
125
- ret = delim
126
- ret += eref_locality_populate(type, node)
127
- ret += " #{from.text}" if from
128
- ret += "&ndash;#{upto.text}" if upto
167
+ return l10n(eref_localities1_zh(target, type, from, upto, node))
168
+ ret = eref_locality_populate(type, node)
169
+ ret += " #{from}" if from
170
+ ret += "&ndash;#{upto}" if upto
129
171
  l10n(ret)
130
172
  end
131
173
 
@@ -20,6 +20,7 @@ module IsoDoc
20
20
  num = BigDecimal(x.text)
21
21
  precision = /\./.match?(x.text) ? x.text.sub(/^.*\./, "").size : 0
22
22
  x.children = localized_number(num, locale, precision)
23
+ rescue ArgumentError => e
23
24
  end
24
25
  end
25
26