From 50a24c62c561cbbfc7c07e78f6bba8c7718745a5 Mon Sep 17 00:00:00 2001 From: Johan Preynat Date: Tue, 3 May 2016 11:26:38 +0200 Subject: [PATCH 1/9] lib/utils/location.js: Add isDataURI() method --- lib/utils/location.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/utils/location.js b/lib/utils/location.js index 84a71ad6c..1afe4150e 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, From d6202e522b7875546bfcd6ed000a72d1910fade4 Mon Sep 17 00:00:00 2001 From: Johan Preynat Date: Tue, 3 May 2016 11:27:05 +0200 Subject: [PATCH 2/9] lib/utils/images.js: Add convertInlinePNG() method --- lib/utils/images.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/utils/images.js b/lib/utils/images.js index e387d6b36..6d4b927ad 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 From 8c4752c5d8e407a2822386710f43d5c34a65f84b Mon Sep 17 00:00:00 2001 From: Johan Preynat Date: Tue, 3 May 2016 11:27:36 +0200 Subject: [PATCH 3/9] lib/utils/__tests__/location.js: Add location.isDataURI() test file --- lib/utils/__tests__/location.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/utils/__tests__/location.js b/lib/utils/__tests__/location.js index f2037ffa3..1d75751dc 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() { From ffe6c36f2f7815d956f38908078cfe0b805cb0d7 Mon Sep 17 00:00:00 2001 From: Johan Preynat Date: Tue, 3 May 2016 11:28:39 +0200 Subject: [PATCH 4/9] lib/output/modifiers/resolveImages.js: Don't resolve images when src is a data URI --- lib/output/modifiers/resolveImages.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/output/modifiers/resolveImages.js b/lib/output/modifiers/resolveImages.js index e401cf500..cc25cfa32 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; } From 0923d1aa24ba3659082f54814f1e2a40f41dcd57 Mon Sep 17 00:00:00 2001 From: Johan Preynat Date: Tue, 3 May 2016 11:29:17 +0200 Subject: [PATCH 5/9] lib/output/modifiers/inlinePng.js: Add modifier to save inline PNG images --- lib/output/modifiers/inlinePng.js | 47 +++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 lib/output/modifiers/inlinePng.js diff --git a/lib/output/modifiers/inlinePng.js b/lib/output/modifiers/inlinePng.js new file mode 100644 index 000000000..161f164d3 --- /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; From 59b7100cf6a363a3b9c7e317ab2810f7b1ee2af2 Mon Sep 17 00:00:00 2001 From: Johan Preynat Date: Tue, 3 May 2016 11:29:40 +0200 Subject: [PATCH 6/9] lib/output/modifiers/__tests__/inlinePng.js: Add test file for inlinePng modifier --- lib/output/modifiers/__tests__/inlinePng.js | 25 +++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 lib/output/modifiers/__tests__/inlinePng.js diff --git a/lib/output/modifiers/__tests__/inlinePng.js b/lib/output/modifiers/__tests__/inlinePng.js new file mode 100644 index 000000000..fb094f79e --- /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('GitBook Logo 20x20'); + + return inlinePng(dir.name, 'index.html', $) + .then(function() { + var $img = $('img'); + var src = $img.attr('src'); + + expect(dir.name).toHaveFile(src); + }); + }); +}); + + From 7f435c56a01f815efc7f89da9eecae372c914f91 Mon Sep 17 00:00:00 2001 From: Johan Preynat Date: Tue, 3 May 2016 11:30:02 +0200 Subject: [PATCH 7/9] lib/output/modifiers/inlineAssets.js: Add inlinePng() modifier --- lib/output/modifiers/inlineAssets.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/output/modifiers/inlineAssets.js b/lib/output/modifiers/inlineAssets.js index ee932eb58..7cd874b54 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, $)); }; } From 5893d3ee649f4f42231895e367dbfc9051dedf0e Mon Sep 17 00:00:00 2001 From: Johan Preynat Date: Tue, 3 May 2016 12:04:04 +0200 Subject: [PATCH 8/9] lib/parse/parseConfig.js: Remove unused require --- lib/parse/parseConfig.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/parse/parseConfig.js b/lib/parse/parseConfig.js index a1e9d69b2..5200de2f9 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'); From 74c3ff80cbc64eb4fb4252ca4cc08076d44c6be2 Mon Sep 17 00:00:00 2001 From: Johan Preynat Date: Tue, 3 May 2016 12:04:52 +0200 Subject: [PATCH 9/9] lib/models/config.js: Fix wrong call to PluginDependency.listToArray() to PluginDependency.listFromArray() --- lib/models/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/models/config.js b/lib/models/config.js index 83dd6d4c9..3310a9333 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); };