isodoc 2.7.3 → 2.8.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.
- 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
|