diff options
author | Samy Pessé <samypesse@gmail.com> | 2015-01-23 00:16:46 +0100 |
---|---|---|
committer | Samy Pessé <samypesse@gmail.com> | 2015-01-23 00:16:46 +0100 |
commit | 19f1d31758727d09a173037583d8ed765d539a33 (patch) | |
tree | 2e47ef2542e5ff0f42e15a034cb0d35af7a83331 | |
parent | ee2420cbcca41f3c33a243084b11fd9946b87ed1 (diff) | |
download | gitbook-19f1d31758727d09a173037583d8ed765d539a33.zip gitbook-19f1d31758727d09a173037583d8ed765d539a33.tar.gz gitbook-19f1d31758727d09a173037583d8ed765d539a33.tar.bz2 |
Add base for ebook generator
-rw-r--r-- | lib/generators/ebook.js | 114 | ||||
-rw-r--r-- | lib/generators/index.js | 7 | ||||
-rw-r--r-- | lib/generators/website.js | 66 | ||||
-rw-r--r-- | lib/utils/string.js | 26 | ||||
-rw-r--r-- | test/pdf.js | 15 |
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); + }); +}); |