isodoc 2.10.0 → 3.0.0

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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/isodoc.gemspec +9 -4
  4. data/lib/isodoc/base_style/all.css +96 -0
  5. data/lib/isodoc/base_style/reset.css +96 -0
  6. data/lib/isodoc/base_style/reset.scss +75 -0
  7. data/lib/isodoc/base_style/scripts.html +45 -14
  8. data/lib/isodoc/class_utils.rb +4 -2
  9. data/lib/isodoc/convert.rb +0 -21
  10. data/lib/isodoc/css.rb +2 -4
  11. data/lib/isodoc/function/blocks.rb +37 -52
  12. data/lib/isodoc/function/blocks_example_note.rb +75 -29
  13. data/lib/isodoc/function/cleanup.rb +9 -44
  14. data/lib/isodoc/function/inline.rb +67 -13
  15. data/lib/isodoc/function/lists.rb +20 -14
  16. data/lib/isodoc/function/references.rb +9 -126
  17. data/lib/isodoc/function/reqt.rb +4 -3
  18. data/lib/isodoc/function/section.rb +30 -35
  19. data/lib/isodoc/function/section_titles.rb +22 -11
  20. data/lib/isodoc/function/table.rb +3 -3
  21. data/lib/isodoc/function/terms.rb +49 -26
  22. data/lib/isodoc/function/to_word_html.rb +46 -16
  23. data/lib/isodoc/function/utils.rb +11 -12
  24. data/lib/isodoc/gem_tasks.rb +1 -1
  25. data/lib/isodoc/html_function/footnotes.rb +2 -1
  26. data/lib/isodoc/html_function/html.rb +5 -7
  27. data/lib/isodoc/html_function/postprocess.rb +25 -6
  28. data/lib/isodoc/html_function/postprocess_cover.rb +13 -7
  29. data/lib/isodoc/html_function/postprocess_footnotes.rb +52 -47
  30. data/lib/isodoc/init.rb +31 -0
  31. data/lib/isodoc/metadata.rb +20 -13
  32. data/lib/isodoc/metadata_contributor.rb +5 -10
  33. data/lib/isodoc/metadata_date.rb +1 -1
  34. data/lib/isodoc/presentation_function/autonum.rb +153 -0
  35. data/lib/isodoc/presentation_function/bibdata.rb +4 -5
  36. data/lib/isodoc/presentation_function/block.rb +107 -40
  37. data/lib/isodoc/presentation_function/concepts.rb +184 -19
  38. data/lib/isodoc/presentation_function/docid.rb +78 -0
  39. data/lib/isodoc/presentation_function/erefs.rb +55 -31
  40. data/lib/isodoc/presentation_function/image.rb +64 -19
  41. data/lib/isodoc/presentation_function/index.rb +180 -0
  42. data/lib/isodoc/presentation_function/inline.rb +47 -16
  43. data/lib/isodoc/presentation_function/math.rb +128 -71
  44. data/lib/isodoc/presentation_function/metadata.rb +29 -66
  45. data/lib/isodoc/presentation_function/refs.rb +75 -28
  46. data/lib/isodoc/presentation_function/reqt.rb +7 -4
  47. data/lib/isodoc/presentation_function/section.rb +51 -129
  48. data/lib/isodoc/presentation_function/sourcecode.rb +52 -19
  49. data/lib/isodoc/presentation_function/terms.rb +107 -118
  50. data/lib/isodoc/presentation_function/title.rb +107 -0
  51. data/lib/isodoc/presentation_function/xrefs.rb +37 -17
  52. data/lib/isodoc/presentation_xml_convert.rb +24 -10
  53. data/lib/isodoc/version.rb +1 -1
  54. data/lib/isodoc/word_function/body.rb +9 -37
  55. data/lib/isodoc/word_function/comments.rb +0 -1
  56. data/lib/isodoc/word_function/footnotes.rb +1 -1
  57. data/lib/isodoc/word_function/lists.rb +13 -9
  58. data/lib/isodoc/word_function/postprocess.rb +6 -0
  59. data/lib/isodoc/word_function/postprocess_cover.rb +6 -4
  60. data/lib/isodoc/word_function/postprocess_table.rb +1 -1
  61. data/lib/isodoc/word_function/postprocess_toc.rb +10 -6
  62. data/lib/isodoc/word_function/table.rb +2 -2
  63. data/lib/isodoc/xref/clause_order.rb +41 -0
  64. data/lib/isodoc/xref/xref_anchor.rb +31 -16
  65. data/lib/isodoc/xref/xref_counter.rb +18 -3
  66. data/lib/isodoc/xref/xref_counter_types.rb +14 -0
  67. data/lib/isodoc/xref/xref_gen.rb +61 -37
  68. data/lib/isodoc/xref/xref_gen_seq.rb +148 -81
  69. data/lib/isodoc/xref/xref_sect_gen.rb +84 -88
  70. data/lib/isodoc/xref/xref_util.rb +53 -4
  71. data/lib/isodoc/xref.rb +29 -10
  72. data/lib/isodoc/xslfo_convert.rb +14 -9
  73. data/lib/isodoc-yaml/i18n-ar.yaml +17 -13
  74. data/lib/isodoc-yaml/i18n-de.yaml +17 -13
  75. data/lib/isodoc-yaml/i18n-en.yaml +18 -17
  76. data/lib/isodoc-yaml/i18n-es.yaml +17 -13
  77. data/lib/isodoc-yaml/i18n-fr.yaml +17 -13
  78. data/lib/isodoc-yaml/i18n-ja.yaml +56 -52
  79. data/lib/isodoc-yaml/i18n-ru.yaml +17 -16
  80. data/lib/isodoc-yaml/i18n-zh-Hans.yaml +17 -13
  81. data/lib/nokogiri/xml/node.rb +14 -0
  82. data/lib/relaton/render-isodoc/config.yml +1 -1
  83. metadata +90 -13
