image-edit-tools 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/index.d.ts +5 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +5 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/mcp/tools.d.ts.map +1 -1
  6. package/dist/mcp/tools.js +90 -0
  7. package/dist/mcp/tools.js.map +1 -1
  8. package/dist/ops/add-text.d.ts.map +1 -1
  9. package/dist/ops/add-text.js +52 -19
  10. package/dist/ops/add-text.js.map +1 -1
  11. package/dist/ops/clip-to-shape.d.ts +3 -0
  12. package/dist/ops/clip-to-shape.d.ts.map +1 -0
  13. package/dist/ops/clip-to-shape.js +58 -0
  14. package/dist/ops/clip-to-shape.js.map +1 -0
  15. package/dist/ops/draw-shape.d.ts +3 -0
  16. package/dist/ops/draw-shape.d.ts.map +1 -0
  17. package/dist/ops/draw-shape.js +54 -0
  18. package/dist/ops/draw-shape.js.map +1 -0
  19. package/dist/ops/drop-shadow.d.ts +3 -0
  20. package/dist/ops/drop-shadow.d.ts.map +1 -0
  21. package/dist/ops/drop-shadow.js +54 -0
  22. package/dist/ops/drop-shadow.js.map +1 -0
  23. package/dist/ops/gradient-overlay.d.ts +3 -0
  24. package/dist/ops/gradient-overlay.d.ts.map +1 -0
  25. package/dist/ops/gradient-overlay.js +49 -0
  26. package/dist/ops/gradient-overlay.js.map +1 -0
  27. package/dist/ops/pipeline.d.ts.map +1 -1
  28. package/dist/ops/pipeline.js +16 -0
  29. package/dist/ops/pipeline.js.map +1 -1
  30. package/dist/ops/rotate.d.ts +3 -0
  31. package/dist/ops/rotate.d.ts.map +1 -0
  32. package/dist/ops/rotate.js +25 -0
  33. package/dist/ops/rotate.js.map +1 -0
  34. package/dist/types.d.ts +93 -1
  35. package/dist/types.d.ts.map +1 -1
  36. package/package.json +1 -1
  37. package/src/index.ts +5 -0
  38. package/src/mcp/tools.ts +86 -0
  39. package/src/ops/add-text.ts +55 -16
  40. package/src/ops/clip-to-shape.ts +63 -0
  41. package/src/ops/draw-shape.ts +58 -0
  42. package/src/ops/drop-shadow.ts +60 -0
  43. package/src/ops/gradient-overlay.ts +62 -0
  44. package/src/ops/pipeline.ts +9 -0
  45. package/src/ops/rotate.ts +27 -0
  46. package/src/types.ts +100 -1
  47. package/tests/unit/clip-to-shape.test.ts +36 -0
  48. package/tests/unit/draw-shape.test.ts +34 -0
  49. package/tests/unit/drop-shadow.test.ts +42 -0
  50. package/tests/unit/gradient-overlay.test.ts +29 -0
  51. package/tests/unit/rotate.test.ts +42 -0
