summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorSamy Pessé <samypesse@gmail.com>2015-10-08 20:58:57 +0200
committerSamy Pessé <samypesse@gmail.com>2015-10-08 20:58:57 +0200
commit4ae5b2e05001df9bdf8c5b64f8a82725e14d6414 (patch)
tree2e4397d33f90ee7bf1034ec4dea1237e611aff36 /lib
parentce1e0e187339976fc3cae7ef70bcfdd0eaf68981 (diff)
parent4369bd8f5e39b1b3563e527fb78c16fc3b34b597 (diff)
downloadgitbook-4ae5b2e05001df9bdf8c5b64f8a82725e14d6414.zip
gitbook-4ae5b2e05001df9bdf8c5b64f8a82725e14d6414.tar.gz
gitbook-4ae5b2e05001df9bdf8c5b64f8a82725e14d6414.tar.bz2
Merge pull request #959 from GitbookIO/improvements/ebooks
Improvements for ebooks
Diffstat (limited to 'lib')
-rw-r--r--lib/configuration.js5
-rw-r--r--lib/generators/ebook.js144
-rw-r--r--lib/generators/website.js76
-rw-r--r--lib/utils/fs.js47
4 files changed, 152 insertions, 120 deletions
diff --git a/lib/configuration.js b/lib/configuration.js
index 3b6b47b..7488fae 100644
--- a/lib/configuration.js
+++ b/lib/configuration.js
@@ -1,5 +1,6 @@
var _ = require('lodash');
var Q = require('q');
+var fs = require('fs');
var path = require('path');
var semver = require('semver');
@@ -284,10 +285,10 @@ Configuration.DEFAULT = {
},
// Header HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
- 'headerTemplate': '',
+ 'headerTemplate': null,
// Footer HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
- 'footerTemplate': ''
+ 'footerTemplate': null
}
};
diff --git a/lib/generators/ebook.js b/lib/generators/ebook.js
index e6a33d3..96775b1 100644
--- a/lib/generators/ebook.js
+++ b/lib/generators/ebook.js
@@ -1,12 +1,13 @@
-var util = require("util");
-var path = require("path");
-var Q = require("q");
-var _ = require("lodash");
-var exec = require("child_process").exec;
+var util = require('util');
+var path = require('path');
+var Q = require('q');
+var _ = require('lodash');
+var juice = require('juice');
+var exec = require('child_process').exec;
-var fs = require("../utils/fs");
-var stringUtils = require("../utils/string");
-var BaseGenerator = require("./website");
+var fs = require('../utils/fs');
+var stringUtils = require('../utils/string');
+var BaseGenerator = require('./website');
var Generator = function(book, format) {
BaseGenerator.apply(this, arguments);
@@ -15,10 +16,10 @@ var Generator = function(book, format) {
this.ebookFormat = format;
// Resources namespace
- this.namespace = "ebook";
+ this.namespace = 'ebook';
// Styles to use
- this.styles = _.compact(["print", "ebook", this.ebookFormat]);
+ this.styles = _.compact(['print', 'ebook', this.ebookFormat]);
// Convert images (svg -> png)
this.convertImages = true;
@@ -26,9 +27,9 @@ var Generator = function(book, format) {
util.inherits(Generator, BaseGenerator);
Generator.prototype.prepareTemplates = function() {
- this.templates.page = this.book.plugins.template("ebook:page") || path.resolve(this.options.theme, "templates/ebook/page.html");
- this.templates.summary = this.book.plugins.template("ebook:summary") || path.resolve(this.options.theme, "templates/ebook/summary.html");
- this.templates.glossary = this.book.plugins.template("ebook:glossary") || path.resolve(this.options.theme, "templates/ebook/glossary.html");
+ this.templates.page = this.book.plugins.template('ebook:page') || path.resolve(this.options.theme, 'templates/ebook/page.html');
+ this.templates.summary = this.book.plugins.template('ebook:summary') || path.resolve(this.options.theme, 'templates/ebook/summary.html');
+ this.templates.glossary = this.book.plugins.template('ebook:glossary') || path.resolve(this.options.theme, 'templates/ebook/glossary.html');
return Q();
};
@@ -37,8 +38,39 @@ Generator.prototype.prepareTemplates = function() {
Generator.prototype.writeSummary = function() {
var that = this;
- that.book.log.info.ln("write SUMMARY.html");
- return this._writeTemplate(this.templates.summary, {}, path.join(this.options.output, "SUMMARY.html"));
+ that.book.log.info.ln('write SUMMARY.html');
+ return this._writeTemplate(this.templates.summary, {}, path.join(this.options.output, 'SUMMARY.html'));
+};
+
+// Return template for footer/header with inlined css
+Generator.prototype.getPDFTemplate = function(id) {
+ var tpl = this.options.pdf[id+'Template'];
+ var defaultTpl = path.resolve(this.options.theme, 'templates/ebook/'+id+'.html');
+ var defaultCSS = path.resolve(this.options.theme, 'assets/pdf.css');
+
+ // Default template from theme
+ if (!tpl && fs.existsSync(defaultTpl)) {
+ tpl = fs.readFileSync(defaultTpl, { encoding: 'utf-8' });
+ }
+
+ // Inline CSS using juice
+ var stylesheets = [];
+
+ // From theme
+ if (fs.existsSync(defaultCSS)) {
+ stylesheets.push(fs.readFileSync(defaultCSS, { encoding: 'utf-8' }));
+ }
+
+ // Custom PDF style
+ if (this.styles.pdf) {
+ stylesheets.push(fs.readFileSync(this.book.resolve(this.styles.pdf), { encoding: 'utf-8' }));
+ }
+
+ tpl = juice(tpl, {
+ extraCss: stylesheets.concat('/n')
+ });
+
+ return tpl;
};
Generator.prototype.finish = function() {
@@ -52,70 +84,70 @@ Generator.prototype.finish = function() {
.then(function() {
if (!that.ebookFormat) return Q();
- if (!that.options.cover && fs.existsSync(path.join(that.options.output, "cover.jpg"))) {
- that.options.cover = path.join(that.options.output, "cover.jpg");
+ if (!that.options.cover && fs.existsSync(path.join(that.options.output, 'cover.jpg'))) {
+ that.options.cover = path.join(that.options.output, 'cover.jpg');
}
var d = Q.defer();
var _options = {
- "--cover": that.options.cover,
- "--title": that.options.title,
- "--comments": that.options.description,
- "--isbn": that.options.isbn,
- "--authors": that.options.author,
- "--language": that.options.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
+ '--cover': that.options.cover,
+ '--title': that.options.title,
+ '--comments': that.options.description,
+ '--isbn': that.options.isbn,
+ '--authors': that.options.author,
+ '--language': that.options.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.ebookFormat == "pdf") {
+ if (that.ebookFormat == 'pdf') {
var pdfOptions = that.options.pdf;
_.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": String(pdfOptions.headerTemplate) || "<p class='header'><span>"+that.options.title+"</span></p>",
- "--pdf-footer-template": String(pdfOptions.footerTemplate) || "<p class='footer'><span>_SECTION_</span> <span style='float:right;'>_PAGENUM_</span></p>"
+ '--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')
});
- } else if (that.ebookFormat == "epub") {
+ } else if (that.ebookFormat == 'epub') {
_.extend(_options, {
- "--dont-split-on-page-breaks": true
+ '--dont-split-on-page-breaks': true
});
}
var command = [
- "ebook-convert",
- path.join(that.options.output, "SUMMARY.html"),
- path.join(that.options.output, "index."+that.ebookFormat),
+ 'ebook-convert',
+ path.join(that.options.output, 'SUMMARY.html'),
+ path.join(that.options.output, 'index.'+that.ebookFormat),
stringUtils.optionsToShellArgs(_options)
- ].join(" ");
+ ].join(' ');
- that.book.log.info("start conversion to", that.ebookFormat, "....");
+ that.book.log.info('start conversion to', that.ebookFormat, '....');
var child = exec(command, function (error, stdout) {
if (error) {
that.book.log.info.fail();
if (error.code == 127) {
- error.message = "Need to install ebook-convert from Calibre";
+ error.message = 'Need to install ebook-convert from Calibre';
} else {
- error.message = error.message + " "+stdout;
+ error.message = error.message + ' '+stdout;
}
return d.reject(error);
}
@@ -124,11 +156,11 @@ Generator.prototype.finish = function() {
d.resolve();
});
- child.stdout.on("data", function (data) {
+ child.stdout.on('data', function (data) {
that.book.log.debug(data);
});
- child.stderr.on("data", function (data) {
+ child.stderr.on('data', function (data) {
that.book.log.debug(data);
});
diff --git a/lib/generators/website.js b/lib/generators/website.js
index e1a3cce..18ec65d 100644
--- a/lib/generators/website.js
+++ b/lib/generators/website.js
@@ -1,18 +1,18 @@
-var util = require("util");
-var path = require("path");
-var Q = require("q");
-var _ = require("lodash");
+var util = require('util');
+var path = require('path');
+var Q = require('q');
+var _ = require('lodash');
-var nunjucks = require("nunjucks");
-var AutoEscapeExtension = require("nunjucks-autoescape")(nunjucks);
-var FilterExtension = require("nunjucks-filter")(nunjucks);
+var nunjucks = require('nunjucks');
+var AutoEscapeExtension = require('nunjucks-autoescape')(nunjucks);
+var FilterExtension = require('nunjucks-filter')(nunjucks);
-var fs = require("../utils/fs");
-var BaseGenerator = require("../generator");
-var links = require("../utils/links");
-var i18n = require("../utils/i18n");
+var fs = require('../utils/fs');
+var BaseGenerator = require('../generator');
+var links = require('../utils/links');
+var i18n = require('../utils/i18n');
-var pkg = require("../../package.json");
+var pkg = require('../../package.json');
var Generator = function() {
BaseGenerator.apply(this, arguments);
@@ -21,10 +21,10 @@ var Generator = function() {
this.revision = new Date();
// Resources namespace
- this.namespace = "website";
+ this.namespace = 'website';
// Style to integrates in the output
- this.styles = ["website"];
+ this.styles = ['website'];
// Convert images (svg -> png)
this.convertImages = false;
@@ -63,9 +63,9 @@ Generator.prototype.prepareStyles = function() {
// Prepare templates
Generator.prototype.prepareTemplates = function() {
- this.templates.page = this.book.plugins.template("site:page") || path.resolve(this.options.theme, "templates/website/page.html");
- this.templates.langs = this.book.plugins.template("site:langs") || path.resolve(this.options.theme, "templates/website/langs.html");
- this.templates.glossary = this.book.plugins.template("site:glossary") || path.resolve(this.options.theme, "templates/website/glossary.html");
+ this.templates.page = this.book.plugins.template('site:page') || path.resolve(this.options.theme, 'templates/website/page.html');
+ this.templates.langs = this.book.plugins.template('site:langs') || path.resolve(this.options.theme, 'templates/website/langs.html');
+ this.templates.glossary = this.book.plugins.template('site:glossary') || path.resolve(this.options.theme, 'templates/website/glossary.html');
return Q();
};
@@ -79,7 +79,7 @@ Generator.prototype.prepareTemplateEngine = function() {
var language = that.book.config.normalizeLanguage();
if (!i18n.hasLocale(language)) {
- that.book.log.warn.ln("Language '"+language+"' is not available as a layout locales (en, "+i18n.getLocales().join(", ")+")");
+ that.book.log.warn.ln('Language "'+language+'" is not available as a layout locales (en, '+i18n.getLocales().join(', ')+')');
}
var folders = _.chain(that.templates)
@@ -96,14 +96,14 @@ Generator.prototype.prepareTemplateEngine = function() {
);
// Add filter
- that.env.addFilter("contentLink", that.book.contentLink.bind(that.book));
- that.env.addFilter("lvl", function(lvl) {
- return lvl.split(".").length;
+ that.env.addFilter('contentLink', that.book.contentLink.bind(that.book));
+ that.env.addFilter('lvl', function(lvl) {
+ return lvl.split('.').length;
});
// Add extension
- that.env.addExtension("AutoEscapeExtension", new AutoEscapeExtension(that.env));
- that.env.addExtension("FilterExtension", new FilterExtension(that.env));
+ that.env.addExtension('AutoEscapeExtension', new AutoEscapeExtension(that.env));
+ that.env.addExtension('FilterExtension', new FilterExtension(that.env));
});
};
@@ -122,20 +122,20 @@ Generator.prototype.convertFile = function(input) {
return that.book.parsePage(input, {
convertImages: that.convertImages,
interpolateTemplate: function(page) {
- return that.callHook("page:before", page);
+ return that.callHook('page:before', page);
},
interpolateContent: function(page) {
- return that.callHook("page", page);
+ return that.callHook('page', page);
}
})
.then(function(page) {
var relativeOutput = that.book.contentPath(page.path);
var output = path.join(that.options.output, relativeOutput);
- var basePath = path.relative(path.dirname(output), that.options.output) || ".";
- if (process.platform === "win32") basePath = basePath.replace(/\\/g, "/");
+ var basePath = path.relative(path.dirname(output), that.options.output) || '.';
+ if (process.platform === 'win32') basePath = basePath.replace(/\\/g, '/');
- that.book.log.debug.ln("write parsed file", page.path, "to", relativeOutput);
+ that.book.log.debug.ln('write parsed file', page.path, 'to', relativeOutput);
return that._writeTemplate(that.templates.page, {
progress: page.progress,
@@ -144,7 +144,7 @@ Generator.prototype.convertFile = function(input) {
content: page.sections,
basePath: basePath,
- staticBase: links.join(basePath, "gitbook")
+ staticBase: links.join(basePath, 'gitbook')
}, output);
});
};
@@ -155,7 +155,7 @@ Generator.prototype.writeLangsIndex = function() {
return this._writeTemplate(this.templates.langs, {
langs: this.book.langs
- }, path.join(this.options.output, "index.html"));
+ }, path.join(this.options.output, 'index.html'));
};
// Write glossary
@@ -163,7 +163,7 @@ Generator.prototype.writeGlossary = function() {
// No glossary
if (this.book.glossary.length === 0) return Q();
- return this._writeTemplate(this.templates.glossary, {}, path.join(this.options.output, "GLOSSARY.html"));
+ return this._writeTemplate(this.templates.glossary, {}, path.join(this.options.output, 'GLOSSARY.html'));
};
// Convert a page into a normalized data set
@@ -180,7 +180,7 @@ Generator.prototype.normalizePage = function(page) {
return Q()
.then(function() {
- return _callHook("page");
+ return _callHook('page');
})
.then(function() {
return page;
@@ -222,10 +222,10 @@ Generator.prototype._writeTemplate = function(tpl, options, output, interpolate)
options: that.options,
- basePath: ".",
- staticBase: path.join(".", "gitbook"),
+ basePath: '.',
+ staticBase: path.join('.', 'gitbook'),
- "__": that.book.i18n.bind(that.book)
+ '__': that.book.i18n.bind(that.book)
}, options)
);
})
@@ -244,15 +244,15 @@ Generator.prototype.copyAssets = function() {
// Copy gitbook assets
return fs.copy(
- path.join(that.options.theme, "assets"),
- path.join(that.options.output, "gitbook")
+ path.join(that.options.theme, 'assets/'+this.namespace),
+ path.join(that.options.output, 'gitbook')
)
// Copy plugins assets
.then(function() {
return Q.all(
_.map(that.book.plugins.list, function(plugin) {
- var pluginAssets = path.join(that.options.output, "gitbook/plugins/", plugin.name);
+ var pluginAssets = path.join(that.options.output, 'gitbook/plugins/', plugin.name);
return plugin.copyAssets(pluginAssets, that.namespace);
})
);
diff --git a/lib/utils/fs.js b/lib/utils/fs.js
index 0a9d846..716e3a0 100644
--- a/lib/utils/fs.js
+++ b/lib/utils/fs.js
@@ -1,10 +1,10 @@
-var _ = require("lodash");
-var Q = require("q");
-var tmp = require("tmp");
-var path = require("path");
-var fs = require("graceful-fs");
-var fsExtra = require("fs-extra");
-var Ignore = require("fstream-ignore");
+var _ = require('lodash');
+var Q = require('q');
+var tmp = require('tmp');
+var path = require('path');
+var fs = require('graceful-fs');
+var fsExtra = require('fs-extra');
+var Ignore = require('fstream-ignore');
var fsUtils = {
tmp: {
@@ -33,7 +33,7 @@ var fsUtils = {
existsSync: fs.existsSync.bind(fs),
readFileSync: fs.readFileSync.bind(fs),
clean: cleanFolder,
- getUniqueFilename: getUniqueFilename,
+ getUniqueFilename: getUniqueFilename
};
// Write a file
@@ -57,14 +57,14 @@ function writeStream(filename, st) {
var wstream = fs.createWriteStream(filename);
- wstream.on("finish", function () {
+ wstream.on('finish', function () {
d.resolve();
});
- wstream.on("error", function (err) {
+ wstream.on('error', function (err) {
d.reject(err);
});
- st.on("error", function(err) {
+ st.on('error', function(err) {
d.reject(err);
});
@@ -77,7 +77,7 @@ function writeStream(filename, st) {
function getUniqueFilename(base, filename) {
if (!filename) {
filename = base;
- base = "/";
+ base = '/';
}
filename = path.resolve(base, filename);
@@ -87,9 +87,8 @@ function getUniqueFilename(base, filename) {
var _filename = filename+ext;
var i = 0;
- while (1) {
- if (!fs.existsSync(filename)) break;
- _filename = filename+"_"+i+ext;
+ while (fs.existsSync(filename)) {
+ _filename = filename+'_'+i+ext;
i = i + 1;
}
@@ -115,20 +114,20 @@ function listFiles(root, options) {
});
// Add extra rules to ignore common folders
- ig.addIgnoreRules(options.ignoreRules, "__custom_stuff");
+ ig.addIgnoreRules(options.ignoreRules, '__custom_stuff');
// Push each file to our list
- ig.on("child", function (c) {
+ ig.on('child', function (c) {
files.push(
- c.path.substr(c.root.path.length + 1) + (c.props.Directory === true ? "/" : "")
+ c.path.substr(c.root.path.length + 1) + (c.props.Directory === true ? '/' : '')
);
});
- ig.on("end", function() {
+ ig.on('end', function() {
// Normalize paths on Windows
- if(process.platform === "win32") {
+ if(process.platform === 'win32') {
return d.resolve(files.map(function(file) {
- return file.replace(/\\/g, "/");
+ return file.replace(/\\/g, '/');
}));
}
@@ -136,7 +135,7 @@ function listFiles(root, options) {
return d.resolve(files);
});
- ig.on("error", d.reject);
+ ig.on('error', d.reject);
return d.promise;
}
@@ -150,8 +149,8 @@ function cleanFolder(root) {
ignoreFiles: [],
ignoreRules: [
// Skip Git and SVN stuff
- ".git/",
- ".svn/"
+ '.git/',
+ '.svn/'
]
})
.then(function(files) {