isodoc 0.5.5 → 0.5.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CODE_OF_CONDUCT.md +46 -0
- data/LICENSE +25 -0
- data/README.adoc +1 -1
- data/Rakefile +6 -0
- data/isodoc.gemspec +1 -0
- data/lib/isodoc.rb +4 -95
- data/lib/isodoc/cleanup.rb +14 -10
- data/lib/isodoc/{notes.rb → comments.rb} +0 -73
- data/lib/isodoc/convert.rb +97 -0
- data/lib/isodoc/footnotes.rb +74 -0
- data/lib/isodoc/html.rb +41 -4
- data/lib/isodoc/i18n-en.yaml +1 -0
- data/lib/isodoc/i18n-fr.yaml +1 -0
- data/lib/isodoc/i18n-zh-Hans.yaml +1 -0
- data/lib/isodoc/i18n.rb +1 -0
- data/lib/isodoc/inline.rb +4 -12
- data/lib/isodoc/iso2wordhtml.rb +26 -13
- data/lib/isodoc/metadata.rb +23 -10
- data/lib/isodoc/references.rb +20 -22
- data/lib/isodoc/section.rb +4 -3
- data/lib/isodoc/table.rb +0 -2
- data/lib/isodoc/terms.rb +2 -13
- data/lib/isodoc/utils.rb +24 -3
- data/lib/isodoc/version.rb +1 -1
- data/lib/isodoc/wordconvert/comments.rb +155 -0
- data/lib/isodoc/wordconvert/convert.rb +31 -0
- data/lib/isodoc/wordconvert/footnotes.rb +80 -0
- data/lib/isodoc/wordconvert/wordconvertmodule.rb +212 -0
- data/lib/isodoc/xref_gen.rb +50 -79
- data/lib/isodoc/xref_sect_gen.rb +82 -0
- data/spec/assets/header.html +7 -0
- data/spec/assets/html.css +2 -0
- data/spec/assets/htmlcover.html +4 -0
- data/spec/assets/htmlintro.html +5 -0
- data/spec/assets/i18n.yaml +2 -0
- data/spec/assets/iso.xml +8 -0
- data/spec/assets/rice_image1.png +0 -0
- data/spec/assets/std.css +2 -0
- data/spec/assets/word.css +2 -0
- data/spec/assets/wordcover.html +3 -0
- data/spec/assets/wordintro.html +4 -0
- data/spec/isodoc/blocks_spec.rb +130 -47
- data/spec/isodoc/cleanup_spec.rb +693 -0
- data/spec/isodoc/footnotes_spec.rb +282 -0
- data/spec/isodoc/i18n_spec.rb +662 -0
- data/spec/isodoc/inline_spec.rb +344 -0
- data/spec/isodoc/lists_spec.rb +81 -18
- data/spec/isodoc/metadata_spec.rb +141 -0
- data/spec/isodoc/postproc_spec.rb +444 -0
- data/spec/isodoc/ref_spec.rb +158 -0
- data/spec/isodoc/section_spec.rb +275 -112
- data/spec/isodoc/table_spec.rb +146 -8
- data/spec/isodoc/terms_spec.rb +118 -0
- data/spec/isodoc/xref_spec.rb +490 -114
- metadata +46 -4
- data/lib/isodoc/postprocessing.rb +0 -176
data/lib/isodoc/section.rb
CHANGED
@@ -3,7 +3,7 @@ module IsoDoc
|
|
3
3
|
def inline_header_title(out, node, c1)
|
4
4
|
out.span **{ class: "zzMoveToFollowing" } do |s|
|
5
5
|
s.b do |b|
|
6
|
-
b << "#{get_anchors[node['id']][:label]}. #{c1.
|
6
|
+
b << "#{get_anchors[node['id']][:label]}. #{c1.content} "
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
@@ -13,7 +13,8 @@ module IsoDoc
|
|
13
13
|
inline_header_title(out, node, c1)
|
14
14
|
else
|
15
15
|
div.send "h#{get_anchors[node['id']][:level]}" do |h|
|
16
|
-
h << "#{get_anchors[node['id']][:label]}.
|
16
|
+
h << "#{get_anchors[node['id']][:label]}. "
|
17
|
+
c1.children.each { |c2| parse(c2, h) }
|
17
18
|
end
|
18
19
|
end
|
19
20
|
end
|
@@ -49,7 +50,7 @@ module IsoDoc
|
|
49
50
|
c.elements.each do |c1|
|
50
51
|
if c1.name == "title"
|
51
52
|
clause_name("#{get_anchors[c['id']][:label]}.",
|
52
|
-
c1.
|
53
|
+
c1.content, s, nil)
|
53
54
|
else
|
54
55
|
parse(c1, s)
|
55
56
|
end
|
data/lib/isodoc/table.rb
CHANGED
@@ -76,9 +76,7 @@ module IsoDoc
|
|
76
76
|
rowmax = td["rowspan"] ? row + td["rowspan"].to_i - 1 : row
|
77
77
|
style += <<~STYLE
|
78
78
|
border-top:#{row.zero? ? "#{SW} 1.5pt;" : 'none;'}
|
79
|
-
mso-border-top-alt:#{row.zero? ? "#{SW} 1.5pt;" : 'none;'}
|
80
79
|
border-bottom:#{SW} #{rowmax == totalrows ? '1.5' : '1.0'}pt;
|
81
|
-
mso-border-bottom-alt:#{SW} #{rowmax == totalrows ? '1.5' : '1.0'}pt;
|
82
80
|
STYLE
|
83
81
|
{ rowspan: td["rowspan"], colspan: td["colspan"],
|
84
82
|
align: td["align"], style: style.gsub(/\n/, "") }
|
data/lib/isodoc/terms.rb
CHANGED
@@ -24,7 +24,7 @@ module IsoDoc
|
|
24
24
|
out.p **{ class: "Terms" } { |p| p << node.text }
|
25
25
|
end
|
26
26
|
|
27
|
-
def para_then_remainder(first, node, p)
|
27
|
+
def para_then_remainder(first, node, p, div)
|
28
28
|
if first.name == "p"
|
29
29
|
first.children.each { |n| parse(n, p) }
|
30
30
|
node.elements.drop(1).each { |n| parse(n, div) }
|
@@ -33,23 +33,12 @@ module IsoDoc
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
def termexample_parse(node, out)
|
37
|
-
out.div **{ class: "Note" } do |div|
|
38
|
-
first = node.first_element_child
|
39
|
-
div.p **{ class: "Note" } do |p|
|
40
|
-
p << l10n("#{@example_lbl}:")
|
41
|
-
insert_tab(p, 1)
|
42
|
-
para_then_remainder(first, node, p)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
36
|
def termnote_parse(node, out)
|
48
37
|
out.div **{ class: "Note" } do |div|
|
49
38
|
first = node.first_element_child
|
50
39
|
div.p **{ class: "Note" } do |p|
|
51
40
|
p << "#{get_anchors[node['id']][:label]}: "
|
52
|
-
para_then_remainder(first, node, p)
|
41
|
+
para_then_remainder(first, node, p, div)
|
53
42
|
end
|
54
43
|
end
|
55
44
|
end
|
data/lib/isodoc/utils.rb
CHANGED
@@ -8,9 +8,7 @@ module IsoDoc
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def insert_tab(out, n)
|
11
|
-
|
12
|
-
[1..n].each { span << "  " }
|
13
|
-
end
|
11
|
+
[1..n].each { out << " " }
|
14
12
|
end
|
15
13
|
|
16
14
|
STAGE_ABBRS = {
|
@@ -119,5 +117,28 @@ module IsoDoc
|
|
119
117
|
end
|
120
118
|
[@openmathdelim, @closemathdelim]
|
121
119
|
end
|
120
|
+
|
121
|
+
def header_strip(h)
|
122
|
+
h = h.to_s.gsub(%r{<br/>}, " ").sub(/<\/?h[12][^>]*>/, "")
|
123
|
+
h1 = to_xhtml_fragment(h.dup)
|
124
|
+
h1.traverse do |x|
|
125
|
+
x.remove if x.name == "span" && x["class"] == "MsoCommentReference"
|
126
|
+
x.remove if x.name == "a" && x["epub:type"] == "footnote"
|
127
|
+
if x.name == "a"
|
128
|
+
x.replace(x.children)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
from_xhtml(h1)
|
132
|
+
end
|
133
|
+
|
134
|
+
def populate_template(docxml, _format)
|
135
|
+
meta = get_metadata
|
136
|
+
docxml = docxml.
|
137
|
+
gsub(/\[TERMREF\]\s*/, l10n("[#{@source_lbl}: ")).
|
138
|
+
gsub(/\s*\[\/TERMREF\]\s*/, l10n("]")).
|
139
|
+
gsub(/\s*\[MODIFICATION\]/, l10n(", #{@modified_lbl} — "))
|
140
|
+
template = Liquid::Template.parse(docxml)
|
141
|
+
template.render(meta.map { |k, v| [k.to_s, v] }.to_h)
|
142
|
+
end
|
122
143
|
end
|
123
144
|
end
|
data/lib/isodoc/version.rb
CHANGED
@@ -0,0 +1,155 @@
|
|
1
|
+
#require "uuidtools"
|
2
|
+
|
3
|
+
#module IsoDoc
|
4
|
+
#class WordConvert
|
5
|
+
#module WordConvertModule
|
6
|
+
#def self.included base
|
7
|
+
#base.class_eval do
|
8
|
+
|
9
|
+
def in_comment
|
10
|
+
@in_comment
|
11
|
+
end
|
12
|
+
|
13
|
+
def comments(div)
|
14
|
+
return if @comments.empty?
|
15
|
+
div.div **{ style: "mso-element:comment-list" } do |div1|
|
16
|
+
@comments.each { |fn| div1.parent << fn }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def review_note_parse(node, out)
|
21
|
+
fn = @comments.length + 1
|
22
|
+
make_comment_link(out, fn, node)
|
23
|
+
@in_comment = true
|
24
|
+
@comments << make_comment_text(node, fn)
|
25
|
+
@in_comment = false
|
26
|
+
end
|
27
|
+
|
28
|
+
def comment_link_attrs(fn, node)
|
29
|
+
{ style: "MsoCommentReference", target: fn,
|
30
|
+
class: "commentLink", from: node["from"],
|
31
|
+
to: node["to"] }
|
32
|
+
end
|
33
|
+
|
34
|
+
# add in from and to links to move the comment into place
|
35
|
+
def make_comment_link(out, fn, node)
|
36
|
+
out.span(**comment_link_attrs(fn, node)) do |s1|
|
37
|
+
s1.span **{ lang: "EN-GB", style: "font-size:9.0pt" } do |s2|
|
38
|
+
s2.a **{ style: "mso-comment-reference:SMC_#{fn};"\
|
39
|
+
"mso-comment-date:#{node['date']}" }
|
40
|
+
s2.span **{ style: "mso-special-character:comment",
|
41
|
+
target: fn } # do |s|
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def make_comment_target(out)
|
47
|
+
out.span **{ style: "MsoCommentReference" } do |s1|
|
48
|
+
s1.span **{ lang: "EN-GB", style: "font-size:9.0pt" } do |s2|
|
49
|
+
s2.span **{ style: "mso-special-character:comment" }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def make_comment_text(node, fn)
|
55
|
+
noko do |xml|
|
56
|
+
xml.div **{ style: "mso-element:comment", id: fn } do |div|
|
57
|
+
div.span **{ style: %{mso-comment-author:"#{node['reviewer']}"} }
|
58
|
+
make_comment_target(div)
|
59
|
+
node.children.each { |n| parse(n, div) }
|
60
|
+
end
|
61
|
+
end.join("\n")
|
62
|
+
end
|
63
|
+
|
64
|
+
def comment_cleanup(docxml)
|
65
|
+
move_comment_link_to_from(docxml)
|
66
|
+
reorder_comments_by_comment_link(docxml)
|
67
|
+
embed_comment_in_comment_list(docxml)
|
68
|
+
end
|
69
|
+
|
70
|
+
#COMMENT_IN_COMMENT_LIST =
|
71
|
+
COMMENT_IN_COMMENT_LIST1 =
|
72
|
+
'//div[@style="mso-element:comment-list"]//'\
|
73
|
+
'span[@style="MsoCommentReference"]'.freeze
|
74
|
+
|
75
|
+
def embed_comment_in_comment_list(docxml)
|
76
|
+
#docxml.xpath(COMMENT_IN_COMMENT_LIST).each do |x|
|
77
|
+
docxml.xpath(COMMENT_IN_COMMENT_LIST1).each do |x|
|
78
|
+
n = x.next_element
|
79
|
+
n&.children&.first&.add_previous_sibling(x.remove)
|
80
|
+
end
|
81
|
+
docxml
|
82
|
+
end
|
83
|
+
|
84
|
+
def move_comment_link_to_from1(x, fromlink)
|
85
|
+
x.remove
|
86
|
+
link = x.at(".//a")
|
87
|
+
fromlink.replace(x)
|
88
|
+
link.children = fromlink
|
89
|
+
end
|
90
|
+
|
91
|
+
def comment_attributes(docxml, x)
|
92
|
+
fromlink = docxml.at("//*[@id='#{x['from']}']")
|
93
|
+
return(nil) if fromlink.nil?
|
94
|
+
tolink = docxml.at("//*[@id='#{x['to']}']") || fromlink
|
95
|
+
target = docxml.at("//*[@id='#{x['target']}']")
|
96
|
+
{ from: fromlink, to: tolink, target: target }
|
97
|
+
end
|
98
|
+
|
99
|
+
def wrap_comment_cont(from, target)
|
100
|
+
s = from.replace("<span style='mso-comment-continuation:#{target}'>")
|
101
|
+
s.first.children = from
|
102
|
+
end
|
103
|
+
|
104
|
+
def skip_comment_wrap(from)
|
105
|
+
from["style"] != "mso-special-character:comment"
|
106
|
+
end
|
107
|
+
|
108
|
+
def insert_comment_cont(from, to, target)
|
109
|
+
# includes_to = from.at(".//*[@id='#{to}']")
|
110
|
+
while !from.nil? && from["id"] != to
|
111
|
+
following = from.xpath("./following::*")
|
112
|
+
(from = following.shift) && incl_to = from.at(".//*[@id='#{to}']")
|
113
|
+
while !incl_to.nil? && !from.nil? && skip_comment_wrap(from)
|
114
|
+
(from = following.shift) && incl_to = from.at(".//*[@id='#{to}']")
|
115
|
+
end
|
116
|
+
wrap_comment_cont(from, target) if !from.nil?
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def move_comment_link_to_from(docxml)
|
121
|
+
docxml.xpath('//span[@style="MsoCommentReference"][@from]').each do |x|
|
122
|
+
attrs = comment_attributes(docxml, x) || next
|
123
|
+
move_comment_link_to_from1(x, attrs[:from])
|
124
|
+
insert_comment_cont(attrs[:from], x["to"], x["target"])
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def get_comments_from_text(docxml, link_order)
|
129
|
+
comments = []
|
130
|
+
docxml.xpath("//div[@style='mso-element:comment']").each do |c|
|
131
|
+
next unless c["id"] && !link_order[c["id"]].nil?
|
132
|
+
comments << { text: c.remove.to_s, id: c["id"] }
|
133
|
+
end
|
134
|
+
comments.sort! { |a, b| link_order[a[:id]] <=> link_order[b[:id]] }
|
135
|
+
# comments
|
136
|
+
end
|
137
|
+
|
138
|
+
#COMMENT_TARGET_XREFS =
|
139
|
+
COMMENT_TARGET_XREFS1 =
|
140
|
+
"//span[@style='mso-special-character:comment']/@target".freeze
|
141
|
+
|
142
|
+
def reorder_comments_by_comment_link(docxml)
|
143
|
+
link_order = {}
|
144
|
+
#docxml.xpath(COMMENT_TARGET_XREFS).each_with_index do |target, i|
|
145
|
+
docxml.xpath(COMMENT_TARGET_XREFS1).each_with_index do |target, i|
|
146
|
+
link_order[target.value] = i
|
147
|
+
end
|
148
|
+
comments = get_comments_from_text(docxml, link_order)
|
149
|
+
list = docxml.at("//*[@style='mso-element:comment-list']") || return
|
150
|
+
list.children = comments.map { |c| c[:text] }.join("\n")
|
151
|
+
end
|
152
|
+
#end
|
153
|
+
#end
|
154
|
+
#end
|
155
|
+
#end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "uuidtools"
|
2
|
+
require "html2doc"
|
3
|
+
require "liquid"
|
4
|
+
|
5
|
+
require_relative "wordconvertmodule"
|
6
|
+
require_relative "comments"
|
7
|
+
require_relative "footnotes"
|
8
|
+
|
9
|
+
module IsoDoc
|
10
|
+
|
11
|
+
module WordConvertModule
|
12
|
+
# http://tech.tulentsev.com/2012/02/ruby-how-to-override-class-method-with-a-module/
|
13
|
+
# https://www.ruby-forum.com/topic/148303
|
14
|
+
#
|
15
|
+
# The following is ugly indeed, but the only way I can split module override methods
|
16
|
+
# across files
|
17
|
+
def self.included base
|
18
|
+
base.class_eval do
|
19
|
+
|
20
|
+
eval File.open(File.join(File.dirname(__FILE__),"wordconvertmodule.rb")).read
|
21
|
+
eval File.open(File.join(File.dirname(__FILE__),"comments.rb")).read
|
22
|
+
eval File.open(File.join(File.dirname(__FILE__),"footnotes.rb")).read
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class WordConvert < Convert
|
28
|
+
include WordConvertModule
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
@@ -0,0 +1,80 @@
|
|
1
|
+
#require "uuidtools"
|
2
|
+
|
3
|
+
#module IsoDoc
|
4
|
+
#class WordConvert
|
5
|
+
#module WordConvertModule
|
6
|
+
#def self.included base
|
7
|
+
#base.class_eval do
|
8
|
+
|
9
|
+
def footnotes(div)
|
10
|
+
return if @footnotes.empty?
|
11
|
+
@footnotes.each { |fn| div.parent << fn }
|
12
|
+
end
|
13
|
+
|
14
|
+
def make_table_footnote_link(out, fnid, fnref)
|
15
|
+
attrs = { href: "##{fnid}", class: "TableFootnoteRef" }
|
16
|
+
out.a **attrs do |a|
|
17
|
+
a << fnref
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def make_table_footnote_target(out, fnid, fnref)
|
22
|
+
attrs = { id: fnid, class: "TableFootnoteRef" }
|
23
|
+
out.a **attrs do |a|
|
24
|
+
a << fnref
|
25
|
+
insert_tab(a, 1)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def make_table_footnote_text(node, fnid, fnref)
|
30
|
+
attrs = { id: "ftn#{fnid}" }
|
31
|
+
noko do |xml|
|
32
|
+
xml.div **attr_code(attrs) do |div|
|
33
|
+
make_table_footnote_target(div, fnid, fnref)
|
34
|
+
node.children.each { |n| parse(n, div) }
|
35
|
+
end
|
36
|
+
end.join("\n")
|
37
|
+
end
|
38
|
+
|
39
|
+
def make_generic_footnote_text(node, fnid)
|
40
|
+
noko do |xml|
|
41
|
+
xml.aside **{ id: "ftn#{fnid}" } do |div|
|
42
|
+
node.children.each { |n| parse(n, div) }
|
43
|
+
end
|
44
|
+
end.join("\n")
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_table_ancestor_id(node)
|
48
|
+
table = node.ancestors("table") || node.ancestors("figure")
|
49
|
+
return UUIDTools::UUID.random_create.to_s if table.empty?
|
50
|
+
table.last["id"]
|
51
|
+
end
|
52
|
+
|
53
|
+
def table_footnote_parse(node, out)
|
54
|
+
fn = node["reference"]
|
55
|
+
tid = get_table_ancestor_id(node)
|
56
|
+
make_table_footnote_link(out, tid + fn, fn)
|
57
|
+
# do not output footnote text if we have already seen it for this table
|
58
|
+
return if @seen_footnote.include?(tid + fn)
|
59
|
+
@in_footnote = true
|
60
|
+
out.aside { |a| a << make_table_footnote_text(node, tid + fn, fn) }
|
61
|
+
@in_footnote = false
|
62
|
+
@seen_footnote << (tid + fn)
|
63
|
+
end
|
64
|
+
|
65
|
+
def footnote_parse(node, out)
|
66
|
+
return table_footnote_parse(node, out) if @in_table || @in_figure
|
67
|
+
fn = node["reference"]
|
68
|
+
out.a **{ "epub:type": "footnote", href: "#ftn#{fn}" } do |a|
|
69
|
+
a.sup { |sup| sup << fn }
|
70
|
+
end
|
71
|
+
return if @seen_footnote.include?(fn)
|
72
|
+
@in_footnote = true
|
73
|
+
@footnotes << make_generic_footnote_text(node, fn)
|
74
|
+
@in_footnote = false
|
75
|
+
@seen_footnote << fn
|
76
|
+
end
|
77
|
+
#end
|
78
|
+
#end
|
79
|
+
#end
|
80
|
+
#end
|
@@ -0,0 +1,212 @@
|
|
1
|
+
|
2
|
+
#require "html2doc"
|
3
|
+
#require "liquid"
|
4
|
+
|
5
|
+
#module IsoDoc
|
6
|
+
#class WordConvert < Convert
|
7
|
+
#module WordConvertModule
|
8
|
+
#def self.included base
|
9
|
+
#base.class_eval do
|
10
|
+
def insert_tab(out, n)
|
11
|
+
out.span **attr_code(style: "mso-tab-count:#{n}") do |span|
|
12
|
+
[1..n].each { span << "  " }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def para_attrs(node)
|
17
|
+
classtype = nil
|
18
|
+
classtype = "Note" if @note
|
19
|
+
classtype = "MsoCommentText" if in_comment
|
20
|
+
classtype = "Sourcecode" if @annotation
|
21
|
+
attrs = { class: classtype, id: node["id"] }
|
22
|
+
unless node["align"].nil?
|
23
|
+
attrs[:align] = node["align"] unless node["align"] == "justify"
|
24
|
+
attrs[:style] = "text-align:#{node['align']}"
|
25
|
+
end
|
26
|
+
attrs
|
27
|
+
end
|
28
|
+
|
29
|
+
def remove_bottom_border(td)
|
30
|
+
td["style"] =
|
31
|
+
td["style"].gsub(/border-bottom:[^;]+;/, "border-bottom:0pt;").
|
32
|
+
gsub(/mso-border-bottom-alt:[^;]+;/, "mso-border-bottom-alt:0pt;")
|
33
|
+
end
|
34
|
+
|
35
|
+
#SW1 = IsoDoc::SW
|
36
|
+
SW1 = "solid windowtext".freeze
|
37
|
+
|
38
|
+
def new_fullcolspan_row(t, tfoot)
|
39
|
+
# how many columns in the table?
|
40
|
+
cols = 0
|
41
|
+
t.at(".//tr").xpath("./td | ./th").each do |td|
|
42
|
+
cols += (td["colspan"] ? td["colspan"].to_i : 1)
|
43
|
+
end
|
44
|
+
style = %{border-top:0pt;mso-border-top-alt:0pt;
|
45
|
+
border-bottom:#{SW1} 1.5pt;mso-border-bottom-alt:#{SW1} 1.5pt;}
|
46
|
+
tfoot.add_child("<tr><td colspan='#{cols}' style='#{style}'/></tr>")
|
47
|
+
tfoot.xpath(".//td").last
|
48
|
+
end
|
49
|
+
|
50
|
+
def make_tr_attr(td, row, totalrows)
|
51
|
+
style = td.name == "th" ? "font-weight:bold;" : ""
|
52
|
+
rowmax = td["rowspan"] ? row + td["rowspan"].to_i - 1 : row
|
53
|
+
style += <<~STYLE
|
54
|
+
border-top:#{row.zero? ? "#{SW1} 1.5pt;" : 'none;'}
|
55
|
+
mso-border-top-alt:#{row.zero? ? "#{SW1} 1.5pt;" : 'none;'}
|
56
|
+
border-bottom:#{SW1} #{rowmax == totalrows ? '1.5' : '1.0'}pt;
|
57
|
+
mso-border-bottom-alt:#{SW1} #{rowmax == totalrows ? '1.5' : '1.0'}pt;
|
58
|
+
STYLE
|
59
|
+
{ rowspan: td["rowspan"], colspan: td["colspan"],
|
60
|
+
align: td["align"], style: style.gsub(/\n/, "") }
|
61
|
+
end
|
62
|
+
|
63
|
+
def section_break(body)
|
64
|
+
body.br **{ clear: "all", class: "section" }
|
65
|
+
end
|
66
|
+
|
67
|
+
def page_break(out)
|
68
|
+
out.br **{
|
69
|
+
clear: "all",
|
70
|
+
style: "mso-special-character:line-break;page-break-before:always",
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
def dt_parse(dt, term)
|
75
|
+
if dt.elements.empty?
|
76
|
+
term.p **attr_code(class: note? ? "Note" : nil,
|
77
|
+
style: "text-align: left;") do |p|
|
78
|
+
p << dt.text
|
79
|
+
end
|
80
|
+
else
|
81
|
+
dt.children.each { |n| parse(n, term) }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def dl_parse(node, out)
|
86
|
+
out.table **{ class: "dl" } do |v|
|
87
|
+
node.elements.each_slice(2) do |dt, dd|
|
88
|
+
v.tr do |tr|
|
89
|
+
tr.td **{ valign: "top", align: "left" } do |term|
|
90
|
+
dt_parse(dt, term)
|
91
|
+
end
|
92
|
+
tr.td **{ valign: "top" } do |listitem|
|
93
|
+
dd.children.each { |n| parse(n, listitem) }
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
|
102
|
+
def postprocess(result, filename, dir)
|
103
|
+
generate_header(filename, dir)
|
104
|
+
result = from_xhtml(cleanup(to_xhtml(result)))
|
105
|
+
toWord(result, filename, dir)
|
106
|
+
end
|
107
|
+
|
108
|
+
def toWord(result, filename, dir)
|
109
|
+
result = from_xhtml(word_cleanup(to_xhtml(result)))
|
110
|
+
result = populate_template(result, :word)
|
111
|
+
Html2Doc.process(result, filename: filename, stylesheet: @wordstylesheet,
|
112
|
+
header_file: "header.html", dir: dir,
|
113
|
+
asciimathdelims: [@openmathdelim, @closemathdelim],
|
114
|
+
liststyles: { ul: @ulstyle, ol: @olstyle })
|
115
|
+
end
|
116
|
+
|
117
|
+
def word_cleanup(docxml)
|
118
|
+
word_preface(docxml)
|
119
|
+
word_annex_cleanup(docxml)
|
120
|
+
docxml
|
121
|
+
end
|
122
|
+
|
123
|
+
# force Annex h2 to be p.h2Annex, so it is not picked up by ToC
|
124
|
+
def word_annex_cleanup(docxml)
|
125
|
+
docxml.xpath("//h2[ancestor::*[@class = 'Section3']]").each do |h2|
|
126
|
+
h2.name = "p"
|
127
|
+
h2["class"] = "h2Annex"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def word_preface(docxml)
|
132
|
+
word_cover(docxml) if @wordcoverpage
|
133
|
+
word_intro(docxml) if @wordintropage
|
134
|
+
end
|
135
|
+
|
136
|
+
def word_cover(docxml)
|
137
|
+
cover = to_xhtml_fragment(File.read(@wordcoverpage, encoding: "UTF-8"))
|
138
|
+
docxml.at('//div[@class="WordSection1"]').children.first.previous =
|
139
|
+
cover.to_xml(encoding: "US-ASCII")
|
140
|
+
end
|
141
|
+
|
142
|
+
def word_intro(docxml)
|
143
|
+
intro = to_xhtml_fragment(File.read(@wordintropage, encoding: "UTF-8").
|
144
|
+
sub(/WORDTOC/, make_WordToC(docxml)))
|
145
|
+
docxml.at('//div[@class="WordSection2"]').children.first.previous =
|
146
|
+
intro.to_xml(encoding: "US-ASCII")
|
147
|
+
end
|
148
|
+
|
149
|
+
def generate_header(filename, _dir)
|
150
|
+
return unless @header
|
151
|
+
template = Liquid::Template.parse(File.read(@header, encoding: "UTF-8"))
|
152
|
+
meta = get_metadata
|
153
|
+
meta[:filename] = filename
|
154
|
+
params = meta.map { |k, v| [k.to_s, v] }.to_h
|
155
|
+
File.open("header.html", "w") do |f|
|
156
|
+
f.write(template.render(params))
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def word_toc_entry(toclevel, heading)
|
161
|
+
bookmark = Random.rand(1000000000)
|
162
|
+
<<~TOC
|
163
|
+
<p class="MsoToc#{toclevel}"><span class="MsoHyperlink"><span
|
164
|
+
lang="EN-GB" style='mso-no-proof:yes'>
|
165
|
+
<a href="#_Toc#{bookmark}">#{heading}<span lang="EN-GB"
|
166
|
+
class="MsoTocTextSpan">
|
167
|
+
<span style='mso-tab-count:1 dotted'>. </span>
|
168
|
+
</span><span lang="EN-GB" class="MsoTocTextSpan">
|
169
|
+
<span style='mso-element:field-begin'></span></span>
|
170
|
+
<span lang="EN-GB"
|
171
|
+
class="MsoTocTextSpan"> PAGEREF _Toc#{bookmark} \\h </span>
|
172
|
+
<span lang="EN-GB" class="MsoTocTextSpan"><span
|
173
|
+
style='mso-element:field-separator'></span></span><span
|
174
|
+
lang="EN-GB" class="MsoTocTextSpan">1</span>
|
175
|
+
<span lang="EN-GB"
|
176
|
+
class="MsoTocTextSpan"></span><span
|
177
|
+
lang="EN-GB" class="MsoTocTextSpan"><span
|
178
|
+
style='mso-element:field-end'></span></span></a></span></span></p>
|
179
|
+
|
180
|
+
TOC
|
181
|
+
end
|
182
|
+
|
183
|
+
#WORD_TOC_PREFACE = <<~TOC.freeze
|
184
|
+
WORD_TOC_PREFACE1 = <<~TOC.freeze
|
185
|
+
<span lang="EN-GB"><span
|
186
|
+
style='mso-element:field-begin'></span><span
|
187
|
+
style='mso-spacerun:yes'> </span>TOC
|
188
|
+
\\o "1-2" \\h \\z \\u <span
|
189
|
+
style='mso-element:field-separator'></span></span>
|
190
|
+
TOC
|
191
|
+
|
192
|
+
#WORD_TOC_SUFFIX = <<~TOC.freeze
|
193
|
+
WORD_TOC_SUFFIX1 = <<~TOC.freeze
|
194
|
+
<p class="MsoToc1"><span lang="EN-GB"><span
|
195
|
+
style='mso-element:field-end'></span></span><span
|
196
|
+
lang="EN-GB"><o:p> </o:p></span></p>
|
197
|
+
TOC
|
198
|
+
|
199
|
+
def make_WordToC(docxml)
|
200
|
+
toc = ""
|
201
|
+
docxml.xpath("//h1 | //h2[not(ancestor::*[@class = 'Section3'])]").
|
202
|
+
each do |h|
|
203
|
+
toc += word_toc_entry(h.name == "h1" ? 1 : 2, header_strip(h))
|
204
|
+
end
|
205
|
+
toc.sub(/(<p class="MsoToc1">)/,
|
206
|
+
#%{\\1#{WORD_TOC_PREFACE}}) + WORD_TOC_SUFFIX
|
207
|
+
%{\\1#{WORD_TOC_PREFACE1}}) + WORD_TOC_SUFFIX1
|
208
|
+
end
|
209
|
+
#end
|
210
|
+
#end
|
211
|
+
#end
|
212
|
+
#end
|