asciidoctor 1.5.8 → 2.0.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (197) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +11 -0
  3. data/CHANGELOG.adoc +628 -45
  4. data/LICENSE +2 -1
  5. data/README-de.adoc +28 -38
  6. data/README-fr.adoc +30 -43
  7. data/README-jp.adoc +255 -201
  8. data/README-zh_CN.adoc +40 -44
  9. data/README.adoc +170 -143
  10. data/asciidoctor.gemspec +22 -34
  11. data/bin/asciidoctor +5 -4
  12. data/data/locale/attributes-ar.adoc +4 -3
  13. data/data/locale/attributes-be.adoc +23 -0
  14. data/data/locale/attributes-bg.adoc +4 -3
  15. data/data/locale/attributes-ca.adoc +6 -5
  16. data/data/locale/attributes-cs.adoc +4 -3
  17. data/data/locale/attributes-da.adoc +6 -5
  18. data/data/locale/attributes-de.adoc +6 -5
  19. data/data/locale/attributes-en.adoc +4 -4
  20. data/data/locale/attributes-es.adoc +6 -5
  21. data/data/locale/attributes-fa.adoc +4 -3
  22. data/data/locale/attributes-fi.adoc +4 -3
  23. data/data/locale/attributes-fr.adoc +8 -7
  24. data/data/locale/attributes-hu.adoc +4 -3
  25. data/data/locale/attributes-id.adoc +4 -3
  26. data/data/locale/attributes-it.adoc +6 -5
  27. data/data/locale/attributes-ja.adoc +4 -3
  28. data/data/locale/{attributes-kr.adoc → attributes-ko.adoc} +4 -3
  29. data/data/locale/attributes-nb.adoc +4 -3
  30. data/data/locale/attributes-nl.adoc +6 -5
  31. data/data/locale/attributes-nn.adoc +4 -3
  32. data/data/locale/attributes-pl.adoc +8 -7
  33. data/data/locale/attributes-pt.adoc +6 -5
  34. data/data/locale/attributes-pt_BR.adoc +6 -5
  35. data/data/locale/attributes-ro.adoc +4 -3
  36. data/data/locale/attributes-ru.adoc +6 -5
  37. data/data/locale/attributes-sr.adoc +4 -4
  38. data/data/locale/attributes-sr_Latn.adoc +4 -4
  39. data/data/locale/attributes-sv.adoc +4 -4
  40. data/data/locale/attributes-th.adoc +23 -0
  41. data/data/locale/attributes-tr.adoc +4 -3
  42. data/data/locale/attributes-uk.adoc +6 -5
  43. data/data/locale/attributes-vi.adoc +23 -0
  44. data/data/locale/attributes-zh_CN.adoc +4 -3
  45. data/data/locale/attributes-zh_TW.adoc +4 -3
  46. data/data/reference/syntax.adoc +296 -0
  47. data/data/stylesheets/asciidoctor-default.css +120 -114
  48. data/data/stylesheets/coderay-asciidoctor.css +15 -17
  49. data/lib/asciidoctor/abstract_block.rb +146 -140
  50. data/lib/asciidoctor/abstract_node.rb +152 -170
  51. data/lib/asciidoctor/attribute_list.rb +77 -89
  52. data/lib/asciidoctor/block.rb +29 -28
  53. data/lib/asciidoctor/callouts.rb +4 -2
  54. data/lib/asciidoctor/cli/invoker.rb +20 -24
  55. data/lib/asciidoctor/cli/options.rb +107 -96
  56. data/lib/asciidoctor/cli.rb +3 -2
  57. data/lib/asciidoctor/convert.rb +199 -0
  58. data/lib/asciidoctor/converter/composite.rb +40 -48
  59. data/lib/asciidoctor/converter/docbook5.rb +627 -644
  60. data/lib/asciidoctor/converter/html5.rb +1053 -951
  61. data/lib/asciidoctor/converter/manpage.rb +581 -532
  62. data/lib/asciidoctor/converter/template.rb +232 -271
  63. data/lib/asciidoctor/converter.rb +370 -185
  64. data/lib/asciidoctor/core_ext/float/truncate.rb +20 -0
  65. data/lib/asciidoctor/core_ext/hash/merge.rb +8 -0
  66. data/lib/asciidoctor/core_ext/match_data/names.rb +7 -0
  67. data/lib/asciidoctor/core_ext/nil_or_empty.rb +1 -0
  68. data/lib/asciidoctor/core_ext/regexp/is_match.rb +4 -2
  69. data/lib/asciidoctor/core_ext.rb +8 -17
  70. data/lib/asciidoctor/document.rb +503 -461
  71. data/lib/asciidoctor/extensions.rb +127 -174
  72. data/lib/asciidoctor/helpers.rb +184 -107
  73. data/lib/asciidoctor/inline.rb +9 -12
  74. data/lib/asciidoctor/list.rb +11 -29
  75. data/lib/asciidoctor/load.rb +119 -0
  76. data/lib/asciidoctor/logging.rb +22 -17
  77. data/lib/asciidoctor/parser.rb +673 -719
  78. data/lib/asciidoctor/path_resolver.rb +48 -33
  79. data/lib/asciidoctor/reader.rb +383 -338
  80. data/lib/asciidoctor/rouge_ext.rb +39 -0
  81. data/lib/asciidoctor/rx.rb +723 -0
  82. data/lib/asciidoctor/section.rb +17 -16
  83. data/lib/asciidoctor/stylesheets.rb +19 -37
  84. data/lib/asciidoctor/substitutors.rb +926 -1022
  85. data/lib/asciidoctor/syntax_highlighter/coderay.rb +88 -0
  86. data/lib/asciidoctor/syntax_highlighter/highlightjs.rb +34 -0
  87. data/lib/asciidoctor/syntax_highlighter/html_pipeline.rb +10 -0
  88. data/lib/asciidoctor/syntax_highlighter/prettify.rb +30 -0
  89. data/lib/asciidoctor/syntax_highlighter/pygments.rb +157 -0
  90. data/lib/asciidoctor/syntax_highlighter/rouge.rb +143 -0
  91. data/lib/asciidoctor/syntax_highlighter.rb +253 -0
  92. data/lib/asciidoctor/table.rb +152 -114
  93. data/lib/asciidoctor/timings.rb +7 -5
  94. data/lib/asciidoctor/version.rb +2 -1
  95. data/lib/asciidoctor/writer.rb +30 -0
  96. data/lib/asciidoctor.rb +266 -1340
  97. data/man/asciidoctor.1 +49 -47
  98. data/man/asciidoctor.adoc +54 -45
  99. metadata +50 -245
  100. data/CONTRIBUTING.adoc +0 -185
  101. data/Gemfile +0 -60
  102. data/Rakefile +0 -129
  103. data/bin/asciidoctor-safe +0 -15
  104. data/features/open_block.feature +0 -92
  105. data/features/pass_block.feature +0 -66
  106. data/features/step_definitions.rb +0 -49
  107. data/features/text_formatting.feature +0 -57
  108. data/features/xref.feature +0 -1039
  109. data/lib/asciidoctor/converter/base.rb +0 -59
  110. data/lib/asciidoctor/converter/docbook45.rb +0 -93
  111. data/lib/asciidoctor/converter/factory.rb +0 -226
  112. data/lib/asciidoctor/core_ext/1.8.7/base64/strict_encode64.rb +0 -6
  113. data/lib/asciidoctor/core_ext/1.8.7/concurrent/hash.rb +0 -5
  114. data/lib/asciidoctor/core_ext/1.8.7/hash/key.rb +0 -4
  115. data/lib/asciidoctor/core_ext/1.8.7/io/binread.rb +0 -6
  116. data/lib/asciidoctor/core_ext/1.8.7/io/write.rb +0 -5
  117. data/lib/asciidoctor/core_ext/1.8.7/string/chr.rb +0 -6
  118. data/lib/asciidoctor/core_ext/1.8.7/string/limit_bytesize.rb +0 -29
  119. data/lib/asciidoctor/core_ext/1.8.7/symbol/empty.rb +0 -6
  120. data/lib/asciidoctor/core_ext/1.8.7/symbol/length.rb +0 -6
  121. data/lib/asciidoctor/core_ext/string/limit_bytesize.rb +0 -10
  122. data/test/api_test.rb +0 -1240
  123. data/test/attribute_list_test.rb +0 -242
  124. data/test/attributes_test.rb +0 -1623
  125. data/test/blocks_test.rb +0 -3870
  126. data/test/converter_test.rb +0 -470
  127. data/test/document_test.rb +0 -1853
  128. data/test/extensions_test.rb +0 -1560
  129. data/test/fixtures/asciidoc_index.txt +0 -521
  130. data/test/fixtures/basic-docinfo-footer.html +0 -6
  131. data/test/fixtures/basic-docinfo-footer.xml +0 -8
  132. data/test/fixtures/basic-docinfo.html +0 -1
  133. data/test/fixtures/basic-docinfo.xml +0 -4
  134. data/test/fixtures/basic.asciidoc +0 -5
  135. data/test/fixtures/chapter-a.adoc +0 -3
  136. data/test/fixtures/child-include.adoc +0 -5
  137. data/test/fixtures/circle.svg +0 -9
  138. data/test/fixtures/custom-backends/erb/html5/block_paragraph.html.erb +0 -6
  139. data/test/fixtures/custom-backends/haml/docbook45/block_paragraph.xml.haml +0 -6
  140. data/test/fixtures/custom-backends/haml/html5/block_paragraph.html.haml +0 -3
  141. data/test/fixtures/custom-backends/haml/html5/block_sidebar.html.haml +0 -5
  142. data/test/fixtures/custom-backends/haml/html5-tweaks/block_paragraph.html.haml +0 -1
  143. data/test/fixtures/custom-backends/slim/docbook45/block_paragraph.xml.slim +0 -6
  144. data/test/fixtures/custom-backends/slim/html5/block_paragraph.html.slim +0 -3
  145. data/test/fixtures/custom-backends/slim/html5/block_sidebar.html.slim +0 -5
  146. data/test/fixtures/custom-docinfodir/basic-docinfo.html +0 -1
  147. data/test/fixtures/custom-docinfodir/docinfo.html +0 -1
  148. data/test/fixtures/docinfo-footer.html +0 -1
  149. data/test/fixtures/docinfo-footer.xml +0 -9
  150. data/test/fixtures/docinfo.html +0 -1
  151. data/test/fixtures/docinfo.xml +0 -3
  152. data/test/fixtures/doctime-localtime.adoc +0 -2
  153. data/test/fixtures/dot.gif +0 -0
  154. data/test/fixtures/encoding.asciidoc +0 -13
  155. data/test/fixtures/file-with-missing-include.adoc +0 -1
  156. data/test/fixtures/grandchild-include.adoc +0 -3
  157. data/test/fixtures/hello-asciidoctor.pdf +0 -69
  158. data/test/fixtures/include-file.asciidoc +0 -24
  159. data/test/fixtures/include-file.jsx +0 -8
  160. data/test/fixtures/include-file.ml +0 -3
  161. data/test/fixtures/include-file.xml +0 -5
  162. data/test/fixtures/lists.adoc +0 -96
  163. data/test/fixtures/master.adoc +0 -5
  164. data/test/fixtures/mismatched-end-tag.adoc +0 -7
  165. data/test/fixtures/other-chapters.adoc +0 -11
  166. data/test/fixtures/outer-include.adoc +0 -5
  167. data/test/fixtures/parent-include-restricted.adoc +0 -5
  168. data/test/fixtures/parent-include.adoc +0 -5
  169. data/test/fixtures/sample.asciidoc +0 -30
  170. data/test/fixtures/section-a.adoc +0 -4
  171. data/test/fixtures/stylesheets/custom.css +0 -3
  172. data/test/fixtures/subdir/index.adoc +0 -3
  173. data/test/fixtures/subdir/inner-include.adoc +0 -3
  174. data/test/fixtures/subdir/middle-include.adoc +0 -5
  175. data/test/fixtures/subs-docinfo.html +0 -2
  176. data/test/fixtures/subs.adoc +0 -6
  177. data/test/fixtures/tagged-class-enclosed.rb +0 -25
  178. data/test/fixtures/tagged-class.rb +0 -23
  179. data/test/fixtures/tip.gif +0 -0
  180. data/test/fixtures/unclosed-tag.adoc +0 -3
  181. data/test/fixtures/unexpected-end-tag.adoc +0 -4
  182. data/test/invoker_test.rb +0 -745
  183. data/test/links_test.rb +0 -855
  184. data/test/lists_test.rb +0 -5151
  185. data/test/logger_test.rb +0 -211
  186. data/test/manpage_test.rb +0 -660
  187. data/test/options_test.rb +0 -262
  188. data/test/paragraphs_test.rb +0 -562
  189. data/test/parser_test.rb +0 -742
  190. data/test/paths_test.rb +0 -395
  191. data/test/preamble_test.rb +0 -173
  192. data/test/reader_test.rb +0 -2161
  193. data/test/sections_test.rb +0 -3575
  194. data/test/substitutions_test.rb +0 -2066
  195. data/test/tables_test.rb +0 -2036
  196. data/test/test_helper.rb +0 -447
  197. data/test/text_test.rb +0 -309