@@ -13,7 +13,7 @@ module IsoDoc
13
13
  node["style"] == "id" and anchor_id_postprocess(node)
14
14
  xref_empty?(node) or return
15
15
  target = docid_l10n(node["target"]) ||
16
- expand_citeas(docid_l10n(node["citeas"]))
16
+ docid_l10n(expand_citeas(node["citeas"]))
17
17
  link = anchor_linkend(node, target)
18
18
  link += eref_localities(node.xpath(ns("./locality | ./localityStack")),
19
19
  link, node)
@@ -25,8 +25,8 @@ module IsoDoc
25
25
  # <locality type="section"><reference>3.1</reference></locality></origin>
26
26
 
27
27
  def unnest_linkend(node)
28
- node.at(ns("./xref[@nested]")) or return
29
- node.xpath(ns("./xref[@nested]")).each { |x| x.delete("nested") }
28
+ node.at(ns("./fmt-xref[@nested]")) or return
29
+ node.xpath(ns("./fmt-xref[@nested]")).each { |x| x.delete("nested") }
30
30
  node.xpath(ns("./location | ./locationStack")).each(&:remove)
31
31
  node.replace(node.children)
32
32
  end
@@ -39,31 +39,56 @@ module IsoDoc
39
39
  def anchor_id_postprocess(node); end
40
40
 
41
41
  def xref(docxml)
42
- docxml.xpath(ns("//xref")).each { |f| xref1(f) }
43
- docxml.xpath(ns("//xref//xref")).each { |f| f.replace(f.children) }
42
+ #docxml.xpath(ns("//display-text")).each { |f| f.replace(f.children) }
43
+ docxml.xpath(ns("//fmt-xref")).each { |f| xref1(f) }
44
+ docxml.xpath(ns("//fmt-xref//fmt-xref")).each { |f| f.replace(f.children) }
45
+ docxml.xpath(ns("//fmt-xref//xref")).each { |f| f.replace(f.children) }
44
46
  end
