lib/asciidoctor-epub3/converter.rb in asciidoctor-epub3-1.5.0.alpha.18 vs lib/asciidoctor-epub3/converter.rb in asciidoctor-epub3-1.5.0.alpha.19
- old
+ new
@@ -37,13 +37,13 @@
logger.debug %(Extracted #{@format.upcase} to #{extract_dir})
end
if @format == :kf8
# QUESTION shouldn't we validate this epub file too?
- distill_epub_to_mobi epub_file, target, @compress, @kindlegen_path
+ distill_epub_to_mobi epub_file, target, @compress
elsif @validate
- validate_epub epub_file, @epubcheck_path
+ validate_epub epub_file
end
end
CsvDelimiterRx = /\s*,\s*/
@@ -103,11 +103,11 @@
def convert node, name = nil, _opts = {}
method_name = %(convert_#{name ||= node.node_name})
if respond_to? method_name
send method_name, node
else
- logger.warn %(#{::File.basename node.attr('docfile')}: conversion missing in backend #{@backend} for #{name})
+ logger.warn %(conversion missing in backend #{@backend} for #{name})
nil
end
end
# See https://asciidoctor.org/docs/user-manual/#book-parts-and-chapters
@@ -141,21 +141,24 @@
title = node.title
end
title
end
+ def icon_names
+ @icon_names ||= []
+ end
+
def convert_document node
@format = node.attr('ebook-format').to_sym
@validate = node.attr? 'ebook-validate'
@extract = node.attr? 'ebook-extract'
@compress = node.attr 'ebook-compress'
@kindlegen_path = node.attr 'ebook-kindlegen-path'
@epubcheck_path = node.attr 'ebook-epubcheck-path'
@xrefs_seen = ::Set.new
- @icon_names = []
- @media_files = []
+ @media_files = {}
@footnotes = []
@book = GEPUB::Book.new 'EPUB/package.opf'
@book.epub_backward_compat = @format != :kf8
@book.language node.attr('lang', 'en'), id: 'pub-language'
@@ -265,18 +268,18 @@
@book.add_item 'toc.ncx', content: toc_ncx.to_ios, id: 'ncx'
docimagesdir = (node.attr 'imagesdir', '.').chomp '/'
docimagesdir = (docimagesdir == '.' ? nil : %(#{docimagesdir}/))
- @media_files.each do |file|
- if file[:name].start_with? %(#{docimagesdir}jacket/cover.)
- logger.warn %(path is reserved for cover artwork: #{file[:name]}; skipping file found in content)
+ @media_files.each do |name, file|
+ if name.start_with? %(#{docimagesdir}jacket/cover.)
+ logger.warn %(path is reserved for cover artwork: #{name}; skipping file found in content)
elsif file[:path].nil? || File.readable?(file[:path])
- mime_types = MIME::Types.type_for file[:name]
+ mime_types = MIME::Types.type_for name
mime_types.delete_if {|x| x.media_type != file[:media_type] }
preferred_mime_type = mime_types.empty? ? nil : mime_types[0].content_type
- @book.add_item file[:name], content: file[:path], media_type: preferred_mime_type
+ @book.add_item name, content: file[:path], media_type: preferred_mime_type
else
logger.error %(#{File.basename node.attr('docfile')}: media file not found or not readable: #{file[:path]})
end
end
@@ -326,20 +329,20 @@
return nil if docid.nil?
chapter_item = @book.add_ordered_item %(#{docid}.xhtml)
doctitle = node.document.doctitle partition: true, use_fallback: true
- doctitle_sanitized = sanitize_xml doctitle.combined, :cdata
+ chapter_title = doctitle.combined
if node.context == :document && doctitle.subtitle?
title = %(#{doctitle.main} )
subtitle = doctitle.subtitle
elsif node.title
# HACK: until we get proper handling of title-only in CSS
title = ''
subtitle = get_numbered_title node
- doctitle_sanitized = sanitize_xml subtitle, :cdata
+ chapter_title = subtitle
else
title = nil
subtitle = nil
end
@@ -358,14 +361,14 @@
@xrefs_seen.clear
content = node.content
# NOTE must run after content is resolved
# TODO perhaps create dynamic CSS file?
- if @icon_names.empty?
+ if icon_names.empty?
icon_css_head = ''
else
- icon_defs = @icon_names.map {|name|
+ icon_defs = icon_names.map {|name|
%(.i-#{name}::before { content: "#{FontIconMap.unicode name}"; })
} * LF
icon_css_head = %(<style>
#{icon_defs}
</style>
@@ -385,11 +388,11 @@
# NOTE kindlegen seems to mangle the <header> element, so we wrap its content in a div
lines = [%(<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xmlns:mml="http://www.w3.org/1998/Math/MathML" xml:lang="#{lang = node.document.attr 'lang', 'en'}" lang="#{lang}">
<head>
<meta charset="UTF-8"/>
-<title>#{doctitle_sanitized}</title>
+<title>#{chapter_title}</title>
<link rel="stylesheet" type="text/css" href="styles/epub3.css"/>
<link rel="stylesheet" type="text/css" href="styles/epub3-css3-only.css" media="(min-device-width: 0px)"/>
#{icon_css_head}<script type="text/javascript"><![CDATA[
document.addEventListener('DOMContentLoaded', function(event, reader) {
if (!(reader = navigator.epubReadingSystem)) {
@@ -399,15 +402,17 @@
document.body.setAttribute('class', reader.name.toLowerCase().replace(/ /g, '-'));
});
]]></script>)]
syntax_hl = node.document.syntax_highlighter
+ epub_type_attr = node.respond_to?(:section) && node.sectname != 'section' ? %( epub:type="#{node.sectname}") : ''
+
lines << (syntax_hl.docinfo :head, node, linkcss: linkcss, self_closing_tag_slash: '/') if syntax_hl&.docinfo? :head
lines << %(</head>
<body>
-<section class="chapter" title="#{doctitle_sanitized}" epub:type="chapter" id="#{docid}">
+<section class="chapter" title=#{chapter_title.encode xml: :attr}#{epub_type_attr} id="#{docid}">
#{header}
#{content})
unless (fns = node.document.footnotes - @footnotes).empty?
@footnotes += fns
@@ -444,15 +449,14 @@
end
def convert_section node
if add_chapter(node).nil?
hlevel = node.level
- epub_type_attr = node.special ? %( epub:type="#{node.sectname}") : ''
+ epub_type_attr = node.sectname != 'section' ? %( epub:type="#{node.sectname}") : ''
div_classes = [%(sect#{node.level}), node.role].compact
title = get_numbered_title node
- title_sanitized = xml_sanitize title
- %(<section class="#{div_classes * ' '}" title="#{title_sanitized}"#{epub_type_attr}>
+ %(<section class="#{div_classes * ' '}" title=#{title.encode xml: :attr}#{epub_type_attr}>
<h#{hlevel} id="#{node.id}">#{title}</h#{hlevel}>#{(content = node.content).empty? ? '' : %(
#{content})}
</section>)
end
end
@@ -531,18 +535,16 @@
end
type = node.attr 'name'
epub_type = case type
when 'tip'
- 'help'
- when 'note'
- 'note'
- when 'important', 'warning', 'caution'
- 'warning'
+ 'tip'
+ when 'important', 'warning', 'caution', 'note'
+ 'notice'
else
logger.warn %(unknown admonition type: #{type})
- 'note'
+ 'notice'
end
%(<aside#{id_attr} class="admonition #{type}"#{title_attr} epub:type="#{epub_type}">
#{title_el}<div class="content">
#{output_content node}
</div>
@@ -565,10 +567,11 @@
id_attribute = node.id ? %( id="#{node.id}") : ''
%(<#{tag_name}#{id_attribute} class="#{['discrete', node.role].compact * ' '}">#{node.title}</#{tag_name}>)
end
def convert_listing node
+ id_attribute = node.id ? %( id="#{node.id}") : ''
nowrap = (node.option? 'nowrap') || !(node.document.attr? 'prewrap')
if node.style == 'source'
lang = node.attr 'language'
syntax_hl = node.document.syntax_highlighter
if syntax_hl
@@ -587,11 +590,11 @@
syntax_hl = nil
end
figure_classes = ['listing']
figure_classes << 'coalesce' if node.option? 'unbreakable'
title_div = node.title? ? %(<figcaption>#{node.captioned_title}</figcaption>) : ''
- %(<figure class="#{figure_classes * ' '}">#{title_div}
+ %(<figure#{id_attribute} class="#{figure_classes * ' '}">#{title_div}
#{syntax_hl ? (syntax_hl.format node, lang, opts) : pre_open + (node.content || '') + pre_close}
</figure>)
end
def convert_stem node
@@ -615,13 +618,17 @@
def load_asciimath
Helpers.require_library('asciimath', true, :warn).nil? ? :unavailable : :loaded
end
- # QUESTION should we wrap the <pre> in either <div> or <figure>?
def convert_literal node
- %(<pre class="screen">#{node.content}</pre>)
+ id_attribute = node.id ? %( id="#{node.id}") : ''
+ title_element = node.title? ? %(<figcaption>#{node.captioned_title}</figcaption>) : ''
+ %(<figure#{id_attribute} class="literalblock#{prepend_space node.role}">
+#{title_element}
+<div class="content"><pre class="screen">#{node.content}</pre></div>
+</figure>)
end
def convert_page_break _node
'<hr epub:type="pagebreak" class="pagebreak"/>'
end
@@ -710,14 +717,12 @@
table_classes << role
end
table_styles = []
if (autowidth = node.option? 'autowidth') && !(node.attr? 'width')
table_classes << 'fit-content'
- elsif (tablewidth = node.attr 'tablepcwidth') == 100
- table_classes << 'stretch'
else
- table_styles << %(width: #{tablewidth}%;)
+ table_styles << %(width: #{node.attr 'tablepcwidth'}%;)
end
table_class_attr = %( class="#{table_classes * ' '}")
table_style_attr = !table_styles.empty? ? %( style="#{table_styles * '; '}") : ''
lines << %(<table#{table_id_attr}#{table_class_attr}#{table_style_attr}>)
@@ -964,10 +969,12 @@
chapter.set_attr 'epub-properties', [] unless chapter.attr? 'epub-properties'
epub_properties = chapter.attr 'epub-properties'
epub_properties << 'svg' unless epub_properties.include? 'svg'
end
+ return if target.start_with? 'data:'
+
if Asciidoctor::Helpers.uriish? target
# We need to add both local and remote media files to manifest
fs_path = nil
else
out_dir = node.attr('outdir', nil, true) || doc_option(node.document, :to_dir)
@@ -976,11 +983,11 @@
base_dir = root_document(node.document).base_dir
fs_path = ::File.join base_dir, target
end
end
# We need *both* virtual and physical image paths. Unfortunately, references[:images] only has one of them.
- @media_files << { name: target, path: fs_path, media_type: media_type }
+ @media_files[target] ||= { path: fs_path, media_type: media_type }
end
def resolve_image_attrs node
img_attrs = []
img_attrs << %(alt="#{node.attr 'alt'}") if node.attr? 'alt'
@@ -1163,11 +1170,11 @@
end
end
def convert_inline_image node
if node.type == 'icon'
- @icon_names << (icon_name = node.target)
+ icon_names << (icon_name = node.target)
i_classes = ['icon', %(i-#{icon_name})]
i_classes << %(icon-#{node.attr 'size'}) if node.attr? 'size'
i_classes << %(icon-flip-#{(node.attr 'flip')[0]}) if node.attr? 'flip'
i_classes << %(icon-rotate-#{node.attr 'rotate'}) if node.attr? 'rotate'
i_classes << node.role if node.role?
@@ -1624,37 +1631,30 @@
.gsub(/<img([^>]+) style="width: (\d\d)%;"/, '<img\1 style="width: \2%; height: \2%;"')
.gsub(/<script type="text\/javascript">.*?<\/script>\n?/m, '')
.to_ios
end
- def get_kindlegen_command kindlegen_path
- unless kindlegen_path.nil?
- logger.debug %(Using ebook-kindlegen-path attribute: #{kindlegen_path})
- return [kindlegen_path]
+ def get_kindlegen_command
+ unless @kindlegen_path.nil?
+ logger.debug %(Using ebook-kindlegen-path attribute: #{@kindlegen_path})
+ return [@kindlegen_path]
end
unless (result = ENV['KINDLEGEN']).nil?
logger.debug %(Using KINDLEGEN env variable: #{result})
return [result]
end
- begin
- require 'kindlegen' unless defined? ::Kindlegen
- result = ::Kindlegen.command.to_s
- logger.debug %(Using KindleGen from gem: #{result})
- [result]
- rescue LoadError => e
- logger.debug %(#{e}; Using KindleGen from PATH)
- [%(kindlegen#{::Gem.win_platform? ? '.exe' : ''})]
- end
+ logger.debug 'Using KindleGen from PATH'
+ [%(kindlegen#{::Gem.win_platform? ? '.exe' : ''})]
end
- def distill_epub_to_mobi epub_file, target, compress, kindlegen_path
+ def distill_epub_to_mobi epub_file, target, compress
mobi_file = ::File.basename target.sub(EpubExtensionRx, '.mobi')
compress_flag = KindlegenCompression[compress ? (compress.empty? ? '1' : compress.to_s) : '0']
- argv = get_kindlegen_command(kindlegen_path) + ['-dont_append_source', compress_flag, '-o', mobi_file, epub_file].compact
+ argv = get_kindlegen_command + ['-dont_append_source', compress_flag, '-o', mobi_file, epub_file].compact
begin
# This duplicates Kindlegen.run, but we want to override executable
out, err, res = Open3.capture3(*argv) do |r|
r.force_encoding 'UTF-8' if ::Gem.win_platform? && r.respond_to?(:force_encoding)
end
@@ -1675,14 +1675,14 @@
else
logger.error %(KindleGen failed to write MOBI to #{output_file})
end
end
- def get_epubcheck_command epubcheck_path
- unless epubcheck_path.nil?
- logger.debug %(Using ebook-epubcheck-path attribute: #{epubcheck_path})
- return [epubcheck_path]
+ def get_epubcheck_command
+ unless @epubcheck_path.nil?
+ logger.debug %(Using ebook-epubcheck-path attribute: #{@epubcheck_path})
+ return [@epubcheck_path]
end
unless (result = ENV['EPUBCHECK']).nil?
logger.debug %(Using EPUBCHECK env variable: #{result})
return [result]
@@ -1696,11 +1696,11 @@
logger.debug %(#{e}; Using EPUBCheck from PATH)
['epubcheck']
end
end
- def validate_epub epub_file, epubcheck_path
- argv = get_epubcheck_command(epubcheck_path) + ['-w', epub_file]
+ def validate_epub epub_file
+ argv = get_epubcheck_command + ['-w', epub_file]
begin
out, err, res = Open3.capture3(*argv)
rescue Errno::ENOENT => e
raise 'Unable to run EPUBCheck. Either install epubcheck-ruby gem or place `epubcheck` executable on PATH or set EPUBCHECK environment variable with path to it', cause: e
end