@@ -1,815 +1,798 @@
1
- # encoding: UTF-8
1
+ # frozen_string_literal: true
2
2
  module Asciidoctor
3
- # A built-in {Converter} implementation that generates DocBook 5 output
4
- # similar to the docbook45 backend from AsciiDoc Python, but migrated to the
5
- # DocBook 5 specification.
6
- class Converter::DocBook5Converter < Converter::BuiltIn
7
- CopyrightRx = /^(.+?)(?: ((?:\d{4}\-)?\d{4}))?$/
8
- ImageMacroRx = /^image::?(.+?)\[(.*?)\]$/
9
-
10
- def document node
11
- result = []
12
- if (root_tag_name = node.doctype) == 'manpage'
13
- root_tag_name = 'refentry'
14
- end
15
- result << '<?xml version="1.0" encoding="UTF-8"?>'
16
- if (doctype_line = doctype_declaration root_tag_name)
17
- result << doctype_line
18
- end
19
- if node.attr? 'toc'
20
- if node.attr? 'toclevels'
21
- result << %(<?asciidoc-toc maxdepth="#{node.attr 'toclevels'}"?>)
22
- else
23
- result << '<?asciidoc-toc?>'
24
- end
25
- end
26
- if node.attr? 'sectnums'
27
- if node.attr? 'sectnumlevels'
28
- result << %(<?asciidoc-numbered maxdepth="#{node.attr 'sectnumlevels'}"?>)
29
- else
30
- result << '<?asciidoc-numbered?>'
31
- end
32
- end
33
- lang_attribute = (node.attr? 'nolang') ? '' : %( #{lang_attribute_name}="#{node.attr 'lang', 'en'}")
34
- result << %(<#{root_tag_name}#{document_ns_attributes node}#{lang_attribute}>)
35
- result << (document_info_tag node, root_tag_name) unless node.noheader
36
- result << node.content if node.blocks?
37
- unless (footer_docinfo = node.docinfo :footer).empty?
38
- result << footer_docinfo
39
- end
40
- result << %(</#{root_tag_name}>)
41
- result.join LF
42
- end
3
+ # A built-in {Converter} implementation that generates DocBook 5 output. The output is inspired by the output produced
4
+ # by the docbook45 backend from AsciiDoc.py, except it has been migrated to the DocBook 5 specification.
5
+ class Converter::DocBook5Converter < Converter::Base
6
+ register_for 'docbook5'
7
+
8
+ # default represents variablelist
9
+ (DLIST_TAGS = {
10
+ 'qanda' => { list: 'qandaset', entry: 'qandaentry', label: 'question', term: 'simpara', item: 'answer' },
11
+ 'glossary' => { list: nil, entry: 'glossentry', term: 'glossterm', item: 'glossdef' },
12
+ }).default = { list: 'variablelist', entry: 'varlistentry', term: 'term', item: 'listitem' }
13
+
14
+ (QUOTE_TAGS = {
15
+ monospaced: ['<literal>', '</literal>'],
16
+ emphasis: ['<emphasis>', '</emphasis>', true],
17
+ strong: ['<emphasis role="strong">', '</emphasis>', true],
18
+ double: ['<quote>', '</quote>', true],
19
+ single: ['<quote>', '</quote>', true],
20
+ mark: ['<emphasis role="marked">', '</emphasis>'],
21
+ superscript: ['<superscript>', '</superscript>'],
22
+ subscript: ['<subscript>', '</subscript>'],
23
+ }).default = ['', '', true]
24
+
25
+ MANPAGE_SECTION_TAGS = { 'section' => 'refsection', 'synopsis' => 'refsynopsisdiv' }
26
+ TABLE_PI_NAMES = ['dbhtml', 'dbfo', 'dblatex']
27
+
28
+ CopyrightRx = /^(#{CC_ANY}+?)(?: ((?:\d{4}-)?\d{4}))?$/
29
+ ImageMacroRx = /^image::?(\S|\S#{CC_ANY}*?\S)\[(#{CC_ANY}+)?\]$/
30
+
31
+ def initialize backend, opts = {}
32
+ @backend = backend
33
+ init_backend_traits basebackend: 'docbook', filetype: 'xml', outfilesuffix: '.xml', supports_templates: true
34
+ end
43
35
 
44
- alias embedded content
36
+ def convert_document node
37
+ result = ['<?xml version="1.0" encoding="UTF-8"?>']
38
+ result << ((node.attr? 'toclevels') ? %(<?asciidoc-toc maxdepth="#{node.attr 'toclevels'}"?>) : '<?asciidoc-toc?>') if node.attr? 'toc'
39
+ result << ((node.attr? 'sectnumlevels') ? %(<?asciidoc-numbered maxdepth="#{node.attr 'sectnumlevels'}"?>) : '<?asciidoc-numbered?>') if node.attr? 'sectnums'
40
+ lang_attribute = (node.attr? 'nolang') ? '' : %( xml:lang="#{node.attr 'lang', 'en'}")
41
+ if (root_tag_name = node.doctype) == 'manpage'
42
+ root_tag_name = 'refentry'
43
+ end
44
+ root_tag_idx = result.size
45
+ id = node.id
46
+ result << (document_info_tag node) unless node.noheader
47
+ unless (docinfo_content = node.docinfo :header).empty?
48
+ result << docinfo_content
49
+ end
50
+ result << node.content if node.blocks?
51
+ unless (docinfo_content = node.docinfo :footer).empty?
52
+ result << docinfo_content
53
+ end
54
+ id, node.id = node.id, nil unless id
55
+ # defer adding root tag in case document ID is auto-generated on demand
56
+ result.insert root_tag_idx, %(<#{root_tag_name} xmlns="http://docbook.org/ns/docbook" xmlns:xl="http://www.w3.org/1999/xlink" version="5.0"#{lang_attribute}#{common_attributes id}>)
57
+ result << %(</#{root_tag_name}>)
58
+ result.join LF
59
+ end
45
60
 
46
- MANPAGE_SECTION_TAGS = { 'section' => 'refsection', 'synopsis' => 'refsynopsisdiv' }
61
+ alias convert_embedded content_only
47
62
 
48
- def section node
49
- if node.document.doctype == 'manpage'
50
- tag_name = MANPAGE_SECTION_TAGS[tag_name = node.sectname] || tag_name
51
- else
52
- tag_name = node.sectname
53
- end
54
- title_el = node.special && (node.option? 'untitled') ? '' : %(<title>#{node.title}</title>\n)
55
- %(<#{tag_name}#{common_attributes node.id, node.role, node.reftext}>
63
+ def convert_section node
64
+ if node.document.doctype == 'manpage'
65
+ tag_name = MANPAGE_SECTION_TAGS[tag_name = node.sectname] || tag_name
66
+ else
67
+ tag_name = node.sectname
68
+ end
69
+ title_el = node.special && (node.option? 'untitled') ? '' : %(<title>#{node.title}</title>\n)
70
+ %(<#{tag_name}#{common_attributes node.id, node.role, node.reftext}>
56
71
  #{title_el}#{node.content}
57
72
  </#{tag_name}>)
58
- end
73
+ end
59
74
 
60
- def admonition node
61
- %(<#{tag_name = node.attr 'name'}#{common_attributes node.id, node.role, node.reftext}>
62
- #{title_tag node}#{resolve_content node}
75
+ def convert_admonition node
76
+ %(<#{tag_name = node.attr 'name'}#{common_attributes node.id, node.role, node.reftext}>
77
+ #{title_tag node}#{enclose_content node}
63
78
  </#{tag_name}>)
64
- end
79
+ end
65
80
 
66
- alias audio skip
81
+ alias convert_audio skip
67
82
 
68
- def colist node
69
- result = []
70
- result << %(<calloutlist#{common_attributes node.id, node.role, node.reftext}>)
71
- result << %(<title>#{node.title}</title>) if node.title?
72
- node.items.each do |item|
73
- result << %(<callout arearefs="#{item.attr 'coids'}">)
74
- result << %(<para>#{item.text}</para>)
75
- result << item.content if item.blocks?
76
- result << '</callout>'
77
- end
78
- result << %(</calloutlist>)
79
- result.join LF
80
- end
81
-
82
- (DLIST_TAGS = {
83
- 'qanda' => {
84
- :list => 'qandaset',
85
- :entry => 'qandaentry',
86
- :label => 'question',
87
- :term => 'simpara',
88
- :item => 'answer'
89
- },
90
- 'glossary' => {
91
- :list => nil,
92
- :entry => 'glossentry',
93
- :term => 'glossterm',
94
- :item => 'glossdef'
95
- }
96
- }).default = { # default is variable
97
- :list => 'variablelist',
98
- :entry => 'varlistentry',
99
- :term => 'term',
100
- :item => 'listitem'
101
- }
102
-
103
- def dlist node
104
- result = []
105
- if node.style == 'horizontal'
106
- result << %(<#{tag_name = node.title? ? 'table' : 'informaltable'}#{common_attributes node.id, node.role, node.reftext} tabstyle="horizontal" frame="none" colsep="0" rowsep="0">
83
+ def convert_colist node
84
+ result = []
85
+ result << %(<calloutlist#{common_attributes node.id, node.role, node.reftext}>)
86
+ result << %(<title>#{node.title}</title>) if node.title?
87
+ node.items.each do |item|
88
+ result << %(<callout arearefs="#{item.attr 'coids'}">)
89
+ result << %(<para>#{item.text}</para>)
90
+ result << item.content if item.blocks?
91
+ result << '</callout>'
92
+ end
93
+ result << %(</calloutlist>)
94
+ result.join LF
95
+ end
96
+
97
+ def convert_dlist node
98
+ result = []
99
+ if node.style == 'horizontal'
100
+ result << %(<#{tag_name = node.title? ? 'table' : 'informaltable'}#{common_attributes node.id, node.role, node.reftext} tabstyle="horizontal" frame="none" colsep="0" rowsep="0">
107
101
  #{title_tag node}<tgroup cols="2">
108
102
  <colspec colwidth="#{node.attr 'labelwidth', 15}*"/>
109
103
  <colspec colwidth="#{node.attr 'itemwidth', 85}*"/>
110
104
  <tbody valign="top">)
111
- node.items.each do |terms, dd|
112
- result << %(<row>
105
+ node.items.each do |terms, dd|
106
+ result << %(<row>
113
107
  <entry>)
114
- [*terms].each do |dt|
115
- result << %(<simpara>#{dt.text}</simpara>)
116
- end
117
- result << %(</entry>
108
+ terms.each {|dt| result << %(<simpara>#{dt.text}</simpara>) }
109
+ result << %(</entry>
118
110
  <entry>)
119
- if dd
120
- result << %(<simpara>#{dd.text}</simpara>) if dd.text?
121
- result << dd.content if dd.blocks?
122
- end
123
- result << %(</entry>
124
- </row>)
111
+ if dd
112
+ result << %(<simpara>#{dd.text}</simpara>) if dd.text?
113
+ result << dd.content if dd.blocks?
125
114
  end
126
- result << %(</tbody>
115
+ result << %(</entry>
116
+ </row>)
117
+ end
118
+ result << %(</tbody>
127
119
  </tgroup>
128
120
  </#{tag_name}>)
129
- else
130
- tags = DLIST_TAGS[node.style]
131
- list_tag = tags[:list]
132
- entry_tag = tags[:entry]
133
- label_tag = tags[:label]
134
- term_tag = tags[:term]
135
- item_tag = tags[:item]
136
- if list_tag
137
- result << %(<#{list_tag}#{common_attributes node.id, node.role, node.reftext}>)
138
- result << %(<title>#{node.title}</title>) if node.title?
139
- end
140
-
141
- node.items.each do |terms, dd|
142
- result << %(<#{entry_tag}>)
143
- result << %(<#{label_tag}>) if label_tag
144
-
145
- [*terms].each do |dt|
146
- result << %(<#{term_tag}>#{dt.text}</#{term_tag}>)
147
- end
121
+ else
122
+ tags = DLIST_TAGS[node.style]
123
+ list_tag = tags[:list]
124
+ entry_tag = tags[:entry]
125
+ label_tag = tags[:label]
126
+ term_tag = tags[:term]
127
+ item_tag = tags[:item]
128
+ if list_tag
129
+ result << %(<#{list_tag}#{common_attributes node.id, node.role, node.reftext}>)
130
+ result << %(<title>#{node.title}</title>) if node.title?
131
+ end
148
132
 
149
- result << %(</#{label_tag}>) if label_tag
150
- result << %(<#{item_tag}>)
151
- if dd
152
- result << %(<simpara>#{dd.text}</simpara>) if dd.text?
153
- result << dd.content if dd.blocks?
154
- end
155
- result << %(</#{item_tag}>)
156
- result << %(</#{entry_tag}>)
133
+ node.items.each do |terms, dd|
134
+ result << %(<#{entry_tag}>)
135
+ result << %(<#{label_tag}>) if label_tag
136
+ terms.each {|dt| result << %(<#{term_tag}>#{dt.text}</#{term_tag}>) }
137
+ result << %(</#{label_tag}>) if label_tag
138
+ result << %(<#{item_tag}>)
139
+ if dd
140
+ result << %(<simpara>#{dd.text}</simpara>) if dd.text?
141
+ result << dd.content if dd.blocks?
157
142
  end
158
-
159
- result << %(</#{list_tag}>) if list_tag
143
+ result << %(</#{item_tag}>)
144
+ result << %(</#{entry_tag}>)
160
145
  end
161
146
 
162
- result.join LF
147
+ result << %(</#{list_tag}>) if list_tag
163
148
  end
164
149
 
165
- def example node
166
- if node.title?
167
- %(<example#{common_attributes node.id, node.role, node.reftext}>
150
+ result.join LF
151
+ end
152
+
153
+ def convert_example node
154
+ if node.title?
155
+ %(<example#{common_attributes node.id, node.role, node.reftext}>
168
156
  <title>#{node.title}</title>
169
- #{resolve_content node}
157
+ #{enclose_content node}
170
158
  </example>)
171
- else
172
- %(<informalexample#{common_attributes node.id, node.role, node.reftext}>
173
- #{resolve_content node}
159
+ else
160
+ %(<informalexample#{common_attributes node.id, node.role, node.reftext}>
161
+ #{enclose_content node}
174
162
  </informalexample>)
175
- end
176
163
  end
164
+ end
177
165
 
178
- def floating_title node
179
- %(<bridgehead#{common_attributes node.id, node.role, node.reftext} renderas="sect#{node.level}">#{node.title}</bridgehead>)
180
- end
166
+ def convert_floating_title node
167
+ %(<bridgehead#{common_attributes node.id, node.role, node.reftext} renderas="sect#{node.level}">#{node.title}</bridgehead>)
168
+ end
181
169
 
182
- def image node
183
- # NOTE according to the DocBook spec, content area, scaling, and scaling to fit are mutually exclusive
184
- # See http://tdg.docbook.org/tdg/4.5/imagedata-x.html#d0e79635
185
- if node.attr? 'scaledwidth'
186
- width_attribute = %( width="#{node.attr 'scaledwidth'}")
187
- depth_attribute = ''
188
- scale_attribute = ''
189
- elsif node.attr? 'scale'
190
- # QUESTION should we set the viewport using width and depth? (the scaled image would be contained within this box)
191
- #width_attribute = (node.attr? 'width') ? %( width="#{node.attr 'width'}") : ''
192
- #depth_attribute = (node.attr? 'height') ? %( depth="#{node.attr 'height'}") : ''
193
- scale_attribute = %( scale="#{node.attr 'scale'}")
194
- else
195
- width_attribute = (node.attr? 'width') ? %( contentwidth="#{node.attr 'width'}") : ''
196
- depth_attribute = (node.attr? 'height') ? %( contentdepth="#{node.attr 'height'}") : ''
197
- scale_attribute = ''
198
- end
199
- align_attribute = (node.attr? 'align') ? %( align="#{node.attr 'align'}") : ''
170
+ def convert_image node
171
+ # NOTE according to the DocBook spec, content area, scaling, and scaling to fit are mutually exclusive
172
+ # See http://tdg.docbook.org/tdg/4.5/imagedata-x.html#d0e79635
173
+ if node.attr? 'scaledwidth'
174
+ width_attribute = %( width="#{node.attr 'scaledwidth'}")
175
+ depth_attribute = ''
176
+ scale_attribute = ''
177
+ elsif node.attr? 'scale'
178
+ # QUESTION should we set the viewport using width and depth? (the scaled image would be contained within this box)
179
+ #width_attribute = (node.attr? 'width') ? %( width="#{node.attr 'width'}") : ''
180
+ #depth_attribute = (node.attr? 'height') ? %( depth="#{node.attr 'height'}") : ''
181
+ scale_attribute = %( scale="#{node.attr 'scale'}")
182
+ else
183
+ width_attribute = (node.attr? 'width') ? %( contentwidth="#{node.attr 'width'}") : ''
184
+ depth_attribute = (node.attr? 'height') ? %( contentdepth="#{node.attr 'height'}") : ''
185
+ scale_attribute = ''
186
+ end
187
+ align_attribute = (node.attr? 'align') ? %( align="#{node.attr 'align'}") : ''
200
188
 
201
- mediaobject = %(<mediaobject>
189
+ mediaobject = %(<mediaobject>
202
190
  <imageobject>
203
191
  <imagedata fileref="#{node.image_uri(node.attr 'target')}"#{width_attribute}#{depth_attribute}#{scale_attribute}#{align_attribute}/>
204
192
  </imageobject>
205
193
  <textobject><phrase>#{node.alt}</phrase></textobject>
206
194
  </mediaobject>)
207
195
 
208
- if node.title?
209
- %(<figure#{common_attributes node.id, node.role, node.reftext}>
196
+ if node.title?
197
+ %(<figure#{common_attributes node.id, node.role, node.reftext}>
210
198
  <title>#{node.title}</title>
211
199
  #{mediaobject}
212
200
  </figure>)
213
- else
214
- %(<informalfigure#{common_attributes node.id, node.role, node.reftext}>
201
+ else
202
+ %(<informalfigure#{common_attributes node.id, node.role, node.reftext}>
215
203
  #{mediaobject}
216
204
  </informalfigure>)
217
- end
218
205
  end
206
+ end
219
207
 
220
- def listing node
221
- informal = !node.title?
222
- listing_attributes = (common_attributes node.id, node.role, node.reftext)
223
- if node.style == 'source' && ((attrs = node.attributes).key? 'language')
224
- if attrs.key? 'linenums'
225
- if attrs.key? 'start'
226
- numbering_attributes = %( linenumbering="numbered" startinglinenumber="#{attrs['start'].to_i}")
227
- else
228
- numbering_attributes = ' linenumbering="numbered"'
229
- end
230
- else
231
- numbering_attributes = ' linenumbering="unnumbered"'
232
- end
233
- listing_content = %(<programlisting#{informal ? listing_attributes : ''} language="#{attrs['language']}"#{numbering_attributes}>#{node.content}</programlisting>)
208
+ def convert_listing node
209
+ informal = !node.title?
210
+ common_attrs = common_attributes node.id, node.role, node.reftext
211
+ if node.style == 'source'
212
+ if (attrs = node.attributes).key? 'linenums'
213
+ numbering_attrs = (attrs.key? 'start') ? %( linenumbering="numbered" startinglinenumber="#{attrs['start'].to_i}") : ' linenumbering="numbered"'
234
214
  else
235
- listing_content = %(<screen#{informal ? listing_attributes : ''}>#{node.content}</screen>)
215
+ numbering_attrs = ' linenumbering="unnumbered"'
236
216
  end
237
- if informal
238
- listing_content
217
+ if attrs.key? 'language'
218
+ wrapped_content = %(<programlisting#{informal ? common_attrs : ''} language="#{attrs['language']}"#{numbering_attrs}>#{node.content}</programlisting>)
239
219
  else
240
- %(<formalpara#{listing_attributes}>
220
+ wrapped_content = %(<screen#{informal ? common_attrs : ''}#{numbering_attrs}>#{node.content}</screen>)
221
+ end
222
+ else
223
+ wrapped_content = %(<screen#{informal ? common_attrs : ''}>#{node.content}</screen>)
224
+ end
225
+ informal ? wrapped_content : %(<formalpara#{common_attrs}>
241
226
  <title>#{node.title}</title>
242
227
  <para>
243
- #{listing_content}
228
+ #{wrapped_content}
244
229
  </para>
245
230
  </formalpara>)
246
- end
247
- end
231
+ end
248
232
 
249
- def literal node
250
- if node.title?
251
- %(<formalpara#{common_attributes node.id, node.role, node.reftext}>
233
+ def convert_literal node
234
+ if node.title?
235
+ %(<formalpara#{common_attributes node.id, node.role, node.reftext}>
252
236
  <title>#{node.title}</title>
253
237
  <para>
254
238
  <literallayout class="monospaced">#{node.content}</literallayout>
255
239
  </para>
256
240
  </formalpara>)
257
- else
258
- %(<literallayout#{common_attributes node.id, node.role, node.reftext} class="monospaced">#{node.content}</literallayout>)
259
- end
241
+ else
242
+ %(<literallayout#{common_attributes node.id, node.role, node.reftext} class="monospaced">#{node.content}</literallayout>)
260
243
  end
244
+ end
261
245
 
262
- def stem node
263
- if (idx = node.subs.index :specialcharacters)
264
- node.subs.delete_at idx
265
- equation = node.content
266
- idx > 0 ? (node.subs.insert idx, :specialcharacters) : (node.subs.unshift :specialcharacters)
267
- else
268
- equation = node.content
269
- end
270
- if node.style == 'asciimath'
271
- if (@asciimath ||= ((defined? ::AsciiMath) || (Helpers.require_library 'asciimath', true, :warn)).nil? ? :unavailable : :loaded) == :loaded
272
- # NOTE fop requires jeuclid to process raw mathml
273
- equation_data = (::AsciiMath.parse equation).to_mathml 'mml:', 'xmlns:mml' => 'http://www.w3.org/1998/Math/MathML'
274
- else
275
- equation_data = %(<mathphrase><![CDATA[#{equation}]]></mathphrase>)
276
- end
277
- else
278
- # unhandled math; pass source to alt and required mathphrase element; dblatex will process alt as LaTeX math
279
- equation_data = %(<alt><![CDATA[#{equation}]]></alt>
246
+ alias convert_pass content_only
247
+
248
+ def convert_stem node
249
+ if (idx = node.subs.index :specialcharacters)
250
+ node.subs.delete_at idx
251
+ equation = node.content || ''
252
+ idx > 0 ? (node.subs.insert idx, :specialcharacters) : (node.subs.unshift :specialcharacters)
253
+ else
254
+ equation = node.content || ''
255
+ end
256
+ if node.style == 'asciimath'
257
+ # NOTE fop requires jeuclid to process mathml markup
258
+ equation_data = asciimath_available? ? ((::AsciiMath.parse equation).to_mathml 'mml:', 'xmlns:mml' => 'http://www.w3.org/1998/Math/MathML') : %(<mathphrase><![CDATA[#{equation}]]></mathphrase>)
259
+ else
260
+ # unhandled math; pass source to alt and required mathphrase element; dblatex will process alt as LaTeX math
261
+ equation_data = %(<alt><![CDATA[#{equation}]]></alt>
280
262
  <mathphrase><![CDATA[#{equation}]]></mathphrase>)
281
- end
282
- if node.title?
283
- %(<equation#{common_attributes node.id, node.role, node.reftext}>
263
+ end
264
+ if node.title?
265
+ %(<equation#{common_attributes node.id, node.role, node.reftext}>
284
266
  <title>#{node.title}</title>
285
267
  #{equation_data}
286
268
  </equation>)
287
- else
288
- # WARNING dblatex displays the <informalequation> element inline instead of block as documented (except w/ mathml)
289
- %(<informalequation#{common_attributes node.id, node.role, node.reftext}>
269
+ else
270
+ # WARNING dblatex displays the <informalequation> element inline instead of block as documented (except w/ mathml)
271
+ %(<informalequation#{common_attributes node.id, node.role, node.reftext}>
290
272
  #{equation_data}
291
273
  </informalequation>)
292
- end
293
274
  end
275
+ end
294
276
 
295
- def olist node
296
- result = []
297
- num_attribute = node.style ? %( numeration="#{node.style}") : ''
298
- start_attribute = (node.attr? 'start') ? %( startingnumber="#{node.attr 'start'}") : ''
299
- result << %(<orderedlist#{common_attributes node.id, node.role, node.reftext}#{num_attribute}#{start_attribute}>)
300
- result << %(<title>#{node.title}</title>) if node.title?
301
- node.items.each do |item|
302
- result << '<listitem>'
303
- result << %(<simpara>#{item.text}</simpara>)
304
- result << item.content if item.blocks?
305
- result << '</listitem>'
306
- end
307
- result << %(</orderedlist>)
308
- result.join LF
309
- end
277
+ def convert_olist node
278
+ result = []
279
+ num_attribute = node.style ? %( numeration="#{node.style}") : ''
280
+ start_attribute = (node.attr? 'start') ? %( startingnumber="#{node.attr 'start'}") : ''
281
+ result << %(<orderedlist#{common_attributes node.id, node.role, node.reftext}#{num_attribute}#{start_attribute}>)
282
+ result << %(<title>#{node.title}</title>) if node.title?
283
+ node.items.each do |item|
284
+ result << %(<listitem#{common_attributes item.id, item.role}>)
285
+ result << %(<simpara>#{item.text}</simpara>)
286
+ result << item.content if item.blocks?
287
+ result << '</listitem>'
288
+ end
289
+ result << %(</orderedlist>)
290
+ result.join LF
291
+ end
310
292
 
311
- def open node
312
- case node.style
313
- when 'abstract'
314
- if node.parent == node.document && node.document.doctype == 'book'
315
- logger.warn 'abstract block cannot be used in a document without a title when doctype is book. Excluding block content.'
316
- ''
317
- else
318
- %(<abstract>
319
- #{title_tag node}#{resolve_content node}
293
+ def convert_open node
294
+ case node.style
295
+ when 'abstract'
296
+ if node.parent == node.document && node.document.doctype == 'book'
297
+ logger.warn 'abstract block cannot be used in a document without a title when doctype is book. Excluding block content.'
298
+ ''
299
+ else
300
+ %(<abstract>
301
+ #{title_tag node}#{enclose_content node}
320
302
  </abstract>)
321
- end
322
- when 'partintro'
323
- unless node.level == 0 && node.parent.context == :section && node.document.doctype == 'book'
324
- logger.error 'partintro block can only be used when doctype is book and must be a child of a book part. Excluding block content.'
325
- ''
326
- else
327
- %(<partintro#{common_attributes node.id, node.role, node.reftext}>
328
- #{title_tag node}#{resolve_content node}
303
+ end
304
+ when 'partintro'
305
+ if node.level == 0 && node.parent.context == :section && node.document.doctype == 'book'
306
+ %(<partintro#{common_attributes node.id, node.role, node.reftext}>
307
+ #{title_tag node}#{enclose_content node}
329
308
  </partintro>)
330
- end
331
309
  else
332
- reftext = node.reftext if (id = node.id)
333
- role = node.role
334
- if node.title?
335
- %(<formalpara#{common_attributes id, role, reftext}>
310
+ logger.error 'partintro block can only be used when doctype is book and must be a child of a book part. Excluding block content.'
311
+ ''
312
+ end
313
+ else
314
+ reftext = node.reftext if (id = node.id)
315
+ role = node.role
316
+ if node.title?
317
+ %(<formalpara#{common_attributes id, role, reftext}>
336
318
  <title>#{node.title}</title>
337
319
  <para>#{content_spacer = node.content_model == :compound ? LF : ''}#{node.content}#{content_spacer}</para>
338
320
  </formalpara>)
339
- elsif id || role
340
- if node.content_model == :compound
341
- %(<para#{common_attributes id, role, reftext}>
321
+ elsif id || role
322
+ if node.content_model == :compound
323
+ %(<para#{common_attributes id, role, reftext}>
342
324
  #{node.content}
343
325
  </para>)
344
- else
345
- %(<simpara#{common_attributes id, role, reftext}>#{node.content}</simpara>)
346
- end
347
326
  else
348
- resolve_content node
327
+ %(<simpara#{common_attributes id, role, reftext}>#{node.content}</simpara>)
349
328
  end
329
+ else
330
+ enclose_content node
350
331
  end
351
332
  end
333
+ end
352
334
 
353
- def page_break node
354
- '<simpara><?asciidoc-pagebreak?></simpara>'
355
- end
335
+ def convert_page_break node
336
+ '<simpara><?asciidoc-pagebreak?></simpara>'
337
+ end
356
338
 
357
- def paragraph node
358
- if node.title?
359
- %(<formalpara#{common_attributes node.id, node.role, node.reftext}>
339
+ def convert_paragraph node
340
+ if node.title?
341
+ %(<formalpara#{common_attributes node.id, node.role, node.reftext}>
360
342
  <title>#{node.title}</title>
361
343
  <para>#{node.content}</para>
362
344
  </formalpara>)
363
- else
364
- %(<simpara#{common_attributes node.id, node.role, node.reftext}>#{node.content}</simpara>)
365
- end
345
+ else
346
+ %(<simpara#{common_attributes node.id, node.role, node.reftext}>#{node.content}</simpara>)
366
347
  end
348
+ end
367
349
 
368
- def preamble node
369
- if node.document.doctype == 'book'
370
- %(<preface#{common_attributes node.id, node.role, node.reftext}>
350
+ def convert_preamble node
351
+ if node.document.doctype == 'book'
352
+ %(<preface#{common_attributes node.id, node.role, node.reftext}>
371
353
  #{title_tag node, false}#{node.content}
372
354
  </preface>)
373
- else
374
- node.content
375
- end
355
+ else
356
+ node.content
376
357
  end
358
+ end
377
359
 
378
- def quote node
379
- blockquote_tag(node, (node.has_role? 'epigraph') && 'epigraph') { resolve_content node }
380
- end
360
+ def convert_quote node
361
+ blockquote_tag(node, (node.has_role? 'epigraph') && 'epigraph') { enclose_content node }
362
+ end
381
363
 
382
- def thematic_break node
383
- '<simpara><?asciidoc-hr?></simpara>'
384
- end
364
+ def convert_thematic_break node
365
+ '<simpara><?asciidoc-hr?></simpara>'
366
+ end
385
367
 
386
- def sidebar node
387
- %(<sidebar#{common_attributes node.id, node.role, node.reftext}>
388
- #{title_tag node}#{resolve_content node}
368
+ def convert_sidebar node
369
+ %(<sidebar#{common_attributes node.id, node.role, node.reftext}>
370
+ #{title_tag node}#{enclose_content node}
389
371
  </sidebar>)
390
- end
391
-
392
- TABLE_PI_NAMES = ['dbhtml', 'dbfo', 'dblatex']
372
+ end
393
373
 
394
- def table node
395
- has_body = false
396
- result = []
397
- pgwide_attribute = (node.option? 'pgwide') ? ' pgwide="1"' : ''
398
- if (frame = node.attr 'frame', 'all') == 'ends'
399
- frame = 'topbot'
400
- end
401
- result << %(<#{tag_name = node.title? ? 'table' : 'informaltable'}#{common_attributes node.id, node.role, node.reftext}#{pgwide_attribute} frame="#{frame}" rowsep="#{['none', 'cols'].include?(node.attr 'grid') ? 0 : 1}" colsep="#{['none', 'rows'].include?(node.attr 'grid') ? 0 : 1}"#{(node.attr? 'orientation', 'landscape', false) ? ' orient="land"' : ''}>)
402
- if (node.option? 'unbreakable')
403
- result << '<?dbfo keep-together="always"?>'
404
- elsif (node.option? 'breakable')
405
- result << '<?dbfo keep-together="auto"?>'
406
- end
407
- result << %(<title>#{node.title}</title>) if tag_name == 'table'
408
- col_width_key = if (width = (node.attr? 'width') ? (node.attr 'width') : nil)
409
- TABLE_PI_NAMES.each do |pi_name|
410
- result << %(<?#{pi_name} table-width="#{width}"?>)
411
- end
412
- 'colabswidth'
413
- else
414
- 'colpcwidth'
415
- end
416
- result << %(<tgroup cols="#{node.attr 'colcount'}">)
417
- node.columns.each do |col|
418
- result << %(<colspec colname="col_#{col.attr 'colnumber'}" colwidth="#{col.attr col_width_key}*"/>)
419
- end
420
- node.rows.by_section.each do |tsec, rows|
421
- next if rows.empty?
422
- has_body = true if tsec == :body
423
- result << %(<t#{tsec}>)
424
- rows.each do |row|
425
- result << '<row>'
426
- row.each do |cell|
427
- halign_attribute = (cell.attr? 'halign') ? %( align="#{cell.attr 'halign'}") : ''
428
- valign_attribute = (cell.attr? 'valign') ? %( valign="#{cell.attr 'valign'}") : ''
429
- colspan_attribute = cell.colspan ? %( namest="col_#{colnum = cell.column.attr 'colnumber'}" nameend="col_#{colnum + cell.colspan - 1}") : ''
430
- rowspan_attribute = cell.rowspan ? %( morerows="#{cell.rowspan - 1}") : ''
431
- # NOTE <entry> may not have whitespace (e.g., line breaks) as a direct descendant according to DocBook rules
432
- entry_start = %(<entry#{halign_attribute}#{valign_attribute}#{colspan_attribute}#{rowspan_attribute}>)
433
- if tsec == :head
434
- cell_content = cell.text
374
+ def convert_table node
375
+ has_body = false
376
+ result = []
377
+ pgwide_attribute = (node.option? 'pgwide') ? ' pgwide="1"' : ''
378
+ frame = 'topbot' if (frame = node.attr 'frame', 'all', 'table-frame') == 'ends'
379
+ grid = node.attr 'grid', nil, 'table-grid'
380
+ result << %(<#{tag_name = node.title? ? 'table' : 'informaltable'}#{common_attributes node.id, node.role, node.reftext}#{pgwide_attribute} frame="#{frame}" rowsep="#{['none', 'cols'].include?(grid) ? 0 : 1}" colsep="#{['none', 'rows'].include?(grid) ? 0 : 1}"#{(node.attr? 'orientation', 'landscape', 'table-orientation') ? ' orient="land"' : ''}>)
381
+ if node.option? 'unbreakable'
382
+ result << '<?dbfo keep-together="always"?>'
383
+ elsif node.option? 'breakable'
384
+ result << '<?dbfo keep-together="auto"?>'
385
+ end
386
+ result << %(<title>#{node.title}</title>) if tag_name == 'table'
387
+ if (width = (node.attr? 'width') ? (node.attr 'width') : nil)
388
+ TABLE_PI_NAMES.each do |pi_name|
389
+ result << %(<?#{pi_name} table-width="#{width}"?>)
390
+ end
391
+ col_width_key = 'colabswidth'
392
+ else
393
+ col_width_key = 'colpcwidth'
394
+ end
395
+ result << %(<tgroup cols="#{node.attr 'colcount'}">)
396
+ node.columns.each do |col|
397
+ result << %(<colspec colname="col_#{col.attr 'colnumber'}" colwidth="#{col.attr col_width_key}*"/>)
398
+ end
399
+ node.rows.to_h.each do |tsec, rows|
400
+ next if rows.empty?
401
+ has_body = true if tsec == :body
402
+ result << %(<t#{tsec}>)
403
+ rows.each do |row|
404
+ result << '<row>'
405
+ row.each do |cell|
406
+ colspan_attribute = cell.colspan ? %( namest="col_#{colnum = cell.column.attr 'colnumber'}" nameend="col_#{colnum + cell.colspan - 1}") : ''
407
+ rowspan_attribute = cell.rowspan ? %( morerows="#{cell.rowspan - 1}") : ''
408
+ # NOTE <entry> may not have whitespace (e.g., line breaks) as a direct descendant according to DocBook rules
409
+ entry_start = %(<entry align="#{cell.attr 'halign'}" valign="#{cell.attr 'valign'}"#{colspan_attribute}#{rowspan_attribute}>)
410
+ if tsec == :head
411
+ cell_content = cell.text
412
+ else
413
+ case cell.style
414
+ when :asciidoc
415
+ cell_content = cell.content
416
+ when :literal
417
+ cell_content = %(<literallayout class="monospaced">#{cell.text}</literallayout>)
418
+ when :header
419
+ cell_content = (cell_content = cell.content).empty? ? '' : %(<simpara><emphasis role="strong">#{cell_content.join '</emphasis></simpara><simpara><emphasis role="strong">'}</emphasis></simpara>)
435
420
  else
436
- case cell.style
437
- when :asciidoc
438
- cell_content = cell.content
439
- when :verse
440
- cell_content = %(<literallayout>#{cell.text}</literallayout>)
441
- when :literal
442
- cell_content = %(<literallayout class="monospaced">#{cell.text}</literallayout>)
443
- when :header
444
- cell_content = (cell_content = cell.content).empty? ? '' : %(<simpara><emphasis role="strong">#{cell_content.join '</emphasis></simpara><simpara><emphasis role="strong">'}</emphasis></simpara>)
445
- else
446
- cell_content = (cell_content = cell.content).empty? ? '' : %(<simpara>#{cell_content.join '</simpara><simpara>'}</simpara>)
447
- end
421
+ cell_content = (cell_content = cell.content).empty? ? '' : %(<simpara>#{cell_content.join '</simpara><simpara>'}</simpara>)
448
422
  end
449
- entry_end = (node.document.attr? 'cellbgcolor') ? %(<?dbfo bgcolor="#{node.document.attr 'cellbgcolor'}"?></entry>) : '</entry>'
450
- result << %(#{entry_start}#{cell_content}#{entry_end})
451
423
  end
452
- result << '</row>'
424
+ entry_end = (node.document.attr? 'cellbgcolor') ? %(<?dbfo bgcolor="#{node.document.attr 'cellbgcolor'}"?></entry>) : '</entry>'
425
+ result << %(#{entry_start}#{cell_content}#{entry_end})
453
426
  end
454
- result << %(</t#{tsec}>)
427
+ result << '</row>'
455
428
  end
456
- result << '</tgroup>'
457
- result << %(</#{tag_name}>)
458
-
459
- logger.warn 'tables must have at least one body row' unless has_body
460
- result.join LF
429
+ result << %(</t#{tsec}>)
461
430
  end
431
+ result << '</tgroup>'
432
+ result << %(</#{tag_name}>)
462
433
 
463
- alias toc skip
434
+ logger.warn 'tables must have at least one body row' unless has_body
435
+ result.join LF
436
+ end
464
437
 
465
- def ulist node
466
- result = []
467
- if node.style == 'bibliography'
468
- result << %(<bibliodiv#{common_attributes node.id, node.role, node.reftext}>)
469
- result << %(<title>#{node.title}</title>) if node.title?
470
- node.items.each do |item|
471
- result << '<bibliomixed>'
472
- result << %(<bibliomisc>#{item.text}</bibliomisc>)
473
- result << item.content if item.blocks?
474
- result << '</bibliomixed>'
475
- end
476
- result << '</bibliodiv>'
477
- else
478
- mark_type = (checklist = node.option? 'checklist') ? 'none' : node.style
479
- mark_attribute = mark_type ? %( mark="#{mark_type}") : ''
480
- result << %(<itemizedlist#{common_attributes node.id, node.role, node.reftext}#{mark_attribute}>)
481
- result << %(<title>#{node.title}</title>) if node.title?
482
- node.items.each do |item|
483
- text_marker = if checklist && (item.attr? 'checkbox')
484
- (item.attr? 'checked') ? '&#10003; ' : '&#10063; '
485
- else
486
- ''
487
- end
488
- result << '<listitem>'
489
- result << %(<simpara>#{text_marker}#{item.text}</simpara>)
490
- result << item.content if item.blocks?
491
- result << '</listitem>'
492
- end
493
- result << '</itemizedlist>'
494
- end
438
+ alias convert_toc skip
495
439
 
496
- result.join LF
440
+ def convert_ulist node
441
+ result = []
442
+ if node.style == 'bibliography'
443
+ result << %(<bibliodiv#{common_attributes node.id, node.role, node.reftext}>)
444
+ result << %(<title>#{node.title}</title>) if node.title?
445
+ node.items.each do |item|
446
+ result << '<bibliomixed>'
447
+ result << %(<bibliomisc>#{item.text}</bibliomisc>)
448
+ result << item.content if item.blocks?
449
+ result << '</bibliomixed>'
450
+ end
451
+ result << '</bibliodiv>'
452
+ else
453
+ mark_type = (checklist = node.option? 'checklist') ? 'none' : node.style
454
+ mark_attribute = mark_type ? %( mark="#{mark_type}") : ''
455
+ result << %(<itemizedlist#{common_attributes node.id, node.role, node.reftext}#{mark_attribute}>)
456
+ result << %(<title>#{node.title}</title>) if node.title?
457
+ node.items.each do |item|
458
+ text_marker = (item.attr? 'checked') ? '&#10003; ' : '&#10063; ' if checklist && (item.attr? 'checkbox')
459
+ result << %(<listitem#{common_attributes item.id, item.role}>)
460
+ result << %(<simpara>#{text_marker || ''}#{item.text}</simpara>)
461
+ result << item.content if item.blocks?
462
+ result << '</listitem>'
463
+ end
464
+ result << '</itemizedlist>'
497
465
  end
466
+ result.join LF
467
+ end
498
468
 
499
- def verse node
500
- blockquote_tag(node, (node.has_role? 'epigraph') && 'epigraph') { %(<literallayout>#{node.content}</literallayout>) }
501
- end
469
+ def convert_verse node
470
+ blockquote_tag(node, (node.has_role? 'epigraph') && 'epigraph') { %(<literallayout>#{node.content}</literallayout>) }
471
+ end
502
472
 
503
- alias video skip
473
+ alias convert_video skip
504
474
 
505
- def inline_anchor node
506
- case node.type
507
- when :ref
508
- %(<anchor#{common_attributes((id = node.id), nil, node.reftext || %([#{id}]))}/>)
509
- when :xref
510
- if (path = node.attributes['path'])
511
- # QUESTION should we use refid as fallback text instead? (like the html5 backend?)
512
- %(<link xl:href="#{node.target}">#{node.text || path}</link>)
513
- else
514
- linkend = node.attributes['fragment'] || node.target
515
- (text = node.text) ? %(<link linkend="#{linkend}">#{text}</link>) : %(<xref linkend="#{linkend}"/>)
516
- end
517
- when :link
518
- %(<link xl:href="#{node.target}">#{node.text}</link>)
519
- when :bibref
520
- # NOTE technically node.text should be node.reftext, but subs have already been applied to text
521
- %(<anchor#{common_attributes node.id, nil, (text = node.text)}/>#{text})
475
+ def convert_inline_anchor node
476
+ case node.type
477
+ when :ref
478
+ %(<anchor#{common_attributes((id = node.id), nil, node.reftext || %([#{id}]))}/>)
479
+ when :xref
480
+ if (path = node.attributes['path'])
481
+ %(<link xl:href="#{node.target}">#{node.text || path}</link>)
522
482
  else
523
- logger.warn %(unknown anchor type: #{node.type.inspect})
524
- nil
525
- end
483
+ if (linkend = node.attributes['refid']).nil_or_empty?
484
+ root_doc = get_root_document node
485
+ # Q: should we warn instead of generating a document ID on demand?
486
+ linkend = (root_doc.id ||= generate_document_id root_doc)
487
+ end
488
+ # NOTE the xref tag in DocBook does not support explicit link text, so the link tag must be used instead
489
+ # The section at http://www.sagehill.net/docbookxsl/CrossRefs.html#IdrefLinks gives an explanation for this choice
490
+ # "link - a cross reference where you supply the text of the reference as the content of the link element."
491
+ (text = node.text) ? %(<link linkend="#{linkend}">#{text}</link>) : %(<xref linkend="#{linkend}"/>)
492
+ end
493
+ when :link
494
+ %(<link xl:href="#{node.target}">#{node.text}</link>)
495
+ when :bibref
496
+ %(<anchor#{common_attributes node.id, nil, (text = "[#{node.reftext || node.id}]")}/>#{text})
497
+ else
498
+ logger.warn %(unknown anchor type: #{node.type.inspect})
499
+ nil
526
500
  end
501
+ end
527
502
 
528
- def inline_break node
529
- %(#{node.text}<?asciidoc-br?>)
530
- end
503
+ def convert_inline_break node
504
+ %(#{node.text}<?asciidoc-br?>)
505
+ end
531
506
 
532
- def inline_button node
533
- %(<guibutton>#{node.text}</guibutton>)
534
- end
507
+ def convert_inline_button node
508
+ %(<guibutton>#{node.text}</guibutton>)
509
+ end
535
510
 
536
- def inline_callout node
537
- %(<co#{common_attributes node.id}/>)
538
- end
511
+ def convert_inline_callout node
512
+ %(<co#{common_attributes node.id}/>)
513
+ end
539
514
 
540
- def inline_footnote node
541
- if node.type == :xref
542
- %(<footnoteref linkend="#{node.target}"/>)
543
- else
544
- %(<footnote#{common_attributes node.id}><simpara>#{node.text}</simpara></footnote>)
545
- end
515
+ def convert_inline_footnote node
516
+ if node.type == :xref
517
+ %(<footnoteref linkend="#{node.target}"/>)
518
+ else
519
+ %(<footnote#{common_attributes node.id}><simpara>#{node.text}</simpara></footnote>)
546
520
  end
521
+ end
547
522
 
548
- def inline_image node
549
- width_attribute = (node.attr? 'width') ? %( contentwidth="#{node.attr 'width'}") : ''
550
- depth_attribute = (node.attr? 'height') ? %( contentdepth="#{node.attr 'height'}") : ''
551
- %(<inlinemediaobject>
523
+ def convert_inline_image node
524
+ width_attribute = (node.attr? 'width') ? %( contentwidth="#{node.attr 'width'}") : ''
525
+ depth_attribute = (node.attr? 'height') ? %( contentdepth="#{node.attr 'height'}") : ''
526
+ %(<inlinemediaobject#{common_attributes nil, node.role}>
552
527
  <imageobject>
553
528
  <imagedata fileref="#{node.type == 'icon' ? (node.icon_uri node.target) : (node.image_uri node.target)}"#{width_attribute}#{depth_attribute}/>
554
529
  </imageobject>
555
530
  <textobject><phrase>#{node.alt}</phrase></textobject>
556
531
  </inlinemediaobject>)
557
- end
532
+ end
558
533
 
559
- def inline_indexterm node
560
- if node.type == :visible
561
- %(<indexterm><primary>#{node.text}</primary></indexterm>#{node.text})
562
- else
563
- terms = node.attr 'terms'
564
- result = []
565
- if (numterms = terms.size) > 2
566
- result << %(<indexterm>
567
- <primary>#{terms[0]}</primary><secondary>#{terms[1]}</secondary><tertiary>#{terms[2]}</tertiary>
568
- </indexterm>)
569
- end
570
- if numterms > 1
571
- result << %(<indexterm>
572
- <primary>#{terms[-2]}</primary><secondary>#{terms[-1]}</secondary>
534
+ def convert_inline_indexterm node
535
+ if (see = node.attr 'see')
536
+ rel = %(\n<see>#{see}</see>)
537
+ elsif (see_also_list = node.attr 'see-also')
538
+ rel = see_also_list.map {|see_also| %(\n<seealso>#{see_also}</seealso>) }.join
539
+ else
540
+ rel = ''
541
+ end
542
+ if node.type == :visible
543
+ %(<indexterm>
544
+ <primary>#{node.text}</primary>#{rel}
545
+ </indexterm>#{node.text})
546
+ elsif (numterms = (terms = node.attr 'terms').size) > 2
547
+ %(<indexterm>
548
+ <primary>#{terms[0]}</primary><secondary>#{terms[1]}</secondary><tertiary>#{terms[2]}</tertiary>#{rel}
549
+ </indexterm>#{(node.document.option? 'indexterm-promotion') ? %[
550
+ <indexterm>
551
+ <primary>#{terms[1]}</primary><secondary>#{terms[2]}</secondary>
552
+ </indexterm>
553
+ <indexterm>
554
+ <primary>#{terms[2]}</primary>
555
+ </indexterm>] : ''})
556
+ elsif numterms > 1
557
+ %(<indexterm>
558
+ <primary>#{terms[0]}</primary><secondary>#{terms[1]}</secondary>#{rel}
559
+ </indexterm>#{(node.document.option? 'indexterm-promotion') ? %[
560
+ <indexterm>
561
+ <primary>#{terms[1]}</primary>
562
+ </indexterm>] : ''})
563
+ else
564
+ %(<indexterm>
565
+ <primary>#{terms[0]}</primary>#{rel}
573
566
  </indexterm>)
574
- end
575
- result << %(<indexterm>
576
- <primary>#{terms[-1]}</primary>
577
- </indexterm>)
578
- result.join LF
579
- end
580
567
  end
568
+ end
581
569
 
582
- def inline_kbd node
583
- if (keys = node.attr 'keys').size == 1
584
- %(<keycap>#{keys[0]}</keycap>)
585
- else
586
- %(<keycombo><keycap>#{keys.join '</keycap><keycap>'}</keycap></keycombo>)
587
- end
570
+ def convert_inline_kbd node
571
+ if (keys = node.attr 'keys').size == 1
572
+ %(<keycap>#{keys[0]}</keycap>)
573
+ else
574
+ %(<keycombo><keycap>#{keys.join '</keycap><keycap>'}</keycap></keycombo>)
588
575
  end
576
+ end
589
577
 
590
- def inline_menu node
591
- menu = node.attr 'menu'
592
- if (submenus = node.attr 'submenus').empty?
593
- if (menuitem = node.attr 'menuitem', nil, false)
594
- %(<menuchoice><guimenu>#{menu}</guimenu> <guimenuitem>#{menuitem}</guimenuitem></menuchoice>)
595
- else
596
- %(<guimenu>#{menu}</guimenu>)
597
- end
578
+ def convert_inline_menu node
579
+ menu = node.attr 'menu'
580
+ if (submenus = node.attr 'submenus').empty?
581
+ if (menuitem = node.attr 'menuitem')
582
+ %(<menuchoice><guimenu>#{menu}</guimenu> <guimenuitem>#{menuitem}</guimenuitem></menuchoice>)
598
583
  else
599
- %(<menuchoice><guimenu>#{menu}</guimenu> <guisubmenu>#{submenus.join '</guisubmenu> <guisubmenu>'}</guisubmenu> <guimenuitem>#{node.attr 'menuitem'}</guimenuitem></menuchoice>)
584
+ %(<guimenu>#{menu}</guimenu>)
600
585
  end
586
+ else
587
+ %(<menuchoice><guimenu>#{menu}</guimenu> <guisubmenu>#{submenus.join '</guisubmenu> <guisubmenu>'}</guisubmenu> <guimenuitem>#{node.attr 'menuitem'}</guimenuitem></menuchoice>)
601
588
  end
589
+ end
602
590
 
603
- (QUOTE_TAGS = {
604
- :monospaced => ['<literal>', '</literal>', false],
605
- :emphasis => ['<emphasis>', '</emphasis>', true],
606
- :strong => ['<emphasis role="strong">', '</emphasis>', true],
607
- :double => ['<quote>', '</quote>', true],
608
- :single => ['<quote>', '</quote>', true],
609
- :mark => ['<emphasis role="marked">', '</emphasis>', false],
610
- :superscript => ['<superscript>', '</superscript>', false],
611
- :subscript => ['<subscript>', '</subscript>', false]
612
- }).default = ['', '', true]
613
-
614
- def inline_quoted node
615
- if (type = node.type) == :asciimath
616
- if (@asciimath ||= ((defined? ::AsciiMath) || (Helpers.require_library 'asciimath', true, :warn)).nil? ? :unavailable : :loaded) == :loaded
617
- # NOTE fop requires jeuclid to process raw mathml
618
- %(<inlineequation>#{(::AsciiMath.parse node.text).to_mathml 'mml:', 'xmlns:mml' => 'http://www.w3.org/1998/Math/MathML'}</inlineequation>)
591
+ def convert_inline_quoted node
592
+ if (type = node.type) == :asciimath
593
+ # NOTE fop requires jeuclid to process mathml markup
594
+ asciimath_available? ? %(<inlineequation>#{(::AsciiMath.parse node.text).to_mathml 'mml:', 'xmlns:mml' => 'http://www.w3.org/1998/Math/MathML'}</inlineequation>) : %(<inlineequation><mathphrase><![CDATA[#{node.text}]]></mathphrase></inlineequation>)
595
+ elsif type == :latexmath
596
+ # unhandled math; pass source to alt and required mathphrase element; dblatex will process alt as LaTeX math
597
+ %(<inlineequation><alt><![CDATA[#{equation = node.text}]]></alt><mathphrase><![CDATA[#{equation}]]></mathphrase></inlineequation>)
598
+ else
599
+ open, close, supports_phrase = QUOTE_TAGS[type]
600
+ text = node.text
601
+ if node.role
602
+ if supports_phrase
603
+ quoted_text = %(#{open}<phrase role="#{node.role}">#{text}</phrase>#{close})
619
604
  else
620
- %(<inlineequation><mathphrase><![CDATA[#{node.text}]]></mathphrase></inlineequation>)
605
+ quoted_text = %(#{open.chop} role="#{node.role}">#{text}#{close})
621
606
  end
622
- elsif type == :latexmath
623
- # unhandled math; pass source to alt and required mathphrase element; dblatex will process alt as LaTeX math
624
- %(<inlineequation><alt><![CDATA[#{equation = node.text}]]></alt><mathphrase><![CDATA[#{equation}]]></mathphrase></inlineequation>)
625
607
  else
626
- open, close, supports_phrase = QUOTE_TAGS[type]
627
- text = node.text
628
- if node.role
629
- if supports_phrase
630
- quoted_text = %(#{open}<phrase role="#{node.role}">#{text}</phrase>#{close})
631
- else
632
- quoted_text = %(#{open.chop} role="#{node.role}">#{text}#{close})
633
- end
634
- else
635
- quoted_text = %(#{open}#{text}#{close})
636
- end
637
-
638
- node.id ? %(<anchor#{common_attributes node.id, nil, text}/>#{quoted_text}) : quoted_text
608
+ quoted_text = %(#{open}#{text}#{close})
639
609
  end
610
+
611
+ node.id ? %(<anchor#{common_attributes node.id, nil, text}/>#{quoted_text}) : quoted_text
640
612
  end
613
+ end
641
614
 
642
- def common_attributes id, role = nil, reftext = nil
643
- attrs = id ? %( xml:id="#{id}") : ''
644
- attrs = %(#{attrs} role="#{role}") if role
645
- if reftext
646
- if (reftext.include? '<') && ((reftext = reftext.gsub XmlSanitizeRx, '').include? ' ')
647
- reftext = (reftext.squeeze ' ').strip
648
- end
649
- reftext = (reftext.gsub '"', '&quot;') if reftext.include? '"'
650
- attrs = %(#{attrs} xreflabel="#{reftext}")
615
+ private
616
+
617
+ def common_attributes id, role = nil, reftext = nil
618
+ if id
619
+ attrs = %( xml:id="#{id}"#{role ? %[ role="#{role}"] : ''})
620
+ elsif role
621
+ attrs = %( role="#{role}")
622
+ else
623
+ attrs = ''
624
+ end
625
+ if reftext
626
+ if (reftext.include? '<') && ((reftext = reftext.gsub XmlSanitizeRx, '').include? ' ')
627
+ reftext = (reftext.squeeze ' ').strip
651
628
  end
629
+ reftext = reftext.gsub '"', '&quot;' if reftext.include? '"'
630
+ %(#{attrs} xreflabel="#{reftext}")
631
+ else
652
632
  attrs
653
633
  end
634
+ end
654
635
 
655
- def doctype_declaration root_tag_name
656
- nil
657
- end
636
+ def author_tag doc, author
637
+ result = []
638
+ result << '<author>'
639
+ result << '<personname>'
640
+ result << %(<firstname>#{doc.sub_replacements author.firstname}</firstname>) if author.firstname
641
+ result << %(<othername>#{doc.sub_replacements author.middlename}</othername>) if author.middlename
642
+ result << %(<surname>#{doc.sub_replacements author.lastname}</surname>) if author.lastname
643
+ result << '</personname>'
644
+ result << %(<email>#{author.email}</email>) if author.email
645
+ result << '</author>'
646
+ result.join LF
647
+ end
658
648
 
659
- def author_tag author
660
- result = []
661
- result << '<author>'
662
- result << '<personname>'
663
- result << %(<firstname>#{author.firstname}</firstname>) if author.firstname
664
- result << %(<othername>#{author.middlename}</othername>) if author.middlename
665
- result << %(<surname>#{author.lastname}</surname>) if author.lastname
666
- result << '</personname>'
667
- result << %(<email>#{author.email}</email>) if author.email
668
- result << '</author>'
669
- result.join LF
670
- end
671
-
672
- def document_info_tag doc, info_tag_prefix, use_info_tag_prefix = false
673
- info_tag_prefix = '' unless use_info_tag_prefix
674
- result = []
675
- result << %(<#{info_tag_prefix}info>)
676
- result << document_title_tags(doc.doctitle :partition => true, :use_fallback => true) unless doc.notitle
677
- if (date = (doc.attr? 'revdate') ? (doc.attr 'revdate') : ((doc.attr? 'reproducible') ? nil : (doc.attr 'docdate')))
678
- result << %(<date>#{date}</date>)
679
- end
680
- if doc.attr? 'copyright'
681
- CopyrightRx =~ (doc.attr 'copyright')
682
- result << '<copyright>'
683
- result << %(<holder>#{$1}</holder>)
684
- result << %(<year>#{$2}</year>) if $2
685
- result << '</copyright>'
649
+ def document_info_tag doc
650
+ result = ['<info>']
651
+ unless doc.notitle
652
+ if (title = doc.doctitle partition: true, use_fallback: true).subtitle?
653
+ result << %(<title>#{title.main}</title>
654
+ <subtitle>#{title.subtitle}</subtitle>)
655
+ else
656
+ result << %(<title>#{title}</title>)
686
657
  end
687
- if doc.has_header?
688
- unless (authors = doc.authors).empty?
689
- if authors.size > 1
690
- result << '<authorgroup>'
691
- authors.each {|author| result << (author_tag author) }
692
- result << '</authorgroup>'
693
- else
694
- result << author_tag(author = authors[0])
695
- result << %(<authorinitials>#{author.initials}</authorinitials>) if author.initials
696
- end
658
+ end
659
+ if (date = (doc.attr? 'revdate') ? (doc.attr 'revdate') : ((doc.attr? 'reproducible') ? nil : (doc.attr 'docdate')))
660
+ result << %(<date>#{date}</date>)
661
+ end
662
+ if doc.attr? 'copyright'
663
+ CopyrightRx =~ (doc.attr 'copyright')
664
+ result << '<copyright>'
665
+ result << %(<holder>#{$1}</holder>)
666
+ result << %(<year>#{$2}</year>) if $2
667
+ result << '</copyright>'
668
+ end
669
+ if doc.header?
670
+ unless (authors = doc.authors).empty?
671
+ if authors.size > 1
672
+ result << '<authorgroup>'
673
+ authors.each {|author| result << (author_tag doc, author) }
674
+ result << '</authorgroup>'
675
+ else
676
+ result << (author_tag doc, (author = authors[0]))
677
+ result << %(<authorinitials>#{author.initials}</authorinitials>) if author.initials
697
678
  end
698
- if (doc.attr? 'revdate') && ((doc.attr? 'revnumber') || (doc.attr? 'revremark'))
699
- result << %(<revhistory>
679
+ end
680
+ if (doc.attr? 'revdate') && ((doc.attr? 'revnumber') || (doc.attr? 'revremark'))
681
+ result << %(<revhistory>
700
682
  <revision>)
701
- result << %(<revnumber>#{doc.attr 'revnumber'}</revnumber>) if doc.attr? 'revnumber'
702
- result << %(<date>#{doc.attr 'revdate'}</date>) if doc.attr? 'revdate'
703
- result << %(<authorinitials>#{doc.attr 'authorinitials'}</authorinitials>) if doc.attr? 'authorinitials'
704
- result << %(<revremark>#{doc.attr 'revremark'}</revremark>) if doc.attr? 'revremark'
705
- result << %(</revision>
683
+ result << %(<revnumber>#{doc.attr 'revnumber'}</revnumber>) if doc.attr? 'revnumber'
684
+ result << %(<date>#{doc.attr 'revdate'}</date>) if doc.attr? 'revdate'
685
+ result << %(<authorinitials>#{doc.attr 'authorinitials'}</authorinitials>) if doc.attr? 'authorinitials'
686
+ result << %(<revremark>#{doc.attr 'revremark'}</revremark>) if doc.attr? 'revremark'
687
+ result << %(</revision>
706
688
  </revhistory>)
689
+ end
690
+ if (doc.attr? 'front-cover-image') || (doc.attr? 'back-cover-image')
691
+ if (back_cover_tag = cover_tag doc, 'back')
692
+ result << (cover_tag doc, 'front', true)
693
+ result << back_cover_tag
694
+ elsif (front_cover_tag = cover_tag doc, 'front')
695
+ result << front_cover_tag
707
696
  end
708
- unless use_info_tag_prefix
709
- if (doc.attr? 'front-cover-image') || (doc.attr? 'back-cover-image')
710
- if (back_cover_tag = cover_tag doc, 'back')
711
- result << (cover_tag doc, 'front', true)
712
- result << back_cover_tag
713
- elsif (front_cover_tag = cover_tag doc, 'front')
714
- result << front_cover_tag
715
- end
716
- end
717
- end
718
- unless (head_docinfo = doc.docinfo).empty?
719
- result << head_docinfo
720
- end
721
- result << %(<orgname>#{doc.attr 'orgname'}</orgname>) if doc.attr? 'orgname'
722
697
  end
723
- result << %(</#{info_tag_prefix}info>)
724
-
725
- if doc.doctype == 'manpage'
726
- result << '<refmeta>'
727
- result << %(<refentrytitle>#{doc.attr 'mantitle'}</refentrytitle>) if doc.attr? 'mantitle'
728
- result << %(<manvolnum>#{doc.attr 'manvolnum'}</manvolnum>) if doc.attr? 'manvolnum'
729
- result << %(<refmiscinfo class="source">#{doc.attr 'mansource', '&#160;'}</refmiscinfo>)
730
- result << %(<refmiscinfo class="manual">#{doc.attr 'manmanual', '&#160;'}</refmiscinfo>)
731
- result << '</refmeta>'
732
- result << '<refnamediv>'
733
- result += (doc.attr 'mannames').map {|n| %(<refname>#{n}</refname>) } if doc.attr? 'mannames'
734
- result << %(<refpurpose>#{doc.attr 'manpurpose'}</refpurpose>) if doc.attr? 'manpurpose'
735
- result << '</refnamediv>'
698
+ result << %(<orgname>#{doc.attr 'orgname'}</orgname>) if doc.attr? 'orgname'
699
+ unless (docinfo_content = doc.docinfo).empty?
700
+ result << docinfo_content
736
701
  end
737
-
738
- result.join LF
739
702
  end
703
+ result << '</info>'
740
704
 
741
- def document_ns_attributes doc
742
- ' xmlns="http://docbook.org/ns/docbook" xmlns:xl="http://www.w3.org/1999/xlink" version="5.0"'
705
+ if doc.doctype == 'manpage'
706
+ result << '<refmeta>'
707
+ result << %(<refentrytitle>#{doc.attr 'mantitle'}</refentrytitle>) if doc.attr? 'mantitle'
708
+ result << %(<manvolnum>#{doc.attr 'manvolnum'}</manvolnum>) if doc.attr? 'manvolnum'
709
+ result << %(<refmiscinfo class="source">#{doc.attr 'mansource', '&#160;'}</refmiscinfo>)
710
+ result << %(<refmiscinfo class="manual">#{doc.attr 'manmanual', '&#160;'}</refmiscinfo>)
711
+ result << '</refmeta>'
712
+ result << '<refnamediv>'
713
+ result += (doc.attr 'mannames').map {|n| %(<refname>#{n}</refname>) } if doc.attr? 'mannames'
714
+ result << %(<refpurpose>#{doc.attr 'manpurpose'}</refpurpose>) if doc.attr? 'manpurpose'
715
+ result << '</refnamediv>'
743
716
  end
744
717
 
745
- def lang_attribute_name
746
- 'xml:lang'
747
- end
718
+ result.join LF
719
+ end
748
720
 
749
- def document_title_tags title
750
- if title.subtitle?
751
- %(<title>#{title.main}</title>
752
- <subtitle>#{title.subtitle}</subtitle>)
753
- else
754
- %(<title>#{title}</title>)
755
- end
721
+ def get_root_document node
722
+ while (node = node.document).nested?
723
+ node = node.parent_document
756
724
  end
725
+ node
726
+ end
757
727
 
758
- # FIXME this should be handled through a template mechanism
759
- def resolve_content node
760
- node.content_model == :compound ? node.content : %(<simpara>#{node.content}</simpara>)
761
- end
728
+ def generate_document_id doc
729
+ %(__#{doc.doctype}-root__)
730
+ end
762
731
 
763
- def title_tag node, optional = true
764
- !optional || node.title? ? %(<title>#{node.title}</title>\n) : ''
765
- end
732
+ # FIXME this should be handled through a template mechanism
733
+ def enclose_content node
734
+ node.content_model == :compound ? node.content : %(<simpara>#{node.content}</simpara>)
735
+ end
766
736
 
767
- def cover_tag doc, face, use_placeholder = false
768
- if (cover_image = doc.attr %(#{face}-cover-image))
769
- width_attr = ''
770
- depth_attr = ''
771
- if (cover_image.include? ':') && ImageMacroRx =~ cover_image
772
- cover_image = doc.image_uri $1
773
- unless $2.empty?
774
- attrs = (AttributeList.new $2).parse ['alt', 'width', 'height']
775
- if attrs.key? 'scaledwidth'
776
- # NOTE scalefit="1" is the default in this case
777
- width_attr = %( width="#{attrs['scaledwidth']}")
778
- else
779
- width_attr = %( contentwidth="#{attrs['width']}") if attrs.key? 'width'
780
- depth_attr = %( contentdepth="#{attrs['height']}") if attrs.key? 'height'
781
- end
737
+ def title_tag node, optional = true
738
+ !optional || node.title? ? %(<title>#{node.title}</title>\n) : ''
739
+ end
740
+
741
+ def cover_tag doc, face, use_placeholder = false
742
+ if (cover_image = doc.attr %(#{face}-cover-image))
743
+ width_attr = ''
744
+ depth_attr = ''
745
+ if (cover_image.include? ':') && ImageMacroRx =~ cover_image
746
+ attrlist = $2
747
+ cover_image = doc.image_uri $1
748
+ if attrlist
749
+ attrs = (AttributeList.new attrlist).parse ['alt', 'width', 'height']
750
+ if attrs.key? 'scaledwidth'
751
+ # NOTE scalefit="1" is the default in this case
752
+ width_attr = %( width="#{attrs['scaledwidth']}")
753
+ else
754
+ width_attr = %( contentwidth="#{attrs['width']}") if attrs.key? 'width'
755
+ depth_attr = %( contentdepth="#{attrs['height']}") if attrs.key? 'height'
782
756
  end
783
757
  end
784
- %(<cover role="#{face}">
758
+ end
759
+ %(<cover role="#{face}">
785
760
  <mediaobject>
786
761
  <imageobject>
787
762
  <imagedata fileref="#{cover_image}"#{width_attr}#{depth_attr}/>
788
763
  </imageobject>
789
764
  </mediaobject>
790
765
  </cover>)
791
- elsif use_placeholder
792
- %(<cover role="#{face}"/>)
793
- end
766
+ elsif use_placeholder
767
+ %(<cover role="#{face}"/>)
794
768
  end
769
+ end
795
770
 
796
- def blockquote_tag node, tag_name = nil
797
- if tag_name
798
- start_tag, end_tag = %(<#{tag_name}), %(</#{tag_name}>)
799
- else
800
- start_tag, end_tag = '<blockquote', '</blockquote>'
801
- end
802
- result = [%(#{start_tag}#{common_attributes node.id, node.role, node.reftext}>)]
803
- result << %(<title>#{node.title}</title>) if node.title?
804
- if (node.attr? 'attribution') || (node.attr? 'citetitle')
805
- result << '<attribution>'
806
- result << (node.attr 'attribution') if node.attr? 'attribution'
807
- result << %(<citetitle>#{node.attr 'citetitle'}</citetitle>) if node.attr? 'citetitle'
808
- result << '</attribution>'
809
- end
810
- result << yield
811
- result << end_tag
812
- result.join LF
813
- end
771
+ def blockquote_tag node, tag_name = nil
772
+ if tag_name
773
+ start_tag, end_tag = %(<#{tag_name}), %(</#{tag_name}>)
774
+ else
775
+ start_tag, end_tag = '<blockquote', '</blockquote>'
776
+ end
777
+ result = [%(#{start_tag}#{common_attributes node.id, node.role, node.reftext}>)]
778
+ result << %(<title>#{node.title}</title>) if node.title?
779
+ if (node.attr? 'attribution') || (node.attr? 'citetitle')
780
+ result << '<attribution>'
781
+ result << (node.attr 'attribution') if node.attr? 'attribution'
782
+ result << %(<citetitle>#{node.attr 'citetitle'}</citetitle>) if node.attr? 'citetitle'
783
+ result << '</attribution>'
784
+ end
785
+ result << yield
786
+ result << end_tag
787
+ result.join LF
788
+ end
789
+
790
+ def asciimath_available?
791
+ (@asciimath_status ||= load_asciimath) == :loaded
814
792
  end
793
+
794
+ def load_asciimath
795
+ (defined? ::AsciiMath.parse) ? :loaded : (Helpers.require_library 'asciimath', true, :warn).nil? ? :unavailable : :loaded
796
+ end
797
+ end
815
798
  end