mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
⚡ Add support for draw and allow font selection EditImage-Node
This commit is contained in:
parent
b81d175145
commit
02669c04f1
|
@ -4,17 +4,23 @@ import {
|
||||||
} from 'n8n-core';
|
} from 'n8n-core';
|
||||||
import {
|
import {
|
||||||
IDataObject,
|
IDataObject,
|
||||||
|
ILoadOptionsFunctions,
|
||||||
INodeExecutionData,
|
INodeExecutionData,
|
||||||
|
INodePropertyOptions,
|
||||||
INodeType,
|
INodeType,
|
||||||
INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import * as gm from 'gm';
|
import * as gm from 'gm';
|
||||||
import { file } from 'tmp-promise';
|
import { file } from 'tmp-promise';
|
||||||
|
import {
|
||||||
|
parse as pathParse,
|
||||||
|
} from 'path';
|
||||||
import {
|
import {
|
||||||
writeFile as fsWriteFile,
|
writeFile as fsWriteFile,
|
||||||
} from 'fs';
|
} from 'fs';
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
const fsWriteFileAsync = promisify(fsWriteFile);
|
const fsWriteFileAsync = promisify(fsWriteFile);
|
||||||
|
import * as getSystemFonts from 'get-system-fonts';
|
||||||
|
|
||||||
|
|
||||||
export class EditImage implements INodeType {
|
export class EditImage implements INodeType {
|
||||||
|
@ -57,6 +63,11 @@ export class EditImage implements INodeType {
|
||||||
value: 'composite',
|
value: 'composite',
|
||||||
description: 'Composite image on top of another one',
|
description: 'Composite image on top of another one',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Draw',
|
||||||
|
value: 'draw',
|
||||||
|
description: 'Draw on image',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'Get Information',
|
name: 'Get Information',
|
||||||
value: 'information',
|
value: 'information',
|
||||||
|
@ -96,6 +107,140 @@ export class EditImage implements INodeType {
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// draw
|
||||||
|
// ----------------------------------
|
||||||
|
{
|
||||||
|
displayName: 'Primitive',
|
||||||
|
name: 'primitive',
|
||||||
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'draw',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Line',
|
||||||
|
value: 'line',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Rectangle',
|
||||||
|
value: 'rectangle',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'rectangle',
|
||||||
|
description: 'The primitive to draw.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Color',
|
||||||
|
name: 'color',
|
||||||
|
type: 'color',
|
||||||
|
default: '#ff000000',
|
||||||
|
typeOptions: {
|
||||||
|
showAlpha: true,
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'draw',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The color of the primitive to draw',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Start Position X',
|
||||||
|
name: 'startPositionX',
|
||||||
|
type: 'number',
|
||||||
|
default: 50,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'draw',
|
||||||
|
],
|
||||||
|
primitive: [
|
||||||
|
'line',
|
||||||
|
'rectangle',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'X (horizontal) start position of the primitive.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Start Position Y',
|
||||||
|
name: 'startPositionY',
|
||||||
|
type: 'number',
|
||||||
|
default: 50,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'draw',
|
||||||
|
],
|
||||||
|
primitive: [
|
||||||
|
'line',
|
||||||
|
'rectangle',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'Y (horizontal) start position of the primitive.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'End Position X',
|
||||||
|
name: 'endPositionX',
|
||||||
|
type: 'number',
|
||||||
|
default: 250,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'draw',
|
||||||
|
],
|
||||||
|
primitive: [
|
||||||
|
'line',
|
||||||
|
'rectangle',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'X (horizontal) end position of the primitive.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'End Position Y',
|
||||||
|
name: 'endPositionY',
|
||||||
|
type: 'number',
|
||||||
|
default: 250,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'draw',
|
||||||
|
],
|
||||||
|
primitive: [
|
||||||
|
'line',
|
||||||
|
'rectangle',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'Y (horizontal) end position of the primitive.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Corner Radius',
|
||||||
|
name: 'cornerRadius',
|
||||||
|
type: 'number',
|
||||||
|
default: 0,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'draw',
|
||||||
|
],
|
||||||
|
primitive: [
|
||||||
|
'rectangle',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'The radius of the corner to create round corners.',
|
||||||
|
},
|
||||||
|
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
// text
|
// text
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
|
@ -501,7 +646,7 @@ export class EditImage implements INodeType {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
description: 'The color to use for the background when image gets rotated by anything which is not a multiple of 90..',
|
description: 'The color to use for the background when image gets rotated by anything which is not a multiple of 90.',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
@ -558,6 +703,23 @@ export class EditImage implements INodeType {
|
||||||
default: '',
|
default: '',
|
||||||
description: 'File name to set in binary data.',
|
description: 'File name to set in binary data.',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Font',
|
||||||
|
name: 'font',
|
||||||
|
type: 'options',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
'/operation': [
|
||||||
|
'text',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'getFonts',
|
||||||
|
},
|
||||||
|
default: 'default',
|
||||||
|
description: 'The font to use.',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Format',
|
displayName: 'Format',
|
||||||
name: 'format',
|
name: 'format',
|
||||||
|
@ -613,6 +775,40 @@ export class EditImage implements INodeType {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
methods = {
|
||||||
|
loadOptions: {
|
||||||
|
async getFonts(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
const files = await getSystemFonts();
|
||||||
|
const returnData: INodePropertyOptions[] = [];
|
||||||
|
|
||||||
|
files.forEach((file: string) => {
|
||||||
|
const pathParts = pathParse(file);
|
||||||
|
returnData.push({
|
||||||
|
name: pathParts.name,
|
||||||
|
value: file,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
returnData.sort((a, b) => {
|
||||||
|
if (a.name < b.name) { return -1; }
|
||||||
|
if (a.name > b.name) { return 1; }
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
returnData.unshift({
|
||||||
|
name: 'default',
|
||||||
|
value: 'default',
|
||||||
|
});
|
||||||
|
|
||||||
|
return returnData;
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
async executeSingle(this: IExecuteSingleFunctions): Promise<INodeExecutionData> {
|
async executeSingle(this: IExecuteSingleFunctions): Promise<INodeExecutionData> {
|
||||||
const item = this.getInputData();
|
const item = this.getInputData();
|
||||||
|
|
||||||
|
@ -670,7 +866,24 @@ export class EditImage implements INodeType {
|
||||||
const positionY = this.getNodeParameter('positionY') as number;
|
const positionY = this.getNodeParameter('positionY') as number;
|
||||||
|
|
||||||
gmInstance = gmInstance.crop(width, height, positionX, positionY);
|
gmInstance = gmInstance.crop(width, height, positionX, positionY);
|
||||||
} else if (operation === 'information') {
|
} else if (operation === 'draw') {
|
||||||
|
const startPositionX = this.getNodeParameter('startPositionX') as number;
|
||||||
|
const startPositionY = this.getNodeParameter('startPositionY') as number;
|
||||||
|
const endPositionX = this.getNodeParameter('endPositionX') as number;
|
||||||
|
const endPositionY = this.getNodeParameter('endPositionY') as number;
|
||||||
|
const primitive = this.getNodeParameter('primitive') as string;
|
||||||
|
const color = this.getNodeParameter('color') as string;
|
||||||
|
|
||||||
|
gmInstance = gmInstance.fill(color);
|
||||||
|
|
||||||
|
if (primitive === 'line') {
|
||||||
|
gmInstance = gmInstance.drawLine(startPositionX, startPositionY, endPositionX, endPositionY);
|
||||||
|
} else if (primitive === 'rectangle') {
|
||||||
|
const cornerRadius = this.getNodeParameter('cornerRadius') as number;
|
||||||
|
gmInstance = gmInstance.drawRectangle(startPositionX, startPositionY, endPositionX, endPositionY, cornerRadius || undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (operation === 'information') {
|
||||||
const imageData = await new Promise<IDataObject>((resolve, reject) => {
|
const imageData = await new Promise<IDataObject>((resolve, reject) => {
|
||||||
gmInstance = gmInstance.identify((error, imageData) => {
|
gmInstance = gmInstance.identify((error, imageData) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
|
@ -738,6 +951,10 @@ export class EditImage implements INodeType {
|
||||||
// Combine the lines to a single string
|
// Combine the lines to a single string
|
||||||
const renderText = lines.join('\n');
|
const renderText = lines.join('\n');
|
||||||
|
|
||||||
|
if (options.font && options.font !== 'default') {
|
||||||
|
gmInstance = gmInstance.font(options.font as string);
|
||||||
|
}
|
||||||
|
|
||||||
gmInstance = gmInstance
|
gmInstance = gmInstance
|
||||||
.fill(fontColor)
|
.fill(fontColor)
|
||||||
.fontSize(fontSize)
|
.fontSize(fontSize)
|
||||||
|
|
|
@ -453,6 +453,7 @@
|
||||||
"@types/redis": "^2.8.11",
|
"@types/redis": "^2.8.11",
|
||||||
"@types/request-promise-native": "~1.0.15",
|
"@types/request-promise-native": "~1.0.15",
|
||||||
"@types/ssh2-sftp-client": "^5.1.0",
|
"@types/ssh2-sftp-client": "^5.1.0",
|
||||||
|
"@types/tmp": "^0.2.0",
|
||||||
"@types/uuid": "^3.4.6",
|
"@types/uuid": "^3.4.6",
|
||||||
"@types/xml2js": "^0.4.3",
|
"@types/xml2js": "^0.4.3",
|
||||||
"gulp": "^4.0.0",
|
"gulp": "^4.0.0",
|
||||||
|
@ -471,6 +472,7 @@
|
||||||
"cron": "^1.7.2",
|
"cron": "^1.7.2",
|
||||||
"eventsource": "^1.0.7",
|
"eventsource": "^1.0.7",
|
||||||
"formidable": "^1.2.1",
|
"formidable": "^1.2.1",
|
||||||
|
"get-system-fonts": "^2.0.2",
|
||||||
"glob-promise": "^3.4.0",
|
"glob-promise": "^3.4.0",
|
||||||
"gm": "^1.23.1",
|
"gm": "^1.23.1",
|
||||||
"imap-simple": "^4.3.0",
|
"imap-simple": "^4.3.0",
|
||||||
|
|
Loading…
Reference in a new issue