Skip to content

Commit d1b7638

Browse files
committed
Embed text-to-image for easier control over canvas and nan versions
1 parent 0a47bf4 commit d1b7638

File tree

5 files changed

+153
-20
lines changed

5 files changed

+153
-20
lines changed

package-lock.json

+7-15
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
"@wdio/sync": "7.28.0",
5555
"async": "3.2.4",
5656
"axios": "0.27.2",
57+
"canvas": "2.11.0",
5758
"chai": "4.3.7",
5859
"chrome-har": "0.13.0",
5960
"config": "3.3.8",
@@ -92,7 +93,6 @@
9293
"stacktrace-parser": "0.1.10",
9394
"testdouble": "3.16.8",
9495
"testingbot-api": "1.0.8",
95-
"text-to-image": "5.2.0",
9696
"twilio": "3.84.0",
9797
"webdriverio": "7.28.0",
9898
"when": "3.7.8",

src/lib/text-to-image.js

+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
const { createCanvas, registerFont } = require('canvas');
2+
3+
// this is text-to-image v5.2 package included directly in oxygen for easier control over canvas and nan versions
4+
5+
const defaults = {
6+
bgColor: '#fff',
7+
customHeight: 0,
8+
bubbleTail: { width: 0, height: 0 },
9+
fontFamily: 'Arial',
10+
fontPath: '',
11+
fontSize: 18,
12+
fontWeight: 'normal',
13+
lineHeight: 28,
14+
margin: 10,
15+
maxWidth: 400,
16+
textAlign: 'left',
17+
textColor: '#000',
18+
verticalAlign: 'top',
19+
};
20+
21+
const createTextData = (text, config, canvas) => {
22+
const { bgColor, fontFamily, fontPath, fontSize, fontWeight, lineHeight, maxWidth, textAlign, textColor, } = config;
23+
if (fontPath) {
24+
registerFont(fontPath, { family: fontFamily });
25+
}
26+
const textCanvas = canvas || createCanvas(maxWidth, 100);
27+
const textContext = textCanvas.getContext('2d');
28+
let textX = 0;
29+
let textY = 0;
30+
if (['center'].includes(textAlign.toLowerCase())) {
31+
textX = maxWidth / 2;
32+
}
33+
if (['right', 'end'].includes(textAlign.toLowerCase())) {
34+
textX = maxWidth;
35+
}
36+
textContext.textAlign = textAlign;
37+
textContext.fillStyle = bgColor;
38+
textContext.fillRect(0, 0, textCanvas.width, textCanvas.height);
39+
textContext.fillStyle = textColor;
40+
textContext.font = `${fontWeight} ${fontSize}px ${fontFamily}`;
41+
textContext.textBaseline = 'top';
42+
const words = text.split(' ');
43+
let wordCount = words.length;
44+
let line = '';
45+
const addNewLines = [];
46+
for (let n = 0; n < wordCount; n += 1) {
47+
let word = words[n];
48+
if (/\n/.test(words[n])) {
49+
const parts = words[n].split('\n');
50+
word = parts.shift() || '';
51+
addNewLines.push(n + 1);
52+
words.splice(n + 1, 0, parts.join('\n'));
53+
wordCount += 1;
54+
}
55+
const testLine = `${line} ${word}`.replace(/^ +/, '').replace(/ +$/, '');
56+
const testLineWidth = textContext.measureText(testLine).width;
57+
if (addNewLines.indexOf(n) > -1 || (testLineWidth > maxWidth && n > 0)) {
58+
textContext.fillText(line, textX, textY);
59+
line = word;
60+
textY += lineHeight;
61+
}
62+
else {
63+
line = testLine;
64+
}
65+
}
66+
textContext.fillText(line, textX, textY);
67+
const height = textY + Math.max(lineHeight, fontSize);
68+
return {
69+
textHeight: height,
70+
textData: textContext.getImageData(0, 0, maxWidth, height),
71+
};
72+
};
73+
74+
const createImageCanvas = (content, conf) => {
75+
const { textHeight } = createTextData(content, {
76+
maxWidth: conf.maxWidth - conf.margin * 2,
77+
fontSize: conf.fontSize,
78+
lineHeight: conf.lineHeight,
79+
bgColor: conf.bgColor,
80+
textColor: conf.textColor,
81+
fontFamily: conf.fontFamily,
82+
fontPath: conf.fontPath,
83+
fontWeight: conf.fontWeight,
84+
textAlign: conf.textAlign,
85+
});
86+
const textHeightWithMargins = textHeight + conf.margin * 2;
87+
if (conf.customHeight && conf.customHeight < textHeightWithMargins) {
88+
console.warn('Text is longer than customHeight, clipping will occur.');
89+
}
90+
const height = conf.customHeight || textHeightWithMargins;
91+
const canvas = createCanvas(conf.maxWidth, height + conf.bubbleTail.height);
92+
const { textData } = createTextData(content, {
93+
maxWidth: conf.maxWidth - conf.margin * 2,
94+
fontSize: conf.fontSize,
95+
lineHeight: conf.lineHeight,
96+
bgColor: conf.bgColor,
97+
textColor: conf.textColor,
98+
fontFamily: conf.fontFamily,
99+
fontPath: conf.fontPath,
100+
fontWeight: conf.fontWeight,
101+
textAlign: conf.textAlign,
102+
}, canvas);
103+
const ctx = canvas.getContext('2d');
104+
ctx.clearRect(0, 0, canvas.width, canvas.height);
105+
ctx.globalAlpha = 1;
106+
ctx.fillStyle = conf.bgColor;
107+
ctx.fillRect(0, 0, canvas.width, height);
108+
if (conf.bubbleTail.width && conf.bubbleTail.height) {
109+
ctx.beginPath();
110+
ctx.moveTo(canvas.width / 2 - conf.bubbleTail.width / 2, height);
111+
ctx.lineTo(canvas.width / 2, canvas.height);
112+
ctx.lineTo(canvas.width / 2 + conf.bubbleTail.width / 2, height);
113+
ctx.closePath();
114+
ctx.fillStyle = conf.bgColor;
115+
ctx.fill();
116+
}
117+
const textX = conf.margin;
118+
let textY = conf.margin;
119+
if (conf.customHeight && conf.verticalAlign === 'center') {
120+
textY =
121+
(conf.customHeight - textData.height) / 2 +
122+
Math.max(0, (conf.lineHeight - conf.fontSize) / 2);
123+
}
124+
ctx.putImageData(textData, textX, textY);
125+
return canvas;
126+
};
127+
128+
const generate = async (content, config) => {
129+
const conf = { ...defaults, ...config };
130+
const canvas = createImageCanvas(content, conf);
131+
return canvas.toDataURL();
132+
};
133+
134+
const generateSync = (content, config) => {
135+
const conf = { ...defaults, ...config };
136+
const canvas = createImageCanvas(content, conf);
137+
return canvas.toDataURL();
138+
};
139+
140+
exports.generate = generate;
141+
exports.generateSync = generateSync;

src/ox_modules/module-mob.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -378,8 +378,8 @@ export default class MobileModule extends WebDriverModule {
378378
if (fetchTitle) {
379379
const title = await this.driver.getTitle();
380380
if (title) {
381-
const textToImage = require('text-to-image');
382-
let titleImage = await textToImage.generate(title, { debug: false, fontFamily: 'Arial' });
381+
const textToImage = require('../lib/text-to-image');
382+
let titleImage = await textToImage.generate(title);
383383
if (titleImage && typeof titleImage === 'string') {
384384
titleImage = titleImage.replace('data:image/png;base64,', '');
385385
images.push(titleImage);

src/ox_modules/module-web.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -508,8 +508,8 @@ export default class WebModule extends WebDriverModule {
508508
const title = await this.driver.getTitle();
509509

510510
if (title) {
511-
const textToImage = require('text-to-image');
512-
let titleImage = await textToImage.generate(title, { debug: false, fontFamily: 'Arial' });
511+
const textToImage = require('../lib/text-to-image');
512+
let titleImage = await textToImage.generate(title);
513513
if (titleImage && typeof titleImage === 'string') {
514514
titleImage = titleImage.replace('data:image/png;base64,', '');
515515
images.push(titleImage);

0 commit comments

Comments
 (0)