image-edit-tools 1.0.0 → 1.0.3
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.
- package/LICENSE +21 -0
- package/README.md +2 -0
- package/dist/ops/add-text.d.ts.map +1 -1
- package/dist/ops/add-text.js +9 -1
- package/dist/ops/add-text.js.map +1 -1
- package/dist/ops/detect-faces.d.ts.map +1 -1
- package/dist/ops/detect-faces.js +10 -3
- package/dist/ops/detect-faces.js.map +1 -1
- package/dist/ops/detect-subject.d.ts.map +1 -1
- package/dist/ops/detect-subject.js +10 -2
- package/dist/ops/detect-subject.js.map +1 -1
- package/dist/ops/pipeline.d.ts.map +1 -1
- package/dist/ops/pipeline.js +3 -2
- package/dist/ops/pipeline.js.map +1 -1
- package/dist/ops/remove-bg.d.ts.map +1 -1
- package/dist/ops/remove-bg.js +10 -4
- package/dist/ops/remove-bg.js.map +1 -1
- package/examples/instagram-card-news.ts +377 -0
- package/package.json +13 -2
- package/src/ops/add-text.ts +10 -1
- package/src/ops/detect-faces.ts +10 -3
- package/src/ops/detect-subject.ts +10 -2
- package/src/ops/pipeline.ts +5 -2
- package/src/ops/remove-bg.ts +10 -4
- package/vitest.config.ts +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kim Soo-young
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -12,6 +12,8 @@ A TypeScript-first, deterministic, purely functional image editing SDK designed
|
|
|
12
12
|
npm install image-edit-tools
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
+
> **Note**: AI features (`removeBg`, `detectFaces`, `detectSubject`) require `@xenova/transformers`, which is an optional dependency. All other 16+ operations work out of the box on any platform with just Node.js and sharp.
|
|
16
|
+
|
|
15
17
|
## Quick Start (Code)
|
|
16
18
|
```typescript
|
|
17
19
|
import { crop, resize, pad } from 'image-edit-tools';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"add-text.d.ts","sourceRoot":"","sources":["../../src/ops/add-text.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAyB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"add-text.d.ts","sourceRoot":"","sources":["../../src/ops/add-text.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAyB,MAAM,aAAa,CAAC;AAqDxF,wBAAsB,OAAO,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE;IAAE,MAAM,EAAE,SAAS,EAAE,CAAA;CAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CA6FvG"}
|
package/dist/ops/add-text.js
CHANGED
|
@@ -25,6 +25,14 @@ function wrapText(text, fontSize, maxWidth) {
|
|
|
25
25
|
lines.push(currentLine);
|
|
26
26
|
return lines;
|
|
27
27
|
}
|
|
28
|
+
function escapeXml(text) {
|
|
29
|
+
return text
|
|
30
|
+
.replace(/&/g, '&')
|
|
31
|
+
.replace(/</g, '<')
|
|
32
|
+
.replace(/>/g, '>')
|
|
33
|
+
.replace(/"/g, '"')
|
|
34
|
+
.replace(/'/g, ''');
|
|
35
|
+
}
|
|
28
36
|
function getAnchorProps(anchor = 'top-left') {
|
|
29
37
|
const parts = anchor.split('-');
|
|
30
38
|
const yAlign = parts.length === 2 ? parts[0] : parts[0] === 'center' ? 'middle' : parts[0];
|
|
@@ -99,7 +107,7 @@ export async function addText(input, options) {
|
|
|
99
107
|
layerSvg += `<text x="${layer.x}" y="${layer.y}" style="${style}">`;
|
|
100
108
|
lines.forEach((line, idx) => {
|
|
101
109
|
let dy = idx === 0 ? 0 : fontSize * lineHeight;
|
|
102
|
-
layerSvg += `<tspan x="${layer.x}" dy="${dy}">${line}</tspan>`;
|
|
110
|
+
layerSvg += `<tspan x="${layer.x}" dy="${dy}">${escapeXml(line)}</tspan>`;
|
|
103
111
|
});
|
|
104
112
|
layerSvg += `</text>`;
|
|
105
113
|
svgBody += `<g style="isolation: isolate">${layerSvg}</g>`;
|
package/dist/ops/add-text.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"add-text.js","sourceRoot":"","sources":["../../src/ops/add-text.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"add-text.js","sourceRoot":"","sources":["../../src/ops/add-text.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detect-faces.d.ts","sourceRoot":"","sources":["../../src/ops/detect-faces.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAa,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"detect-faces.d.ts","sourceRoot":"","sources":["../../src/ops/detect-faces.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAa,MAAM,aAAa,CAAC;AAKzE,wBAAsB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CA0CnF"}
|
package/dist/ops/detect-faces.js
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
import { ErrorCode } from '../types.js';
|
|
2
2
|
import { loadImage } from '../utils/load-image.js';
|
|
3
3
|
import { ok, err } from '../utils/result.js';
|
|
4
|
-
import { pipeline, RawImage } from '@xenova/transformers';
|
|
5
4
|
import sharp from 'sharp';
|
|
6
5
|
export async function detectFaces(input) {
|
|
7
6
|
try {
|
|
8
7
|
const buffer = await loadImage(input);
|
|
9
|
-
let detector;
|
|
8
|
+
let detector, pipeline_tf, RawImage;
|
|
10
9
|
try {
|
|
11
|
-
|
|
10
|
+
const tf = await import('@xenova/transformers');
|
|
11
|
+
pipeline_tf = tf.pipeline;
|
|
12
|
+
RawImage = tf.RawImage;
|
|
13
|
+
}
|
|
14
|
+
catch (e) {
|
|
15
|
+
return err('Failed to load @xenova/transformers (check architecture bindings).', ErrorCode.PROCESSING_FAILED);
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
detector = await pipeline_tf('object-detection', 'Xenova/detr-resnet-50', {
|
|
12
19
|
quantized: true,
|
|
13
20
|
});
|
|
14
21
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detect-faces.js","sourceRoot":"","sources":["../../src/ops/detect-faces.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmC,SAAS,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,
|
|
1
|
+
{"version":3,"file":"detect-faces.js","sourceRoot":"","sources":["../../src/ops/detect-faces.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detect-subject.d.ts","sourceRoot":"","sources":["../../src/ops/detect-subject.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAa,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"detect-subject.d.ts","sourceRoot":"","sources":["../../src/ops/detect-subject.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAa,MAAM,aAAa,CAAC;AAMzE,wBAAsB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAiFrF"}
|
|
@@ -3,12 +3,20 @@ import { loadImage } from '../utils/load-image.js';
|
|
|
3
3
|
import { err, ok } from '../utils/result.js';
|
|
4
4
|
import { getImageMetadata } from '../utils/validate.js';
|
|
5
5
|
import sharp from 'sharp';
|
|
6
|
-
import { AutoModel, AutoProcessor, RawImage } from '@xenova/transformers';
|
|
7
6
|
export async function detectSubject(input) {
|
|
8
7
|
try {
|
|
9
8
|
const buffer = await loadImage(input);
|
|
10
9
|
const meta = await getImageMetadata(buffer);
|
|
11
|
-
let model, processor;
|
|
10
|
+
let model, processor, AutoModel, AutoProcessor, RawImage;
|
|
11
|
+
try {
|
|
12
|
+
const tf = await import('@xenova/transformers');
|
|
13
|
+
AutoModel = tf.AutoModel;
|
|
14
|
+
AutoProcessor = tf.AutoProcessor;
|
|
15
|
+
RawImage = tf.RawImage;
|
|
16
|
+
}
|
|
17
|
+
catch (e) {
|
|
18
|
+
return err('Failed to load @xenova/transformers (check architecture bindings).', ErrorCode.PROCESSING_FAILED);
|
|
19
|
+
}
|
|
12
20
|
try {
|
|
13
21
|
model = await AutoModel.from_pretrained('briaai/RMBG-1.4', {
|
|
14
22
|
config: { model_type: 'custom' },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detect-subject.js","sourceRoot":"","sources":["../../src/ops/detect-subject.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmC,SAAS,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"detect-subject.js","sourceRoot":"","sources":["../../src/ops/detect-subject.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;
|
|
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"}
|
package/dist/ops/pipeline.js
CHANGED
|
@@ -10,7 +10,6 @@ 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 { removeBg } from './remove-bg.js';
|
|
14
13
|
export async function pipeline(input, operations) {
|
|
15
14
|
let currentImage = input;
|
|
16
15
|
for (let i = 0; i < operations.length; i++) {
|
|
@@ -51,9 +50,11 @@ export async function pipeline(input, operations) {
|
|
|
51
50
|
case 'optimize':
|
|
52
51
|
result = await optimize(currentImage, op);
|
|
53
52
|
break;
|
|
54
|
-
case 'removeBg':
|
|
53
|
+
case 'removeBg': {
|
|
54
|
+
const { removeBg } = await import('./remove-bg.js');
|
|
55
55
|
result = await removeBg(currentImage, op);
|
|
56
56
|
break;
|
|
57
|
+
}
|
|
57
58
|
default:
|
|
58
59
|
return { ok: false, error: `Unknown operation`, code: ErrorCode.INVALID_INPUT, step: i };
|
|
59
60
|
}
|
package/dist/ops/pipeline.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../../src/ops/pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8C,SAAS,EAAE,MAAM,aAAa,CAAC;AACpF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../../src/ops/pipeline.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remove-bg.d.ts","sourceRoot":"","sources":["../../src/ops/remove-bg.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW,EAAa,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"remove-bg.d.ts","sourceRoot":"","sources":["../../src/ops/remove-bg.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW,EAAa,MAAM,aAAa,CAAC;AAKlF,wBAAsB,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,WAAW,CAAC,CAiFrG"}
|
package/dist/ops/remove-bg.js
CHANGED
|
@@ -3,12 +3,20 @@ import { ErrorCode } from '../types.js';
|
|
|
3
3
|
import { loadImage } from '../utils/load-image.js';
|
|
4
4
|
import { err, ok } from '../utils/result.js';
|
|
5
5
|
import { getImageMetadata } from '../utils/validate.js';
|
|
6
|
-
import { AutoModel, AutoProcessor, RawImage } from '@xenova/transformers';
|
|
7
6
|
export async function removeBg(input, options = {}) {
|
|
8
7
|
try {
|
|
9
8
|
const buffer = await loadImage(input);
|
|
10
9
|
const meta = await getImageMetadata(buffer);
|
|
11
|
-
let model, processor;
|
|
10
|
+
let model, processor, AutoModel, AutoProcessor, RawImage;
|
|
11
|
+
try {
|
|
12
|
+
const tf = await import('@xenova/transformers');
|
|
13
|
+
AutoModel = tf.AutoModel;
|
|
14
|
+
AutoProcessor = tf.AutoProcessor;
|
|
15
|
+
RawImage = tf.RawImage;
|
|
16
|
+
}
|
|
17
|
+
catch (e) {
|
|
18
|
+
return err('Failed to load @xenova/transformers (check architecture bindings).', ErrorCode.PROCESSING_FAILED);
|
|
19
|
+
}
|
|
12
20
|
try {
|
|
13
21
|
model = await AutoModel.from_pretrained('briaai/RMBG-1.4', {
|
|
14
22
|
config: { model_type: 'custom' },
|
|
@@ -31,8 +39,6 @@ export async function removeBg(input, options = {}) {
|
|
|
31
39
|
return err('Model unavailable. Run: npx image-edit-tools download-models', ErrorCode.MODEL_NOT_FOUND);
|
|
32
40
|
}
|
|
33
41
|
// Process image
|
|
34
|
-
const rawImage = await RawImage.fromURL(URL.createObjectURL(new Blob([buffer]))); // Alternative for node:
|
|
35
|
-
// RawImage from buffer is easier using sharp. We need uint8 array of RGB or RGBA.
|
|
36
42
|
const rawRgb = await sharp(buffer).ensureAlpha().raw().toBuffer();
|
|
37
43
|
const img = new RawImage(new Uint8ClampedArray(rawRgb), meta.width, meta.height, 4);
|
|
38
44
|
const { pixel_values } = await processor(img);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remove-bg.js","sourceRoot":"","sources":["../../src/ops/remove-bg.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAA4C,SAAS,EAAE,MAAM,aAAa,CAAC;AAClF,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"remove-bg.js","sourceRoot":"","sources":["../../src/ops/remove-bg.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAA4C,SAAS,EAAE,MAAM,aAAa,CAAC;AAClF,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,KAAiB,EAAE,UAA2B,EAAE;IAC7E,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,KAAK,CAAC,CAAC;QACtC,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,MAAM,CAAC,CAAC;QAE5C,IAAI,KAAU,EAAE,SAAc,EAAE,SAAc,EAAE,aAAkB,EAAE,QAAa,CAAC;QAClF,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,sBAAsB,CAAC,CAAC;YAChD,SAAS,GAAG,EAAE,CAAC,SAAS,CAAC;YACzB,aAAa,GAAG,EAAE,CAAC,aAAa,CAAC;YACjC,QAAQ,GAAG,EAAE,CAAC,QAAQ,CAAC;QACzB,CAAC;QAAC,OAAO,CAAM,EAAE,CAAC;YAChB,OAAO,GAAG,CAAC,oEAAoE,EAAE,SAAS,CAAC,iBAAiB,CAAC,CAAC;QAChH,CAAC;QAED,IAAI,CAAC;YACH,KAAK,GAAG,MAAM,SAAS,CAAC,eAAe,CAAC,iBAAiB,EAAE;gBACzD,MAAM,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE;gBAChC,SAAS,EAAE,IAAI,CAAC,2CAA2C;aAC5D,CAAC,CAAC;YACH,SAAS,GAAG,MAAM,aAAa,CAAC,eAAe,CAAC,iBAAiB,EAAE;gBACjE,MAAM,EAAE;oBACN,YAAY,EAAE,IAAI;oBAClB,MAAM,EAAE,KAAK;oBACb,UAAU,EAAE,IAAI;oBAChB,SAAS,EAAE,IAAI;oBACf,UAAU,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;oBAC3B,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;oBACpB,QAAQ,EAAE,CAAC;oBACX,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE;iBACpC;aACF,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,CAAM,EAAE,CAAC;YAChB,OAAO,GAAG,CAAC,8DAA8D,EAAE,SAAS,CAAC,eAAe,CAAC,CAAC;QACxG,CAAC;QAED,gBAAgB;QAChB,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;QAClE,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,IAAI,iBAAiB,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAEpF,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC;QAC9C,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;QAExD,gFAAgF;QAChF,oCAAoC;QACpC,MAAM,QAAQ,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC;YAC5C,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,qCAAqC;QACnH,CAAC;QAED,MAAM,UAAU,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC;aACvG,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,sBAAsB;aACvE,QAAQ,EAAE,CAAC;QAEd,IAAI,SAAS,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC;aAChC,WAAW,EAAE;aACb,WAAW,CAAC,UAAU,CAAC,CAAC,6BAA6B;aACrD,GAAG,EAAE;aACL,QAAQ,EAAE,CAAC;QAEd,yBAAyB;QACzB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;YACzB,SAAS,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC;iBAC/B,OAAO,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC;iBAC7C,QAAQ,EAAE,CAAC;QAChB,CAAC;aAAM,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;YAChC,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;YACpD,0FAA0F;YAC1F,MAAM,UAAU,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;YACnG,SAAS,GAAG,MAAM,KAAK,CAAC,UAAU,CAAC;iBAChC,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;iBAChD,QAAQ,EAAE,CAAC;QAChB,CAAC;QAED,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC;IACvB,CAAC;IAAC,OAAO,CAAM,EAAE,CAAC;QAChB,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC;QAC5B,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,OAAO,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,YAAY,CAAC,CAAC;QAClE,IAAI,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAAE,OAAO,GAAG,CAAC,gBAAgB,EAAE,SAAS,CAAC,aAAa,CAAC,CAAC;QAClF,OAAO,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAC/C,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Instagram Card News Generator
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates: resize, pad, addText, composite, convert, pipeline
|
|
5
|
+
* Output: 1080×1080 Instagram carousel slides (cover + 3 content slides + closing)
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* npx tsx examples/instagram-card-news.ts
|
|
9
|
+
*/
|
|
10
|
+
import sharp from 'sharp';
|
|
11
|
+
import { writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
12
|
+
import { join, dirname } from 'path';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
import { addText } from '../src/ops/add-text.js';
|
|
15
|
+
import { composite } from '../src/ops/composite.js';
|
|
16
|
+
|
|
17
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
18
|
+
const __dirname = dirname(__filename);
|
|
19
|
+
const OUTPUT_DIR = join(__dirname, 'output');
|
|
20
|
+
|
|
21
|
+
if (!existsSync(OUTPUT_DIR)) mkdirSync(OUTPUT_DIR, { recursive: true });
|
|
22
|
+
|
|
23
|
+
// ── Design Tokens ────────────────────────────────────────────────────────────
|
|
24
|
+
const SIZE = 1080;
|
|
25
|
+
const PALETTE = {
|
|
26
|
+
bg: '#0F172A', // slate-900
|
|
27
|
+
accent: '#3B82F6', // blue-500
|
|
28
|
+
surface: '#1E293B', // slate-800
|
|
29
|
+
text: '#F8FAFC', // slate-50
|
|
30
|
+
muted: '#94A3B8', // slate-400
|
|
31
|
+
highlight:'#F59E0B', // amber-500
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// ── Helper: create a solid-color 1080×1080 canvas ────────────────────────────
|
|
35
|
+
async function canvas(color: string): Promise<Buffer> {
|
|
36
|
+
return sharp({
|
|
37
|
+
create: { width: SIZE, height: SIZE, channels: 4, background: color }
|
|
38
|
+
}).png().toBuffer();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ── Helper: create a rounded rectangle shape ─────────────────────────────────
|
|
42
|
+
async function roundedRect(
|
|
43
|
+
w: number, h: number, color: string, radius: number, opacity = 1
|
|
44
|
+
): Promise<Buffer> {
|
|
45
|
+
const svg = `<svg width="${w}" height="${h}">
|
|
46
|
+
<rect width="${w}" height="${h}" rx="${radius}" ry="${radius}" fill="${color}" opacity="${opacity}"/>
|
|
47
|
+
</svg>`;
|
|
48
|
+
return sharp(Buffer.from(svg)).png().toBuffer();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ── Helper: create a circle ──────────────────────────────────────────────────
|
|
52
|
+
async function circle(r: number, color: string): Promise<Buffer> {
|
|
53
|
+
const d = r * 2;
|
|
54
|
+
const svg = `<svg width="${d}" height="${d}">
|
|
55
|
+
<circle cx="${r}" cy="${r}" r="${r}" fill="${color}"/>
|
|
56
|
+
</svg>`;
|
|
57
|
+
return sharp(Buffer.from(svg)).png().toBuffer();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ── Helper: create a gradient banner ─────────────────────────────────────────
|
|
61
|
+
async function gradientBanner(w: number, h: number): Promise<Buffer> {
|
|
62
|
+
const svg = `<svg width="${w}" height="${h}">
|
|
63
|
+
<defs>
|
|
64
|
+
<linearGradient id="g" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
65
|
+
<stop offset="0%" stop-color="${PALETTE.accent}"/>
|
|
66
|
+
<stop offset="100%" stop-color="#8B5CF6"/>
|
|
67
|
+
</linearGradient>
|
|
68
|
+
</defs>
|
|
69
|
+
<rect width="${w}" height="${h}" rx="24" ry="24" fill="url(#g)"/>
|
|
70
|
+
</svg>`;
|
|
71
|
+
return sharp(Buffer.from(svg)).png().toBuffer();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ── Helper: number badge ─────────────────────────────────────────────────────
|
|
75
|
+
async function numberBadge(num: number): Promise<Buffer> {
|
|
76
|
+
const size = 80;
|
|
77
|
+
const svg = `<svg width="${size}" height="${size}">
|
|
78
|
+
<circle cx="${size/2}" cy="${size/2}" r="${size/2}" fill="${PALETTE.accent}"/>
|
|
79
|
+
<text x="${size/2}" y="${size/2}" text-anchor="middle" dominant-baseline="central"
|
|
80
|
+
font-family="sans-serif" font-weight="bold" font-size="36" fill="white">${num}</text>
|
|
81
|
+
</svg>`;
|
|
82
|
+
return sharp(Buffer.from(svg)).png().toBuffer();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
86
|
+
// SLIDE 1: Cover
|
|
87
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
88
|
+
async function slide1_cover(): Promise<Buffer> {
|
|
89
|
+
let bg = await canvas(PALETTE.bg);
|
|
90
|
+
|
|
91
|
+
// Accent gradient bar at top
|
|
92
|
+
const topBar = await gradientBanner(SIZE, 8);
|
|
93
|
+
bg = await sharp(bg).composite([{ input: topBar, top: 0, left: 0 }]).png().toBuffer();
|
|
94
|
+
|
|
95
|
+
// Decorative circles
|
|
96
|
+
const circle1 = await circle(200, PALETTE.accent + '15');
|
|
97
|
+
const circle2 = await circle(140, PALETTE.highlight + '10');
|
|
98
|
+
bg = await sharp(bg).composite([
|
|
99
|
+
{ input: circle1, top: -60, left: -60 },
|
|
100
|
+
{ input: circle2, top: 800, left: 880 },
|
|
101
|
+
]).png().toBuffer();
|
|
102
|
+
|
|
103
|
+
// Surface card
|
|
104
|
+
const card = await roundedRect(900, 500, PALETTE.surface, 32, 0.9);
|
|
105
|
+
bg = await sharp(bg).composite([{ input: card, top: 260, left: 90 }]).png().toBuffer();
|
|
106
|
+
|
|
107
|
+
// Main title text
|
|
108
|
+
const result = await addText(bg, { layers: [
|
|
109
|
+
{
|
|
110
|
+
text: '🚀',
|
|
111
|
+
x: SIZE / 2, y: 180,
|
|
112
|
+
fontSize: 80,
|
|
113
|
+
color: PALETTE.text,
|
|
114
|
+
anchor: 'top-center',
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
text: 'Image Edit Tools',
|
|
118
|
+
x: SIZE / 2, y: 340,
|
|
119
|
+
fontSize: 56,
|
|
120
|
+
color: PALETTE.text,
|
|
121
|
+
anchor: 'top-center',
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
text: 'AI 에이전트를 위한',
|
|
125
|
+
x: SIZE / 2, y: 430,
|
|
126
|
+
fontSize: 36,
|
|
127
|
+
color: PALETTE.highlight,
|
|
128
|
+
anchor: 'top-center',
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
text: '이미지 편집 SDK',
|
|
132
|
+
x: SIZE / 2, y: 480,
|
|
133
|
+
fontSize: 36,
|
|
134
|
+
color: PALETTE.highlight,
|
|
135
|
+
anchor: 'top-center',
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
text: 'TypeScript · Sharp · MCP',
|
|
139
|
+
x: SIZE / 2, y: 580,
|
|
140
|
+
fontSize: 28,
|
|
141
|
+
color: PALETTE.muted,
|
|
142
|
+
anchor: 'top-center',
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
text: '← 스와이프하여 알아보기',
|
|
146
|
+
x: SIZE / 2, y: 950,
|
|
147
|
+
fontSize: 24,
|
|
148
|
+
color: PALETTE.muted,
|
|
149
|
+
anchor: 'top-center',
|
|
150
|
+
},
|
|
151
|
+
]});
|
|
152
|
+
if (!result.ok) throw new Error(result.error);
|
|
153
|
+
return result.data;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
157
|
+
// SLIDE 2: Features Overview
|
|
158
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
159
|
+
async function slide2_features(): Promise<Buffer> {
|
|
160
|
+
let bg = await canvas(PALETTE.bg);
|
|
161
|
+
|
|
162
|
+
// Header stripe
|
|
163
|
+
const stripe = await gradientBanner(SIZE, 120);
|
|
164
|
+
bg = await sharp(bg).composite([{ input: stripe, top: 0, left: 0 }]).png().toBuffer();
|
|
165
|
+
|
|
166
|
+
// Feature cards
|
|
167
|
+
const features = [
|
|
168
|
+
{ icon: 'A', title: 'Crop + Resize', desc: '절대/비율/종횡비 크롭' },
|
|
169
|
+
{ icon: 'B', title: 'Adjust + Filter', desc: '밝기, 대비, 채도, 온도' },
|
|
170
|
+
{ icon: 'C', title: 'Watermark', desc: '텍스트/이미지 워터마크' },
|
|
171
|
+
{ icon: 'D', title: 'Pipeline', desc: '체인 파이프라인 처리' },
|
|
172
|
+
];
|
|
173
|
+
|
|
174
|
+
const cardW = 420, cardH = 180, gap = 40;
|
|
175
|
+
const startY = 180;
|
|
176
|
+
|
|
177
|
+
for (let i = 0; i < features.length; i++) {
|
|
178
|
+
const col = i % 2;
|
|
179
|
+
const row = Math.floor(i / 2);
|
|
180
|
+
const x = 60 + col * (cardW + gap);
|
|
181
|
+
const y = startY + row * (cardH + gap);
|
|
182
|
+
|
|
183
|
+
const card = await roundedRect(cardW, cardH, PALETTE.surface, 20);
|
|
184
|
+
bg = await sharp(bg).composite([{ input: card, top: y, left: x }]).png().toBuffer();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Add text for header and each feature
|
|
188
|
+
const textLayers = [
|
|
189
|
+
{ text: '주요 기능', x: SIZE / 2, y: 50, fontSize: 40, color: '#FFFFFF', anchor: 'top-center' as const },
|
|
190
|
+
];
|
|
191
|
+
|
|
192
|
+
for (let i = 0; i < features.length; i++) {
|
|
193
|
+
const col = i % 2;
|
|
194
|
+
const row = Math.floor(i / 2);
|
|
195
|
+
const x = 60 + col * (cardW + gap);
|
|
196
|
+
const y = startY + row * (cardH + gap);
|
|
197
|
+
|
|
198
|
+
textLayers.push(
|
|
199
|
+
{ text: features[i].icon, x: x + 30, y: y + 30, fontSize: 40, color: PALETTE.text, anchor: 'top-left' as const },
|
|
200
|
+
{ text: features[i].title, x: x + 90, y: y + 35, fontSize: 28, color: PALETTE.text, anchor: 'top-left' as const },
|
|
201
|
+
{ text: features[i].desc, x: x + 30, y: y + 100, fontSize: 22, color: PALETTE.muted, anchor: 'top-left' as const },
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Bottom slide counter
|
|
206
|
+
textLayers.push(
|
|
207
|
+
{ text: '2 / 5', x: SIZE / 2, y: 980, fontSize: 20, color: PALETTE.muted, anchor: 'top-center' as const },
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
const res = await addText(bg, { layers: textLayers });
|
|
211
|
+
if (!res.ok) throw new Error(res.error);
|
|
212
|
+
return res.data;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
216
|
+
// SLIDE 3: Code Example
|
|
217
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
218
|
+
async function slide3_code(): Promise<Buffer> {
|
|
219
|
+
let bg = await canvas(PALETTE.bg);
|
|
220
|
+
|
|
221
|
+
// Code block background
|
|
222
|
+
const codeCard = await roundedRect(920, 500, '#0D1117', 20);
|
|
223
|
+
bg = await sharp(bg).composite([{ input: codeCard, top: 250, left: 80 }]).png().toBuffer();
|
|
224
|
+
|
|
225
|
+
const codeLines = [
|
|
226
|
+
'import resize, addText from',
|
|
227
|
+
" 'image-edit-tools'",
|
|
228
|
+
'',
|
|
229
|
+
'const img = await resize(',
|
|
230
|
+
" 'photo.jpg',",
|
|
231
|
+
' width: 1080, height: 1080',
|
|
232
|
+
')',
|
|
233
|
+
'',
|
|
234
|
+
'const card = await addText(',
|
|
235
|
+
' img.data,',
|
|
236
|
+
" layers: [text: 'Hello!']",
|
|
237
|
+
')',
|
|
238
|
+
];
|
|
239
|
+
|
|
240
|
+
const codeLayers = [
|
|
241
|
+
{
|
|
242
|
+
text: '💻 코드 예시',
|
|
243
|
+
x: SIZE / 2, y: 100,
|
|
244
|
+
fontSize: 44, color: PALETTE.text,
|
|
245
|
+
anchor: 'top-center' as const,
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
text: '단 몇 줄로 이미지 편집 완료',
|
|
249
|
+
x: SIZE / 2, y: 170,
|
|
250
|
+
fontSize: 26, color: PALETTE.muted,
|
|
251
|
+
anchor: 'top-center' as const,
|
|
252
|
+
},
|
|
253
|
+
];
|
|
254
|
+
|
|
255
|
+
codeLines.forEach((line, i) => {
|
|
256
|
+
if (!line) return;
|
|
257
|
+
let color = PALETTE.text;
|
|
258
|
+
if (line.includes('import') || line.includes('const') || line.includes('await')) color = '#FF7B72';
|
|
259
|
+
if (line.startsWith(" '") || line.includes("'")) color = '#A5D6FF';
|
|
260
|
+
if (line.includes('width') || line.includes('height') || line.includes('layers')) color = '#FFA657';
|
|
261
|
+
|
|
262
|
+
codeLayers.push({
|
|
263
|
+
text: line,
|
|
264
|
+
x: 120,
|
|
265
|
+
y: 285 + i * 38,
|
|
266
|
+
fontSize: 24,
|
|
267
|
+
color,
|
|
268
|
+
anchor: 'top-left' as const,
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
codeLayers.push({
|
|
273
|
+
text: '3 / 5', x: SIZE / 2, y: 980,
|
|
274
|
+
fontSize: 20, color: PALETTE.muted, anchor: 'top-center' as const,
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
const res = await addText(bg, { layers: codeLayers });
|
|
278
|
+
if (!res.ok) throw new Error(res.error);
|
|
279
|
+
return res.data;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
283
|
+
// SLIDE 4: MCP Integration
|
|
284
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
285
|
+
async function slide4_mcp(): Promise<Buffer> {
|
|
286
|
+
let bg = await canvas(PALETTE.bg);
|
|
287
|
+
|
|
288
|
+
// Badges
|
|
289
|
+
const badge1 = await numberBadge(1);
|
|
290
|
+
const badge2 = await numberBadge(2);
|
|
291
|
+
const badge3 = await numberBadge(3);
|
|
292
|
+
|
|
293
|
+
// Step cards
|
|
294
|
+
const stepCard = await roundedRect(820, 140, PALETTE.surface, 16);
|
|
295
|
+
bg = await sharp(bg).composite([
|
|
296
|
+
{ input: stepCard, top: 280, left: 130 },
|
|
297
|
+
{ input: stepCard, top: 480, left: 130 },
|
|
298
|
+
{ input: stepCard, top: 680, left: 130 },
|
|
299
|
+
{ input: badge1, top: 310, left: 60 },
|
|
300
|
+
{ input: badge2, top: 510, left: 60 },
|
|
301
|
+
{ input: badge3, top: 710, left: 60 },
|
|
302
|
+
]).png().toBuffer();
|
|
303
|
+
|
|
304
|
+
const res = await addText(bg, { layers: [
|
|
305
|
+
{ text: '🤖 MCP 연동', x: SIZE / 2, y: 80, fontSize: 44, color: PALETTE.text, anchor: 'top-center' as const },
|
|
306
|
+
{ text: 'AI 에이전트에서 바로 사용', x: SIZE / 2, y: 150, fontSize: 26, color: PALETTE.muted, anchor: 'top-center' as const },
|
|
307
|
+
|
|
308
|
+
{ text: 'npm install', x: 170, y: 310, fontSize: 26, color: PALETTE.highlight, anchor: 'top-left' as const },
|
|
309
|
+
{ text: 'image-edit-tools 설치', x: 170, y: 355, fontSize: 22, color: PALETTE.muted, anchor: 'top-left' as const },
|
|
310
|
+
|
|
311
|
+
{ text: 'MCP 설정 추가', x: 170, y: 510, fontSize: 26, color: PALETTE.highlight, anchor: 'top-left' as const },
|
|
312
|
+
{ text: 'claude_desktop_config.json 수정', x: 170, y: 555, fontSize: 22, color: PALETTE.muted, anchor: 'top-left' as const },
|
|
313
|
+
|
|
314
|
+
{ text: 'AI에게 요청', x: 170, y: 710, fontSize: 26, color: PALETTE.highlight, anchor: 'top-left' as const },
|
|
315
|
+
{ text: '이 사진 1080x1080으로 크롭해줘', x: 170, y: 755, fontSize: 22, color: PALETTE.muted, anchor: 'top-left' as const },
|
|
316
|
+
|
|
317
|
+
{ text: '4 / 5', x: SIZE / 2, y: 980, fontSize: 20, color: PALETTE.muted, anchor: 'top-center' as const },
|
|
318
|
+
]});
|
|
319
|
+
if (!res.ok) throw new Error(res.error);
|
|
320
|
+
return res.data;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
324
|
+
// SLIDE 5: Closing CTA
|
|
325
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
326
|
+
async function slide5_cta(): Promise<Buffer> {
|
|
327
|
+
let bg = await canvas(PALETTE.bg);
|
|
328
|
+
|
|
329
|
+
// Large decorative gradient circle
|
|
330
|
+
const bigCircle = await circle(300, PALETTE.accent + '20');
|
|
331
|
+
bg = await sharp(bg).composite([
|
|
332
|
+
{ input: bigCircle, top: SIZE / 2 - 300, left: SIZE / 2 - 300 },
|
|
333
|
+
]).png().toBuffer();
|
|
334
|
+
|
|
335
|
+
// CTA button shape
|
|
336
|
+
const ctaBtn = await roundedRect(500, 80, PALETTE.accent, 40);
|
|
337
|
+
bg = await sharp(bg).composite([
|
|
338
|
+
{ input: ctaBtn, top: 650, left: SIZE / 2 - 250 },
|
|
339
|
+
]).png().toBuffer();
|
|
340
|
+
|
|
341
|
+
const res = await addText(bg, { layers: [
|
|
342
|
+
{ text: '⭐', x: SIZE / 2, y: 250, fontSize: 100, color: PALETTE.text, anchor: 'top-center' as const },
|
|
343
|
+
{ text: '지금 시작하세요', x: SIZE / 2, y: 400, fontSize: 48, color: PALETTE.text, anchor: 'top-center' as const },
|
|
344
|
+
{ text: 'npm install image-edit-tools', x: SIZE / 2, y: 500, fontSize: 30, color: PALETTE.highlight, anchor: 'top-center' as const },
|
|
345
|
+
{ text: '설치하기', x: SIZE / 2, y: 670, fontSize: 28, color: '#FFFFFF', anchor: 'top-center' as const },
|
|
346
|
+
{ text: 'github.com/swimmingkiim/image-edit-tools', x: SIZE / 2, y: 820, fontSize: 22, color: PALETTE.muted, anchor: 'top-center' as const },
|
|
347
|
+
{ text: '5 / 5', x: SIZE / 2, y: 980, fontSize: 20, color: PALETTE.muted, anchor: 'top-center' as const },
|
|
348
|
+
]});
|
|
349
|
+
if (!res.ok) throw new Error(res.error);
|
|
350
|
+
return res.data;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
354
|
+
// Main
|
|
355
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
356
|
+
async function main() {
|
|
357
|
+
console.log('📸 Generating Instagram card news slides...\n');
|
|
358
|
+
|
|
359
|
+
const slides = [
|
|
360
|
+
{ name: 'slide-1-cover', fn: slide1_cover },
|
|
361
|
+
{ name: 'slide-2-features', fn: slide2_features },
|
|
362
|
+
{ name: 'slide-3-code', fn: slide3_code },
|
|
363
|
+
{ name: 'slide-4-mcp', fn: slide4_mcp },
|
|
364
|
+
{ name: 'slide-5-cta', fn: slide5_cta },
|
|
365
|
+
];
|
|
366
|
+
|
|
367
|
+
for (const slide of slides) {
|
|
368
|
+
const buf = await slide.fn();
|
|
369
|
+
const outPath = join(OUTPUT_DIR, `${slide.name}.png`);
|
|
370
|
+
writeFileSync(outPath, buf);
|
|
371
|
+
console.log(` ✅ ${slide.name}.png (${(buf.length / 1024).toFixed(1)} KB)`);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
console.log(`\n🎉 Done! ${slides.length} slides saved to examples/output/`);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
main().catch(console.error);
|
package/package.json
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "image-edit-tools",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Deterministic image editing SDK for AI agents. Ships with MCP tools.",
|
|
5
|
+
"author": "swimmingkiim",
|
|
6
|
+
"homepage": "https://github.com/swimmingkiim/image-edit-tools#readme",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/swimmingkiim/image-edit-tools.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/swimmingkiim/image-edit-tools/issues"
|
|
13
|
+
},
|
|
5
14
|
"type": "module",
|
|
6
15
|
"main": "dist/index.js",
|
|
7
16
|
"types": "dist/index.d.ts",
|
|
@@ -25,12 +34,14 @@
|
|
|
25
34
|
},
|
|
26
35
|
"dependencies": {
|
|
27
36
|
"@modelcontextprotocol/sdk": "^1.1.0",
|
|
28
|
-
"@xenova/transformers": "^2.17.2",
|
|
29
37
|
"color-thief-node": "^1.0.4",
|
|
30
38
|
"node-fetch": "^3.3.2",
|
|
31
39
|
"sharp": "^0.33.5",
|
|
32
40
|
"tesseract.js": "^5.1.1"
|
|
33
41
|
},
|
|
42
|
+
"optionalDependencies": {
|
|
43
|
+
"@xenova/transformers": "^2.17.2"
|
|
44
|
+
},
|
|
34
45
|
"devDependencies": {
|
|
35
46
|
"@types/node": "^20.14.2",
|
|
36
47
|
"@vitest/coverage-v8": "^1.6.0",
|
package/src/ops/add-text.ts
CHANGED
|
@@ -24,6 +24,15 @@ function wrapText(text: string, fontSize: number, maxWidth?: number): string[] {
|
|
|
24
24
|
return lines;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
function escapeXml(text: string): string {
|
|
28
|
+
return text
|
|
29
|
+
.replace(/&/g, '&')
|
|
30
|
+
.replace(/</g, '<')
|
|
31
|
+
.replace(/>/g, '>')
|
|
32
|
+
.replace(/"/g, '"')
|
|
33
|
+
.replace(/'/g, ''');
|
|
34
|
+
}
|
|
35
|
+
|
|
27
36
|
function getAnchorProps(anchor: TextAnchor = 'top-left'): { textAnchor: string, dominantBaseline: string } {
|
|
28
37
|
const parts = anchor.split('-');
|
|
29
38
|
const yAlign = parts.length === 2 ? parts[0] : parts[0] === 'center' ? 'middle' : parts[0];
|
|
@@ -109,7 +118,7 @@ export async function addText(input: ImageInput, options: { layers: TextLayer[]
|
|
|
109
118
|
layerSvg += `<text x="${layer.x}" y="${layer.y}" style="${style}">`;
|
|
110
119
|
lines.forEach((line, idx) => {
|
|
111
120
|
let dy = idx === 0 ? 0 : fontSize * lineHeight;
|
|
112
|
-
layerSvg += `<tspan x="${layer.x}" dy="${dy}">${line}</tspan>`;
|
|
121
|
+
layerSvg += `<tspan x="${layer.x}" dy="${dy}">${escapeXml(line)}</tspan>`;
|
|
113
122
|
});
|
|
114
123
|
layerSvg += `</text>`;
|
|
115
124
|
|
package/src/ops/detect-faces.ts
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
import { BoundingBox, ImageInput, Result, ErrorCode } from '../types.js';
|
|
2
2
|
import { loadImage } from '../utils/load-image.js';
|
|
3
3
|
import { ok, err } from '../utils/result.js';
|
|
4
|
-
import { pipeline, RawImage } from '@xenova/transformers';
|
|
5
4
|
import sharp from 'sharp';
|
|
6
5
|
|
|
7
6
|
export async function detectFaces(input: ImageInput): Promise<Result<BoundingBox[]>> {
|
|
8
7
|
try {
|
|
9
8
|
const buffer = await loadImage(input);
|
|
10
|
-
let detector: any;
|
|
9
|
+
let detector: any, pipeline_tf: any, RawImage: any;
|
|
11
10
|
try {
|
|
12
|
-
|
|
11
|
+
const tf = await import('@xenova/transformers');
|
|
12
|
+
pipeline_tf = tf.pipeline;
|
|
13
|
+
RawImage = tf.RawImage;
|
|
14
|
+
} catch (e: any) {
|
|
15
|
+
return err('Failed to load @xenova/transformers (check architecture bindings).', ErrorCode.PROCESSING_FAILED);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
detector = await pipeline_tf('object-detection', 'Xenova/detr-resnet-50', {
|
|
13
20
|
quantized: true,
|
|
14
21
|
});
|
|
15
22
|
} catch (e) {
|
|
@@ -3,14 +3,22 @@ import { loadImage } from '../utils/load-image.js';
|
|
|
3
3
|
import { err, ok } from '../utils/result.js';
|
|
4
4
|
import { getImageMetadata } from '../utils/validate.js';
|
|
5
5
|
import sharp from 'sharp';
|
|
6
|
-
import { AutoModel, AutoProcessor, RawImage } from '@xenova/transformers';
|
|
7
6
|
|
|
8
7
|
export async function detectSubject(input: ImageInput): Promise<Result<BoundingBox[]>> {
|
|
9
8
|
try {
|
|
10
9
|
const buffer = await loadImage(input);
|
|
11
10
|
const meta = await getImageMetadata(buffer);
|
|
12
11
|
|
|
13
|
-
let model: any, processor: any;
|
|
12
|
+
let model: any, processor: any, AutoModel: any, AutoProcessor: any, RawImage: any;
|
|
13
|
+
try {
|
|
14
|
+
const tf = await import('@xenova/transformers');
|
|
15
|
+
AutoModel = tf.AutoModel;
|
|
16
|
+
AutoProcessor = tf.AutoProcessor;
|
|
17
|
+
RawImage = tf.RawImage;
|
|
18
|
+
} catch (e: any) {
|
|
19
|
+
return err('Failed to load @xenova/transformers (check architecture bindings).', ErrorCode.PROCESSING_FAILED);
|
|
20
|
+
}
|
|
21
|
+
|
|
14
22
|
try {
|
|
15
23
|
model = await AutoModel.from_pretrained('briaai/RMBG-1.4', {
|
|
16
24
|
config: { model_type: 'custom' },
|
package/src/ops/pipeline.ts
CHANGED
|
@@ -10,7 +10,6 @@ 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 { removeBg } from './remove-bg.js';
|
|
14
13
|
|
|
15
14
|
export async function pipeline(input: ImageInput, operations: PipelineOperation[]): Promise<ImageResult & { step?: number }> {
|
|
16
15
|
let currentImage = input;
|
|
@@ -32,7 +31,11 @@ export async function pipeline(input: ImageInput, operations: PipelineOperation[
|
|
|
32
31
|
case 'watermark': result = await watermark(currentImage, op); break;
|
|
33
32
|
case 'convert': result = await convert(currentImage, op); break;
|
|
34
33
|
case 'optimize': result = await optimize(currentImage, op); break;
|
|
35
|
-
case 'removeBg':
|
|
34
|
+
case 'removeBg': {
|
|
35
|
+
const { removeBg } = await import('./remove-bg.js');
|
|
36
|
+
result = await removeBg(currentImage, op);
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
36
39
|
default:
|
|
37
40
|
return { ok: false, error: `Unknown operation`, code: ErrorCode.INVALID_INPUT, step: i };
|
|
38
41
|
}
|
package/src/ops/remove-bg.ts
CHANGED
|
@@ -3,14 +3,22 @@ import { RemoveBgOptions, ImageInput, ImageResult, ErrorCode } from '../types.js
|
|
|
3
3
|
import { loadImage } from '../utils/load-image.js';
|
|
4
4
|
import { err, ok } from '../utils/result.js';
|
|
5
5
|
import { getImageMetadata } from '../utils/validate.js';
|
|
6
|
-
import { AutoModel, AutoProcessor, RawImage } from '@xenova/transformers';
|
|
7
6
|
|
|
8
7
|
export async function removeBg(input: ImageInput, options: RemoveBgOptions = {}): Promise<ImageResult> {
|
|
9
8
|
try {
|
|
10
9
|
const buffer = await loadImage(input);
|
|
11
10
|
const meta = await getImageMetadata(buffer);
|
|
12
11
|
|
|
13
|
-
let model: any, processor: any;
|
|
12
|
+
let model: any, processor: any, AutoModel: any, AutoProcessor: any, RawImage: any;
|
|
13
|
+
try {
|
|
14
|
+
const tf = await import('@xenova/transformers');
|
|
15
|
+
AutoModel = tf.AutoModel;
|
|
16
|
+
AutoProcessor = tf.AutoProcessor;
|
|
17
|
+
RawImage = tf.RawImage;
|
|
18
|
+
} catch (e: any) {
|
|
19
|
+
return err('Failed to load @xenova/transformers (check architecture bindings).', ErrorCode.PROCESSING_FAILED);
|
|
20
|
+
}
|
|
21
|
+
|
|
14
22
|
try {
|
|
15
23
|
model = await AutoModel.from_pretrained('briaai/RMBG-1.4', {
|
|
16
24
|
config: { model_type: 'custom' },
|
|
@@ -33,8 +41,6 @@ export async function removeBg(input: ImageInput, options: RemoveBgOptions = {})
|
|
|
33
41
|
}
|
|
34
42
|
|
|
35
43
|
// Process image
|
|
36
|
-
const rawImage = await RawImage.fromURL(URL.createObjectURL(new Blob([buffer]))); // Alternative for node:
|
|
37
|
-
// RawImage from buffer is easier using sharp. We need uint8 array of RGB or RGBA.
|
|
38
44
|
const rawRgb = await sharp(buffer).ensureAlpha().raw().toBuffer();
|
|
39
45
|
const img = new RawImage(new Uint8ClampedArray(rawRgb), meta.width, meta.height, 4);
|
|
40
46
|
|