isodoc 1.7.4 → 1.7.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/isodoc.gemspec +7 -5
- data/lib/isodoc/class_utils.rb +25 -2
- data/lib/isodoc/convert.rb +2 -0
- data/lib/isodoc/function/to_word_html.rb +2 -1
- data/lib/isodoc/function/utils.rb +34 -14
- data/lib/isodoc/html_function/comments.rb +107 -111
- data/lib/isodoc/html_function/footnotes.rb +68 -67
- data/lib/isodoc/html_function/html.rb +113 -103
- data/lib/isodoc/presentation_function/block.rb +73 -78
- data/lib/isodoc/presentation_function/concept.rb +68 -0
- data/lib/isodoc/presentation_function/image.rb +112 -0
- data/lib/isodoc/presentation_function/inline.rb +20 -49
- data/lib/isodoc/presentation_xml_convert.rb +1 -0
- data/lib/isodoc/version.rb +1 -1
- data/lib/isodoc/word_function/body.rb +176 -174
- data/lib/isodoc/word_function/comments.rb +117 -112
- data/lib/isodoc/word_function/footnotes.rb +88 -86
- data/lib/isodoc/word_function/inline.rb +42 -67
- data/lib/isodoc/word_function/postprocess_cover.rb +121 -110
- data/lib/isodoc/xref/xref_gen.rb +153 -150
- data/lib/isodoc/xref/xref_sect_gen.rb +134 -129
- data/lib/isodoc/xslfo_convert.rb +7 -6
- data/spec/assets/i18n.yaml +3 -1
- data/spec/assets/odf.svg +1 -4
- data/spec/isodoc/blocks_spec.rb +229 -157
- data/spec/isodoc/i18n_spec.rb +3 -3
- data/spec/isodoc/inline_spec.rb +304 -105
- data/spec/isodoc/postproc_spec.rb +38 -0
- data/spec/isodoc/presentation_xml_spec.rb +60 -0
- data/spec/isodoc/section_spec.rb +125 -0
- data/spec/isodoc/terms_spec.rb +116 -0
- data/spec/isodoc/xslfo_convert_spec.rb +16 -4
- metadata +57 -27
@@ -1,145 +1,150 @@
|
|
1
|
-
module IsoDoc
|
2
|
-
module
|
1
|
+
module IsoDoc
|
2
|
+
module WordFunction
|
3
|
+
module Comments
|
4
|
+
def in_comment
|
5
|
+
@in_comment
|
6
|
+
end
|
3
7
|
|
4
|
-
|
5
|
-
|
6
|
-
end
|
8
|
+
def comments(div)
|
9
|
+
return if @comments.empty?
|
7
10
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
@comments.each { |fn| div1.parent << fn }
|
11
|
+
div.div **{ style: "mso-element:comment-list" } do |div1|
|
12
|
+
@comments.each { |fn| div1.parent << fn }
|
13
|
+
end
|
12
14
|
end
|
13
|
-
end
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
16
|
+
def review_note_parse(node, out)
|
17
|
+
fn = @comments.length + 1
|
18
|
+
make_comment_link(out, fn, node)
|
19
|
+
@in_comment = true
|
20
|
+
@comments << make_comment_text(node, fn)
|
21
|
+
@in_comment = false
|
22
|
+
end
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
def comment_link_attrs(fnote, node)
|
25
|
+
{ style: "MsoCommentReference", target: fnote,
|
26
|
+
class: "commentLink", from: node["from"],
|
27
|
+
to: node["to"] }
|
28
|
+
end
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
30
|
+
# add in from and to links to move the comment into place
|
31
|
+
def make_comment_link(out, fnote, node)
|
32
|
+
out.span(**comment_link_attrs(fnote, node)) do |s1|
|
33
|
+
s1.span **{ lang: "EN-GB", style: "font-size:9.0pt" } do |s2|
|
34
|
+
s2.a **{ style: "mso-comment-reference:SMC_#{fnote};"\
|
35
|
+
"mso-comment-date:#{node['date'].gsub(/[:-]+/,
|
36
|
+
'')}" }
|
37
|
+
s2.span **{ style: "mso-special-character:comment",
|
38
|
+
target: fnote } # do |s|
|
39
|
+
end
|
37
40
|
end
|
38
41
|
end
|
39
|
-
end
|
40
42
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
43
|
+
def make_comment_target(out)
|
44
|
+
out.span **{ style: "MsoCommentReference" } do |s1|
|
45
|
+
s1.span **{ lang: "EN-GB", style: "font-size:9.0pt" } do |s2|
|
46
|
+
s2.span **{ style: "mso-special-character:comment" }
|
47
|
+
end
|
45
48
|
end
|
46
49
|
end
|
47
|
-
end
|
48
50
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
51
|
+
def make_comment_text(node, fnote)
|
52
|
+
noko do |xml|
|
53
|
+
xml.div **{ style: "mso-element:comment", id: fnote } do |div|
|
54
|
+
div.span **{ style: %{mso-comment-author:"#{node['reviewer']}"} }
|
55
|
+
make_comment_target(div)
|
56
|
+
node.children.each { |n| parse(n, div) }
|
57
|
+
end
|
58
|
+
end.join("\n")
|
59
|
+
end
|
58
60
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
61
|
+
def comment_cleanup(docxml)
|
62
|
+
move_comment_link_to_from(docxml)
|
63
|
+
reorder_comments_by_comment_link(docxml)
|
64
|
+
embed_comment_in_comment_list(docxml)
|
65
|
+
end
|
64
66
|
|
65
|
-
|
66
|
-
|
67
|
-
|
67
|
+
COMMENT_IN_COMMENT_LIST1 =
|
68
|
+
'//div[@style="mso-element:comment-list"]//'\
|
69
|
+
'span[@style="MsoCommentReference"]'.freeze
|
68
70
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
71
|
+
def embed_comment_in_comment_list(docxml)
|
72
|
+
# docxml.xpath(COMMENT_IN_COMMENT_LIST).each do |x|
|
73
|
+
docxml.xpath(COMMENT_IN_COMMENT_LIST1).each do |x|
|
74
|
+
n = x.next_element
|
75
|
+
n&.children&.first&.add_previous_sibling(x.remove)
|
76
|
+
end
|
77
|
+
docxml
|
74
78
|
end
|
75
|
-
docxml
|
76
|
-
end
|
77
79
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
80
|
+
def move_comment_link_to_from1(tolink, fromlink)
|
81
|
+
tolink.remove
|
82
|
+
link = tolink.at(".//a")
|
83
|
+
fromlink.replace(tolink)
|
84
|
+
link.children = fromlink
|
85
|
+
end
|
84
86
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
tolink = docxml.at("//*[@id='#{x['to']}']") || fromlink
|
89
|
-
target = docxml.at("//*[@id='#{x['target']}']")
|
90
|
-
{ from: fromlink, to: tolink, target: target }
|
91
|
-
end
|
87
|
+
def comment_attributes(docxml, x)
|
88
|
+
fromlink = docxml.at("//*[@id='#{x['from']}']")
|
89
|
+
return(nil) if fromlink.nil?
|
92
90
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
91
|
+
tolink = docxml.at("//*[@id='#{x['to']}']") || fromlink
|
92
|
+
target = docxml.at("//*[@id='#{x['target']}']")
|
93
|
+
{ from: fromlink, to: tolink, target: target }
|
94
|
+
end
|
97
95
|
|
98
|
-
|
99
|
-
|
100
|
-
|
96
|
+
def wrap_comment_cont(from, target)
|
97
|
+
s = from.replace("<span style='mso-comment-continuation:#{target}'>")
|
98
|
+
s.first.children = from
|
99
|
+
end
|
100
|
+
|
101
|
+
def skip_comment_wrap(from)
|
102
|
+
from["style"] != "mso-special-character:comment"
|
103
|
+
end
|
101
104
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
105
|
+
def insert_comment_cont(from, upto, target)
|
106
|
+
# includes_to = from.at(".//*[@id='#{upto}']")
|
107
|
+
while !from.nil? && from["id"] != upto
|
108
|
+
following = from.xpath("./following::*")
|
109
|
+
(from = following.shift) && incl_to = from.at(".//*[@id='#{upto}']")
|
110
|
+
while !incl_to.nil? && !from.nil? && skip_comment_wrap(from)
|
111
|
+
(from = following.shift) && incl_to = from.at(".//*[@id='#{upto}']")
|
112
|
+
end
|
113
|
+
wrap_comment_cont(from, target) if !from.nil?
|
109
114
|
end
|
110
|
-
wrap_comment_cont(from, target) if !from.nil?
|
111
115
|
end
|
112
|
-
end
|
113
116
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
117
|
+
def move_comment_link_to_from(docxml)
|
118
|
+
docxml.xpath('//span[@style="MsoCommentReference"][@from]').each do |x|
|
119
|
+
attrs = comment_attributes(docxml, x) || next
|
120
|
+
move_comment_link_to_from1(x, attrs[:from])
|
121
|
+
insert_comment_cont(attrs[:from], x["to"], x["target"])
|
122
|
+
end
|
119
123
|
end
|
120
|
-
end
|
121
124
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
125
|
+
def get_comments_from_text(docxml, link_order)
|
126
|
+
comments = []
|
127
|
+
docxml.xpath("//div[@style='mso-element:comment']").each do |c|
|
128
|
+
next unless c["id"] && !link_order[c["id"]].nil?
|
129
|
+
|
130
|
+
comments << { text: c.remove.to_s, id: c["id"] }
|
131
|
+
end
|
132
|
+
comments.sort! { |a, b| link_order[a[:id]] <=> link_order[b[:id]] }
|
133
|
+
# comments
|
127
134
|
end
|
128
|
-
comments.sort! { |a, b| link_order[a[:id]] <=> link_order[b[:id]] }
|
129
|
-
# comments
|
130
|
-
end
|
131
135
|
|
132
|
-
|
133
|
-
|
136
|
+
COMMENT_TARGET_XREFS1 =
|
137
|
+
"//span[@style='mso-special-character:comment']/@target".freeze
|
134
138
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
+
def reorder_comments_by_comment_link(docxml)
|
140
|
+
link_order = {}
|
141
|
+
docxml.xpath(COMMENT_TARGET_XREFS1).each_with_index do |target, i|
|
142
|
+
link_order[target.value] = i
|
143
|
+
end
|
144
|
+
comments = get_comments_from_text(docxml, link_order)
|
145
|
+
list = docxml.at("//*[@style='mso-element:comment-list']") || return
|
146
|
+
list.children = comments.map { |c| c[:text] }.join("\n")
|
139
147
|
end
|
140
|
-
comments = get_comments_from_text(docxml, link_order)
|
141
|
-
list = docxml.at("//*[@style='mso-element:comment-list']") || return
|
142
|
-
list.children = comments.map { |c| c[:text] }.join("\n")
|
143
148
|
end
|
144
149
|
end
|
145
150
|
end
|
@@ -1,112 +1,114 @@
|
|
1
|
-
module IsoDoc
|
2
|
-
module
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
ret
|
1
|
+
module IsoDoc
|
2
|
+
module WordFunction
|
3
|
+
module Footnotes
|
4
|
+
def bookmarkid
|
5
|
+
ret = "X"
|
6
|
+
until !@bookmarks_allocated[ret]
|
7
|
+
ret = Random.rand(1000000000)
|
8
|
+
end
|
9
|
+
@bookmarks_allocated[ret] = true
|
10
|
+
sprintf "%09d", ret
|
7
11
|
end
|
8
|
-
@bookmarks_allocated[ret] = true
|
9
|
-
sprintf "%09d", ret
|
10
|
-
end
|
11
|
-
|
12
|
-
def footnotes(div)
|
13
|
-
return if @footnotes.empty?
|
14
12
|
|
15
|
-
|
16
|
-
|
13
|
+
def footnotes(div)
|
14
|
+
return if @footnotes.empty?
|
17
15
|
|
18
|
-
|
19
|
-
attrs = { href: "##{fnid}", class: "TableFootnoteRef" }
|
20
|
-
out.a **attrs do |a|
|
21
|
-
a << fnref
|
16
|
+
@footnotes.each { |fn| div.parent << fn }
|
22
17
|
end
|
23
|
-
end
|
24
18
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
out.span **attrs do |a|
|
19
|
+
def make_table_footnote_link(out, fnid, fnref)
|
20
|
+
attrs = { href: "##{fnid}", class: "TableFootnoteRef" }
|
21
|
+
out.a **attrs do |a|
|
29
22
|
a << fnref
|
30
23
|
end
|
31
|
-
insert_tab(s, 1)
|
32
24
|
end
|
33
|
-
end
|
34
25
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
26
|
+
def make_table_footnote_target(out, fnid, fnref)
|
27
|
+
attrs = { id: fnid, class: "TableFootnoteRef" }
|
28
|
+
out.span do |s|
|
29
|
+
out.span **attrs do |a|
|
30
|
+
a << fnref
|
31
|
+
end
|
32
|
+
insert_tab(s, 1)
|
41
33
|
end
|
42
|
-
end
|
43
|
-
end
|
34
|
+
end
|
44
35
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
36
|
+
def make_table_footnote_text(node, fnid, fnref)
|
37
|
+
attrs = { id: "ftn#{fnid}" }
|
38
|
+
noko do |xml|
|
39
|
+
xml.div **attr_code(attrs) do |div|
|
40
|
+
make_table_footnote_target(div, fnid, fnref)
|
41
|
+
node.children.each { |n| parse(n, div) }
|
42
|
+
end
|
43
|
+
end.join("\n")
|
44
|
+
end
|
52
45
|
|
53
|
-
|
54
|
-
|
55
|
-
|
46
|
+
def make_generic_footnote_text(node, fnid)
|
47
|
+
noko do |xml|
|
48
|
+
xml.aside **{ id: "ftn#{fnid}" } do |div|
|
49
|
+
node.children.each { |n| parse(n, div) }
|
50
|
+
end
|
51
|
+
end.join("\n")
|
52
|
+
end
|
56
53
|
|
57
|
-
|
58
|
-
|
54
|
+
def get_table_ancestor_id(node)
|
55
|
+
table = node.ancestors("table") || node.ancestors("figure")
|
56
|
+
return UUIDTools::UUID.random_create.to_s if table.empty?
|
59
57
|
|
60
|
-
|
61
|
-
|
62
|
-
tid = get_table_ancestor_id(node)
|
63
|
-
make_table_footnote_link(out, tid + fn, fn)
|
64
|
-
# do not output footnote text if we have already seen it for this table
|
65
|
-
return if @seen_footnote.include?(tid + fn)
|
66
|
-
|
67
|
-
@in_footnote = true
|
68
|
-
out.aside { |a| a << make_table_footnote_text(node, tid + fn, fn) }
|
69
|
-
@in_footnote = false
|
70
|
-
@seen_footnote << (tid + fn)
|
71
|
-
end
|
58
|
+
table.last["id"]
|
59
|
+
end
|
72
60
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
61
|
+
def table_footnote_parse(node, out)
|
62
|
+
fn = node["reference"] || UUIDTools::UUID.random_create.to_s
|
63
|
+
tid = get_table_ancestor_id(node)
|
64
|
+
make_table_footnote_link(out, tid + fn, fn)
|
65
|
+
# do not output footnote text if we have already seen it for this table
|
66
|
+
return if @seen_footnote.include?(tid + fn)
|
67
|
+
|
68
|
+
@in_footnote = true
|
69
|
+
out.aside { |a| a << make_table_footnote_text(node, tid + fn, fn) }
|
70
|
+
@in_footnote = false
|
71
|
+
@seen_footnote << (tid + fn)
|
79
72
|
end
|
80
|
-
out.span **{ style: "mso-element:field-end" }
|
81
|
-
end
|
82
73
|
|
83
|
-
|
84
|
-
|
85
|
-
|
74
|
+
def seen_footnote_parse(_node, out, footnote)
|
75
|
+
out.span **{ style: "mso-element:field-begin" }
|
76
|
+
out << " NOTEREF _Ref#{@fn_bookmarks[footnote]} \\f \\h"
|
77
|
+
out.span **{ style: "mso-element:field-separator" }
|
78
|
+
out.span **{ class: "MsoFootnoteReference" } do |s|
|
79
|
+
s << footnote
|
80
|
+
end
|
81
|
+
out.span **{ style: "mso-element:field-end" }
|
82
|
+
end
|
83
|
+
|
84
|
+
def footnote_parse(node, out)
|
85
|
+
return table_footnote_parse(node, out) if (@in_table || @in_figure) &&
|
86
|
+
!node.ancestors.map(&:name).include?("name")
|
86
87
|
|
87
|
-
|
88
|
-
|
88
|
+
fn = node["reference"] || UUIDTools::UUID.random_create.to_s
|
89
|
+
return seen_footnote_parse(node, out, fn) if @seen_footnote.include?(fn)
|
89
90
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
91
|
+
@fn_bookmarks[fn] = bookmarkid
|
92
|
+
out.span **{ style: "mso-bookmark:_Ref#{@fn_bookmarks[fn]}" } do |s|
|
93
|
+
s.a **{ class: "FootnoteRef", "epub:type": "footnote",
|
94
|
+
href: "#ftn#{fn}" } do |a|
|
95
|
+
a.sup { |sup| sup << fn }
|
96
|
+
end
|
95
97
|
end
|
98
|
+
@in_footnote = true
|
99
|
+
@footnotes << make_generic_footnote_text(node, fn)
|
100
|
+
@in_footnote = false
|
101
|
+
@seen_footnote << fn
|
96
102
|
end
|
97
|
-
@in_footnote = true
|
98
|
-
@footnotes << make_generic_footnote_text(node, fn)
|
99
|
-
@in_footnote = false
|
100
|
-
@seen_footnote << fn
|
101
|
-
end
|
102
103
|
|
103
|
-
|
104
|
-
|
104
|
+
def make_footnote(node, footnote)
|
105
|
+
return if @seen_footnote.include?(footnote)
|
105
106
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
107
|
+
@in_footnote = true
|
108
|
+
@footnotes << make_generic_footnote_text(node, footnote)
|
109
|
+
@in_footnote = false
|
110
|
+
@seen_footnote << footnote
|
111
|
+
end
|
110
112
|
end
|
111
113
|
end
|
112
114
|
end
|
@@ -1,83 +1,58 @@
|
|
1
|
-
module IsoDoc
|
2
|
-
module
|
3
|
-
|
4
|
-
body
|
5
|
-
p
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
def page_break(out)
|
10
|
-
out.p do |p|
|
11
|
-
p.br **{ clear: "all",
|
12
|
-
style: "mso-special-character:line-break;"\
|
13
|
-
"page-break-before:always" }
|
1
|
+
module IsoDoc
|
2
|
+
module WordFunction
|
3
|
+
module Body
|
4
|
+
def section_break(body)
|
5
|
+
body.p do |p|
|
6
|
+
p.br **{ clear: "all", class: "section" }
|
7
|
+
end
|
14
8
|
end
|
15
|
-
end
|
16
9
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
10
|
+
def page_break(out)
|
11
|
+
out.p do |p|
|
12
|
+
p.br **{ clear: "all",
|
13
|
+
style: "mso-special-character:line-break;"\
|
14
|
+
"page-break-before:always" }
|
15
|
+
end
|
22
16
|
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def svg_parse(node, out)
|
26
|
-
svg = Base64.strict_encode64(node.to_xml)
|
27
|
-
r = node.replace("<img src='data:image/svg+xml;base64,#{svg}' mimetype='image/svg+xml'/>").first
|
28
|
-
image_parse(r, out, nil)
|
29
|
-
end
|
30
17
|
|
31
|
-
|
32
|
-
|
33
|
-
return node["src"] unless %r{^data:}.match node["src"]
|
34
|
-
save_dataimage(node["src"])
|
35
|
-
end
|
18
|
+
def pagebreak_parse(node, out)
|
19
|
+
return page_break(out) if node["orientation"].nil?
|
36
20
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
width: node["width"] }
|
43
|
-
out.img **attr_code(attrs)
|
44
|
-
image_title_parse(out, caption)
|
45
|
-
end
|
21
|
+
out.p do |p|
|
22
|
+
p.br **{ clear: "all", class: "section",
|
23
|
+
orientation: node["orientation"] }
|
24
|
+
end
|
25
|
+
end
|
46
26
|
|
47
|
-
|
48
|
-
|
49
|
-
end
|
27
|
+
def imgsrc(node)
|
28
|
+
return node["src"] unless %r{^data:}.match? node["src"]
|
50
29
|
|
51
|
-
|
52
|
-
|
53
|
-
uri = node["src"]
|
54
|
-
%r{^data:}.match(uri) and uri = save_dataimage(uri)
|
55
|
-
ret = svg_to_emf_filename(uri)
|
56
|
-
File.exists?(ret) and return ret
|
57
|
-
exe = inkscape_installed? or return nil
|
58
|
-
system %(#{exe} --export-type="emf" #{uri}) and
|
59
|
-
return ret
|
60
|
-
nil
|
61
|
-
end
|
30
|
+
save_dataimage(node["src"])
|
31
|
+
end
|
62
32
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
exe = File.join(path, "#{cmd}#{ext}")
|
69
|
-
return exe if File.executable?(exe) && !File.directory?(exe)
|
33
|
+
def image_parse(node, out, caption)
|
34
|
+
if emf = node.at(ns("./emf"))
|
35
|
+
node["src"] = emf["src"]
|
36
|
+
node["mimetype"] = "image/x-emf"
|
37
|
+
node.children.remove
|
70
38
|
end
|
39
|
+
attrs = { src: imgsrc(node),
|
40
|
+
height: node["height"], alt: node["alt"],
|
41
|
+
title: node["title"], width: node["width"] }
|
42
|
+
out.img **attr_code(attrs)
|
43
|
+
image_title_parse(out, caption)
|
71
44
|
end
|
72
|
-
nil
|
73
|
-
end
|
74
45
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
46
|
+
def xref_parse(node, out)
|
47
|
+
target = if /#/.match?(node["target"])
|
48
|
+
node["target"].sub(/#/, ".doc#")
|
49
|
+
else
|
50
|
+
"##{node['target']}"
|
51
|
+
end
|
52
|
+
out.a(**{ href: target }) do |l|
|
79
53
|
node.children.each { |n| parse(n, l) }
|
80
54
|
end
|
55
|
+
end
|
81
56
|
end
|
82
57
|
end
|
83
58
|
end
|