lib/asciidoctor-epub3/converter.rb in asciidoctor-epub3-1.5.0.alpha.7 vs lib/asciidoctor-epub3/converter.rb in asciidoctor-epub3-1.5.0.alpha.8
- old
+ new
@@ -1,6 +1,6 @@
-# encoding: UTF-8
+# encoding: utf-8
require_relative 'spine_item_processor'
require_relative 'font_icon_map'
module Asciidoctor
module Epub3
@@ -56,12 +56,10 @@
RightAngleQuote = '›'
CalloutStartNum = %(\u2460)
XmlElementRx = /<\/?.+?>/
CharEntityRx = /&#(\d{2,6});/
- NamedEntityRx = /&([A-Z]+);/
- UppercaseTagRx = /<(\/)?([A-Z]+)>/
FromHtmlSpecialCharsMap = {
'<' => '<',
'>' => '>',
'&' => '&'
@@ -98,26 +96,21 @@
end
def document node
docid = node.id
- if (doctitle = node.doctitle partition: true, sanitize: true, use_fallback: true).subtitle?
- title = doctitle.main
- title_upper = title.upcase
+ if (doctitle = node.doctitle partition: true, use_fallback: true).subtitle?
+ title = %(#{doctitle.main} )
subtitle = doctitle.subtitle
else
# HACK until we get proper handling of title-only in CSS
- title = title_upper = ''
+ title = ''
subtitle = doctitle.combined
end
- doctitle_sanitized = doctitle.combined
+ doctitle_sanitized = (node.doctitle sanitize: true, use_fallback: true).to_s
subtitle_formatted = subtitle.split.map {|w| %(<b>#{w}</b>) } * ' '
- # FIXME use uppercase pcdata helper to make less fragile (see logic in Asciidoctor PDF)
- subtitle_formatted_upper = subtitle_formatted.upcase
- .gsub(UppercaseTagRx) { %(<#{$1}#{$2.downcase}>) }
- .gsub(NamedEntityRx) { %(&#{$1.downcase};) }
if (node.attr 'publication-type', 'book') == 'book'
byline = nil
else
author = node.attr 'author'
@@ -169,11 +162,11 @@
</head>
<body>
<section class="chapter" title="#{doctitle_sanitized.gsub '"', '"'}" epub:type="chapter" id="#{docid}">
#{icon_css_scoped}<header>
<div class="chapter-header">
-#{byline}<h1 class="chapter-title">#{title_upper}#{subtitle ? %[ <small class="subtitle">#{subtitle_formatted_upper}</small>] : nil}</h1>
+#{byline}<h1 class="chapter-title">#{title}#{subtitle ? %[<small class="subtitle">#{subtitle_formatted}</small>] : ''}</h1>
</div>
</header>
#{content})]
if node.footnotes?
@@ -272,10 +265,11 @@
content
end
end
def admonition node
+ id_attr = node.id ? %( id="#{node.id}") : nil
if node.title?
title = node.title
title_sanitized = xml_sanitize title
title_attr = %( title="#{node.caption}: #{title_sanitized}")
title_el = %(<h2>#{title}</h2>
@@ -292,21 +286,22 @@
when 'note'
'note'
when 'important', 'warning', 'caution'
'warning'
end
- %(<aside class="admonition #{type}"#{title_attr} epub:type="#{epub_type}">
+ %(<aside#{id_attr} class="admonition #{type}"#{title_attr} epub:type="#{epub_type}">
#{title_el}<div class="content">
#{convert_content node}
</div>
</aside>)
end
def example node
+ id_attr = node.id ? %( id="#{node.id}") : nil
title_div = node.title? ? %(<div class="example-title">#{node.title}</div>
) : nil
- %(<div class="example">
+ %(<div#{id_attr} class="example">
#{title_div}<div class="example-content">
#{convert_content node}
</div>
</div>)
end
@@ -342,10 +337,13 @@
def thematic_break node
'<hr class="thematicbreak"/>'
end
def quote node
+ id_attr = %( id="#{node.id}") if node.id
+ class_attr = (role = node.role) ? %( class="blockquote #{role}") : ' class="blockquote"'
+
footer_content = []
if (attribution = node.attr 'attribution')
footer_content << attribution
end
@@ -361,18 +359,21 @@
footer_tag = footer_content.empty? ? nil : %(
<footer>~ #{footer_content * ' '}</footer>)
content = (convert_content node).strip.
sub(OpenParagraphTagRx, '<p><span class="open-quote">“</span>').
sub(CloseParagraphTagRx, '<span class="close-quote">”</span></p>')
- %(<div class="blockquote">
+ %(<div#{id_attr}#{class_attr}>
<blockquote>
#{content}#{footer_tag}
</blockquote>
</div>)
end
def verse node
+ id_attr = %( id="#{node.id}") if node.id
+ class_attr = (role = node.role) ? %( class="verse #{role}") : ' class="verse"'
+
footer_content = []
if (attribution = node.attr 'attribution')
footer_content << attribution
end
@@ -381,11 +382,11 @@
footer_content << %(<cite title="#{citetitle_sanitized}">#{citetitle}</cite>)
end
footer_tag = footer_content.size > 0 ? %(
<span class="attribution">~ #{footer_content * ', '}</span>) : nil
- %(<div class="verse">
+ %(<div#{id_attr}#{class_attr}>
<pre>#{node.content}#{footer_tag}</pre>
</div>)
end
def sidebar node
@@ -393,13 +394,11 @@
if node.title?
classes << 'titled'
title = node.title
title_sanitized = xml_sanitize title
title_attr = %( title="#{title_sanitized}")
- # FIXME use uppercase pcdata helper to make less fragile (see logic in Asciidoctor PDF)
- title_upper = title.upcase.gsub(NamedEntityRx) { %(&#{$1.downcase};) }
- title_el = %(<h2>#{title_upper}</h2>
+ title_el = %(<h2>#{title}</h2>
)
else
title_attr = nil
title_el = nil
end
@@ -416,18 +415,20 @@
lines << %(<div class="content">)
table_id_attr = node.id ? %( id="#{node.id}") : nil
frame_class = {
'all' => 'table-framed',
'topbot' => 'table-framed-topbot',
- 'sides' => 'table-framed-sides'
+ 'sides' => 'table-framed-sides',
+ 'none' => ''
}
grid_class = {
'all' => 'table-grid',
'rows' => 'table-grid-rows',
- 'cols' => 'table-grid-cols'
+ 'cols' => 'table-grid-cols',
+ 'none' => ''
}
- table_classes = %W(table #{frame_class[(node.attr 'frame')] || frame_class['topbot']} #{grid_class[(node.attr 'grid')] || grid_class['rows']})
+ table_classes = %W(table #{frame_class[node.attr 'frame'] || frame_class['topbot']} #{grid_class[node.attr 'grid'] || grid_class['rows']})
if (role = node.role)
table_classes << role
end
table_class_attr = %( class="#{table_classes * ' '}")
table_styles = []
@@ -623,20 +624,18 @@
end
def image node
target = node.attr 'target'
type = (::File.extname target)[1..-1]
+ id_attr = node.id ? %( id="#{node.id}") : ''
img_attrs = [%(alt="#{node.attr 'alt'}")]
case type
when 'svg'
img_attrs << %(style="width: #{node.attr 'scaledwidth', '100%'}")
# TODO make this a convenience method on document
- epub_properties = (node.document.attr 'epub-properties') || []
- unless epub_properties.include? 'svg'
- epub_properties << 'svg'
- node.document.attributes['epub-properties'] = epub_properties
- end
+ epub_properties = (node.document.attributes['epub-properties'] ||= [])
+ epub_properties << 'svg' unless epub_properties.include? 'svg'
else
if node.attr? 'scaledwidth'
img_attrs << %(style="width: #{node.attr 'scaledwidth'}")
end
end
@@ -655,11 +654,11 @@
# Aldiko doesn't not scale width to 100% by default
img_attrs << %(width="100%")
end
end
=end
- %(<figure class="image">
+ %(<figure#{id_attr} class="image#{prepend_space node.role}">
<div class="content">
<img src="#{node.image_uri node.attr('target')}" #{img_attrs * ' '}/>
</div>#{node.title? ? %[
<figcaption>#{node.captioned_title}</figcaption>] : nil}
</figure>)
@@ -690,24 +689,35 @@
id_attr = %( id="xref--#{refdoc_id}")
end
id_attr = nil unless @xrefs_seen.add? refid
refdoc = doc.references[:spine_items].find {|it| refdoc_id == (it.id || (it.attr 'docname')) }
if refdoc
- if (reftext = refdoc.references[:ids][refdoc_refid])
- text ||= reftext
+ # QUESTION should we invoke xreftext for references in other documents?
+ if (refs = refdoc.references[:refs]) && ::Asciidoctor::Document === (ref = refs[refdoc_refid])
+ text ||= (ref.attr 'docreftext') || ref.doctitle
+ elsif (xreftext = refdoc.references[:ids][refdoc_refid])
+ text ||= xreftext
else
warn %(asciidoctor: WARNING: #{::File.basename(doc.attr 'docfile')}: invalid reference to unknown anchor in #{refdoc_id} chapter: #{refdoc_refid})
end
else
warn %(asciidoctor: WARNING: #{::File.basename(doc.attr 'docfile')}: invalid reference to anchor in unknown chapter: #{refdoc_id})
end
else
id_attr = (@xrefs_seen.add? refid) ? %( id="xref-#{refid}") : nil
- if (reftext = doc.references[:ids][refid])
- text ||= reftext
+ if (refs = doc.references[:refs])
+ if ::Asciidoctor::AbstractNode === (ref = refs[refid])
+ xreftext = text || ref.xreftext((@xrefstyle ||= (doc.attr 'xrefstyle')))
+ end
else
- # FIXME we get false negatives for reference to bibref
+ xreftext = doc.references[:ids][refid]
+ end
+
+ if xreftext
+ text ||= xreftext
+ else
+ # FIXME we get false negatives for reference to bibref when using Asciidoctor < 1.5.6
warn %(asciidoctor: WARNING: #{::File.basename(doc.attr 'docfile')}: invalid reference to unknown local anchor (or valid bibref): #{refid})
end
end
%(<a#{id_attr} href="#{target}" class="xref">#{text || "[#{refid}]"}</a>)
when :ref
@@ -755,12 +765,20 @@
i_classes << %(icon-rotate-#{node.attr 'rotate'}) if node.attr? 'rotate'
i_classes << node.role if node.role?
%(<i class="#{i_classes * ' '}"></i>)
else
target = node.image_uri node.target
- class_attr = %( class="#{node.role}") if node.role?
- %(<img src="#{target}" alt="#{node.attr 'alt'}"#{class_attr}/>)
+ img_attrs = [%(alt="#{node.attr 'alt'}"), %(class="inline#{node.role? ? " #{node.role}" : ''}")]
+ if target.end_with? '.svg'
+ img_attrs << %(style="width: #{node.attr 'scaledwidth', '100%'}")
+ # TODO make this a convenience method on document
+ epub_properties = (node.document.attributes['epub-properties'] ||= [])
+ epub_properties << 'svg' unless epub_properties.include? 'svg'
+ elsif node.attr? 'scaledwidth'
+ img_attrs << %(style="width: #{node.attr 'scaledwidth'}")
+ end
+ %(<img src="#{target}" #{img_attrs * ' '}/>)
end
end
def inline_indexterm node
node.type == :visible ? node.text : ''
@@ -837,14 +855,19 @@
if last_block.context == :paragraph
last_block.attributes['role'] = last_block.role? ? %(#{last_block.role} last) : 'last'
end
nil
end
+
+ # Prepend a space to the value if it's non-nil, otherwise return empty string.
+ def prepend_space value
+ value ? %( #{value}) : ''
+ end
end
class DocumentIdGenerator
ReservedIds = %w(cover nav ncx)
- CharRefRx = /&(?:([a-zA-Z]{2,})|#(\d{2,6})|#x([a-fA-F0-9]{2,5}));/
+ CharRefRx = /&(?:([a-zA-Z][a-zA-Z]+\d{0,2})|#(\d\d\d{0,4})|#x([\da-fA-F][\da-fA-F][\da-fA-F]{0,3}));/
if defined? __dir__
InvalidIdCharsRx = /[^\p{Word}]+/
LeadingDigitRx = /^\p{Nd}/
else
InvalidIdCharsRx = /[^[:word:]]+/