diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 17ce9fda..ddf79037 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -11,6 +11,8 @@ For a detailed view of what has changed, refer to the {uri-repo}/commits/master[ * make `KINDLEGEN` env var work again (#269) * enable Pygments on non-Windows JRuby platforms (#264) * provide a human-readable error message when we fail to find KindleGen (#268) +* try to use KindleGen/EPUBCheck binary from `$PATH` (#276) +* add `ebook-kindlegen-path`/`ebook-epubcheck-path` attributes to override KindleGen/EPUBCheck executable location (#276) == 1.5.0.alpha.11 (2020-01-26) - @slonopotamus diff --git a/README.adoc b/README.adoc index 982098bd..9b99f49b 100644 --- a/README.adoc +++ b/README.adoc @@ -540,14 +540,22 @@ For more information about this attribute and other related attributes, see {uri *-D, --destination-dir* :: Writes files to specified directory (defaults to the current directory) +*-a ebook-epubcheck-path=*:: + Specifies path to EPUBCheck executable to use with `-a ebook-validate`. + This attribute takes precedence over `EPUBCHECK` environment variable. + *-a ebook-extract* :: Extracts the EPUB3 to a folder in the destination directory after the file is generated *-a ebook-format=* :: Specifies the ebook format to generate (epub3 or kf8, default: epub3) +*-a ebook-kindlegen-path=*:: + Specifies path to KindleGen executable to use when producing KF8/Mobi. + This attribute takes precedence over `KINDLEGEN` environment variable. + *-a ebook-validate* :: - Runs {uri-epubcheck}[EpubCheck] to validate output file against the EPUB3 specification + Runs {uri-epubcheck}[EPUBCheck] to validate output file against the EPUB3 specification *-a ebook-compress=<0|1|2|none|standard|huffdic>* :: Controls the compression type used by kindlegen (0=none [default if unset], 1=standard [default if empty], 2=huffdic) @@ -555,6 +563,16 @@ For more information about this attribute and other related attributes, see {uri *-v, --version* :: Display the program version +=== Environment variables + +*EPUBCHECK*:: + Specifies path to EPUBCheck executable to use with `-a ebook-validate`. + Effect of this variable can be overriden with `-a ebook-epubcheck-path` attribute. + +*KINDLEGEN*:: + Specifies path to KindleGen executable to use when producing KF8/Mobi. + Effect of this variable can be overriden with `-a ebook-kindlegen-path` attribute. + === EPUB3 Archive Structure Here's a sample manifest of files found in an EPUB3 document produced by {project-name}. diff --git a/WORKLOG.adoc b/WORKLOG.adoc index 5a26de65..57064056 100644 --- a/WORKLOG.adoc +++ b/WORKLOG.adoc @@ -35,7 +35,6 @@ * allow role to be applied to chapter (which means the role must be on the document) * should we enable hyphens in non-Kindle? ** we can reenable once we confirm it doesn't crash Kindle for Mac -* document KINDLEGEN and EPUBCHECK env vars * part vs chapter? a difference? * create a video to demonstrate Google Play Books read aloud reading complete sentences in books generated with Asciidoctor EPUB3 diff --git a/lib/asciidoctor-epub3/converter.rb b/lib/asciidoctor-epub3/converter.rb index 20cabb39..956124d3 100644 --- a/lib/asciidoctor-epub3/converter.rb +++ b/lib/asciidoctor-epub3/converter.rb @@ -21,6 +21,8 @@ def initialize backend, opts htmlsyntax 'xml' @validate = false @extract = false + @kindlegen_path = nil + @epubcheck_path = nil end def convert node, name = nil @@ -28,6 +30,8 @@ def convert node, name = nil @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' spine_items = node.references[:spine_items] if spine_items.nil? logger.error %(#{::File.basename node.document.attr('docfile')}: failed to find spine items, produced file will be invalid) @@ -44,7 +48,7 @@ def convert node, name = nil # FIXME: we have to package in write because we don't have access to target before this point def write packager, target - packager.package validate: @validate, extract: @extract, compress: @compress, target: target + packager.package validate: @validate, extract: @extract, compress: @compress, kindlegen_path: @kindlegen_path, epubcheck_path: @epubcheck_path, target: target nil end end diff --git a/lib/asciidoctor-epub3/packager.rb b/lib/asciidoctor-epub3/packager.rb index 8763afcb..e8109e33 100644 --- a/lib/asciidoctor-epub3/packager.rb +++ b/lib/asciidoctor-epub3/packager.rb @@ -609,30 +609,46 @@ def package options = {} if fmt == :kf8 # QUESTION shouldn't we validate this epub file too? - distill_epub_to_mobi epub_file, target, options[:compress] + distill_epub_to_mobi epub_file, target, options[:compress], options[:kindlegen_path] elsif options[:validate] - validate_epub epub_file + validate_epub epub_file, options[:epubcheck_path] end end - def distill_epub_to_mobi epub_file, target, compress - if !(kindlegen_cmd = ENV['KINDLEGEN']).nil? - argv = [kindlegen_cmd] - else - begin - require 'kindlegen' unless defined? ::Kindlegen - rescue LoadError => e - raise 'Unable to find KindleGen. Either install the kindlegen gem or set KINDLEGEN environment variable with path to KindleGen executable', cause: e - end - argv = [::Kindlegen.command.to_s] + def get_kindlegen_command kindlegen_path + 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 + end + + def distill_epub_to_mobi epub_file, target, compress, kindlegen_path mobi_file = ::File.basename target.sub(EpubExtensionRx, '.mobi') compress_flag = KindlegenCompression[compress ? (compress.empty? ? '1' : compress.to_s) : '0'] - argv += ['-dont_append_source', compress_flag, '-o', mobi_file, epub_file].compact - # This duplicates Kindlegen.run, but we want to override executable - out, err, res = Open3.capture3(*argv) do |r| - r.force_encoding 'UTF-8' if windows? && r.respond_to?(:force_encoding) + argv = get_kindlegen_command(kindlegen_path) + ['-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 + rescue Errno::ENOENT => e + raise 'Unable to run KindleGen. Either install the kindlegen gem or set KINDLEGEN environment variable with path to KindleGen executable', cause: e end out.each_line do |line| @@ -650,15 +666,34 @@ def distill_epub_to_mobi epub_file, target, compress end end - def validate_epub epub_file - if !(epubcheck = ENV['EPUBCHECK']).nil? - argv = [epubcheck] - else - argv = [::Gem.ruby, ::Gem.bin_path('epubcheck-ruby', 'epubcheck')] + def get_epubcheck_command epubcheck_path + 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] end - argv += ['-w', epub_file] - out, err, res = Open3.capture3(*argv) + begin + result = ::Gem.bin_path 'epubcheck-ruby', 'epubcheck' + logger.debug %(Using EPUBCheck from gem: #{result}) + [::Gem.ruby, result] + rescue ::Gem::Exception => e + 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] + begin + out, err, res = Open3.capture3(*argv) + rescue Errno::ENOENT => e + raise 'Unable to run EPUBCheck. Either install epubcheck-ruby gem or set EPUBCHECK environment variable with path to EPUBCheck executable', cause: e + end out.each_line do |line| logger.info line