diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/cli/buildEbook.js | 6 | ||||
-rw-r--r-- | lib/json/encodeOutput.js | 3 | ||||
-rw-r--r-- | lib/output/ebook/getConvertOptions.js | 73 | ||||
-rw-r--r-- | lib/output/ebook/getCoverPath.js | 30 | ||||
-rw-r--r-- | lib/output/ebook/getPDFTemplate.js | 42 | ||||
-rw-r--r-- | lib/output/ebook/index.js | 2 | ||||
-rw-r--r-- | lib/output/ebook/onFinish.js | 66 | ||||
-rw-r--r-- | lib/output/ebook/onInit.js | 21 | ||||
-rw-r--r-- | lib/output/ebook/options.js | 17 | ||||
-rw-r--r-- | lib/utils/fs.js | 17 |
10 files changed, 249 insertions, 28 deletions
diff --git a/lib/cli/buildEbook.js b/lib/cli/buildEbook.js index ce1d836..405d838 100644 --- a/lib/cli/buildEbook.js +++ b/lib/cli/buildEbook.js @@ -47,18 +47,18 @@ module.exports = function(format) { var langOutputFile = path.join( path.dirname(outputFile), - path.basename(outputFile, format) + '_' + langID + format + path.basename(outputFile, format) + '_' + langID + '.' + format ); return fs.copy( - path.resolve(outputFolder, langID, 'index' + format), + path.resolve(outputFolder, langID, 'index.' + format), langOutputFile ); }) .thenResolve(languages.getCount()); } else { return fs.copy( - path.resolve(outputFolder, 'index' + format), + path.resolve(outputFolder, 'index.' + format), outputFile ).thenResolve(1); } diff --git a/lib/json/encodeOutput.js b/lib/json/encodeOutput.js index bb53a62..9054124 100644 --- a/lib/json/encodeOutput.js +++ b/lib/json/encodeOutput.js @@ -9,6 +9,7 @@ var encodeBook = require('./encodeBook'); function encodeOutputToJson(output) { var book = output.getBook(); var generator = output.getGenerator(); + var options = output.getOptions(); var result = encodeBook(book); @@ -16,6 +17,8 @@ function encodeOutputToJson(output) { name: generator }; + result.options = options.toJS(); + return result; } diff --git a/lib/output/ebook/getConvertOptions.js b/lib/output/ebook/getConvertOptions.js new file mode 100644 index 0000000..bc80493 --- /dev/null +++ b/lib/output/ebook/getConvertOptions.js @@ -0,0 +1,73 @@ +var extend = require('extend'); + +var Promise = require('../../utils/promise'); +var getPDFTemplate = require('./getPDFTemplate'); +var getCoverPath = require('./getCoverPath'); + +/** + Generate options for ebook-convert + + @param {Output} + @return {Promise<Object>} +*/ +function getConvertOptions(output) { + var options = output.getOptions(); + var format = options.get('format'); + + var book = output.getBook(); + var config = book.getConfig(); + + return Promise() + .then(function() { + var coverPath = getCoverPath(output); + var options = { + '--cover': coverPath, + '--title': config.getValue('title'), + '--comments': config.getValue('description'), + '--isbn': config.getValue('isbn'), + '--authors': config.getValue('author'), + '--language': book.getLanguage() || config.getValue('language'), + '--book-producer': 'GitBook', + '--publisher': 'GitBook', + '--chapter': 'descendant-or-self::*[contains(concat(\' \', normalize-space(@class), \' \'), \' book-chapter \')]', + '--level1-toc': 'descendant-or-self::*[contains(concat(\' \', normalize-space(@class), \' \'), \' book-chapter-1 \')]', + '--level2-toc': 'descendant-or-self::*[contains(concat(\' \', normalize-space(@class), \' \'), \' book-chapter-2 \')]', + '--level3-toc': 'descendant-or-self::*[contains(concat(\' \', normalize-space(@class), \' \'), \' book-chapter-3 \')]', + '--max-levels': '1', + '--no-chapters-in-toc': true, + '--breadth-first': true, + '--dont-split-on-page-breaks': format === 'epub'? true : undefined + }; + + if (format !== 'pdf') { + return options; + } + + return Promise.all([ + getPDFTemplate(output, 'header'), + getPDFTemplate(output, 'footer') + ]) + .spread(function(headerTpl, footerTpl) { + var pdfOptions = config.getValue('pdf').toJS(); + + return options = extend(options, { + '--chapter-mark': String(pdfOptions.chapterMark), + '--page-breaks-before': String(pdfOptions.pageBreaksBefore), + '--margin-left': String(pdfOptions.margin.left), + '--margin-right': String(pdfOptions.margin.right), + '--margin-top': String(pdfOptions.margin.top), + '--margin-bottom': String(pdfOptions.margin.bottom), + '--pdf-default-font-size': String(pdfOptions.fontSize), + '--pdf-mono-font-size': String(pdfOptions.fontSize), + '--paper-size': String(pdfOptions.paperSize), + '--pdf-page-numbers': Boolean(pdfOptions.pageNumbers), + '--pdf-sans-family': String(pdfOptions.fontFamily), + '--pdf-header-template': headerTpl, + '--pdf-footer-template': footerTpl + }); + }); + }); +} + + +module.exports = getConvertOptions; diff --git a/lib/output/ebook/getCoverPath.js b/lib/output/ebook/getCoverPath.js new file mode 100644 index 0000000..c2192d4 --- /dev/null +++ b/lib/output/ebook/getCoverPath.js @@ -0,0 +1,30 @@ +var path = require('path'); +var fs = require('../../utils/fs'); + +/** + Resolve path to cover file to use + + @param {Output} + @return {String} +*/ +function getCoverPath(output) { + var outputRoot = output.getRoot(); + var book = output.getBook(); + var config = book.getConfig(); + var cover = config.getValue('cover', 'cover.jpg'); + + // Resolve to absolute + cover = fs.pickFile(outputRoot, cover); + if (cover) { + return cover; + } + + // Multilingual? try parent folder + if (book.isLanguageBook()) { + cover = fs.pickFile(path.join(outputRoot, '..'), cover); + } + + return cover; +} + +module.exports = getCoverPath; diff --git a/lib/output/ebook/getPDFTemplate.js b/lib/output/ebook/getPDFTemplate.js new file mode 100644 index 0000000..f7a450d --- /dev/null +++ b/lib/output/ebook/getPDFTemplate.js @@ -0,0 +1,42 @@ +var juice = require('juice'); + +var WebsiteGenerator = require('../website'); +var JSONUtils = require('../../json'); +var Templating = require('../../templating'); +var Promise = require('../../utils/promise'); + + +/** + Generate PDF header/footer templates + + @param {Output} output + @param {String} type + @return {String} +*/ +function getPDFTemplate(output, type) { + var filePath = 'pdf_' + type + '.html'; + var outputRoot = output.getRoot(); + var engine = WebsiteGenerator.createTemplateEngine(output, filePath); + + // Generate context + var context = JSONUtils.encodeOutput(output); + context.page = { + num: '_PAGENUM_', + title: '_TITLE_', + section: '_SECTION_' + }; + + // Render the theme + return Templating.renderFile(engine, 'ebook/' + filePath, context) + + // Inline css and assets + .then(function(html) { + return Promise.nfcall(juice.juiceResources, html, { + webResources: { + relativeTo: outputRoot + } + }); + }); +} + +module.exports = getPDFTemplate; diff --git a/lib/output/ebook/index.js b/lib/output/ebook/index.js index 344a6c5..786a10a 100644 --- a/lib/output/ebook/index.js +++ b/lib/output/ebook/index.js @@ -3,7 +3,7 @@ var WebsiteGenerator = require('../website'); module.exports = extend({}, WebsiteGenerator, { name: 'ebook', - onInit: require('./onInit'), + Options: require('./options'), onPage: require('./onPage'), onFinish: require('./onFinish') }); diff --git a/lib/output/ebook/onFinish.js b/lib/output/ebook/onFinish.js index e82f679..17a8e5e 100644 --- a/lib/output/ebook/onFinish.js +++ b/lib/output/ebook/onFinish.js @@ -1,16 +1,22 @@ +var path = require('path'); + var WebsiteGenerator = require('../website'); var JSONUtils = require('../../json'); var Templating = require('../../templating'); +var Promise = require('../../utils/promise'); +var error = require('../../utils/error'); +var command = require('../../utils/command'); var writeFile = require('../helper/writeFile'); +var getConvertOptions = require('./getConvertOptions'); + /** - Finish the generation, generates the SUMMARY.html + Write the SUMMARY.html @param {Output} @return {Output} */ -function onFinish(output) { - var book = output.getBook(); +function writeSummary(output) { var options = output.getOptions(); var prefix = options.get('prefix'); @@ -27,4 +33,58 @@ function onFinish(output) { }); } +/** + Generate the ebook file as "index.pdf" + + @param {Output} + @return {Output} +*/ +function runEbookConvert(output) { + var logger = output.getLogger(); + var options = output.getOptions(); + var format = options.get('format'); + var outputFolder = output.getRoot(); + + if (!format) { + return Promise(output); + } + + return getConvertOptions(output) + .then(function(options) { + var cmd = [ + 'ebook-convert', + path.resolve(outputFolder, 'SUMMARY.html'), + path.resolve(outputFolder, 'index.' + format), + command.optionsToShellArgs(options) + ].join(' '); + + return command.exec(cmd) + .progress(function(data) { + logger.debug(data); + }) + .fail(function(err) { + if (err.code == 127) { + throw error.RequireInstallError({ + cmd: 'ebook-convert', + install: 'Install it from Calibre: https://calibre-ebook.com' + }); + } + + throw error.EbookError(err); + }); + }) + .thenResolve(output); +} + +/** + Finish the generation, generates the SUMMARY.html + + @param {Output} + @return {Output} +*/ +function onFinish(output) { + return writeSummary(output) + .then(runEbookConvert); +} + module.exports = onFinish; diff --git a/lib/output/ebook/onInit.js b/lib/output/ebook/onInit.js deleted file mode 100644 index 402e318..0000000 --- a/lib/output/ebook/onInit.js +++ /dev/null @@ -1,21 +0,0 @@ -var WebsiteGenerator = require('../website'); - -/** - Initialize the generator - - @param {Output} - @return {Output} -*/ -function onInit(output) { - return WebsiteGenerator.onInit(output) - .then(function(resultOutput) { - var options = resultOutput.getOptions(); - - options = options.set('directoryIndex', false); - options = options.set('prefix', 'ebook'); - - return resultOutput.setOptions(options); - }); -} - -module.exports = onInit; diff --git a/lib/output/ebook/options.js b/lib/output/ebook/options.js new file mode 100644 index 0000000..ea7b8b4 --- /dev/null +++ b/lib/output/ebook/options.js @@ -0,0 +1,17 @@ +var Immutable = require('immutable'); + +var Options = Immutable.Record({ + // Root folder for the output + root: String(), + + // Prefix for generation + prefix: String('ebook'), + + // Format to generate using ebook-convert + format: String(), + + // Force use of absolute urls ("index.html" instead of "/") + directoryIndex: Boolean(false) +}); + +module.exports = Options; diff --git a/lib/utils/fs.js b/lib/utils/fs.js index be3ec0e..3f97096 100644 --- a/lib/utils/fs.js +++ b/lib/utils/fs.js @@ -113,6 +113,22 @@ function assertFile(filePath, generator) { }); } +/** + Pick a file, returns the absolute path if exists, undefined otherwise + + @param {String} rootFolder + @param {String} fileName + @return {String} +*/ +function pickFile(rootFolder, fileName) { + var result = path.join(rootFolder, fileName); + if (fs.existsSync(result)) { + return result; + } + + return undefined; +} + module.exports = { exists: fileExists, existsSync: fs.existsSync, @@ -120,6 +136,7 @@ module.exports = { readFile: Promise.nfbind(fs.readFile), writeFile: Promise.nfbind(fs.writeFile), assertFile: assertFile, + pickFile: pickFile, stat: Promise.nfbind(fs.stat), statSync: fs.statSync, readdir: Promise.nfbind(fs.readdir), |