text-to-image
Version:
A library for generating an image data URI representing an image containing the text of your choice.
140 lines (139 loc) • 5.07 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateSync = exports.generate = void 0;
const canvas_1 = require("canvas");
const defaults = {
bgColor: '#fff',
customHeight: 0,
fontFamily: 'Helvetica',
fontPath: '',
fontSize: 18,
fontWeight: 'normal',
lineHeight: 28,
margin: 10,
maxWidth: 400,
textAlign: 'left',
textColor: '#000',
verticalAlign: 'top',
extensions: [],
};
const createTextData = (text, config, canvas) => {
const { bgColor, fontFamily, fontPath, fontSize, fontWeight, lineHeight, maxWidth, textAlign, textColor, } = config;
if (fontPath) {
(0, canvas_1.registerFont)(fontPath, { family: fontFamily });
}
const textCanvas = canvas ?? (0, canvas_1.createCanvas)(maxWidth, 100);
const textContext = textCanvas.getContext('2d');
let textX = 0;
let textY = 0;
if (['center'].includes(textAlign.toLowerCase())) {
textX = maxWidth / 2;
}
if (['right', 'end'].includes(textAlign.toLowerCase())) {
textX = maxWidth;
}
textContext.textAlign = textAlign;
textContext.fillStyle = bgColor;
textContext.fillRect(0, 0, textCanvas.width, textCanvas.height);
textContext.fillStyle = textColor;
textContext.font = `${fontWeight.toString()} ${fontSize.toString()}px ${fontFamily}`;
textContext.textBaseline = 'top';
const words = text.split(' ');
let wordCount = words.length;
let line = '';
const addNewLines = [];
for (let n = 0; n < wordCount; n += 1) {
let word = words[n];
if (words[n].includes('\n')) {
const parts = words[n].split('\n');
word = parts.shift() || '';
addNewLines.push(n + 1);
words.splice(n + 1, 0, parts.join('\n'));
wordCount += 1;
}
const testLine = `${line} ${word}`.replace(/^ +|(?<! ) +$/g, '');
const testLineWidth = textContext.measureText(testLine).width;
if (addNewLines.includes(n) || (testLineWidth > maxWidth && n > 0)) {
textContext.fillText(line, textX, textY);
line = word;
textY += lineHeight;
}
else {
line = testLine;
}
}
textContext.fillText(line, textX, textY);
const height = textY + Math.max(lineHeight, fontSize);
return {
textHeight: height,
textData: textContext.getImageData(0, 0, maxWidth, height),
};
};
const createImageCanvas = (content, conf) => {
const { textHeight } = createTextData(content, {
maxWidth: conf.maxWidth - conf.margin * 2,
fontSize: conf.fontSize,
lineHeight: conf.lineHeight,
bgColor: conf.bgColor,
textColor: conf.textColor,
fontFamily: conf.fontFamily,
fontPath: conf.fontPath,
fontWeight: conf.fontWeight,
textAlign: conf.textAlign,
});
const textHeightWithMargins = textHeight + conf.margin * 2;
if (conf.customHeight && conf.customHeight < textHeightWithMargins) {
console.warn('Text is longer than customHeight, clipping will occur.');
}
const height = conf.customHeight || textHeightWithMargins;
const canvas = (0, canvas_1.createCanvas)(conf.maxWidth, height);
const { textData } = createTextData(content, {
maxWidth: conf.maxWidth - conf.margin * 2,
fontSize: conf.fontSize,
lineHeight: conf.lineHeight,
bgColor: conf.bgColor,
textColor: conf.textColor,
fontFamily: conf.fontFamily,
fontPath: conf.fontPath,
fontWeight: conf.fontWeight,
textAlign: conf.textAlign,
}, canvas);
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.globalAlpha = 1;
ctx.fillStyle = conf.bgColor;
ctx.fillRect(0, 0, canvas.width, height);
const textX = conf.margin;
let textY = conf.margin;
if (conf.customHeight && conf.verticalAlign === 'center') {
textY =
(conf.customHeight - textData.height) / 2 +
Math.max(0, (conf.lineHeight - conf.fontSize) / 2);
}
ctx.putImageData(textData, textX, textY);
return canvas;
};
const generate = async (content, config) => {
const conf = {
...defaults,
...config,
};
const canvas = createImageCanvas(content, conf);
const finalCanvas = await conf.extensions.reduce(async (prevCanvasPromise, extension) => {
const resolvedPrev = await prevCanvasPromise;
return extension(resolvedPrev, conf);
}, Promise.resolve(canvas));
const dataUrl = finalCanvas.toDataURL();
return dataUrl;
};
exports.generate = generate;
const generateSync = (content, config) => {
const conf = { ...defaults, ...config };
const canvas = createImageCanvas(content, conf);
const finalCanvas = conf.extensions.reduce((prevCanvas, extension) => {
return extension(prevCanvas, conf);
}, canvas);
const dataUrl = finalCanvas.toDataURL();
return dataUrl;
};
exports.generateSync = generateSync;
;