asciidoctor 2.0.12 → 2.0.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +142 -22
- data/LICENSE +1 -1
- data/README-de.adoc +15 -6
- data/README-fr.adoc +14 -8
- data/README-jp.adoc +15 -6
- data/README-zh_CN.adoc +14 -5
- data/README.adoc +135 -125
- data/asciidoctor.gemspec +4 -11
- data/data/locale/attributes-be.adoc +23 -0
- data/data/locale/attributes-it.adoc +4 -4
- data/data/locale/attributes-nl.adoc +6 -6
- data/data/locale/attributes-th.adoc +23 -0
- data/data/locale/attributes-vi.adoc +23 -0
- data/data/reference/syntax.adoc +14 -7
- data/data/stylesheets/asciidoctor-default.css +51 -52
- data/lib/asciidoctor.rb +12 -12
- data/lib/asciidoctor/abstract_block.rb +4 -4
- data/lib/asciidoctor/abstract_node.rb +10 -9
- data/lib/asciidoctor/attribute_list.rb +6 -6
- data/lib/asciidoctor/block.rb +6 -6
- data/lib/asciidoctor/cli/invoker.rb +0 -1
- data/lib/asciidoctor/cli/options.rb +27 -26
- data/lib/asciidoctor/convert.rb +1 -0
- data/lib/asciidoctor/converter.rb +5 -3
- data/lib/asciidoctor/converter/docbook5.rb +45 -26
- data/lib/asciidoctor/converter/html5.rb +89 -69
- data/lib/asciidoctor/converter/manpage.rb +113 -86
- data/lib/asciidoctor/converter/template.rb +11 -12
- data/lib/asciidoctor/document.rb +44 -51
- data/lib/asciidoctor/extensions.rb +10 -12
- data/lib/asciidoctor/helpers.rb +3 -6
- data/lib/asciidoctor/list.rb +2 -6
- data/lib/asciidoctor/load.rb +13 -11
- data/lib/asciidoctor/logging.rb +10 -8
- data/lib/asciidoctor/parser.rb +135 -150
- data/lib/asciidoctor/path_resolver.rb +3 -3
- data/lib/asciidoctor/reader.rb +72 -71
- data/lib/asciidoctor/rx.rb +4 -3
- data/lib/asciidoctor/substitutors.rb +117 -117
- data/lib/asciidoctor/syntax_highlighter.rb +8 -11
- data/lib/asciidoctor/syntax_highlighter/coderay.rb +2 -1
- data/lib/asciidoctor/syntax_highlighter/highlightjs.rb +1 -1
- data/lib/asciidoctor/syntax_highlighter/pygments.rb +6 -5
- data/lib/asciidoctor/syntax_highlighter/rouge.rb +33 -26
- data/lib/asciidoctor/table.rb +17 -19
- data/lib/asciidoctor/timings.rb +3 -3
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +10 -11
- data/man/asciidoctor.adoc +8 -7
- metadata +14 -67
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module Asciidoctor
|
3
3
|
# A built-in {Converter} implementation that generates HTML 5 output
|
4
|
-
# consistent with the html5 backend from AsciiDoc
|
4
|
+
# consistent with the html5 backend from AsciiDoc.py.
|
5
5
|
class Converter::Html5Converter < Converter::Base
|
6
6
|
register_for 'html5'
|
7
7
|
|
@@ -21,7 +21,7 @@ class Converter::Html5Converter < Converter::Base
|
|
21
21
|
#latexmath: INLINE_MATH_DELIMITERS[:latexmath] + [false],
|
22
22
|
}).default = ['', '']
|
23
23
|
|
24
|
-
DropAnchorRx =
|
24
|
+
DropAnchorRx = %r(<(?:a\b[^>]*|/a)>)
|
25
25
|
StemBreakRx = / *\\\n(?:\\?\n)*|\n\n+/
|
26
26
|
if RUBY_ENGINE == 'opal'
|
27
27
|
# NOTE In JavaScript, ^ matches the start of the string when the m flag is not set
|
@@ -48,44 +48,45 @@ class Converter::Html5Converter < Converter::Base
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def convert node, transform = node.node_name, opts = nil
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
51
|
+
case transform
|
52
|
+
when 'inline_quoted' then convert_inline_quoted node
|
53
|
+
when 'paragraph' then convert_paragraph node
|
54
|
+
when 'inline_anchor' then convert_inline_anchor node
|
55
|
+
when 'section' then convert_section node
|
56
|
+
when 'listing' then convert_listing node
|
57
|
+
when 'literal' then convert_literal node
|
58
|
+
when 'ulist' then convert_ulist node
|
59
|
+
when 'olist' then convert_olist node
|
60
|
+
when 'dlist' then convert_dlist node
|
61
|
+
when 'admonition' then convert_admonition node
|
62
|
+
when 'colist' then convert_colist node
|
63
|
+
when 'embedded' then convert_embedded node
|
64
|
+
when 'example' then convert_example node
|
65
|
+
when 'floating_title' then convert_floating_title node
|
66
|
+
when 'image' then convert_image node
|
67
|
+
when 'inline_break' then convert_inline_break node
|
68
|
+
when 'inline_button' then convert_inline_button node
|
69
|
+
when 'inline_callout' then convert_inline_callout node
|
70
|
+
when 'inline_footnote' then convert_inline_footnote node
|
71
|
+
when 'inline_image' then convert_inline_image node
|
72
|
+
when 'inline_indexterm' then convert_inline_indexterm node
|
73
|
+
when 'inline_kbd' then convert_inline_kbd node
|
74
|
+
when 'inline_menu' then convert_inline_menu node
|
75
|
+
when 'open' then convert_open node
|
76
|
+
when 'page_break' then convert_page_break node
|
77
|
+
when 'preamble' then convert_preamble node
|
78
|
+
when 'quote' then convert_quote node
|
79
|
+
when 'sidebar' then convert_sidebar node
|
80
|
+
when 'stem' then convert_stem node
|
81
|
+
when 'table' then convert_table node
|
82
|
+
when 'thematic_break' then convert_thematic_break node
|
83
|
+
when 'verse' then convert_verse node
|
84
|
+
when 'video' then convert_video node
|
85
|
+
when 'document' then convert_document node
|
86
|
+
when 'toc' then convert_toc node
|
87
|
+
when 'pass' then convert_pass node
|
88
|
+
when 'audio' then convert_audio node
|
89
|
+
else; super
|
89
90
|
end
|
90
91
|
end
|
91
92
|
|
@@ -350,9 +351,10 @@ MathJax.Hub.Register.StartupHook("AsciiMath Jax Ready", function () {
|
|
350
351
|
stitle = section.captioned_title
|
351
352
|
elsif section.numbered && slevel <= sectnumlevels
|
352
353
|
if slevel < 2 && node.document.doctype == 'book'
|
353
|
-
|
354
|
+
case section.sectname
|
355
|
+
when 'chapter'
|
354
356
|
stitle = %(#{(signifier = node.document.attributes['chapter-signifier']) ? "#{signifier} " : ''}#{section.sectnum} #{section.title})
|
355
|
-
|
357
|
+
when 'part'
|
356
358
|
stitle = %(#{(signifier = node.document.attributes['part-signifier']) ? "#{signifier} " : ''}#{section.sectnum nil, ':'} #{section.title})
|
357
359
|
else
|
358
360
|
stitle = %(#{section.sectnum} #{section.title})
|
@@ -383,9 +385,10 @@ MathJax.Hub.Register.StartupHook("AsciiMath Jax Ready", function () {
|
|
383
385
|
title = node.captioned_title
|
384
386
|
elsif node.numbered && level <= (doc_attrs['sectnumlevels'] || 3).to_i
|
385
387
|
if level < 2 && node.document.doctype == 'book'
|
386
|
-
|
388
|
+
case node.sectname
|
389
|
+
when 'chapter'
|
387
390
|
title = %(#{(signifier = doc_attrs['chapter-signifier']) ? "#{signifier} " : ''}#{node.sectnum} #{node.title})
|
388
|
-
|
391
|
+
when 'part'
|
389
392
|
title = %(#{(signifier = doc_attrs['part-signifier']) ? "#{signifier} " : ''}#{node.sectnum nil, ':'} #{node.title})
|
390
393
|
else
|
391
394
|
title = %(#{node.sectnum} #{node.title})
|
@@ -513,16 +516,16 @@ Your browser does not support the audio tag.
|
|
513
516
|
result = []
|
514
517
|
id_attribute = node.id ? %( id="#{node.id}") : ''
|
515
518
|
|
516
|
-
|
519
|
+
case node.style
|
517
520
|
when 'qanda'
|
518
|
-
['qlist', 'qanda', node.role]
|
521
|
+
classes = ['qlist', 'qanda', node.role]
|
519
522
|
when 'horizontal'
|
520
|
-
['hdlist', node.role]
|
523
|
+
classes = ['hdlist', node.role]
|
521
524
|
else
|
522
|
-
['dlist', node.style, node.role]
|
523
|
-
end
|
525
|
+
classes = ['dlist', node.style, node.role]
|
526
|
+
end
|
524
527
|
|
525
|
-
class_attribute = %( class="#{classes.join ' '}")
|
528
|
+
class_attribute = %( class="#{classes.compact.join ' '}")
|
526
529
|
|
527
530
|
result << %(<div#{id_attribute}#{class_attribute}>)
|
528
531
|
result << %(<div class="title">#{node.title}</div>) if node.title?
|
@@ -578,12 +581,11 @@ Your browser does not support the audio tag.
|
|
578
581
|
terms.each do |dt|
|
579
582
|
result << %(<dt#{dt_style_attribute}>#{dt.text}</dt>)
|
580
583
|
end
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
end
|
584
|
+
next unless dd
|
585
|
+
result << '<dd>'
|
586
|
+
result << %(<p>#{dd.text}</p>) if dd.text?
|
587
|
+
result << dd.content if dd.blocks?
|
588
|
+
result << '</dd>'
|
587
589
|
end
|
588
590
|
result << '</dl>'
|
589
591
|
end
|
@@ -759,8 +761,8 @@ Your browser does not support the audio tag.
|
|
759
761
|
logger.error 'partintro block can only be used when doctype is book and must be a child of a book part. Excluding block content.'
|
760
762
|
''
|
761
763
|
else
|
762
|
-
|
763
|
-
|
764
|
+
id_attr = node.id ? %( id="#{node.id}") : ''
|
765
|
+
title_el = node.title? ? %(<div class="title">#{node.title}</div>\n) : ''
|
764
766
|
%(<div#{id_attr} class="openblock#{style && style != 'open' ? " #{style}" : ''}#{(role = node.role) ? " #{role}" : ''}">
|
765
767
|
#{title_el}<div class="content">
|
766
768
|
#{node.content}
|
@@ -1111,14 +1113,17 @@ Your browser does not support the video tag.
|
|
1111
1113
|
else
|
1112
1114
|
attrs = node.role ? %( class="#{node.role}") : ''
|
1113
1115
|
unless (text = node.text)
|
1114
|
-
refid = node.attributes['refid']
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1116
|
+
if AbstractNode === (ref = (@refs ||= node.document.catalog[:refs])[refid = node.attributes['refid']] || (refid.nil_or_empty? ? (top = get_root_document node) : nil))
|
1117
|
+
if (@resolving_xref ||= (outer = true)) && outer
|
1118
|
+
if (text = ref.xreftext node.attr 'xrefstyle', nil, true)
|
1119
|
+
text = text.gsub DropAnchorRx, '' if text.include? '<a'
|
1120
|
+
else
|
1121
|
+
text = top ? '[^top]' : %([#{refid}])
|
1122
|
+
end
|
1123
|
+
@resolving_xref = nil
|
1124
|
+
else
|
1125
|
+
text = top ? '[^top]' : %([#{refid}])
|
1120
1126
|
end
|
1121
|
-
@resolving_xref = nil
|
1122
1127
|
else
|
1123
1128
|
text = %([#{refid}])
|
1124
1129
|
end
|
@@ -1186,7 +1191,11 @@ Your browser does not support the video tag.
|
|
1186
1191
|
img = %([#{node.alt}])
|
1187
1192
|
else
|
1188
1193
|
target = node.target
|
1189
|
-
attrs = [
|
1194
|
+
attrs = []
|
1195
|
+
attrs << %( width="#{node.attr 'width'}") if node.attr? 'width'
|
1196
|
+
attrs << %( height="#{node.attr 'height'}") if node.attr? 'height'
|
1197
|
+
attrs << %( title="#{node.attr 'title'}") if node.attr? 'title'
|
1198
|
+
attrs = attrs.empty? ? '' : attrs.join
|
1190
1199
|
if type != 'icon' && ((node.attr? 'format', 'svg') || (target.include? '.svg')) &&
|
1191
1200
|
node.document.safe < SafeMode::SECURE && ((svg = (node.option? 'inline')) || (obj = (node.option? 'interactive')))
|
1192
1201
|
if svg
|
@@ -1310,13 +1319,24 @@ Your browser does not support the video tag.
|
|
1310
1319
|
manname_id_attr = (manname_id = node.attr 'manname-id') ? %( id="#{manname_id}") : ''
|
1311
1320
|
%(<h2#{manname_id_attr}>#{manname_title}</h2>
|
1312
1321
|
<div class="sectionbody">
|
1313
|
-
<p>#{node.attr '
|
1322
|
+
<p>#{(node.attr 'mannames').join ', '} - #{node.attr 'manpurpose'}</p>
|
1314
1323
|
</div>)
|
1315
1324
|
end
|
1316
1325
|
|
1326
|
+
def get_root_document node
|
1327
|
+
while (node = node.document).nested?
|
1328
|
+
node = node.parent_document
|
1329
|
+
end
|
1330
|
+
node
|
1331
|
+
end
|
1332
|
+
|
1317
1333
|
# NOTE adapt to older converters that relied on unprefixed method names
|
1318
|
-
def method_missing id, *
|
1319
|
-
!((name = id.to_s).start_with? 'convert_') && (handles? name) ? (send %(convert_#{name}), *
|
1334
|
+
def method_missing id, *args
|
1335
|
+
!((name = id.to_s).start_with? 'convert_') && (handles? name) ? (send %(convert_#{name}), *args) : super
|
1336
|
+
end
|
1337
|
+
|
1338
|
+
def respond_to_missing? id, *options
|
1339
|
+
!((name = id.to_s).start_with? 'convert_') && (handles? name)
|
1320
1340
|
end
|
1321
1341
|
end
|
1322
1342
|
end
|
@@ -2,8 +2,12 @@
|
|
2
2
|
module Asciidoctor
|
3
3
|
# A built-in {Converter} implementation that generates the man page (troff) format.
|
4
4
|
#
|
5
|
-
# The output
|
6
|
-
#
|
5
|
+
# The output of this converter adheres to the man definition as defined by
|
6
|
+
# groff and uses the manpage output of the DocBook toolchain as a foundation.
|
7
|
+
# That means if you've previously been generating man pages using the a2x tool
|
8
|
+
# from AsciiDoc.py, you should be able to achieve a very similar result
|
9
|
+
# using this converter. Though you'll also get to enjoy some notable
|
10
|
+
# enhancements that have been added since, such as the customizable linkstyle.
|
7
11
|
#
|
8
12
|
# See http://www.gnu.org/software/groff/manual/html_node/Man-usage.html#Man-usage
|
9
13
|
class Converter::ManPageConverter < Converter::Base
|
@@ -18,10 +22,13 @@ class Converter::ManPageConverter < Converter::Base
|
|
18
22
|
LiteralBackslashRx = /\A\\|(#{ESC})?\\/
|
19
23
|
LeadingPeriodRx = /^\./
|
20
24
|
EscapedMacroRx = /^(?:#{ESC}\\c\n)?#{ESC}\.((?:URL|MTO) "#{CC_ANY}*?" "#{CC_ANY}*?" )( |[^\s]*)(#{CC_ANY}*?)(?: *#{ESC}\\c)?$/
|
21
|
-
|
25
|
+
MalformedEscapedMacroRx = /(#{ESC}\\c) (#{ESC}\.(?:URL|MTO) )/
|
26
|
+
MockMacroRx = %r(</?(#{ESC}\\[^>]+)>)
|
22
27
|
EmDashCharRefRx = /—(?:​)?/
|
23
28
|
EllipsisCharRefRx = /…(?:​)?/
|
24
29
|
WrappedIndentRx = /#{CG_BLANK}*#{LF}#{CG_BLANK}*/
|
30
|
+
XMLMarkupRx = /&#?[a-z\d]+;|</
|
31
|
+
PCDATAFilterRx = /(&#?[a-z\d]+;|<[^>]+>)|([^&<]+)/
|
25
32
|
|
26
33
|
def initialize backend, opts = {}
|
27
34
|
@backend = backend
|
@@ -91,17 +98,14 @@ class Converter::ManPageConverter < Converter::Base
|
|
91
98
|
if node.attr? 'manpurpose'
|
92
99
|
mannames = node.attr 'mannames', [manname]
|
93
100
|
result << %(.SH "#{(node.attr 'manname-title', 'NAME').upcase}"
|
94
|
-
#{mannames.map {|n| manify n }.join ', '} \\- #{manify node.attr('manpurpose'), whitespace: :normalize})
|
101
|
+
#{mannames.map {|n| (manify n).gsub '\-', '-' }.join ', '} \\- #{manify node.attr('manpurpose'), whitespace: :normalize})
|
95
102
|
end
|
96
103
|
end
|
97
104
|
|
98
105
|
result << node.content
|
99
106
|
|
100
107
|
# QUESTION should NOTES come after AUTHOR(S)?
|
101
|
-
|
102
|
-
result << '.SH "NOTES"'
|
103
|
-
result.concat(node.footnotes.map {|fn| %(#{fn.index}. #{fn.text}) })
|
104
|
-
end
|
108
|
+
append_footnotes result, node
|
105
109
|
|
106
110
|
unless (authors = node.authors).empty?
|
107
111
|
if authors.size > 1
|
@@ -124,10 +128,7 @@ class Converter::ManPageConverter < Converter::Base
|
|
124
128
|
def convert_embedded node
|
125
129
|
result = [node.content]
|
126
130
|
|
127
|
-
|
128
|
-
result << '.SH "NOTES"'
|
129
|
-
result.concat(node.footnotes.map {|fn| %(#{fn.index}. #{fn.text}) })
|
130
|
-
end
|
131
|
+
append_footnotes result, node
|
131
132
|
|
132
133
|
# QUESTION should we add an AUTHOR(S) section?
|
133
134
|
|
@@ -142,7 +143,7 @@ class Converter::ManPageConverter < Converter::Base
|
|
142
143
|
stitle = node.captioned_title
|
143
144
|
else
|
144
145
|
macro = 'SH'
|
145
|
-
stitle = node.title
|
146
|
+
stitle = uppercase_pcdata node.title
|
146
147
|
end
|
147
148
|
result << %(.#{macro} "#{manify stitle}"
|
148
149
|
#{node.content})
|
@@ -315,8 +316,9 @@ r lw(\n(.lu*75u/100u).'
|
|
315
316
|
end
|
316
317
|
end
|
317
318
|
|
318
|
-
|
319
|
-
|
319
|
+
def convert_page_break node
|
320
|
+
'.bp'
|
321
|
+
end
|
320
322
|
|
321
323
|
def convert_paragraph node
|
322
324
|
if node.title?
|
@@ -417,15 +419,7 @@ allbox tab(:);'
|
|
417
419
|
end
|
418
420
|
row_text[row_index] << %(T{#{LF}.sp#{LF})
|
419
421
|
cell_halign = (cell.attr 'halign', 'left').chr
|
420
|
-
if tsec == :
|
421
|
-
if row_header[row_index].empty? || row_header[row_index][cell_index].empty?
|
422
|
-
row_header[row_index][cell_index] << %(#{cell_halign}tB)
|
423
|
-
else
|
424
|
-
row_header[row_index][cell_index + 1] ||= []
|
425
|
-
row_header[row_index][cell_index + 1] << %(#{cell_halign}tB)
|
426
|
-
end
|
427
|
-
row_text[row_index] << %(#{manify cell.text, whitespace: :normalize}#{LF})
|
428
|
-
elsif tsec == :body
|
422
|
+
if tsec == :body
|
429
423
|
if row_header[row_index].empty? || row_header[row_index][cell_index].empty?
|
430
424
|
row_header[row_index][cell_index] << %(#{cell_halign}t)
|
431
425
|
else
|
@@ -441,7 +435,7 @@ allbox tab(:);'
|
|
441
435
|
cell_content = manify cell.content.join, whitespace: :normalize
|
442
436
|
end
|
443
437
|
row_text[row_index] << %(#{cell_content}#{LF})
|
444
|
-
|
438
|
+
else # tsec == :head || tsec == :foot
|
445
439
|
if row_header[row_index].empty? || row_header[row_index][cell_index].empty?
|
446
440
|
row_header[row_index][cell_index] << %(#{cell_halign}tB)
|
447
441
|
else
|
@@ -531,12 +525,13 @@ allbox tab(:);'
|
|
531
525
|
result.join LF
|
532
526
|
end
|
533
527
|
|
534
|
-
# FIXME git uses [verse] for the synopsis; detect this special case
|
535
528
|
def convert_verse node
|
536
529
|
result = []
|
537
|
-
|
530
|
+
if node.title?
|
531
|
+
result << %(.sp
|
538
532
|
.B #{manify node.title}
|
539
|
-
.br)
|
533
|
+
.br)
|
534
|
+
end
|
540
535
|
attribution_line = (node.attr? 'citetitle') ? %(#{node.attr 'citetitle'} ) : nil
|
541
536
|
attribution_line = (node.attr? 'attribution') ? %[#{attribution_line}\\(em #{node.attr 'attribution'}] : nil
|
542
537
|
result << %(.sp
|
@@ -584,8 +579,16 @@ allbox tab(:);'
|
|
584
579
|
%(#{ESC_BS}c#{LF}#{ESC_FS}#{macro} "#{target}" "#{text}" )
|
585
580
|
when :xref
|
586
581
|
unless (text = node.text)
|
587
|
-
refid = node.attributes['refid']
|
588
|
-
|
582
|
+
if AbstractNode === (ref = (@refs ||= node.document.catalog[:refs])[refid = node.attributes['refid']] || (refid.nil_or_empty? ? (top = get_root_document node) : nil))
|
583
|
+
if (@resolving_xref ||= (outer = true)) && outer && (text = ref.xreftext node.attr 'xrefstyle', nil, true)
|
584
|
+
text = uppercase_pcdata text if ref.context === :section && ref.level < 2 && text == ref.title
|
585
|
+
else
|
586
|
+
text = top ? '[^top]' : %([#{refid}])
|
587
|
+
end
|
588
|
+
@resolving_xref = nil if outer
|
589
|
+
else
|
590
|
+
text = %([#{refid}])
|
591
|
+
end
|
589
592
|
end
|
590
593
|
text
|
591
594
|
when :ref, :bibref
|
@@ -602,14 +605,13 @@ allbox tab(:);'
|
|
602
605
|
end
|
603
606
|
|
604
607
|
def convert_inline_button node
|
605
|
-
%(
|
608
|
+
%(<#{ESC_BS}fB>[#{ESC_BS}0#{node.text}#{ESC_BS}0]</#{ESC_BS}fP>)
|
606
609
|
end
|
607
610
|
|
608
611
|
def convert_inline_callout node
|
609
|
-
%(
|
612
|
+
%(<#{ESC_BS}fB>(#{node.text})<#{ESC_BS}fP>)
|
610
613
|
end
|
611
614
|
|
612
|
-
# TODO supposedly groff has footnotes, but we're in search of an example
|
613
615
|
def convert_inline_footnote node
|
614
616
|
if (index = node.attr 'index')
|
615
617
|
%([#{index}])
|
@@ -627,57 +629,68 @@ allbox tab(:);'
|
|
627
629
|
end
|
628
630
|
|
629
631
|
def convert_inline_kbd node
|
630
|
-
|
631
|
-
keys[0]
|
632
|
-
else
|
633
|
-
keys.join %(#{ESC_BS}0+#{ESC_BS}0)
|
634
|
-
end
|
632
|
+
%[<#{ESC_BS}f(CR>#{(keys = node.attr 'keys').size == 1 ? keys[0] : (keys.join "#{ESC_BS}0+#{ESC_BS}0")}</#{ESC_BS}fP>]
|
635
633
|
end
|
636
634
|
|
637
635
|
def convert_inline_menu node
|
638
636
|
caret = %[#{ESC_BS}0#{ESC_BS}(fc#{ESC_BS}0]
|
639
637
|
menu = node.attr 'menu'
|
640
638
|
if !(submenus = node.attr 'submenus').empty?
|
641
|
-
submenu_path = submenus.map {|item| %(
|
642
|
-
%(
|
639
|
+
submenu_path = submenus.map {|item| %(<#{ESC_BS}fI>#{item}</#{ESC_BS}fP>) }.join caret
|
640
|
+
%(<#{ESC_BS}fI>#{menu}</#{ESC_BS}fP>#{caret}#{submenu_path}#{caret}<#{ESC_BS}fI>#{node.attr 'menuitem'}</#{ESC_BS}fP>)
|
643
641
|
elsif (menuitem = node.attr 'menuitem')
|
644
|
-
%(
|
642
|
+
%(<#{ESC_BS}fI>#{menu}#{caret}#{menuitem}</#{ESC_BS}fP>)
|
645
643
|
else
|
646
|
-
%(
|
644
|
+
%(<#{ESC_BS}fI>#{menu}</#{ESC_BS}fP>)
|
647
645
|
end
|
648
646
|
end
|
649
647
|
|
650
|
-
# NOTE use fake
|
648
|
+
# NOTE use fake XML elements to prevent creating artificial word boundaries
|
651
649
|
def convert_inline_quoted node
|
652
650
|
case node.type
|
653
651
|
when :emphasis
|
654
|
-
%(
|
652
|
+
%(<#{ESC_BS}fI>#{node.text}</#{ESC_BS}fP>)
|
655
653
|
when :strong
|
656
|
-
%(
|
654
|
+
%(<#{ESC_BS}fB>#{node.text}</#{ESC_BS}fP>)
|
657
655
|
when :monospaced
|
658
|
-
%[
|
656
|
+
%[<#{ESC_BS}f(CR>#{node.text}</#{ESC_BS}fP>]
|
659
657
|
when :single
|
660
|
-
%[
|
658
|
+
%[<#{ESC_BS}(oq>#{node.text}</#{ESC_BS}(cq>]
|
661
659
|
when :double
|
662
|
-
%[
|
660
|
+
%[<#{ESC_BS}(lq>#{node.text}</#{ESC_BS}(rq>]
|
663
661
|
else
|
664
662
|
node.text
|
665
663
|
end
|
666
664
|
end
|
667
665
|
|
668
666
|
def self.write_alternate_pages mannames, manvolnum, target
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
end
|
667
|
+
return unless mannames && mannames.size > 1
|
668
|
+
mannames.shift
|
669
|
+
manvolext = %(.#{manvolnum})
|
670
|
+
dir, basename = ::File.split target
|
671
|
+
mannames.each do |manname|
|
672
|
+
::File.write ::File.join(dir, %(#{manname}#{manvolext})), %(.so #{basename}), mode: FILE_WRITE_MODE
|
676
673
|
end
|
677
674
|
end
|
678
675
|
|
679
676
|
private
|
680
677
|
|
678
|
+
def append_footnotes result, node
|
679
|
+
if node.footnotes? && !(node.attr? 'nofootnotes')
|
680
|
+
result << '.SH "NOTES"'
|
681
|
+
node.footnotes.each do |fn|
|
682
|
+
result << %(.IP [#{fn.index}])
|
683
|
+
# NOTE restore newline in escaped macro that gets removed by normalize_text in substitutor
|
684
|
+
if (text = fn.text).include? %(#{ESC}\\c #{ESC}.)
|
685
|
+
text = (manify %(#{text.gsub MalformedEscapedMacroRx, %(\\1#{LF}\\2)} ), whitespace: :normalize).chomp ' '
|
686
|
+
else
|
687
|
+
text = manify text, whitespace: :normalize
|
688
|
+
end
|
689
|
+
result << text
|
690
|
+
end
|
691
|
+
end
|
692
|
+
end
|
693
|
+
|
681
694
|
# Converts HTML entity references back to their original form, escapes
|
682
695
|
# special man characters and strips trailing whitespace.
|
683
696
|
#
|
@@ -699,42 +712,56 @@ allbox tab(:);'
|
|
699
712
|
else
|
700
713
|
str = str.tr_s WHITESPACE, ' '
|
701
714
|
end
|
702
|
-
str = str
|
703
|
-
gsub(LiteralBackslashRx) { $1 ? $& : '\\(rs' }
|
704
|
-
gsub(EllipsisCharRefRx, '...')
|
705
|
-
gsub(LeadingPeriodRx, '\\\&.')
|
706
|
-
# drop orphaned \c escape lines, unescape troff macro, quote adjacent character, isolate macro line
|
707
|
-
|
708
|
-
|
709
|
-
gsub('
|
710
|
-
gsub('&
|
711
|
-
gsub('
|
712
|
-
gsub('&#
|
713
|
-
gsub('&#
|
714
|
-
gsub('&#
|
715
|
-
gsub('&#
|
716
|
-
gsub('&#
|
717
|
-
gsub(
|
718
|
-
gsub('&#
|
719
|
-
gsub('&#
|
720
|
-
gsub(
|
721
|
-
gsub('&#
|
722
|
-
gsub('&#
|
723
|
-
gsub('&#
|
724
|
-
gsub('&#
|
725
|
-
gsub('&#
|
726
|
-
gsub('&#
|
727
|
-
gsub('
|
728
|
-
gsub('
|
729
|
-
gsub(
|
730
|
-
gsub(
|
731
|
-
gsub(
|
732
|
-
|
715
|
+
str = str
|
716
|
+
.gsub(LiteralBackslashRx) { $1 ? $& : '\\(rs' } # literal backslash (not a troff escape sequence)
|
717
|
+
.gsub(EllipsisCharRefRx, '...') # horizontal ellipsis
|
718
|
+
.gsub(LeadingPeriodRx, '\\\&.') # leading . is used in troff for macro call or other formatting; replace with \&.
|
719
|
+
.gsub(EscapedMacroRx) do # drop orphaned \c escape lines, unescape troff macro, quote adjacent character, isolate macro line
|
720
|
+
(rest = $3.lstrip).empty? ? %(.#{$1}"#{$2}") : %(.#{$1}"#{$2.rstrip}"#{LF}#{rest})
|
721
|
+
end
|
722
|
+
.gsub('-', '\-')
|
723
|
+
.gsub('<', '<')
|
724
|
+
.gsub('>', '>')
|
725
|
+
.gsub('+', '+') # plus sign; alternately could use \c(pl
|
726
|
+
.gsub(' ', '\~') # non-breaking space
|
727
|
+
.gsub('©', '\(co') # copyright sign
|
728
|
+
.gsub('®', '\(rg') # registered sign
|
729
|
+
.gsub('™', '\(tm') # trademark sign
|
730
|
+
.gsub('°', '\(de') # degree sign
|
731
|
+
.gsub(' ', ' ') # thin space
|
732
|
+
.gsub('–', '\(en') # en dash
|
733
|
+
.gsub(EmDashCharRefRx, '\(em') # em dash
|
734
|
+
.gsub('‘', '\(oq') # left single quotation mark
|
735
|
+
.gsub('’', '\(cq') # right single quotation mark
|
736
|
+
.gsub('“', '\(lq') # left double quotation mark
|
737
|
+
.gsub('”', '\(rq') # right double quotation mark
|
738
|
+
.gsub('←', '\(<-') # leftwards arrow
|
739
|
+
.gsub('→', '\(->') # rightwards arrow
|
740
|
+
.gsub('⇐', '\(lA') # leftwards double arrow
|
741
|
+
.gsub('⇒', '\(rA') # rightwards double arrow
|
742
|
+
.gsub('​', '\:') # zero width space
|
743
|
+
.gsub('&', '&') # literal ampersand (NOTE must take place after any other replacement that includes &)
|
744
|
+
.gsub('\'', '\*(Aq') # apostrophe / neutral single quote
|
745
|
+
.gsub(MockMacroRx, '\1') # mock boundary
|
746
|
+
.gsub(ESC_BS, '\\') # unescape troff backslash (NOTE update if more escapes are added)
|
747
|
+
.gsub(ESC_FS, '.') # unescape full stop in troff commands (NOTE must take place after gsub(LeadingPeriodRx))
|
748
|
+
.rstrip # strip trailing space
|
733
749
|
opts[:append_newline] ? %(#{str}#{LF}) : str
|
734
750
|
end
|
735
751
|
|
752
|
+
def uppercase_pcdata string
|
753
|
+
(XMLMarkupRx.match? string) ? string.gsub(PCDATAFilterRx) { $2 ? $2.upcase : $1 } : string.upcase
|
754
|
+
end
|
755
|
+
|
736
756
|
def enclose_content node
|
737
757
|
node.content_model == :compound ? node.content : %(.sp#{LF}#{manify node.content, whitespace: :normalize})
|
738
758
|
end
|
759
|
+
|
760
|
+
def get_root_document node
|
761
|
+
while (node = node.document).nested?
|
762
|
+
node = node.parent_document
|
763
|
+
end
|
764
|
+
node
|
765
|
+
end
|
739
766
|
end
|
740
767
|
end
|