diff --git a/packages/nodes-base/nodes/EditImage.node.ts b/packages/nodes-base/nodes/EditImage.node.ts
index f11aee8737..d5366bda9e 100644
--- a/packages/nodes-base/nodes/EditImage.node.ts
+++ b/packages/nodes-base/nodes/EditImage.node.ts
@@ -6,6 +6,7 @@ import {
IDataObject,
ILoadOptionsFunctions,
INodeExecutionData,
+ INodeProperties,
INodePropertyOptions,
INodeType,
INodeTypeDescription,
@@ -17,12 +18,701 @@ import {
} from 'path';
import {
writeFile as fsWriteFile,
+ writeFileSync as fsWriteFileSync,
} from 'fs';
import { promisify } from 'util';
const fsWriteFileAsync = promisify(fsWriteFile);
import * as getSystemFonts from 'get-system-fonts';
+const nodeOperations: INodePropertyOptions[] = [
+ {
+ name: 'Blur',
+ value: 'blur',
+ description: 'Adds a blur to the image and so makes it less sharp',
+ },
+ {
+ name: 'Border',
+ value: 'border',
+ description: 'Adds a border to the image',
+ },
+ {
+ name: 'Composite',
+ value: 'composite',
+ description: 'Composite image on top of another one',
+ },
+ {
+ name: 'Create',
+ value: 'create',
+ description: 'Create a new image',
+ },
+ {
+ name: 'Crop',
+ value: 'crop',
+ description: 'Crops the image',
+ },
+ {
+ name: 'Draw',
+ value: 'draw',
+ description: 'Draw on image',
+ },
+ {
+ name: 'Rotate',
+ value: 'rotate',
+ description: 'Rotate image',
+ },
+ {
+ name: 'Resize',
+ value: 'resize',
+ description: 'Change the size of image',
+ },
+ {
+ name: 'Shear',
+ value: 'shear',
+ description: 'Shear image along the X or Y axis',
+ },
+ {
+ name: 'Text',
+ value: 'text',
+ description: 'Adds text to image',
+ },
+];
+
+
+const nodeOperationOptions: INodeProperties[] = [
+
+ // ----------------------------------
+ // create
+ // ----------------------------------
+ {
+ displayName: 'Background Color',
+ name: 'backgroundColor',
+ type: 'color',
+ default: '#ffffff00',
+ typeOptions: {
+ showAlpha: true,
+ },
+ displayOptions: {
+ show: {
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ description: 'The background color of the image to create.',
+ },
+ {
+ displayName: 'Image Width',
+ name: 'width',
+ type: 'number',
+ default: 50,
+ typeOptions: {
+ minValue: 1,
+ },
+ displayOptions: {
+ show: {
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ description: 'The width of the image to create.',
+ },
+ {
+ displayName: 'Image Height',
+ name: 'height',
+ type: 'number',
+ default: 50,
+ typeOptions: {
+ minValue: 1,
+ },
+ displayOptions: {
+ show: {
+ operation: [
+ 'create',
+ ],
+ },
+ },
+ description: 'The height of the image to create.',
+ },
+
+
+ // ----------------------------------
+ // 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
+ // ----------------------------------
+ {
+ displayName: 'Text',
+ name: 'text',
+ typeOptions: {
+ rows: 5,
+ },
+ type: 'string',
+ default: '',
+ placeholder: 'Text to render',
+ displayOptions: {
+ show: {
+ operation: [
+ 'text',
+ ],
+ },
+ },
+ description: 'Text to write on the image.',
+ },
+ {
+ displayName: 'Font Size',
+ name: 'fontSize',
+ type: 'number',
+ default: 18,
+ displayOptions: {
+ show: {
+ operation: [
+ 'text',
+ ],
+ },
+ },
+ description: 'Size of the text.',
+ },
+ {
+ displayName: 'Font Color',
+ name: 'fontColor',
+ type: 'color',
+ default: '#000000',
+ displayOptions: {
+ show: {
+ operation: [
+ 'text',
+ ],
+ },
+ },
+ description: 'Color of the text.',
+ },
+ {
+ displayName: 'Position X',
+ name: 'positionX',
+ type: 'number',
+ default: 50,
+ displayOptions: {
+ show: {
+ operation: [
+ 'text',
+ ],
+ },
+ },
+ description: 'X (horizontal) position of the text.',
+ },
+ {
+ displayName: 'Position Y',
+ name: 'positionY',
+ type: 'number',
+ default: 50,
+ displayOptions: {
+ show: {
+ operation: [
+ 'text',
+ ],
+ },
+ },
+ description: 'Y (vertical) position of the text.',
+ },
+ {
+ displayName: 'Max Line Length',
+ name: 'lineLength',
+ type: 'number',
+ typeOptions: {
+ minValue: 1,
+ },
+ default: 80,
+ displayOptions: {
+ show: {
+ operation: [
+ 'text',
+ ],
+ },
+ },
+ description: 'Max amount of characters in a line before a
line-break should get added.',
+ },
+
+ // ----------------------------------
+ // blur
+ // ----------------------------------
+ {
+ displayName: 'Blur',
+ name: 'blur',
+ type: 'number',
+ typeOptions: {
+ minValue: 0,
+ maxValue: 1000,
+ },
+ default: 5,
+ displayOptions: {
+ show: {
+ operation: [
+ 'blur',
+ ],
+ },
+ },
+ description: 'How strong the blur should be',
+ },
+ {
+ displayName: 'Sigma',
+ name: 'sigma',
+ type: 'number',
+ typeOptions: {
+ minValue: 0,
+ maxValue: 1000,
+ },
+ default: 2,
+ displayOptions: {
+ show: {
+ operation: [
+ 'blur',
+ ],
+ },
+ },
+ description: 'The sigma of the blur',
+ },
+
+
+ // ----------------------------------
+ // border
+ // ----------------------------------
+ {
+ displayName: 'Border Width',
+ name: 'borderWidth',
+ type: 'number',
+ default: 10,
+ displayOptions: {
+ show: {
+ operation: [
+ 'border',
+ ],
+ },
+ },
+ description: 'The width of the border',
+ },
+ {
+ displayName: 'Border Height',
+ name: 'borderHeight',
+ type: 'number',
+ default: 10,
+ displayOptions: {
+ show: {
+ operation: [
+ 'border',
+ ],
+ },
+ },
+ description: 'The height of the border',
+ },
+ {
+ displayName: 'Border Color',
+ name: 'borderColor',
+ type: 'color',
+ default: '#000000',
+ displayOptions: {
+ show: {
+ operation: [
+ 'border',
+ ],
+ },
+ },
+ description: 'Color of the border.',
+ },
+
+
+ // ----------------------------------
+ // composite
+ // ----------------------------------
+ {
+ displayName: 'Composite Image Property',
+ name: 'dataPropertyNameComposite',
+ type: 'string',
+ default: '',
+ placeholder: 'data2',
+ displayOptions: {
+ show: {
+ operation: [
+ 'composite',
+ ],
+ },
+ },
+ description: 'The name of the binary property which contains the data of the image to
composite on top of image which is found in Property Name.',
+ },
+ {
+ displayName: 'Position X',
+ name: 'positionX',
+ type: 'number',
+ default: 0,
+ displayOptions: {
+ show: {
+ operation: [
+ 'composite',
+ ],
+ },
+ },
+ description: 'X (horizontal) position of composite image.',
+ },
+ {
+ displayName: 'Position Y',
+ name: 'positionY',
+ type: 'number',
+ default: 0,
+ displayOptions: {
+ show: {
+ operation: [
+ 'composite',
+ ],
+ },
+ },
+ description: 'Y (vertical) position of composite image.',
+ },
+
+ // ----------------------------------
+ // crop
+ // ----------------------------------
+ {
+ displayName: 'Width',
+ name: 'width',
+ type: 'number',
+ default: 500,
+ displayOptions: {
+ show: {
+ operation: [
+ 'crop',
+ ],
+ },
+ },
+ description: 'Crop width',
+ },
+ {
+ displayName: 'Height',
+ name: 'height',
+ type: 'number',
+ default: 500,
+ displayOptions: {
+ show: {
+ operation: [
+ 'crop',
+ ],
+ },
+ },
+ description: 'Crop height',
+ },
+ {
+ displayName: 'Position X',
+ name: 'positionX',
+ type: 'number',
+ default: 0,
+ displayOptions: {
+ show: {
+ operation: [
+ 'crop',
+ ],
+ },
+ },
+ description: 'X (horizontal) position to crop from.',
+ },
+ {
+ displayName: 'Position Y',
+ name: 'positionY',
+ type: 'number',
+ default: 0,
+ displayOptions: {
+ show: {
+ operation: [
+ 'crop',
+ ],
+ },
+ },
+ description: 'Y (vertical) position to crop from.',
+ },
+
+ // ----------------------------------
+ // resize
+ // ----------------------------------
+ {
+ displayName: 'Width',
+ name: 'width',
+ type: 'number',
+ default: 500,
+ displayOptions: {
+ show: {
+ operation: [
+ 'resize',
+ ],
+ },
+ },
+ description: 'New width of the image',
+ },
+ {
+ displayName: 'Height',
+ name: 'height',
+ type: 'number',
+ default: 500,
+ displayOptions: {
+ show: {
+ operation: [
+ 'resize',
+ ],
+ },
+ },
+ description: 'New height of the image',
+ },
+ {
+ displayName: 'Option',
+ name: 'resizeOption',
+ type: 'options',
+ options: [
+ {
+ name: 'Ignore Aspect Ratio',
+ value: 'ignoreAspectRatio',
+ description: 'Ignore aspect ratio and resize exactly to specified values',
+ },
+ {
+ name: 'Maximum area',
+ value: 'maximumArea',
+ description: 'Specified values are maximum area',
+ },
+ {
+ name: 'Minimum Area',
+ value: 'minimumArea',
+ description: 'Specified values are minimum area',
+ },
+ {
+ name: 'Only if larger',
+ value: 'onlyIfLarger',
+ description: 'Resize only if image is larger than width or height',
+ },
+ {
+ name: 'Only if smaller',
+ value: 'onlyIfSmaller',
+ description: 'Resize only if image is smaller than width or height',
+ },
+ {
+ name: 'Percent',
+ value: 'percent',
+ description: 'Width and height are specified in percents.',
+ },
+ ],
+ default: 'maximumArea',
+ displayOptions: {
+ show: {
+ operation: [
+ 'resize',
+ ],
+ },
+ },
+ description: 'How to resize the image.',
+ },
+
+ // ----------------------------------
+ // rotate
+ // ----------------------------------
+ {
+ displayName: 'Rotate',
+ name: 'rotate',
+ type: 'number',
+ typeOptions: {
+ minValue: -360,
+ maxValue: 360,
+ },
+ default: 0,
+ displayOptions: {
+ show: {
+ operation: [
+ 'rotate',
+ ],
+ },
+ },
+ description: 'How much the image should be rotated',
+ },
+ {
+ displayName: 'Background Color',
+ name: 'backgroundColor',
+ type: 'color',
+ default: '#ffffffff',
+ typeOptions: {
+ showAlpha: true,
+ },
+ displayOptions: {
+ show: {
+ operation: [
+ 'rotate',
+ ],
+ },
+ },
+ description: 'The color to use for the background when image gets rotated by anything which is not a multiple of 90.',
+ },
+
+
+ // ----------------------------------
+ // shear
+ // ----------------------------------
+ {
+ displayName: 'Degrees X',
+ name: 'degreesX',
+ type: 'number',
+ default: 0,
+ displayOptions: {
+ show: {
+ operation: [
+ 'shear',
+ ],
+ },
+ },
+ description: 'X (horizontal) shear degrees.',
+ },
+ {
+ displayName: 'Degrees Y',
+ name: 'degreesY',
+ type: 'number',
+ default: 0,
+ displayOptions: {
+ show: {
+ operation: [
+ 'shear',
+ ],
+ },
+ },
+ description: 'Y (vertical) shear degrees.',
+ },
+];
+
+
export class EditImage implements INodeType {
description: INodeTypeDescription = {
displayName: 'Edit Image',
@@ -43,66 +733,25 @@ export class EditImage implements INodeType {
name: 'operation',
type: 'options',
options: [
- {
- name: 'Blur',
- value: 'blur',
- description: 'Adds a blur to the image and so makes it less sharp',
- },
- {
- name: 'Border',
- value: 'border',
- description: 'Adds a border to the image',
- },
- {
- name: 'Create',
- value: 'create',
- description: 'Create a new image',
- },
- {
- name: 'Crop',
- value: 'crop',
- description: 'Crops the image',
- },
- {
- name: 'Composite',
- value: 'composite',
- description: 'Composite image on top of another one',
- },
- {
- name: 'Draw',
- value: 'draw',
- description: 'Draw on image',
- },
{
name: 'Get Information',
value: 'information',
description: 'Returns image information like resolution',
},
{
- name: 'Rotate',
- value: 'rotate',
- description: 'Rotate image',
+ name: 'Multi Step',
+ value: 'multiStep',
+ description: 'Perform multiple operations',
},
- {
- name: 'Resize',
- value: 'resize',
- description: 'Change the size of image',
- },
- {
- name: 'Shear',
- value: 'shear',
- description: 'Shear image along the X or Y axis',
- },
- {
- name: 'Text',
- value: 'text',
- description: 'Adds text to image',
- },
- ],
+ ...nodeOperations,
+ ].sort((a, b) => {
+ if ((a as INodePropertyOptions).name.toLowerCase() < (b as INodePropertyOptions).name.toLowerCase()) { return -1; }
+ if ((a as INodePropertyOptions).name.toLowerCase() > (b as INodePropertyOptions).name.toLowerCase()) { return 1; }
+ return 0;
+ }) as INodePropertyOptions[],
default: 'border',
description: 'The operation to perform.',
},
-
{
displayName: 'Property Name',
name: 'dataPropertyName',
@@ -113,636 +762,62 @@ export class EditImage implements INodeType {
// ----------------------------------
- // create
+ // multiStep
// ----------------------------------
{
- displayName: 'Background Color',
- name: 'backgroundColor',
- type: 'color',
- default: '#ffffff00',
+ displayName: 'Operations',
+ name: 'operations',
+ placeholder: 'Add Operation',
+ type: 'fixedCollection',
typeOptions: {
- showAlpha: true,
+ multipleValues: true,
},
displayOptions: {
show: {
operation: [
- 'create',
- ],
- },
- },
- description: 'The background color of the image to create.',
- },
- {
- displayName: 'Image Width',
- name: 'width',
- type: 'number',
- default: 50,
- typeOptions: {
- minValue: 1,
- },
- displayOptions: {
- show: {
- operation: [
- 'create',
- ],
- },
- },
- description: 'The width of the image to create.',
- },
- {
- displayName: 'Image Height',
- name: 'height',
- type: 'number',
- default: 50,
- typeOptions: {
- minValue: 1,
- },
- displayOptions: {
- show: {
- operation: [
- 'create',
- ],
- },
- },
- description: 'The height of the image to create.',
- },
-
-
- // ----------------------------------
- // draw
- // ----------------------------------
- {
- displayName: 'Primitive',
- name: 'primitive',
- type: 'options',
- displayOptions: {
- show: {
- operation: [
- 'draw',
+ 'multiStep',
],
},
},
+ description: 'The operations to perform.',
+ default: {},
options: [
{
- name: 'Line',
- value: 'line',
- },
- {
- name: 'Rectangle',
- value: 'rectangle',
+ name: 'operations',
+ displayName: 'Operations',
+ values: [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ options: nodeOperations,
+ default: '',
+ description: 'The operation to perform.',
+ },
+ ...nodeOperationOptions,
+ {
+ displayName: 'Font',
+ name: 'font',
+ type: 'options',
+ displayOptions: {
+ show: {
+ 'operation': [
+ 'text',
+ ],
+ },
+ },
+ typeOptions: {
+ loadOptionsMethod: 'getFonts',
+ },
+ default: 'default',
+ description: 'The font to use.',
+ },
+ ],
},
],
- 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
- // ----------------------------------
- {
- displayName: 'Text',
- name: 'text',
- typeOptions: {
- rows: 5,
- },
- type: 'string',
- default: '',
- placeholder: 'Text to render',
- displayOptions: {
- show: {
- operation: [
- 'text',
- ],
- },
- },
- description: 'Text to write on the image.',
- },
- {
- displayName: 'Font Size',
- name: 'fontSize',
- type: 'number',
- default: 18,
- displayOptions: {
- show: {
- operation: [
- 'text',
- ],
- },
- },
- description: 'Size of the text.',
- },
- {
- displayName: 'Font Color',
- name: 'fontColor',
- type: 'color',
- default: '#000000',
- displayOptions: {
- show: {
- operation: [
- 'text',
- ],
- },
- },
- description: 'Color of the text.',
- },
- {
- displayName: 'Position X',
- name: 'positionX',
- type: 'number',
- default: 50,
- displayOptions: {
- show: {
- operation: [
- 'text',
- ],
- },
- },
- description: 'X (horizontal) position of the text.',
- },
- {
- displayName: 'Position Y',
- name: 'positionY',
- type: 'number',
- default: 50,
- displayOptions: {
- show: {
- operation: [
- 'text',
- ],
- },
- },
- description: 'Y (vertical) position of the text.',
- },
- {
- displayName: 'Max Line Length',
- name: 'lineLength',
- type: 'number',
- typeOptions: {
- minValue: 1,
- },
- default: 80,
- displayOptions: {
- show: {
- operation: [
- 'text',
- ],
- },
- },
- description: 'Max amount of characters in a line before a
line-break should get added.',
- },
-
-
- // ----------------------------------
- // blur
- // ----------------------------------
- {
- displayName: 'Blur',
- name: 'blur',
- type: 'number',
- typeOptions: {
- minValue: 0,
- maxValue: 1000,
- },
- default: 5,
- displayOptions: {
- show: {
- operation: [
- 'blur',
- ],
- },
- },
- description: 'How strong the blur should be',
- },
- {
- displayName: 'Sigma',
- name: 'sigma',
- type: 'number',
- typeOptions: {
- minValue: 0,
- maxValue: 1000,
- },
- default: 2,
- displayOptions: {
- show: {
- operation: [
- 'blur',
- ],
- },
- },
- description: 'The sigma of the blur',
- },
-
-
- // ----------------------------------
- // border
- // ----------------------------------
- {
- displayName: 'Border Width',
- name: 'borderWidth',
- type: 'number',
- default: 10,
- displayOptions: {
- show: {
- operation: [
- 'border',
- ],
- },
- },
- description: 'The width of the border',
- },
- {
- displayName: 'Border Height',
- name: 'borderHeight',
- type: 'number',
- default: 10,
- displayOptions: {
- show: {
- operation: [
- 'border',
- ],
- },
- },
- description: 'The height of the border',
- },
- {
- displayName: 'Border Color',
- name: 'borderColor',
- type: 'color',
- default: '#000000',
- displayOptions: {
- show: {
- operation: [
- 'border',
- ],
- },
- },
- description: 'Color of the border.',
- },
-
-
- // ----------------------------------
- // composite
- // ----------------------------------
- {
- displayName: 'Composite Image Property',
- name: 'dataPropertyNameComposite',
- type: 'string',
- default: '',
- placeholder: 'data2',
- displayOptions: {
- show: {
- operation: [
- 'composite',
- ],
- },
- },
- description: 'The name of the binary property which contains the data of the image to
composite on top of image which is found in Property Name.',
- },
- {
- displayName: 'Position X',
- name: 'positionX',
- type: 'number',
- default: 0,
- displayOptions: {
- show: {
- operation: [
- 'composite',
- ],
- },
- },
- description: 'X (horizontal) position of composite image.',
- },
- {
- displayName: 'Position Y',
- name: 'positionY',
- type: 'number',
- default: 0,
- displayOptions: {
- show: {
- operation: [
- 'composite',
- ],
- },
- },
- description: 'Y (vertical) position of composite image.',
- },
-
- // ----------------------------------
- // crop
- // ----------------------------------
- {
- displayName: 'Width',
- name: 'width',
- type: 'number',
- default: 500,
- displayOptions: {
- show: {
- operation: [
- 'crop',
- ],
- },
- },
- description: 'Crop width',
- },
- {
- displayName: 'Height',
- name: 'height',
- type: 'number',
- default: 500,
- displayOptions: {
- show: {
- operation: [
- 'crop',
- ],
- },
- },
- description: 'Crop height',
- },
- {
- displayName: 'Position X',
- name: 'positionX',
- type: 'number',
- default: 0,
- displayOptions: {
- show: {
- operation: [
- 'crop',
- ],
- },
- },
- description: 'X (horizontal) position to crop from.',
- },
- {
- displayName: 'Position Y',
- name: 'positionY',
- type: 'number',
- default: 0,
- displayOptions: {
- show: {
- operation: [
- 'crop',
- ],
- },
- },
- description: 'Y (vertical) position to crop from.',
- },
-
- // ----------------------------------
- // resize
- // ----------------------------------
- {
- displayName: 'Width',
- name: 'width',
- type: 'number',
- default: 500,
- displayOptions: {
- show: {
- operation: [
- 'resize',
- ],
- },
- },
- description: 'New width of the image',
- },
- {
- displayName: 'Height',
- name: 'height',
- type: 'number',
- default: 500,
- displayOptions: {
- show: {
- operation: [
- 'resize',
- ],
- },
- },
- description: 'New height of the image',
- },
- {
- displayName: 'Option',
- name: 'resizeOption',
- type: 'options',
- options: [
- {
- name: 'Ignore Aspect Ratio',
- value: 'ignoreAspectRatio',
- description: 'Ignore aspect ratio and resize exactly to specified values',
- },
- {
- name: 'Maximum area',
- value: 'maximumArea',
- description: 'Specified values are maximum area',
- },
- {
- name: 'Minimum Area',
- value: 'minimumArea',
- description: 'Specified values are minimum area',
- },
- {
- name: 'Only if larger',
- value: 'onlyIfLarger',
- description: 'Resize only if image is larger than width or height',
- },
- {
- name: 'Only if smaller',
- value: 'onlyIfSmaller',
- description: 'Resize only if image is smaller than width or height',
- },
- {
- name: 'Percent',
- value: 'percent',
- description: 'Width and height are specified in percents.',
- },
- ],
- default: 'maximumArea',
- displayOptions: {
- show: {
- operation: [
- 'resize',
- ],
- },
- },
- description: 'How to resize the image.',
- },
-
- // ----------------------------------
- // rotate
- // ----------------------------------
- {
- displayName: 'Rotate',
- name: 'rotate',
- type: 'number',
- typeOptions: {
- minValue: -360,
- maxValue: 360,
- },
- default: 0,
- displayOptions: {
- show: {
- operation: [
- 'rotate',
- ],
- },
- },
- description: 'How much the image should be rotated',
- },
- {
- displayName: 'Background Color',
- name: 'backgroundColor',
- type: 'color',
- default: '#ffffffff',
- typeOptions: {
- showAlpha: true,
- },
- displayOptions: {
- show: {
- operation: [
- 'rotate',
- ],
- },
- },
- description: 'The color to use for the background when image gets rotated by anything which is not a multiple of 90.',
- },
-
-
- // ----------------------------------
- // shear
- // ----------------------------------
- {
- displayName: 'Degrees X',
- name: 'degreesX',
- type: 'number',
- default: 0,
- displayOptions: {
- show: {
- operation: [
- 'shear',
- ],
- },
- },
- description: 'X (horizontal) shear degrees.',
- },
- {
- displayName: 'Degrees Y',
- name: 'degreesY',
- type: 'number',
- default: 0,
- displayOptions: {
- show: {
- operation: [
- 'shear',
- ],
- },
- },
- description: 'Y (vertical) shear degrees.',
},
+ ...nodeOperationOptions,
{
displayName: 'Options',
name: 'options',
@@ -882,17 +957,91 @@ export class EditImage implements INodeType {
const options = this.getNodeParameter('options', {}) as IDataObject;
- let gmInstance: gm.State;
- if (operation === 'create') {
- const backgroundColor = this.getNodeParameter('backgroundColor') as string;
- const width = this.getNodeParameter('width') as number;
- const height = this.getNodeParameter('height') as number;
+ const cleanupFunctions: Array<() => void> = [];
- gmInstance = gm(width, height, backgroundColor);
- if (!options.format) {
- options.format = 'png';
- }
+ let gmInstance: gm.State;
+
+ const requiredOperationParameters: {
+ [key: string]: string[],
+ } = {
+ blur: [
+ 'blur',
+ 'sigma',
+ ],
+ create: [
+ 'backgroundColor',
+ 'height',
+ 'width',
+ ],
+ crop: [
+ 'height',
+ 'positionX',
+ 'positionY',
+ 'width',
+ ],
+ composite: [
+ 'dataPropertyNameComposite',
+ 'positionX',
+ 'positionY',
+ ],
+ draw: [
+ 'color',
+ 'cornerRadius',
+ 'endPositionX',
+ 'endPositionY',
+ 'primitive',
+ 'startPositionX',
+ 'startPositionY',
+ ],
+ information: [],
+ resize: [
+ 'height',
+ 'resizeOption',
+ 'width',
+ ],
+ rotate: [
+ 'backgroundColor',
+ 'rotate',
+ ],
+ shear: [
+ 'degreesX',
+ 'degreesY',
+ ],
+ text: [
+ 'font',
+ 'fontColor',
+ 'fontSize',
+ 'lineLength',
+ 'positionX',
+ 'positionY',
+ 'text',
+ ],
+ };
+
+ let operations: IDataObject[] = [];
+ if (operation === 'multiStep') {
+ // Operation parameters are already in the correct format
+ const operationsData = this.getNodeParameter('operations', { operations: [] }) as IDataObject;
+ operations = operationsData.operations as IDataObject[];
} else {
+ // Operation parameters have to first get collected
+ const operationParameters: IDataObject = {};
+ requiredOperationParameters[operation].forEach(parameterName => {
+ try {
+ operationParameters[parameterName] = this.getNodeParameter(parameterName);
+ } catch (e) {}
+ });
+
+ operations = [
+ {
+ operation,
+ ...operationParameters,
+ }
+ ];
+ }
+
+ if (operations[0].operation !== 'create') {
+ // "create" generates a new image so does not require any incoming data.
if (item.binary === undefined) {
throw new Error('Item does not contain any binary data.');
}
@@ -905,60 +1054,8 @@ export class EditImage implements INodeType {
gmInstance = gmInstance.background('transparent');
}
- const cleanupFunctions: Array<() => void> = [];
-
- if (operation === 'blur') {
- const blur = this.getNodeParameter('blur') as number;
- const sigma = this.getNodeParameter('sigma') as number;
- gmInstance = gmInstance.blur(blur, sigma);
- } else if (operation === 'border') {
- const borderWidth = this.getNodeParameter('borderWidth') as number;
- const borderHeight = this.getNodeParameter('borderHeight') as number;
- const borderColor = this.getNodeParameter('borderColor') as string;
-
- gmInstance = gmInstance.borderColor(borderColor).border(borderWidth, borderHeight);
- } else if (operation === 'composite') {
- const dataPropertyNameComposite = this.getNodeParameter('dataPropertyNameComposite') as string;
- const positionX = this.getNodeParameter('positionX') as number;
- const positionY = this.getNodeParameter('positionY') as number;
-
- const geometryString = (positionX >= 0 ? '+' : '') + positionX + (positionY >= 0 ? '+' : '') + positionY;
-
- if (item.binary![dataPropertyNameComposite as string] === undefined) {
- throw new Error(`Item does not contain any binary data with the name "${dataPropertyNameComposite}".`);
- }
-
- const { fd, path, cleanup } = await file();
- cleanupFunctions.push(cleanup);
- fsWriteFileAsync(fd, Buffer.from(item.binary![dataPropertyNameComposite as string].data, BINARY_ENCODING));
-
- gmInstance = gmInstance.composite(path).geometry(geometryString);
- } else if (operation === 'crop') {
- const width = this.getNodeParameter('width') as number;
- const height = this.getNodeParameter('height') as number;
-
- const positionX = this.getNodeParameter('positionX') as number;
- const positionY = this.getNodeParameter('positionY') as number;
-
- gmInstance = gmInstance.crop(width, height, positionX, positionY);
- } 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') {
+ if (operation === 'information') {
+ // Just return the information
const imageData = await new Promise((resolve, reject) => {
gmInstance = gmInstance.identify((error, imageData) => {
if (error) {
@@ -970,72 +1067,99 @@ export class EditImage implements INodeType {
});
item.json = imageData;
- } else if (operation === 'resize') {
- const width = this.getNodeParameter('width') as number;
- const height = this.getNodeParameter('height') as number;
- const resizeOption = this.getNodeParameter('resizeOption') as string;
+ return item;
+ }
- // By default use "maximumArea"
- let option: gm.ResizeOption = '@';
- if (resizeOption === 'ignoreAspectRatio') {
- option = '!';
- } else if (resizeOption === 'minimumArea') {
- option = '^';
- } else if (resizeOption === 'onlyIfSmaller') {
- option = '<';
- } else if (resizeOption === 'onlyIfLarger') {
- option = '>';
- } else if (resizeOption === 'percent') {
- option = '%';
- }
+ for (const operationData of operations) {
+ if (operationData.operation === 'blur') {
+ gmInstance = gmInstance!.blur(operationData.blur as number, operationData.sigma as number);
+ } else if (operationData.operation === 'border') {
+ gmInstance = gmInstance!.borderColor(operationData.borderColor as string).border(operationData.borderWidth as number, operationData.borderHeight as number);
+ } else if (operationData.operation === 'composite') {
+ const positionX = operationData.positionX as number;
+ const positionY = operationData.positionY as number;
- gmInstance = gmInstance.resize(width, height, option);
- } else if (operation === 'rotate') {
- const rotate = this.getNodeParameter('rotate') as number;
- const backgroundColor = this.getNodeParameter('backgroundColor') as string;
- gmInstance = gmInstance.rotate(backgroundColor, rotate);
- } else if (operation === 'shear') {
- const xDegrees = this.getNodeParameter('degreesX') as number;
- const yDegress = this.getNodeParameter('degreesY') as number;
- gmInstance = gmInstance.shear(xDegrees, yDegress);
- } else if (operation === 'text') {
- const fontColor = this.getNodeParameter('fontColor') as string;
- const fontSize = this.getNodeParameter('fontSize') as number;
- const lineLength = this.getNodeParameter('lineLength') as number;
- const positionX = this.getNodeParameter('positionX') as number;
- const positionY = this.getNodeParameter('positionY') as number;
- const text = this.getNodeParameter('text') as string || '';
+ const geometryString = (positionX >= 0 ? '+' : '') + positionX + (positionY >= 0 ? '+' : '') + positionY;
- // Split the text in multiple lines
- const lines: string[] = [];
- let currentLine = '';
- (text as string).split('\n').forEach((textLine: string) => {
- textLine.split(' ').forEach((textPart: string) => {
- if ((currentLine.length + textPart.length + 1) > lineLength) {
- lines.push(currentLine.trim());
- currentLine = `${textPart} `;
- return;
+ if (item.binary![operationData.dataPropertyNameComposite as string] === undefined) {
+ throw new Error(`Item does not contain any binary data with the name "${operationData.dataPropertyNameComposite}".`);
+ }
+
+ gmInstance = await gmInstance!;
+ const { fd, path, cleanup } = await file();
+ cleanupFunctions.push(cleanup);
+ await fsWriteFileAsync(fd, Buffer.from(item.binary![operationData.dataPropertyNameComposite as string].data, BINARY_ENCODING));
+
+ gmInstance = gmInstance!.composite(path).geometry(geometryString);
+ } else if (operationData.operation === 'create') {
+ gmInstance = gm(operationData.width as number, operationData.height as number, operationData.backgroundColor as string);
+ if (!options.format) {
+ options.format = 'png';
}
- currentLine += `${textPart} `;
+ } else if (operationData.operation === 'crop') {
+ gmInstance = gmInstance!.crop(operationData.width as number, operationData.height as number, operationData.positionX as number, operationData.positionY as number);
+ } else if (operationData.operation === 'draw') {
+ gmInstance = gmInstance!.fill(operationData.color as string);
+
+ if (operationData.primitive === 'line') {
+ gmInstance = gmInstance.drawLine(operationData.startPositionX as number, operationData.startPositionY as number, operationData.endPositionX as number, operationData.endPositionY as number);
+ } else if (operationData.primitive === 'rectangle') {
+ gmInstance = gmInstance.drawRectangle(operationData.startPositionX as number, operationData.startPositionY as number, operationData.endPositionX as number, operationData.endPositionY as number, operationData.cornerRadius as number || undefined);
+ }
+ } else if (operationData.operation === 'resize') {
+ const resizeOption = operationData.resizeOption as string;
+
+ // By default use "maximumArea"
+ let option: gm.ResizeOption = '@';
+ if (resizeOption === 'ignoreAspectRatio') {
+ option = '!';
+ } else if (resizeOption === 'minimumArea') {
+ option = '^';
+ } else if (resizeOption === 'onlyIfSmaller') {
+ option = '<';
+ } else if (resizeOption === 'onlyIfLarger') {
+ option = '>';
+ } else if (resizeOption === 'percent') {
+ option = '%';
+ }
+
+ gmInstance = gmInstance!.resize(operationData.width as number, operationData.height as number, option);
+ } else if (operationData.operation === 'rotate') {
+ gmInstance = gmInstance!.rotate(operationData.backgroundColor as string, operationData.rotate as number);
+ } else if (operationData.operation === 'shear') {
+ gmInstance = gmInstance!.shear(operationData.degreesX as number, operationData.degreesY as number);
+ } else if (operationData.operation === 'text') {
+ // Split the text in multiple lines
+ const lines: string[] = [];
+ let currentLine = '';
+ (operationData.text as string).split('\n').forEach((textLine: string) => {
+ textLine.split(' ').forEach((textPart: string) => {
+ if ((currentLine.length + textPart.length + 1) > (operationData.lineLength as number)) {
+ lines.push(currentLine.trim());
+ currentLine = `${textPart} `;
+ return;
+ }
+ currentLine += `${textPart} `;
+ });
+
+ lines.push(currentLine.trim());
+ currentLine = '';
});
- lines.push(currentLine.trim());
- currentLine = '';
- });
+ // Combine the lines to a single string
+ const renderText = lines.join('\n');
- // Combine the lines to a single string
- const renderText = lines.join('\n');
+ const font = options.font || operationData.font;
- if (options.font && options.font !== 'default') {
- gmInstance = gmInstance.font(options.font as string);
+ if (font && font !== 'default') {
+ gmInstance = gmInstance!.font(font as string);
+ }
+
+ gmInstance = gmInstance!
+ .fill(operationData.fontColor as string)
+ .fontSize(operationData.fontSize as number)
+ .drawText(operationData.positionX as number, operationData.positionY as number, renderText);
}
-
- gmInstance = gmInstance
- .fill(fontColor)
- .fontSize(fontSize)
- .drawText(positionX, positionY, renderText);
- } else if (operation !== 'create') {
- throw new Error(`The operation "${operation}" is not supported!`);
}
const newItem: INodeExecutionData = {
@@ -1060,11 +1184,11 @@ export class EditImage implements INodeType {
}
if (options.quality !== undefined) {
- gmInstance = gmInstance.quality(options.quality as number);
+ gmInstance = gmInstance!.quality(options.quality as number);
}
if (options.format !== undefined) {
- gmInstance = gmInstance.setFormat(options.format as string);
+ gmInstance = gmInstance!.setFormat(options.format as string);
newItem.binary![dataPropertyName as string].fileExtension = options.format as string;
newItem.binary![dataPropertyName as string].mimeType = `image/${options.format}`;
const fileName = newItem.binary![dataPropertyName as string].fileName;