Complete ebook generation using ebook-convert

This commit is contained in:
Samy Pessé
2016-04-29 16:50:50 +02:00
parent ceb4627f4e
commit 98f70879bd
10 changed files with 249 additions and 28 deletions
+3 -3
View File
@@ -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);
}
+3
View File
@@ -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;
}
+73
View File
@@ -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;
+30
View File
@@ -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;
+42
View File
@@ -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;
+1 -1
View File
@@ -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')
});
+63 -3
View File
@@ -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;
-21
View File
@@ -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;
+17
View File
@@ -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;
+17
View File
@@ -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),