asciidoctor 2.0.6 → 2.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +159 -6
  3. data/LICENSE +2 -1
  4. data/README-de.adoc +5 -5
  5. data/README-fr.adoc +4 -4
  6. data/README-jp.adoc +248 -183
  7. data/README-zh_CN.adoc +6 -6
  8. data/README.adoc +17 -11
  9. data/asciidoctor.gemspec +8 -8
  10. data/data/locale/attributes-ar.adoc +4 -3
  11. data/data/locale/attributes-bg.adoc +4 -3
  12. data/data/locale/attributes-ca.adoc +6 -5
  13. data/data/locale/attributes-cs.adoc +4 -3
  14. data/data/locale/attributes-da.adoc +6 -5
  15. data/data/locale/attributes-de.adoc +4 -4
  16. data/data/locale/attributes-en.adoc +4 -4
  17. data/data/locale/attributes-es.adoc +6 -5
  18. data/data/locale/attributes-fa.adoc +4 -3
  19. data/data/locale/attributes-fi.adoc +4 -3
  20. data/data/locale/attributes-fr.adoc +6 -5
  21. data/data/locale/attributes-hu.adoc +4 -3
  22. data/data/locale/attributes-id.adoc +4 -3
  23. data/data/locale/attributes-it.adoc +4 -3
  24. data/data/locale/attributes-ja.adoc +4 -3
  25. data/data/locale/{attributes-kr.adoc → attributes-ko.adoc} +4 -3
  26. data/data/locale/attributes-nb.adoc +4 -3
  27. data/data/locale/attributes-nl.adoc +4 -3
  28. data/data/locale/attributes-nn.adoc +4 -3
  29. data/data/locale/attributes-pl.adoc +8 -7
  30. data/data/locale/attributes-pt.adoc +6 -5
  31. data/data/locale/attributes-pt_BR.adoc +6 -5
  32. data/data/locale/attributes-ro.adoc +4 -3
  33. data/data/locale/attributes-ru.adoc +6 -5
  34. data/data/locale/attributes-sr.adoc +4 -4
  35. data/data/locale/attributes-sr_Latn.adoc +4 -4
  36. data/data/locale/attributes-sv.adoc +4 -4
  37. data/data/locale/attributes-tr.adoc +4 -3
  38. data/data/locale/attributes-uk.adoc +6 -5
  39. data/data/locale/attributes-zh_CN.adoc +4 -3
  40. data/data/locale/attributes-zh_TW.adoc +4 -3
  41. data/data/stylesheets/asciidoctor-default.css +29 -26
  42. data/lib/asciidoctor.rb +94 -1098
  43. data/lib/asciidoctor/abstract_block.rb +19 -11
  44. data/lib/asciidoctor/abstract_node.rb +21 -15
  45. data/lib/asciidoctor/attribute_list.rb +59 -67
  46. data/lib/asciidoctor/cli/invoker.rb +2 -0
  47. data/lib/asciidoctor/cli/options.rb +8 -8
  48. data/lib/asciidoctor/convert.rb +198 -0
  49. data/lib/asciidoctor/converter.rb +14 -13
  50. data/lib/asciidoctor/converter/docbook5.rb +9 -25
  51. data/lib/asciidoctor/converter/html5.rb +65 -42
  52. data/lib/asciidoctor/converter/manpage.rb +13 -12
  53. data/lib/asciidoctor/converter/template.rb +6 -3
  54. data/lib/asciidoctor/document.rb +40 -48
  55. data/lib/asciidoctor/extensions.rb +3 -3
  56. data/lib/asciidoctor/helpers.rb +38 -39
  57. data/lib/asciidoctor/inline.rb +1 -1
  58. data/lib/asciidoctor/load.rb +117 -0
  59. data/lib/asciidoctor/parser.rb +29 -25
  60. data/lib/asciidoctor/path_resolver.rb +35 -25
  61. data/lib/asciidoctor/reader.rb +14 -7
  62. data/lib/asciidoctor/rx.rb +722 -0
  63. data/lib/asciidoctor/substitutors.rb +62 -40
  64. data/lib/asciidoctor/syntax_highlighter.rb +22 -8
  65. data/lib/asciidoctor/syntax_highlighter/coderay.rb +1 -1
  66. data/lib/asciidoctor/syntax_highlighter/highlightjs.rb +12 -4
  67. data/lib/asciidoctor/syntax_highlighter/prettify.rb +7 -4
  68. data/lib/asciidoctor/syntax_highlighter/pygments.rb +2 -3
  69. data/lib/asciidoctor/syntax_highlighter/rouge.rb +18 -11
  70. data/lib/asciidoctor/table.rb +49 -20
  71. data/lib/asciidoctor/version.rb +1 -1
  72. data/man/asciidoctor.1 +17 -17
  73. data/man/asciidoctor.adoc +15 -14
  74. metadata +12 -9