@@ -0,0 +1,49 @@
1
+ import sharp from 'sharp';
2
+ import { ErrorCode } from '../types.js';
3
+ import { loadImage } from '../utils/load-image.js';
4
+ import { err, ok } from '../utils/result.js';
5
+ import { getImageMetadata } from '../utils/validate.js';
6
+ function buildGradientSvg(width, height, direction, color, opacity, coverage) {
7
+ // Map direction to SVG linearGradient coordinates
8
+ const dirMap = {
9
+ 'top': { x1: '0%', y1: '0%', x2: '0%', y2: '100%' },
10
+ 'bottom': { x1: '0%', y1: '100%', x2: '0%', y2: '0%' },
11
+ 'left': { x1: '0%', y1: '0%', x2: '100%', y2: '0%' },
12
+ 'right': { x1: '100%', y1: '0%', x2: '0%', y2: '0%' },
13
+ 'top-left': { x1: '0%', y1: '0%', x2: '100%', y2: '100%' },
14
+ 'top-right': { x1: '100%', y1: '0%', x2: '0%', y2: '100%' },
15
+ 'bottom-left': { x1: '0%', y1: '100%', x2: '100%', y2: '0%' },
16
+ 'bottom-right': { x1: '100%', y1: '100%', x2: '0%', y2: '0%' },
17
+ };
18
+ const d = dirMap[direction] ?? dirMap['bottom'];
19
+ const stopOffset = Math.round(coverage * 100);
20
+ return `<svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">
21
+ <defs>
22
+ <linearGradient id="g" x1="${d.x1}" y1="${d.y1}" x2="${d.x2}" y2="${d.y2}">
23
+ <stop offset="0%" stop-color="${color}" stop-opacity="${opacity}"/>
24
+ <stop offset="${stopOffset}%" stop-color="${color}" stop-opacity="0"/>
25
+ </linearGradient>
26
+ </defs>
27
+ <rect width="${width}" height="${height}" fill="url(#g)"/>
28
+ </svg>`;
29
+ }
30
+ export async function gradientOverlay(input, options = {}) {
31
+ try {
32
+ const buffer = await loadImage(input);
33
+ const meta = await getImageMetadata(buffer);
34
+ const { width, height } = meta;
35
+ const direction = options.direction ?? 'bottom';
36
+ const color = options.color ?? '#000000';
37
+ const opacity = options.opacity ?? 0.7;
38
+ const coverage = options.coverage ?? 0.5;
39
+ const svg = buildGradientSvg(width, height, direction, color, opacity, coverage);
40
+ const output = await sharp(buffer)
41
+ .composite([{ input: Buffer.from(svg), blend: 'over' }])
42
+ .toBuffer();
43
+ return ok(output);
44
+ }
45
+ catch (e) {
46
+ return err(e.message || 'Gradient overlay failed', ErrorCode.PROCESSING_FAILED);
47
+ }
48
+ }
49
+ //# sourceMappingURL=gradient-overlay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gradient-overlay.js","sourceRoot":"","sources":["../../src/ops/gradient-overlay.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/ops/pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,iBAAiB,EAAa,MAAM,aAAa,CAAC;AAapF,wBAAsB,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,iBAAiB,EAAE,GAAG,OAAO,CAAC,WAAW,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAkD3H"}
1
+ {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/ops/pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,iBAAiB,EAAa,MAAM,aAAa,CAAC;AAiBpF,wBAAsB,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,iBAAiB,EAAE,GAAG,OAAO,CAAC,WAAW,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAsD3H"}
@@ -10,6 +10,10 @@ import { composite } from './composite.js';
10
10
  import { watermark } from './watermark.js';
11
11
  import { convert } from './convert.js';
12
12
  import { optimize } from './optimize.js';
13
+ import { rotate } from './rotate.js';
14
+ import { gradientOverlay } from './gradient-overlay.js';
15
+ import { clipToShape } from './clip-to-shape.js';
16
+ import { dropShadow } from './drop-shadow.js';
13
17
  export async function pipeline(input, operations) {
14
18
  let currentImage = input;
15
19
  for (let i = 0; i < operations.length; i++) {
@@ -50,6 +54,18 @@ export async function pipeline(input, operations) {
50
54
  case 'optimize':
51
55
  result = await optimize(currentImage, op);
52
56
  break;
57
+ case 'rotate':
58
+ result = await rotate(currentImage, op);
59
+ break;
60
+ case 'gradientOverlay':
61
+ result = await gradientOverlay(currentImage, op);
62
+ break;
63
+ case 'clipToShape':
64
+ result = await clipToShape(currentImage, op);
65
+ break;
66
+ case 'dropShadow':
67
+ result = await dropShadow(currentImage, op);
68
+ break;
53
69
  case 'removeBg': {
54
70
  const { removeBg } = await import('./remove-bg.js');
55
71
  result = await removeBg(currentImage, op);
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../../src/ops/pipeline.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../../src/ops/pipeline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { RotateOptions, ImageInput, ImageResult } from '../types.js';
2
+ export declare function rotate(input: ImageInput, options: RotateOptions): Promise<ImageResult>;
3
+ //# sourceMappingURL=rotate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rotate.d.ts","sourceRoot":"","sources":["../../src/ops/rotate.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAa,MAAM,aAAa,CAAC;AAIhF,wBAAsB,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,WAAW,CAAC,CAqB5F"}
@@ -0,0 +1,25 @@
1
+ import sharp from 'sharp';
2
+ import { ErrorCode } from '../types.js';
3
+ import { loadImage } from '../utils/load-image.js';
4
+ import { err, ok } from '../utils/result.js';
5
+ export async function rotate(input, options) {
6
+ try {
7
+ const buffer = await loadImage(input);
8
+ const angle = options.angle % 360;
9
+ if (angle === 0)
10
+ return ok(buffer);
11
+ const bgColor = options.background ?? { r: 0, g: 0, b: 0, alpha: 0 };
12
+ const background = typeof bgColor === 'string'
13
+ ? bgColor
14
+ : bgColor;
15
+ const output = await sharp(buffer)
16
+ .rotate(angle, { background })
17
+ .png()
18
+ .toBuffer();
19
+ return ok(output);
20
+ }
21
+ catch (e) {
22
+ return err(e.message || 'Rotation failed', ErrorCode.PROCESSING_FAILED);
23
+ }
24
+ }
25
+ //# sourceMappingURL=rotate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rotate.js","sourceRoot":"","sources":["../../src/ops/rotate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAA0C,SAAS,EAAE,MAAM,aAAa,CAAC;AAChF,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,oBAAoB,CAAC;AAE7C,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,KAAiB,EAAE,OAAsB;IACpE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,KAAK,CAAC,CAAC;QACtC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,GAAG,GAAG,CAAC;QAElC,IAAI,KAAK,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC;QAEnC,MAAM,OAAO,GAAG,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QACrE,MAAM,UAAU,GAAG,OAAO,OAAO,KAAK,QAAQ;YAC5C,CAAC,CAAC,OAAO;YACT,CAAC,CAAC,OAAO,CAAC;QAEZ,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC;aAC/B,MAAM,CAAC,KAAK,EAAE,EAAE,UAAU,EAAE,CAAC;aAC7B,GAAG,EAAE;aACL,QAAQ,EAAE,CAAC;QAEd,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC;IACpB,CAAC;IAAC,OAAO,CAAM,EAAE,CAAC;QAChB,OAAO,GAAG,CAAC,CAAC,CAAC,OAAO,IAAI,iBAAiB,EAAE,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAC1E,CAAC;AACH,CAAC"}
package/dist/types.d.ts CHANGED
@@ -121,6 +121,20 @@ export interface TextLayer {
121
121
  maxWidth?: number;
122
122
  lineHeight?: number;
123
123
  background?: TextBackground;
124
+ /** Letter spacing in pixels. Default: 0 */
125
+ letterSpacing?: number;
126
+ /** Text stroke (outline) */
127
+ stroke?: {
128
+ color: string;
129
+ width: number;
130
+ };
131
+ /** Text shadow */
132
+ textShadow?: {
133
+ color: string;
134
+ offsetX: number;
135
+ offsetY: number;
136
+ blur?: number;
137
+ };
124
138
  }
125
139
  export type BlendMode = 'over' | 'multiply' | 'screen' | 'overlay' | 'darken' | 'lighten';
126
140
  export interface CompositeLayer {
@@ -199,6 +213,76 @@ export interface ImageMetadata {
199
213
  density?: number;
200
214
  exif?: Record<string, unknown>;
201
215
  }
216
+ export interface RotateOptions {
217
+ /** Rotation angle in degrees (0–360). Positive = clockwise. */
218
+ angle: number;
219
+ /** Background color for exposed areas. Default: 'transparent' */
220
+ background?: string;
221
+ }
222
+ export type GradientDirection = 'top' | 'bottom' | 'left' | 'right' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
223
+ export interface GradientOverlayOptions {
224
+ /** Direction the gradient fades FROM (opaque end). Default: 'bottom' */
225
+ direction?: GradientDirection;
226
+ /** Gradient color. Default: '#000000' */
227
+ color?: string;
228
+ /** Opacity of the opaque end. 0–1. Default: 0.7 */
229
+ opacity?: number;
230
+ /** How much of the image the gradient covers. 0–1. Default: 0.5 */
231
+ coverage?: number;
232
+ }
233
+ export type ClipShape = 'circle' | 'ellipse' | 'rounded-rect';
234
+ export interface ClipToShapeOptions {
235
+ shape: ClipShape;
236
+ /** Border radius for 'rounded-rect'. Default: 32 */
237
+ borderRadius?: number;
238
+ }
239
+ export type ShapeType = 'rect' | 'circle' | 'ellipse' | 'line';
240
+ export interface DrawShapeOptions {
241
+ /** Canvas width */
242
+ width: number;
243
+ /** Canvas height */
244
+ height: number;
245
+ /** Shape to draw */
246
+ shape: ShapeType;
247
+ /** Fill color. Default: 'transparent' */
248
+ fill?: string;
249
+ /** Fill opacity. 0–1. Default: 1 */
250
+ fillOpacity?: number;
251
+ /** Stroke color */
252
+ stroke?: string;
253
+ /** Stroke width. Default: 0 */
254
+ strokeWidth?: number;
255
+ /** Border radius (rect only). Default: 0 */
256
+ borderRadius?: number;
257
+ /** Circle/ellipse center X. Defaults to canvas center */
258
+ cx?: number;
259
+ /** Circle/ellipse center Y. Defaults to canvas center */
260
+ cy?: number;
261
+ /** Circle radius or ellipse rx */
262
+ r?: number;
263
+ /** Ellipse ry */
264
+ ry?: number;
265
+ /** Line start X */
266
+ x1?: number;
267
+ /** Line start Y */
268
+ y1?: number;
269
+ /** Line end X */
270
+ x2?: number;
271
+ /** Line end Y */
272
+ y2?: number;
273
+ }
274
+ export interface DropShadowOptions {
275
+ /** Shadow color. Default: 'rgba(0,0,0,0.5)' */
276
+ color?: string;
277
+ /** Shadow offset X. Default: 4 */
278
+ offsetX?: number;
279
+ /** Shadow offset Y. Default: 4 */
280
+ offsetY?: number;
281
+ /** Blur radius. Default: 8 */
282
+ blur?: number;
283
+ /** Expand canvas to fit shadow. Default: true */
284
+ expand?: boolean;
285
+ }
202
286
  export type PipelineOperation = ({
203
287
  op: 'crop';
204
288
  } & CropOptions) | ({
@@ -226,7 +310,15 @@ export type PipelineOperation = ({
226
310
  op: 'optimize';
227
311
  } & OptimizeOptions) | ({
228
312
  op: 'removeBg';
229
- } & RemoveBgOptions);
313
+ } & RemoveBgOptions) | ({
314
+ op: 'rotate';
315
+ } & RotateOptions) | ({
316
+ op: 'gradientOverlay';
317
+ } & GradientOverlayOptions) | ({
318
+ op: 'clipToShape';
319
+ } & ClipToShapeOptions) | ({
320
+ op: 'dropShadow';
321
+ } & DropShadowOptions);
230
322
  export interface BatchOptions {
231
323
  concurrency?: number;
232
324
  onProgress?: (done: number, total: number) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;AAEA;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;AAIzC,MAAM,MAAM,EAAE,CAAC,CAAC,IAAI;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAC/D,MAAM,MAAM,GAAG,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAChE,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;AACpC,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;AAEzC,oBAAY,SAAS;IACnB,aAAa,kBAAkB;IAC/B,kBAAkB,uBAAuB;IACzC,aAAa,kBAAkB;IAC/B,YAAY,iBAAiB;IAC7B,iBAAiB,sBAAsB;IACvC,eAAe,oBAAoB;IACnC,OAAO,YAAY;CACpB;AAID,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,CAAC,EAAE,UAAU,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC1E;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC3E;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAA;CAAE,GACtF;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAIxB,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;AAC5E,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE7D,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,4EAA4E;IAC5E,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAID,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAID,MAAM,WAAW,aAAa;IAC5B,mBAAmB;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mBAAmB;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uBAAuB;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,eAAe;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAID,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;AAEpF,MAAM,MAAM,aAAa,GACrB;IAAE,MAAM,EAAE,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC,CAAA;CAAE,GACzC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAIvC,MAAM,WAAW,UAAU;IACzB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,yCAAyC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAID,MAAM,MAAM,UAAU,GAClB,UAAU,GAAG,YAAY,GAAG,WAAW,GACvC,aAAa,GAAG,QAAQ,GAAG,cAAc,GACzC,aAAa,GAAG,eAAe,GAAG,cAAc,CAAC;AAErD,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;IACpC,oCAAoC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,cAAc,CAAC;CAC7B;AAID,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE1F,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,UAAU,CAAC;IAClB,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAID,MAAM,MAAM,iBAAiB,GACzB,UAAU,GAAG,YAAY,GAAG,WAAW,GACvC,QAAQ,GACR,aAAa,GAAG,eAAe,GAAG,cAAc,GAChD,MAAM,CAAC;AAEX,MAAM,MAAM,gBAAgB,GACxB;IACE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GACD;IACE,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAIN,MAAM,MAAM,OAAO,GACf,WAAW,GAAG,OAAO,GAAG,WAAW,GACnC,MAAM,GAAG,QAAQ,GAAG,MAAM,GAC1B,WAAW,GAAG,OAAO,GAAG,WAAW,CAAC;AAExC,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB;AAID,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,CAAC;AAE9C,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yCAAyC;IACzC,YAAY,CAAC,EAAE,UAAU,CAAC;CAC3B;AAID,MAAM,WAAW,WAAW;IAC1B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAID,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AAEpE,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,YAAY,CAAC;IACrB,yBAAyB;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gCAAgC;IAChC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,wCAAwC;IACxC,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAID,MAAM,WAAW,eAAe;IAC9B,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAID,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAID,MAAM,MAAM,iBAAiB,GACzB,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,WAAW,CAAC,GAC9B,CAAC;IAAE,EAAE,EAAE,QAAQ,CAAA;CAAE,GAAG,aAAa,CAAC,GAClC,CAAC;IAAE,EAAE,EAAE,KAAK,CAAA;CAAE,GAAG,UAAU,CAAC,GAC5B,CAAC;IAAE,EAAE,EAAE,QAAQ,CAAA;CAAE,GAAG,aAAa,CAAC,GAClC,CAAC;IAAE,EAAE,EAAE,QAAQ,CAAA;CAAE,GAAG,aAAa,CAAC,GAClC,CAAC;IAAE,EAAE,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,UAAU,EAAE,CAAA;CAAE,CAAC,GAC7C,CAAC;IAAE,EAAE,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,SAAS,EAAE,CAAA;CAAE,CAAC,GACxC,CAAC;IAAE,EAAE,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,cAAc,EAAE,CAAA;CAAE,CAAC,GAC/C,CAAC;IAAE,EAAE,EAAE,WAAW,CAAA;CAAE,GAAG,gBAAgB,CAAC,GACxC,CAAC;IAAE,EAAE,EAAE,SAAS,CAAA;CAAE,GAAG,cAAc,CAAC,GACpC,CAAC;IAAE,EAAE,EAAE,UAAU,CAAA;CAAE,GAAG,eAAe,CAAC,GACtC,CAAC;IAAE,EAAE,EAAE,UAAU,CAAA;CAAE,GAAG,eAAe,CAAC,CAAC;AAE3C,MAAM,WAAW,YAAY;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACpD"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "image-edit-tools",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Deterministic image editing SDK for AI agents. Ships with MCP tools.",
5
5
  "author": "swimmingkiim",
6
6
  "homepage": "https://github.com/swimmingkiim/image-edit-tools#readme",
package/src/index.ts CHANGED
@@ -22,3 +22,8 @@ export { detectFaces } from './ops/detect-faces.js';
22
22
  export { extractText } from './ops/extract-text.js';
23
23
  export { pipeline } from './ops/pipeline.js';
24
24
  export { batch } from './ops/batch.js';
25
+ export { rotate } from './ops/rotate.js';
26
+ export { gradientOverlay } from './ops/gradient-overlay.js';
27
+ export { clipToShape } from './ops/clip-to-shape.js';
28
+ export { drawShape } from './ops/draw-shape.js';
29
+ export { dropShadow } from './ops/drop-shadow.js';
package/src/mcp/tools.ts CHANGED
@@ -219,6 +219,80 @@ export const allTools: Tool[] = [
219
219
  },
220
220
  required: ['images', 'operation', 'options']
221
221
  }
222
+ },
223
+ {
224
+ name: 'image_rotate',
225
+ description: 'Rotates an image by an arbitrary angle. Exposed areas are transparent by default.',
226
+ inputSchema: {
227
+ type: 'object',
228
+ properties: {
229
+ image: { type: 'string' },
230
+ angle: { type: 'number', description: 'Rotation angle in degrees (0-360), clockwise' },
231
+ background: { type: 'string', description: 'Background color for exposed areas. Default: transparent' }
232
+ },
233
+ required: ['image', 'angle']
234
+ }
235
+ },
236
+ {
237
+ name: 'image_gradient_overlay',
238
+ description: 'Applies a gradient overlay for text readability. Great for placing text over photos.',
239
+ inputSchema: {
240
+ type: 'object',
241
+ properties: {
242
+ image: { type: 'string' },
243
+ direction: { type: 'string', enum: ['top','bottom','left','right','top-left','top-right','bottom-left','bottom-right'] },
244
+ color: { type: 'string', description: 'Gradient color. Default: #000000' },
245
+ opacity: { type: 'number', description: '0-1. Default: 0.7' },
246
+ coverage: { type: 'number', description: '0-1 how much of image is covered. Default: 0.5' }
247
+ },
248
+ required: ['image']
249
+ }
250
+ },
251
+ {
252
+ name: 'image_clip_to_shape',
253
+ description: 'Clips an image to a shape: circle, ellipse, or rounded-rect. Perfect for profile photos.',
254
+ inputSchema: {
255
+ type: 'object',
256
+ properties: {
257
+ image: { type: 'string' },
258
+ shape: { type: 'string', enum: ['circle', 'ellipse', 'rounded-rect'] },
259
+ borderRadius: { type: 'number', description: 'For rounded-rect. Default: 32' }
260
+ },
261
+ required: ['image', 'shape']
262
+ }
263
+ },
264
+ {
265
+ name: 'image_draw_shape',
266
+ description: 'Creates a new image containing a shape (rect, circle, ellipse, line). Use with composite to layer.',
267
+ inputSchema: {
268
+ type: 'object',
269
+ properties: {
270
+ width: { type: 'number' }, height: { type: 'number' },
271
+ shape: { type: 'string', enum: ['rect', 'circle', 'ellipse', 'line'] },
272
+ fill: { type: 'string' }, fillOpacity: { type: 'number' },
273
+ stroke: { type: 'string' }, strokeWidth: { type: 'number' },
274
+ borderRadius: { type: 'number' },
275
+ cx: { type: 'number' }, cy: { type: 'number' }, r: { type: 'number' }, ry: { type: 'number' },
276
+ x1: { type: 'number' }, y1: { type: 'number' }, x2: { type: 'number' }, y2: { type: 'number' }
277
+ },
278
+ required: ['width', 'height', 'shape']
279
+ }
280
+ },
281
+ {
282
+ name: 'image_drop_shadow',
283
+ description: 'Adds a drop shadow behind the image. Expands canvas to fit shadow.',
284
+ inputSchema: {
285
+ type: 'object',
286
+ properties: {
287
+ image: { type: 'string' },
288
+ color: { type: 'string', description: 'Shadow color. Default: rgba(0,0,0,0.5)' },
289
+ offsetX: { type: 'number', description: 'Default: 4' },
290
+ offsetY: { type: 'number', description: 'Default: 4' },
291
+ blur: { type: 'number', description: 'Blur radius. Default: 8' },
292
+ expand: { type: 'boolean', description: 'Expand canvas. Default: true' }
293
+ },
294
+ required: ['image']
295
+ }
222
296
  }
223
297
  ];
224
298
 
@@ -260,6 +334,18 @@ export async function handleTool(name: string, args: Record<string, any>): Promi
260
334
  const txt = await api.extractText(image, args);
261
335
  return JSON.stringify(txt);
262
336
  }
337
+ else if (name === 'image_rotate') result = await api.rotate(image, args as any);
338
+ else if (name === 'image_gradient_overlay') result = await api.gradientOverlay(image, args as any);
339
+ else if (name === 'image_clip_to_shape') result = await api.clipToShape(image, args as any);
340
+ else if (name === 'image_draw_shape') {
341
+ result = await api.drawShape(args as any);
342
+ if (result && result.ok && Buffer.isBuffer(result.data)) {
343
+ const b64 = result.data.toString('base64');
344
+ return JSON.stringify({ ok: true, data: `data:image/png;base64,${b64}` });
345
+ }
346
+ return JSON.stringify(result);
347
+ }
348
+ else if (name === 'image_drop_shadow') result = await api.dropShadow(image, args as any);
263
349
  else {
264
350
  return JSON.stringify({ error: `Tool ${name} not implemented`, code: 'INVALID_INPUT' });
265
351
  }
@@ -33,23 +33,29 @@ function escapeXml(text: string): string {
33
33
  .replace(/'/g, '&apos;');
34
34
  }
35
35
 
36
- function getAnchorProps(anchor: TextAnchor = 'top-left'): { textAnchor: string, dominantBaseline: string } {
36
+ function getAnchorProps(anchor: TextAnchor = 'top-left', fontSize: number = 24): { textAnchor: string, yOffset: number } {
37
37
  const parts = anchor.split('-');
38
38
  const yAlign = parts.length === 2 ? parts[0] : parts[0] === 'center' ? 'middle' : parts[0];
39
39
  const xAlign = parts.length === 2 ? parts[1] : parts[0] === 'center' ? 'center' : 'left';
40
40
 
41
- let dominantBaseline = 'hanging'; // top
42
- if (yAlign === 'bottom') dominantBaseline = 'auto'; // bottom is harder, usually means using y directly on baseline, but we can do mathematical offset. We'll rely on SVG baselines.
43
- else if (yAlign === 'middle' || yAlign === 'center') dominantBaseline = 'middle';
44
- else if (yAlign === 'auto') dominantBaseline = 'auto';
45
- // Sharp's librsvg supports dominant-baseline: text-before-edge (top), middle, alphabetic (bottom)
46
- const baselineMap: Record<string, string> = { top: 'text-before-edge', middle: 'middle', bottom: 'alphabetic', center: 'middle' };
41
+ // librsvg does NOT reliably support dominant-baseline values other than 'auto' (alphabetic).
42
+ // Instead of relying on dominant-baseline, we compute a y-offset to position text correctly.
43
+ // With 'auto' (alphabetic baseline), y = text baseline (bottom of caps).
44
+ // To make y = text top, we shift down by ~0.8 * fontSize.
45
+ // To make y = text middle, we shift down by ~0.35 * fontSize.
46
+ let yOffset = 0;
47
+ if (yAlign === 'top') {
48
+ yOffset = Math.round(fontSize * 0.8);
49
+ } else if (yAlign === 'middle' || yAlign === 'center') {
50
+ yOffset = Math.round(fontSize * 0.35);
51
+ }
52
+ // 'bottom' / 'auto' → yOffset = 0 (alphabetic baseline is already at y)
47
53
 
48
54
  let textAnchor = 'start';
49
55
  if (xAlign === 'center') textAnchor = 'middle';
50
56
  else if (xAlign === 'right') textAnchor = 'end';
51
57
 
52
- return { textAnchor, dominantBaseline: baselineMap[yAlign] || 'text-before-edge' };
58
+ return { textAnchor, yOffset };
53
59
  }
54
60
 
55
61
  export async function addText(input: ImageInput, options: { layers: TextLayer[] }): Promise<ImageResult> {
@@ -82,14 +88,26 @@ export async function addText(input: ImageInput, options: { layers: TextLayer[]
82
88
  const totalHeight = lines.length * fontSize * lineHeight;
83
89
  const approxMaxWidth = Math.max(...lines.map(l => l.length * fontSize * 0.6));
84
90
 
85
- const { textAnchor, dominantBaseline } = getAnchorProps(layer.anchor);
91
+ const { textAnchor, yOffset } = getAnchorProps(layer.anchor, fontSize);
92
+ const renderY = layer.y + yOffset;
86
93
 
87
94
  let align = textAnchor;
88
95
  if (layer.align) {
89
96
  align = layer.align === 'left' ? 'start' : layer.align === 'right' ? 'end' : 'middle';
90
97
  }
91
98
 
92
- const style = `font-family: ${fontFamily}; font-size: ${fontSize}px; fill: ${color}; opacity: ${opacity}; text-anchor: ${align}; dominant-baseline: ${dominantBaseline};`;
99
+ // Always use dominant-baseline: auto (alphabetic) the only value librsvg reliably supports
100
+ let style = `font-family: ${fontFamily}; font-size: ${fontSize}px; fill: ${color}; opacity: ${opacity}; text-anchor: ${align}; dominant-baseline: auto;`;
101
+
102
+ // Letter spacing
103
+ if (layer.letterSpacing) {
104
+ style += ` letter-spacing: ${layer.letterSpacing}px;`;
105
+ }
106
+
107
+ // Stroke (outline) — paint-order renders stroke behind fill
108
+ if (layer.stroke) {
109
+ style += ` stroke: ${layer.stroke.color}; stroke-width: ${layer.stroke.width}px; paint-order: stroke;`;
110
+ }
93
111
 
94
112
  let layerSvg = '';
95
113
 
@@ -99,6 +117,7 @@ export async function addText(input: ImageInput, options: { layers: TextLayer[]
99
117
  const bgOpacity = bg.opacity ?? 1.0;
100
118
  const radius = bg.borderRadius ?? 0;
101
119
 
120
+ // Background rect is positioned relative to the *intended* y (layer.y), not renderY
102
121
  let rectX = layer.x - pad;
103
122
  let rectY = layer.y - pad;
104
123
 
@@ -108,16 +127,33 @@ export async function addText(input: ImageInput, options: { layers: TextLayer[]
108
127
  rectX = layer.x - approxMaxWidth - pad;
109
128
  }
110
129
 
111
- if (dominantBaseline === 'middle') {
130
+ // Adjust for anchor vertical alignment
131
+ const parts = (layer.anchor ?? 'top-left').split('-');
132
+ const vAlign = parts.length === 2 ? parts[0] : parts[0] === 'center' ? 'middle' : parts[0];
133
+ if (vAlign === 'middle' || vAlign === 'center') {
112
134
  rectY = layer.y - (totalHeight / 2) - pad;
113
- } else if (dominantBaseline === 'alphabetic') { // bottom
135
+ } else if (vAlign === 'bottom') {
114
136
  rectY = layer.y - totalHeight - pad + fontSize;
115
137
  }
116
138
 
117
139
  layerSvg += `<rect x="${rectX}" y="${rectY}" width="${approxMaxWidth + pad * 2}" height="${totalHeight + pad * 2}" fill="${bg.color}" opacity="${bgOpacity}" rx="${radius}" ry="${radius}" />`;
118
140
  }
119
141
 
120
- layerSvg += `<text x="${layer.x}" y="${layer.y}" style="${style}">`;
142
+ // Text shadow: render a duplicate text behind the main text
143
+ if (layer.textShadow) {
144
+ const ts = layer.textShadow;
145
+ const shadowStyle = `font-family: ${fontFamily}; font-size: ${fontSize}px; fill: ${ts.color}; opacity: ${opacity}; text-anchor: ${align}; dominant-baseline: auto;${layer.letterSpacing ? ` letter-spacing: ${layer.letterSpacing}px;` : ''}`;
146
+ const sx = layer.x + ts.offsetX;
147
+ const sy = renderY + ts.offsetY;
148
+ layerSvg += `<text x="${sx}" y="${sy}" style="${shadowStyle}">`;
149
+ lines.forEach((line, idx) => {
150
+ let dy = idx === 0 ? 0 : fontSize * lineHeight;
151
+ layerSvg += `<tspan x="${sx}" dy="${dy}">${escapeXml(line)}</tspan>`;
152
+ });
153
+ layerSvg += `</text>`;
154
+ }
155
+
156
+ layerSvg += `<text x="${layer.x}" y="${renderY}" style="${style}">`;
121
157
  lines.forEach((line, idx) => {
122
158
  let dy = idx === 0 ? 0 : fontSize * lineHeight;
123
159
  layerSvg += `<tspan x="${layer.x}" dy="${dy}">${escapeXml(line)}</tspan>`;
@@ -126,13 +162,16 @@ export async function addText(input: ImageInput, options: { layers: TextLayer[]
126
162
 
127
163
  svgBody += `<g style="isolation: isolate">${layerSvg}</g>`;
128
164
 
129
- // Compute bounding box for overflow detection
165
+ // Compute bounding box for overflow detection (using intended y, not renderY)
130
166
  let boxX = layer.x;
131
167
  let boxY = layer.y;
132
168
  if (textAnchor === 'middle') boxX -= approxMaxWidth / 2;
133
169
  else if (textAnchor === 'end') boxX -= approxMaxWidth;
134
- if (dominantBaseline === 'middle') boxY -= totalHeight / 2;
135
- else if (dominantBaseline === 'alphabetic') boxY -= totalHeight - fontSize;
170
+
171
+ const anchorParts = (layer.anchor ?? 'top-left').split('-');
172
+ const vAlignBox = anchorParts.length === 2 ? anchorParts[0] : anchorParts[0] === 'center' ? 'middle' : anchorParts[0];
173
+ if (vAlignBox === 'middle' || vAlignBox === 'center') boxY -= totalHeight / 2;
174
+ else if (vAlignBox === 'bottom') boxY -= totalHeight - fontSize;
136
175
 
137
176
  const boxBottom = boxY + totalHeight;
138
177
  const boxRight = boxX + approxMaxWidth;
@@ -0,0 +1,63 @@
1
+ import sharp from 'sharp';
2
+ import { ClipToShapeOptions, ImageInput, ImageResult, ErrorCode } from '../types.js';
3
+ import { loadImage } from '../utils/load-image.js';
4
+ import { err, ok } from '../utils/result.js';
5
+ import { getImageMetadata } from '../utils/validate.js';
6
+
7
+ function buildClipSvg(width: number, height: number, options: ClipToShapeOptions): string {
8
+ const { shape } = options;
9
+ let clipContent: string;
10
+
11
+ switch (shape) {
12
+ case 'circle': {
13
+ const r = Math.min(width, height) / 2;
14
+ const cx = width / 2;
15
+ const cy = height / 2;
16
+ clipContent = `<circle cx="${cx}" cy="${cy}" r="${r}"/>`;
17
+ break;
18
+ }
19
+ case 'ellipse': {
20
+ clipContent = `<ellipse cx="${width / 2}" cy="${height / 2}" rx="${width / 2}" ry="${height / 2}"/>`;
21
+ break;
22
+ }
23
+ case 'rounded-rect': {
24
+ const radius = options.borderRadius ?? 32;
25
+ clipContent = `<rect width="${width}" height="${height}" rx="${radius}" ry="${radius}"/>`;
26
+ break;
27
+ }
28
+ default:
29
+ clipContent = `<rect width="${width}" height="${height}"/>`;
30
+ }
31
+
32
+ // Create an SVG mask: white shape on black = keep shape area
33
+ return `<svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">
34
+ ${clipContent.replace('/>', ' fill="white"/>')}
35
+ </svg>`;
36
+ }
37
+
38
+ export async function clipToShape(input: ImageInput, options: ClipToShapeOptions): Promise<ImageResult> {
39
+ try {
40
+ const buffer = await loadImage(input);
41
+ const meta = await getImageMetadata(buffer);
42
+ const { width, height } = meta;
43
+
44
+ const maskSvg = buildClipSvg(width, height, options);
45
+ const mask = await sharp(Buffer.from(maskSvg))
46
+ .resize(width, height)
47
+ .grayscale()
48
+ .toBuffer();
49
+
50
+ const output = await sharp(buffer)
51
+ .ensureAlpha()
52
+ .composite([{
53
+ input: mask,
54
+ blend: 'dest-in'
55
+ }])
56
+ .png()
57
+ .toBuffer();
58
+
59
+ return ok(output);
60
+ } catch (e: any) {
61
+ return err(e.message || 'Clip to shape failed', ErrorCode.PROCESSING_FAILED);
62
+ }
63
+ }