summaryrefslogtreecommitdiffstats
path: root/lib/output2/ebook.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/output2/ebook.js')
-rw-r--r--lib/output2/ebook.js193
1 files changed, 193 insertions, 0 deletions
diff --git a/lib/output2/ebook.js b/lib/output2/ebook.js
new file mode 100644
index 0000000..2b8fac9
--- /dev/null
+++ b/lib/output2/ebook.js
@@ -0,0 +1,193 @@
+var _ = require('lodash');
+var util = require('util');
+var juice = require('juice');
+
+var command = require('../utils/command');
+var fs = require('../utils/fs');
+var Promise = require('../utils/promise');
+var error = require('../utils/error');
+var WebsiteOutput = require('./website');
+var assetsInliner = require('./assets-inliner');
+
+function _EbookOutput() {
+ WebsiteOutput.apply(this, arguments);
+
+ // ebook-convert does not support link like "./"
+ this.opts.directoryIndex = false;
+}
+util.inherits(_EbookOutput, WebsiteOutput);
+
+var EbookOutput = assetsInliner(_EbookOutput);
+
+EbookOutput.prototype.name = 'ebook';
+
+// Return context for templating
+// Incldue type of ebbook generated
+EbookOutput.prototype.getSelfContext = function() {
+ var ctx = EbookOutput.super_.prototype.getSelfContext.apply(this);
+ ctx.format = this.opts.format;
+
+ return ctx;
+};
+
+// Finish generation, create ebook using ebook-convert
+EbookOutput.prototype.finish = function() {
+ var that = this;
+ if (that.book.isMultilingual()) {
+ return EbookOutput.super_.prototype.finish.apply(that);
+ }
+
+ return Promise()
+ .then(function() {
+ return EbookOutput.super_.prototype.finish.apply(that);
+ })
+
+ // Generate SUMMARY.html
+ .then(function() {
+ return that.render('summary', 'SUMMARY.html', that.getContext());
+ })
+
+ // Start ebook-convert
+ .then(function() {
+ return that.ebookConvertOption();
+ })
+
+ .then(function(options) {
+ if (!that.opts.format) return;
+
+ var cmd = [
+ 'ebook-convert',
+ that.resolve('SUMMARY.html'),
+ that.resolve('index.'+that.opts.format),
+ command.optionsToShellArgs(options)
+ ].join(' ');
+
+ return command.exec(cmd)
+ .progress(function(data) {
+ that.book.log.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);
+ });
+ });
+};
+
+// Generate header/footer for PDF
+EbookOutput.prototype.getPDFTemplate = function(tpl) {
+ var that = this;
+ var context = _.extend(
+ {
+ // Nunjucks context mapping to ebook-convert templating
+ page: {
+ num: '_PAGENUM_',
+ title: '_TITLE_',
+ section: '_SECTION_'
+ }
+ },
+ this.getContext()
+ );
+
+ return this.renderAsString('pdf_'+tpl, context)
+
+ // Inline css, include css relative to the output folder
+ .then(function(output) {
+ return Promise.nfcall(juice.juiceResources, output, {
+ webResources: {
+ relativeTo: that.root()
+ }
+ });
+ });
+};
+
+// Locate the cover file to use
+// Use configuration or search a "cover.jpg" file
+// For multi-lingual book, it can use the one from the main book
+EbookOutput.prototype.locateCover = function() {
+ var cover = this.book.config.get('cover', 'cover.jpg');
+
+ // Resolve to absolute
+ cover = this.resolve(cover);
+
+ // Cover doesn't exist and multilingual?
+ if (!fs.existsSync(cover)) {
+ if (this.parent) return this.parent.locateCover();
+ else return undefined;
+ }
+
+ return cover;
+};
+
+// Generate options for ebook-convert
+EbookOutput.prototype.ebookConvertOption = function() {
+ var that = this;
+
+ var options = {
+ '--cover': this.locateCover(),
+ '--title': that.book.config.get('title'),
+ '--comments': that.book.config.get('description'),
+ '--isbn': that.book.config.get('isbn'),
+ '--authors': that.book.config.get('author'),
+ '--language': that.book.config.get('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 \')]',
+ '--no-chapters-in-toc': true,
+ '--max-levels': '1',
+ '--breadth-first': true
+ };
+
+ if (that.opts.format == 'epub') {
+ options = _.extend(options, {
+ '--dont-split-on-page-breaks': true
+ });
+ }
+
+ if (that.opts.format != 'pdf') return Promise(options);
+
+ var pdfOptions = that.book.config.get('pdf');
+
+ 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-header-template': that.getPDFTemplate('header'),
+ '--pdf-footer-template': that.getPDFTemplate('footer'),
+ '--pdf-sans-family': String(pdfOptions.fontFamily)
+ });
+
+ return that.getPDFTemplate('header')
+ .then(function(tpl) {
+ options['--pdf-header-template'] = tpl;
+
+ return that.getPDFTemplate('footer');
+ })
+ .then(function(tpl) {
+ options['--pdf-footer-template'] = tpl;
+
+ return options;
+ });
+};
+
+// Don't write multi-lingual index for wbook
+EbookOutput.prototype.outputMultilingualIndex = function() {
+
+};
+
+module.exports = EbookOutput;