isodoc 2.7.3 → 2.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eb9c5b4a4ae20094a096ef37012c56b2f125a636c0aa426d135f1b8257cbf205
4
- data.tar.gz: a1f5bbeed6904bd7d958ee0fdaeb4b180f0bf5405a42d556f7e787153247130d
3
+ metadata.gz: 7399ef0fe36a0b345f15af67b09daec6f68ed36ce2226af1469ace300d4e3fa9
4
+ data.tar.gz: 5d03fbb31c3a38b11ca47d34a1673326f68e7db9f577de5995944b4c1e77d1f1
5
5
  SHA512:
6
- metadata.gz: a9aa50e9e6b759064321dbb68b46bbe9ab6d321ac73d584aad19b8ecedb109ee48119d06e72c03f7802910946d73ae72e08b24b502d3495d04e7805141de522a
7
- data.tar.gz: fb4483cd05281ce5278d745c2ecc7f8b73139e1e47a643700c9112747ce475869c570a1555d836277cb6ed90c7d544b43eda21bc6b81a5f08b7470fb14ad8f73
6
+ metadata.gz: 3b5fb43c2a320525f7a1ce8e30d376ee687fd3295e16aa9336e846db7f93958d130e50b896216cbc651e4b33037840c97d9df2b6a9f578cc8dfef34a89bed098
7
+ data.tar.gz: 5b9bb3130bde935c9d420c02092d5c341ed2bdb18e7a5aaa2e718a58798c165c8130b7a036ff4619b19ef2655d41223a7f13b5c110bd0e5b2881a877920d0119
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ spec/assets/img.svg
data/Gemfile CHANGED
@@ -6,7 +6,4 @@ git_source(:github) { |repo| "https://github.com/#{repo}" }
6
6
 
7
7
  gemspec
8
8
 
9
- if File.exist? "Gemfile.devel"
10
- eval File.read("Gemfile.devel"), nil, "Gemfile.devel" # rubocop:disable Security/Eval
11
- end
12
-
9
+ eval_gemfile("Gemfile.devel") rescue nil
data/isodoc.gemspec CHANGED
@@ -31,7 +31,6 @@ Gem::Specification.new do |spec|
31
31
  spec.add_dependency "html2doc", "~> 1.7.1"
32
32
  spec.add_dependency "htmlentities", "~> 4.3.4"
33
33
  # spec.add_dependency "isodoc-i18n", "~> 1.1.0" # already in relaton-render and mn-requirements
34
- spec.add_dependency "emf2svg"
35
34
  spec.add_dependency "liquid", "~> 5"
36
35
  #spec.add_dependency "relaton-cli"
37
36
  # spec.add_dependency "metanorma-utils", "~> 1.5.0" # already in isodoc-i18n
@@ -44,6 +43,7 @@ Gem::Specification.new do |spec|
44
43
  spec.add_dependency "thread_safe"
45
44
  spec.add_dependency "twitter_cldr", ">= 6.6.0"
46
45
  spec.add_dependency "uuidtools"
46
+ spec.add_dependency "vectory", "~> 0.6"
47
47
 
48
48
  spec.add_development_dependency "debug"
49
49
  spec.add_development_dependency "equivalent-xml", "~> 0.6"
@@ -132,6 +132,14 @@ a.FootnoteRef, span.FootnoteRef {
132
132
  color: red;
133
133
  text-decoration: line-through; }
134
134
 
135
+ ruby {
136
+ ruby-position: over;
137
+ -webkit-ruby-position: before; }
138
+
139
+ ruby ruby {
140
+ ruby-position: under;
141
+ -webkit-ruby-position: after; }
142
+
135
143
  /* code highlighting with line numbers */
