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 +4 -4
- data/.gitignore +1 -0
- data/Gemfile +1 -4
- data/isodoc.gemspec +1 -1
- data/lib/isodoc/base_style/all.css +8 -0
- data/lib/isodoc/base_style/reset.css +8 -0
- data/lib/isodoc/base_style/reset.scss +3 -0
- data/lib/isodoc/function/inline.rb +22 -2
- data/lib/isodoc/function/to_word_html.rb +3 -0
- data/lib/isodoc/html_function/html.rb +5 -0
- data/lib/isodoc/html_function/postprocess.rb +1 -1
- data/lib/isodoc/metadata.rb +6 -6
- data/lib/isodoc/metadata_date.rb +4 -4
- data/lib/isodoc/presentation_function/bibdata.rb +0 -156
- data/lib/isodoc/presentation_function/image.rb +38 -76
- data/lib/isodoc/presentation_function/inline.rb +16 -0
- data/lib/isodoc/presentation_function/math.rb +23 -10
- data/lib/isodoc/presentation_function/metadata.rb +184 -0
- data/lib/isodoc/presentation_xml_convert.rb +3 -0
- data/lib/isodoc/version.rb +1 -1
- data/lib/isodoc/word_function/inline.rb +26 -5
- data/lib/isodoc.rb +1 -0
- metadata +18 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7399ef0fe36a0b345f15af67b09daec6f68ed36ce2226af1469ace300d4e3fa9
|
4
|
+
data.tar.gz: 5d03fbb31c3a38b11ca47d34a1673326f68e7db9f577de5995944b4c1e77d1f1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3b5fb43c2a320525f7a1ce8e30d376ee687fd3295e16aa9336e846db7f93958d130e50b896216cbc651e4b33037840c97d9df2b6a9f578cc8dfef34a89bed098
|
7
|
+
data.tar.gz: 5b9bb3130bde935c9d420c02092d5c341ed2bdb18e7a5aaa2e718a58798c165c8130b7a036ff4619b19ef2655d41223a7f13b5c110bd0e5b2881a877920d0119
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
spec/assets/img.svg
|
data/Gemfile
CHANGED
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.
|
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
|
data/lib/isodoc/metadata.rb
CHANGED
@@ -106,12 +106,12 @@ module IsoDoc
|
|
106
106
|
end
|
107
107
|
|
108
108
|
def version(isoxml, _out)
|
109
|
-
set(:edition, isoxml
|
109
|
+
set(:edition, isoxml.at(ns("//bibdata/edition#{NOLANG}"))&.text)
|
110
110
|
set(:edition_display,
|
111
|
-
isoxml
|
112
|
-
set(:docyear, isoxml
|
113
|
-
set(:draft, isoxml
|
114
|
-
revdate = isoxml
|
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
|
122
|
+
main = isoxml.at(ns("//bibdata/title[@language='#{@lang}']"))&.text
|
123
123
|
set(:doctitle, main)
|
124
124
|
end
|
125
125
|
|
data/lib/isodoc/metadata_date.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
module IsoDoc
|
2
2
|
class Metadata
|
3
|
-
DATETYPES =
|
4
|
-
|
5
|
-
|
6
|
-
|
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:
|
96
|
-
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
|
-
|
101
|
-
|
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
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
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
|
-
|
126
|
+
|
114
127
|
svg_impose_height_attr(node)
|
115
|
-
|
116
|
-
if
|
117
|
-
|
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
|
-
|
121
|
-
|
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 {}
|
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
|
-
|
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
|
-
|
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
|
data/lib/isodoc/version.rb
CHANGED
@@ -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
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.
|
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:
|
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
|