summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSamy Pesse <samypesse@gmail.com>2016-02-12 23:57:43 +0100
committerSamy Pesse <samypesse@gmail.com>2016-02-12 23:57:43 +0100
commit4c6717e23488656686f276aa2b40ce1d1c7641f8 (patch)
treec728f106224cedc836b83b06609011990eac4f5a
parent39b6562d1445e9a6c43a377d2a978eefa6458755 (diff)
downloadgitbook-4c6717e23488656686f276aa2b40ce1d1c7641f8.zip
gitbook-4c6717e23488656686f276aa2b40ce1d1c7641f8.tar.gz
gitbook-4c6717e23488656686f276aa2b40ce1d1c7641f8.tar.bz2
Add conversion of svg to png for assets inliner
-rw-r--r--lib/output/assets-inliner.js42
-rw-r--r--lib/output/base.js11
-rw-r--r--lib/output/ebook.js14
-rw-r--r--lib/output/folder.js10
-rw-r--r--lib/page/html.js21
-rw-r--r--lib/page/index.js18
-rw-r--r--lib/utils/command.js2
-rw-r--r--lib/utils/fs.js1
-rw-r--r--lib/utils/images.js5
-rw-r--r--lib/utils/location.js31
-rw-r--r--test/all.js1
-rw-r--r--test/assets-inliner.js26
-rw-r--r--test/page.js23
13 files changed, 182 insertions, 23 deletions
diff --git a/lib/output/assets-inliner.js b/lib/output/assets-inliner.js
new file mode 100644
index 0000000..5f33956
--- /dev/null
+++ b/lib/output/assets-inliner.js
@@ -0,0 +1,42 @@
+var _ = require('lodash');
+var util = require('util');
+var path = require('path');
+
+var FolderOutput = require('./folder');
+var imagesUtil = require('../utils/images');
+
+/*
+Utility mixin to inline all the assets in a book:
+ - Outline <svg> tags
+ - Convert svg images as png
+ - Download remote images
+*/
+
+function AssetsInliner() {
+ FolderOutput.apply(this, arguments);
+}
+util.inherits(AssetsInliner, FolderOutput);
+
+// Output a SVG buffer as a file
+AssetsInliner.prototype.onOutputSVG = function(page, svg) {
+ this.log.debug.ln('output svg from', page.path);
+ var filename = _.uniqueId('svg_') + '.png';
+
+ return imagesUtil.convertSVGBufferToPNG(svg, this.resolve(filename))
+ .thenResolve('/' + filename);
+};
+
+// Output an image as a file
+AssetsInliner.prototype.onOutputImage = function(page, imgFile) {
+ if (path.extname(imgFile).toLowerCase() != '.svg') {
+ return imgFile;
+ }
+
+ // Convert SVG to PNG
+ var filename = _.uniqueId('svg_') + '.png';
+ return imagesUtil.convertSVGToPNG(page.resolve(imgFile), this.resolve(filename))
+ .thenResolve('/' + filename);
+};
+
+
+module.exports = AssetsInliner;
diff --git a/lib/output/base.js b/lib/output/base.js
index d301b14..dd62cff 100644
--- a/lib/output/base.js
+++ b/lib/output/base.js
@@ -84,7 +84,7 @@ Output.prototype.prepare = function() {
// Write a page (parsable file), ex: markdown, etc
Output.prototype.onPage = function(page) {
-
+ return page.parse(this);
};
// Copy an asset file (non-parsable), ex: images, etc
@@ -100,15 +100,20 @@ Output.prototype.onRelativeLink = function(currentPage, href) {
return href;
};
-// Output a SVG as a file
+// Output a SVG buffer as a file
Output.prototype.onOutputSVG = function(page, svg) {
return null;
};
+// Output an image as a file
+Output.prototype.onOutputImage = function(page, imgFile) {
+ // Don't replace it
+ return imgFile;
+};
+
// Finish the generation
Output.prototype.finish = function() {
};
-
module.exports = Output;
diff --git a/lib/output/ebook.js b/lib/output/ebook.js
new file mode 100644
index 0000000..b968006
--- /dev/null
+++ b/lib/output/ebook.js
@@ -0,0 +1,14 @@
+var util = require('util');
+
+var WebsiteOutput = require('./website');
+var AssetsInliner = require('./assets-inliner');
+
+function EbookOutput() {
+ WebsiteOutput.apply(this, arguments);
+ AssetsInliner.call(this);
+}
+util.inherits(EbookOutput, AssetsInliner);
+util.inherits(EbookOutput, WebsiteOutput);
+
+
+module.exports = EbookOutput;
diff --git a/lib/output/folder.js b/lib/output/folder.js
index 7e6ddf0..4b1d9fa 100644
--- a/lib/output/folder.js
+++ b/lib/output/folder.js
@@ -5,7 +5,6 @@ var path = require('path');
var Output = require('./base');
var fs = require('../utils/fs');
var pathUtil = require('../utils/path');
-var imagesUtil = require('../utils/images');
var Promise = require('../utils/promise');
/*
@@ -26,15 +25,6 @@ FolderOutput.prototype.onAsset = function(filename) {
);
};
-// Output a SVG as a file
-Output.prototype.onOutputSVG = function(page, svg) {
- this.log.debug.ln('output svg from', page.path);
- var filename = _.uniqueId('svg_') + '.png';
-
- return imagesUtil.convertSVGBufferToPNG(svg, this.resolve(filename))
- .thenResolve('/' + filename);
-};
-
// Prepare the generation by creating the output folder
FolderOutput.prototype.prepare = function() {
return fs.mkdirp(this.root());
diff --git a/lib/page/html.js b/lib/page/html.js
index 948462a..255bf48 100644
--- a/lib/page/html.js
+++ b/lib/page/html.js
@@ -19,11 +19,12 @@ function HTMLPipeline(htmlString, opts) {
_.bindAll(this);
this.opts = _.defaults(opts || {}, {
- convertImages: true,
-
// Calcul new href for a relative link
onRelativeLink: _.identity,
+ // Output an image
+ onImage: _.identity,
+
// Output a svg, if returns null the svg is kept inlined
onOutputSVG: _.constant(null)
});
@@ -56,7 +57,22 @@ HTMLPipeline.prototype.normalizeLinks = function() {
// External links
$a.attr('target', '_blank');
}
+ });
+};
+
+// Normalize images
+HTMLPipeline.prototype.normalizeImages = function() {
+ var that = this;
+
+ var $imgs = this.$('img');
+
+ return Promise.serie($imgs, function(img) {
+ var $img = that.$(img);
+ return Promise(that.opts.onImage($img.attr('src')))
+ .then(function(filename) {
+ $img.attr('src', filename);
+ });
});
};
@@ -101,6 +117,7 @@ HTMLPipeline.prototype.output = function() {
return Promise()
.then(this.normalizeLinks)
+ .then(this.normalizeImages)
.then(this.addHeadingIDs)
.then(this.outlineSVG)
.then(function() {
diff --git a/lib/page/index.js b/lib/page/index.js
index 282cffe..e96f89b 100644
--- a/lib/page/index.js
+++ b/lib/page/index.js
@@ -4,6 +4,7 @@ var parsers = require('gitbook-parsers');
var error = require('../utils/error');
var pathUtil = require('../utils/path');
+var location = require('../utils/location');
var HTMLPipeline = require('./html');
/*
@@ -62,6 +63,21 @@ Page.prototype.outputPath = function(ext) {
return output;
};
+// Resolve a filename relative to this page
+// It returns a path relative to the book root folder
+Page.prototype.resolveLocal = function() {
+ var dir = path.dirname(this.path);
+ var file = path.join.apply(path, _.toArray(arguments));
+
+ return location.toAbsolute(file, dir, '');
+};
+
+// Resolve a filename relative to this page
+// It returns an absolute path for the FS
+Page.prototype.resolve = function() {
+ return this.book.resolve(this.resolveLocal.apply(this, arguments));
+};
+
// Update content of the page
Page.prototype.update = function(content) {
this.content = content;
@@ -118,8 +134,8 @@ Page.prototype.parse = function(output) {
// Normalize HTML output
.then(function() {
var pipelineOpts = {
- // Replace links to page of summary
onRelativeLink: _.partial(output.onRelativeLink, that),
+ onImage: _.partial(output.onOutputImage, that),
onOutputSVG: _.partial(output.onOutputSVG, that)
};
var pipeline = new HTMLPipeline(that.content, pipelineOpts);
diff --git a/lib/utils/command.js b/lib/utils/command.js
index 4269d6c..c3e33c7 100644
--- a/lib/utils/command.js
+++ b/lib/utils/command.js
@@ -19,7 +19,7 @@ function spawn(command, args, options) {
return Promise.reject(new Error('Command execution is not possible on this platform'));
}
- var d = Promise.deferred();
+ var d = Promise.defer();
var child = childProcess.spawn(command, args, options);
child.on('error', function(error) {
diff --git a/lib/utils/fs.js b/lib/utils/fs.js
index 7745448..ea9546b 100644
--- a/lib/utils/fs.js
+++ b/lib/utils/fs.js
@@ -61,6 +61,7 @@ function genTmpFile(opts) {
module.exports = {
exists: fileExists,
+ existsSync: fs.existsSync,
mkdirp: Promise.nfbind(mkdirp),
readFile: Promise.nfbind(fs.readFile),
writeFile: Promise.nfbind(fs.writeFile),
diff --git a/lib/utils/images.js b/lib/utils/images.js
index 45bc0b0..8169b06 100644
--- a/lib/utils/images.js
+++ b/lib/utils/images.js
@@ -1,5 +1,3 @@
-var fs = require('fs');
-
var Promise = require('./promise');
var command = require('./command');
var fs = require('./fs');
@@ -38,6 +36,5 @@ function convertSVGBufferToPNG(buf, dest) {
module.exports = {
convertSVGToPNG: convertSVGToPNG,
- convertSVGBufferToPNG: convertSVGBufferToPNG,
- INVALID: ['.svg']
+ convertSVGBufferToPNG: convertSVGBufferToPNG
}; \ No newline at end of file
diff --git a/lib/utils/location.js b/lib/utils/location.js
index efe1425..3f3cb37 100644
--- a/lib/utils/location.js
+++ b/lib/utils/location.js
@@ -1,4 +1,5 @@
var url = require('url');
+var path = require('path');
// Is the url an external url
function isExternal(href) {
@@ -24,8 +25,36 @@ function isAnchor(href) {
}
}
+// Normalize a path to be a link
+function normalize(s) {
+ return s.replace(/\\/g, '/');
+}
+
+// Convert relative to absolute path
+// dir: directory parent of the file currently in rendering process
+// outdir: directory parent from the html output
+function toAbsolute(_href, dir, outdir) {
+ outdir = outdir == undefined? dir : outdir;
+
+ if (isExternal(_href)) return _href;
+
+ // Path "_href" inside the base folder
+ var hrefInRoot = path.normalize(path.join(dir, _href));
+ if (_href[0] == '/') hrefInRoot = path.normalize(_href.slice(1));
+
+ // Make it relative to output
+ _href = path.relative(outdir, hrefInRoot);
+
+ // Normalize windows paths
+ _href = normalize(_href);
+
+ return _href;
+}
+
module.exports = {
isExternal: isExternal,
isRelative: isRelative,
- isAnchor: isAnchor
+ isAnchor: isAnchor,
+ normalize: normalize,
+ toAbsolute: toAbsolute
};
diff --git a/test/all.js b/test/all.js
index 08be793..242324b 100644
--- a/test/all.js
+++ b/test/all.js
@@ -13,3 +13,4 @@ require('./template');
// Output
require('./output-json');
+require('./assets-inliner');
diff --git a/test/assets-inliner.js b/test/assets-inliner.js
new file mode 100644
index 0000000..479d788
--- /dev/null
+++ b/test/assets-inliner.js
@@ -0,0 +1,26 @@
+var mock = require('./mock');
+var AssetsInliner = require('../lib/output/assets-inliner');
+
+describe('Assets Inliner Output', function() {
+
+ describe('SVG', function() {
+ var output;
+
+ before(function() {
+ return mock.outputDefaultBook(AssetsInliner, {
+ 'README.md': '![image](test.svg)',
+ 'test.svg': '<svg width="100" height="100"><circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" /></svg>'
+ })
+ .then(function(_output) {
+ output = _output;
+ });
+ });
+
+ it('should correctly convert to PNG', function() {
+ var readme = output.book.getPage('README.md');
+ console.log(readme.content)
+ });
+
+ });
+});
+
diff --git a/test/page.js b/test/page.js
index 01c6466..9007c45 100644
--- a/test/page.js
+++ b/test/page.js
@@ -7,7 +7,8 @@ describe('Page', function() {
before(function() {
return mock.setupDefaultBook({
'heading.md': '# Hello\n\n## World',
- 'links.md': '[link](hello.md) [readme](README.md)'
+ 'links.md': '[link](hello.md) [readme](README.md)',
+ 'folder/paths.md': ''
})
.then(function(_book) {
book = _book;
@@ -17,6 +18,26 @@ describe('Page', function() {
});
});
+ describe('.resolveLocal', function() {
+ it('should correctly resolve path to file', function() {
+ var page = book.addPage('heading.md');
+
+ page.resolveLocal('test.png').should.equal('test.png');
+ page.resolveLocal('/test.png').should.equal('test.png');
+ page.resolveLocal('test/hello.png').should.equal('test/hello.png');
+ page.resolveLocal('/test/hello.png').should.equal('test/hello.png');
+ });
+
+ it('should correctly resolve path to file (2)', function() {
+ var page = book.addPage('folder/paths.md');
+
+ page.resolveLocal('test.png').should.equal('folder/test.png');
+ page.resolveLocal('/test.png').should.equal('test.png');
+ page.resolveLocal('test/hello.png').should.equal('folder/test/hello.png');
+ page.resolveLocal('/test/hello.png').should.equal('test/hello.png');
+ });
+ });
+
describe('Headings', function() {
it('should add a default ID to headings', function() {
var page = book.addPage('heading.md');