136
144
  table.rouge-line-table td.rouge-gutter {
137
145
  -moz-user-select: none;
@@ -132,6 +132,14 @@ a.FootnoteRef, span.FootnoteRef {
132
132
  color: red;
133
133
  text-decoration: line-through; }
134
134
 
135
+ ruby {
136
+ ruby-position: over;
137
+ -webkit-ruby-position: before; }
138
+
139
+ ruby ruby {
140
+ ruby-position: under;
141
+ -webkit-ruby-position: after; }
142
+
135
143
  /* code highlighting with line numbers */
136
144
  table.rouge-line-table td.rouge-gutter {
137
145
  -moz-user-select: none;
@@ -163,6 +163,9 @@ a.FootnoteRef, span.FootnoteRef {
163
163
  text-decoration: line-through;
164
164
  }
165
165
 
166
+ ruby { ruby-position: over; -webkit-ruby-position: before;}
167
+ ruby ruby { ruby-position: under; -webkit-ruby-position: after; }
168
+
166
169
  /* code highlighting with line numbers */
167
170
 
168
171
  table.rouge-line-table td.rouge-gutter {
@@ -87,12 +87,14 @@ module IsoDoc
87
87
  MATHML = { "m" => "http://www.w3.org/1998/Math/MathML" }.freeze
88
88
 
89
89
  def mathml_parse(node)
90
- node.at("./m:math", MATHML)&.to_xml
90
+ # node.xpath("./m:math", MATHML).map(&:to_xml).join
91
+ node.xpath(ns("./asciimath | ./latexmath")).each(&:remove)
92
+ node.xpath(ns("./br")).each { |e| e.namespace = nil }
93
+ node.elements
91
94
  end
92
95
 
93
96
  def asciimath_parse(node)
94
97
  a = node.at(ns("./asciimath"))&.text || node.text
95
-
96
98
  "#{@openmathdelim}#{HTMLEntities.new.encode(a)}" \
97
99
  "#{@closemathdelim}"
98
100
  end
@@ -155,6 +157,24 @@ module IsoDoc
155
157
  p.b(role: "strong") { |e| e << text }
156
158
  end
157
159
  end
160
+
161
+ def ruby_parse(node, out)
162
+ out.ruby do |e|
163
+ node.children.each { |n| parse(n, e) }
164
+ end
165
+ end
166
+
167
+ def rt_parse(node, out)
168
+ out.rt do |e|
169
+ node.children.each { |n| parse(n, e) }
170
+ end
171
+ end
172
+
173
+ def rb_parse(node, out)
174
+ out.rb do |e|
175
+ node.children.each { |n| parse(n, e) }
176
+ end
177
+ end
158
178
  end
159
179
  end
160
180
  end
@@ -253,6 +253,9 @@ module IsoDoc
253
253
  when "span" then span_parse(node, out)
254
254
  when "location" then location_parse(node, out)
255
255
  when "columnbreak" then columnbreak_parse(node, out)
256
+ when "ruby" then ruby_parse(node, out)
257
+ when "rt" then rt_parse(node, out)
258
+ when "rb" then rb_parse(node, out)
256
259
  else error_parse(node, out)
257
260
  end
258
261
  end
@@ -15,6 +15,11 @@ module IsoDoc
15
15
  end.join("\n")
16
16
  end
17
17
 
18
+ def preprocess_xslt(docxml)
19
+
20
+ super
21
+ end
22
+
18
23
  def make_body1(body, _docxml)
19
24
  return if @bare
20
25
 
@@ -85,7 +85,7 @@ module IsoDoc
85
85
  end
86
86
 
87
87
  def datauri(img)
88
- img["src"] = Metanorma::Utils::datauri(img["src"], @localdir)
88
+ img["src"] = Vectory::Utils::datauri(img["src"], @localdir)
89
89
  end
90
90
 
91
91
  def image_suffix(img)
@@ -106,12 +106,12 @@ module IsoDoc
106
106
  end
107
107
 
108
108
  def version(isoxml, _out)
109
- set(:edition, isoxml&.at(ns("//bibdata/edition#{NOLANG}"))&.text)
109
+ set(:edition, isoxml.at(ns("//bibdata/edition#{NOLANG}"))&.text)
110
110
  set(:edition_display,
111
- isoxml&.at(ns("//bibdata/edition#{currlang}"))&.text)
112
- set(:docyear, isoxml&.at(ns("//bibdata/copyright/from"))&.text)
113
- set(:draft, isoxml&.at(ns("//bibdata/version/draft"))&.text)
114
- revdate = isoxml&.at(ns("//bibdata/version/revision-date"))&.text
111
+ isoxml.at(ns("//bibdata/edition#{currlang}"))&.text)
112
+ set(:docyear, isoxml.at(ns("//bibdata/copyright/from"))&.text)
113
+ set(:draft, isoxml.at(ns("//bibdata/version/draft"))&.text)
114
+ revdate = isoxml.at(ns("//bibdata/version/revision-date"))&.text
115
115
  set(:revdate, revdate)
116
116
  set(:revdate_monthyear, monthyr(revdate))
117
117
  set(:draftinfo,
@@ -119,7 +119,7 @@ module IsoDoc
119
119
  end
120
120
 
121
121
  def title(isoxml, _out)
122
- main = isoxml&.at(ns("//bibdata/title[@language='#{@lang}']"))&.text
122
+ main = isoxml.at(ns("//bibdata/title[@language='#{@lang}']"))&.text
123
123
  set(:doctitle, main)
124
124
  end
125
125
 
@@ -1,9 +1,9 @@
1
1
  module IsoDoc
2
2
  class Metadata
3
- DATETYPES = %w{published accessed created implemented obsoleted confirmed
4
- updated corrected issued received transmitted copied unchanged
5
- circulated vote-started
6
- vote-ended}.freeze
3
+ DATETYPES =
4
+ %w{published accessed created implemented obsoleted confirmed updated
5
+ corrected issued received transmitted copied unchanged circulated
6
+ adapted announced vote-started vote-ended stable-until}.freeze
7
7
 
8
8
  def months
9
9
  {
@@ -3,84 +3,10 @@ require "csv"
3
3
  module IsoDoc
4
4
  class PresentationXMLConvert < ::IsoDoc::Convert
5
5
  def bibdata(docxml)
6
- toc_metadata(docxml)
7
- fonts_metadata(docxml)
8
- preprocess_xslt_insert(docxml)
9
6
  docid_prefixes(docxml)
10
7
  a = bibdata_current(docxml) or return
11
8
  address_precompose(a)
12
9
  bibdata_i18n(a)
13
- a.next =
14
- "<localized-strings>#{i8n_name(trim_hash(@i18n.get), '').join}" \
15
- "</localized-strings>"
16
- end
17
-
18
- def extension_insert(xml, path = [])
19
- ins = extension_insert_pt(xml)
20
- path.each do |n|
21
- ins = ins.at(ns("./#{n}")) || ins.add_child("<#{n}/>").first
22
- end
23
- ins
24
- end
25
-
26
- def extension_insert_pt(xml)
27
- xml.at(ns("//metanorma-extension")) ||
28
- xml.at(ns("//bibdata"))&.after("<metanorma-extension/>")
29
- &.next_element ||
30
- xml.root.elements.first.before("<metanorma-extension/>")
31
- .previous_element
32
- end
33
-
34
- def preprocess_xslt_insert(docxml)
35
- content = ""
36
- p = passthrough_xslt and content += p
37
- p = preprocess_xslt_read and content += File.read(p)
38
- content.empty? and return
39
- ins = extension_insert(docxml, %w(render))
40
- ins << content
41
- end
42
-
43
- def passthrough_xslt
44
- @output_formats.nil? and return nil
45
- @output_formats.empty? and return nil
46
- @output_formats.each_key.with_object([]) do |k, m|
47
- m << <<~XSLT
48
- <preprocess-xslt format="#{k}">
49
- <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" version="1.0">
50
- <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="no"/>
51
- <xsl:template match="@* | node()">
52
- <xsl:copy>
53
- <xsl:apply-templates select="@* | node()"/>
54
- </xsl:copy>
55
- </xsl:template>
56
- <xsl:template match="*[local-name() = 'passthrough']">
57
- <xsl:if test="contains(@formats,',#{k},')"> <!-- delimited -->
58
- <xsl:copy>
59
- <xsl:apply-templates select="@* | node()"/>
60
- </xsl:copy>
61
- </xsl:if>
62
- </xsl:template>
63
- </xsl:stylesheet>
64
- </preprocess-xslt>
65
- XSLT
66
- end.join("\n")
67
- end
68
-
69
- # read in from file, but with `<preprocess-xslt @format="">` wrapper
70
- def preprocess_xslt_read
71
- html_doc_path("preprocess.xslt")
72
- end
73
-
74
- def toc_metadata(docxml)
75
- @tocfigures || @toctables || @tocrecommendations or return
76
- ins = extension_insert(docxml)
77
- @tocfigures and
78
- ins << "<toc type='figure'><title>#{@i18n.toc_figures}</title></toc>"
79
- @toctables and
80
- ins << "<toc type='table'><title>#{@i18n.toc_tables}</title></toc>"
81
- @tocfigures and
82
- ins << "<toc type='recommendation'><title>#{@i18n.toc_recommendations}" \
83
- "</title></toc>"
84
10
  end
85
11
 
86
12
  def address_precompose(bib)
@@ -92,26 +18,6 @@ module IsoDoc
92
18
  end
93
19
  end
94
20
 
95
- def fonts_metadata(xmldoc)
96
- ins = presmeta_insert_pt(xmldoc)
97
- @fontist_fonts and CSV.parse_line(@fontist_fonts, col_sep: ";")
98
- .map(&:strip).reverse.each do |f|
99
- ins.next = presmeta("fonts", f)
100
- end
101
- @fontlicenseagreement and
102
- ins.next = presmeta("font-license-agreement", @fontlicenseagreement)
103
- end
104
-
105
- def presmeta_insert_pt(xmldoc)
106
- xmldoc.at(ns("//presentation-metadata")) ||
107
- xmldoc.at(ns("//metanorma-extension")) || xmldoc.at(ns("//bibdata"))
108
- end
109
-
110
- def presmeta(name, value)
111
- "<presentation-metadata><name>#{name}</name><value>#{value}</value>" \
112
- "</presentation-metadata>"
113
- end
114
-
115
21
  def address_precompose1(addr)
116
22
  ret = []
117
23
  addr.xpath(ns("./street")).each { |s| ret << to_xml(s.children) }
@@ -176,67 +82,5 @@ module IsoDoc
176
82
  tag.next["language"] = lang
177
83
  tag.next.children = value
178
84
  end
179
-
180
- def i18n_tag(key, value)
181
- "<localized-string key='#{key}' language='#{@lang}'>#{value}" \
182
- "</localized-string>"
183
- end
184
-
185
- def i18n_safe(key)
186
- key.to_s.gsub(/\s|\./, "_")
187
- end
188
-
189
- def i8n_name(hash, pref)
190
- case hash
191
- when Hash then i8n_name1(hash, pref)
192
- when Array
193
- hash.reject { |a| blank?(a) }.each_with_object([])
194
- .with_index do |(v1, g), i|
195
- i8n_name(v1, "#{i18n_safe(k)}.#{i}").each { |x| g << x }
196
- end
197
- else [i18n_tag(pref, hash)]
198
- end
199
- end
200
-
201
- def i8n_name1(hash, pref)
202
- hash.reject { |_k, v| blank?(v) }.each_with_object([]) do |(k, v), g|
203
- case v
204
- when Hash then i8n_name(v, i18n_safe(k)).each { |x| g << x }
205
- when Array
206
- v.reject { |a| blank?(a) }.each_with_index do |v1, i|
207
- i8n_name(v1, "#{i18n_safe(k)}.#{i}").each { |x| g << x }
208
- end
209
- else
210
- g << i18n_tag("#{pref}#{pref.empty? ? '' : '.'}#{i18n_safe(k)}", v)
211
- end
212
- end
213
- end
214
-
215
- # https://stackoverflow.com/a/31822406
216
- def blank?(elem)
217
- elem.nil? || (elem.respond_to?(:empty?) && elem.empty?)
218
- end
219
-
220
- def trim_hash(hash)
221
- loop do
222
- h_new = trim_hash1(hash)
223
- break hash if hash == h_new
224
-
225
- hash = h_new
226
- end
227
- end
228
-
229
- def trim_hash1(hash)
230
- hash.is_a?(Hash) or return hash
231
- hash.each_with_object({}) do |(k, v), g|
232
- blank?(v) and next
233
- g[k] = case v
234
- when Hash then trim_hash1(hash[k])
235
- when Array
236
- hash[k].map { |a| trim_hash1(a) }.reject { |a| blank?(a) }
237
- else v
238
- end
239
- end
240
- end
241
85
  end
242
86
  end
@@ -1,5 +1,4 @@
1
1
  require "base64"
2
- require "emf2svg"
3
2
 
4
3
  module IsoDoc
5
4
  class PresentationXMLConvert < ::IsoDoc::Convert
@@ -92,33 +91,56 @@ module IsoDoc
92
91
 
93
92
  def svg_prep(img)
94
93
  img["mimetype"] = "image/svg+xml"
95
- %r{^data:image}.match?(img["src"]) or
96
- img["src"] = Metanorma::Utils::datauri(img["src"])
94
+ %r{^data:}.match?(img["src"]) or
95
+ img["src"] = Vectory::Emf.from_path(img["src"]).to_uri.content
97
96
  end
98
97
 
99
98
  def emf_to_svg(img)
100
- emf = Metanorma::Utils::save_dataimage(img.at(ns("./emf/@src")).text)
101
- Emf2svg.from_file(emf).sub(/<\?[^>]+>/, "")
99
+ datauri_src = img.at(ns("./emf/@src")).text
100
+ Vectory::Emf.from_datauri(datauri_src)
101
+ .to_svg
102
+ .content
103
+ .sub(/<\?[^>]+>/, "")
102
104
  end
103
105
 
104
106
  def eps_to_svg(node)
105
- uri = eps_to_svg_uri(node)
106
- ret = imgfile_suffix(uri, "svg")
107
- File.exist?(ret) and return ret
108
- inkscape_convert(uri, ret, "--export-plain-svg")
107
+ if !node.text.strip.empty? || %r{^data:}.match?(node["src"])
108
+ return eps_to_svg_from_node(node)
109
+ end
110
+
111
+ target_path = imgfile_suffix(node["src"], "svg")
112
+ return target_path if File.exist?(target_path)
113
+
114
+ eps_to_svg_from_node(node, target_path)
115
+ end
116
+
117
+ def eps_to_svg_from_node(node, target_path = nil)
118
+ svg = Vectory::Eps.from_node(node).to_svg
119
+ return svg.write(target_path).path if target_path
120
+
121
+ svg.write.path
109
122
  end
110
123
 
111
124
  def svg_to_emf(node)
112
125
  @output_formats[:doc] or return
113
- uri = svg_to_emf_uri(node)
126
+
114
127
  svg_impose_height_attr(node)
115
- ret = imgfile_suffix(uri, "emf")
116
- if File.exist?(ret) && File.exist?(node["src"])
117
- warn "Exists: #{ret}, Exists: #{node['src']}"
118
- return ret
128
+
129
+ if node.elements&.first&.name == "svg" || %r{^data:}.match?(node["src"])
130
+ return svg_to_emf_from_node(node)
119
131
  end
120
- warn "Converting..."
121
- inkscape_convert(uri, ret, '--export-type="emf"')
132
+
133
+ target_path = imgfile_suffix(node["src"], "emf")
134
+ return target_path if File.exist?(target_path)
135
+
136
+ svg_to_emf_from_node(node, target_path)
137
+ end
138
+
139
+ def svg_to_emf_from_node(node, target_path = nil)
140
+ emf = Vectory::Svg.from_node(node).to_emf
141
+ return emf.write(target_path).to_uri.content if target_path
142
+
143
+ emf.to_uri.content
122
144
  end
123
145
 
124
146
  def svg_impose_height_attr(node)
@@ -129,68 +151,8 @@ module IsoDoc
129
151
  node["width"] = e["width"]
130
152
  end
131
153
 
132
- def inkscape_convert(uri, file, option)
133
- exe = inkscape_installed? or raise "Inkscape missing in PATH, unable" \
134
- "to convert image #{uri}. Aborting."
135
- uri = Metanorma::Utils::external_path uri
136
- exe = Metanorma::Utils::external_path exe
137
- warn %(#{exe} #{option} #{uri})
138
- err = system %(#{exe} #{option} #{uri})
139
- File.exist?(file) and return Metanorma::Utils::datauri(file)
140
- file2 = uri + File.extname(file)
141
- warn "Checking #{file2}"
142
- File.exist?(file2) and return Metanorma::Utils::datauri(file2)
143
- raise %(Fail on #{exe} #{option} #{uri} outputting #{file}: status #{err})
144
- end
145
-
146
- def svg_to_emf_uri(node)
147
- uri = svg_to_emf_uri_convert(node)
148
- cache_dataimage(uri)
149
- end
150
-
151
- def eps_to_svg_uri(node)
152
- uri = eps_to_svg_uri_convert(node)
153
- cache_dataimage(uri)
154
- end
155
-
156
- def cache_dataimage(uri)
157
- if %r{^data:}.match?(uri)
158
- uri = save_dataimage(uri)
159
- end
160
- uri
161
- end
162
-
163
- def svg_to_emf_uri_convert(node)
164
- if node.elements&.first&.name == "svg"
165
- a = Base64.strict_encode64(node.children.to_xml)
166
- "data:image/svg+xml;base64,#{a}"
167
- else node["src"]
168
- end
169
- end
170
-
171
- def eps_to_svg_uri_convert(node)
172
- if node.text.strip.empty?
173
- node["src"]
174
- else
175
- a = Base64.strict_encode64(node.children.to_xml)
176
- "data:application/postscript;base64,#{a}"
177
- end
178
- end
179
-
180
154
  def imgfile_suffix(uri, suffix)
181
155
  "#{File.join(File.dirname(uri), File.basename(uri, '.*'))}.#{suffix}"
182
156
  end
183
-
184
- def inkscape_installed?
185
- cmd = "inkscape"
186
- exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""]
187
- ENV["PATH"].split(File::PATH_SEPARATOR).each do |path|
188
- exts.each do |ext|
189
- exe = File.join(path, "#{cmd}#{ext}")
190
- return exe if File.executable?(exe) && !File.directory?(exe)
191
- end
192
- end
193
- nil
194
- end
195
157
  end
196
158
  end
@@ -137,6 +137,22 @@ module IsoDoc
137
137
  end
138
138
  end
139
139
 
140
+ def ruby(docxml)
141
+ (docxml.xpath(ns("//ruby")) - docxml.xpath(ns("//ruby//ruby")))
142
+ .each do |r|
143
+ ruby1(r)
144
+ end
145
+ end
146
+
147
+ def ruby1(elem)
148
+ v = elem.at(ns("./pronunciation | ./annotation")).remove
149
+ elem.xpath(ns("./ruby")).each do |r|
150
+ ruby1(r)
151
+ end
152
+ t = elem.children.to_xml
153
+ elem.replace("<ruby><rb>#{t}</rb><rt>#{v['value']}</rt></ruby>")
154
+ end
155
+
140
156
  private
141
157
 
142
158
  def found_matching_variant_sibling(node)
@@ -7,8 +7,9 @@ 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) }
10
11
  locale = twitter_cldr_localiser
11
- docxml.xpath("//m:math", MATHML).each do |f|
12
+ docxml.xpath("//m:math", MATHML).each do |f| # rubocop:disable Style/CombinableLoops
12
13
  mathml1(f, locale)
13
14
  end
14
15
  end
@@ -77,8 +78,7 @@ module IsoDoc
77
78
  end
78
79
 
79
80
  def parse_localize_number
80
- return {} unless @localizenumber
81
-
81
+ @localizenumber or return {}
82
82
  m = %r{(?<group>[^#])?(?<groupdigits>#+0)(?<decimal>.)(?<fractdigits>#+)(?<fractgroup>[^#])?}
83
83
  .match(@localizenumber) or return {}
84
84
  ret = { decimal: m[:decimal], group_digits: m[:groupdigits].size,
@@ -90,17 +90,13 @@ module IsoDoc
90
90
  end
91
91
 
92
92
  def asciimath_dup(node)
93
- return if @suppressasciimathdup || node.parent.at(ns("./asciimath"))
94
-
93
+ @suppressasciimathdup || node.parent.at(ns("./asciimath")) and return
95
94
  math = node.to_xml.gsub(/ xmlns=["'][^"']+["']/, "")
96
95
  .gsub(%r{<[^:/>]+:}, "<").gsub(%r{</[^:/>]+:}, "</")
97
96
  ret = Plurimath::Math.parse(math, "mathml").to_asciimath
98
- ret = HTMLEntities.new.encode(ret, :basic)
99
- node.next = "<asciimath>#{ret}</asciimath>"
97
+ node.next = "<asciimath>#{@c.encode(ret, :basic)}</asciimath>"
100
98
  rescue StandardError => e
101
- warn "Failure to convert MathML to AsciiMath"
102
- warn node.parent.to_xml
103
- warn e
99
+ warn "Failure to convert MathML to AsciiMath\n#{node.parent.to_xml}\n#{e}"
104
100
  end
105
101
 
106
102
  def maths_just_numeral(node)
@@ -114,6 +110,23 @@ module IsoDoc
114
110
 
115
111
  def mathml1(node, locale)
116
112
  mathml_style_inherit(node)
113
+ mathml_number(node, locale)
114
+ end
115
+
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/>"
123
+ 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
+ end
128
+
129
+ def mathml_number(node, locale)
117
130
  justnumeral = node.elements.size == 1 && node.elements.first.name == "mn"
118
131
  justnumeral or asciimath_dup(node)
119
132
  localize_maths(node, locale)
@@ -0,0 +1,184 @@
1
+ module IsoDoc
2
+ class PresentationXMLConvert < ::IsoDoc::Convert
3
+ def metadata(docxml)
4
+ toc_metadata(docxml)
5
+ fonts_metadata(docxml)
6
+ preprocess_xslt_insert(docxml)
7
+ a = docxml.at(ns("//bibdata")) or return
8
+ a.next =
9
+ "<localized-strings>#{i8n_name(trim_hash(@i18n.get), '').join}" \
10
+ "</localized-strings>"
11
+ end
12
+
13
+ def extension_insert(xml, path = [])
14
+ ins = extension_insert_pt(xml)
15
+ path.each do |n|
16
+ ins = ins.at(ns("./#{n}")) || ins.add_child("<#{n}/>").first
17
+ end
18
+ ins
19
+ end
20
+
21
+ def extension_insert_pt(xml)
22
+ xml.at(ns("//metanorma-extension")) ||
23
+ xml.at(ns("//bibdata"))&.after("<metanorma-extension/>")
24
+ &.next_element ||
25
+ xml.root.elements.first.before("<metanorma-extension/>")
26
+ .previous_element
27
+ end
28
+
29
+ def toc_metadata(docxml)
30
+ @tocfigures || @toctables || @tocrecommendations or return
31
+ ins = extension_insert(docxml)
32
+ @tocfigures and
33
+ ins << "<toc type='figure'><title>#{@i18n.toc_figures}</title></toc>"
34
+ @toctables and
35
+ ins << "<toc type='table'><title>#{@i18n.toc_tables}</title></toc>"
36
+ @tocfigures and
37
+ ins << "<toc type='recommendation'><title>#{@i18n.toc_recommendations}" \
38
+ "</title></toc>"
39
+ end
40
+
41
+ def fonts_metadata(xmldoc)
42
+ ins = presmeta_insert_pt(xmldoc)
43
+ @fontist_fonts and CSV.parse_line(@fontist_fonts, col_sep: ";")
44
+ .map(&:strip).reverse.each do |f|
45
+ ins.next = presmeta("fonts", f)
46
+ end
47
+ @fontlicenseagreement and
48
+ ins.next = presmeta("font-license-agreement", @fontlicenseagreement)
49
+ end
50
+
51
+ def presmeta_insert_pt(xmldoc)
52
+ xmldoc.at(ns("//presentation-metadata")) ||
53
+ xmldoc.at(ns("//metanorma-extension")) || xmldoc.at(ns("//bibdata"))
54
+ end
55
+
56
+ def presmeta(name, value)
57
+ "<presentation-metadata><name>#{name}</name><value>#{value}</value>" \
58
+ "</presentation-metadata>"
59
+ end
60
+
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
+ def i18n_tag(key, value)
123
+ "<localized-string key='#{key}' language='#{@lang}'>#{value}" \
124
+ "</localized-string>"
125
+ end
126
+
127
+ def i18n_safe(key)
128
+ key.to_s.gsub(/\s|\./, "_")
129
+ end
130
+
131
+ def i8n_name(hash, pref)
132
+ case hash
133
+ when Hash then i8n_name1(hash, pref)
134
+ when Array
135
+ hash.reject { |a| blank?(a) }.each_with_object([])
136
+ .with_index do |(v1, g), i|
137
+ i8n_name(v1, "#{i18n_safe(k)}.#{i}").each { |x| g << x }
138
+ end
139
+ else [i18n_tag(pref, hash)]
140
+ end
141
+ end
142
+
143
+ def i8n_name1(hash, pref)
144
+ hash.reject { |_k, v| blank?(v) }.each_with_object([]) do |(k, v), g|
145
+ case v
146
+ when Hash then i8n_name(v, i18n_safe(k)).each { |x| g << x }
147
+ when Array
148
+ v.reject { |a| blank?(a) }.each_with_index do |v1, i|
149
+ i8n_name(v1, "#{i18n_safe(k)}.#{i}").each { |x| g << x }
150
+ end
151
+ else
152
+ g << i18n_tag("#{pref}#{pref.empty? ? '' : '.'}#{i18n_safe(k)}", v)
153
+ end
154
+ end
155
+ end
156
+
157
+ # https://stackoverflow.com/a/31822406
158
+ def blank?(elem)
159
+ elem.nil? || (elem.respond_to?(:empty?) && elem.empty?)
160
+ end
161
+
162
+ def trim_hash(hash)
163
+ loop do
164
+ h_new = trim_hash1(hash)
165
+ break hash if hash == h_new
166
+
167
+ hash = h_new
168
+ end
169
+ end
170
+
171
+ def trim_hash1(hash)
172
+ hash.is_a?(Hash) or return hash
173
+ hash.each_with_object({}) do |(k, v), g|
174
+ blank?(v) and next
175
+ g[k] = case v
176
+ when Hash then trim_hash1(hash[k])
177
+ when Array
178
+ hash[k].map { |a| trim_hash1(a) }.reject { |a| blank?(a) }
179
+ else v
180
+ end
181
+ end
182
+ end
183
+ end
184
+ end
@@ -8,6 +8,7 @@ require_relative "presentation_function/inline"
8
8
  require_relative "presentation_function/math"
9
9
  require_relative "presentation_function/section"
10
10
  require_relative "presentation_function/bibdata"
11
+ require_relative "presentation_function/metadata"
11
12
 
12
13
  module IsoDoc
13
14
  class PresentationXMLConvert < ::IsoDoc::Convert
@@ -35,6 +36,7 @@ module IsoDoc
35
36
 
36
37
  def conversions(docxml)
37
38
  semantic_xml_insert(docxml)
39
+ metadata docxml
38
40
  bibdata docxml
39
41
  @xrefs.parse docxml
40
42
  section docxml
@@ -86,6 +88,7 @@ module IsoDoc
86
88
  quotesource docxml # feeds docxml
87
89
  eref2link docxml
88
90
  mathml docxml
91
+ ruby docxml
89
92
  variant docxml
90
93
  identifier docxml
91
94
  date docxml
@@ -1,3 +1,3 @@
1
1
  module IsoDoc
2
- VERSION = "2.7.3".freeze
2
+ VERSION = "2.8.0".freeze
3
3
  end
@@ -42,6 +42,15 @@ module IsoDoc
42
42
  end
43
43
 
44
44
  def image_parse(node, out, caption)
45
+ emf_attributes(node)
46
+ attrs = { src: imgsrc(node),
47
+ height: node["height"], alt: node["alt"],
48
+ title: node["title"], width: node["width"] }
49
+ out.img **attr_code(attrs)
50
+ image_title_parse(out, caption)
51
+ end
52
+
53
+ def emf_attributes(node)
45
54
  if emf = node.at(ns("./emf"))
46
55
  node["src"] = emf["src"]
47
56
  node["height"] ||= emf["height"]
@@ -49,11 +58,6 @@ module IsoDoc
49
58
  node["mimetype"] = "image/x-emf"
50
59
  node.children.remove
51
60
  end
52
- attrs = { src: imgsrc(node),
53
- height: node["height"], alt: node["alt"],
54
- title: node["title"], width: node["width"] }
55
- out.img **attr_code(attrs)
56
- image_title_parse(out, caption)
57
61
  end
58
62
 
59
63
  def xref_parse(node, out)
@@ -73,6 +77,23 @@ module IsoDoc
73
77
 
74
78
  url.sub(/#{File.extname(url)}$/, ".doc")
75
79
  end
80
+
81
+ def ruby_parse(node, out)
82
+ if r = node.at(ns("./rb[ruby]"))
83
+ double_ruby = r.at(ns("./ruby/rt")).remove
84
+ r.replace(r.at(ns("./ruby/rb")))
85
+ end
86
+ out.ruby do |e|
87
+ node.children.each { |n| parse(n, e) }
88
+ end
89
+ double_ruby and out << "(#{double_ruby.text})"
90
+ end
91
+
92
+ def rt_parse(node, out)
93
+ out.rt **{ style: "font-size: 6pt;" } do |e|
94
+ node.children.each { |n| parse(n, e) }
95
+ end
96
+ end
76
97
  end
77
98
  end
78
99
  end
data/lib/isodoc.rb CHANGED
@@ -10,6 +10,7 @@ require "html2doc"
10
10
  require "liquid"
11
11
  require "htmlentities"
12
12
  require "relaton-render"
13
+ require "vectory"
13
14
 
14
15
  require "isodoc/common"
15
16
  require "isodoc/convert"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: isodoc
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.3
4
+ version: 2.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-12-11 00:00:00.000000000 Z
11
+ date: 2024-01-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: html2doc
@@ -38,20 +38,6 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: 4.3.4
41
- - !ruby/object:Gem::Dependency
42
- name: emf2svg
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :runtime
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
41
  - !ruby/object:Gem::Dependency
56
42
  name: liquid
57
43
  requirement: !ruby/object:Gem::Requirement
@@ -178,6 +164,20 @@ dependencies:
178
164
  - - ">="
179
165
  - !ruby/object:Gem::Version
180
166
  version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: vectory
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: '0.6'
174
+ type: :runtime
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: '0.6'
181
181
  - !ruby/object:Gem::Dependency
182
182
  name: debug
183
183
  requirement: !ruby/object:Gem::Requirement
@@ -329,6 +329,7 @@ executables: []
329
329
  extensions: []
330
330
  extra_rdoc_files: []
331
331
  files:
332
+ - ".gitignore"
332
333
  - ".hound.yml"
333
334
  - ".oss-guides.rubocop.yml"
334
335
  - ".rubocop.yml"
@@ -410,6 +411,7 @@ files:
410
411
  - lib/isodoc/presentation_function/image.rb
411
412
  - lib/isodoc/presentation_function/inline.rb
412
413
  - lib/isodoc/presentation_function/math.rb
414
+ - lib/isodoc/presentation_function/metadata.rb
413
415
  - lib/isodoc/presentation_function/refs.rb
414
416
  - lib/isodoc/presentation_function/reqt.rb
415
417
  - lib/isodoc/presentation_function/section.rb