asciidoctor 0.1.4 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of asciidoctor might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +209 -25
- data/{LICENSE → LICENSE.adoc} +4 -3
- data/README.adoc +392 -395
- data/Rakefile +94 -137
- data/benchmark/benchmark.rb +127 -0
- data/benchmark/sample-data/mdbasics.adoc +334 -0
- data/bin/asciidoctor +5 -8
- data/bin/asciidoctor-safe +4 -8
- data/compat/asciidoc.conf +78 -11
- data/compat/font-awesome-3-compat.css +397 -0
- data/data/stylesheets/asciidoctor-default.css +399 -0
- data/data/stylesheets/coderay-asciidoctor.css +89 -0
- data/features/open_block.feature +92 -0
- data/features/pass_block.feature +66 -0
- data/features/step_definitions.rb +42 -0
- data/features/text_formatting.feature +55 -0
- data/features/xref.feature +116 -0
- data/lib/asciidoctor.rb +1155 -605
- data/lib/asciidoctor/abstract_block.rb +157 -71
- data/lib/asciidoctor/abstract_node.rb +150 -93
- data/lib/asciidoctor/attribute_list.rb +85 -90
- data/lib/asciidoctor/block.rb +51 -24
- data/lib/asciidoctor/callouts.rb +4 -7
- data/lib/asciidoctor/cli.rb +3 -0
- data/lib/asciidoctor/cli/invoker.rb +86 -76
- data/lib/asciidoctor/cli/options.rb +111 -61
- data/lib/asciidoctor/converter.rb +232 -0
- data/lib/asciidoctor/converter/base.rb +58 -0
- data/lib/asciidoctor/converter/composite.rb +66 -0
- data/lib/asciidoctor/converter/docbook45.rb +94 -0
- data/lib/asciidoctor/converter/docbook5.rb +684 -0
- data/lib/asciidoctor/converter/factory.rb +225 -0
- data/lib/asciidoctor/converter/html5.rb +1081 -0
- data/lib/asciidoctor/converter/template.rb +296 -0
- data/lib/asciidoctor/core_ext.rb +7 -0
- data/lib/asciidoctor/core_ext/object/nil_or_empty.rb +23 -0
- data/lib/asciidoctor/core_ext/string/chr.rb +6 -0
- data/lib/asciidoctor/core_ext/symbol/length.rb +6 -0
- data/lib/asciidoctor/document.rb +590 -304
- data/lib/asciidoctor/extensions.rb +1100 -308
- data/lib/asciidoctor/helpers.rb +109 -46
- data/lib/asciidoctor/inline.rb +16 -9
- data/lib/asciidoctor/list.rb +23 -15
- data/lib/asciidoctor/opal_ext.rb +4 -0
- data/lib/asciidoctor/opal_ext/comparable.rb +38 -0
- data/lib/asciidoctor/opal_ext/dir.rb +13 -0
- data/lib/asciidoctor/opal_ext/error.rb +2 -0
- data/lib/asciidoctor/opal_ext/file.rb +125 -0
- data/lib/asciidoctor/{lexer.rb → parser.rb} +646 -455
- data/lib/asciidoctor/path_resolver.rb +141 -77
- data/lib/asciidoctor/reader.rb +257 -187
- data/lib/asciidoctor/section.rb +12 -16
- data/lib/asciidoctor/stylesheets.rb +91 -0
- data/lib/asciidoctor/substitutors.rb +1548 -0
- data/lib/asciidoctor/table.rb +73 -57
- data/lib/asciidoctor/timings.rb +39 -0
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +22 -14
- data/man/asciidoctor.adoc +18 -10
- data/test/attributes_test.rb +314 -14
- data/test/blocks_test.rb +763 -118
- data/test/converter_test.rb +352 -0
- data/test/document_test.rb +518 -199
- data/test/extensions_test.rb +273 -103
- data/test/fixtures/asciidoc_index.txt +27 -13
- data/test/fixtures/basic-docinfo.xml +1 -1
- data/test/fixtures/chapter-a.adoc +3 -0
- data/test/fixtures/custom-backends/erb/html5/block_paragraph.html.erb +6 -0
- data/test/fixtures/docinfo.xml +1 -1
- data/test/fixtures/include-file.asciidoc +2 -0
- data/test/fixtures/master.adoc +5 -0
- data/test/invoker_test.rb +173 -61
- data/test/links_test.rb +97 -21
- data/test/lists_test.rb +181 -22
- data/test/options_test.rb +86 -2
- data/test/paragraphs_test.rb +47 -5
- data/test/{lexer_test.rb → parser_test.rb} +128 -57
- data/test/paths_test.rb +36 -1
- data/test/preamble_test.rb +25 -17
- data/test/reader_test.rb +404 -249
- data/test/sections_test.rb +623 -58
- data/test/substitutions_test.rb +609 -132
- data/test/tables_test.rb +198 -24
- data/test/test_helper.rb +101 -31
- data/test/text_test.rb +88 -31
- metadata +160 -64
- data/Gemfile +0 -12
- data/Guardfile +0 -18
- data/asciidoctor.gemspec +0 -143
- data/lib/asciidoctor/backends/_stylesheets.rb +0 -466
- data/lib/asciidoctor/backends/base_template.rb +0 -114
- data/lib/asciidoctor/backends/docbook45.rb +0 -774
- data/lib/asciidoctor/backends/docbook5.rb +0 -103
- data/lib/asciidoctor/backends/html5.rb +0 -1214
- data/lib/asciidoctor/renderer.rb +0 -259
- data/lib/asciidoctor/substituters.rb +0 -1083
- data/test/fixtures/asciidoc.txt +0 -105
- data/test/fixtures/ascshort.txt +0 -32
- data/test/fixtures/list_elements.asciidoc +0 -10
- data/test/renderer_test.rb +0 -162
@@ -0,0 +1,58 @@
|
|
1
|
+
module Asciidoctor
|
2
|
+
module Converter; end # required for Opal
|
3
|
+
|
4
|
+
# An abstract base class for defining converters that can be used to convert
|
5
|
+
# {AbstractNode} objects in a parsed AsciiDoc document to a backend format
|
6
|
+
# such as HTML or DocBook.
|
7
|
+
#
|
8
|
+
# Concrete subclasses must implement the {#convert} method and, optionally,
|
9
|
+
# the {#convert_with_options} method.
|
10
|
+
class Converter::Base
|
11
|
+
include Converter
|
12
|
+
end
|
13
|
+
|
14
|
+
# An abstract base class for built-in {Converter} classes.
|
15
|
+
class Converter::BuiltIn
|
16
|
+
def initialize backend, opts = {}
|
17
|
+
end
|
18
|
+
|
19
|
+
# Public: Converts the specified {AbstractNode} using the specified transform.
|
20
|
+
#
|
21
|
+
# See {Converter#convert} for more details.
|
22
|
+
#
|
23
|
+
# Returns the [String] result of conversion
|
24
|
+
def convert node, transform = nil
|
25
|
+
transform ||= node.node_name
|
26
|
+
send transform, node
|
27
|
+
end
|
28
|
+
|
29
|
+
# Public: Converts the specified {AbstractNode} using the specified transform
|
30
|
+
# with additional options.
|
31
|
+
#
|
32
|
+
# See {Converter#convert_with_options} for more details.
|
33
|
+
#
|
34
|
+
# Returns the [String] result of conversion
|
35
|
+
def convert_with_options node, transform = nil, opts = {}
|
36
|
+
transform ||= node.node_name
|
37
|
+
send transform, node, opts
|
38
|
+
end
|
39
|
+
|
40
|
+
alias :handles? :respond_to?
|
41
|
+
|
42
|
+
# Public: Returns the converted content of the {AbstractNode}.
|
43
|
+
#
|
44
|
+
# Returns the converted [String] content of the {AbstractNode}.
|
45
|
+
def content node
|
46
|
+
node.content
|
47
|
+
end
|
48
|
+
|
49
|
+
alias :pass :content
|
50
|
+
|
51
|
+
# Public: Skips conversion of the {AbstractNode}.
|
52
|
+
#
|
53
|
+
# Returns [NilClass]
|
54
|
+
def skip node
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Asciidoctor
|
2
|
+
# A {Converter} implementation that delegates to the chain of {Converter}
|
3
|
+
# objects passed to the constructor. Selects the first {Converter} that
|
4
|
+
# identifies itself as the handler for a given transform.
|
5
|
+
class Converter::CompositeConverter < Converter::Base
|
6
|
+
|
7
|
+
# Get the Array of Converter objects in the chain
|
8
|
+
attr_reader :converters
|
9
|
+
|
10
|
+
def initialize backend, *converters
|
11
|
+
@backend = backend
|
12
|
+
(@converters = converters.flatten.compact).each do |converter|
|
13
|
+
converter.composed self if converter.respond_to? :composed
|
14
|
+
end
|
15
|
+
@converter_map = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
# Public: Delegates to the first converter that identifies itself as the
|
19
|
+
# handler for the given transform.
|
20
|
+
#
|
21
|
+
# node - the AbstractNode to convert
|
22
|
+
# transform - the optional String transform, or the name of the node if no
|
23
|
+
# transform is specified. (default: nil)
|
24
|
+
#
|
25
|
+
# Returns the String result returned from the delegate's convert method
|
26
|
+
def convert node, transform = nil
|
27
|
+
transform ||= node.node_name
|
28
|
+
# QUESTION is there a way we can control whether to use convert or send?
|
29
|
+
(converter_for transform).convert node, transform
|
30
|
+
end
|
31
|
+
|
32
|
+
# Public: Delegates to the first converter that identifies itself as the
|
33
|
+
# handler for the given transform. The optional Hash is passed as the last
|
34
|
+
# option to the delegate's convert method.
|
35
|
+
#
|
36
|
+
# node - the AbstractNode to convert
|
37
|
+
# transform - the optional String transform, or the name of the node if no
|
38
|
+
# transform is specified. (default: nil)
|
39
|
+
# opts - a optional Hash that is passed to the delegate's convert method. (default: {})
|
40
|
+
#
|
41
|
+
# Returns the String result returned from the delegate's convert method
|
42
|
+
def convert_with_options node, transform = nil, opts = {}
|
43
|
+
transform ||= node.node_name
|
44
|
+
# QUESTION should we check arity, or perhaps do a rescue ::ArgumentError?
|
45
|
+
(converter_for transform).convert_with_options node, transform, opts
|
46
|
+
end
|
47
|
+
|
48
|
+
# Public: Retrieve the converter for the specified transform.
|
49
|
+
#
|
50
|
+
# Returns the matching [Converter] object
|
51
|
+
def converter_for transform
|
52
|
+
@converter_map[transform] ||= find_converter transform
|
53
|
+
end
|
54
|
+
|
55
|
+
# Internal: Find the converter for the specified transform.
|
56
|
+
# Raise an exception if no converter is found.
|
57
|
+
#
|
58
|
+
# Returns the matching [Converter] object
|
59
|
+
def find_converter transform
|
60
|
+
@converters.each do |candidate|
|
61
|
+
return candidate if candidate.handles? transform
|
62
|
+
end
|
63
|
+
raise %(Could not find a converter to handle transform: #{transform})
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'asciidoctor/converter/docbook5'
|
2
|
+
|
3
|
+
module Asciidoctor
|
4
|
+
# A built-in {Converter} implementation that generates DocBook 4.5 output
|
5
|
+
# consistent with the docbook45 backend from AsciiDoc Python.
|
6
|
+
class Converter::DocBook45Converter < Converter::DocBook5Converter
|
7
|
+
def admonition node
|
8
|
+
# address a bug in the DocBook 4.5 DTD
|
9
|
+
if node.parent.context == :example
|
10
|
+
%(<para>
|
11
|
+
#{super}
|
12
|
+
</para>)
|
13
|
+
else
|
14
|
+
super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def olist node
|
19
|
+
result = []
|
20
|
+
num_attribute = node.style ? %( numeration="#{node.style}") : nil
|
21
|
+
start_attribute = (node.attr? 'start') ? %( override="#{node.attr 'start'}") : nil
|
22
|
+
result << %(<orderedlist#{common_attributes node.id, node.role, node.reftext}#{num_attribute}>)
|
23
|
+
result << %(<title>#{node.title}</title>) if node.title?
|
24
|
+
node.items.each_with_index do |item, idx|
|
25
|
+
result << (idx == 0 ? %(<listitem#{start_attribute}>) : '<listitem>')
|
26
|
+
result << %(<simpara>#{item.text}</simpara>)
|
27
|
+
result << item.content if item.blocks?
|
28
|
+
result << '</listitem>'
|
29
|
+
end
|
30
|
+
result << %(</orderedlist>)
|
31
|
+
result * EOL
|
32
|
+
end
|
33
|
+
|
34
|
+
def inline_anchor node
|
35
|
+
target = node.target
|
36
|
+
case node.type
|
37
|
+
when :ref
|
38
|
+
%(<anchor#{common_attributes target, nil, node.text}/>)
|
39
|
+
when :xref
|
40
|
+
if node.attr? 'path', nil
|
41
|
+
linkend = (node.attr 'fragment') || target
|
42
|
+
(text = node.text) ? %(<link linkend="#{linkend}">#{text}</link>) : %(<xref linkend="#{linkend}"/>)
|
43
|
+
else
|
44
|
+
text = node.text || (node.attr 'path')
|
45
|
+
%(<ulink url="#{target}">#{text}</ulink>)
|
46
|
+
end
|
47
|
+
when :link
|
48
|
+
%(<ulink url="#{target}">#{node.text}</ulink>)
|
49
|
+
when :bibref
|
50
|
+
%(<anchor#{common_attributes target, nil, "[#{target}]"}/>[#{target}])
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def author_element doc, index = nil
|
55
|
+
firstname_key = index ? %(firstname_#{index}) : 'firstname'
|
56
|
+
middlename_key = index ? %(middlename_#{index}) : 'middlename'
|
57
|
+
lastname_key = index ? %(lastname_#{index}) : 'lastname'
|
58
|
+
email_key = index ? %(email_#{index}) : 'email'
|
59
|
+
|
60
|
+
result = []
|
61
|
+
result << '<author>'
|
62
|
+
result << %(<firstname>#{doc.attr firstname_key}</firstname>) if doc.attr? firstname_key
|
63
|
+
result << %(<othername>#{doc.attr middlename_key}</othername>) if doc.attr? middlename_key
|
64
|
+
result << %(<surname>#{doc.attr lastname_key}</surname>) if doc.attr? lastname_key
|
65
|
+
result << %(<email>#{doc.attr email_key}</email>) if doc.attr? email_key
|
66
|
+
result << '</author>'
|
67
|
+
|
68
|
+
result * EOL
|
69
|
+
end
|
70
|
+
|
71
|
+
def common_attributes id, role = nil, reftext = nil
|
72
|
+
res = id ? %( id="#{id}") : ''
|
73
|
+
res = %(#{res} role="#{role}") if role
|
74
|
+
res = %(#{res} xreflabel="#{reftext}") if reftext
|
75
|
+
res
|
76
|
+
end
|
77
|
+
|
78
|
+
def doctype_declaration root_tag_name
|
79
|
+
%(<!DOCTYPE #{root_tag_name} PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">)
|
80
|
+
end
|
81
|
+
|
82
|
+
def document_info_element doc, info_tag_prefix
|
83
|
+
super doc, info_tag_prefix, true
|
84
|
+
end
|
85
|
+
|
86
|
+
def document_ns_attributes doc
|
87
|
+
if (ns = doc.attr 'xmlns')
|
88
|
+
ns.empty? ? ' xmlns="http://docbook.org/ns/docbook"' : %( xmlns="#{ns}")
|
89
|
+
else
|
90
|
+
nil
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,684 @@
|
|
1
|
+
module Asciidoctor
|
2
|
+
# A built-in {Converter} implementation that generates DocBook 5 output
|
3
|
+
# similar to the docbook45 backend from AsciiDoc Python, but migrated to the
|
4
|
+
# DocBook 5 specification.
|
5
|
+
class Converter::DocBook5Converter < Converter::BuiltIn
|
6
|
+
def document node
|
7
|
+
result = []
|
8
|
+
root_tag_name = node.doctype
|
9
|
+
result << '<?xml version="1.0" encoding="UTF-8"?>'
|
10
|
+
if (doctype_line = doctype_declaration root_tag_name)
|
11
|
+
result << doctype_line
|
12
|
+
end
|
13
|
+
result << '<?asciidoc-toc?>' if node.attr? 'toc'
|
14
|
+
result << '<?asciidoc-numbered?>' if node.attr? 'sectnums'
|
15
|
+
lang_attribute = (node.attr? 'nolang') ? nil : %( lang="#{node.attr 'lang', 'en'}")
|
16
|
+
result << %(<#{root_tag_name}#{document_ns_attributes node}#{lang_attribute}>)
|
17
|
+
result << (document_info_element node, root_tag_name)
|
18
|
+
result << node.content if node.blocks?
|
19
|
+
unless (footer_docinfo = node.docinfo :footer).empty?
|
20
|
+
result << footer_docinfo
|
21
|
+
end
|
22
|
+
result << %(</#{root_tag_name}>)
|
23
|
+
|
24
|
+
result * EOL
|
25
|
+
end
|
26
|
+
|
27
|
+
alias :embedded :content
|
28
|
+
|
29
|
+
def section node
|
30
|
+
tag_name = if node.special
|
31
|
+
node.level <= 1 ? node.sectname : 'section'
|
32
|
+
else
|
33
|
+
node.document.doctype == 'book' && node.level <= 1 ? (node.level == 0 ? 'part' : 'chapter') : 'section'
|
34
|
+
end
|
35
|
+
%(<#{tag_name}#{common_attributes node.id, node.role, node.reftext}>
|
36
|
+
<title>#{node.title}</title>
|
37
|
+
#{node.content}
|
38
|
+
</#{tag_name}>)
|
39
|
+
end
|
40
|
+
|
41
|
+
def admonition node
|
42
|
+
%(<#{tag_name = node.attr 'name'}#{common_attributes node.id, node.role, node.reftext}>
|
43
|
+
#{title_tag node}#{resolve_content node}
|
44
|
+
</#{tag_name}>)
|
45
|
+
end
|
46
|
+
|
47
|
+
alias :audio :skip
|
48
|
+
|
49
|
+
def colist node
|
50
|
+
result = []
|
51
|
+
result << %(<calloutlist#{common_attributes node.id, node.role, node.reftext}>)
|
52
|
+
result << %(<title>#{node.title}</title>) if node.title?
|
53
|
+
node.items.each do |item|
|
54
|
+
result << %(<callout arearefs="#{item.attr 'coids'}">)
|
55
|
+
result << %(<para>#{item.text}</para>)
|
56
|
+
result << item.content if item.blocks?
|
57
|
+
result << '</callout>'
|
58
|
+
end
|
59
|
+
result << %(</calloutlist>)
|
60
|
+
result * EOL
|
61
|
+
end
|
62
|
+
|
63
|
+
DLIST_TAGS = {
|
64
|
+
'labeled' => {
|
65
|
+
:list => 'variablelist',
|
66
|
+
:entry => 'varlistentry',
|
67
|
+
:term => 'term',
|
68
|
+
:item => 'listitem'
|
69
|
+
},
|
70
|
+
'qanda' => {
|
71
|
+
:list => 'qandaset',
|
72
|
+
:entry => 'qandaentry',
|
73
|
+
:label => 'question',
|
74
|
+
:term => 'simpara',
|
75
|
+
:item => 'answer'
|
76
|
+
},
|
77
|
+
'glossary' => {
|
78
|
+
:list => nil,
|
79
|
+
:entry => 'glossentry',
|
80
|
+
:term => 'glossterm',
|
81
|
+
:item => 'glossdef'
|
82
|
+
}
|
83
|
+
}
|
84
|
+
DLIST_TAGS.default = DLIST_TAGS['labeled']
|
85
|
+
|
86
|
+
def dlist node
|
87
|
+
result = []
|
88
|
+
if node.style == 'horizontal'
|
89
|
+
result << %(<#{tag_name = node.title? ? 'table' : 'informaltable'}#{common_attributes node.id, node.role, node.reftext} tabstyle="horizontal" frame="none" colsep="0" rowsep="0">
|
90
|
+
#{title_tag node}<tgroup cols="2">
|
91
|
+
<colspec colwidth="#{node.attr 'labelwidth', 15}*"/>
|
92
|
+
<colspec colwidth="#{node.attr 'itemwidth', 85}*"/>
|
93
|
+
<tbody valign="top">)
|
94
|
+
node.items.each do |terms, dd|
|
95
|
+
result << %(<row>
|
96
|
+
<entry>)
|
97
|
+
[*terms].each do |dt|
|
98
|
+
result << %(<simpara>#{dt.text}</simpara>)
|
99
|
+
end
|
100
|
+
result << %(</entry>
|
101
|
+
<entry>)
|
102
|
+
unless dd.nil?
|
103
|
+
result << %(<simpara>#{dd.text}</simpara>) if dd.text?
|
104
|
+
result << dd.content if dd.blocks?
|
105
|
+
end
|
106
|
+
result << %(</entry>
|
107
|
+
</row>)
|
108
|
+
end
|
109
|
+
result << %(</tbody>
|
110
|
+
</tgroup>
|
111
|
+
</#{tag_name}>)
|
112
|
+
else
|
113
|
+
tags = DLIST_TAGS[node.style]
|
114
|
+
list_tag = tags[:list]
|
115
|
+
entry_tag = tags[:entry]
|
116
|
+
label_tag = tags[:label]
|
117
|
+
term_tag = tags[:term]
|
118
|
+
item_tag = tags[:item]
|
119
|
+
if list_tag
|
120
|
+
result << %(<#{list_tag}#{common_attributes node.id, node.role, node.reftext}>)
|
121
|
+
result << %(<title>#{node.title}</title>) if node.title?
|
122
|
+
end
|
123
|
+
|
124
|
+
node.items.each do |terms, dd|
|
125
|
+
result << %(<#{entry_tag}>)
|
126
|
+
result << %(<#{label_tag}>) if label_tag
|
127
|
+
|
128
|
+
[*terms].each do |dt|
|
129
|
+
result << %(<#{term_tag}>#{dt.text}</#{term_tag}>)
|
130
|
+
end
|
131
|
+
|
132
|
+
result << %(</#{label_tag}>) if label_tag
|
133
|
+
result << %(<#{item_tag}>)
|
134
|
+
unless dd.nil?
|
135
|
+
result << %(<simpara>#{dd.text}</simpara>) if dd.text?
|
136
|
+
result << dd.content if dd.blocks?
|
137
|
+
end
|
138
|
+
result << %(</#{item_tag}>)
|
139
|
+
result << %(</#{entry_tag}>)
|
140
|
+
end
|
141
|
+
|
142
|
+
result << %(</#{list_tag}>) if list_tag
|
143
|
+
end
|
144
|
+
|
145
|
+
result * EOL
|
146
|
+
end
|
147
|
+
|
148
|
+
def example node
|
149
|
+
if node.title?
|
150
|
+
%(<example#{common_attributes node.id, node.role, node.reftext}>
|
151
|
+
<title>#{node.title}</title>
|
152
|
+
#{resolve_content node}
|
153
|
+
</example>)
|
154
|
+
else
|
155
|
+
%(<informalexample#{common_attributes node.id, node.role, node.reftext}>
|
156
|
+
#{resolve_content node}
|
157
|
+
</informalexample>)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def floating_title node
|
162
|
+
%(<bridgehead#{common_attributes node.id, node.role, node.reftext} renderas="sect#{node.level}">#{node.title}</bridgehead>)
|
163
|
+
end
|
164
|
+
|
165
|
+
def image node
|
166
|
+
width_attribute = (node.attr? 'width') ? %( contentwidth="#{node.attr 'width'}") : nil
|
167
|
+
depth_attribute = (node.attr? 'height') ? %( contentdepth="#{node.attr 'height'}") : nil
|
168
|
+
swidth_attribute = (node.attr? 'scaledwidth') ? %( width="#{node.attr 'scaledwidth'}" scalefit="1") : nil
|
169
|
+
scale_attribute = (node.attr? 'scale') ? %( scale="#{node.attr 'scale'}") : nil
|
170
|
+
align_attribute = (node.attr? 'align') ? %( align="#{node.attr 'align'}") : nil
|
171
|
+
|
172
|
+
mediaobject = %(<mediaobject>
|
173
|
+
<imageobject>
|
174
|
+
<imagedata fileref="#{node.image_uri(node.attr 'target')}"#{width_attribute}#{depth_attribute}#{swidth_attribute}#{scale_attribute}#{align_attribute}/>
|
175
|
+
</imageobject>
|
176
|
+
<textobject><phrase>#{node.attr 'alt'}</phrase></textobject>
|
177
|
+
</mediaobject>)
|
178
|
+
|
179
|
+
if node.title?
|
180
|
+
%(<figure#{common_attributes node.id, node.role, node.reftext}>
|
181
|
+
<title>#{node.title}</title>
|
182
|
+
#{mediaobject}
|
183
|
+
</figure>)
|
184
|
+
else
|
185
|
+
%(<informalfigure#{common_attributes node.id, node.role, node.reftext}>
|
186
|
+
#{mediaobject}
|
187
|
+
</informalfigure>)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def listing node
|
192
|
+
informal = !node.title?
|
193
|
+
listing_attributes = (common_attributes node.id, node.role, node.reftext)
|
194
|
+
if node.style == 'source' && (node.attr? 'language')
|
195
|
+
numbering = (node.attr? 'linenums') ? 'numbered' : 'unnumbered'
|
196
|
+
listing_content = %(<programlisting#{informal ? listing_attributes : nil} language="#{node.attr 'language', nil, false}" linenumbering="#{numbering}">#{node.content}</programlisting>)
|
197
|
+
else
|
198
|
+
listing_content = %(<screen#{informal ? listing_attributes : nil}>#{node.content}</screen>)
|
199
|
+
end
|
200
|
+
if informal
|
201
|
+
listing_content
|
202
|
+
else
|
203
|
+
%(<formalpara#{listing_attributes}>
|
204
|
+
<title>#{node.title}</title>
|
205
|
+
<para>
|
206
|
+
#{listing_content}
|
207
|
+
</para>
|
208
|
+
</formalpara>)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def literal node
|
213
|
+
if node.title?
|
214
|
+
%(<formalpara#{common_attributes node.id, node.role, node.reftext}>
|
215
|
+
<title>#{node.title}</title>
|
216
|
+
<para>
|
217
|
+
<literallayout class="monospaced">#{node.content}</literallayout>
|
218
|
+
</para>
|
219
|
+
</formalpara>)
|
220
|
+
else
|
221
|
+
%(<literallayout#{common_attributes node.id, node.role, node.reftext} class="monospaced">#{node.content}</literallayout>)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def stem node
|
226
|
+
if (idx = node.subs.index :specialcharacters)
|
227
|
+
node.subs.delete :specialcharacters
|
228
|
+
end
|
229
|
+
equation = node.content
|
230
|
+
node.subs.insert idx, :specialcharacters if idx
|
231
|
+
if node.style == 'latexmath'
|
232
|
+
equation_data = %(<alt><![CDATA[#{equation}]]></alt>
|
233
|
+
<mediaobject><textobject><phrase></phrase></textobject></mediaobject>)
|
234
|
+
# asciimath
|
235
|
+
else
|
236
|
+
# DocBook backends can't handle AsciiMath, so output raw expression in text object
|
237
|
+
equation_data = %(<mediaobject><textobject><phrase><![CDATA[#{equation}]]></phrase></textobject></mediaobject>)
|
238
|
+
end
|
239
|
+
if node.title?
|
240
|
+
%(<equation#{common_attributes node.id, node.role, node.reftext}>
|
241
|
+
<title>#{node.title}</title>
|
242
|
+
#{equation_data}
|
243
|
+
</equation>)
|
244
|
+
else
|
245
|
+
%(<informalequation#{common_attributes node.id, node.role, node.reftext}>
|
246
|
+
#{equation_data}
|
247
|
+
</informalequation>)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def olist node
|
252
|
+
result = []
|
253
|
+
num_attribute = node.style ? %( numeration="#{node.style}") : nil
|
254
|
+
start_attribute = (node.attr? 'start') ? %( startingnumber="#{node.attr 'start'}") : nil
|
255
|
+
result << %(<orderedlist#{common_attributes node.id, node.role, node.reftext}#{num_attribute}#{start_attribute}>)
|
256
|
+
result << %(<title>#{node.title}</title>) if node.title?
|
257
|
+
node.items.each do |item|
|
258
|
+
result << '<listitem>'
|
259
|
+
result << %(<simpara>#{item.text}</simpara>)
|
260
|
+
result << item.content if item.blocks?
|
261
|
+
result << '</listitem>'
|
262
|
+
end
|
263
|
+
result << %(</orderedlist>)
|
264
|
+
result * EOL
|
265
|
+
end
|
266
|
+
|
267
|
+
def open node
|
268
|
+
case node.style
|
269
|
+
when 'abstract'
|
270
|
+
if node.parent == node.document && node.document.attr?('doctype', 'book')
|
271
|
+
warn 'asciidoctor: WARNING: abstract block cannot be used in a document without a title when doctype is book. Excluding block content.'
|
272
|
+
''
|
273
|
+
else
|
274
|
+
%(<abstract>
|
275
|
+
#{title_tag node}#{resolve_content node}
|
276
|
+
</abstract>)
|
277
|
+
end
|
278
|
+
when 'partintro'
|
279
|
+
unless node.level == 0 && node.parent.context == :section && node.document.doctype == 'book'
|
280
|
+
warn 'asciidoctor: ERROR: partintro block can only be used when doctype is book and it\'s a child of a part section. Excluding block content.'
|
281
|
+
''
|
282
|
+
else
|
283
|
+
%(<partintro#{common_attributes node.id, node.role, node.reftext}>
|
284
|
+
#{title_tag node}#{resolve_content node}
|
285
|
+
</partintro>)
|
286
|
+
end
|
287
|
+
else
|
288
|
+
node.content
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
def page_break node
|
293
|
+
'<simpara><?asciidoc-pagebreak?></simpara>'
|
294
|
+
end
|
295
|
+
|
296
|
+
def paragraph node
|
297
|
+
if node.title?
|
298
|
+
%(<formalpara#{common_attributes node.id, node.role, node.reftext}>
|
299
|
+
<title>#{node.title}</title>
|
300
|
+
<para>#{node.content}</para>
|
301
|
+
</formalpara>)
|
302
|
+
else
|
303
|
+
%(<simpara#{common_attributes node.id, node.role, node.reftext}>#{node.content}</simpara>)
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
def preamble node
|
308
|
+
if node.document.doctype == 'book'
|
309
|
+
%(<preface#{common_attributes node.id, node.role, node.reftext}>
|
310
|
+
#{title_tag node, false}#{node.content}
|
311
|
+
</preface>)
|
312
|
+
else
|
313
|
+
node.content
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
def quote node
|
318
|
+
result = []
|
319
|
+
result << %(<blockquote#{common_attributes node.id, node.role, node.reftext}>)
|
320
|
+
result << %(<title>#{node.title}</title>) if node.title?
|
321
|
+
if (node.attr? 'attribution') || (node.attr? 'citetitle')
|
322
|
+
result << '<attribution>'
|
323
|
+
if node.attr? 'attribution'
|
324
|
+
result << (node.attr 'attribution')
|
325
|
+
end
|
326
|
+
if node.attr? 'citetitle'
|
327
|
+
result << %(<citetitle>#{node.attr 'citetitle'}</citetitle>)
|
328
|
+
end
|
329
|
+
result << '</attribution>'
|
330
|
+
end
|
331
|
+
result << (resolve_content node)
|
332
|
+
result << '</blockquote>'
|
333
|
+
result * EOL
|
334
|
+
end
|
335
|
+
|
336
|
+
def thematic_break node
|
337
|
+
'<simpara><?asciidoc-hr?></simpara>'
|
338
|
+
end
|
339
|
+
|
340
|
+
def sidebar node
|
341
|
+
%(<sidebar#{common_attributes node.id, node.role, node.reftext}>
|
342
|
+
#{title_tag node}#{resolve_content node}
|
343
|
+
</sidebar>)
|
344
|
+
end
|
345
|
+
|
346
|
+
TABLE_PI_NAMES = ['dbhtml', 'dbfo', 'dblatex']
|
347
|
+
TABLE_SECTIONS = [:head, :foot, :body]
|
348
|
+
|
349
|
+
def table node
|
350
|
+
has_body = false
|
351
|
+
result = []
|
352
|
+
pgwide_attribute = (node.option? 'pgwide') ? ' pgwide="1"' : nil
|
353
|
+
result << %(<#{tag_name = node.title? ? 'table' : 'informaltable'}#{common_attributes node.id, node.role, node.reftext}#{pgwide_attribute} frame="#{node.attr 'frame', 'all'}" rowsep="#{['none', 'cols'].include?(node.attr 'grid') ? 0 : 1}" colsep="#{['none', 'rows'].include?(node.attr 'grid') ? 0 : 1}">)
|
354
|
+
result << %(<title>#{node.title}</title>) if tag_name == 'table'
|
355
|
+
if (width = (node.attr? 'width') ? (node.attr 'width') : nil)
|
356
|
+
TABLE_PI_NAMES.each do |pi_name|
|
357
|
+
result << %(<?#{pi_name} table-width="#{width}"?>)
|
358
|
+
end
|
359
|
+
end
|
360
|
+
result << %(<tgroup cols="#{node.attr 'colcount'}">)
|
361
|
+
node.columns.each do |col|
|
362
|
+
result << %(<colspec colname="col_#{col.attr 'colnumber'}" colwidth="#{col.attr(width ? 'colabswidth' : 'colpcwidth')}*"/>)
|
363
|
+
end
|
364
|
+
TABLE_SECTIONS.select {|tblsec| !node.rows[tblsec].empty? }.each do |tblsec|
|
365
|
+
has_body = true if tblsec == :body
|
366
|
+
result << %(<t#{tblsec}>)
|
367
|
+
node.rows[tblsec].each do |row|
|
368
|
+
result << '<row>'
|
369
|
+
row.each do |cell|
|
370
|
+
halign_attribute = (cell.attr? 'halign') ? %( align="#{cell.attr 'halign'}") : nil
|
371
|
+
valign_attribute = (cell.attr? 'valign') ? %( valign="#{cell.attr 'valign'}") : nil
|
372
|
+
colspan_attribute = cell.colspan ? %( namest="col_#{colnum = cell.column.attr 'colnumber'}" nameend="col_#{colnum + cell.colspan - 1}") : nil
|
373
|
+
rowspan_attribute = cell.rowspan ? %( morerows="#{cell.rowspan - 1}") : nil
|
374
|
+
# NOTE <entry> may not have whitespace (e.g., line breaks) as a direct descendant according to DocBook rules
|
375
|
+
entry_start = %(<entry#{halign_attribute}#{valign_attribute}#{colspan_attribute}#{rowspan_attribute}>)
|
376
|
+
cell_content = if tblsec == :head
|
377
|
+
cell.text
|
378
|
+
else
|
379
|
+
case cell.style
|
380
|
+
when :asciidoc
|
381
|
+
cell.content
|
382
|
+
when :verse
|
383
|
+
%(<literallayout>#{cell.text}</literallayout>)
|
384
|
+
when :literal
|
385
|
+
%(<literallayout class="monospaced">#{cell.text}</literallayout>)
|
386
|
+
when :header
|
387
|
+
cell.content.map {|text| %(<simpara><emphasis role="strong">#{text}</emphasis></simpara>) }.join
|
388
|
+
else
|
389
|
+
cell.content.map {|text| %(<simpara>#{text}</simpara>) }.join
|
390
|
+
end
|
391
|
+
end
|
392
|
+
entry_end = (node.document.attr? 'cellbgcolor') ? %(<?dbfo bgcolor="#{node.document.attr 'cellbgcolor'}"?></entry>) : '</entry>'
|
393
|
+
result << %(#{entry_start}#{cell_content}#{entry_end})
|
394
|
+
end
|
395
|
+
result << '</row>'
|
396
|
+
end
|
397
|
+
result << %(</t#{tblsec}>)
|
398
|
+
end
|
399
|
+
result << '</tgroup>'
|
400
|
+
result << %(</#{tag_name}>)
|
401
|
+
|
402
|
+
warn 'asciidoctor: WARNING: tables must have at least one body row' unless has_body
|
403
|
+
result * EOL
|
404
|
+
end
|
405
|
+
|
406
|
+
alias :toc :skip
|
407
|
+
|
408
|
+
def ulist node
|
409
|
+
result = []
|
410
|
+
if node.style == 'bibliography'
|
411
|
+
result << %(<bibliodiv#{common_attributes node.id, node.role, node.reftext}>)
|
412
|
+
result << %(<title>#{node.title}</title>) if node.title?
|
413
|
+
node.items.each do |item|
|
414
|
+
result << '<bibliomixed>'
|
415
|
+
result << %(<bibliomisc>#{item.text}</bibliomisc>)
|
416
|
+
result << item.content if item.blocks?
|
417
|
+
result << '</bibliomixed>'
|
418
|
+
end
|
419
|
+
result << '</bibliodiv>'
|
420
|
+
else
|
421
|
+
mark_type = (checklist = node.option? 'checklist') ? 'none' : node.style
|
422
|
+
mark_attribute = mark_type ? %( mark="#{mark_type}") : nil
|
423
|
+
result << %(<itemizedlist#{common_attributes node.id, node.role, node.reftext}#{mark_attribute}>)
|
424
|
+
result << %(<title>#{node.title}</title>) if node.title?
|
425
|
+
node.items.each do |item|
|
426
|
+
text_marker = if checklist && (item.attr? 'checkbox')
|
427
|
+
(item.attr? 'checked') ? '✓ ' : '❏ '
|
428
|
+
else
|
429
|
+
nil
|
430
|
+
end
|
431
|
+
result << '<listitem>'
|
432
|
+
result << %(<simpara>#{text_marker}#{item.text}</simpara>)
|
433
|
+
result << item.content if item.blocks?
|
434
|
+
result << '</listitem>'
|
435
|
+
end
|
436
|
+
result << '</itemizedlist>'
|
437
|
+
end
|
438
|
+
|
439
|
+
result * EOL
|
440
|
+
end
|
441
|
+
|
442
|
+
def verse node
|
443
|
+
result = []
|
444
|
+
result << %(<blockquote#{common_attributes node.id, node.role, node.reftext}>)
|
445
|
+
result << %(<title>#{node.title}</title>) if node.title?
|
446
|
+
if (node.attr? 'attribution') || (node.attr? 'citetitle')
|
447
|
+
result << '<attribution>'
|
448
|
+
if node.attr? 'attribution'
|
449
|
+
result << (node.attr 'attribution')
|
450
|
+
end
|
451
|
+
if node.attr? 'citetitle'
|
452
|
+
result << %(<citetitle>#{node.attr 'citetitle'}</citetitle>)
|
453
|
+
end
|
454
|
+
result << '</attribution>'
|
455
|
+
end
|
456
|
+
result << %(<literallayout>#{node.content}</literallayout>)
|
457
|
+
result << '</blockquote>'
|
458
|
+
result * EOL
|
459
|
+
end
|
460
|
+
|
461
|
+
alias :video :skip
|
462
|
+
|
463
|
+
def inline_anchor node
|
464
|
+
case node.type
|
465
|
+
when :ref
|
466
|
+
%(<anchor#{common_attributes node.target, nil, node.text}/>)
|
467
|
+
when :xref
|
468
|
+
if node.attr? 'path', nil
|
469
|
+
linkend = (node.attr 'fragment') || node.target
|
470
|
+
(text = node.text) ? %(<link linkend="#{linkend}">#{text}</link>) : %(<xref linkend="#{linkend}"/>)
|
471
|
+
else
|
472
|
+
%(<link xlink:href="#{target}">#{node.text || (node.attr 'path')}</link>)
|
473
|
+
end
|
474
|
+
when :link
|
475
|
+
%(<link xlink:href="#{node.target}">#{node.text}</link>)
|
476
|
+
when :bibref
|
477
|
+
%(<anchor#{common_attributes node.target, nil, "[#{node.target}]"}/>[#{node.target}])
|
478
|
+
else
|
479
|
+
warn %(asciidoctor: WARNING: unknown anchor type: #{node.type.inspect})
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
def inline_break node
|
484
|
+
%(#{node.text}<?asciidoc-br?>)
|
485
|
+
end
|
486
|
+
|
487
|
+
def inline_button node
|
488
|
+
%(<guibutton>#{node.text}</guibutton>)
|
489
|
+
end
|
490
|
+
|
491
|
+
def inline_callout node
|
492
|
+
%(<co#{common_attributes node.id}/>)
|
493
|
+
end
|
494
|
+
|
495
|
+
def inline_footnote node
|
496
|
+
if node.type == :xref
|
497
|
+
%(<footnoteref linkend="#{node.target}"/>)
|
498
|
+
else
|
499
|
+
%(<footnote#{common_attributes node.id}><simpara>#{node.text}</simpara></footnote>)
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
def inline_image node
|
504
|
+
width_attribute = (node.attr? 'width') ? %( contentwidth="#{node.attr 'width'}") : nil
|
505
|
+
depth_attribute = (node.attr? 'height') ? %( contentdepth="#{node.attr 'height'}") : nil
|
506
|
+
%(<inlinemediaobject>
|
507
|
+
<imageobject>
|
508
|
+
<imagedata fileref="#{node.type == 'icon' ? (node.icon_uri node.target) : (node.image_uri node.target)}"#{width_attribute}#{depth_attribute}/>
|
509
|
+
</imageobject>
|
510
|
+
<textobject><phrase>#{node.attr 'alt'}</phrase></textobject>
|
511
|
+
</inlinemediaobject>)
|
512
|
+
end
|
513
|
+
|
514
|
+
def inline_indexterm node
|
515
|
+
if node.type == :visible
|
516
|
+
%(<indexterm><primary>#{node.text}</primary></indexterm>#{node.text})
|
517
|
+
else
|
518
|
+
terms = node.attr 'terms'
|
519
|
+
result = []
|
520
|
+
if (numterms = terms.size) > 2
|
521
|
+
result << %(<indexterm>
|
522
|
+
<primary>#{terms[0]}</primary><secondary>#{terms[1]}</secondary><tertiary>#{terms[2]}</tertiary>
|
523
|
+
</indexterm>)
|
524
|
+
end
|
525
|
+
if numterms > 1
|
526
|
+
result << %(<indexterm>
|
527
|
+
<primary>#{terms[-2]}</primary><secondary>#{terms[-1]}</secondary>
|
528
|
+
</indexterm>)
|
529
|
+
end
|
530
|
+
result << %(<indexterm>
|
531
|
+
<primary>#{terms[-1]}</primary>
|
532
|
+
</indexterm>)
|
533
|
+
result * EOL
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
def inline_kbd node
|
538
|
+
if (keys = node.attr 'keys').size == 1
|
539
|
+
%(<keycap>#{keys[0]}</keycap>)
|
540
|
+
else
|
541
|
+
key_combo = keys.map {|key| %(<keycap>#{key}</keycap>) }.join
|
542
|
+
%(<keycombo>#{key_combo}</keycombo>)
|
543
|
+
end
|
544
|
+
end
|
545
|
+
|
546
|
+
def inline_menu node
|
547
|
+
menu = node.attr 'menu'
|
548
|
+
if !(submenus = node.attr 'submenus').empty?
|
549
|
+
submenu_path = submenus.map {|submenu| %(<guisubmenu>#{submenu}</guisubmenu> ) }.join.chop
|
550
|
+
%(<menuchoice><guimenu>#{menu}</guimenu> #{submenu_path} <guimenuitem>#{node.attr 'menuitem'}</guimenuitem></menuchoice>)
|
551
|
+
elsif (menuitem = node.attr 'menuitem')
|
552
|
+
%(<menuchoice><guimenu>#{menu}</guimenu> <guimenuitem>#{menuitem}</guimenuitem></menuchoice>)
|
553
|
+
else
|
554
|
+
%(<guimenu>#{menu}</guimenu>)
|
555
|
+
end
|
556
|
+
end
|
557
|
+
|
558
|
+
QUOTE_TAGS = {
|
559
|
+
:emphasis => ['<emphasis>', '</emphasis>', true],
|
560
|
+
:strong => ['<emphasis role="strong">', '</emphasis>', true],
|
561
|
+
:monospaced => ['<literal>', '</literal>', false],
|
562
|
+
:superscript => ['<superscript>', '</superscript>', false],
|
563
|
+
:subscript => ['<subscript>', '</subscript>', false],
|
564
|
+
:double => ['“', '”', true],
|
565
|
+
:single => ['‘', '’', true],
|
566
|
+
:mark => ['<emphasis role="marked">', '</emphasis>', false]
|
567
|
+
}
|
568
|
+
QUOTE_TAGS.default = [nil, nil, true]
|
569
|
+
|
570
|
+
def inline_quoted node
|
571
|
+
if (type = node.type) == :latexmath
|
572
|
+
%(<inlineequation>
|
573
|
+
<alt><![CDATA[#{node.text}]]></alt>
|
574
|
+
<inlinemediaobject><textobject><phrase><![CDATA[#{node.text}]]></phrase></textobject></inlinemediaobject>
|
575
|
+
</inlineequation>)
|
576
|
+
else
|
577
|
+
open, close, supports_phrase = QUOTE_TAGS[type]
|
578
|
+
text = node.text
|
579
|
+
if (role = node.role)
|
580
|
+
if supports_phrase
|
581
|
+
quoted_text = %(#{open}<phrase role="#{role}">#{text}</phrase>#{close})
|
582
|
+
else
|
583
|
+
quoted_text = %(#{open.chop} role="#{role}">#{text}#{close})
|
584
|
+
end
|
585
|
+
else
|
586
|
+
quoted_text = %(#{open}#{text}#{close})
|
587
|
+
end
|
588
|
+
|
589
|
+
node.id ? %(<anchor#{common_attributes node.id, nil, text}/>#{quoted_text}) : quoted_text
|
590
|
+
end
|
591
|
+
end
|
592
|
+
|
593
|
+
def author_element doc, index = nil
|
594
|
+
firstname_key = index ? %(firstname_#{index}) : 'firstname'
|
595
|
+
middlename_key = index ? %(middlename_#{index}) : 'middlename'
|
596
|
+
lastname_key = index ? %(lastname_#{index}) : 'lastname'
|
597
|
+
email_key = index ? %(email_#{index}) : 'email'
|
598
|
+
|
599
|
+
result = []
|
600
|
+
result << '<author>'
|
601
|
+
result << '<personname>'
|
602
|
+
result << %(<firstname>#{doc.attr firstname_key}</firstname>) if doc.attr? firstname_key
|
603
|
+
result << %(<othername>#{doc.attr middlename_key}</othername>) if doc.attr? middlename_key
|
604
|
+
result << %(<surname>#{doc.attr lastname_key}</surname>) if doc.attr? lastname_key
|
605
|
+
result << '</personname>'
|
606
|
+
result << %(<email>#{doc.attr email_key}</email>) if doc.attr? email_key
|
607
|
+
result << '</author>'
|
608
|
+
|
609
|
+
result * EOL
|
610
|
+
end
|
611
|
+
|
612
|
+
def common_attributes id, role = nil, reftext = nil
|
613
|
+
res = id ? %( xml:id="#{id}") : ''
|
614
|
+
res = %(#{res} role="#{role}") if role
|
615
|
+
res = %(#{res} xreflabel="#{reftext}") if reftext
|
616
|
+
res
|
617
|
+
end
|
618
|
+
|
619
|
+
def doctype_declaration root_tag_name
|
620
|
+
nil
|
621
|
+
end
|
622
|
+
|
623
|
+
def document_info_element doc, info_tag_prefix, use_info_tag_prefix = false
|
624
|
+
info_tag_prefix = '' unless use_info_tag_prefix
|
625
|
+
result = []
|
626
|
+
result << %(<#{info_tag_prefix}info>)
|
627
|
+
result << document_title_tags(doc.doctitle :partition => true, :use_fallback => true) unless doc.notitle
|
628
|
+
result << %(<date>#{(doc.attr? 'revdate') ? (doc.attr 'revdate') : (doc.attr 'docdate')}</date>)
|
629
|
+
if doc.has_header?
|
630
|
+
if doc.attr? 'author'
|
631
|
+
if (authorcount = (doc.attr 'authorcount').to_i) < 2
|
632
|
+
result << (author_element doc)
|
633
|
+
result << %(<authorinitials>#{doc.attr 'authorinitials'}</authorinitials>) if doc.attr? 'authorinitials'
|
634
|
+
else
|
635
|
+
result << '<authorgroup>'
|
636
|
+
authorcount.times do |index|
|
637
|
+
result << (author_element doc, index + 1)
|
638
|
+
end
|
639
|
+
result << '</authorgroup>'
|
640
|
+
end
|
641
|
+
end
|
642
|
+
if (doc.attr? 'revdate') && ((doc.attr? 'revnumber') || (doc.attr? 'revremark'))
|
643
|
+
result << %(<revhistory>
|
644
|
+
<revision>)
|
645
|
+
result << %(<revnumber>#{doc.attr 'revnumber'}</revnumber>) if doc.attr? 'revnumber'
|
646
|
+
result << %(<date>#{doc.attr 'revdate'}</date>) if doc.attr? 'revdate'
|
647
|
+
result << %(<authorinitials>#{doc.attr 'authorinitials'}</authorinitials>) if doc.attr? 'authorinitials'
|
648
|
+
result << %(<revremark>#{doc.attr 'revremark'}</revremark>) if doc.attr? 'revremark'
|
649
|
+
result << %(</revision>
|
650
|
+
</revhistory>)
|
651
|
+
end
|
652
|
+
unless (header_docinfo = doc.docinfo :header).empty?
|
653
|
+
result << header_docinfo
|
654
|
+
end
|
655
|
+
result << %(<orgname>#{doc.attr 'orgname'}</orgname>) if doc.attr? 'orgname'
|
656
|
+
end
|
657
|
+
result << %(</#{info_tag_prefix}info>)
|
658
|
+
|
659
|
+
result * EOL
|
660
|
+
end
|
661
|
+
|
662
|
+
def document_ns_attributes doc
|
663
|
+
' xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0"'
|
664
|
+
end
|
665
|
+
|
666
|
+
def document_title_tags title
|
667
|
+
if title.subtitle?
|
668
|
+
%(<title>#{title.main}</title>
|
669
|
+
<subtitle>#{title.subtitle}</subtitle>)
|
670
|
+
else
|
671
|
+
%(<title>#{title}</title>)
|
672
|
+
end
|
673
|
+
end
|
674
|
+
|
675
|
+
# FIXME this should be handled through a template mechanism
|
676
|
+
def resolve_content node
|
677
|
+
node.content_model == :compound ? node.content : %(<simpara>#{node.content}</simpara>)
|
678
|
+
end
|
679
|
+
|
680
|
+
def title_tag node, optional = true
|
681
|
+
!optional || node.title? ? %(<title>#{node.title}</title>\n) : nil
|
682
|
+
end
|
683
|
+
end
|
684
|
+
end
|