45
47
 
46
48
  def eref(docxml)
47
- docxml.xpath(ns("//eref")).each { |f| xref1(f) }
48
- docxml.xpath(ns("//eref//xref")).each { |f| f.replace(f.children) }
49
+ docxml.xpath(ns("//eref[@deleteme]")).each { |f| redundant_eref(f) }
50
+ docxml.xpath(ns("//fmt-eref")).each { |f| xref1(f) }
51
+ docxml.xpath(ns("//fmt-eref//fmt-xref")).each { |f| f.replace(f.children) }
49
52
  docxml.xpath(ns("//erefstack")).each { |f| erefstack1(f) }
50
53
  end
51
54
 
55
+ # redundant eref copied from quote/source
56
+ def redundant_eref(elem)
57
+ if elem.next.name == "semx"
58
+ elem.next.elements.first.delete("deleteme")
59
+ elem.next.replace(elem.next.children)
60
+ end
61
+ elem.remove
62
+ end
63
+
52
64
  def origin(docxml)
53
- docxml.xpath(ns("//origin[not(termref)]")).each { |f| xref1(f) }
65
+ docxml.xpath(ns("//fmt-origin[not(.//termref)]")).each { |f| xref1(f) }
54
66
  end
55
67
 
56
- def quotesource(docxml)
57
- docxml.xpath(ns("//quote/source")).each { |f| xref1(f) }
58
- docxml.xpath(ns("//quote/source//xref")).each do |f|
68
+ # KILL
69
+ def quotesourcex(docxml)
70
+ docxml.xpath(ns("//quote//source")).each { |f| xref1(f) }
71
+ docxml.xpath(ns("//quote//source//xref")).each do |f|
59
72
  f.replace(f.children)
60
73
  end
61
74
  end
62
75
 
76
+ # do not change to Presentation XML rendering
77
+ def sem_xml_descendant?(node)
78
+ !node.ancestors("preferred, admitted, deprecated, related, " \
79
+ "definition, termsource").empty? and return true
80
+ !node.ancestors("xref, eref, origin, link").empty? and return true
81
+ !node.ancestors("requirement, recommendation, permission").empty? &&
82
+ node.ancestors("fmt-provision").empty? and return true
83
+ false
84
+ end
85
+
63
86
  def xref1(node)
87
+ sem_xml_descendant?(node) and return
64
88
  get_linkend(node)
65
89
  end
66
90
 
91
+ # there should be no //variant in bibdata now
67
92
  def variant(xml)
68
93
  b = xml.xpath(ns("//bibdata//variant"))
69
94
  (xml.xpath(ns("//variant")) - b).each { |f| variant1(f) }
@@ -85,7 +110,10 @@ module IsoDoc
85
110
 
86
111
  def identifier(docxml)
87
112
  docxml.xpath(ns("//identifier")).each do |n|
88
- n.name = "tt"
113
+ %w(bibdata bibitem requirement recommendation permission)
114
+ .include?(n.parent.name) and next
115
+ s = semx_fmt_dup(n)
116
+ n.next = "<fmt-identifier><tt>#{to_xml(s)}</tt></fmt-identifier>"
89
117
  end
90
118
  end
91
119
 
@@ -98,7 +126,10 @@ module IsoDoc
98
126
 
99
127
  def date1(elem)
100
128
  elem["value"] && elem["format"] or return
101
- elem.replace(@i18n.date(elem["value"], elem["format"].strip))
129
+ val = @i18n.date(elem["value"], elem["format"].strip)
130
+ d = semx_fmt_dup(elem)
131
+ d << val
132
+ elem.next = "<fmt-date>#{to_xml(d)}</fmt-date>"
102
133
  end
103
134
 
104
135
  def inline_format(docxml)
@@ -160,9 +191,9 @@ module IsoDoc
160
191
  def passthrough1(elem, formats)
161
192
  (elem["formats"] && !elem["formats"].empty?) or elem["formats"] = "all"