@@ -85,23 +85,24 @@ module Converter
85
85
  # Public: Derive backend traits (basebackend, filetype, outfilesuffix, htmlsyntax) from the given backend.
86
86
  #
87
87
  # backend - the String backend from which to derive the traits
88
+ # basebackend - the String basebackend to use in favor of deriving one from the backend (optional, default: nil)
88
89
  #
89
90
  # Returns the backend traits for the given backend as a [Hash].
90
- def self.derive_backend_traits backend
91
+ def self.derive_backend_traits backend, basebackend = nil
91
92
  return {} unless backend
92
- if (t_outfilesuffix = DEFAULT_EXTENSIONS[(t_basebackend = backend.sub TrailingDigitsRx, '')])
93
- t_filetype = t_outfilesuffix.slice 1, t_outfilesuffix.length
93
+ if (outfilesuffix = DEFAULT_EXTENSIONS[(basebackend ||= backend.sub TrailingDigitsRx, '')])
94
+ filetype = outfilesuffix.slice 1, outfilesuffix.length
94
95
  else
95
- t_outfilesuffix = %(.#{t_filetype = t_basebackend})
96
+ outfilesuffix = %(.#{filetype = basebackend})
96
97
  end
97
- t_filetype == 'html' ?
98
- { basebackend: t_basebackend, filetype: t_filetype, htmlsyntax: 'html', outfilesuffix: t_outfilesuffix } :
99
- { basebackend: t_basebackend, filetype: t_filetype, outfilesuffix: t_outfilesuffix }
98
+ filetype == 'html' ?
99
+ { basebackend: basebackend, filetype: filetype, htmlsyntax: 'html', outfilesuffix: outfilesuffix } :
100
+ { basebackend: basebackend, filetype: filetype, outfilesuffix: outfilesuffix }
100
101
  end
101
102
 
102
103
  module BackendTraits
103
104
  def basebackend value = nil
104
- value ? (backend_traits[:basebackend] = value) : backend_traits[:basebackend]
105
+ value ? ((backend_traits value)[:basebackend] = value) : backend_traits[:basebackend]
105
106
  end
106
107
 
107
108
  def filetype value = nil
@@ -128,15 +129,15 @@ module Converter
128
129
  @backend_traits = value || {}
129
130
  end
130
131
 
131
- def backend_traits
132
- @backend_traits ||= Converter.derive_backend_traits @backend
132
+ def backend_traits basebackend = nil
133
+ @backend_traits ||= Converter.derive_backend_traits @backend, basebackend
133
134
  end
134
135
 
135
136
  alias backend_info backend_traits
136
137
 
137
138
  # Deprecated: Use {Converter.derive_backend_traits} instead.
138
- def self.derive_backend_traits backend
139
- Converter.derive_backend_traits backend
139
+ def self.derive_backend_traits backend, basebackend = nil
140
+ Converter.derive_backend_traits backend, basebackend
140
141
  end
141
142
  end
142
143
 
@@ -148,7 +149,7 @@ module Converter
148
149
  #
149
150
  # Returns nothing.
150
151
  def register_for *backends
151
- Converter.register self, *backends
152
+ Converter.register self, *(backends.map {|backend| backend.to_s })
152
153
  end
153
154
  end
154
155
 
@@ -25,8 +25,8 @@ class Converter::DocBook5Converter < Converter::Base
25
25
  MANPAGE_SECTION_TAGS = { 'section' => 'refsection', 'synopsis' => 'refsynopsisdiv' }
26
26
  TABLE_PI_NAMES = ['dbhtml', 'dbfo', 'dblatex']
27
27
 
28
- CopyrightRx = /^(.+?)(?: ((?:\d{4}\-)?\d{4}))?$/
29
- ImageMacroRx = /^image::?(.+?)\[(.*?)\]$/
28
+ CopyrightRx = /^(#{CC_ANY}+?)(?: ((?:\d{4}\-)?\d{4}))?$/
29
+ ImageMacroRx = /^image::?(\S|\S#{CC_ANY}*?\S)\[(#{CC_ANY}+)?\]$/
30
30
 
31
31
  def initialize backend, opts = {}
32
32
  @backend = backend
@@ -35,20 +35,8 @@ class Converter::DocBook5Converter < Converter::Base
35
35
 
36
36
  def convert_document node
37
37
  result = ['<?xml version="1.0" encoding="UTF-8"?>']
38
- if node.attr? 'toc'
39
- if node.attr? 'toclevels'
40
- result << %(<?asciidoc-toc maxdepth="#{node.attr 'toclevels'}"?>)
41
- else
42
- result << '<?asciidoc-toc?>'
43
- end
44
- end
45
- if node.attr? 'sectnums'
46
- if node.attr? 'sectnumlevels'
47
- result << %(<?asciidoc-numbered maxdepth="#{node.attr 'sectnumlevels'}"?>)
48
- else
49
- result << '<?asciidoc-numbered?>'
50
- end
51
- end
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'
52
40
  lang_attribute = (node.attr? 'nolang') ? '' : %( xml:lang="#{node.attr 'lang', 'en'}")
53
41
  if (root_tag_name = node.doctype) == 'manpage'
54
42
  root_tag_name = 'refentry'
@@ -383,9 +371,7 @@ class Converter::DocBook5Converter < Converter::Base
383
371
  has_body = false
384
372
  result = []
385
373
  pgwide_attribute = (node.option? 'pgwide') ? ' pgwide="1"' : ''
386
- if (frame = node.attr 'frame', 'all', 'table-frame') == 'ends'
387
- frame = 'topbot'
388
- end
374
+ frame = 'topbot' if (frame = node.attr 'frame', 'all', 'table-frame') == 'ends'
389
375
  grid = node.attr 'grid', nil, 'table-grid'
390
376
  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"' : ''}>)
391
377
  if (node.option? 'unbreakable')
@@ -413,12 +399,10 @@ class Converter::DocBook5Converter < Converter::Base
413
399
  rows.each do |row|
414
400
  result << '<row>'
415
401
  row.each do |cell|
416
- halign_attribute = (cell.attr? 'halign') ? %( align="#{cell.attr 'halign'}") : ''
417
- valign_attribute = (cell.attr? 'valign') ? %( valign="#{cell.attr 'valign'}") : ''
418
402
  colspan_attribute = cell.colspan ? %( namest="col_#{colnum = cell.column.attr 'colnumber'}" nameend="col_#{colnum + cell.colspan - 1}") : ''
419
403
  rowspan_attribute = cell.rowspan ? %( morerows="#{cell.rowspan - 1}") : ''
420
404
  # NOTE <entry> may not have whitespace (e.g., line breaks) as a direct descendant according to DocBook rules
421
- entry_start = %(<entry#{halign_attribute}#{valign_attribute}#{colspan_attribute}#{rowspan_attribute}>)
405
+ entry_start = %(<entry align="#{cell.attr 'halign'}" valign="#{cell.attr 'valign'}"#{colspan_attribute}#{rowspan_attribute}>)
422
406
  if tsec == :head
423
407
  cell_content = cell.text
424
408
  else
@@ -499,7 +483,7 @@ class Converter::DocBook5Converter < Converter::Base
499
483
  when :link
500
484
  %(<link xl:href="#{node.target}">#{node.text}</link>)
501
485
  when :bibref
502
- %(<anchor#{common_attributes node.id, nil, "[#{node.reftext || node.id}]"}/>#{text})
486
+ %(<anchor#{common_attributes node.id, nil, (text = "[#{node.reftext || node.id}]")}/>#{text})
503
487
  else
504
488
  logger.warn %(unknown anchor type: #{node.type.inspect})
505
489
  nil
@@ -634,7 +618,7 @@ class Converter::DocBook5Converter < Converter::Base
634
618
  if (reftext.include? '<') && ((reftext = reftext.gsub XmlSanitizeRx, '').include? ' ')
635
619
  reftext = (reftext.squeeze ' ').strip
636
620
  end
637
- reftext = (reftext.gsub '"', '&quot;') if reftext.include? '"'
621
+ reftext = reftext.gsub '"', '&quot;' if reftext.include? '"'
638
622
  %(#{attrs} xreflabel="#{reftext}")
639
623
  else
640
624
  attrs
@@ -742,7 +726,7 @@ class Converter::DocBook5Converter < Converter::Base
742
726
  if (cover_image.include? ':') && ImageMacroRx =~ cover_image
743
727
  attrlist = $2
744
728
  cover_image = doc.image_uri $1
745
- unless attrlist.empty?
729
+ if attrlist
746
730
  attrs = (AttributeList.new attrlist).parse ['alt', 'width', 'height']
747
731
  if attrs.key? 'scaledwidth'
748
732
  # NOTE scalefit="1" is the default in this case
@@ -21,11 +21,17 @@ class Converter::Html5Converter < Converter::Base
21
21
  #latexmath: INLINE_MATH_DELIMITERS[:latexmath] + [false],
22
22
  }).default = ['', '']
23
23
 
24
- DropAnchorRx = /<(?:a[^>+]+|\/a)>/
24
+ DropAnchorRx = /<(?:a\b[^>]*|\/a)>/
25
25
  StemBreakRx = / *\\\n(?:\\?\n)*|\n\n+/
26
- SvgPreambleRx = /\A.*?(?=<svg\b)/m
27
- SvgStartTagRx = /\A<svg[^>]*>/
28
- DimensionAttributeRx = /\s(?:width|height|style)=(["']).*?\1/
26
+ if RUBY_ENGINE == 'opal'
27
+ # NOTE In JavaScript, ^ matches the start of the string when the m flag is not set
28
+ SvgPreambleRx = /^#{CC_ALL}*?(?=<svg[\s>])/
29
+ SvgStartTagRx = /^<svg(?:\s[^>]*)?>/
30
+ else
31
+ SvgPreambleRx = /\A.*?(?=<svg[\s>])/m
32
+ SvgStartTagRx = /\A<svg(?:\s[^>]*)?>/
33
+ end
34
+ DimensionAttributeRx = /\s(?:width|height|style)=(["'])#{CC_ANY}*?\1/
29
35
 
30
36
  def initialize backend, opts = {}
31
37
  @backend = backend
@@ -90,6 +96,7 @@ class Converter::Html5Converter < Converter::Base
90
96
  end
91
97
  cdn_base_url = %(#{asset_uri_scheme}//cdnjs.cloudflare.com/ajax/libs)
92
98
  linkcss = node.attr? 'linkcss'
99
+ max_width_attr = (node.attr? 'max-width') ? %( style="max-width: #{node.attr 'max-width'};") : ''
93
100
  result = ['<!DOCTYPE html>']
94
101
  lang_attribute = (node.attr? 'nolang') ? '' : %( lang="#{node.attr 'lang', 'en'}")
95
102
  result << %(<html#{@xml_mode ? ' xmlns="http://www.w3.org/1999/xhtml"' : ''}#{lang_attribute}>)
@@ -132,7 +139,7 @@ class Converter::Html5Converter < Converter::Base
132
139
  result << %(<link rel="stylesheet" href="#{node.normalize_web_path((node.attr 'stylesheet'), (node.attr 'stylesdir', ''))}"#{slash}>)
133
140
  else
134
141
  result << %(<style>
135
- #{node.read_asset node.normalize_system_path((node.attr 'stylesheet'), (node.attr 'stylesdir', '')), warn_on_failure: true, label: 'stylesheet'}
142
+ #{node.read_contents (node.attr 'stylesheet'), start: (node.attr 'stylesdir'), warn_on_failure: true, label: 'stylesheet'}
136
143
  </style>)
137
144
  end
138
145
  end
@@ -146,8 +153,8 @@ class Converter::Html5Converter < Converter::Base
146
153
  end
147
154
  end
148
155
 
149
- if (syntax_hl = node.syntax_highlighter) && (syntax_hl.docinfo? :head)
150
- result << (syntax_hl.docinfo :head, node, linkcss: linkcss)
156
+ if (syntax_hl = node.syntax_highlighter)
157
+ result << (syntax_hl_docinfo_head_idx = result.size)
151
158
  end
152
159
 
153
160
  unless (docinfo_content = node.docinfo).empty?
@@ -155,29 +162,27 @@ class Converter::Html5Converter < Converter::Base
155
162
  end
156
163
 
157
164
  result << '</head>'
158
- body_attrs = node.id ? [%(id="#{node.id}")] : []
165
+ id_attr = node.id ? %( id="#{node.id}") : ''
159
166
  if (sectioned = node.sections?) && (node.attr? 'toc-class') && (node.attr? 'toc') && (node.attr? 'toc-placement', 'auto')
160
167
  classes = [node.doctype, (node.attr 'toc-class'), %(toc-#{node.attr 'toc-position', 'header'})]
161
168
  else
162
169
  classes = [node.doctype]
163
170
  end
164
171
  classes << node.role if node.role?
165
- body_attrs << %(class="#{classes.join ' '}")
166
- body_attrs << %(style="max-width: #{node.attr 'max-width'};") if node.attr? 'max-width'
167
- result << %(<body #{body_attrs.join ' '}>)
172
+ result << %(<body#{id_attr} class="#{classes.join ' '}">)
168
173
 
169
174
  unless (docinfo_content = node.docinfo :header).empty?
170
175
  result << docinfo_content
171
176
  end
172
177
 
173
178
  unless node.noheader
174
- result << '<div id="header">'
179
+ result << %(<div id="header"#{max_width_attr}>)
175
180
  if node.doctype == 'manpage'
176
181
  result << %(<h1>#{node.doctitle} Manual Page</h1>)
177
182
  if sectioned && (node.attr? 'toc') && (node.attr? 'toc-placement', 'auto')
178
183
  result << %(<div id="toc" class="#{node.attr 'toc-class', 'toc'}">
179
184
  <div id="toctitle">#{node.attr 'toc-title'}</div>
180
- #{convert_outline node}
185
+ #{node.converter.convert node, 'outline'}
181
186
  </div>)
182
187
  end
183
188
  result << (generate_manname_section node) if node.attr? 'manpurpose'
@@ -210,19 +215,19 @@ class Converter::Html5Converter < Converter::Base
210
215
  if sectioned && (node.attr? 'toc') && (node.attr? 'toc-placement', 'auto')
211
216
  result << %(<div id="toc" class="#{node.attr 'toc-class', 'toc'}">
212
217
  <div id="toctitle">#{node.attr 'toc-title'}</div>
213
- #{convert_outline node}
218
+ #{node.converter.convert node, 'outline'}
214
219
  </div>)
215
220
  end
216
221
  end
217
222
  result << '</div>'
218
223
  end
219
224
 
220
- result << %(<div id="content">
225
+ result << %(<div id="content"#{max_width_attr}>
221
226
  #{node.content}
222
227
  </div>)
223
228
 
224
229
  if node.footnotes? && !(node.attr? 'nofootnotes')
225
- result << %(<div id="footnotes">
230
+ result << %(<div id="footnotes"#{max_width_attr}>
226
231
  <hr#{slash}>)
227
232
  node.footnotes.each do |footnote|
228
233
  result << %(<div class="footnote" id="_footnotedef_#{footnote.index}">
@@ -233,7 +238,7 @@ class Converter::Html5Converter < Converter::Base
233
238
  end
234
239
 
235
240
  unless node.nofooter
236
- result << '<div id="footer">'
241
+ result << %(<div id="footer"#{max_width_attr}>)
237
242
  result << '<div id="footer-text">'
238
243
  result << %(#{node.attr 'version-label'} #{node.attr 'revnumber'}#{br}) if node.attr? 'revnumber'
239
244
  result << %(#{node.attr 'last-update-label'} #{node.attr 'docdatetime'}) if (node.attr? 'last-update-label') && !(node.attr? 'reproducible')
@@ -244,8 +249,15 @@ class Converter::Html5Converter < Converter::Base
244
249
  # JavaScript (and auxiliary stylesheets) loaded at the end of body for performance reasons
245
250
  # See http://www.html5rocks.com/en/tutorials/speed/script-loading/
246
251
 
247
- if syntax_hl && (syntax_hl.docinfo? :footer)
248
- result << (syntax_hl.docinfo :footer, node, cdn_base_url: cdn_base_url, linkcss: linkcss, self_closing_tag_slash: slash)
252
+ if syntax_hl
253
+ if syntax_hl.docinfo? :head
254
+ result[syntax_hl_docinfo_head_idx] = syntax_hl.docinfo :head, node, cdn_base_url: cdn_base_url, linkcss: linkcss, self_closing_tag_slash: slash
255
+ else
256
+ result.delete_at syntax_hl_docinfo_head_idx
257
+ end
258
+ if syntax_hl.docinfo? :footer
259
+ result << (syntax_hl.docinfo :footer, node, cdn_base_url: cdn_base_url, linkcss: linkcss, self_closing_tag_slash: slash)
260
+ end
249
261
  end
250
262
 
251
263
  if node.attr? 'stem'
@@ -269,7 +281,7 @@ MathJax.Hub.Config({
269
281
  })
270
282
  MathJax.Hub.Register.StartupHook("AsciiMath Jax Ready", function () {
271
283
  MathJax.InputJax.AsciiMath.postfilterHooks.Add(function (data, node) {
272
- if ((node = data.script.parentNode) && (node = node.parentNode) && node.classList.contains('stemblock')) {
284
+ if ((node = data.script.parentNode) && (node = node.parentNode) && node.classList.contains("stemblock")) {
273
285
  data.math.root.display = "block"
274
286
  }
275
287
  return data
@@ -305,7 +317,7 @@ MathJax.Hub.Register.StartupHook("AsciiMath Jax Ready", function () {
305
317
  if node.sections? && (node.attr? 'toc') && (toc_p = node.attr 'toc-placement') != 'macro' && toc_p != 'preamble'
306
318
  result << %(<div id="toc" class="toc">
307
319
  <div id="toctitle">#{node.attr 'toc-title'}</div>
308
- #{convert_outline node}
320
+ #{node.converter.convert node, 'outline'}
309
321
  </div>)
310
322
  end
311
323
 
@@ -622,9 +634,7 @@ Your browser does not support the audio tag.
622
634
  end
623
635
  end
624
636
  img ||= %(<img src="#{node.image_uri target}" alt="#{encode_attribute_value node.alt}"#{width_attr}#{height_attr}#{@void_element_slash}>)
625
- if node.attr? 'link'
626
- img = %(<a class="image" href="#{node.attr 'link'}"#{(append_link_constraint_attrs node).join}>#{img}</a>)
627
- end
637
+ img = %(<a class="image" href="#{node.attr 'link'}"#{(append_link_constraint_attrs node).join}>#{img}</a>) if node.attr? 'link'
628
638
  id_attr = node.id ? %( id="#{node.id}") : ''
629
639
  classes = ['imageblock']
630
640
  classes << (node.attr 'float') if node.attr? 'float'
@@ -683,8 +693,8 @@ Your browser does not support the audio tag.
683
693
  open, close = BLOCK_MATH_DELIMITERS[style = node.style.to_sym]
684
694
  if (equation = node.content)
685
695
  if style == :asciimath && (equation.include? LF)
686
- br = %(<br#{@void_element_slash}>#{LF})
687
- equation = equation.gsub(StemBreakRx) { %(#{close}#{br * ($&.count LF)}#{open}) }
696
+ br = %(#{LF}<br#{@void_element_slash}>)
697
+ equation = equation.gsub(StemBreakRx) { %(#{close}#{br * (($&.count LF) - 1)}#{LF}#{open}) }
688
698
  end
689
699
  unless (equation.start_with? open) && (equation.end_with? close)
690
700
  equation = %(#{open}#{equation}#{close})
@@ -790,7 +800,7 @@ Your browser does not support the audio tag.
790
800
  toc = %(
791
801
  <div id="toc" class="#{doc.attr 'toc-class', 'toc'}">
792
802
  <div id="toctitle">#{doc.attr 'toc-title'}</div>
793
- #{convert_outline doc}
803
+ #{doc.converter.convert doc, 'outline'}
794
804
  </div>)
795
805
  else
796
806
  toc = ''
@@ -842,7 +852,8 @@ Your browser does not support the audio tag.
842
852
  def convert_table node
843
853
  result = []
844
854
  id_attribute = node.id ? %( id="#{node.id}") : ''
845
- classes = ['tableblock', %(frame-#{node.attr 'frame', 'all', 'table-frame'}), %(grid-#{node.attr 'grid', 'all', 'table-grid'})]
855
+ frame = 'ends' if (frame = node.attr 'frame', 'all', 'table-frame') == 'topbot'
856
+ classes = ['tableblock', %(frame-#{frame}), %(grid-#{node.attr 'grid', 'all', 'table-grid'})]
846
857
  if (stripes = node.attr 'stripes', nil, 'table-stripes')
847
858
  classes << %(stripes-#{stripes})
848
859
  end
@@ -928,7 +939,7 @@ Your browser does not support the audio tag.
928
939
 
929
940
  %(<div#{id_attr} class="#{role}">
930
941
  <div#{title_id_attr} class="title">#{title}</div>
931
- #{convert_outline doc, toclevels: levels}
942
+ #{doc.converter.convert doc, 'outline', toclevels: levels}
932
943
  </div>)
933
944
  end
934
945
 
@@ -1083,7 +1094,7 @@ Your browser does not support the audio tag.
1083
1094
  time_anchor = (start_t || end_t) ? %(#t=#{start_t || ''}#{end_t ? ",#{end_t}" : ''}) : ''
1084
1095
  %(<div#{id_attribute}#{class_attribute}>#{title_element}
1085
1096
  <div class="content">
1086
- <video src="#{node.media_uri(node.attr 'target')}#{time_anchor}"#{width_attribute}#{height_attribute}#{poster_attribute}#{(node.option? 'autoplay') ? (append_boolean_attribute 'autoplay', xml) : ''}#{(node.option? 'nocontrols') ? '' : (append_boolean_attribute 'controls', xml)}#{(node.option? 'loop') ? (append_boolean_attribute 'loop', xml) : ''}#{preload_attribute}>
1097
+ <video src="#{node.media_uri(node.attr 'target')}#{time_anchor}"#{width_attribute}#{height_attribute}#{poster_attribute}#{(node.option? 'autoplay') ? (append_boolean_attribute 'autoplay', xml) : ''}#{(node.option? 'muted') ? (append_boolean_attribute 'muted', xml) : ''}#{(node.option? 'nocontrols') ? '' : (append_boolean_attribute 'controls', xml)}#{(node.option? 'loop') ? (append_boolean_attribute 'loop', xml) : ''}#{preload_attribute}>
1087
1098
  Your browser does not support the video tag.
1088
1099
  </video>
1089
1100
  </div>
@@ -1101,8 +1112,13 @@ Your browser does not support the video tag.
1101
1112
  attrs = node.role ? %( class="#{node.role}") : ''
1102
1113
  unless (text = node.text)
1103
1114
  refid = node.attributes['refid']
1104
- if AbstractNode === (ref = (@refs ||= node.document.catalog[:refs])[refid])
1105
- text = (ref.xreftext node.attr('xrefstyle', nil, true)) || %([#{refid}])
1115
+ if AbstractNode === (ref = (@refs ||= node.document.catalog[:refs])[refid]) && (@resolving_xref ||= (outer = true)) && outer
1116
+ if !(text = ref.xreftext node.attr 'xrefstyle', nil, true)
1117
+ text = %([#{refid}])
1118
+ elsif text.include? '<a'
1119
+ text = text.gsub DropAnchorRx, ''
1120
+ end
1121
+ @resolving_xref = nil
1106
1122
  else
1107
1123
  text = %([#{refid}])
1108
1124
  end
@@ -1138,8 +1154,10 @@ Your browser does not support the video tag.
1138
1154
  elsif node.document.attr? 'icons'
1139
1155
  src = node.icon_uri("callouts/#{node.text}")
1140
1156
  %(<img src="#{src}" alt="#{node.text}"#{@void_element_slash}>)
1157
+ elsif ::Array === (guard = node.attributes['guard'])
1158
+ %(&lt;!--<b class="conum">(#{node.text})</b>--&gt;)
1141
1159
  else
1142
- %(#{node.attributes['guard']}<b class="conum">(#{node.text})</b>)
1160
+ %(#{guard}<b class="conum">(#{node.text})</b>)
1143
1161
  end
1144
1162
  end
1145
1163
 
@@ -1157,7 +1175,7 @@ Your browser does not support the video tag.
1157
1175
  end
1158
1176
 
1159
1177
  def convert_inline_image node
1160
- if (type = node.type) == 'icon' && (node.document.attr? 'icons', 'font')
1178
+ if (type = node.type || 'image') == 'icon' && (node.document.attr? 'icons', 'font')
1161
1179
  class_attr_val = %(fa fa-#{node.target})
1162
1180
  { 'size' => 'fa-', 'rotate' => 'fa-rotate-', 'flip' => 'fa-flip-' }.each do |key, prefix|
1163
1181
  class_attr_val = %(#{class_attr_val} #{prefix}#{node.attr key}) if node.attr? key
@@ -1180,9 +1198,7 @@ Your browser does not support the video tag.
1180
1198
  end
1181
1199
  img ||= %(<img src="#{type == 'icon' ? (node.icon_uri target) : (node.image_uri target)}" alt="#{encode_attribute_value node.alt}"#{attrs}#{@void_element_slash}>)
1182
1200
  end
1183
- if node.attr? 'link'
1184
- img = %(<a class="image" href="#{node.attr 'link'}"#{(append_link_constraint_attrs node).join}>#{img}</a>)
1185
- end
1201
+ img = %(<a class="image" href="#{node.attr 'link'}"#{(append_link_constraint_attrs node).join}>#{img}</a>) if node.attr? 'link'
1186
1202
  if (role = node.role)
1187
1203
  if node.attr? 'float'
1188
1204
  class_attr_val = %(#{type} #{node.attr 'float'} #{role})
@@ -1246,15 +1262,22 @@ Your browser does not support the video tag.
1246
1262
 
1247
1263
  # NOTE expose read_svg_contents for Bespoke converter
1248
1264
  def read_svg_contents node, target
1249
- if (svg = node.read_contents target, start: (node.document.attr 'imagesdir'), normalize: true, label: 'SVG')
1265
+ if (svg = node.read_contents target, start: (node.document.attr 'imagesdir'), normalize: true, label: 'SVG', warn_if_empty: true)
1266
+ return if svg.empty?
1250
1267
  svg = svg.sub SvgPreambleRx, '' unless svg.start_with? '<svg'
1251
- old_start_tag = new_start_tag = nil
1268
+ old_start_tag = new_start_tag = start_tag_match = nil
1252
1269
  # NOTE width, height and style attributes are removed if either width or height is specified
1253
1270
  ['width', 'height'].each do |dim|
1254
1271
  if node.attr? dim
1255
- new_start_tag = (old_start_tag = (svg.match SvgStartTagRx)[0]).gsub DimensionAttributeRx, '' unless new_start_tag
1256
- # QUESTION should we add px since it's already the default?
1257
- new_start_tag = %(#{new_start_tag.chop} #{dim}="#{node.attr dim}px">)
1272
+ unless new_start_tag
1273
+ next if (start_tag_match ||= (svg.match SvgStartTagRx) || :no_match) == :no_match
1274
+ new_start_tag = (old_start_tag = start_tag_match[0]).gsub DimensionAttributeRx, ''
1275
+ end
1276
+ unless (dim_val = node.attr dim).end_with? '%'
1277
+ # QUESTION should we add px since it's already the default?
1278
+ dim_val += 'px'
1279
+ end
1280
+ new_start_tag = %(#{new_start_tag.chop} #{dim}="#{dim_val}">)
1258
1281
  end
1259
1282
  end
1260
1283
  svg = %(#{new_start_tag}#{svg[old_start_tag.length..-1]}) if new_start_tag
@@ -15,9 +15,9 @@ class Converter::ManPageConverter < Converter::Base
15
15
  ESC_BS = %(#{ESC}\\) # escaped backslash (indicates troff formatting sequence)
16
16
  ESC_FS = %(#{ESC}.) # escaped full stop (indicates troff macro)
17
17
 
18
- LiteralBackslashRx = /(?:\A|[^#{ESC}])\\/
18
+ LiteralBackslashRx = /\A\\|(#{ESC})?\\/
19
19
  LeadingPeriodRx = /^\./
20
- EscapedMacroRx = /^(?:#{ESC}\\c\n)?#{ESC}\.((?:URL|MTO) ".*?" ".*?" )( |[^\s]*)(.*?)(?: *#{ESC}\\c)?$/
20
+ EscapedMacroRx = /^(?:#{ESC}\\c\n)?#{ESC}\.((?:URL|MTO) "#{CC_ANY}*?" "#{CC_ANY}*?" )( |[^\s]*)(#{CC_ANY}*?)(?: *#{ESC}\\c)?$/
21
21
  MockBoundaryRx = /<\/?BOUNDARY>/
22
22
  EmDashCharRefRx = /&#8212;(?:&#8203;)?/
23
23
  EllipsisCharRefRx = /&#8230;(?:&#8203;)?/
@@ -247,7 +247,9 @@ r lw(\n(.lu*75u/100u).'
247
247
  result << %(.sp
248
248
  .if n .RS 4
249
249
  .nf
250
+ .fam C
250
251
  #{manify node.content, whitespace: :preserve}
252
+ .fam
251
253
  .fi
252
254
  .if n .RE)
253
255
  result.join LF
@@ -261,7 +263,9 @@ r lw(\n(.lu*75u/100u).'
261
263
  result << %(.sp
262
264
  .if n .RS 4
263
265
  .nf
266
+ .fam C
264
267
  #{manify node.content, whitespace: :preserve}
268
+ .fam
265
269
  .fi
266
270
  .if n .RE)
267
271
  result.join LF
@@ -284,15 +288,16 @@ r lw(\n(.lu*75u/100u).'
284
288
  .B #{manify node.title}
285
289
  .br) if node.title?
286
290
 
291
+ start = (node.attr 'start', 1).to_i
287
292
  node.items.each_with_index do |item, idx|
288
293
  result << %(.sp
289
294
  .RS 4
290
295
  .ie n \\{\\
291
- \\h'-04' #{idx + 1}.\\h'+01'\\c
296
+ \\h'-04' #{numeral = idx + start}.\\h'+01'\\c
292
297
  .\\}
293
298
  .el \\{\\
294
299
  . sp -1
295
- . IP " #{idx + 1}." 4.2
300
+ . IP " #{numeral}." 4.2
296
301
  .\\}
297
302
  #{manify item.text, whitespace: :normalize})
298
303
  result << item.content if item.blocks?
@@ -580,11 +585,7 @@ allbox tab(:);'
580
585
  when :xref
581
586
  unless (text = node.text)
582
587
  refid = node.attributes['refid']
583
- if AbstractNode === (ref = (@refs ||= node.document.catalog[:refs])[refid])
584
- text = (ref.xreftext node.attr('xrefstyle', nil, true)) || %([#{refid}])
585
- else
586
- text = %([#{refid}])
587
- end
588
+ text = %([#{refid}]) unless AbstractNode === (ref = (@refs ||= node.document.catalog[:refs])[refid]) && (@resolving_xref ||= outer = true) && outer && (text = ref.xreftext node.attr 'xrefstyle', nil, true)
588
589
  end
589
590
  text
590
591
  when :ref, :bibref
@@ -699,7 +700,8 @@ allbox tab(:);'
699
700
  str = str.tr_s WHITESPACE, ' '
700
701
  end
701
702
  str = str.
702
- gsub(LiteralBackslashRx, '\&(rs'). # literal backslash (not a troff escape sequence)
703
+ gsub(LiteralBackslashRx) { $1 ? $& : '\\(rs' }. # literal backslash (not a troff escape sequence)
704
+ gsub(EllipsisCharRefRx, '...'). # horizontal ellipsis
703
705
  gsub(LeadingPeriodRx, '\\\&.'). # leading . is used in troff for macro call or other formatting; replace with \&.
704
706
  # drop orphaned \c escape lines, unescape troff macro, quote adjacent character, isolate macro line
705
707
  gsub(EscapedMacroRx) { (rest = $3.lstrip).empty? ? %(.#$1"#$2") : %(.#$1"#$2"#{LF}#{rest}) }.
@@ -717,13 +719,12 @@ allbox tab(:);'
717
719
  gsub('&#8217;', '\(cq'). # right single quotation mark
718
720
  gsub('&#8220;', '\(lq'). # left double quotation mark
719
721
  gsub('&#8221;', '\(rq'). # right double quotation mark
720
- gsub(EllipsisCharRefRx, '...'). # horizontal ellipsis
721
722
  gsub('&#8592;', '\(<-'). # leftwards arrow
722
723
  gsub('&#8594;', '\(->'). # rightwards arrow
723
724
  gsub('&#8656;', '\(lA'). # leftwards double arrow
724
725
  gsub('&#8658;', '\(rA'). # rightwards double arrow
725
726
  gsub('&#8203;', '\:'). # zero width space
726
- gsub('&amp;','&'). # literal ampersand (NOTE must take place after any other replacement that includes &)
727
+ gsub('&amp;', '&'). # literal ampersand (NOTE must take place after any other replacement that includes &)
727
728
  gsub('\'', '\(aq'). # apostrophe-quote
728
729
  gsub(MockBoundaryRx, ''). # mock boundary
729
730
  gsub(ESC_BS, '\\'). # unescape troff backslash (NOTE update if more escapes are added)