isodoc 1.6.0 → 1.6.5
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/.github/workflows/rake.yml +2 -12
- data/.hound.yml +3 -1
- data/.rubocop.yml +4 -8
- data/Rakefile +2 -2
- data/bin/rspec +1 -2
- data/isodoc.gemspec +4 -3
- data/lib/isodoc-yaml/i18n-ar.yaml +152 -0
- data/lib/isodoc-yaml/i18n-de.yaml +149 -0
- data/lib/isodoc-yaml/i18n-en.yaml +1 -0
- data/lib/isodoc-yaml/i18n-es.yaml +151 -0
- data/lib/isodoc-yaml/i18n-fr.yaml +1 -0
- data/lib/isodoc-yaml/i18n-ru.yaml +154 -0
- data/lib/isodoc-yaml/i18n-zh-Hans.yaml +1 -0
- data/lib/isodoc.rb +0 -2
- data/lib/isodoc/common.rb +2 -0
- data/lib/isodoc/convert.rb +10 -4
- data/lib/isodoc/css.rb +30 -26
- data/lib/isodoc/function/blocks.rb +26 -8
- data/lib/isodoc/function/blocks_example_note.rb +2 -2
- data/lib/isodoc/function/cleanup.rb +53 -45
- data/lib/isodoc/function/form.rb +51 -0
- data/lib/isodoc/function/inline.rb +8 -7
- data/lib/isodoc/function/references.rb +71 -77
- data/lib/isodoc/function/section.rb +28 -16
- data/lib/isodoc/function/table.rb +22 -22
- data/lib/isodoc/function/terms.rb +6 -7
- data/lib/isodoc/function/to_word_html.rb +19 -25
- data/lib/isodoc/function/utils.rb +180 -160
- data/lib/isodoc/gem_tasks.rb +36 -38
- data/lib/isodoc/headlesshtml_convert.rb +8 -7
- data/lib/isodoc/html_convert.rb +10 -4
- data/lib/isodoc/html_function/comments.rb +14 -12
- data/lib/isodoc/html_function/footnotes.rb +14 -7
- data/lib/isodoc/html_function/form.rb +62 -0
- data/lib/isodoc/html_function/html.rb +30 -26
- data/lib/isodoc/html_function/postprocess.rb +191 -226
- data/lib/isodoc/html_function/postprocess_footnotes.rb +59 -0
- data/lib/isodoc/html_function/sectionsplit.rb +230 -0
- data/lib/isodoc/i18n.rb +33 -31
- data/lib/isodoc/metadata.rb +22 -20
- data/lib/isodoc/metadata_contributor.rb +31 -28
- data/lib/isodoc/pdf_convert.rb +11 -13
- data/lib/isodoc/presentation_function/bibdata.rb +54 -30
- data/lib/isodoc/presentation_function/block.rb +17 -8
- data/lib/isodoc/presentation_function/inline.rb +72 -120
- data/lib/isodoc/presentation_function/math.rb +84 -0
- data/lib/isodoc/presentation_function/section.rb +55 -19
- data/lib/isodoc/presentation_xml_convert.rb +2 -0
- data/lib/isodoc/sassc_importer.rb +1 -1
- data/lib/isodoc/version.rb +1 -1
- data/lib/isodoc/word_function/body.rb +28 -24
- data/lib/isodoc/word_function/footnotes.rb +22 -15
- data/lib/isodoc/word_function/postprocess.rb +50 -36
- data/lib/isodoc/xref.rb +11 -10
- data/lib/isodoc/xref/xref_counter.rb +32 -17
- data/lib/isodoc/xref/xref_gen.rb +33 -21
- data/lib/isodoc/xref/xref_gen_seq.rb +60 -35
- data/lib/isodoc/xref/xref_sect_gen.rb +37 -35
- data/spec/assets/scripts_override.html +3 -0
- data/spec/isodoc/blocks_spec.rb +2258 -2622
- data/spec/isodoc/cleanup_spec.rb +1103 -1107
- data/spec/isodoc/form_spec.rb +156 -0
- data/spec/isodoc/i18n_spec.rb +802 -917
- data/spec/isodoc/inline_spec.rb +1105 -921
- data/spec/isodoc/lists_spec.rb +316 -315
- data/spec/isodoc/metadata_spec.rb +384 -379
- data/spec/isodoc/postproc_spec.rb +1783 -1549
- data/spec/isodoc/presentation_xml_spec.rb +355 -278
- data/spec/isodoc/ref_spec.rb +718 -723
- data/spec/isodoc/section_spec.rb +216 -199
- data/spec/isodoc/sectionsplit_spec.rb +190 -0
- data/spec/isodoc/table_spec.rb +41 -42
- data/spec/isodoc/terms_spec.rb +84 -84
- data/spec/isodoc/xref_spec.rb +1024 -930
- metadata +33 -7
@@ -0,0 +1,84 @@
|
|
1
|
+
require "twitter_cldr"
|
2
|
+
require "bigdecimal"
|
3
|
+
|
4
|
+
module IsoDoc
|
5
|
+
class PresentationXMLConvert < ::IsoDoc::Convert
|
6
|
+
MATHML = { "m" => "http://www.w3.org/1998/Math/MathML" }.freeze
|
7
|
+
|
8
|
+
def mathml(docxml)
|
9
|
+
locale = twitter_cldr_localiser
|
10
|
+
docxml.xpath("//m:math", MATHML).each do |f|
|
11
|
+
mathml1(f, locale)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# symbols is merged into
|
16
|
+
# TwitterCldr::DataReaders::NumberDataReader.new(locale).symbols
|
17
|
+
def localize_maths(node, locale)
|
18
|
+
node.xpath(".//m:mn", MATHML).each do |x|
|
19
|
+
num = BigDecimal(x.text)
|
20
|
+
precision = /\./.match?(x.text) ? x.text.sub(/^.*\./, "").size : 0
|
21
|
+
x.children = localized_number(num, locale, precision)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# By itself twitter-cldr does not support fraction part digits grouping
|
26
|
+
# and custom delimeter, will decorate fraction part manually
|
27
|
+
def localized_number(num, locale, precision)
|
28
|
+
localized = localized_number1(num, locale, precision)
|
29
|
+
twitter_cldr_reader_symbols = twitter_cldr_reader(locale)
|
30
|
+
return localized unless twitter_cldr_reader_symbols[:decimal]
|
31
|
+
|
32
|
+
integer, fraction = localized.split(twitter_cldr_reader_symbols[:decimal])
|
33
|
+
return localized if fraction.nil? || fraction.length.zero?
|
34
|
+
|
35
|
+
[integer, decorate_fraction_part(fraction, locale)]
|
36
|
+
.join(twitter_cldr_reader_symbols[:decimal])
|
37
|
+
end
|
38
|
+
|
39
|
+
def localized_number1(num, locale, precision)
|
40
|
+
if precision.zero?
|
41
|
+
num.localize(locale).to_s
|
42
|
+
else
|
43
|
+
num.localize(locale).to_decimal.to_s(precision: precision)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def decorate_fraction_part(fract, locale)
|
48
|
+
result = []
|
49
|
+
twitter_cldr_reader_symbols = twitter_cldr_reader(locale)
|
50
|
+
fract = fract.slice(0..(twitter_cldr_reader_symbols[:precision] || -1))
|
51
|
+
fr_group_digits = twitter_cldr_reader_symbols[:fraction_group_digits] || 1
|
52
|
+
until fract.empty?
|
53
|
+
result.push(fract.slice!(0, fr_group_digits))
|
54
|
+
end
|
55
|
+
result.join(twitter_cldr_reader_symbols[:fraction_group].to_s)
|
56
|
+
end
|
57
|
+
|
58
|
+
def twitter_cldr_localiser_symbols
|
59
|
+
{}
|
60
|
+
end
|
61
|
+
|
62
|
+
def twitter_cldr_reader(locale)
|
63
|
+
num = TwitterCldr::DataReaders::NumberDataReader.new(locale)
|
64
|
+
num.symbols.merge!(twitter_cldr_localiser_symbols)
|
65
|
+
end
|
66
|
+
|
67
|
+
def twitter_cldr_localiser
|
68
|
+
locale = TwitterCldr.supported_locale?(@lang.to_sym) ? @lang.to_sym : :en
|
69
|
+
twitter_cldr_reader(locale)
|
70
|
+
locale
|
71
|
+
end
|
72
|
+
|
73
|
+
def mathml1(node, locale)
|
74
|
+
localize_maths(node, locale)
|
75
|
+
return unless node.elements.size == 1 && node.elements.first.name == "mn"
|
76
|
+
|
77
|
+
if node.parent.name == "stem"
|
78
|
+
node.parent.replace(node.at("./m:mn", MATHML).children)
|
79
|
+
else
|
80
|
+
node.replace(node.at("./m:mn", MATHML).children)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -2,20 +2,21 @@ module IsoDoc
|
|
2
2
|
class PresentationXMLConvert < ::IsoDoc::Convert
|
3
3
|
def clause(docxml)
|
4
4
|
docxml.xpath(ns("//clause | "\
|
5
|
-
"//terms | //definitions | //references"))
|
6
|
-
|
5
|
+
"//terms | //definitions | //references"))
|
6
|
+
.each do |f|
|
7
7
|
clause1(f)
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
def clause1(
|
12
|
-
level = @xrefs.anchor(
|
13
|
-
t =
|
14
|
-
return if !
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
11
|
+
def clause1(elem)
|
12
|
+
level = @xrefs.anchor(elem["id"], :level, false) || "1"
|
13
|
+
t = elem.at(ns("./title")) and t["depth"] = level
|
14
|
+
return if !elem.ancestors("boilerplate").empty? ||
|
15
|
+
@suppressheadingnumbers || elem["unnumbered"]
|
16
|
+
|
17
|
+
lbl = @xrefs.anchor(elem["id"], :label,
|
18
|
+
elem.parent.name != "sections") or return
|
19
|
+
prefix_name(elem, "<tab/>", "#{lbl}#{clausedelim}", "title")
|
19
20
|
end
|
20
21
|
|
21
22
|
def annex(docxml)
|
@@ -24,12 +25,12 @@ module IsoDoc
|
|
24
25
|
end
|
25
26
|
end
|
26
27
|
|
27
|
-
def annex1(
|
28
|
-
lbl = @xrefs.anchor(
|
29
|
-
if t =
|
28
|
+
def annex1(elem)
|
29
|
+
lbl = @xrefs.anchor(elem["id"], :label)
|
30
|
+
if t = elem.at(ns("./title"))
|
30
31
|
t.children = "<strong>#{t.children.to_xml}</strong>"
|
31
32
|
end
|
32
|
-
prefix_name(
|
33
|
+
prefix_name(elem, "<br/><br/>", lbl, "title")
|
33
34
|
end
|
34
35
|
|
35
36
|
def term(docxml)
|
@@ -38,18 +39,53 @@ module IsoDoc
|
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
41
|
-
def term1(
|
42
|
-
lbl = @xrefs.get[
|
43
|
-
prefix_name(
|
42
|
+
def term1(elem)
|
43
|
+
lbl = @xrefs.get[elem["id"]][:label] or return
|
44
|
+
prefix_name(elem, "", "#{lbl}#{clausedelim}", "name")
|
44
45
|
end
|
45
46
|
|
46
47
|
def references(docxml)
|
48
|
+
docid_prefixes(docxml)
|
49
|
+
end
|
50
|
+
|
51
|
+
def docid_prefixes(docxml)
|
52
|
+
docxml.xpath(ns("//references/bibitem/docidentifier")).each do |i|
|
53
|
+
i.children = @xrefs.klass.docid_prefix(i["type"], i.text)
|
54
|
+
end
|
47
55
|
end
|
48
56
|
|
49
57
|
def index(docxml)
|
50
|
-
docxml.xpath(ns("//index | //index-xref | //indexsect")).each
|
51
|
-
|
58
|
+
docxml.xpath(ns("//index | //index-xref | //indexsect")).each(&:remove)
|
59
|
+
end
|
60
|
+
|
61
|
+
def display_order_at(docxml, xpath, idx)
|
62
|
+
return idx unless c = docxml.at(ns(xpath))
|
63
|
+
|
64
|
+
idx += 1
|
65
|
+
c["displayorder"] = idx
|
66
|
+
idx
|
67
|
+
end
|
68
|
+
|
69
|
+
def display_order_xpath(docxml, xpath, idx)
|
70
|
+
docxml.xpath(ns(xpath)).each do |c|
|
71
|
+
idx += 1
|
72
|
+
c["displayorder"] = idx
|
52
73
|
end
|
74
|
+
idx
|
75
|
+
end
|
76
|
+
|
77
|
+
def display_order(docxml)
|
78
|
+
i = 0
|
79
|
+
i = display_order_xpath(docxml, "//preface/*", i)
|
80
|
+
i = display_order_at(docxml, "//clause[@type = 'scope']", i)
|
81
|
+
i = display_order_at(docxml, @xrefs.klass.norm_ref_xpath, i)
|
82
|
+
i = display_order_at(docxml, "//sections/terms | "\
|
83
|
+
"//sections/clause[descendant::terms]", i)
|
84
|
+
i = display_order_at(docxml, "//sections/definitions", i)
|
85
|
+
i = display_order_xpath(docxml, @xrefs.klass.middle_clause(docxml), i)
|
86
|
+
i = display_order_xpath(docxml, "//annex", i)
|
87
|
+
i = display_order_xpath(docxml, @xrefs.klass.bibliography_xpath, i)
|
88
|
+
display_order_xpath(docxml, "//indexsect", i)
|
53
89
|
end
|
54
90
|
end
|
55
91
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require_relative "presentation_function/block"
|
2
2
|
require_relative "presentation_function/inline"
|
3
|
+
require_relative "presentation_function/math"
|
3
4
|
require_relative "presentation_function/section"
|
4
5
|
require_relative "presentation_function/bibdata"
|
5
6
|
|
@@ -27,6 +28,7 @@ module IsoDoc
|
|
27
28
|
end
|
28
29
|
|
29
30
|
def section(docxml)
|
31
|
+
display_order docxml
|
30
32
|
clause docxml
|
31
33
|
annex docxml
|
32
34
|
term docxml
|
data/lib/isodoc/version.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require_relative "./table
|
2
|
-
require_relative "./inline
|
1
|
+
require_relative "./table"
|
2
|
+
require_relative "./inline"
|
3
3
|
|
4
4
|
module IsoDoc::WordFunction
|
5
5
|
module Body
|
@@ -71,8 +71,8 @@ module IsoDoc::WordFunction
|
|
71
71
|
node.xpath(ns("./note")).each { |n| parse(n, out) }
|
72
72
|
end
|
73
73
|
|
74
|
-
WORD_DT_ATTRS = {class: @note ? "Note" : nil, align: "left",
|
75
|
-
|
74
|
+
WORD_DT_ATTRS = { class: @note ? "Note" : nil, align: "left",
|
75
|
+
style: "margin-left:0pt;text-align:left;" }.freeze
|
76
76
|
|
77
77
|
def dt_parse(dt, term)
|
78
78
|
term.p **attr_code(WORD_DT_ATTRS) do |p|
|
@@ -102,6 +102,7 @@ module IsoDoc::WordFunction
|
|
102
102
|
|
103
103
|
def dl_parse_notes(node, v)
|
104
104
|
return if node.elements.reject { |n| dt_dd? n }.empty?
|
105
|
+
|
105
106
|
v.tr do |tr|
|
106
107
|
tr.td **{ colspan: 2 } do |td|
|
107
108
|
node.elements.reject { |n| dt_dd? n }.each { |n| parse(n, td) }
|
@@ -109,19 +110,19 @@ module IsoDoc::WordFunction
|
|
109
110
|
end
|
110
111
|
end
|
111
112
|
|
112
|
-
def figure_get_or_make_dl(
|
113
|
-
dl =
|
113
|
+
def figure_get_or_make_dl(node)
|
114
|
+
dl = node.at(".//table[@class = 'dl']")
|
114
115
|
if dl.nil?
|
115
|
-
|
116
|
-
dl =
|
116
|
+
node.add_child("<p><b>#{@i18n.key}</b></p><table class='dl'></table>")
|
117
|
+
dl = node.at(".//table[@class = 'dl']")
|
117
118
|
end
|
118
119
|
dl
|
119
120
|
end
|
120
121
|
|
121
|
-
def figure_aside_process(
|
122
|
+
def figure_aside_process(fig, aside, key)
|
122
123
|
# get rid of footnote link, it is in diagram
|
123
|
-
|
124
|
-
fnref =
|
124
|
+
fig&.at("./a[@class='TableFootnoteRef']")&.remove
|
125
|
+
fnref = fig.at(".//span[@class='TableFootnoteRef']/..")
|
125
126
|
tr = key.add_child("<tr></tr>").first
|
126
127
|
dt = tr.add_child("<td valign='top' align='left'></td>").first
|
127
128
|
dd = tr.add_child("<td valign='top'></td>").first
|
@@ -174,31 +175,32 @@ module IsoDoc::WordFunction
|
|
174
175
|
attrs[:align] = node["align"] unless node["align"] == "justify"
|
175
176
|
attrs[:style] += "text-align:#{node['align']};"
|
176
177
|
end
|
177
|
-
attrs[:style] +=
|
178
|
+
attrs[:style] += keep_style(node).to_s
|
178
179
|
attrs[:style] = nil if attrs[:style].empty?
|
179
180
|
attrs
|
180
181
|
end
|
181
182
|
|
182
183
|
def example_table_attr(node)
|
183
184
|
super.merge({
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
185
|
+
style: "mso-table-lspace:15.0cm;margin-left:423.0pt;"\
|
186
|
+
"mso-table-rspace:15.0cm;margin-right:423.0pt;"\
|
187
|
+
"mso-table-anchor-horizontal:column;"\
|
188
|
+
"mso-table-overlap:never;border-collapse:collapse;"\
|
189
|
+
"#{keep_style(node)}",
|
190
|
+
})
|
190
191
|
end
|
191
192
|
|
192
|
-
def formula_where(
|
193
|
-
return unless
|
193
|
+
def formula_where(deflist, out)
|
194
|
+
return unless deflist
|
195
|
+
|
194
196
|
out.p { |p| p << @i18n.where }
|
195
|
-
parse(
|
197
|
+
parse(deflist, out)
|
196
198
|
out.parent.at("./table")["class"] = "formula_dl"
|
197
199
|
end
|
198
200
|
|
199
201
|
def formula_parse1(node, out)
|
200
202
|
out.div **attr_code(class: "formula") do |div|
|
201
|
-
div.p do |
|
203
|
+
div.p do |_p|
|
202
204
|
parse(node.at(ns("./stem")), div)
|
203
205
|
insert_tab(div, 1)
|
204
206
|
if lbl = node&.at(ns("./name"))&.text
|
@@ -209,7 +211,7 @@ module IsoDoc::WordFunction
|
|
209
211
|
end
|
210
212
|
|
211
213
|
def li_parse(node, out)
|
212
|
-
out.li
|
214
|
+
out.li **attr_code(id: node["id"]) do |li|
|
213
215
|
if node["uncheckedcheckbox"] == "true"
|
214
216
|
li << '<span class="zzMoveToFollowing">☐ </span>'
|
215
217
|
elsif node["checkedcheckbox"] == "true"
|
@@ -220,7 +222,9 @@ module IsoDoc::WordFunction
|
|
220
222
|
end
|
221
223
|
|
222
224
|
def suffix_url(url)
|
223
|
-
return url if %r{^
|
225
|
+
return url if %r{^https?://}.match?(url)
|
226
|
+
return url unless File.extname(url).empty?
|
227
|
+
|
224
228
|
url.sub(/#{File.extname(url)}$/, ".doc")
|
225
229
|
end
|
226
230
|
end
|
@@ -2,7 +2,7 @@ module IsoDoc::WordFunction
|
|
2
2
|
module Footnotes
|
3
3
|
def bookmarkid
|
4
4
|
ret = "X"
|
5
|
-
until !@bookmarks_allocated[ret]
|
5
|
+
until !@bookmarks_allocated[ret]
|
6
6
|
ret = Random.rand(1000000000)
|
7
7
|
end
|
8
8
|
@bookmarks_allocated[ret] = true
|
@@ -11,6 +11,7 @@ module IsoDoc::WordFunction
|
|
11
11
|
|
12
12
|
def footnotes(div)
|
13
13
|
return if @footnotes.empty?
|
14
|
+
|
14
15
|
@footnotes.each { |fn| div.parent << fn }
|
15
16
|
end
|
16
17
|
|
@@ -52,6 +53,7 @@ module IsoDoc::WordFunction
|
|
52
53
|
def get_table_ancestor_id(node)
|
53
54
|
table = node.ancestors("table") || node.ancestors("figure")
|
54
55
|
return UUIDTools::UUID.random_create.to_s if table.empty?
|
56
|
+
|
55
57
|
table.last["id"]
|
56
58
|
end
|
57
59
|
|
@@ -61,30 +63,34 @@ module IsoDoc::WordFunction
|
|
61
63
|
make_table_footnote_link(out, tid + fn, fn)
|
62
64
|
# do not output footnote text if we have already seen it for this table
|
63
65
|
return if @seen_footnote.include?(tid + fn)
|
66
|
+
|
64
67
|
@in_footnote = true
|
65
68
|
out.aside { |a| a << make_table_footnote_text(node, tid + fn, fn) }
|
66
69
|
@in_footnote = false
|
67
70
|
@seen_footnote << (tid + fn)
|
68
71
|
end
|
69
72
|
|
70
|
-
def seen_footnote_parse(
|
71
|
-
out.span **{style: "mso-element:field-begin"}
|
72
|
-
out << " NOTEREF _Ref#{@fn_bookmarks[
|
73
|
-
out.span **{style: "mso-element:field-separator"}
|
74
|
-
out.span **{class: "MsoFootnoteReference"} do |s|
|
75
|
-
s <<
|
73
|
+
def seen_footnote_parse(_node, out, footnote)
|
74
|
+
out.span **{ style: "mso-element:field-begin" }
|
75
|
+
out << " NOTEREF _Ref#{@fn_bookmarks[footnote]} \\f \\h"
|
76
|
+
out.span **{ style: "mso-element:field-separator" }
|
77
|
+
out.span **{ class: "MsoFootnoteReference" } do |s|
|
78
|
+
s << footnote
|
76
79
|
end
|
77
|
-
out.span **{style: "mso-element:field-end"}
|
80
|
+
out.span **{ style: "mso-element:field-end" }
|
78
81
|
end
|
79
82
|
|
80
83
|
def footnote_parse(node, out)
|
81
84
|
return table_footnote_parse(node, out) if (@in_table || @in_figure) &&
|
82
|
-
!node.ancestors.map {|m| m.name }.include?("name")
|
85
|
+
!node.ancestors.map { |m| m.name }.include?("name")
|
86
|
+
|
83
87
|
fn = node["reference"] || UUIDTools::UUID.random_create.to_s
|
84
88
|
return seen_footnote_parse(node, out, fn) if @seen_footnote.include?(fn)
|
89
|
+
|
85
90
|
@fn_bookmarks[fn] = bookmarkid
|
86
|
-
out.span **{style: "mso-bookmark:_Ref#{@fn_bookmarks[fn]}"} do |s|
|
87
|
-
s.a **{ "class": "FootnoteRef", "epub:type": "footnote",
|
91
|
+
out.span **{ style: "mso-bookmark:_Ref#{@fn_bookmarks[fn]}" } do |s|
|
92
|
+
s.a **{ "class": "FootnoteRef", "epub:type": "footnote",
|
93
|
+
href: "#ftn#{fn}" } do |a|
|
88
94
|
a.sup { |sup| sup << fn }
|
89
95
|
end
|
90
96
|
end
|
@@ -94,12 +100,13 @@ module IsoDoc::WordFunction
|
|
94
100
|
@seen_footnote << fn
|
95
101
|
end
|
96
102
|
|
97
|
-
def make_footnote(node,
|
98
|
-
return if @seen_footnote.include?(
|
103
|
+
def make_footnote(node, footnote)
|
104
|
+
return if @seen_footnote.include?(footnote)
|
105
|
+
|
99
106
|
@in_footnote = true
|
100
|
-
@footnotes << make_generic_footnote_text(node,
|
107
|
+
@footnotes << make_generic_footnote_text(node, footnote)
|
101
108
|
@in_footnote = false
|
102
|
-
@seen_footnote <<
|
109
|
+
@seen_footnote << footnote
|
103
110
|
end
|
104
111
|
end
|
105
112
|
end
|
@@ -1,17 +1,17 @@
|
|
1
1
|
require "fileutils"
|
2
|
-
require_relative "./postprocess_cover
|
2
|
+
require_relative "./postprocess_cover"
|
3
3
|
|
4
4
|
module IsoDoc::WordFunction
|
5
5
|
module Postprocess
|
6
6
|
# add namespaces for Word fragments
|
7
7
|
WORD_NOKOHEAD = <<~HERE.freeze
|
8
|
-
|
9
|
-
|
10
|
-
xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office"
|
11
|
-
xmlns:w="urn:schemas-microsoft-com:office:word"
|
12
|
-
xmlns:m="http://schemas.microsoft.com/office/2004/12/omml">
|
13
|
-
|
14
|
-
|
8
|
+
<!DOCTYPE html SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
9
|
+
<html xmlns="http://www.w3.org/1999/xhtml"
|
10
|
+
xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office"
|
11
|
+
xmlns:w="urn:schemas-microsoft-com:office:word"
|
12
|
+
xmlns:m="http://schemas.microsoft.com/office/2004/12/omml">
|
13
|
+
<head> <title></title> <meta charset="UTF-8" /> </head>
|
14
|
+
<body> </body> </html>
|
15
15
|
HERE
|
16
16
|
|
17
17
|
def to_word_xhtml_fragment(xml)
|
@@ -22,7 +22,8 @@ xmlns:m="http://schemas.microsoft.com/office/2004/12/omml">
|
|
22
22
|
def table_note_cleanup(docxml)
|
23
23
|
super
|
24
24
|
# preempt html2doc putting MsoNormal there
|
25
|
-
docxml.xpath("//p[not(self::*[@class])][ancestor::*[@class = 'Note']]")
|
25
|
+
docxml.xpath("//p[not(self::*[@class])][ancestor::*[@class = 'Note']]")
|
26
|
+
.each do |p|
|
26
27
|
p["class"] = "Note"
|
27
28
|
end
|
28
29
|
end
|
@@ -38,16 +39,21 @@ xmlns:m="http://schemas.microsoft.com/office/2004/12/omml">
|
|
38
39
|
def toWord(result, filename, dir, header)
|
39
40
|
result = from_xhtml(word_cleanup(to_xhtml(result)))
|
40
41
|
@wordstylesheet = wordstylesheet_update
|
41
|
-
Html2Doc.process(
|
42
|
-
|
43
|
-
|
44
|
-
|
42
|
+
Html2Doc.process(
|
43
|
+
result,
|
44
|
+
filename: filename,
|
45
|
+
stylesheet: @wordstylesheet&.path,
|
46
|
+
header_file: header&.path, dir: dir,
|
47
|
+
asciimathdelims: [@openmathdelim, @closemathdelim],
|
48
|
+
liststyles: { ul: @ulstyle, ol: @olstyle }
|
49
|
+
)
|
45
50
|
header&.unlink
|
46
|
-
@wordstylesheet
|
51
|
+
@wordstylesheet.unlink if @wordstylesheet.is_a?(Tempfile)
|
47
52
|
end
|
48
53
|
|
49
|
-
def wordstylesheet_update
|
54
|
+
def wordstylesheet_update
|
50
55
|
return if @wordstylesheet.nil?
|
56
|
+
|
51
57
|
f = File.open(@wordstylesheet.path, "a")
|
52
58
|
@landscapestyle.empty? or f.write(@landscapestyle)
|
53
59
|
if @wordstylesheet_override && @wordstylesheet
|
@@ -62,7 +68,8 @@ xmlns:m="http://schemas.microsoft.com/office/2004/12/omml">
|
|
62
68
|
|
63
69
|
def word_admonition_images(docxml)
|
64
70
|
docxml.xpath("//div[@class = 'Admonition']//img").each do |i|
|
65
|
-
i["width"], i["height"] =
|
71
|
+
i["width"], i["height"] =
|
72
|
+
Html2Doc.image_resize(i, image_localfile(i), @maxheight, 300)
|
66
73
|
end
|
67
74
|
end
|
68
75
|
|
@@ -88,21 +95,23 @@ xmlns:m="http://schemas.microsoft.com/office/2004/12/omml">
|
|
88
95
|
cells2d = {}
|
89
96
|
docxml.xpath("//table[colgroup]").each do |t|
|
90
97
|
w = colgroup_widths(t)
|
91
|
-
t.xpath(".//tr").each_with_index { |
|
98
|
+
t.xpath(".//tr").each_with_index { |_tr, r| cells2d[r] = {} }
|
92
99
|
t.xpath(".//tr").each_with_index do |tr, r|
|
93
|
-
tr.xpath("./td | ./th").each_with_index do |td,
|
100
|
+
tr.xpath("./td | ./th").each_with_index do |td, _i|
|
94
101
|
x = 0
|
95
102
|
rs = td&.attr("rowspan")&.to_i || 1
|
96
103
|
cs = td&.attr("colspan")&.to_i || 1
|
97
|
-
while cells2d[r][x]
|
98
|
-
x += 1
|
104
|
+
while cells2d[r][x]
|
105
|
+
x += 1
|
99
106
|
end
|
100
|
-
|
101
|
-
|
107
|
+
(r..(r + rs - 1)).each do |y2|
|
108
|
+
(x..(x + cs - 1)).each do |x2|
|
102
109
|
cells2d[y2][x2] = 1
|
103
110
|
end
|
104
111
|
end
|
105
|
-
width = (x..(x+cs-1)).each_with_object({width: 0})
|
112
|
+
width = (x..(x + cs - 1)).each_with_object({ width: 0 }) do |z, m|
|
113
|
+
m[:width] += w[z]
|
114
|
+
end
|
106
115
|
td["width"] = "#{width[:width]}%"
|
107
116
|
x += cs
|
108
117
|
end
|
@@ -111,8 +120,8 @@ xmlns:m="http://schemas.microsoft.com/office/2004/12/omml">
|
|
111
120
|
end
|
112
121
|
|
113
122
|
# assume percentages
|
114
|
-
def colgroup_widths(
|
115
|
-
|
123
|
+
def colgroup_widths(table)
|
124
|
+
table.xpath("./colgroup/col").each_with_object([]) do |c, m|
|
116
125
|
m << c["width"].sub(/%$/, "").to_f
|
117
126
|
end
|
118
127
|
end
|
@@ -127,12 +136,13 @@ xmlns:m="http://schemas.microsoft.com/office/2004/12/omml">
|
|
127
136
|
|
128
137
|
def style_update(node, css)
|
129
138
|
return unless node
|
130
|
-
|
139
|
+
|
140
|
+
node["style"] = node["style"] ? node["style"].sub(/;?$/, ";#{css}") : css
|
131
141
|
end
|
132
142
|
|
133
143
|
def word_image_caption(docxml)
|
134
|
-
docxml.xpath("//p[@class = 'FigureTitle' or @class = 'SourceTitle']")
|
135
|
-
each do |t|
|
144
|
+
docxml.xpath("//p[@class = 'FigureTitle' or @class = 'SourceTitle']")
|
145
|
+
.each do |t|
|
136
146
|
if t&.previous_element&.name == "img"
|
137
147
|
img = t.previous_element
|
138
148
|
t.previous_element.swap("<p class=\'figure\'>#{img.to_xml}</p>")
|
@@ -150,7 +160,8 @@ xmlns:m="http://schemas.microsoft.com/office/2004/12/omml">
|
|
150
160
|
xpath.each do |list|
|
151
161
|
(list.xpath(".//li") - list.xpath(".//ol//li | .//ul//li")).each do |l|
|
152
162
|
l.xpath("./p | ./div | ./table").each_with_index do |p, i|
|
153
|
-
next if i
|
163
|
+
next if i.zero?
|
164
|
+
|
154
165
|
p.wrap(%{<div class="ListContLevel#{lvl}"/>})
|
155
166
|
end
|
156
167
|
list_add(l.xpath(".//ul") - l.xpath(".//ul//ul | .//ol//ul"), lvl + 1)
|
@@ -162,20 +173,21 @@ xmlns:m="http://schemas.microsoft.com/office/2004/12/omml">
|
|
162
173
|
def word_table_align(docxml)
|
163
174
|
docxml.xpath("//td[@align]/p | //th[@align]/p").each do |p|
|
164
175
|
next if p["align"]
|
165
|
-
|
176
|
+
|
177
|
+
style_update(p, "text-align: #{p.parent['align']}")
|
166
178
|
end
|
167
179
|
end
|
168
180
|
|
169
181
|
def word_table_separator(docxml)
|
170
182
|
docxml.xpath("//p[@class = 'TableTitle']").each do |t|
|
171
183
|
next unless t.children.empty?
|
184
|
+
|
172
185
|
t["style"] = t["style"].sub(/;?$/, ";font-size:0pt;")
|
173
186
|
t.children = " "
|
174
187
|
end
|
175
188
|
end
|
176
189
|
|
177
|
-
def word_annex_cleanup(docxml)
|
178
|
-
end
|
190
|
+
def word_annex_cleanup(docxml); end
|
179
191
|
|
180
192
|
def word_example_cleanup(docxml)
|
181
193
|
docxml.xpath("//div[@class = 'example']//p[not(@class)]").each do |p|
|
@@ -194,19 +206,21 @@ xmlns:m="http://schemas.microsoft.com/office/2004/12/omml">
|
|
194
206
|
docxml.xpath("//div[p/br]").each do |d|
|
195
207
|
/^WordSection\d+_\d+$/.match(d["class"]) or next
|
196
208
|
d.elements[0].name == "p" && !d.elements[0].elements.empty? or next
|
197
|
-
d.elements[0].elements[0].name == "br" &&
|
198
|
-
"
|
209
|
+
d.elements[0].elements[0].name == "br" &&
|
210
|
+
d.elements[0].elements[0]["style"] ==
|
211
|
+
"mso-special-character:line-break;page-break-before:always" or next
|
199
212
|
d.elements[0].remove
|
200
213
|
end
|
201
214
|
end
|
202
215
|
|
203
216
|
def word_footnote_format(docxml)
|
204
|
-
# the content is in a[@epub:type = 'footnote']//sup, but in Word,
|
217
|
+
# the content is in a[@epub:type = 'footnote']//sup, but in Word,
|
205
218
|
# we need to inject content around the autonumbered footnote reference
|
206
219
|
docxml.xpath("//a[@epub:type = 'footnote']").each do |x|
|
207
220
|
footnote_reference_format(x)
|
208
221
|
end
|
209
|
-
docxml.xpath("//a[@class = 'TableFootnoteRef'] |
|
222
|
+
docxml.xpath("//a[@class = 'TableFootnoteRef'] | "\
|
223
|
+
"//span[@class = 'TableFootnoteRef']").each do |x|
|
210
224
|
table_footnote_reference_format(x)
|
211
225
|
end
|
212
226
|
docxml
|