metanorma-ietf 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.hound.yml +3 -0
- data/.oss-guides.rubocop.yml +1077 -0
- data/.rspec +2 -0
- data/.rubocop.ribose.yml +65 -0
- data/.rubocop.tb.yml +650 -0
- data/.rubocop.yml +15 -0
- data/.travis.yml +23 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/Guardfile +22 -0
- data/LICENSE +25 -0
- data/README.adoc +1660 -0
- data/Rakefile +6 -0
- data/bin/asciidoctor-rfc2 +14 -0
- data/bin/asciidoctor-rfc3 +14 -0
- data/bin/console +14 -0
- data/bin/rspec +17 -0
- data/bin/setup +8 -0
- data/docs/installation.md +21 -0
- data/docs/navigation.md +10 -0
- data/docs/overview.md +5 -0
- data/lib/asciidoctor/rfc.rb +8 -0
- data/lib/asciidoctor/rfc/common/base.rb +531 -0
- data/lib/asciidoctor/rfc/common/front.rb +120 -0
- data/lib/asciidoctor/rfc/v2/base.rb +379 -0
- data/lib/asciidoctor/rfc/v2/blocks.rb +261 -0
- data/lib/asciidoctor/rfc/v2/converter.rb +60 -0
- data/lib/asciidoctor/rfc/v2/front.rb +69 -0
- data/lib/asciidoctor/rfc/v2/inline_anchor.rb +111 -0
- data/lib/asciidoctor/rfc/v2/lists.rb +135 -0
- data/lib/asciidoctor/rfc/v2/table.rb +114 -0
- data/lib/asciidoctor/rfc/v2/validate.rb +32 -0
- data/lib/asciidoctor/rfc/v2/validate2.rng +716 -0
- data/lib/asciidoctor/rfc/v3/base.rb +329 -0
- data/lib/asciidoctor/rfc/v3/blocks.rb +246 -0
- data/lib/asciidoctor/rfc/v3/converter.rb +62 -0
- data/lib/asciidoctor/rfc/v3/front.rb +122 -0
- data/lib/asciidoctor/rfc/v3/inline_anchor.rb +89 -0
- data/lib/asciidoctor/rfc/v3/lists.rb +176 -0
- data/lib/asciidoctor/rfc/v3/svg.rng +9081 -0
- data/lib/asciidoctor/rfc/v3/table.rb +65 -0
- data/lib/asciidoctor/rfc/v3/validate.rb +34 -0
- data/lib/asciidoctor/rfc/v3/validate.rng +2143 -0
- data/lib/metanorma-ietf.rb +7 -0
- data/lib/metanorma/ietf.rb +8 -0
- data/lib/metanorma/ietf/processor.rb +89 -0
- data/lib/metanorma/ietf/version.rb +5 -0
- data/metanorma-ietf.gemspec +51 -0
- data/rfc2629-other.ent +61 -0
- data/rfc2629-xhtml.ent +165 -0
- data/rfc2629.dtd +312 -0
- metadata +289 -0
@@ -0,0 +1,120 @@
|
|
1
|
+
module Asciidoctor
|
2
|
+
module Rfc::Common
|
3
|
+
module Front
|
4
|
+
def title(node, xml)
|
5
|
+
title_attributes = {
|
6
|
+
abbrev: node.attr("abbrev"),
|
7
|
+
}
|
8
|
+
xml.title **attr_code(title_attributes) do |t|
|
9
|
+
t << node.doctitle
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Syntax:
|
14
|
+
# = Title
|
15
|
+
# Author;Author_2;Author_3
|
16
|
+
# :fullname
|
17
|
+
# :lastname
|
18
|
+
# :organization
|
19
|
+
# :email
|
20
|
+
# :fullname_2
|
21
|
+
# :lastname_2
|
22
|
+
# :organization_2
|
23
|
+
# :email_2
|
24
|
+
# :fullname_3
|
25
|
+
# :lastname_3
|
26
|
+
# :organization_3
|
27
|
+
# :email_3
|
28
|
+
# @note recurse: author, author_2, author_3...
|
29
|
+
def author(node, xml)
|
30
|
+
author1(node, "", xml)
|
31
|
+
i = 2
|
32
|
+
loop do
|
33
|
+
suffix = "_#{i}"
|
34
|
+
author = node.attr("author#{suffix}")
|
35
|
+
fullname = node.attr("fullname#{suffix}")
|
36
|
+
break unless [author, fullname].any?
|
37
|
+
author1(node, suffix, xml)
|
38
|
+
i += 1
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Syntax:
|
43
|
+
# = Title
|
44
|
+
# Author (contains author firstname lastname middlename authorinitials email: Firstname Middlename Lastname <Email>)
|
45
|
+
# :fullname
|
46
|
+
# :lastname
|
47
|
+
# :forename_initials (excludes surname, unlike Asciidoc "initials" attribute)
|
48
|
+
# :organization
|
49
|
+
# :email
|
50
|
+
# :role
|
51
|
+
# :fax
|
52
|
+
# :uri
|
53
|
+
# :phone
|
54
|
+
# :postalLine (mutually exclusive with street city etc) (lines broken up by "\ ")
|
55
|
+
# :street
|
56
|
+
# :city
|
57
|
+
# :region
|
58
|
+
# :country
|
59
|
+
# :code
|
60
|
+
def author1(node, suffix, xml)
|
61
|
+
role = node.attr("role#{suffix}")
|
62
|
+
role = nil if role == "author"
|
63
|
+
author_attributes = {
|
64
|
+
fullname: node.attr("author#{suffix}") || node.attr("fullname#{suffix}"),
|
65
|
+
surname: node.attr("lastname#{suffix}"),
|
66
|
+
initials: node.attr("forename_initials#{suffix}"),
|
67
|
+
role: role,
|
68
|
+
}
|
69
|
+
|
70
|
+
xml.author **attr_code(author_attributes) do |xml_sub|
|
71
|
+
organization node, suffix, xml_sub
|
72
|
+
address node, suffix, xml_sub
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def date1(revdate, xml)
|
77
|
+
revdate.gsub!(/T.*$/, "")
|
78
|
+
if revdate.length == 4 && revdate =~ /^\d\d\d\d$/
|
79
|
+
date_attributes = {
|
80
|
+
year: revdate,
|
81
|
+
}
|
82
|
+
elsif revdate =~ /^\d\d\d\d-?\d\d$/
|
83
|
+
matched = /^(?<year>\d\d\d\d)-(?<month>\d\d)$/.match revdate
|
84
|
+
date_attributes = {
|
85
|
+
month: Date::MONTHNAMES[(matched[:month]).to_i],
|
86
|
+
year: matched[:year],
|
87
|
+
}
|
88
|
+
else
|
89
|
+
d = Date.iso8601 revdate
|
90
|
+
date_attributes = {
|
91
|
+
day: d.day.to_s.gsub(/^0/, ""),
|
92
|
+
month: Date::MONTHNAMES[d.month],
|
93
|
+
year: d.year,
|
94
|
+
}
|
95
|
+
end
|
96
|
+
xml.date **attr_code(date_attributes)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Syntax:
|
100
|
+
# = Title
|
101
|
+
# Author
|
102
|
+
# :revdate or :date
|
103
|
+
def date(node, xml)
|
104
|
+
revdate = node.attr("revdate") || node.attr("date")
|
105
|
+
if revdate.nil?
|
106
|
+
revdate = DateTime.now.iso8601
|
107
|
+
warn %(asciidoctor: WARNING (#{current_location(node)}): revdate attribute missing from header, provided current date)
|
108
|
+
end
|
109
|
+
unless revdate.nil?
|
110
|
+
begin
|
111
|
+
date1(revdate, xml)
|
112
|
+
rescue ArgumentError # invalid date
|
113
|
+
warn %(asciidoctor: WARNING (#{current_location(node)}): invalid date in header, provided current date)
|
114
|
+
date1(DateTime.now.iso8601, xml)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,379 @@
|
|
1
|
+
require "pp"
|
2
|
+
# coding: utf-8
|
3
|
+
module Asciidoctor
|
4
|
+
module Rfc::V2
|
5
|
+
module Base
|
6
|
+
# Syntax:
|
7
|
+
# =Title
|
8
|
+
# Author
|
9
|
+
# :status
|
10
|
+
# :consensus
|
11
|
+
# :name
|
12
|
+
# :number
|
13
|
+
#
|
14
|
+
# :ipr
|
15
|
+
# :obsoletes
|
16
|
+
# :updates
|
17
|
+
# :submissionType
|
18
|
+
# :indexInclude
|
19
|
+
# :ipr-extract
|
20
|
+
# :sort-refs
|
21
|
+
# :sym-refs
|
22
|
+
# :toc-include
|
23
|
+
#
|
24
|
+
# ABSTRACT
|
25
|
+
#
|
26
|
+
# NOTEs
|
27
|
+
#
|
28
|
+
# ==first title
|
29
|
+
# CONTENT
|
30
|
+
#
|
31
|
+
# [bibliography] # start of back matter
|
32
|
+
# == Bibliography
|
33
|
+
#
|
34
|
+
# [appendix] # start of back matter if not already started
|
35
|
+
# == Appendix
|
36
|
+
def document(node)
|
37
|
+
$seen_back_matter = false
|
38
|
+
$smart_quotes = (node.attr("smart-quotes") != "false")
|
39
|
+
$inline_definition_lists = (node.attr("inline-definition-lists") == "true")
|
40
|
+
|
41
|
+
result = []
|
42
|
+
result << '<?xml version="1.0" encoding="UTF-8"?>'
|
43
|
+
|
44
|
+
is_rfc = node.attr("doctype") == "rfc"
|
45
|
+
|
46
|
+
consensus_value = {
|
47
|
+
"false" => "no",
|
48
|
+
"true" => "yes",
|
49
|
+
}[node.attr("consensus")] || node.attr("consensus")
|
50
|
+
|
51
|
+
category = node.attr("status")
|
52
|
+
category = "info" if category == "informational"
|
53
|
+
category = "std" if category == "standard"
|
54
|
+
category = "exp" if category == "experimental"
|
55
|
+
|
56
|
+
rfc_attributes = {
|
57
|
+
ipr: node.attr("ipr"),
|
58
|
+
obsoletes: node.attr("obsoletes"),
|
59
|
+
updates: node.attr("updates"),
|
60
|
+
category: category,
|
61
|
+
consensus: consensus_value,
|
62
|
+
submissionType: node.attr("submission-type") || "IETF",
|
63
|
+
iprExtract: node.attr("ipr-extract"),
|
64
|
+
docName: (node.attr("name") unless is_rfc),
|
65
|
+
number: (node.attr("name") if is_rfc),
|
66
|
+
seriesNo: node.attr("series-no"),
|
67
|
+
"xml:lang": node.attr("xml-lang"),
|
68
|
+
}
|
69
|
+
|
70
|
+
rfc_open = noko { |xml| xml.rfc **attr_code(rfc_attributes) }.join.gsub(/\/>$/, ">")
|
71
|
+
result << rfc_open
|
72
|
+
|
73
|
+
result << noko { |xml| front node, xml }
|
74
|
+
result.last.last.gsub! /<\/front>$/, "" # FIXME: this is a hack!
|
75
|
+
result << "</front><middle1>"
|
76
|
+
|
77
|
+
result << node.content if node.blocks?
|
78
|
+
result << ($seen_back_matter ? "</back>" : "</middle>")
|
79
|
+
result << "</rfc>"
|
80
|
+
|
81
|
+
# <middle> needs to move after preamble
|
82
|
+
result = result.flatten
|
83
|
+
result = if result.any? { |e| e =~ /<\/front><middle>/ } && result.any? { |e| e =~ /<\/front><middle1>/ }
|
84
|
+
result.reject { |e| e =~ /<\/front><middle1>/ }
|
85
|
+
else
|
86
|
+
result.map { |e| e =~ /<\/front><middle1>/ ? "</front><middle>" : e }
|
87
|
+
end
|
88
|
+
|
89
|
+
ret = result * "\n"
|
90
|
+
ret = cleanup(ret)
|
91
|
+
ret1 = Nokogiri::XML(ret)
|
92
|
+
# Validate::validate(ret1)
|
93
|
+
ret1 = set_pis(node, ret1)
|
94
|
+
ret1 = insert_biblio(node, ret1) unless node.attr("biblio-dir").nil? || node.attr("biblio-dir").empty?
|
95
|
+
Validate::validate(ret1)
|
96
|
+
ret1 = resolve_references(node, ret1)
|
97
|
+
# Validate::validate(ret1)
|
98
|
+
ret1.to_xml
|
99
|
+
end
|
100
|
+
|
101
|
+
def resolve_references(node, doc)
|
102
|
+
extract_entities(node, doc).each do |entity|
|
103
|
+
Nokogiri::XML::EntityDecl::new(entity[:entity], doc,
|
104
|
+
Nokogiri::XML::EntityDecl::EXTERNAL_GENERAL_PARSED,
|
105
|
+
nil, entity[:url], nil)
|
106
|
+
entity[:node].replace(Nokogiri::XML::EntityReference.new(doc, entity[:entity]))
|
107
|
+
end
|
108
|
+
doc
|
109
|
+
end
|
110
|
+
|
111
|
+
def inline_break(node)
|
112
|
+
noko do |xml|
|
113
|
+
xml << node.text
|
114
|
+
xml.vspace
|
115
|
+
end.join
|
116
|
+
end
|
117
|
+
|
118
|
+
def inline_quoted(node)
|
119
|
+
noko do |xml|
|
120
|
+
case node.type
|
121
|
+
when :emphasis
|
122
|
+
xml.spanx node.text, style: "emph"
|
123
|
+
when :strong
|
124
|
+
xml.spanx node.text, style: "strong"
|
125
|
+
when :monospaced
|
126
|
+
xml.spanx node.text, style: "verb"
|
127
|
+
when :double
|
128
|
+
xml << ($smart_quotes ? "“#{node.text}”" : "\"#{node.text}\"")
|
129
|
+
when :single
|
130
|
+
xml << ($smart_quotes ? "‘#{node.text}’" : "'#{node.text}'")
|
131
|
+
when :superscript
|
132
|
+
xml << "^#{node.text}^"
|
133
|
+
when :subscript
|
134
|
+
xml << "_#{node.text}_"
|
135
|
+
else
|
136
|
+
# [bcp14]#MUST NOT#
|
137
|
+
if node.role == "bcp14"
|
138
|
+
xml.spanx node.text.upcase, style: "strong"
|
139
|
+
elsif node.role == "comment"
|
140
|
+
xml.comment " " + node.text + " "
|
141
|
+
else
|
142
|
+
xml << node.text
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end.join
|
146
|
+
end
|
147
|
+
|
148
|
+
# Syntax:
|
149
|
+
# [[id]]
|
150
|
+
# Text
|
151
|
+
def paragraph(node)
|
152
|
+
if node.role == "comment"
|
153
|
+
return noko do |xml|
|
154
|
+
xml.comment " " + [flatten_rawtext(node)].flatten.join("\n") + " "
|
155
|
+
end.join("\n")
|
156
|
+
end
|
157
|
+
|
158
|
+
t_attributes = {
|
159
|
+
anchor: node.id,
|
160
|
+
}
|
161
|
+
|
162
|
+
noko do |xml|
|
163
|
+
xml.t **attr_code(t_attributes) do |xml_t|
|
164
|
+
xml_t << node.content
|
165
|
+
end
|
166
|
+
end.join("\n")
|
167
|
+
end
|
168
|
+
|
169
|
+
def verse(node)
|
170
|
+
result = []
|
171
|
+
|
172
|
+
t_attributes = {
|
173
|
+
anchor: node.id,
|
174
|
+
}
|
175
|
+
|
176
|
+
result << noko do |xml|
|
177
|
+
xml.t **attr_code(t_attributes) do |xml_t|
|
178
|
+
xml_t << node.content.gsub("\n\n", "<vspace blankLines=\"1\"/>").gsub("\n", "<vspace/>\n")
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
result
|
183
|
+
end
|
184
|
+
|
185
|
+
# Syntax:
|
186
|
+
# [[id]]
|
187
|
+
# == title
|
188
|
+
# Content
|
189
|
+
#
|
190
|
+
# [bibliography]
|
191
|
+
# == References
|
192
|
+
#
|
193
|
+
# [bibliography]
|
194
|
+
# === Normative|Informative References
|
195
|
+
# ++++
|
196
|
+
# RFC XML references
|
197
|
+
# ++++
|
198
|
+
def section(node)
|
199
|
+
result = []
|
200
|
+
if node.attr("style") == "bibliography" ||
|
201
|
+
node.parent.context == :section && node.parent.attr("style") == "bibliography"
|
202
|
+
$xreftext = {}
|
203
|
+
$processing_reflist = true
|
204
|
+
|
205
|
+
references_attributes = {
|
206
|
+
title: node.title,
|
207
|
+
}
|
208
|
+
if node.blocks.empty?
|
209
|
+
result << noko { |xml| xml.references **references_attributes }
|
210
|
+
end
|
211
|
+
node.blocks.each do |block|
|
212
|
+
if block.context == :section
|
213
|
+
result << node.content
|
214
|
+
elsif block.context == :pass
|
215
|
+
# NOTE: references are assumed to be found in a single passthrough
|
216
|
+
# block containing <reference> tags.
|
217
|
+
|
218
|
+
result << noko do |xml|
|
219
|
+
xml.references **references_attributes do |xml_references|
|
220
|
+
# NOTE: we're allowing the user to do more or less whathever
|
221
|
+
# in the passthrough since the xpath below just fishes out ALL
|
222
|
+
# <reference>s in an unrooted fragment, regardless of structure.
|
223
|
+
Nokogiri::XML::DocumentFragment.
|
224
|
+
parse(block.content).xpath(".//reference").
|
225
|
+
each { |reference| xml_references << reference.to_xml }
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
result = result.unshift("</middle><back>") unless $seen_back_matter
|
232
|
+
$processing_reflist = false
|
233
|
+
$seen_back_matter = true
|
234
|
+
else
|
235
|
+
if node.attr("style") == "appendix"
|
236
|
+
result << "</middle><back>" unless $seen_back_matter
|
237
|
+
$seen_back_matter = true
|
238
|
+
end
|
239
|
+
|
240
|
+
section_attributes = {
|
241
|
+
anchor: node.id,
|
242
|
+
title: node.title.gsub(/<[^>]+>/, ""),
|
243
|
+
}
|
244
|
+
|
245
|
+
result << noko do |xml|
|
246
|
+
xml.section **attr_code(section_attributes) do |xml_section|
|
247
|
+
xml_section << node.content
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
result
|
253
|
+
end
|
254
|
+
|
255
|
+
# Syntax:
|
256
|
+
# [[id]]
|
257
|
+
# .Name
|
258
|
+
# [link=xxx,align=left|center|right,alt=alt_text,type]
|
259
|
+
# image::filename[alt_text,width,height]
|
260
|
+
# @note ignoring width, height attributes
|
261
|
+
def image(node)
|
262
|
+
uri = node.image_uri node.attr("target")
|
263
|
+
artwork_attributes = {
|
264
|
+
align: node.attr("align"),
|
265
|
+
alt: node.alt,
|
266
|
+
height: node.attr("height"),
|
267
|
+
name: node.title,
|
268
|
+
src: uri,
|
269
|
+
type: node.attr("type"),
|
270
|
+
width: node.attr("width"),
|
271
|
+
}
|
272
|
+
|
273
|
+
noko do |xml|
|
274
|
+
if node.parent.context != :example
|
275
|
+
figure_attributes = {
|
276
|
+
anchor: node.id,
|
277
|
+
}
|
278
|
+
xml.figure **attr_code(figure_attributes) do |xml_figure|
|
279
|
+
xml_figure.artwork **attr_code(artwork_attributes)
|
280
|
+
end
|
281
|
+
else
|
282
|
+
xml.artwork **attr_code(artwork_attributes)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
# clean up XML
|
288
|
+
def cleanup(doc)
|
289
|
+
xmldoc = Nokogiri::XML(doc) do |config|
|
290
|
+
config.noent
|
291
|
+
end
|
292
|
+
|
293
|
+
# any crefs that are direct children of section should become children of the preceding
|
294
|
+
# paragraph, if it exists; otherwise, they need to be wrapped in a paragraph
|
295
|
+
crefs = xmldoc.xpath("//cref")
|
296
|
+
crefs.each do |cref|
|
297
|
+
if cref.parent.name == "section"
|
298
|
+
prev = cref.previous_element
|
299
|
+
if !prev.nil? && prev.name == "t"
|
300
|
+
cref.parent = prev
|
301
|
+
else
|
302
|
+
t = Nokogiri::XML::Element.new("t", xmldoc)
|
303
|
+
cref.before(t)
|
304
|
+
cref.parent = t
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
# any instances of spanx must be stripped of any internal tags
|
309
|
+
spanxs = xmldoc.xpath("//spanx[descendant::*]")
|
310
|
+
while !spanxs.empty?
|
311
|
+
spanx = spanxs[0]
|
312
|
+
spanx_text = ""
|
313
|
+
spanx.traverse do |node|
|
314
|
+
spanx_text = spanx_text + node.text.gsub(/<[^>]+>/, "") if node.text?
|
315
|
+
end
|
316
|
+
spanx.children = spanx_text
|
317
|
+
spanxs = xmldoc.xpath("//spanx[descendant::*]")
|
318
|
+
end
|
319
|
+
|
320
|
+
xmldoc.root = merge_vspace(xmldoc.root)
|
321
|
+
xmldoc = smart_quote_cleanup(xmldoc) unless $smart_quotes
|
322
|
+
xmldoc.to_xml(encoding: "US-ASCII")
|
323
|
+
end
|
324
|
+
|
325
|
+
def merge_vspace(node)
|
326
|
+
nodes = []
|
327
|
+
newnodes = []
|
328
|
+
node.children.each do |element|
|
329
|
+
nodes << element
|
330
|
+
end
|
331
|
+
|
332
|
+
counter = 0
|
333
|
+
while counter < nodes.size
|
334
|
+
if nodes[counter].name == "vspace"
|
335
|
+
blankLines = 0
|
336
|
+
while counter < nodes.size && nodes[counter].name == "vspace"
|
337
|
+
blankLines += 1
|
338
|
+
if nodes[counter][:blankLines].nil?
|
339
|
+
blankLines += nodes[counter][:blankLines].to_i
|
340
|
+
end
|
341
|
+
if counter + 1 < nodes.size && nodes[counter + 1].text? && nodes[counter + 1].text =~ /\A[\n ]+\Z/m
|
342
|
+
counter += 1
|
343
|
+
end
|
344
|
+
counter += 1
|
345
|
+
end
|
346
|
+
counter -= 1 if counter == nodes.size
|
347
|
+
newnodes << noko do |xml|
|
348
|
+
xml.vspace **attr_code(blankLines: (blankLines - 1))
|
349
|
+
end.join
|
350
|
+
else
|
351
|
+
newnodes << merge_vspace(nodes[counter])
|
352
|
+
counter += 1
|
353
|
+
end
|
354
|
+
end
|
355
|
+
node.children.remove
|
356
|
+
newnodes.each do |item|
|
357
|
+
node.add_child(item)
|
358
|
+
end
|
359
|
+
node
|
360
|
+
end
|
361
|
+
|
362
|
+
# replace any <t>text</t> instances with <vspace blankLines="1"/>text
|
363
|
+
def para_to_vspace(doc)
|
364
|
+
xmldoc = Nokogiri::XML("<fragment>#{doc}</fragment>")
|
365
|
+
paras = xmldoc.xpath("/fragment/t")
|
366
|
+
paras.each do |para|
|
367
|
+
# we do not insert vspace if the para contains a list: space will go there anyway
|
368
|
+
unless para.element_children.size == 1 && para.element_children[0].name == "list"
|
369
|
+
vspace = Nokogiri::XML::Element.new("vspace", xmldoc.document)
|
370
|
+
vspace["blankLines"] = "1"
|
371
|
+
para.before(vspace)
|
372
|
+
end
|
373
|
+
para.replace(para.children)
|
374
|
+
end
|
375
|
+
xmldoc.root.children.to_xml(encoding: "US-ASCII")
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
379
|
+
end
|