mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-24 19:32:07 +00:00
Complete generator for ebooks
This commit is contained in:
+16
-2
@@ -7,7 +7,6 @@ var pathUtil = require('../utils/path');
|
||||
var location = require('../utils/location');
|
||||
var PluginsManager = require('../plugins');
|
||||
var TemplateEngine = require('../template');
|
||||
var gitbook = require('../gitbook');
|
||||
|
||||
/*
|
||||
Output is like a stream interface for a parsed book
|
||||
@@ -16,9 +15,13 @@ to output "something".
|
||||
The process is mostly on the behavior of "onPage" and "onAsset"
|
||||
*/
|
||||
|
||||
function Output(book) {
|
||||
function Output(book, opts) {
|
||||
_.bindAll(this);
|
||||
|
||||
this.opts = _.defaults(opts || {}, {
|
||||
|
||||
});
|
||||
|
||||
this.book = book;
|
||||
this.log = this.book.log;
|
||||
|
||||
@@ -175,6 +178,17 @@ Output.prototype.onResolveTemplate = function(from, to) {
|
||||
|
||||
// ---- Utilities ----
|
||||
|
||||
// Return a default context for templates
|
||||
Output.prototype.getContext = function() {
|
||||
return _.extend(
|
||||
{},
|
||||
this.book.getContext(),
|
||||
this.book.summary.getContext(),
|
||||
this.book.glossary.getContext(),
|
||||
this.book.config.getContext()
|
||||
);
|
||||
};
|
||||
|
||||
// Resolve a file path in the context of a specific page
|
||||
// Result is an "absolute path relative to the output folder"
|
||||
Output.prototype.resolveForPage = function(page, href) {
|
||||
|
||||
+143
-2
@@ -1,14 +1,155 @@
|
||||
var _ = require('lodash');
|
||||
var util = require('util');
|
||||
var juice = require('juice');
|
||||
|
||||
var command = require('../utils/command');
|
||||
var fs = require('../utils/fs');
|
||||
var WebsiteOutput = require('./website');
|
||||
var assetsInliner = require('./assets-inliner');
|
||||
|
||||
function EbookOutput() {
|
||||
function _EbookOutput() {
|
||||
WebsiteOutput.apply(this, arguments);
|
||||
}
|
||||
util.inherits(EbookOutput, WebsiteOutput);
|
||||
|
||||
var EbookOutput = assetsInliner(_EbookOutput);
|
||||
|
||||
EbookOutput.prototype.name = 'ebook';
|
||||
|
||||
// Finish generation, create ebook using ebook-convert
|
||||
EbookOutput.prototype.finish = function() {
|
||||
var that = this;
|
||||
|
||||
module.exports = assetsInliner(EbookOutput);
|
||||
return Promise()
|
||||
.then(function() {
|
||||
return EbookOutput.super_.prototype.finish.apply(that);
|
||||
})
|
||||
|
||||
// Generate SUMMARY.html
|
||||
.then(function() {
|
||||
return that.render('summary', that.getContext())
|
||||
.then(function(html) {
|
||||
return that.writeFile(
|
||||
that.resolve('SUMMARY.html'),
|
||||
html
|
||||
);
|
||||
});
|
||||
})
|
||||
|
||||
// Start ebook-convert
|
||||
.then(function() {
|
||||
return that.ebookConvertOption();
|
||||
})
|
||||
|
||||
.then(function(options) {
|
||||
var cmd = [
|
||||
'ebook-convert',
|
||||
that.resolve('SUMMARY.html'),
|
||||
that.resolve('index.'+that.opts.format),
|
||||
command.optionsToShellArgs(options)
|
||||
].join(' ');
|
||||
|
||||
return command.exec(cmd)
|
||||
.fail(function(err) {
|
||||
if (err.code == 127) {
|
||||
err = new Error('Need to install ebook-convert from Calibre');
|
||||
}
|
||||
|
||||
throw 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_'
|
||||
}
|
||||
},
|
||||
this.getContext()
|
||||
);
|
||||
|
||||
return this.render('pdf_'+tpl, context)
|
||||
|
||||
// Inline css, include css relative to the output folder
|
||||
.then(function(output) {
|
||||
return Promise.nfcall(juice.juiceResources, tpl, {
|
||||
webResources: {
|
||||
relativeTo: that.root()
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Generate options for ebook-convert
|
||||
EbookOutput.prototype.ebookConvertOption = function() {
|
||||
var that = this;
|
||||
var cover = this.book.config.get('cover');
|
||||
|
||||
if (!cover && fs.existsSync(this.resolve('cover.jpg'))) {
|
||||
cover = this.resolve('cover.jpg');
|
||||
}
|
||||
|
||||
var options = {
|
||||
'--cover': cover,
|
||||
'--title': this.book.config.get('title'),
|
||||
'--comments': this.book.config.get('description'),
|
||||
'--isbn': this.book.config.get('isbn'),
|
||||
'--authors': this.book.config.get('author'),
|
||||
'--language': this.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 (this.opts.format == 'epub') {
|
||||
options = _.extend(options, {
|
||||
'--dont-split-on-page-breaks': true
|
||||
});
|
||||
}
|
||||
|
||||
if (this.opts.format != 'pdf') return Promise(options);
|
||||
|
||||
var pdfOptions = this.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 this.getPDFTemplate('header')
|
||||
.then(function(tpl) {
|
||||
options['--pdf-header-template'] = tpl;
|
||||
|
||||
return that.getPDFTemplate('footer');
|
||||
})
|
||||
.then(function(tpl) {
|
||||
options['--pdf-footer-template'] = tpl;
|
||||
|
||||
return options;
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = EbookOutput;
|
||||
|
||||
+21
-1
@@ -1,3 +1,4 @@
|
||||
var _ = require('lodash');
|
||||
var childProcess = require('child_process');
|
||||
var Promise = require('./promise');
|
||||
|
||||
@@ -37,8 +38,27 @@ function spawn(command, args, options) {
|
||||
return d.promise;
|
||||
}
|
||||
|
||||
// Transform an option object to a command line string
|
||||
function escapeShellArg(s) {
|
||||
s = s.replace(/"/g, '\\"');
|
||||
return '"' + s + '"';
|
||||
}
|
||||
|
||||
function optionsToShellArgs(options) {
|
||||
return _.chain(options)
|
||||
.map(function(value, key) {
|
||||
if (value === null || value === undefined || value === false) return null;
|
||||
if (value === true) return key;
|
||||
return key + '=' + escapeShellArg(value);
|
||||
})
|
||||
.compact()
|
||||
.value()
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isAvailable: isAvailable,
|
||||
exec: exec,
|
||||
spawn: spawn
|
||||
spawn: spawn,
|
||||
optionsToShellArgs: optionsToShellArgs
|
||||
};
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@
|
||||
"dom-serializer": "0.1.0",
|
||||
"spawn-cmd": "0.0.2",
|
||||
"escape-string-regexp": "1.0.3",
|
||||
"juice": "1.5.0",
|
||||
"juice": "1.9.0",
|
||||
"jsonschema": "1.0.2",
|
||||
"json-schema-defaults": "0.1.1",
|
||||
"merge-defaults": "0.2.1",
|
||||
|
||||
Reference in New Issue
Block a user