mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-21 01:53:26 +00:00
Complete ebook generation using ebook-convert
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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')
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user