isodoc 2.4.1 → 2.4.2
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/lib/isodoc/base_style/all.css +29 -0
- data/lib/isodoc/base_style/metanorma_word.css +9 -0
- data/lib/isodoc/base_style/metanorma_word.scss +11 -0
- data/lib/isodoc/base_style/reset.css +29 -0
- data/lib/isodoc/base_style/reset.scss +34 -0
- data/lib/isodoc/base_style/rouge.css +39 -0
- data/lib/isodoc/base_style/typography.scss +1 -1
- data/lib/isodoc/function/blocks.rb +26 -16
- data/lib/isodoc/function/inline.rb +13 -13
- data/lib/isodoc/function/table.rb +1 -1
- data/lib/isodoc/function/to_word_html.rb +0 -4
- data/lib/isodoc/function/utils.rb +5 -1
- data/lib/isodoc/html_function/html.rb +13 -13
- data/lib/isodoc/html_function/postprocess.rb +14 -1
- data/lib/isodoc/presentation_function/block.rb +1 -98
- data/lib/isodoc/presentation_function/sourcecode.rb +114 -0
- data/lib/isodoc/presentation_function/terms.rb +14 -27
- data/lib/isodoc/version.rb +1 -1
- data/lib/isodoc/word_function/body.rb +16 -16
- data/lib/isodoc/word_function/postprocess.rb +27 -52
- data/lib/isodoc/word_function/postprocess_cover.rb +29 -157
- data/lib/isodoc/word_function/postprocess_toc.rb +165 -0
- data/lib/isodoc/word_function/table.rb +2 -1
- data/lib/isodoc/xref/xref_gen.rb +2 -4
- metadata +5 -2
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
module IsoDoc
|
|
2
|
+
class PresentationXMLConvert < ::IsoDoc::Convert
|
|
3
|
+
def sourcehighlighter_css(docxml)
|
|
4
|
+
@sourcehighlighter or return
|
|
5
|
+
ins = docxml.at(ns("//misc-container")) ||
|
|
6
|
+
docxml.at(ns("//bibdata")).after("<misc-container/>").next_element
|
|
7
|
+
ins << "<source-highlighter-css>#{sourcehighlighter_css_file}" \
|
|
8
|
+
"</source-highlighter-css>"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def sourcehighlighter_css_file
|
|
12
|
+
File.read(File.join(File.dirname(__FILE__), "..", "base_style",
|
|
13
|
+
"rouge.css"))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def sourcehighlighter
|
|
17
|
+
@sourcehighlighter or return
|
|
18
|
+
f = Rouge::Formatters::HTML.new
|
|
19
|
+
opts = { gutter_class: "rouge-gutter", code_class: "rouge-code" }
|
|
20
|
+
{ formatter: f,
|
|
21
|
+
formatter_line: Rouge::Formatters::HTMLLineTable.new(f, opts) }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def sourcecode(docxml)
|
|
25
|
+
sourcehighlighter_css(docxml)
|
|
26
|
+
@highlighter = sourcehighlighter
|
|
27
|
+
docxml.xpath(ns("//sourcecode")).each do |f|
|
|
28
|
+
sourcecode1(f)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def sourcecode1(elem)
|
|
33
|
+
source_highlight(elem)
|
|
34
|
+
source_label(elem)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def source_highlight(elem)
|
|
38
|
+
@highlighter or return
|
|
39
|
+
markup = source_remove_markup(elem)
|
|
40
|
+
p = source_lex(elem)
|
|
41
|
+
elem.children = if elem["linenums"] == "true"
|
|
42
|
+
r = sourcecode_table_to_elem(elem, p)
|
|
43
|
+
source_restore_markup_table(r, markup)
|
|
44
|
+
else
|
|
45
|
+
r = @highlighter[:formatter].format(p)
|
|
46
|
+
source_restore_markup(Nokogiri::XML.fragment(r), markup)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def source_remove_markup(elem)
|
|
51
|
+
ret = {}
|
|
52
|
+
name = elem.at(ns("./name")) and ret[:name] = name.remove.to_xml
|
|
53
|
+
ret[:ann] = elem.xpath(ns("./annotation")).each(&:remove)
|
|
54
|
+
ret[:call] = elem.xpath(ns("./callout")).each_with_object([]) do |c, m|
|
|
55
|
+
m << { xml: c.remove.to_xml, line: c.line - elem.line }
|
|
56
|
+
end
|
|
57
|
+
ret
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def source_restore_markup(wrapper, markup)
|
|
61
|
+
ret = source_restore_callouts(wrapper, markup[:call])
|
|
62
|
+
"#{markup[:name]}#{ret}#{markup[:ann]}"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def source_restore_markup_table(wrapper, markup)
|
|
66
|
+
source_restore_callouts_table(wrapper, markup[:call])
|
|
67
|
+
ret = to_xml(wrapper)
|
|
68
|
+
"#{markup[:name]}#{ret}#{markup[:ann]}"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def source_restore_callouts(code, callouts)
|
|
72
|
+
text = to_xml(code)
|
|
73
|
+
text.split(/[\n\r]/).each_with_index do |c, i|
|
|
74
|
+
while !callouts.empty? && callouts[0][:line] == i
|
|
75
|
+
c.sub!(/\s+$/, " <span class='c'>#{callouts[0][:xml]}</span> ")
|
|
76
|
+
callouts.shift
|
|
77
|
+
end
|
|
78
|
+
end.join("\n")
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def source_restore_callouts_table(table, callouts)
|
|
82
|
+
table.xpath(".//td[@class = 'rouge-code']/sourcecode")
|
|
83
|
+
.each_with_index do |c, i|
|
|
84
|
+
while !callouts.empty? && callouts[0][:line] == i
|
|
85
|
+
c << " <span class='c'>#{callouts[0][:xml]}</span> "
|
|
86
|
+
callouts.shift
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def sourcecode_table_to_elem(elem, tokens)
|
|
92
|
+
r = Nokogiri::XML(@highlighter[:formatter_line].format(tokens)).root
|
|
93
|
+
r.xpath(".//td[@class = 'rouge-code']/pre").each do |pre|
|
|
94
|
+
%w(style).each { |n| elem[n] and pre[n] = elem[n] }
|
|
95
|
+
pre.name = "sourcecode"
|
|
96
|
+
pre.children = to_xml(pre.children).sub(/\s+$/, "")
|
|
97
|
+
end
|
|
98
|
+
r
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def source_lex(elem)
|
|
102
|
+
l = (Rouge::Lexer.find(elem["lang"] || "plaintext") ||
|
|
103
|
+
Rouge::Lexer.find("plaintext"))
|
|
104
|
+
l.lex(@c.decode(elem.children.to_xml))
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def source_label(elem)
|
|
108
|
+
labelled_ancestor(elem) and return
|
|
109
|
+
lbl = @xrefs.anchor(elem["id"], :label, false) or return
|
|
110
|
+
prefix_name(elem, block_delim,
|
|
111
|
+
l10n("#{lower2cap @i18n.figure} #{lbl}"), "name")
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -46,9 +46,8 @@ module IsoDoc
|
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def concept1_linkmention(ref, renderterm, opts)
|
|
49
|
-
|
|
50
|
-
!renderterm.nil? && !ref.nil?
|
|
51
|
-
|
|
49
|
+
(opts[:linkmention] == "true" &&
|
|
50
|
+
!renderterm.nil? && !ref.nil?) or return
|
|
52
51
|
ref2 = ref.clone
|
|
53
52
|
r2 = renderterm.clone
|
|
54
53
|
renderterm.replace(ref2).children = r2
|
|
@@ -56,16 +55,14 @@ module IsoDoc
|
|
|
56
55
|
|
|
57
56
|
def concept1_ref(_node, ref, opts)
|
|
58
57
|
ref.nil? and return
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
opts[:ref] == "false" and return ref.remove
|
|
61
59
|
r = concept1_ref_content(ref)
|
|
62
60
|
ref = r.at("./descendant-or-self::xmlns:xref | " \
|
|
63
61
|
"./descendant-or-self::xmlns:eref | " \
|
|
64
62
|
"./descendant-or-self::xmlns:termref")
|
|
65
63
|
%w(xref eref).include? ref&.name and get_linkend(ref)
|
|
66
|
-
|
|
64
|
+
opts[:linkref] == "false" && %w(xref eref).include?(ref&.name) and
|
|
67
65
|
ref.replace(ref.children)
|
|
68
|
-
end
|
|
69
66
|
end
|
|
70
67
|
|
|
71
68
|
def concept1_ref_content(ref)
|
|
@@ -97,9 +94,8 @@ module IsoDoc
|
|
|
97
94
|
|
|
98
95
|
def designation(docxml)
|
|
99
96
|
docxml.xpath(ns("//term")).each { |t| merge_second_preferred(t) }
|
|
100
|
-
docxml.xpath(ns("//preferred | //admitted | //deprecates"))
|
|
101
|
-
designation1(p)
|
|
102
|
-
end
|
|
97
|
+
docxml.xpath(ns("//preferred | //admitted | //deprecates"))
|
|
98
|
+
.each { |p| designation1(p) }
|
|
103
99
|
end
|
|
104
100
|
|
|
105
101
|
def merge_second_preferred(term)
|
|
@@ -112,11 +108,10 @@ module IsoDoc
|
|
|
112
108
|
end
|
|
113
109
|
|
|
114
110
|
def merge_second_preferred1(pref, second)
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
end
|
|
111
|
+
merge_preferred_eligible?(pref, second) or return
|
|
112
|
+
n1 = pref.at(ns("./expression/name"))
|
|
113
|
+
n2 = second.remove.at(ns("./expression/name"))
|
|
114
|
+
n1.children = l10n("#{to_xml(n1.children)}; #{Common::to_xml(n2.children)}")
|
|
120
115
|
end
|
|
121
116
|
|
|
122
117
|
def merge_preferred_eligible?(first, second)
|
|
@@ -132,7 +127,6 @@ module IsoDoc
|
|
|
132
127
|
s = desgn.at(ns("./termsource"))
|
|
133
128
|
name = desgn.at(ns("./expression/name | ./letter-symbol/name | " \
|
|
134
129
|
"./graphical-symbol")) or return
|
|
135
|
-
|
|
136
130
|
designation_annotate(desgn, name)
|
|
137
131
|
s and desgn.next = s
|
|
138
132
|
end
|
|
@@ -156,8 +150,7 @@ module IsoDoc
|
|
|
156
150
|
def designation_field(desgn, name)
|
|
157
151
|
f = desgn.xpath(ns("./field-of-application | ./usage-info"))
|
|
158
152
|
&.map { |u| to_xml(u.children) }&.join(", ")
|
|
159
|
-
|
|
160
|
-
|
|
153
|
+
f&.empty? and return nil
|
|
161
154
|
name << ", <#{f}>"
|
|
162
155
|
end
|
|
163
156
|
|
|
@@ -178,27 +171,21 @@ module IsoDoc
|
|
|
178
171
|
loc = [desgn&.at(ns("./expression/@language"))&.text,
|
|
179
172
|
desgn&.at(ns("./expression/@script"))&.text,
|
|
180
173
|
desgn&.at(ns("./@geographic-area"))&.text].compact
|
|
181
|
-
|
|
182
|
-
|
|
174
|
+
loc.empty? and return
|
|
183
175
|
name << ", #{loc.join(' ')}"
|
|
184
176
|
end
|
|
185
177
|
|
|
186
178
|
def designation_pronunciation(desgn, name)
|
|
187
179
|
f = desgn.at(ns("./expression/pronunciation")) or return
|
|
188
|
-
|
|
189
180
|
name << ", /#{to_xml(f.children)}/"
|
|
190
181
|
end
|
|
191
182
|
|
|
192
183
|
def termexample(docxml)
|
|
193
|
-
docxml.xpath(ns("//termexample")).each
|
|
194
|
-
example1(f)
|
|
195
|
-
end
|
|
184
|
+
docxml.xpath(ns("//termexample")).each { |f| example1(f) }
|
|
196
185
|
end
|
|
197
186
|
|
|
198
187
|
def termnote(docxml)
|
|
199
|
-
docxml.xpath(ns("//termnote")).each
|
|
200
|
-
termnote1(f)
|
|
201
|
-
end
|
|
188
|
+
docxml.xpath(ns("//termnote")).each { |f| termnote1(f) }
|
|
202
189
|
end
|
|
203
190
|
|
|
204
191
|
def termnote1(elem)
|
data/lib/isodoc/version.rb
CHANGED
|
@@ -19,14 +19,14 @@ module IsoDoc
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def make_body1(body, _docxml)
|
|
22
|
-
body.div
|
|
22
|
+
body.div class: "WordSection1" do |div1|
|
|
23
23
|
div1.p { |p| p << " " } # placeholder
|
|
24
24
|
end
|
|
25
25
|
section_break(body)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def make_body2(body, docxml)
|
|
29
|
-
body.div
|
|
29
|
+
body.div class: "WordSection2" do |div2|
|
|
30
30
|
boilerplate docxml, div2
|
|
31
31
|
preface_block docxml, div2
|
|
32
32
|
abstract docxml, div2
|
|
@@ -40,7 +40,7 @@ module IsoDoc
|
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
def make_body3(body, docxml)
|
|
43
|
-
body.div
|
|
43
|
+
body.div class: "WordSection3" do |div3|
|
|
44
44
|
middle docxml, div3
|
|
45
45
|
footnotes div3
|
|
46
46
|
comments div3
|
|
@@ -93,7 +93,7 @@ module IsoDoc
|
|
|
93
93
|
|
|
94
94
|
def dl_parse_table(node, out)
|
|
95
95
|
list_title_parse(node, out)
|
|
96
|
-
out.table
|
|
96
|
+
out.table class: "dl" do |v|
|
|
97
97
|
node.elements.select { |n| dt_dd?(n) }
|
|
98
98
|
.each_slice(2) do |dt, dd|
|
|
99
99
|
dl_parse_table1(v, dt, dd)
|
|
@@ -104,10 +104,10 @@ module IsoDoc
|
|
|
104
104
|
|
|
105
105
|
def dl_parse_table1(table, dterm, ddefn)
|
|
106
106
|
table.tr do |tr|
|
|
107
|
-
tr.td
|
|
107
|
+
tr.td valign: "top", align: "left" do |term|
|
|
108
108
|
dt_parse(dterm, term)
|
|
109
109
|
end
|
|
110
|
-
tr.td
|
|
110
|
+
tr.td valign: "top" do |listitem|
|
|
111
111
|
ddefn.children.each { |n| parse(n, listitem) }
|
|
112
112
|
end
|
|
113
113
|
end
|
|
@@ -118,7 +118,7 @@ module IsoDoc
|
|
|
118
118
|
return if remainder.empty?
|
|
119
119
|
|
|
120
120
|
out.tr do |tr|
|
|
121
|
-
tr.td
|
|
121
|
+
tr.td colspan: 2 do |td|
|
|
122
122
|
remainder.each { |n| parse(n, td) }
|
|
123
123
|
end
|
|
124
124
|
end
|
|
@@ -149,8 +149,8 @@ module IsoDoc
|
|
|
149
149
|
|
|
150
150
|
def note_p_parse(node, div)
|
|
151
151
|
name = node&.at(ns("./name"))&.remove
|
|
152
|
-
div.p
|
|
153
|
-
p.span
|
|
152
|
+
div.p class: "Note" do |p|
|
|
153
|
+
p.span class: "note_label" do |s|
|
|
154
154
|
name&.children&.each { |n| parse(n, s) }
|
|
155
155
|
end
|
|
156
156
|
insert_tab(p, 1)
|
|
@@ -161,8 +161,8 @@ module IsoDoc
|
|
|
161
161
|
|
|
162
162
|
def note_parse1(node, div)
|
|
163
163
|
name = node&.at(ns("./name"))&.remove
|
|
164
|
-
div.p
|
|
165
|
-
p.span
|
|
164
|
+
div.p class: "Note" do |p|
|
|
165
|
+
p.span class: "note_label" do |s|
|
|
166
166
|
name&.children&.each { |n| parse(n, s) }
|
|
167
167
|
end
|
|
168
168
|
insert_tab(p, 1)
|
|
@@ -173,7 +173,7 @@ module IsoDoc
|
|
|
173
173
|
def termnote_parse(node, out)
|
|
174
174
|
name = node&.at(ns("./name"))&.remove
|
|
175
175
|
out.div **note_attrs(node) do |div|
|
|
176
|
-
div.p
|
|
176
|
+
div.p class: "Note" do |p|
|
|
177
177
|
if name
|
|
178
178
|
name.children.each { |n| parse(n, p) }
|
|
179
179
|
p << termnote_delim
|
|
@@ -196,10 +196,10 @@ module IsoDoc
|
|
|
196
196
|
|
|
197
197
|
def example_table_attr(node)
|
|
198
198
|
super.merge(
|
|
199
|
-
style: "mso-table-lspace:15.0cm;margin-left:423.0pt;"\
|
|
200
|
-
"mso-table-rspace:15.0cm;margin-right:423.0pt;"\
|
|
201
|
-
"mso-table-anchor-horizontal:column;"\
|
|
202
|
-
"mso-table-overlap:never;border-collapse:collapse;"\
|
|
199
|
+
style: "mso-table-lspace:15.0cm;margin-left:423.0pt;" \
|
|
200
|
+
"mso-table-rspace:15.0cm;margin-right:423.0pt;" \
|
|
201
|
+
"mso-table-anchor-horizontal:column;" \
|
|
202
|
+
"mso-table-overlap:never;border-collapse:collapse;" \
|
|
203
203
|
"#{keep_style(node)}",
|
|
204
204
|
)
|
|
205
205
|
end
|
|
@@ -1,32 +1,15 @@
|
|
|
1
1
|
require "fileutils"
|
|
2
2
|
require_relative "./postprocess_cover"
|
|
3
|
+
require_relative "./postprocess_toc"
|
|
3
4
|
|
|
4
5
|
module IsoDoc
|
|
5
6
|
module WordFunction
|
|
6
7
|
module Postprocess
|
|
7
|
-
# add namespaces for Word fragments
|
|
8
|
-
WORD_NOKOHEAD = <<~HERE.freeze
|
|
9
|
-
<!DOCTYPE html SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
|
10
|
-
<html xmlns="http://www.w3.org/1999/xhtml"
|
|
11
|
-
xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office"
|
|
12
|
-
xmlns:w="urn:schemas-microsoft-com:office:word"
|
|
13
|
-
xmlns:m="http://schemas.microsoft.com/office/2004/12/omml">
|
|
14
|
-
<head> <title></title> <meta charset="UTF-8" /> </head>
|
|
15
|
-
<body> </body> </html>
|
|
16
|
-
HERE
|
|
17
|
-
|
|
18
|
-
def to_word_xhtml_fragment(xml)
|
|
19
|
-
doc = ::Nokogiri::XML.parse(WORD_NOKOHEAD)
|
|
20
|
-
::Nokogiri::XML::DocumentFragment.new(doc, xml, doc.root)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
8
|
def table_note_cleanup(docxml)
|
|
24
9
|
super
|
|
25
10
|
# preempt html2doc putting MsoNormal there
|
|
26
11
|
docxml.xpath("//p[not(self::*[@class])][ancestor::*[@class = 'Note']]")
|
|
27
|
-
.each
|
|
28
|
-
p["class"] = "Note"
|
|
29
|
-
end
|
|
12
|
+
.each { |p| p["class"] = "Note" }
|
|
30
13
|
end
|
|
31
14
|
|
|
32
15
|
def postprocess(result, filename, dir)
|
|
@@ -56,23 +39,6 @@ module IsoDoc
|
|
|
56
39
|
"Sourcecode"
|
|
57
40
|
end
|
|
58
41
|
|
|
59
|
-
def wordstylesheet_update
|
|
60
|
-
return if @wordstylesheet.nil?
|
|
61
|
-
|
|
62
|
-
f = File.open(@wordstylesheet.path, "a")
|
|
63
|
-
@landscapestyle.empty? or f.write(@landscapestyle)
|
|
64
|
-
s = @meta.get[:code_css] and
|
|
65
|
-
f.write(s.gsub(/sourcecode/, "p.#{sourcecode_style}"))
|
|
66
|
-
if @wordstylesheet_override && @wordstylesheet
|
|
67
|
-
f.write(@wordstylesheet_override.read)
|
|
68
|
-
@wordstylesheet_override.close
|
|
69
|
-
elsif @wordstylesheet_override && !@wordstylesheet
|
|
70
|
-
@wordstylesheet = @wordstylesheet_override
|
|
71
|
-
end
|
|
72
|
-
f.close
|
|
73
|
-
@wordstylesheet
|
|
74
|
-
end
|
|
75
|
-
|
|
76
42
|
def word_admonition_images(docxml)
|
|
77
43
|
docxml.xpath("//div[@class = 'Admonition']//img").each do |i|
|
|
78
44
|
i["width"], i["height"] =
|
|
@@ -84,6 +50,7 @@ module IsoDoc
|
|
|
84
50
|
def word_cleanup(docxml)
|
|
85
51
|
word_annex_cleanup(docxml)
|
|
86
52
|
word_preface(docxml)
|
|
53
|
+
word_sourcecode_annotations(docxml)
|
|
87
54
|
word_sourcecode_table(docxml)
|
|
88
55
|
word_nested_tables(docxml)
|
|
89
56
|
word_colgroup(docxml)
|
|
@@ -101,18 +68,29 @@ module IsoDoc
|
|
|
101
68
|
docxml
|
|
102
69
|
end
|
|
103
70
|
|
|
104
|
-
def
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
d.
|
|
71
|
+
def word_sourcecode_annotations(html)
|
|
72
|
+
ann = ".//div[@class = 'annotation']"
|
|
73
|
+
html.xpath("//p[@class = '#{sourcecode_style}'][#{ann}]")
|
|
74
|
+
.each do |p|
|
|
75
|
+
ins = p.after("<p class='#{sourcecode_style}'/>").next_element
|
|
76
|
+
p.xpath(ann).each do |d|
|
|
77
|
+
ins << d.remove.children
|
|
111
78
|
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def word_sourcecode_table(docxml)
|
|
83
|
+
s = "p[@class='#{sourcecode_style}']"
|
|
84
|
+
docxml.xpath("//#{s}/div[@class='table_container']").each do |d|
|
|
85
|
+
pre = d.at(".//#{s}")
|
|
86
|
+
to_sourcecode_para(pre)
|
|
87
|
+
d["id"] = d.parent["id"]
|
|
88
|
+
d.parent.replace(d)
|
|
89
|
+
end
|
|
112
90
|
end
|
|
113
91
|
|
|
114
92
|
def to_sourcecode_para(pre)
|
|
115
|
-
@sourcecode =
|
|
93
|
+
@sourcecode = "pre"
|
|
116
94
|
pre.traverse do |x|
|
|
117
95
|
x.text? or next
|
|
118
96
|
ret = []
|
|
@@ -173,8 +151,7 @@ module IsoDoc
|
|
|
173
151
|
end
|
|
174
152
|
|
|
175
153
|
def style_update(node, css)
|
|
176
|
-
|
|
177
|
-
|
|
154
|
+
node or return
|
|
178
155
|
node["style"] =
|
|
179
156
|
node["style"] ? node["style"].sub(/;?$/, ";#{css}") : css
|
|
180
157
|
end
|
|
@@ -182,11 +159,11 @@ module IsoDoc
|
|
|
182
159
|
def word_image_caption(docxml)
|
|
183
160
|
docxml.xpath("//p[@class = 'FigureTitle' or @class = 'SourceTitle']")
|
|
184
161
|
.each do |t|
|
|
185
|
-
if t
|
|
162
|
+
if t.previous_element&.name == "img"
|
|
186
163
|
img = t.previous_element
|
|
187
164
|
t.previous_element.swap("<p class='figure'>#{img.to_xml}</p>")
|
|
188
165
|
end
|
|
189
|
-
style_update(t
|
|
166
|
+
style_update(t.previous_element, "page-break-after:avoid;")
|
|
190
167
|
end
|
|
191
168
|
end
|
|
192
169
|
|
|
@@ -213,16 +190,14 @@ module IsoDoc
|
|
|
213
190
|
|
|
214
191
|
def word_table_align(docxml)
|
|
215
192
|
docxml.xpath("//td[@align]/p | //th[@align]/p").each do |p|
|
|
216
|
-
|
|
217
|
-
|
|
193
|
+
p["align"] and next
|
|
218
194
|
style_update(p, "text-align: #{p.parent['align']}")
|
|
219
195
|
end
|
|
220
196
|
end
|
|
221
197
|
|
|
222
198
|
def word_table_separator(docxml)
|
|
223
199
|
docxml.xpath("//p[@class = 'TableTitle']").each do |t|
|
|
224
|
-
|
|
225
|
-
|
|
200
|
+
t.children.empty? or next
|
|
226
201
|
t["style"] = t["style"].sub(/;?$/, ";font-size:0pt;")
|
|
227
202
|
t.children = " "
|
|
228
203
|
end
|
|
@@ -23,164 +23,36 @@ module IsoDoc
|
|
|
23
23
|
introxml.to_xml(encoding: "US-ASCII")
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
<<~TOC
|
|
54
|
-
<span lang="EN-GB"><span style='mso-element:field-begin'></span><span
|
|
55
|
-
style='mso-spacerun:yes'> </span>TOC \\o "1-#{level}" \\h \\z \\u <span
|
|
56
|
-
style='mso-element:field-separator'></span></span>
|
|
57
|
-
TOC
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
WORD_TOC_SUFFIX1 = <<~TOC.freeze
|
|
61
|
-
<p class="MsoToc1"><span lang="EN-GB"><span
|
|
62
|
-
style='mso-element:field-end'></span></span><span
|
|
63
|
-
lang="EN-GB"><o:p> </o:p></span></p>
|
|
64
|
-
TOC
|
|
65
|
-
|
|
66
|
-
def make_WordToC(docxml, level)
|
|
67
|
-
toc = ""
|
|
68
|
-
# docxml.xpath("//h1 | //h2[not(ancestor::*[@class = 'Section3'])]").
|
|
69
|
-
xpath = (1..level).each.map { |i| "//h#{i}" }.join (" | ")
|
|
70
|
-
docxml.xpath(xpath).each do |h|
|
|
71
|
-
toc += word_toc_entry(h.name[1].to_i, header_strip(h))
|
|
72
|
-
end
|
|
73
|
-
toc.sub(/(<p class="MsoToc1">)/,
|
|
74
|
-
%{\\1#{word_toc_preface(level)}}) + WORD_TOC_SUFFIX1
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
# inheriting gems need to add native Word name of style, if different
|
|
78
|
-
# including both CSS style name and human readable style name.
|
|
79
|
-
# Any human readable style name needs to come first for the Word template
|
|
80
|
-
# to work in regenerating the ToC
|
|
81
|
-
def table_toc_class
|
|
82
|
-
%w(TableTitle tabletitle)
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def figure_toc_class
|
|
86
|
-
%w(FigureTitle figuretitle)
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def reqt_toc_class
|
|
90
|
-
%w(RecommendationTitle RecommendationTestTitle
|
|
91
|
-
recommendationtitle recommendationtesttitle)
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def toc_word_class_list(classes)
|
|
95
|
-
classes.map do |x|
|
|
96
|
-
/ /.match?(x) ? %("#{x}") : x
|
|
97
|
-
end.join(",")
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def word_toc_reqt_preface1
|
|
101
|
-
<<~TOC
|
|
102
|
-
<span lang="EN-GB"><span style='mso-element:field-begin'></span><span
|
|
103
|
-
style='mso-spacerun:yes'> </span>TOC \\h \\z \\t #{toc_word_class_list reqt_toc_class}
|
|
104
|
-
<span style='mso-element:field-separator'></span></span>
|
|
105
|
-
TOC
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
def word_toc_table_preface1
|
|
109
|
-
<<~TOC
|
|
110
|
-
<span lang="EN-GB"><span style='mso-element:field-begin'></span><span style='mso-spacerun:yes'> </span>TOC
|
|
111
|
-
\\h \\z \\t #{toc_word_class_list table_toc_class} <span style='mso-element:field-separator'></span></span>
|
|
112
|
-
TOC
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
def word_toc_figure_preface1
|
|
116
|
-
<<~TOC
|
|
117
|
-
<span lang="EN-GB"><span style='mso-element:field-begin'></span><span style='mso-spacerun:yes'> </span>TOC
|
|
118
|
-
\\h \\z \\t #{toc_word_class_list figure_toc_class} <span style='mso-element:field-separator'></span></span>
|
|
119
|
-
TOC
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
def table_toc_xpath
|
|
123
|
-
attr = table_toc_class.map { |x| "@class = '#{x}'" }
|
|
124
|
-
"//p[#{attr.join(' or ')}]"
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
def make_table_word_toc(docxml)
|
|
128
|
-
(docxml.at(table_toc_xpath) && @toctablestitle) or return ""
|
|
129
|
-
toc = %{<p class="TOCTitle">#{@toctablestitle}</p>}
|
|
130
|
-
docxml.xpath(table_toc_xpath).each do |h|
|
|
131
|
-
toc += word_toc_entry(1, header_strip(h))
|
|
132
|
-
end
|
|
133
|
-
toc.sub(/(<p class="MsoToc1">)/,
|
|
134
|
-
%{\\1#{word_toc_table_preface1}}) + WORD_TOC_SUFFIX1
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
def figure_toc_xpath
|
|
138
|
-
attr = figure_toc_class.map { |x| "@class = '#{x}'" }
|
|
139
|
-
"//p[#{attr.join(' or ')}]"
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def make_figure_word_toc(docxml)
|
|
143
|
-
(docxml.at(figure_toc_xpath) && @tocfigurestitle) or return ""
|
|
144
|
-
toc = %{<p class="TOCTitle">#{@tocfigurestitle}</p>}
|
|
145
|
-
docxml.xpath(figure_toc_xpath).each do |h|
|
|
146
|
-
toc += word_toc_entry(1, header_strip(h))
|
|
147
|
-
end
|
|
148
|
-
toc.sub(/(<p class="MsoToc1">)/,
|
|
149
|
-
%{\\1#{word_toc_figure_preface1}}) + WORD_TOC_SUFFIX1
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
def reqt_toc_xpath
|
|
153
|
-
attr = reqt_toc_class.map { |x| "@class = '#{x}'" }
|
|
154
|
-
"//p[#{attr.join(' or ')}]"
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
def make_recommendation_word_toc(docxml)
|
|
158
|
-
(docxml.at(reqt_toc_xpath) && @tocrecommendationstitle) or return ""
|
|
159
|
-
toc = %{<p class="TOCTitle">#{@tocrecommendationstitle}</p>}
|
|
160
|
-
docxml.xpath(reqt_toc_xpath).sort_by do |h|
|
|
161
|
-
recommmendation_sort_key(h.text)
|
|
162
|
-
end.each do |h|
|
|
163
|
-
toc += word_toc_entry(1, header_strip(h))
|
|
164
|
-
end
|
|
165
|
-
toc.sub(/(<p class="MsoToc1">)/,
|
|
166
|
-
%{\\1#{word_toc_reqt_preface1}}) + WORD_TOC_SUFFIX1
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
def recommmendation_sort_key(header)
|
|
170
|
-
m = /^([^0-9]+) (\d+)/.match(header) || /^([^:]+)/.match(header)
|
|
171
|
-
m ||= [header, nil]
|
|
172
|
-
ret = "#{recommmendation_sort_key1(m[1])}::"
|
|
173
|
-
m[2] and ret += ("%04d" % m[2].to_i).to_s
|
|
174
|
-
ret
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
def recommmendation_sort_key1(type)
|
|
178
|
-
case type&.downcase
|
|
179
|
-
when "requirement" then "04"
|
|
180
|
-
when "recommendation" then "05"
|
|
181
|
-
when "permission" then "06"
|
|
182
|
-
else type
|
|
26
|
+
# add namespaces for Word fragments
|
|
27
|
+
WORD_NOKOHEAD = <<~HERE.freeze
|
|
28
|
+
<!DOCTYPE html SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
|
29
|
+
<html xmlns="http://www.w3.org/1999/xhtml"
|
|
30
|
+
xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office"
|
|
31
|
+
xmlns:w="urn:schemas-microsoft-com:office:word"
|
|
32
|
+
xmlns:m="http://schemas.microsoft.com/office/2004/12/omml">
|
|
33
|
+
<head> <title></title> <meta charset="UTF-8" /> </head>
|
|
34
|
+
<body> </body> </html>
|
|
35
|
+
HERE
|
|
36
|
+
|
|
37
|
+
def to_word_xhtml_fragment(xml)
|
|
38
|
+
doc = ::Nokogiri::XML.parse(WORD_NOKOHEAD)
|
|
39
|
+
::Nokogiri::XML::DocumentFragment.new(doc, xml, doc.root)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def wordstylesheet_update
|
|
43
|
+
@wordstylesheet.nil? and return
|
|
44
|
+
f = File.open(@wordstylesheet.path, "a")
|
|
45
|
+
@landscapestyle.empty? or f.write(@landscapestyle)
|
|
46
|
+
s = @meta.get[:code_css] and
|
|
47
|
+
f.write(s.gsub(/sourcecode/, "p.#{sourcecode_style}"))
|
|
48
|
+
if @wordstylesheet_override && @wordstylesheet
|
|
49
|
+
f.write(@wordstylesheet_override.read)
|
|
50
|
+
@wordstylesheet_override.close
|
|
51
|
+
elsif @wordstylesheet_override && !@wordstylesheet
|
|
52
|
+
@wordstylesheet = @wordstylesheet_override
|
|
183
53
|
end
|
|
54
|
+
f.close
|
|
55
|
+
@wordstylesheet
|
|
184
56
|
end
|
|
185
57
|
|
|
186
58
|
def authority_cleanup1(docxml, klass)
|