isodoc 0.5.5 → 0.5.7
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/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
|