summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/cli/buildEbook.js6
-rw-r--r--lib/json/encodeOutput.js3
-rw-r--r--lib/output/ebook/getConvertOptions.js73
-rw-r--r--lib/output/ebook/getCoverPath.js30
-rw-r--r--lib/output/ebook/getPDFTemplate.js42
-rw-r--r--lib/output/ebook/index.js2
-rw-r--r--lib/output/ebook/onFinish.js66
-rw-r--r--lib/output/ebook/onInit.js21
-rw-r--r--lib/output/ebook/options.js17
-rw-r--r--lib/utils/fs.js17
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),