summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSamy Pessé <samypesse@gmail.com>2016-05-03 12:10:09 +0200
committerSamy Pessé <samypesse@gmail.com>2016-05-03 12:10:09 +0200
commitc84eaa83dd3bf307d5cc35b01febb2b6cc6ffbab (patch)
treef65d81365e10ccd831455df300db3e8c4086a9a8
parent6b17d08892828818216a260576c83b7203c2098f (diff)
parent74c3ff80cbc64eb4fb4252ca4cc08076d44c6be2 (diff)
downloadgitbook-c84eaa83dd3bf307d5cc35b01febb2b6cc6ffbab.zip
gitbook-c84eaa83dd3bf307d5cc35b01febb2b6cc6ffbab.tar.gz
gitbook-c84eaa83dd3bf307d5cc35b01febb2b6cc6ffbab.tar.bz2
Merge pull request #1259 from GitbookIO/png-data-uri
Adding data-uri support for PNG Images in Books
-rw-r--r--lib/models/config.js2
-rw-r--r--lib/output/modifiers/__tests__/inlinePng.js25
-rw-r--r--lib/output/modifiers/inlineAssets.js4
-rw-r--r--lib/output/modifiers/inlinePng.js47
-rw-r--r--lib/output/modifiers/resolveImages.js2
-rw-r--r--lib/parse/parseConfig.js2
-rw-r--r--lib/utils/__tests__/location.js9
-rw-r--r--lib/utils/images.js18
-rw-r--r--lib/utils/location.js14
9 files changed, 115 insertions, 8 deletions
diff --git a/lib/models/config.js b/lib/models/config.js
index 83dd6d4..3310a93 100644
--- a/lib/models/config.js
+++ b/lib/models/config.js
@@ -75,7 +75,7 @@ Config.prototype.getPluginDependencies = function() {
@return {Config}
*/
Config.prototype.setPluginDependencies = function(deps) {
- var plugins = PluginDependency.listToArray(deps);
+ var plugins = PluginDependency.listFromArray(deps);
return this.setValue('plugins', plugins);
};
diff --git a/lib/output/modifiers/__tests__/inlinePng.js b/lib/output/modifiers/__tests__/inlinePng.js
new file mode 100644
index 0000000..fb094f7
--- /dev/null
+++ b/lib/output/modifiers/__tests__/inlinePng.js
@@ -0,0 +1,25 @@
+var cheerio = require('cheerio');
+var tmp = require('tmp');
+var inlinePng = require('../inlinePng');
+
+describe('inlinePng', function() {
+ var dir;
+
+ beforeEach(function() {
+ dir = tmp.dirSync();
+ });
+
+ pit('should write an inline PNG using data URI as a file', function() {
+ var $ = cheerio.load('<img alt="GitBook Logo 20x20" src="data:image/png;base64,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"/>');
+
+ return inlinePng(dir.name, 'index.html', $)
+ .then(function() {
+ var $img = $('img');
+ var src = $img.attr('src');
+
+ expect(dir.name).toHaveFile(src);
+ });
+ });
+});
+
+
diff --git a/lib/output/modifiers/inlineAssets.js b/lib/output/modifiers/inlineAssets.js
index ee932eb..7cd874b 100644
--- a/lib/output/modifiers/inlineAssets.js
+++ b/lib/output/modifiers/inlineAssets.js
@@ -1,5 +1,6 @@
var svgToImg = require('./svgToImg');
var svgToPng = require('./svgToPng');
+var inlinePng = require('./inlinePng');
var resolveImages = require('./resolveImages');
var fetchRemoteImages = require('./fetchRemoteImages');
@@ -20,7 +21,8 @@ function inlineAssets(rootFolder, currentFile) {
.then(fetchRemoteImages.bind(null, rootFolder, currentFile, $))
.then(svgToImg.bind(null, rootFolder, currentFile, $))
- .then(svgToPng.bind(null, rootFolder, currentFile, $));
+ .then(svgToPng.bind(null, rootFolder, currentFile, $))
+ .then(inlinePng.bind(null, rootFolder, currentFile, $));
};
}
diff --git a/lib/output/modifiers/inlinePng.js b/lib/output/modifiers/inlinePng.js
new file mode 100644
index 0000000..161f164
--- /dev/null
+++ b/lib/output/modifiers/inlinePng.js
@@ -0,0 +1,47 @@
+var crc = require('crc');
+var path = require('path');
+
+var imagesUtil = require('../../utils/images');
+var fs = require('../../utils/fs');
+var LocationUtils = require('../../utils/location');
+
+var editHTMLElement = require('./editHTMLElement');
+
+/**
+ Convert all inline PNG images to PNG file
+
+ @param {String} rootFolder
+ @param {HTMLDom} $
+ @return {Promise}
+*/
+function inlinePng(rootFolder, currentFile, $) {
+ var currentDirectory = path.dirname(currentFile);
+
+ return editHTMLElement($, 'img', function($img) {
+ var src = $img.attr('src');
+ if (!LocationUtils.isDataURI(src)) {
+ return;
+ }
+
+ // We avoid generating twice the same PNG
+ var hash = crc.crc32(src).toString(16);
+ var fileName = hash + '.png';
+
+ // Result file path
+ var filePath = path.join(rootFolder, fileName);
+
+ return fs.assertFile(filePath, function() {
+ return imagesUtil.convertInlinePNG(src, filePath);
+ })
+ .then(function() {
+ // Convert filename to a relative filename
+ fileName = LocationUtils.relative(currentDirectory, fileName);
+
+ // Replace src
+ $img.attr('src', fileName);
+ });
+ });
+}
+
+
+module.exports = inlinePng;
diff --git a/lib/output/modifiers/resolveImages.js b/lib/output/modifiers/resolveImages.js
index e401cf5..cc25cfa 100644
--- a/lib/output/modifiers/resolveImages.js
+++ b/lib/output/modifiers/resolveImages.js
@@ -16,7 +16,7 @@ function resolveImages(currentFile, $) {
return editHTMLElement($, 'img', function($img) {
var src = $img.attr('src');
- if (LocationUtils.isExternal(src)) {
+ if (LocationUtils.isExternal(src) || LocationUtils.isDataURI(src)) {
return;
}
diff --git a/lib/parse/parseConfig.js b/lib/parse/parseConfig.js
index a1e9d69..5200de2 100644
--- a/lib/parse/parseConfig.js
+++ b/lib/parse/parseConfig.js
@@ -1,5 +1,3 @@
-var is = require('is');
-
var Promise = require('../utils/promise');
var Config = require('../models/config');
diff --git a/lib/utils/__tests__/location.js b/lib/utils/__tests__/location.js
index f2037ff..1d75751 100644
--- a/lib/utils/__tests__/location.js
+++ b/lib/utils/__tests__/location.js
@@ -9,6 +9,15 @@ describe('LocationUtils', function() {
expect(LocationUtils.isExternal('test.md')).toBe(false);
expect(LocationUtils.isExternal('folder/test.md')).toBe(false);
expect(LocationUtils.isExternal('/folder/test.md')).toBe(false);
+ expect(LocationUtils.isExternal('data:image/png')).toBe(false);
+ });
+
+ it('should correctly test data:uri location', function() {
+ expect(LocationUtils.isDataURI('data:image/png')).toBe(true);
+ expect(LocationUtils.isDataURI('http://google.fr')).toBe(false);
+ expect(LocationUtils.isDataURI('https://google.fr')).toBe(false);
+ expect(LocationUtils.isDataURI('test.md')).toBe(false);
+ expect(LocationUtils.isDataURI('data.md')).toBe(false);
});
it('should correctly detect anchor location', function() {
diff --git a/lib/utils/images.js b/lib/utils/images.js
index e387d6b..6d4b927 100644
--- a/lib/utils/images.js
+++ b/lib/utils/images.js
@@ -38,7 +38,23 @@ function convertSVGBufferToPNG(buf, dest) {
});
}
+// Converts a inline data: to png file
+function convertInlinePNG(source, dest) {
+ if (!/^data\:image\/png/.test(source)) return Promise.reject(new Error('Source is not a PNG data-uri'));
+
+ var base64data = source.split('data:image/png;base64,')[1];
+ var buf = new Buffer(base64data, 'base64');
+
+ return fs.writeFile(dest, buf)
+ .then(function() {
+ if (fs.existsSync(dest)) return;
+
+ throw new Error('Error converting '+source+' into '+dest);
+ });
+}
+
module.exports = {
convertSVGToPNG: convertSVGToPNG,
- convertSVGBufferToPNG: convertSVGBufferToPNG
+ convertSVGBufferToPNG: convertSVGBufferToPNG,
+ convertInlinePNG: convertInlinePNG
}; \ No newline at end of file
diff --git a/lib/utils/location.js b/lib/utils/location.js
index 84a71ad..1afe415 100644
--- a/lib/utils/location.js
+++ b/lib/utils/location.js
@@ -4,7 +4,16 @@ var path = require('path');
// Is the url an external url
function isExternal(href) {
try {
- return Boolean(url.parse(href).protocol);
+ return Boolean(url.parse(href).protocol) && !isDataURI(href);
+ } catch(err) {
+ return false;
+ }
+}
+
+// Is the url an iniline data-uri
+function isDataURI(href) {
+ try {
+ return Boolean(url.parse(href).protocol) && (url.parse(href).protocol === 'data:');
} catch(err) {
return false;
}
@@ -39,7 +48,7 @@ function normalize(s) {
@return {String}
*/
function toAbsolute(_href, dir, outdir) {
- if (isExternal(_href)) return _href;
+ if (isExternal(_href) || isDataURI(_href)) return _href;
outdir = outdir == undefined? dir : outdir;
_href = normalize(_href);
@@ -97,6 +106,7 @@ function areIdenticalPaths(p1, p2) {
module.exports = {
areIdenticalPaths: areIdenticalPaths,
+ isDataURI: isDataURI,
isExternal: isExternal,
isRelative: isRelative,
isAnchor: isAnchor,