n8n-nodes-image-editor-pro
Version:
Advanced image editing node for n8n using Sharp and Canvas
170 lines (169 loc) • 6.56 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.imageEditor = imageEditor;
const axios_1 = __importDefault(require("axios"));
const sharp_1 = __importDefault(require("sharp"));
const canvas_1 = require("@napi-rs/canvas");
async function imageEditor({ mode, input, options, }) {
var _a, _b;
const images = [];
if ((_a = input.binaryArray) === null || _a === void 0 ? void 0 : _a.length) {
for (const binary of input.binaryArray) {
images.push(Buffer.from(binary.data, 'base64'));
}
}
else if (input.binary) {
images.push(Buffer.from(input.binary.data, 'base64'));
}
for (const url of (_b = input.urls) !== null && _b !== void 0 ? _b : []) {
const res = await axios_1.default.get(url, { responseType: 'arraybuffer' });
images.push(Buffer.from(res.data));
}
if (!images.length)
throw new Error('No valid images provided');
switch (mode) {
case 'collage':
return createCollage(images, options);
case 'addText':
return addText(images[0], options);
case 'addWatermark':
return addWatermark(images[0], options);
default:
throw new Error('Invalid mode');
}
}
async function createCollage(images, opts) {
const { rows, columns, spacing, backgroundColor } = opts;
const thumbWidth = 300;
const thumbHeight = 300;
const canvasWidth = columns * thumbWidth + spacing * (columns - 1);
const canvasHeight = rows * thumbHeight + spacing * (rows - 1);
const canvas = (0, canvas_1.createCanvas)(canvasWidth, canvasHeight);
const ctx = canvas.getContext('2d');
ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
let index = 0;
for (let row = 0; row < rows; row++) {
for (let col = 0; col < columns; col++) {
if (index >= images.length)
break;
const img = await (0, canvas_1.loadImage)(await (0, sharp_1.default)(images[index]).resize(thumbWidth, thumbHeight).png().toBuffer());
ctx.drawImage(img, col * (thumbWidth + spacing), row * (thumbHeight + spacing));
index++;
}
}
return canvas.toBuffer('image/png');
}
async function addText(image, opts) {
var _a, _b, _c, _d, _e, _f, _g, _h;
const img = await (0, sharp_1.default)(image).ensureAlpha().png().toBuffer();
const loadedImg = await (0, canvas_1.loadImage)(img);
const { width, height } = loadedImg;
const canvas = (0, canvas_1.createCanvas)(width, height);
const ctx = canvas.getContext('2d');
ctx.drawImage(loadedImg, 0, 0);
ctx.globalAlpha = (_a = opts.opacity) !== null && _a !== void 0 ? _a : 1;
ctx.font = `${opts.fontSize}px sans-serif`;
let x = 0;
let y = 0;
if (typeof opts.position === 'string') {
switch (opts.position) {
case 'top-left':
x = 20;
y = opts.fontSize + 20;
break;
case 'center':
x = width / 2;
y = height / 2;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
break;
case 'bottom-right':
x = width - 20;
y = height - 20;
ctx.textAlign = 'right';
ctx.textBaseline = 'bottom';
break;
}
}
else {
x = opts.position.x;
y = opts.position.y;
}
const padding = (_b = opts.shapePadding) !== null && _b !== void 0 ? _b : 20;
const shapeWidth = (_c = opts.shapeWidth) !== null && _c !== void 0 ? _c : ctx.measureText(opts.text).width + padding;
const shapeHeight = (_d = opts.shapeHeight) !== null && _d !== void 0 ? _d : opts.fontSize + padding;
const radius = Math.max(shapeWidth, shapeHeight) / 2;
const bgColor = opts.backgroundColor || 'rgba(255,255,255,0.5)';
const borderWidth = (_e = opts.borderWidth) !== null && _e !== void 0 ? _e : 0;
const borderColor = opts.borderColor || 'transparent';
let textX = x;
let textY = y;
switch (opts.textAlignInShape) {
case 'top':
textY = y - shapeHeight / 2 + opts.fontSize;
break;
case 'bottom':
textY = y + shapeHeight / 2 - opts.fontSize / 2;
break;
case 'center':
textY = y;
ctx.textBaseline = 'middle';
break;
case 'custom':
textX += (_f = opts.textOffsetX) !== null && _f !== void 0 ? _f : 0;
textY += (_g = opts.textOffsetY) !== null && _g !== void 0 ? _g : 0;
break;
}
if (opts.backgroundShape === 'circle') {
ctx.beginPath();
ctx.arc(x, y - opts.fontSize / 2, radius, 0, Math.PI * 2);
ctx.fillStyle = bgColor;
ctx.fill();
if (borderWidth > 0) {
ctx.lineWidth = borderWidth;
ctx.strokeStyle = borderColor;
ctx.stroke();
}
}
else if (opts.backgroundShape === 'square') {
const boxX = x - shapeWidth / 2;
const boxY = y - shapeHeight / 2;
ctx.fillRect(boxX, boxY, shapeWidth, shapeHeight);
ctx.fillStyle = bgColor;
ctx.fillRect(boxX, boxY, shapeWidth, shapeHeight);
if (borderWidth > 0) {
ctx.lineWidth = borderWidth;
ctx.strokeStyle = borderColor;
ctx.strokeRect(boxX, boxY, shapeWidth, shapeHeight);
}
}
else if (opts.backgroundShape === 'rectangle') {
const boxX = x - shapeWidth / 2;
const boxY = y - shapeHeight / 2;
ctx.fillRect(boxX, boxY, shapeWidth, shapeHeight);
ctx.fillStyle = bgColor;
ctx.fillRect(boxX, boxY, shapeWidth, shapeHeight);
if (borderWidth > 0) {
ctx.lineWidth = borderWidth;
ctx.strokeStyle = borderColor;
ctx.strokeRect(boxX, boxY, shapeWidth, shapeHeight);
}
}
ctx.fillStyle = opts.color;
ctx.globalAlpha = (_h = opts.opacity) !== null && _h !== void 0 ? _h : 1;
ctx.fillText(opts.text, textX, textY);
return canvas.toBuffer('image/png');
}
async function addWatermark(image, opts) {
return addText(image, {
text: opts.content,
color: '#000000',
fontSize: 32,
position: opts.position,
opacity: opts.opacity,
});
}