diff options
-rw-r--r-- | lib/output/assets-inliner.js | 42 | ||||
-rw-r--r-- | lib/output/base.js | 11 | ||||
-rw-r--r-- | lib/output/ebook.js | 14 | ||||
-rw-r--r-- | lib/output/folder.js | 10 | ||||
-rw-r--r-- | lib/page/html.js | 21 | ||||
-rw-r--r-- | lib/page/index.js | 18 | ||||
-rw-r--r-- | lib/utils/command.js | 2 | ||||
-rw-r--r-- | lib/utils/fs.js | 1 | ||||
-rw-r--r-- | lib/utils/images.js | 5 | ||||
-rw-r--r-- | lib/utils/location.js | 31 | ||||
-rw-r--r-- | test/all.js | 1 | ||||
-rw-r--r-- | test/assets-inliner.js | 26 | ||||
-rw-r--r-- | test/page.js | 23 |
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': '', + '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'); |