162
193
  f = elem["formats"].split(",")
163
- (f - formats).size == f.size or elem["formats"] = "all"
164
- elem["formats"] == "all" and elem["formats"] = formats.join(",")
165
- elem["formats"] = ",#{elem['formats']},"
194
+ (f - formats).size == f.size or f = "all"
195
+ f == ["all"] and f = formats.dup
196
+ elem["formats"] = " #{f.join(' ')} "
166
197
  end
167
198
 
168
199
  def extract_custom_charsets(docxml)
@@ -7,9 +7,13 @@ module IsoDoc
7
7
  MATHML = { "m" => "http://www.w3.org/1998/Math/MathML" }.freeze
8
8
 
9
9
  def mathml(docxml)
10
- docxml.xpath("//m:math", MATHML).each { |f| mathml_linebreak(f) }
11
- locale = twitter_cldr_localiser
10
+ docxml.xpath(ns("//stem")).each { |s| stem_dup(s) }
11
+ locale = @lang.to_sym
12
+ @numfmt = Plurimath::NumberFormatter
13
+ .new(locale, localize_number: @localizenumber,
14
+ localizer_symbols: twitter_cldr_localiser_symbols)
12
15
  docxml.xpath("//m:math", MATHML).each do |f| # rubocop:disable Style/CombinableLoops
16
+ f.parent&.parent&.name == "fmt-stem" or next
13
17
  mathml1(f, locale)
14
18
  end
15
19
  end
@@ -18,81 +22,124 @@ module IsoDoc
18
22
  # TwitterCldr::DataReaders::NumberDataReader.new(locale).symbols
19
23
  def localize_maths(node, locale)
20
24
  node.xpath(".//m:mn", MATHML).each do |x|
21
- num = BigDecimal(x.text)
22
- precision = /\./.match?(x.text) ? x.text.sub(/^.*\./, "").size : 0
23
- x.children = localized_number(num, locale, precision)
25
+ fmt = x["data-metanorma-numberformat"]
26
+ x.delete("data-metanorma-numberformat")
27
+ x.children =
28
+ if !fmt.nil? && !fmt.empty?
29
+ explicit_number_formatter(x, locale, fmt)
30
+ else implicit_number_formatter(x, locale)
31
+ end
24
32
  rescue ArgumentError
33
+ rescue StandardError, RuntimeError => e
34
+ warn "Failure to localise MathML/mn\n#{node.parent.to_xml}\n#{e}"
25
35
  end
26
36
  end
27
37
 
28
- # By itself twitter-cldr does not support fraction part digits grouping
29
- # and custom delimeter, will decorate fraction part manually
30
- def localized_number(num, locale, precision)
31
- localized = localized_number1(num, locale, precision)
32
- twitter_cldr_reader_symbols = twitter_cldr_reader(locale)
33
- return localized unless twitter_cldr_reader_symbols[:decimal]
34
-
35
- integer, fraction = localized.split(twitter_cldr_reader_symbols[:decimal])
36
- return localized if fraction.nil? || fraction.empty?
38
+ def normalise_number(num)
39
+ n = BigDecimal(num).to_s("F")
40
+ /\.\d/.match?(num) or n.sub!(/\.\d+$/, "")
41
+ n
42
+ end
37
43
 
38
- [integer, decorate_fraction_part(fraction, locale)]
39
- .join(twitter_cldr_reader_symbols[:decimal])
44
+ def implicit_number_formatter(num, locale)
45
+ num.ancestors("formula").empty? or return
46
+ ## by default, no formatting in formulas
47
+ fmt = { significant: num_totaldigits(num.text) }.compact
48
+ n = normalise_number(num.text)
49
+ @numfmt.localized_number(n, locale:, format: fmt,
50
+ precision: num_precision(num.text))
40
51
  end
41
52
 
