From 5c65529db46030ba5ec97edb19b6fb2204a7ee1b Mon Sep 17 00:00:00 2001 From: Liviu Tudor Date: Fri, 11 Nov 2016 12:37:47 -0800 Subject: [PATCH 1/2] ignoring IntelliJ files added support for background color updated docco for added support for text color updated unit test to check if travis is different fixing tests --- .gitignore | 3 + README.md | 6 +- lib/text-to-image.js | 24 ++++--- package.json | 1 + test/helpers/setup.js | 29 +++++++++ test/text-to-image.spec.js | 125 ++++++++++++++++++++++++++++++++++++- 6 files changed, 179 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 5148e52..f71d11e 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,6 @@ jspm_packages # Optional REPL history .node_repl_history + +# IntelliJ +.idea diff --git a/README.md b/README.md index 2f76ec1..268fdb1 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,8 @@ The ```generate``` function takes an optional second parameter containing config | lineHeight | 28 | The line height for the generated text. | | margin | 10 | The margin (all sides) between the text and the border of the image. | | debug | false | Set to true to turn on debug mode (see below). | +| bgColor | `"#FFFFFF"` | Sets the background color of the image. | +| textColor | `"#000000"` | Sets the text color. | Example: @@ -53,7 +55,9 @@ Example: maxWidth: 720, fontSize: 18, lineHeight: 30, - margin: 5 + margin: 5, + bgColor: "blue", + textColor: "red" }).then(function (dataUri) { console.log(dataUri); }); diff --git a/lib/text-to-image.js b/lib/text-to-image.js index adaf385..28a669b 100644 --- a/lib/text-to-image.js +++ b/lib/text-to-image.js @@ -16,14 +16,24 @@ var fs = require('fs'), function generateImage(content, config) { var conf = _.defaults(config, defaults); - var textData = createTextData(content, conf.maxWidth - conf.margin, conf.fontSize, conf.lineHeight); + // select background or use default if not specified + var background = '#fff'; + if (conf.bgColor) { + background = conf.bgColor; + } + + var textColor = '#000'; + if (conf.textColor) { + textColor = conf.textColor; + } + + var textData = createTextData(content, conf.maxWidth - conf.margin, conf.fontSize, conf.lineHeight, background, textColor); var canvas = new Canvas(conf.maxWidth, textData.height + conf.margin * 2), ctx = canvas.getContext('2d'); ctx.globalAlpha = 1; - // make background - ctx.fillStyle = "#fff"; + ctx.fillStyle = background; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.putImageData(textData, conf.margin, conf.margin); @@ -45,19 +55,19 @@ function generateImage(content, config) { } -function createTextData(text, maxWidth, fontSize, lineHeight) { +function createTextData(text, maxWidth, fontSize, lineHeight, bgColor, textColor) { // create a tall context so we definitely can fit all text var textCanvas = new Canvas(maxWidth, 1000), textContext = textCanvas.getContext('2d'), textX = 0, textY = 0; - // make background white - textContext.fillStyle = "#fff"; + // make background the color passed in + textContext.fillStyle = bgColor; textContext.fillRect(0, 0, textCanvas.width, textCanvas.height); // make text - textContext.fillStyle = "#000"; + textContext.fillStyle = textColor; textContext.font = 'normal ' + fontSize + 'px Helvetica'; textContext.textBaseline = 'top'; diff --git a/package.json b/package.json index 19080a9..7b89443 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "image-size": "^0.5.0", "istanbul": "^0.4.4", "mocha": "^3.0.0", + "readimage": "^1.1.1", "sinon": "^1.17.4", "sinon-chai": "^2.8.0" } diff --git a/test/helpers/setup.js b/test/helpers/setup.js index 23743e9..20b8efd 100644 --- a/test/helpers/setup.js +++ b/test/helpers/setup.js @@ -12,3 +12,32 @@ global.assert = chai.assert; chai.use(chaiAsPromised); chai.use(chaiThings); chai.use(sinonChai); + +function paddedHex(intVal) { + var s = intVal.toString(16); + if (s.length == 1) { + s = "0" + s; + } + return s; +} + +global.extractColors = function(image) { + var pixels = image.frames[0].data; + var colorMap = new Map(); + for( var i = 0; i < pixels.length; i += 4 ) { + var r = pixels[i], + g = pixels[i + 1], + b = pixels[i + 2], + a = pixels[i + 3]; + + var hexNotation = '#' + paddedHex(r) + paddedHex(g) + paddedHex(b); + var currValue = colorMap.get(hexNotation); + if( currValue ) { + currValue++; + } else { + currValue = 1; + } + colorMap.set(hexNotation, currValue); + } + return colorMap; +} \ No newline at end of file diff --git a/test/text-to-image.spec.js b/test/text-to-image.spec.js index 04b9146..f0fd8bf 100644 --- a/test/text-to-image.spec.js +++ b/test/text-to-image.spec.js @@ -8,7 +8,8 @@ describe("the text-to-image generator", function () { glob = require('glob'), fs = require('fs'), path = require('path'), - sizeOf = require('image-size'); + sizeOf = require('image-size'), + readimage = require('readimage'); beforeEach(function () { imageGenerator = require('../lib/text-to-image'); @@ -93,4 +94,126 @@ describe("the text-to-image generator", function () { }); }); + it("should default to a white background no transparency", function () { + return Promise.all([ + imageGenerator.generate('Lorem ipsum dolor sit amet.', { + debug: true + }) + ]).then(function () { + var images = glob.sync(path.join(process.cwd(), '*.png')); + var imageData = fs.readFileSync(images[0]); + return new Promise(function(resolve, reject) { + readimage(imageData, function(err, image) { + if( err ) { + reject(err); + } else { + resolve(image); + } + }) + }); + }).then(function(image) { + expect(image.frames.length).to.equal(1); + expect(image.frames[0].data[0]).to.equals(0xff); + expect(image.frames[0].data[1]).to.equals(0xff); + expect(image.frames[0].data[2]).to.equals(0xff); + expect(image.frames[0].data[3]).to.equals(0xff); + }); + }); + + it("should use the background color specified with no transparency", function () { + return Promise.all([ + imageGenerator.generate('Lorem ipsum dolor sit amet.', { + debug: true, + bgColor: '#001122' + }) + ]).then(function () { + var images = glob.sync(path.join(process.cwd(), '*.png')); + var imageData = fs.readFileSync(images[0]); + return new Promise(function(resolve, reject) { + readimage(imageData, function(err, image) { + if( err ) { + reject(err); + } else { + resolve(image); + } + }) + }); + }).then(function(image) { + expect(image.frames.length).to.equal(1); + expect(image.frames[0].data[0]).to.equals(0x00); + expect(image.frames[0].data[1]).to.equals(0x11); + expect(image.frames[0].data[2]).to.equals(0x22); + expect(image.frames[0].data[3]).to.equals(0xff); + }); + }); + + it("should default to a black text color", function () { + var WIDTH = 720; + var HEIGHT = 220; + return Promise.all([ + imageGenerator.generate('Lorem ipsum dolor sit amet.', { + debug: true, + maxWidth: WIDTH, + fontSize: 100, + lineHeight: 100 + }) + ]).then(function () { + var images = glob.sync(path.join(process.cwd(), '*.png')); + var dimensions = sizeOf(images[0]); + expect(dimensions.width).to.equals(WIDTH); + expect(dimensions.height).to.equals(220); + var imageData = fs.readFileSync(images[0]); + return new Promise(function(resolve, reject) { + readimage(imageData, function(err, image) { + if( err ) { + reject(err); + } else { + resolve(image); + } + }) + }); + }).then(function(image) { + var map = extractColors(image); + // GIMP reports 256 colors on this image + expect(map.size).to.be.within(2,256); + expect(map.get('#000000')).to.be.above(10); + expect(map.get('#ffffff')).to.be.above(100); + }); + }); + + it("should use the text color specified", function () { + var WIDTH = 720; + var HEIGHT = 220; + return Promise.all([ + imageGenerator.generate('Lorem ipsum dolor sit amet.', { + debug: true, + maxWidth: WIDTH, + fontSize: 100, + lineHeight: 100, + textColor: "#112233" + }) + ]).then(function () { + var images = glob.sync(path.join(process.cwd(), '*.png')); + var dimensions = sizeOf(images[0]); + expect(dimensions.width).to.equals(WIDTH); + expect(dimensions.height).to.equals(220); + var imageData = fs.readFileSync(images[0]); + return new Promise(function(resolve, reject) { + readimage(imageData, function(err, image) { + if( err ) { + reject(err); + } else { + resolve(image); + } + }) + }); + }).then(function(image) { + var map = extractColors(image); + // GIMP reports 256 colors on this image + expect(map.size).to.be.within(2,256); + expect(map.get('#112233')).to.be.above(10); + expect(map.get('#ffffff')).to.be.above(100); + }); + }); + }); From 31e043ea656b3e78f8a926d14585dd51d2697e57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Bostr=C3=B6m?= Date: Mon, 14 Nov 2016 00:21:04 +0200 Subject: [PATCH 2/2] Tweak and cleanup PR --- lib/text-to-image.js | 19 +++------- test/helpers/extractColors.js | 34 ++++++++++++++++++ test/helpers/setup.js | 29 ---------------- test/text-to-image.spec.js | 65 +++++++++++++++++++---------------- 4 files changed, 75 insertions(+), 72 deletions(-) create mode 100644 test/helpers/extractColors.js diff --git a/lib/text-to-image.js b/lib/text-to-image.js index bd7f29e..647ab36 100644 --- a/lib/text-to-image.js +++ b/lib/text-to-image.js @@ -10,30 +10,21 @@ var fs = require('fs'), maxWidth: 400, fontSize: 18, lineHeight: 28, - margin: 10 + margin: 10, + bgColor: '#fff', + textColor: '#000' }; function generateImage(content, config) { var conf = _.defaults(config, defaults); - // select background or use default if not specified - var background = '#fff'; - if (conf.bgColor) { - background = conf.bgColor; - } - - var textColor = '#000'; - if (conf.textColor) { - textColor = conf.textColor; - } - - var textData = createTextData(content, conf.maxWidth - conf.margin, conf.fontSize, conf.lineHeight, background, textColor); + var textData = createTextData(content, conf.maxWidth - conf.margin, conf.fontSize, conf.lineHeight, conf.bgColor, conf.textColor); var canvas = new Canvas(conf.maxWidth, textData.height + conf.margin * 2), ctx = canvas.getContext('2d'); ctx.globalAlpha = 1; - ctx.fillStyle = background; + ctx.fillStyle = conf.bgColor; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.putImageData(textData, conf.margin, conf.margin); diff --git a/test/helpers/extractColors.js b/test/helpers/extractColors.js new file mode 100644 index 0000000..b7c251d --- /dev/null +++ b/test/helpers/extractColors.js @@ -0,0 +1,34 @@ +"use strict"; + +module.exports = extractColors; + +function extractColors(image) { + var pixels = image.frames[0].data; + var colorMap = {}; + for (var i = 0; i < pixels.length; i += 4) { + var r = pixels[i], + g = pixels[i + 1], + b = pixels[i + 2]; + // a = pixels[i + 3] + + var hexNotation = '#' + paddedHex(r) + paddedHex(g) + paddedHex(b); + var currValue = colorMap[hexNotation]; + if (currValue) { + currValue += 1; + } else { + currValue = 1; + } + colorMap[hexNotation] = currValue; + } + + return colorMap; +} + +function paddedHex(intVal) { + var s = intVal.toString(16); + if (s.length === 1) { + s = "0" + s; + } + + return s; +} diff --git a/test/helpers/setup.js b/test/helpers/setup.js index 20b8efd..23743e9 100644 --- a/test/helpers/setup.js +++ b/test/helpers/setup.js @@ -12,32 +12,3 @@ global.assert = chai.assert; chai.use(chaiAsPromised); chai.use(chaiThings); chai.use(sinonChai); - -function paddedHex(intVal) { - var s = intVal.toString(16); - if (s.length == 1) { - s = "0" + s; - } - return s; -} - -global.extractColors = function(image) { - var pixels = image.frames[0].data; - var colorMap = new Map(); - for( var i = 0; i < pixels.length; i += 4 ) { - var r = pixels[i], - g = pixels[i + 1], - b = pixels[i + 2], - a = pixels[i + 3]; - - var hexNotation = '#' + paddedHex(r) + paddedHex(g) + paddedHex(b); - var currValue = colorMap.get(hexNotation); - if( currValue ) { - currValue++; - } else { - currValue = 1; - } - colorMap.set(hexNotation, currValue); - } - return colorMap; -} \ No newline at end of file diff --git a/test/text-to-image.spec.js b/test/text-to-image.spec.js index f0fd8bf..4c628db 100644 --- a/test/text-to-image.spec.js +++ b/test/text-to-image.spec.js @@ -9,7 +9,8 @@ describe("the text-to-image generator", function () { fs = require('fs'), path = require('path'), sizeOf = require('image-size'), - readimage = require('readimage'); + readimage = require('readimage'), + extractColors = require('./helpers/extractColors'); beforeEach(function () { imageGenerator = require('../lib/text-to-image'); @@ -102,16 +103,17 @@ describe("the text-to-image generator", function () { ]).then(function () { var images = glob.sync(path.join(process.cwd(), '*.png')); var imageData = fs.readFileSync(images[0]); - return new Promise(function(resolve, reject) { - readimage(imageData, function(err, image) { - if( err ) { + + return new Promise(function (resolve, reject) { + readimage(imageData, function (err, image) { + if (err) { reject(err); } else { resolve(image); } - }) + }); }); - }).then(function(image) { + }).then(function (image) { expect(image.frames.length).to.equal(1); expect(image.frames[0].data[0]).to.equals(0xff); expect(image.frames[0].data[1]).to.equals(0xff); @@ -129,16 +131,17 @@ describe("the text-to-image generator", function () { ]).then(function () { var images = glob.sync(path.join(process.cwd(), '*.png')); var imageData = fs.readFileSync(images[0]); - return new Promise(function(resolve, reject) { - readimage(imageData, function(err, image) { - if( err ) { + + return new Promise(function (resolve, reject) { + readimage(imageData, function (err, image) { + if (err) { reject(err); } else { resolve(image); } - }) + }); }); - }).then(function(image) { + }).then(function (image) { expect(image.frames.length).to.equal(1); expect(image.frames[0].data[0]).to.equals(0x00); expect(image.frames[0].data[1]).to.equals(0x11); @@ -150,6 +153,7 @@ describe("the text-to-image generator", function () { it("should default to a black text color", function () { var WIDTH = 720; var HEIGHT = 220; + return Promise.all([ imageGenerator.generate('Lorem ipsum dolor sit amet.', { debug: true, @@ -161,29 +165,31 @@ describe("the text-to-image generator", function () { var images = glob.sync(path.join(process.cwd(), '*.png')); var dimensions = sizeOf(images[0]); expect(dimensions.width).to.equals(WIDTH); - expect(dimensions.height).to.equals(220); + expect(dimensions.height).to.equals(HEIGHT); var imageData = fs.readFileSync(images[0]); - return new Promise(function(resolve, reject) { - readimage(imageData, function(err, image) { - if( err ) { + + return new Promise(function (resolve, reject) { + readimage(imageData, function (err, image) { + if (err) { reject(err); } else { resolve(image); } - }) + }); }); - }).then(function(image) { + }).then(function (image) { var map = extractColors(image); // GIMP reports 256 colors on this image - expect(map.size).to.be.within(2,256); - expect(map.get('#000000')).to.be.above(10); - expect(map.get('#ffffff')).to.be.above(100); + expect(Object.keys(map).length).to.be.within(2, 256); + expect(map['#000000']).to.be.above(10); + expect(map['#ffffff']).to.be.above(100); }); }); it("should use the text color specified", function () { var WIDTH = 720; var HEIGHT = 220; + return Promise.all([ imageGenerator.generate('Lorem ipsum dolor sit amet.', { debug: true, @@ -196,23 +202,24 @@ describe("the text-to-image generator", function () { var images = glob.sync(path.join(process.cwd(), '*.png')); var dimensions = sizeOf(images[0]); expect(dimensions.width).to.equals(WIDTH); - expect(dimensions.height).to.equals(220); + expect(dimensions.height).to.equals(HEIGHT); var imageData = fs.readFileSync(images[0]); - return new Promise(function(resolve, reject) { - readimage(imageData, function(err, image) { - if( err ) { + + return new Promise(function (resolve, reject) { + readimage(imageData, function (err, image) { + if (err) { reject(err); } else { resolve(image); } - }) + }); }); - }).then(function(image) { + }).then(function (image) { var map = extractColors(image); // GIMP reports 256 colors on this image - expect(map.size).to.be.within(2,256); - expect(map.get('#112233')).to.be.above(10); - expect(map.get('#ffffff')).to.be.above(100); + expect(Object.keys(map).length).to.be.within(2, 256); + expect(map['#112233']).to.be.above(10); + expect(map['#ffffff']).to.be.above(100); }); });