summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSamy Pessé <samypesse@gmail.com>2015-01-23 00:16:46 +0100
committerSamy Pessé <samypesse@gmail.com>2015-01-23 00:16:46 +0100
commit19f1d31758727d09a173037583d8ed765d539a33 (patch)
tree2e47ef2542e5ff0f42e15a034cb0d35af7a83331
parentee2420cbcca41f3c33a243084b11fd9946b87ed1 (diff)
downloadgitbook-19f1d31758727d09a173037583d8ed765d539a33.zip
gitbook-19f1d31758727d09a173037583d8ed765d539a33.tar.gz
gitbook-19f1d31758727d09a173037583d8ed765d539a33.tar.bz2
Add base for ebook generator
-rw-r--r--lib/generators/ebook.js114
-rw-r--r--lib/generators/index.js7
-rw-r--r--lib/generators/website.js66
-rw-r--r--lib/utils/string.js26
-rw-r--r--test/pdf.js15
5 files changed, 196 insertions, 32 deletions
diff --git a/lib/generators/ebook.js b/lib/generators/ebook.js
new file mode 100644
index 0000000..fa5dcbd
--- /dev/null
+++ b/lib/generators/ebook.js
@@ -0,0 +1,114 @@
+var util = require("util");
+var path = require("path");
+var Q = require("q");
+var _ = require("lodash");
+var exec = require('child_process').exec;
+
+var fs = require("../utils/fs");
+var stringUtils = require("../utils/string");
+var BaseGenerator = require("./website");
+
+var Generator = function(book, format) {
+ BaseGenerator.apply(this, arguments);
+
+ // eBook format
+ this.ebookFormat = format || "pdf";
+
+ // Styles to use
+ this.styles = ["ebook", this.ebookFormat];
+};
+util.inherits(Generator, BaseGenerator);
+
+Generator.prototype.prepareTemplates = function() {
+ this.templates["page"] = this.plugins.template("ebook:page") || path.resolve(this.options.theme, 'templates/ebook/page.html');
+ this.templates["summary"] = this.plugins.template("ebook:summary") || path.resolve(this.options.theme, 'templates/ebook/summary.html');
+ this.templates["glossary"] = this.plugins.template("ebook:glossary") || path.resolve(this.options.theme, 'templates/ebook/glossary.html');
+
+ return Q();
+};
+
+// Generate table of contents
+Generator.prototype.writeSummary = function() {
+ var that = this;
+
+ return this._writeTemplate(this.templates["summary"], {}, path.join(this.options.output, "SUMMARY.html"));
+};
+
+
+Generator.prototype.langsIndex = function(langs) {
+ return Q();
+};
+
+Generator.prototype.finish = function() {
+ var that = this;
+
+ return BaseGenerator.prototype.finish.apply(this)
+ .then(function() {
+ return that.writeSummary();
+ })
+ .then(function() {
+ var d = Q.defer();
+
+ if (!that.options.cover && fs.existsSync(path.join(that.options.output, "cover.jpg"))) {
+ that.options.cover = path.join(that.options.output, "cover.jpg");
+ }
+
+ var _options = {
+ "--cover": that.options.cover,
+ "--title": that.options.title,
+ "--comments": that.options.description,
+ "--isbn": that.options.isbn,
+ "--authors": that.options.author,
+ "--publisher": "GitBook",
+ "--chapter": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter ')]",
+ "--chapter-mark": "pagebreak",
+ "--page-breaks-before": "/",
+ "--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.ebookFormat == "pdf") {
+ var pdfOptions = that.options.pdf;
+
+ _.extend(_options, {
+ "--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": String(pdfOptions.headerTemplate),
+ "--pdf-footer-template": String(pdfOptions.footerTemplate)
+ });
+ }
+
+ var command = [
+ "ebook-convert",
+ path.join(that.options.output, "SUMMARY.html"),
+ path.join(that.options.output, "index."+that.ebookFormat),
+ stringUtils.optionsToShellArgs(_options)
+ ].join(" ");
+
+ exec(command, function (error, stdout, stderr) {
+ if (error) {
+ if (error.code == 127) {
+ error.message = "Need to install ebook-convert from Calibre";
+ } else {
+ error.message = error.message + " "+stdout;
+ }
+ return d.reject(error);
+ }
+ d.resolve();
+ });
+
+ return d.promise;
+ });
+};
+
+module.exports = Generator;
diff --git a/lib/generators/index.js b/lib/generators/index.js
index 978e08a..9189196 100644
--- a/lib/generators/index.js
+++ b/lib/generators/index.js
@@ -1,5 +1,10 @@
+var _ = require("lodash");
+var EbookGenerator = require("./ebook");
module.exports = {
json: require("./json"),
- website: require("./website")
+ website: require("./website"),
+ pdf: _.partialRight(EbookGenerator, "pdf"),
+ mobi: _.partialRight(EbookGenerator, "mobi"),
+ epub: _.partialRight(EbookGenerator, "epub")
};
diff --git a/lib/generators/website.js b/lib/generators/website.js
index 6db2fa4..c54380e 100644
--- a/lib/generators/website.js
+++ b/lib/generators/website.js
@@ -20,25 +20,24 @@ var Generator = function() {
// Style to integrates in the output
this.styles = ["website"];
+
+ // Templates
+ this.templates = {};
};
util.inherits(Generator, BaseGenerator);
// Prepare the genertor
Generator.prototype.prepare = function() {
- var that = this;
-
return BaseGenerator.prototype.prepare.apply(this)
- .then(function() {
- return that.prepareStyles();
- })
- .then(function() {
- return that.prepareTemplates();
- });
+ .then(this.prepareStyles)
+ .then(this.prepareTemplates)
+ .then(this.prepareTemplateEngine);
};
// Prepare all styles
Generator.prototype.prepareStyles = function() {
var that = this;
+
this.styles = _.chain(this.styles)
.map(function(style) {
var stylePath = that.options.styles[style];
@@ -49,28 +48,33 @@ Generator.prototype.prepareStyles = function() {
})
.compact()
.value();
+
+ return Q();
};
-// Prepare template engine
+// Prepare templates
Generator.prototype.prepareTemplates = function() {
- this.pageTemplate = this.plugins.template("site:page") || path.resolve(this.options.theme, 'templates/website/page.html');
- this.langsTemplate = this.plugins.template("site:langs") || path.resolve(this.options.theme, 'templates/website/langs.html');
- this.glossaryTemplate = this.plugins.template("site:glossary") || path.resolve(this.options.theme, 'templates/website/glossary.html');
-
- var folders = _.chain(
- [
- this.pageTemplate, this.langsTemplate, this.glossaryTemplate
- ])
- .map(path.dirname)
- .uniq()
- .value();
-
- this.env = new nunjucks.Environment(
- new nunjucks.FileSystemLoader(folders),
- {
- autoescape: true
- }
- );
+ this.templates["page"] = this.plugins.template("site:page") || path.resolve(this.options.theme, 'templates/website/page.html');
+ this.templates["langs"] = this.plugins.template("site:langs") || path.resolve(this.options.theme, 'templates/website/langs.html');
+ this.templates["glossary"] = this.plugins.template("site:glossary") || path.resolve(this.options.theme, 'templates/website/glossary.html');
+
+ return Q();
+};
+
+// Prepare template engine
+Generator.prototype.prepareTemplateEngine = function() {
+ var folders = _.chain(this.templates)
+ .values()
+ .map(path.dirname)
+ .uniq()
+ .value();
+
+ this.env = new nunjucks.Environment(
+ new nunjucks.FileSystemLoader(folders),
+ {
+ autoescape: true
+ }
+ );
// Add filter
this.env.addFilter("contentLink", this.book.contentLink.bind(this.book));
@@ -82,7 +86,7 @@ Generator.prototype.prepareTemplates = function() {
this.env.addExtension('AutoEscapeExtension', new AutoEscapeExtension(this.env));
this.env.addExtension('FilterExtension', new FilterExtension(this.env));
- return Q();
+ return Q();
};
// Finis generation
@@ -105,7 +109,7 @@ Generator.prototype.writeParsedFile = function(page) {
return that.normalizePage(page)
.then(function() {
- return that._writeTemplate(that.pageTemplate, {
+ return that._writeTemplate(that.templates["page"], {
progress: page.progress,
_input: page.path,
@@ -121,7 +125,7 @@ Generator.prototype.writeParsedFile = function(page) {
Generator.prototype.langsIndex = function(langs) {
var that = this;
- return this._writeTemplate(this.langsTemplate, {
+ return this._writeTemplate(this.templates["langs"], {
langs: langs
}, path.join(this.options.output, "index.html"));
};
@@ -133,7 +137,7 @@ Generator.prototype.writeGlossary = function() {
// No glossary
if (this.book.glossary.length == 0) return Q();
- return this._writeTemplate(this.glossaryTemplate, {}, path.join(this.options.output, "GLOSSARY.html"));
+ return this._writeTemplate(this.templates["glossary"], {}, path.join(this.options.output, "GLOSSARY.html"));
};
// Write the search index
diff --git a/lib/utils/string.js b/lib/utils/string.js
new file mode 100644
index 0000000..54c4c66
--- /dev/null
+++ b/lib/utils/string.js
@@ -0,0 +1,26 @@
+var _ = require("lodash");
+
+function escapeShellArg(arg) {
+ var ret = '';
+
+ ret = arg.replace(/"/g, '\\"');
+
+ return "\"" + ret + "\"";
+}
+
+function optionsToShellArgs(options) {
+ return _.chain(options)
+ .map(function(value, key) {
+ if (value == null || value === false) return null;
+ if (value === true) return key;
+ return key+"="+escapeShellArg(value);
+ })
+ .compact()
+ .value()
+ .join(" ");
+}
+
+module.exports = {
+ escapeShellArg: escapeShellArg,
+ optionsToShellArgs: optionsToShellArgs
+};
diff --git a/test/pdf.js b/test/pdf.js
new file mode 100644
index 0000000..9b5cf42
--- /dev/null
+++ b/test/pdf.js
@@ -0,0 +1,15 @@
+var path = require('path');
+var _ = require('lodash');
+var assert = require('assert');
+
+var fs = require("fs");
+var fsUtil = require("../lib/utils/fs");
+
+
+describe('PDF Generator', function () {
+ it('should correctly generate a pdf', function(done) {
+ testGeneration(books[1], "pdf", function(output) {
+ assert(fs.existsSync(path.join(output, "book.pdf")));
+ }, done);
+ });
+});