apexify.js 5.0.0 → 5.0.1
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/CHANGELOG.md +137 -0
- package/README.md +63 -3
- package/apex-banner.png +0 -0
- package/dist/cjs/Canvas/ApexPainter.d.ts +63 -1
- package/dist/cjs/Canvas/ApexPainter.d.ts.map +1 -1
- package/dist/cjs/Canvas/ApexPainter.js +196 -0
- package/dist/cjs/Canvas/ApexPainter.js.map +1 -1
- package/dist/cjs/Canvas/utils/types.d.ts +32 -0
- package/dist/cjs/Canvas/utils/types.d.ts.map +1 -1
- package/dist/cjs/Canvas/utils/utils.d.ts +2 -2
- package/dist/cjs/Canvas/utils/utils.d.ts.map +1 -1
- package/dist/esm/Canvas/ApexPainter.d.ts +63 -1
- package/dist/esm/Canvas/ApexPainter.d.ts.map +1 -1
- package/dist/esm/Canvas/ApexPainter.js +196 -0
- package/dist/esm/Canvas/ApexPainter.js.map +1 -1
- package/dist/esm/Canvas/utils/types.d.ts +32 -0
- package/dist/esm/Canvas/utils/types.d.ts.map +1 -1
- package/dist/esm/Canvas/utils/utils.d.ts +2 -2
- package/dist/esm/Canvas/utils/utils.d.ts.map +1 -1
- package/lib/Canvas/ApexPainter.ts +211 -2
- package/lib/Canvas/utils/types.ts +35 -0
- package/lib/Canvas/utils/utils.ts +5 -2
- package/package.json +7 -2
- package/types/imgur.d.ts +65 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Apexify.js will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [5.0.0] - 2024-12-20
|
|
9
|
+
|
|
10
|
+
### 🎉 Major Feature Release - Advanced Image & Canvas Features
|
|
11
|
+
|
|
12
|
+
#### ✨ Added
|
|
13
|
+
|
|
14
|
+
##### Background Enhancements
|
|
15
|
+
- **Background Image Filters**: Apply filters directly to background images via `customBg.filters`
|
|
16
|
+
- **Background Image Opacity**: Control background image transparency with `customBg.opacity`
|
|
17
|
+
- **Video Backgrounds**: Support for video backgrounds with frame extraction via `videoBg` option
|
|
18
|
+
- Extract specific frames from videos
|
|
19
|
+
- Loop and autoplay support
|
|
20
|
+
- Opacity control for video backgrounds
|
|
21
|
+
|
|
22
|
+
##### Image Processing Enhancements
|
|
23
|
+
- **Image Masking**: Apply masks to images with multiple modes (`alpha`, `luminance`, `inverse`)
|
|
24
|
+
- **Custom Clip Paths**: Define custom polygon clipping paths for images
|
|
25
|
+
- **Image Distortion**:
|
|
26
|
+
- Perspective distortion with 4-point control
|
|
27
|
+
- Bulge/pinch effects with intensity control
|
|
28
|
+
- Mesh warping with customizable grid control points
|
|
29
|
+
- **Effects Stack**: Professional image effects
|
|
30
|
+
- Vignette effect with intensity and size control
|
|
31
|
+
- Lens flare with position and intensity
|
|
32
|
+
- Chromatic aberration effect
|
|
33
|
+
- Film grain effect
|
|
34
|
+
- **Enhanced Filters**:
|
|
35
|
+
- `filterIntensity` - Global intensity multiplier for all filters
|
|
36
|
+
- `filterOrder` - Control when filters are applied (`pre` or `post` transformation)
|
|
37
|
+
|
|
38
|
+
##### Text Enhancements
|
|
39
|
+
- **Text on Paths**: Render text along curves and paths
|
|
40
|
+
- Support for line, arc, bezier, and quadratic curve paths
|
|
41
|
+
- Offset control for text distance from path
|
|
42
|
+
- Automatic text distribution along path
|
|
43
|
+
|
|
44
|
+
##### Custom Lines Enhancements
|
|
45
|
+
- **Advanced Path Options**:
|
|
46
|
+
- Smooth path interpolation with tension control
|
|
47
|
+
- Bezier curve paths
|
|
48
|
+
- Catmull-Rom spline paths
|
|
49
|
+
- Closed path support
|
|
50
|
+
- **Arrow Markers**:
|
|
51
|
+
- Start and end arrows on lines
|
|
52
|
+
- Customizable arrow size and style (filled/outline)
|
|
53
|
+
- Color control for arrows
|
|
54
|
+
- **Path Markers**:
|
|
55
|
+
- Add markers at any position along a path (0-1)
|
|
56
|
+
- Multiple marker shapes: circle, square, diamond, arrow
|
|
57
|
+
- Customizable marker size and color
|
|
58
|
+
- **Line Patterns**:
|
|
59
|
+
- Built-in patterns: dots, dashes, custom segments
|
|
60
|
+
- Pattern offset control
|
|
61
|
+
- **Line Textures**: Apply texture images to lines
|
|
62
|
+
|
|
63
|
+
##### New Utility Methods
|
|
64
|
+
- **Batch Operations**: `batch()` - Process multiple operations in parallel
|
|
65
|
+
- **Chain Operations**: `chain()` - Chain operations sequentially
|
|
66
|
+
- **Image Stitching**: `stitchImages()` - Stitch multiple images together
|
|
67
|
+
- Horizontal, vertical, and grid layouts
|
|
68
|
+
- Overlap and blend support
|
|
69
|
+
- Spacing control
|
|
70
|
+
- **Collage Maker**: `createCollage()` - Create image collages
|
|
71
|
+
- Grid, masonry, carousel, and custom layouts
|
|
72
|
+
- Automatic spacing and alignment
|
|
73
|
+
- Background and border radius support
|
|
74
|
+
- **Image Compression**: `compress()` - Compress images with quality control
|
|
75
|
+
- Support for JPEG, WebP, and AVIF formats
|
|
76
|
+
- Progressive JPEG support
|
|
77
|
+
- Max width/height constraints
|
|
78
|
+
- **Color Palette Extraction**: `extractPalette()` - Extract color palettes from images
|
|
79
|
+
- Multiple algorithms: k-means, median-cut, octree
|
|
80
|
+
- Customizable color count
|
|
81
|
+
- Multiple output formats: hex, rgb, hsl
|
|
82
|
+
- **Advanced Save Method**: `save()` - Save buffers to local files with extensive customization
|
|
83
|
+
- Multiple file formats: PNG, JPEG, WebP, AVIF, GIF
|
|
84
|
+
- Smart naming patterns: timestamp, counter, or custom
|
|
85
|
+
- Auto-create directories
|
|
86
|
+
- Quality control for JPEG/WebP
|
|
87
|
+
- Prefix/suffix support
|
|
88
|
+
- Overwrite protection with auto-renaming
|
|
89
|
+
- Batch saving with `saveMultiple()`
|
|
90
|
+
- Counter management with `resetSaveCounter()`
|
|
91
|
+
|
|
92
|
+
#### 🔧 Technical Improvements
|
|
93
|
+
- Added comprehensive TypeScript type definitions for all new features
|
|
94
|
+
- Created utility modules for better code organization:
|
|
95
|
+
- `imageMasking.ts` - Image masking and distortion utilities
|
|
96
|
+
- `imageEffects.ts` - Image effects utilities
|
|
97
|
+
- `textPathRenderer.ts` - Text path rendering utilities
|
|
98
|
+
- `advancedLines.ts` - Advanced line drawing utilities
|
|
99
|
+
- `batchOperations.ts` - Batch and chain operation utilities
|
|
100
|
+
- `imageStitching.ts` - Image stitching and collage utilities
|
|
101
|
+
- `imageCompression.ts` - Image compression and palette extraction utilities
|
|
102
|
+
- Enhanced error handling and validation
|
|
103
|
+
- Improved type safety across all new features
|
|
104
|
+
|
|
105
|
+
#### 📚 Documentation
|
|
106
|
+
- Updated README with new features
|
|
107
|
+
- Added comprehensive examples for all new features
|
|
108
|
+
- Enhanced API documentation
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## [4.9.28] - Previous Release
|
|
113
|
+
|
|
114
|
+
### Features
|
|
115
|
+
- Enhanced text renderer with decorations
|
|
116
|
+
- Professional pattern system
|
|
117
|
+
- Advanced gradient support
|
|
118
|
+
- Comprehensive shape drawing
|
|
119
|
+
- Chart generation capabilities
|
|
120
|
+
- GIF creation support
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## [4.9.0] - Initial Major Release
|
|
125
|
+
|
|
126
|
+
### Features
|
|
127
|
+
- Core canvas creation
|
|
128
|
+
- Image and shape drawing
|
|
129
|
+
- Text rendering
|
|
130
|
+
- Basic filters and effects
|
|
131
|
+
- Format conversion
|
|
132
|
+
- Background removal
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
For a complete list of changes, please refer to the [GitHub repository](https://github.com/zenith-79/apexify.js).
|
|
137
|
+
|
package/README.md
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
**The Ultimate TypeScript Canvas Library for Node.js**
|
|
8
8
|
|
|
9
|
-
[](https://badge.fury.io/js/apexify)
|
|
9
|
+
[](https://badge.fury.io/js/apexify.js)
|
|
10
|
+
[](https://www.npmjs.com/package/apexify.js)
|
|
10
11
|
[](https://www.typescriptlang.org/)
|
|
11
12
|
[](https://nodejs.org/)
|
|
12
13
|
[](LICENSE)
|
|
@@ -64,6 +65,7 @@
|
|
|
64
65
|
- **Color Detection**: Extract and analyze colors from images
|
|
65
66
|
- **Batch Operations**: Process multiple operations in parallel
|
|
66
67
|
- **Chain Operations**: Chain operations sequentially for complex workflows
|
|
68
|
+
- **Advanced File Saving**: Save buffers to local files with smart naming, format conversion, and batch support
|
|
67
69
|
|
|
68
70
|
---
|
|
69
71
|
|
|
@@ -168,10 +170,64 @@ const textImage = await painter.createText({
|
|
|
168
170
|
}
|
|
169
171
|
}, heartImage);
|
|
170
172
|
|
|
171
|
-
// Save the result
|
|
172
|
-
|
|
173
|
+
// Save the result using the advanced save method
|
|
174
|
+
const saveResult = await painter.save(textImage, {
|
|
175
|
+
directory: './output',
|
|
176
|
+
filename: 'beautiful-artwork',
|
|
177
|
+
format: 'png'
|
|
178
|
+
});
|
|
179
|
+
console.log(`Saved to: ${saveResult.path} (${saveResult.size} bytes)`);
|
|
180
|
+
|
|
181
|
+
// Or use the simple approach (auto-generated filename)
|
|
182
|
+
const autoSave = await painter.save(textImage);
|
|
183
|
+
// Saves to: ./ApexPainter_output/20241220_143025_123.png
|
|
173
184
|
```
|
|
174
185
|
|
|
186
|
+
### 💾 **Advanced Save Method**
|
|
187
|
+
|
|
188
|
+
The `save()` method provides powerful file saving capabilities with extensive customization:
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
// Simple save with auto-generated timestamp name
|
|
192
|
+
const canvas = await painter.createCanvas({ width: 800, height: 600 });
|
|
193
|
+
const result = await painter.save(canvas.buffer);
|
|
194
|
+
// Saves to: ./ApexPainter_output/20241220_143025_123.png
|
|
195
|
+
|
|
196
|
+
// Custom filename and directory
|
|
197
|
+
await painter.save(canvas.buffer, {
|
|
198
|
+
directory: './my-images',
|
|
199
|
+
filename: 'my-canvas',
|
|
200
|
+
format: 'jpg',
|
|
201
|
+
quality: 95
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// Save with counter naming
|
|
205
|
+
await painter.save(canvas.buffer, {
|
|
206
|
+
naming: 'counter',
|
|
207
|
+
prefix: 'image-',
|
|
208
|
+
counterStart: 1
|
|
209
|
+
});
|
|
210
|
+
// Saves to: ./ApexPainter_output/image-1.png, image-2.png, etc.
|
|
211
|
+
|
|
212
|
+
// Save multiple buffers in batch
|
|
213
|
+
const buffers = [canvas1.buffer, canvas2.buffer, canvas3.buffer];
|
|
214
|
+
const results = await painter.saveMultiple(buffers, {
|
|
215
|
+
prefix: 'batch-',
|
|
216
|
+
naming: 'counter'
|
|
217
|
+
});
|
|
218
|
+
// Saves: batch-1.png, batch-2.png, batch-3.png
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Save Options:**
|
|
222
|
+
- `directory` - Output directory (default: `./ApexPainter_output`)
|
|
223
|
+
- `filename` - Custom filename (auto-generated if not provided)
|
|
224
|
+
- `format` - File format: `png`, `jpg`, `jpeg`, `webp`, `avif`, `gif` (default: `png`)
|
|
225
|
+
- `quality` - Quality for JPEG/WebP (0-100, default: 90)
|
|
226
|
+
- `naming` - Naming pattern: `timestamp`, `counter`, or `custom` (default: `timestamp`)
|
|
227
|
+
- `prefix` / `suffix` - Add prefix/suffix to filenames
|
|
228
|
+
- `overwrite` - Overwrite existing files (default: `false`, auto-renames if exists)
|
|
229
|
+
- `createDirectory` - Auto-create directory if missing (default: `true`)
|
|
230
|
+
|
|
175
231
|
### 📝 **Flexible Array Support**
|
|
176
232
|
|
|
177
233
|
Both `createImage()` and `createText()` methods accept **single objects** OR **arrays of objects**:
|
|
@@ -583,6 +639,9 @@ const buttonText = await painter.createText({
|
|
|
583
639
|
| `createText()` | Add text to canvas | `TextProperties \| TextProperties[]` |
|
|
584
640
|
| `createChart()` | Generate charts | `ChartConfig` |
|
|
585
641
|
| `createGIF()` | Create animated GIFs | `GIFConfig` |
|
|
642
|
+
| `save()` | Save buffer to local file with advanced options | `Buffer, SaveOptions?` |
|
|
643
|
+
| `saveMultiple()` | Save multiple buffers in batch | `Buffer[], SaveOptions?` |
|
|
644
|
+
| `resetSaveCounter()` | Reset the save counter for naming | `void` |
|
|
586
645
|
|
|
587
646
|
### 🔄 **Flexible Parameters**
|
|
588
647
|
|
|
@@ -652,6 +711,7 @@ This allows you to add multiple elements in one call for better performance and
|
|
|
652
711
|
- 🎨 **Collage Maker**: Create beautiful image collages
|
|
653
712
|
- 📦 **Image Compression**: Optimize images with quality control
|
|
654
713
|
- 🎨 **Color Palette Extraction**: Extract color palettes from images
|
|
714
|
+
- 💾 **Advanced Save Method**: Save buffers to files with smart naming, format conversion, and batch support
|
|
655
715
|
|
|
656
716
|
See [CHANGELOG.md](CHANGELOG.md) for complete details.
|
|
657
717
|
|
package/apex-banner.png
ADDED
|
Binary file
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PathLike } from "fs";
|
|
2
|
-
import { OutputFormat, CanvasConfig, TextProperties, ImageProperties, GIFOptions, GIFResults, CustomOptions, cropOptions, GradientConfig, Frame, ExtractFramesOptions, ResizeOptions, MaskOptions, BlendOptions, ImageFilter, BatchOperation, ChainOperation, StitchOptions, CollageLayout, CompressionOptions, PaletteOptions } from "./utils/utils";
|
|
2
|
+
import { OutputFormat, CanvasConfig, TextProperties, ImageProperties, GIFOptions, GIFResults, CustomOptions, cropOptions, GradientConfig, Frame, ExtractFramesOptions, ResizeOptions, MaskOptions, BlendOptions, ImageFilter, BatchOperation, ChainOperation, StitchOptions, CollageLayout, CompressionOptions, PaletteOptions, SaveOptions, SaveResult } from "./utils/utils";
|
|
3
3
|
interface CanvasResults {
|
|
4
4
|
buffer: Buffer;
|
|
5
5
|
canvas: CanvasConfig;
|
|
@@ -7,6 +7,7 @@ interface CanvasResults {
|
|
|
7
7
|
export declare class ApexPainter {
|
|
8
8
|
#private;
|
|
9
9
|
private format?;
|
|
10
|
+
private saveCounter;
|
|
10
11
|
constructor({ type }?: OutputFormat);
|
|
11
12
|
createCanvas(canvas: CanvasConfig): Promise<CanvasResults>;
|
|
12
13
|
createImage(images: ImageProperties | ImageProperties[], canvasBuffer: CanvasResults | Buffer): Promise<Buffer>;
|
|
@@ -135,6 +136,67 @@ export declare class ApexPainter {
|
|
|
135
136
|
* ```
|
|
136
137
|
*/
|
|
137
138
|
outPut(results: Buffer): Promise<Buffer | string | Blob | ArrayBuffer>;
|
|
139
|
+
/**
|
|
140
|
+
* Advanced save method to save buffers to local files with extensive customization options.
|
|
141
|
+
*
|
|
142
|
+
* @param buffer - Buffer to save (from createCanvas, createImage, createText, etc.)
|
|
143
|
+
* @param options - Save options for file path, format, naming, etc.
|
|
144
|
+
* @returns SaveResult with file path, name, size, and format
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```typescript
|
|
148
|
+
* // Simple save with auto-generated name
|
|
149
|
+
* const canvas = await painter.createCanvas({ width: 800, height: 600 });
|
|
150
|
+
* const result = await painter.save(canvas.buffer);
|
|
151
|
+
* // Saves to: ./ApexPainter_output/20241220_143025_123.png
|
|
152
|
+
*
|
|
153
|
+
* // Custom filename and directory
|
|
154
|
+
* await painter.save(canvas.buffer, {
|
|
155
|
+
* directory: './my-images',
|
|
156
|
+
* filename: 'my-canvas',
|
|
157
|
+
* format: 'jpg',
|
|
158
|
+
* quality: 95
|
|
159
|
+
* });
|
|
160
|
+
*
|
|
161
|
+
* // Save with counter naming
|
|
162
|
+
* await painter.save(canvas.buffer, {
|
|
163
|
+
* naming: 'counter',
|
|
164
|
+
* prefix: 'image-',
|
|
165
|
+
* counterStart: 1
|
|
166
|
+
* });
|
|
167
|
+
* // Saves to: ./ApexPainter_output/image-1.png, image-2.png, etc.
|
|
168
|
+
*
|
|
169
|
+
* // Save multiple buffers
|
|
170
|
+
* const buffers = [canvas1.buffer, canvas2.buffer, canvas3.buffer];
|
|
171
|
+
* const results = await painter.saveMultiple(buffers, {
|
|
172
|
+
* prefix: 'batch-',
|
|
173
|
+
* naming: 'counter'
|
|
174
|
+
* });
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
save(buffer: Buffer, options?: SaveOptions): Promise<SaveResult>;
|
|
178
|
+
/**
|
|
179
|
+
* Save multiple buffers at once with batch options.
|
|
180
|
+
*
|
|
181
|
+
* @param buffers - Array of buffers to save
|
|
182
|
+
* @param options - Save options (applied to all files)
|
|
183
|
+
* @returns Array of SaveResult objects
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* ```typescript
|
|
187
|
+
* const canvas1 = await painter.createCanvas({ width: 800, height: 600 });
|
|
188
|
+
* const canvas2 = await painter.createCanvas({ width: 800, height: 600 });
|
|
189
|
+
* const results = await painter.saveMultiple([canvas1.buffer, canvas2.buffer], {
|
|
190
|
+
* prefix: 'batch-',
|
|
191
|
+
* naming: 'counter'
|
|
192
|
+
* });
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
saveMultiple(buffers: Buffer[], options?: SaveOptions): Promise<SaveResult[]>;
|
|
196
|
+
/**
|
|
197
|
+
* Reset the save counter (useful when using 'counter' naming).
|
|
198
|
+
*/
|
|
199
|
+
resetSaveCounter(): void;
|
|
138
200
|
}
|
|
139
201
|
export {};
|
|
140
202
|
//# sourceMappingURL=ApexPainter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApexPainter.d.ts","sourceRoot":"","sources":["../../../lib/Canvas/ApexPainter.ts"],"names":[],"mappings":"AAKA,OAAW,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AAElC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,WAAW,EAIpF,cAAc,EAAE,KAAK,EACrD,oBAAoB,EAAa,aAAa,EAAE,WAAW,EAAE,YAAY,EAIzE,WAAW,
|
|
1
|
+
{"version":3,"file":"ApexPainter.d.ts","sourceRoot":"","sources":["../../../lib/Canvas/ApexPainter.ts"],"names":[],"mappings":"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"}
|
|
@@ -16,6 +16,7 @@ const enhancedTextRenderer_1 = require("./utils/Texts/enhancedTextRenderer");
|
|
|
16
16
|
const enhancedPatternRenderer_1 = require("./utils/Patterns/enhancedPatternRenderer");
|
|
17
17
|
class ApexPainter {
|
|
18
18
|
format;
|
|
19
|
+
saveCounter = 1;
|
|
19
20
|
constructor({ type } = { type: 'buffer' }) {
|
|
20
21
|
this.format = { type: type || 'buffer' };
|
|
21
22
|
}
|
|
@@ -1849,6 +1850,201 @@ class ApexPainter {
|
|
|
1849
1850
|
throw new Error(`outPut failed: ${errorMessage}`);
|
|
1850
1851
|
}
|
|
1851
1852
|
}
|
|
1853
|
+
/**
|
|
1854
|
+
* Advanced save method to save buffers to local files with extensive customization options.
|
|
1855
|
+
*
|
|
1856
|
+
* @param buffer - Buffer to save (from createCanvas, createImage, createText, etc.)
|
|
1857
|
+
* @param options - Save options for file path, format, naming, etc.
|
|
1858
|
+
* @returns SaveResult with file path, name, size, and format
|
|
1859
|
+
*
|
|
1860
|
+
* @example
|
|
1861
|
+
* ```typescript
|
|
1862
|
+
* // Simple save with auto-generated name
|
|
1863
|
+
* const canvas = await painter.createCanvas({ width: 800, height: 600 });
|
|
1864
|
+
* const result = await painter.save(canvas.buffer);
|
|
1865
|
+
* // Saves to: ./ApexPainter_output/20241220_143025_123.png
|
|
1866
|
+
*
|
|
1867
|
+
* // Custom filename and directory
|
|
1868
|
+
* await painter.save(canvas.buffer, {
|
|
1869
|
+
* directory: './my-images',
|
|
1870
|
+
* filename: 'my-canvas',
|
|
1871
|
+
* format: 'jpg',
|
|
1872
|
+
* quality: 95
|
|
1873
|
+
* });
|
|
1874
|
+
*
|
|
1875
|
+
* // Save with counter naming
|
|
1876
|
+
* await painter.save(canvas.buffer, {
|
|
1877
|
+
* naming: 'counter',
|
|
1878
|
+
* prefix: 'image-',
|
|
1879
|
+
* counterStart: 1
|
|
1880
|
+
* });
|
|
1881
|
+
* // Saves to: ./ApexPainter_output/image-1.png, image-2.png, etc.
|
|
1882
|
+
*
|
|
1883
|
+
* // Save multiple buffers
|
|
1884
|
+
* const buffers = [canvas1.buffer, canvas2.buffer, canvas3.buffer];
|
|
1885
|
+
* const results = await painter.saveMultiple(buffers, {
|
|
1886
|
+
* prefix: 'batch-',
|
|
1887
|
+
* naming: 'counter'
|
|
1888
|
+
* });
|
|
1889
|
+
* ```
|
|
1890
|
+
*/
|
|
1891
|
+
async save(buffer, options) {
|
|
1892
|
+
try {
|
|
1893
|
+
if (!Buffer.isBuffer(buffer)) {
|
|
1894
|
+
throw new Error("save: buffer must be a Buffer.");
|
|
1895
|
+
}
|
|
1896
|
+
const opts = {
|
|
1897
|
+
directory: options?.directory ?? './ApexPainter_output',
|
|
1898
|
+
filename: options?.filename,
|
|
1899
|
+
format: options?.format ?? 'png',
|
|
1900
|
+
quality: options?.quality ?? 90,
|
|
1901
|
+
createDirectory: options?.createDirectory ?? true,
|
|
1902
|
+
naming: options?.naming ?? 'timestamp',
|
|
1903
|
+
counterStart: options?.counterStart ?? 1,
|
|
1904
|
+
prefix: options?.prefix ?? '',
|
|
1905
|
+
suffix: options?.suffix ?? '',
|
|
1906
|
+
overwrite: options?.overwrite ?? false
|
|
1907
|
+
};
|
|
1908
|
+
// Create directory if needed
|
|
1909
|
+
if (opts.createDirectory && !fs_1.default.existsSync(opts.directory)) {
|
|
1910
|
+
fs_1.default.mkdirSync(opts.directory, { recursive: true });
|
|
1911
|
+
}
|
|
1912
|
+
// Generate filename
|
|
1913
|
+
let filename;
|
|
1914
|
+
if (opts.filename) {
|
|
1915
|
+
filename = opts.filename;
|
|
1916
|
+
// Add extension if not present
|
|
1917
|
+
if (!filename.includes('.')) {
|
|
1918
|
+
filename += `.${opts.format}`;
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
else {
|
|
1922
|
+
// Auto-generate filename based on naming pattern
|
|
1923
|
+
switch (opts.naming) {
|
|
1924
|
+
case 'timestamp':
|
|
1925
|
+
const now = new Date();
|
|
1926
|
+
const timestamp = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}${String(now.getDate()).padStart(2, '0')}_${String(now.getHours()).padStart(2, '0')}${String(now.getMinutes()).padStart(2, '0')}${String(now.getSeconds()).padStart(2, '0')}_${String(now.getMilliseconds()).padStart(3, '0')}`;
|
|
1927
|
+
filename = `${opts.prefix}${timestamp}${opts.suffix}.${opts.format}`;
|
|
1928
|
+
break;
|
|
1929
|
+
case 'counter':
|
|
1930
|
+
filename = `${opts.prefix}${this.saveCounter}${opts.suffix}.${opts.format}`;
|
|
1931
|
+
this.saveCounter++;
|
|
1932
|
+
break;
|
|
1933
|
+
case 'custom':
|
|
1934
|
+
filename = `${opts.prefix}${opts.suffix}.${opts.format}`;
|
|
1935
|
+
break;
|
|
1936
|
+
default:
|
|
1937
|
+
filename = `${opts.prefix}${Date.now()}${opts.suffix}.${opts.format}`;
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
// Handle file overwrite
|
|
1941
|
+
const filePath = path_1.default.join(opts.directory, filename);
|
|
1942
|
+
if (!opts.overwrite && fs_1.default.existsSync(filePath)) {
|
|
1943
|
+
// Add number suffix if file exists
|
|
1944
|
+
let counter = 1;
|
|
1945
|
+
let newPath = filePath;
|
|
1946
|
+
const ext = path_1.default.extname(filePath);
|
|
1947
|
+
const baseName = path_1.default.basename(filePath, ext);
|
|
1948
|
+
const dir = path_1.default.dirname(filePath);
|
|
1949
|
+
while (fs_1.default.existsSync(newPath)) {
|
|
1950
|
+
newPath = path_1.default.join(dir, `${baseName}_${counter}${ext}`);
|
|
1951
|
+
counter++;
|
|
1952
|
+
}
|
|
1953
|
+
filename = path_1.default.basename(newPath);
|
|
1954
|
+
}
|
|
1955
|
+
// Convert buffer format if needed
|
|
1956
|
+
let finalBuffer = buffer;
|
|
1957
|
+
if (opts.format !== 'png') {
|
|
1958
|
+
// Use Sharp for format conversion
|
|
1959
|
+
const sharp = require('sharp');
|
|
1960
|
+
let sharpImage = sharp(buffer);
|
|
1961
|
+
switch (opts.format) {
|
|
1962
|
+
case 'jpg':
|
|
1963
|
+
case 'jpeg':
|
|
1964
|
+
finalBuffer = await sharpImage
|
|
1965
|
+
.jpeg({ quality: opts.quality, progressive: false })
|
|
1966
|
+
.toBuffer();
|
|
1967
|
+
break;
|
|
1968
|
+
case 'webp':
|
|
1969
|
+
finalBuffer = await sharpImage
|
|
1970
|
+
.webp({ quality: opts.quality })
|
|
1971
|
+
.toBuffer();
|
|
1972
|
+
break;
|
|
1973
|
+
case 'avif':
|
|
1974
|
+
finalBuffer = await sharpImage
|
|
1975
|
+
.avif({ quality: opts.quality })
|
|
1976
|
+
.toBuffer();
|
|
1977
|
+
break;
|
|
1978
|
+
case 'gif':
|
|
1979
|
+
// GIF requires special handling - keep as PNG if not already GIF
|
|
1980
|
+
if (!buffer.toString('ascii', 0, 3).includes('GIF')) {
|
|
1981
|
+
console.warn('save: Converting to GIF may not preserve quality. Consider using PNG.');
|
|
1982
|
+
finalBuffer = buffer; // Keep original for now
|
|
1983
|
+
}
|
|
1984
|
+
break;
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
// Write file
|
|
1988
|
+
const finalPath = path_1.default.join(opts.directory, filename);
|
|
1989
|
+
fs_1.default.writeFileSync(finalPath, finalBuffer);
|
|
1990
|
+
return {
|
|
1991
|
+
path: finalPath,
|
|
1992
|
+
filename: filename,
|
|
1993
|
+
size: finalBuffer.length,
|
|
1994
|
+
format: opts.format
|
|
1995
|
+
};
|
|
1996
|
+
}
|
|
1997
|
+
catch (error) {
|
|
1998
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
1999
|
+
throw new Error(`save failed: ${errorMessage}`);
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
/**
|
|
2003
|
+
* Save multiple buffers at once with batch options.
|
|
2004
|
+
*
|
|
2005
|
+
* @param buffers - Array of buffers to save
|
|
2006
|
+
* @param options - Save options (applied to all files)
|
|
2007
|
+
* @returns Array of SaveResult objects
|
|
2008
|
+
*
|
|
2009
|
+
* @example
|
|
2010
|
+
* ```typescript
|
|
2011
|
+
* const canvas1 = await painter.createCanvas({ width: 800, height: 600 });
|
|
2012
|
+
* const canvas2 = await painter.createCanvas({ width: 800, height: 600 });
|
|
2013
|
+
* const results = await painter.saveMultiple([canvas1.buffer, canvas2.buffer], {
|
|
2014
|
+
* prefix: 'batch-',
|
|
2015
|
+
* naming: 'counter'
|
|
2016
|
+
* });
|
|
2017
|
+
* ```
|
|
2018
|
+
*/
|
|
2019
|
+
async saveMultiple(buffers, options) {
|
|
2020
|
+
try {
|
|
2021
|
+
if (!Array.isArray(buffers) || buffers.length === 0) {
|
|
2022
|
+
throw new Error("saveMultiple: buffers must be a non-empty array.");
|
|
2023
|
+
}
|
|
2024
|
+
const results = [];
|
|
2025
|
+
const baseCounter = options?.counterStart ?? this.saveCounter;
|
|
2026
|
+
for (let i = 0; i < buffers.length; i++) {
|
|
2027
|
+
const bufferOptions = {
|
|
2028
|
+
...options,
|
|
2029
|
+
counterStart: baseCounter + i,
|
|
2030
|
+
naming: options?.naming === 'counter' ? 'counter' : options?.naming
|
|
2031
|
+
};
|
|
2032
|
+
const result = await this.save(buffers[i], bufferOptions);
|
|
2033
|
+
results.push(result);
|
|
2034
|
+
}
|
|
2035
|
+
return results;
|
|
2036
|
+
}
|
|
2037
|
+
catch (error) {
|
|
2038
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
2039
|
+
throw new Error(`saveMultiple failed: ${errorMessage}`);
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
/**
|
|
2043
|
+
* Reset the save counter (useful when using 'counter' naming).
|
|
2044
|
+
*/
|
|
2045
|
+
resetSaveCounter() {
|
|
2046
|
+
this.saveCounter = 1;
|
|
2047
|
+
}
|
|
1852
2048
|
/**
|
|
1853
2049
|
* Applies stroke style to shape context
|
|
1854
2050
|
* @private
|