42
- def localized_number1(num, locale, precision)
43
- if precision.zero?
44
- num.localize(locale).to_s
45
- else
46
- num.localize(locale).to_decimal.to_s(precision: precision)
53
+ def numberformat_extract(options)
54
+ options.gsub!(/([a-z_]+)='/, %('\\1=))
55
+ CSV.parse_line(options, quote_char: "'").each_with_object({}) do |x, acc|
56
+ m = /^(.+?)=(.+)?$/.match(x) or next
57
+ acc[m[1].to_sym] = m[2].sub(/^(["'])(.+)\1$/, "\\2")
47
58
  end
48
59
  end
49
60
 
50
- def decorate_fraction_part(fract, locale)
51
- result = []
52
- twitter_cldr_reader_symbols = twitter_cldr_reader(locale)
53
- fract = fract.slice(0..(twitter_cldr_reader_symbols[:precision] || -1))
54
- fr_group_digits = twitter_cldr_reader_symbols[:fraction_group_digits] || 1
55
- until fract.empty?
56
- result.push(fract.slice!(0, fr_group_digits))
61
+ def numberformat_type(ret)
62
+ %i(precision significant digit_count group_digits fraction_group_digits)
63
+ .each do |i|
64
+ ret[i] &&= ret[i].to_i
65
+ end
66
+ %i(notation exponent_sign number_sign locale).each do |i|
67
+ ret[i] &&= ret[i].to_sym
57
68
  end
58
- result.join(twitter_cldr_reader_symbols[:fraction_group].to_s)
69
+ ret
59
70
  end
60
71
 
61
- def twitter_cldr_localiser_symbols
62
- {}
72
+ def explicit_number_formatter(num, locale, options)
73
+ ret = numberformat_type(numberformat_extract(options))
74
+ l = ret[:locale] || locale
75
+ precision, symbols, significant = explicit_number_formatter_cfg(num, ret)
76
+ n = normalise_number(num.text)
77
+ Plurimath::NumberFormatter.new(l)
78
+ .localized_number(n, precision:,
79
+ format: symbols.merge(significant:))
63
80
  end
64
81
 
65
- def twitter_cldr_reader(locale)
66
- return @twitter_cldr_reader if @twitter_cldr_reader
82
+ def explicit_number_formatter_cfg(num, fmt)
83
+ symbols = twitter_cldr_localiser_symbols.dup.transform_values do |v|
84
+ v.is_a?(String) ? HTMLEntities.new.decode(v) : v
85
+ end.merge(fmt)
86
+ symbols = large_notation_fmt(symbols, num.text)
87
+ [symbols[:precision] || num_precision(num.text), symbols,
88
+ explicit_number_formatter_signif(num, symbols)]
89
+ end
67
90
 
68
- num = TwitterCldr::DataReaders::NumberDataReader.new(locale)
69
- @twitter_cldr_reader = num.symbols.merge!(twitter_cldr_localiser_symbols)
70
- .merge!(parse_localize_number)
71
- @twitter_cldr_reader
91
+ def explicit_number_formatter_signif(num, symbols)
92
+ signif = symbols[:significant]
93
+ (symbols.keys & %i(precision digit_count)).empty? and
94
+ signif ||= num_totaldigits(num.text)
95
+ signif
72
96
  end
73
97
 
74
- def twitter_cldr_localiser
75
- locale = TwitterCldr.supported_locale?(@lang.to_sym) ? @lang.to_sym : :en
76
- twitter_cldr_reader(locale)
77
- locale
98
+ def large_notation_fmt(symbols, num)
99
+ n = symbols[:large_notation]
100
+ min = BigDecimal(symbols[:large_notation_min] || "1e-6")
101
+ max = BigDecimal(symbols[:large_notation_max] || "1e6")
102
+ n1 = large_notation_fmt1(num, n, min, max) and symbols[:notation] = n1
103
+ symbols.delete(:large_notation)
104
+ symbols.delete(:large_notation_min)
105
+ symbols.delete(:large_notation_max)
106
+ symbols
78
107
  end
79
108
 
80
- def parse_localize_number
81
- @localizenumber or return {}
82
- m = %r{(?<group>[^#])?(?<groupdigits>#+0)(?<decimal>.)(?<fractdigits>#+)(?<fractgroup>[^#])?}
83
- .match(@localizenumber) or return {}
84
- ret = { decimal: m[:decimal], group_digits: m[:groupdigits].size,
85
- fraction_group_digits: m[:fractdigits].size,
86
- group: m[:group] || "",
87
- fraction_group: m[:fractgroup] || "" }.compact
88
- %i(group fraction_group).each { |x| ret[x] == " " and ret[x] = "\u00A0" }
89
- ret
109
+ def large_notation_fmt1(num, notation, min, max)
110
+ notation.nil? || notation == "nil" and return nil
111
+ val = BigDecimal(num).abs
112
+ val.zero? and return nil
113
+ val < min and return notation
114
+ val > max and return notation
115
+ nil
116
+ end
117
+
118
+ def num_precision(num)
119
+ precision = nil
120
+ /\.(?!\d+e)/.match?(num) and
121
+ precision = twitter_cldr_localiser_symbols[:precision] ||
122
+ num.sub(/^.*\./, "").size
123
+ precision
124
+ end
125
+
126
+ def num_totaldigits(num)
127
+ totaldigits = nil
128
+ /\.(?=\d+e)/.match?(num) and
129
+ totaldigits = twitter_cldr_localiser_symbols[:significant] ||
130
+ num.sub(/^0\./, ".").sub(/^.*\./, "").sub(/e.*$/, "").size
131
+ totaldigits
132
+ end
133
+
134
+ def twitter_cldr_localiser_symbols
135
+ {}
90
136
  end
91
137
 
92
138
  def asciimath_dup(node)
93
139
  @suppressasciimathdup || node.parent.at(ns("./asciimath")) and return
94
140
  math = node.to_xml.gsub(/ xmlns=["'][^"']+["']/, "")
95
141
  .gsub(%r{<[^:/>]+:}, "<").gsub(%r{</[^:/>]+:}, "</")
142
+ .gsub(%r{ data-metanorma-numberformat="[^"]+"}, "")
96
143
  ret = Plurimath::Math.parse(math, "mathml").to_asciimath
97
144
  node.next = "<asciimath>#{@c.encode(ret, :basic)}</asciimath>"
98
145
  rescue StandardError => e
@@ -100,12 +147,13 @@ module IsoDoc
100
147
  end
101
148
 
102
149
  def maths_just_numeral(node)
103
- mn = node.at(".//m:mn", MATHML).children
104
- if node.parent.name == "stem"
105
- node.parent.replace(mn)
106
- else
150
+ mn = node.at(".//m:mn", MATHML).children.text
151
+ .sub(/\^([0-9+-]+)$/, "<sup>\\1</sup>")
152
+ #if node.parent.parent.name == "stem"
153
+ #node.replace(mn)
154
+ #else
107
155
  node.replace(mn)
108
- end
156
+ #end
109
157
  end
110
158
 
111
159
  def mathml1(node, locale)
@@ -113,24 +161,33 @@ module IsoDoc
113
161
  mathml_number(node, locale)
114
162
  end
115
163
 
116
- def mathml_linebreak(node)
117
- node.at(".//*/@linebreak") or return
118
- m = Plurimath::Math.parse(node.to_xml, :mathml)
119
- .to_mathml(split_on_linebreak: true)
120
- ret = Nokogiri::XML("<m>#{m}</m>").root
121
- ret.elements.each_with_index do |e, i|
122
- i.zero? or e.previous = "<br/>"
164
+ def stem_dup(node)
165
+ sem_xml_descendant?(node) and return
166
+ ret = semx_fmt_dup(node)
167
+ f = Nokogiri::XML::Node.new("fmt-stem", node.document)
168
+ t = node["type"] and f["type"] = t
169
+ f << ret
170
+ node.next = f
171
+ end
172
+
173
+ # convert any Ascii superscripts to correct(ish) MathML
174
+ # Not bothering to match times, base of 1.0 x 10^-20, just ^-20
175
+ def mn_to_msup(node)
176
+ node.xpath(".//m:mn", MATHML).each do |n|
177
+ m = %r{^(.+)\^([0-9+-]+)$}.match(n.text) or next
178
+ n.replace("<msup><mn>#{m[1]}</mn><mn>#{m[2]}</mn></msup>")
123
179
  end
124
- node.replace(<<~OUTPUT)
125
- <math-with-linebreak>#{ret.children}</math-with-linebreak><math-no-linebreak>#{node.to_xml}</math-no-linebreak>
126
- OUTPUT
127
180
  end
128
181
 
129
182
  def mathml_number(node, locale)
130
183
  justnumeral = numeric_mathml?(node)
131
184
  justnumeral or asciimath_dup(node)
132
185
  localize_maths(node, locale)
133
- justnumeral and maths_just_numeral(node)
186
+ if justnumeral
187
+ maths_just_numeral(node)
188
+ else
189
+ mn_to_msup(node)
190
+ end
134
191
  end
135
192
 
136
193
  def numeric_mathml?(node)
@@ -152,11 +209,11 @@ module IsoDoc
152
209
  def mathml_number_to_number(node)
153
210
  (node.elements.size == 1 && node.elements.first.name == "mn") or return
154
211
  repl = node.at("./m:mn", MATHML).children
155
- if node.parent.name == "stem"
156
- node.parent.replace(repl)
157
- else
212
+ #if node.parent.name == "stem"
213
+ #node.parent.replace(repl)
214
+ #else
158
215
  node.replace(repl)
159
- end
216
+ #end
160
217
  end
161
218
  end
162
219
  end
@@ -3,13 +3,35 @@ module IsoDoc
3
3
  def metadata(docxml)
4
4
  toc_metadata(docxml)
5
5
  fonts_metadata(docxml)
6
- preprocess_xslt_insert(docxml)
6
+ attachments_extract(docxml)
7
+ localized_strings(docxml)
8
+ a = docxml.at(ns("//metanorma-extension")) or return
9
+ a.elements.empty? and a.remove
10
+ end
11
+
12
+ def localized_strings(docxml)
7
13
  a = docxml.at(ns("//bibdata")) or return
8
14
  a.next =
9
15
  "<localized-strings>#{i8n_name(trim_hash(@i18n.get), '').join}" \
10
16
  "</localized-strings>"
11
17
  end
12
18
 
19
+ def attachments_extract(docxml)
20
+ docxml.at(ns("//metanorma-extension/attachment")) or return
21
+ dir = File.join(@localdir, "_#{@outputfile}_attachments")
22
+ FileUtils.rm_rf(dir)
23
+ FileUtils.mkdir_p(dir)
24
+ docxml.xpath(ns("//metanorma-extension/attachment")).each do |a|
25
+ save_attachment(a, dir)
26
+ end
27
+ end
28
+
29
+ def save_attachment(attachment, dir)
30
+ n = File.join(dir, File.basename(attachment["name"]))
31
+ c = attachment.text.sub(%r{^data:[^;]+;(?:charset=[^;]+;)?base64,}, "")
32
+ File.open(n, "wb") { |f| f.write(Base64.decode64(c)) }
33
+ end
34
+
13
35
  def extension_insert(xml, path = [])
14
36
  ins = extension_insert_pt(xml)
15
37
  path.each do |n|
@@ -20,9 +42,11 @@ module IsoDoc
20
42
 
21
43
  def extension_insert_pt(xml)
22
44
  xml.at(ns("//metanorma-extension")) ||
23
- xml.at(ns("//bibdata"))&.after("<metanorma-extension/>")
45
+ xml.at(ns("//bibdata"))
46
+ &.after("<metanorma-extension> </metanorma-extension>")
24
47
  &.next_element ||
25
- xml.root.elements.first.before("<metanorma-extension/>")
48
+ xml.root.elements.first
49
+ .before("<metanorma-extension> </metanorma-extension>")
26
50
  .previous_element
27
51
  end
28
52
 
@@ -41,7 +65,7 @@ module IsoDoc
41
65
  def fonts_metadata(xmldoc)
42
66
  ins = presmeta_insert_pt(xmldoc)
43
67
  @fontist_fonts and CSV.parse_line(@fontist_fonts, col_sep: ";")
44
- .map(&:strip).reverse.each do |f|
68
+ .map(&:strip).reverse_each do |f|
45
69
  ins.next = presmeta("fonts", f)
46
70
  end
47
71
  @fontlicenseagreement and
@@ -50,7 +74,7 @@ module IsoDoc
50
74
 
51
75
  def presmeta_insert_pt(xmldoc)
52
76
  xmldoc.at(ns("//presentation-metadata")) ||
53
- xmldoc.at(ns("//metanorma-extension")) || xmldoc.at(ns("//bibdata"))
77
+ extension_insert_pt(xmldoc).children.last
54
78
  end
55
79
 
56
80
  def presmeta(name, value)
@@ -58,67 +82,6 @@ module IsoDoc
58
82
  "</presentation-metadata>"
59
83
  end
60
84
 
61
- def preprocess_xslt_insert(docxml)
62
- content = ""
63
- p = passthrough_xslt and content += p
64
- p = preprocess_xslt_read and content += File.read(p)
65
- content.empty? and return
66
- ins = extension_insert(docxml, %w(render))
67
- ins << content
68
- end
69
-
70
- COPY_XSLT =
71
- '<xsl:copy><xsl:apply-templates select="@* | node()"/></xsl:copy>'.freeze
72
- COPY_CHILDREN_XSLT =
73
- '<xsl:apply-templates select="node()"/>'.freeze
74
-
75
- def xslt_template(content)
76
- <<~XSLT
77
- <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" version="1.0">
78
- <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="no"/>
79
- <xsl:template match="@* | node()">#{COPY_XSLT}</xsl:template>
80
- #{content}
81
- </xsl:stylesheet>
82
- XSLT
83
- end
84
-
85
- def passthrough_xslt
86
- @output_formats.nil? and return nil
87
- @output_formats.empty? and return nil
88
- @output_formats.each_key.with_object([]) do |k, m|
89
- m << <<~XSLT
90
- <preprocess-xslt format="#{k}">
91
- #{xslt_template(<<~XSLT1)
92
- <xsl:template match="*[local-name() = 'passthrough']">
93
- <xsl:if test="contains(@formats,',#{k},')"> <!-- delimited -->
94
- #{COPY_XSLT}
95
- </xsl:if>
96
- </xsl:template>
97
- XSLT1
98
- }
99
- </preprocess-xslt>
100
- XSLT
101
- m << <<~XSLT
102
- <preprocess-xslt format="#{k}">
103
- #{xslt_template(<<~XSLT1)
104
- <xsl:template match="*[local-name() = 'math-with-linebreak']">
105
- #{k == 'pdf' ? COPY_CHILDREN_XSLT : ''}
106
- </xsl:template>
107
- <xsl:template match="*[local-name() = 'math-no-linebreak']">
108
- #{k == 'pdf' ? '' : COPY_CHILDREN_XSLT}
109
- </xsl:template>
110
- XSLT1
111
- }
112
- </preprocess-xslt>
113
- XSLT
114
- end.join("\n")
115
- end
116
-
117
- # read in from file, but with `<preprocess-xslt @format="">` wrapper
118
- def preprocess_xslt_read
119
- html_doc_path("preprocess.xslt")
120
- end
121
-
122
85
  def i18n_tag(key, value)
123
86
  "<localized-string key='#{key}' language='#{@lang}'>#{value}" \
124
87
  "</localized-string>"