@keeratita/heic-converter 0.2.0 โ 0.4.0
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/README.md +160 -14
- package/dist/heic-decoder.wasm +0 -0
- package/dist/index.d.mts +136 -8
- package/dist/index.d.ts +136 -8
- package/dist/index.js +6 -6
- package/dist/index.mjs +6 -6
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @keeratita/heic-converter
|
|
2
2
|
|
|
3
|
-
A modern, lightweight TypeScript library to convert `.heic` and `.heif` images to standard web formats (JPEG, PNG, SVG) client-side in the browser or on the backend in Node.js.
|
|
3
|
+
A modern, lightweight TypeScript library to convert `.heic` and `.heif` images to standard web formats (JPEG, PNG, WebP, SVG) client-side in the browser or on the backend in Node.js.
|
|
4
4
|
|
|
5
5
|
Designed specifically for environments with strict **Content Security Policy (CSP)** rules, it is built with WebAssembly compiled **without** dynamic code execution (`eval()` or `new Function()`).
|
|
6
6
|
|
|
@@ -10,10 +10,13 @@ Designed specifically for environments with strict **Content Security Policy (CS
|
|
|
10
10
|
|
|
11
11
|
- ๐ **CSP Compliant**: Emscripten glue code is compiled with `-s DYNAMIC_EXECUTION=0`. Safe to run without `'unsafe-eval'`.
|
|
12
12
|
- ๐งฉ **Dependency Injection Architecture**: Swap the decoder module easily by implementing a simple `IHeicDecoder` interface.
|
|
13
|
-
- โก **Optimized Performance**:
|
|
13
|
+
- โก **Optimized Performance**: A fresh decoder instance is created and released per conversion โ memory is reclaimed promptly and concurrent conversions never share mutable WASM state.
|
|
14
14
|
- ๐ **Isomorphic / Universal**: Runs in Node.js (decoding) and browser (decoding & canvas-based encoding).
|
|
15
15
|
- ๐ฆ **No Bloat**: Zero external production dependencies. Small footprint.
|
|
16
|
-
- ๐จ **Format Support**: Convert to `jpeg` (with quality configuration), `png`, and `svg` (embedded lossless vector).
|
|
16
|
+
- ๐จ **Format Support**: Convert to `jpeg` (with quality configuration), `png`, `webp`, and `svg` (embedded lossless vector).
|
|
17
|
+
- ๐ **Resize Support**: Downscale with `maxWidth`/`maxHeight` or apply a uniform `scale` factor during conversion.
|
|
18
|
+
- ๐ **Batch Conversion**: Convert many images at once with bounded concurrency via `convertMany`.
|
|
19
|
+
- ๐งต **Web Worker Helper**: Offload conversions to a Web Worker with `convertHeicInWorker` to keep the UI thread responsive.
|
|
17
20
|
|
|
18
21
|
---
|
|
19
22
|
|
|
@@ -56,7 +59,32 @@ const imageUrl = URL.createObjectURL(jpegBlob);
|
|
|
56
59
|
document.querySelector('img').src = imageUrl;
|
|
57
60
|
```
|
|
58
61
|
|
|
59
|
-
### 2. Browser:
|
|
62
|
+
### 2. Browser: WebP Output
|
|
63
|
+
|
|
64
|
+
WebP offers excellent compression with quality configuration:
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { convertHeic } from '@keeratita/heic-converter';
|
|
68
|
+
|
|
69
|
+
const webpBlob = await convertHeic(heicBlob, {
|
|
70
|
+
to: 'webp',
|
|
71
|
+
quality: 0.8,
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 3. Browser: SVG Output
|
|
76
|
+
|
|
77
|
+
SVG wraps the raster image as an embedded lossless PNG inside an SVG container:
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
import { convertHeic } from '@keeratita/heic-converter';
|
|
81
|
+
|
|
82
|
+
const svgBlob = await convertHeic(heicBlob, {
|
|
83
|
+
to: 'svg',
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 4. Browser: Serving and Locating WASM (Custom Assets Path)
|
|
60
88
|
|
|
61
89
|
By default, the library tries to fetch `heic-decoder.wasm` relative to the current module script path (`import.meta.url`).
|
|
62
90
|
|
|
@@ -93,7 +121,7 @@ const jpegBlob = await convertHeic(heicBlob, {
|
|
|
93
121
|
});
|
|
94
122
|
```
|
|
95
123
|
|
|
96
|
-
###
|
|
124
|
+
### 5. Node.js: Decoding Raw Pixel Data
|
|
97
125
|
|
|
98
126
|
Since Node.js lacks the native browser Canvas API, `convertHeic` (which relies on Canvas to encode raster formats) will throw an error on the backend.
|
|
99
127
|
|
|
@@ -110,11 +138,12 @@ async function convertNode() {
|
|
|
110
138
|
const decoder = new LibheifDecoder();
|
|
111
139
|
await decoder.initialize();
|
|
112
140
|
|
|
113
|
-
// Decodes to { width, height, data: Uint8ClampedArray (RGBA) }
|
|
141
|
+
// Decodes to { width, height, data: Uint8ClampedArray (RGBA) }.
|
|
142
|
+
// data is an independent copy โ safe to use after decoder.free().
|
|
114
143
|
const { width, height, data } = await decoder.decode(heicData);
|
|
115
144
|
|
|
116
145
|
// Process raw pixels using sharp
|
|
117
|
-
await sharp(Buffer.from(data
|
|
146
|
+
await sharp(Buffer.from(data), {
|
|
118
147
|
raw: { width, height, channels: 4 },
|
|
119
148
|
})
|
|
120
149
|
.toFormat('jpeg')
|
|
@@ -125,7 +154,7 @@ async function convertNode() {
|
|
|
125
154
|
}
|
|
126
155
|
```
|
|
127
156
|
|
|
128
|
-
###
|
|
157
|
+
### 6. Progress Tracking (e.g. for Large Images)
|
|
129
158
|
|
|
130
159
|
For large images, you can pass an `onProgress` callback to track the conversion progress (0% to 100%):
|
|
131
160
|
|
|
@@ -145,6 +174,89 @@ const jpegBlob = await convertHeic(heicBlob, {
|
|
|
145
174
|
> [!TIP]
|
|
146
175
|
> Since the WebAssembly module runs on the main browser thread, the UI thread will be occupied during conversion. For maximum responsiveness when converting large images, it is highly recommended to run this library inside a standard JS **Web Worker** and communicate progress back to the main thread.
|
|
147
176
|
|
|
177
|
+
### 7. Resizing Images
|
|
178
|
+
|
|
179
|
+
Downscale to fit within maximum dimensions (aspect ratio is preserved, images smaller than the bounds are never upscaled):
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
import { convertHeic } from '@keeratita/heic-converter';
|
|
183
|
+
|
|
184
|
+
const thumbnailBlob = await convertHeic(heicBlob, {
|
|
185
|
+
to: 'jpeg',
|
|
186
|
+
maxWidth: 800,
|
|
187
|
+
maxHeight: 600,
|
|
188
|
+
});
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Or apply a uniform scale factor (can also upscale):
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
const halfSizeBlob = await convertHeic(heicBlob, {
|
|
195
|
+
to: 'webp',
|
|
196
|
+
scale: 0.5,
|
|
197
|
+
});
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### 8. Batch Conversion
|
|
201
|
+
|
|
202
|
+
Convert many images at once with bounded concurrency (default `4`). Results are returned in input order; if any conversion fails, the promise rejects as soon as the failure is known with an error that identifies the failing item:
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
import { convertMany } from '@keeratita/heic-converter';
|
|
206
|
+
|
|
207
|
+
const blobs = await convertMany(heicFiles, {
|
|
208
|
+
to: 'png',
|
|
209
|
+
concurrency: 3,
|
|
210
|
+
onProgress: (index, percent) => {
|
|
211
|
+
console.log(`Image ${index}: ${Math.round(percent)}%`);
|
|
212
|
+
},
|
|
213
|
+
});
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### 9. Web Worker Conversion
|
|
217
|
+
|
|
218
|
+
Run the conversion inside a Web Worker so the main thread stays responsive. Create a worker script that uses this library:
|
|
219
|
+
|
|
220
|
+
```js
|
|
221
|
+
// converter.worker.js
|
|
222
|
+
import { convertHeic } from '@keeratita/heic-converter';
|
|
223
|
+
|
|
224
|
+
self.onmessage = async (event) => {
|
|
225
|
+
const { input, options } = event.data;
|
|
226
|
+
try {
|
|
227
|
+
const blob = await convertHeic(input, {
|
|
228
|
+
...options,
|
|
229
|
+
onProgress: (percent) => self.postMessage({ type: 'progress', percent }),
|
|
230
|
+
});
|
|
231
|
+
self.postMessage({ type: 'result', ok: true, blob });
|
|
232
|
+
} catch (error) {
|
|
233
|
+
self.postMessage({ type: 'result', ok: false, error: error?.stack ?? error?.message ?? String(error) });
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Then convert from the main thread:
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
import { convertHeicInWorker } from '@keeratita/heic-converter';
|
|
242
|
+
|
|
243
|
+
const jpegBlob = await convertHeicInWorker(heicBlob, {
|
|
244
|
+
workerUrl: new URL('./converter.worker.js', import.meta.url),
|
|
245
|
+
workerType: 'module',
|
|
246
|
+
to: 'jpeg',
|
|
247
|
+
quality: 0.9,
|
|
248
|
+
onProgress: (percent) => console.log(`${Math.round(percent)}%`),
|
|
249
|
+
});
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
> [!NOTE]
|
|
253
|
+
> - Use `workerType: 'module'` when the worker script uses ES module imports (as in the example above). The default is `'classic'`, which requires the script to be pre-bundled (e.g. by Vite or webpack) โ static ES imports are not supported in classic workers.
|
|
254
|
+
> - `workerUrl` should be a compile-time constant; the script runs with the page's privileges.
|
|
255
|
+
> - Only `progress` and `result` messages are understood; any other message type is ignored.
|
|
256
|
+
> - `decoder`, `onProgress`, and `workerUrl` are not structured-cloneable, so they are not sent to the worker. Progress is forwarded through `{ type: 'progress', percent }` messages instead.
|
|
257
|
+
> - `timeoutMs` (default `60000`) bounds how long the promise waits for a result; set `0` to disable.
|
|
258
|
+
> - This helper is **browser-only**: it rejects in Node.js, where there is no global `Worker`.
|
|
259
|
+
|
|
148
260
|
---
|
|
149
261
|
|
|
150
262
|
## ๐ Content Security Policy (CSP)
|
|
@@ -168,10 +280,35 @@ Converts a HEIC image file to a standard web format.
|
|
|
168
280
|
|
|
169
281
|
- **`input`**: `Blob | File | ArrayBuffer | Uint8Array`
|
|
170
282
|
- **`options`**: (optional) `ConvertOptions`
|
|
171
|
-
- `to`: `'jpeg' | 'jpg' | 'png' | 'svg'` (Default: `'jpeg'`)
|
|
172
|
-
- `quality`: `number` (0.0 to 1.0,
|
|
283
|
+
- `to`: `'jpeg' | 'jpg' | 'png' | 'webp' | 'svg'` (Default: `'jpeg'`)
|
|
284
|
+
- `quality`: `number` (0.0 to 1.0, applicable to JPEG and WebP. Default: `0.92`)
|
|
173
285
|
- `decoder`: `IHeicDecoder` (Inject custom decoder instance)
|
|
174
286
|
- `onProgress`: `(percent: number) => void` (Optional callback, receives progress percentage from `0` to `100` during decoding)
|
|
287
|
+
- `maxWidth`: `number` (Downscale to fit within this width, preserving aspect ratio. Never upscales)
|
|
288
|
+
- `maxHeight`: `number` (Downscale to fit within this height, preserving aspect ratio. Never upscales)
|
|
289
|
+
- `scale`: `number` (Uniform scale factor, e.g. `0.5` halves the image. Takes precedence over `maxWidth`/`maxHeight`)
|
|
290
|
+
- **Returns**: `Promise<Blob>`
|
|
291
|
+
|
|
292
|
+
### `convertMany(inputs, options?)`
|
|
293
|
+
|
|
294
|
+
Converts multiple HEIC images with bounded concurrency. Results are returned in input order; rejects as soon as a conversion fails, with an error identifying the failing item index.
|
|
295
|
+
|
|
296
|
+
- **`inputs`**: `Array<Blob | File | ArrayBuffer | Uint8Array>`
|
|
297
|
+
- **`options`**: (optional) `ConvertManyOptions` โ same as `ConvertOptions`, except `onProgress` uses the batch signature below; plus:
|
|
298
|
+
- `concurrency`: `number` (Maximum concurrent conversions. Default: `4`)
|
|
299
|
+
- `onProgress`: `(index: number, percent: number) => void` (Per-item progress callback)
|
|
300
|
+
- `decoder`: `IHeicDecoder` (Optional. When provided, the same instance is shared by all concurrent conversions and must be safe for concurrent `decode()` calls)
|
|
301
|
+
- **Returns**: `Promise<Blob[]>`
|
|
302
|
+
|
|
303
|
+
### `convertHeicInWorker(input, options)`
|
|
304
|
+
|
|
305
|
+
Converts a HEIC image inside a Web Worker. The worker script must implement the message protocol shown in [Usage section 9](#9-web-worker-conversion). Browser-only; rejects in Node.js.
|
|
306
|
+
|
|
307
|
+
- **`input`**: `Blob | File | ArrayBuffer | Uint8Array`
|
|
308
|
+
- **`options`**: `WorkerConvertOptions` โ same as `ConvertOptions`, plus:
|
|
309
|
+
- `workerUrl`: `string | URL` (URL of the worker script; should be a compile-time constant)
|
|
310
|
+
- `workerType`: `'classic' | 'module'` (Worker script type. Default: `'classic'`; use `'module'` for scripts with ES imports)
|
|
311
|
+
- `timeoutMs`: `number` (Maximum wait for the result in milliseconds. Default: `60000`; `0` disables)
|
|
175
312
|
- **Returns**: `Promise<Blob>`
|
|
176
313
|
|
|
177
314
|
### `LibheifDecoder(options?)`
|
|
@@ -182,13 +319,13 @@ The default WASM-based implementation of `IHeicDecoder`.
|
|
|
182
319
|
- `locateFile`: `(path: string, prefix: string) => string`
|
|
183
320
|
- `wasmBinary`: `ArrayBuffer`
|
|
184
321
|
- **Methods**:
|
|
185
|
-
- `initialize(): Promise<void>`: Loads and initializes the WASM wrapper.
|
|
186
|
-
- `decode(data: Uint8Array, onProgress?: (percent: number) => void): Promise<DecodedImage>`: Decodes the HEIC bytes to raw RGBA, with optional progress callback.
|
|
187
|
-
- `free(): void`: Releases allocated WebAssembly heap memory.
|
|
322
|
+
- `initialize(): Promise<void>`: Loads and initializes the WASM wrapper. Safe to call concurrently; the module loads at most once per instance.
|
|
323
|
+
- `decode(data: Uint8Array, onProgress?: (percent: number) => void): Promise<DecodedImage>`: Decodes the HEIC bytes to raw RGBA, with optional progress callback. Returns an independent copy of the pixel data โ safe to use after `free()`.
|
|
324
|
+
- `free(): void`: Releases allocated WebAssembly heap memory. Idempotent.
|
|
188
325
|
|
|
189
326
|
### `freeSharedDecoder()`
|
|
190
327
|
|
|
191
|
-
|
|
328
|
+
Kept for **API compatibility**. Decoders are now created and released per conversion, so there is no shared instance to release โ calling this function is a no-op.
|
|
192
329
|
|
|
193
330
|
```typescript
|
|
194
331
|
import { freeSharedDecoder } from '@keeratita/heic-converter';
|
|
@@ -225,6 +362,15 @@ npm run build
|
|
|
225
362
|
npm run test
|
|
226
363
|
```
|
|
227
364
|
|
|
365
|
+
### Run Browser E2E Tests
|
|
366
|
+
|
|
367
|
+
Real browser conversions against the CSP sandbox and the GitHub Pages demo (`docs/`):
|
|
368
|
+
|
|
369
|
+
```bash
|
|
370
|
+
npx playwright install chromium # one-time
|
|
371
|
+
npm run test:e2e
|
|
372
|
+
```
|
|
373
|
+
|
|
228
374
|
### Run Interactive CSP Sandbox
|
|
229
375
|
|
|
230
376
|
To test the converter in a local browser running under a strict Content Security Policy, start the sandbox server:
|
package/dist/heic-decoder.wasm
CHANGED
|
Binary file
|
package/dist/index.d.mts
CHANGED
|
@@ -19,8 +19,28 @@ interface IHeicDecoder {
|
|
|
19
19
|
*/
|
|
20
20
|
free(): void;
|
|
21
21
|
}
|
|
22
|
-
type ImageFormat = 'jpeg' | 'jpg' | 'png' | 'svg';
|
|
23
|
-
|
|
22
|
+
type ImageFormat = 'jpeg' | 'jpg' | 'png' | 'svg' | 'webp';
|
|
23
|
+
type HeicInput = Blob | File | ArrayBuffer | Uint8Array;
|
|
24
|
+
interface ResizeOptions {
|
|
25
|
+
/**
|
|
26
|
+
* Maximum width in pixels. The image is downscaled to fit within this
|
|
27
|
+
* bound while preserving the aspect ratio. Images smaller than the bound
|
|
28
|
+
* are never upscaled.
|
|
29
|
+
*/
|
|
30
|
+
maxWidth?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Maximum height in pixels. The image is downscaled to fit within this
|
|
33
|
+
* bound while preserving the aspect ratio. Images smaller than the bound
|
|
34
|
+
* are never upscaled.
|
|
35
|
+
*/
|
|
36
|
+
maxHeight?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Uniform scale factor applied to both dimensions (e.g. 0.5 halves the
|
|
39
|
+
* image). Takes precedence over `maxWidth` and `maxHeight` when set.
|
|
40
|
+
*/
|
|
41
|
+
scale?: number;
|
|
42
|
+
}
|
|
43
|
+
interface ConvertOptions extends ResizeOptions {
|
|
24
44
|
/**
|
|
25
45
|
* Target format for the conversion.
|
|
26
46
|
* @default 'jpeg'
|
|
@@ -28,7 +48,7 @@ interface ConvertOptions {
|
|
|
28
48
|
to?: ImageFormat;
|
|
29
49
|
/**
|
|
30
50
|
* Quality of the converted image (between 0.0 and 1.0).
|
|
31
|
-
*
|
|
51
|
+
* Applicable for 'jpeg', 'jpg', and 'webp' formats.
|
|
32
52
|
* @default 0.92
|
|
33
53
|
*/
|
|
34
54
|
quality?: number;
|
|
@@ -42,6 +62,25 @@ interface ConvertOptions {
|
|
|
42
62
|
*/
|
|
43
63
|
onProgress?: (percent: number) => void;
|
|
44
64
|
}
|
|
65
|
+
interface ConvertManyOptions extends Omit<ConvertOptions, 'onProgress'> {
|
|
66
|
+
/**
|
|
67
|
+
* Maximum number of conversions running concurrently.
|
|
68
|
+
* @default 4
|
|
69
|
+
*/
|
|
70
|
+
concurrency?: number;
|
|
71
|
+
/**
|
|
72
|
+
* Optional progress callback that receives the item index and its
|
|
73
|
+
* progress percentage (0 to 100) during decoding.
|
|
74
|
+
*/
|
|
75
|
+
onProgress?: (index: number, percent: number) => void;
|
|
76
|
+
/**
|
|
77
|
+
* Optional custom decoder implementation to inject. When provided, the
|
|
78
|
+
* same instance is shared by all concurrent conversions, so it must be
|
|
79
|
+
* safe for concurrent `decode()` calls. If not provided, a fresh
|
|
80
|
+
* default LibheifDecoder is created per item.
|
|
81
|
+
*/
|
|
82
|
+
decoder?: IHeicDecoder;
|
|
83
|
+
}
|
|
45
84
|
|
|
46
85
|
interface LibheifDecoderOptions {
|
|
47
86
|
/**
|
|
@@ -59,9 +98,15 @@ declare class LibheifDecoder implements IHeicDecoder {
|
|
|
59
98
|
private options?;
|
|
60
99
|
private module;
|
|
61
100
|
private decoderInstance;
|
|
101
|
+
/**
|
|
102
|
+
* Memoized module-loading promise so concurrent initialize()/decode() calls
|
|
103
|
+
* on the same instance never instantiate (or mutate) the module twice.
|
|
104
|
+
*/
|
|
105
|
+
private initPromise;
|
|
62
106
|
constructor(options?: LibheifDecoderOptions);
|
|
63
107
|
/**
|
|
64
108
|
* Initializes the WebAssembly module and instantiates the HEIC decoder.
|
|
109
|
+
* Safe to call concurrently: the module is loaded at most once per instance.
|
|
65
110
|
*/
|
|
66
111
|
initialize(): Promise<void>;
|
|
67
112
|
/**
|
|
@@ -72,22 +117,105 @@ declare class LibheifDecoder implements IHeicDecoder {
|
|
|
72
117
|
decode(data: Uint8Array, onProgress?: (percent: number) => void): Promise<DecodedImage>;
|
|
73
118
|
/**
|
|
74
119
|
* Cleans up the WebAssembly decoder instance and resources.
|
|
120
|
+
* Idempotent: safe to call multiple times. After free(), the instance must
|
|
121
|
+
* be re-initialized (createHeicDecoderModule runs again on the next call).
|
|
75
122
|
*/
|
|
76
123
|
free(): void;
|
|
77
124
|
}
|
|
78
125
|
|
|
126
|
+
interface WorkerConvertOptions extends ConvertOptions {
|
|
127
|
+
/**
|
|
128
|
+
* URL of the worker script that performs the conversion. Should be a
|
|
129
|
+
* compile-time constant (e.g. `new URL('./worker.js', import.meta.url)`
|
|
130
|
+
* under a bundler); the script runs with the page's privileges.
|
|
131
|
+
*/
|
|
132
|
+
workerUrl: string | URL;
|
|
133
|
+
/**
|
|
134
|
+
* Maximum time in milliseconds to wait for the worker result before
|
|
135
|
+
* rejecting with a timeout. Defaults to 60000 (60s). Set to 0 to
|
|
136
|
+
* disable the timeout.
|
|
137
|
+
*/
|
|
138
|
+
timeoutMs?: number;
|
|
139
|
+
/**
|
|
140
|
+
* Worker script type. Use `'module'` when the script uses ES module
|
|
141
|
+
* imports (e.g. `import { convertHeic } from ...`); `'classic'`
|
|
142
|
+
* scripts must be pre-bundled.
|
|
143
|
+
* @default 'classic'
|
|
144
|
+
*/
|
|
145
|
+
workerType?: 'classic' | 'module';
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Converts a HEIC image inside a Web Worker so the main thread stays
|
|
149
|
+
* responsive during the (potentially slow) WASM decode.
|
|
150
|
+
*
|
|
151
|
+
* The worker script is user-provided and must handle the following message
|
|
152
|
+
* protocol:
|
|
153
|
+
*
|
|
154
|
+
* ```js
|
|
155
|
+
* // converter.worker.js
|
|
156
|
+
* import { convertHeic } from '@keeratita/heic-converter';
|
|
157
|
+
*
|
|
158
|
+
* self.onmessage = async (event) => {
|
|
159
|
+
* const { input, options } = event.data;
|
|
160
|
+
* try {
|
|
161
|
+
* const blob = await convertHeic(input, {
|
|
162
|
+
* ...options,
|
|
163
|
+
* onProgress: (percent) => self.postMessage({ type: 'progress', percent }),
|
|
164
|
+
* });
|
|
165
|
+
* self.postMessage({ type: 'result', ok: true, blob });
|
|
166
|
+
* } catch (error) {
|
|
167
|
+
* self.postMessage({ type: 'result', ok: false, error: error?.stack ?? error?.message ?? String(error) });
|
|
168
|
+
* }
|
|
169
|
+
* };
|
|
170
|
+
* ```
|
|
171
|
+
*
|
|
172
|
+
* ```ts
|
|
173
|
+
* const jpegBlob = await convertHeicInWorker(heicBlob, {
|
|
174
|
+
* workerUrl: new URL('./converter.worker.js', import.meta.url),
|
|
175
|
+
* workerType: 'module',
|
|
176
|
+
* to: 'jpeg',
|
|
177
|
+
* });
|
|
178
|
+
* ```
|
|
179
|
+
*
|
|
180
|
+
* Only `progress` and `result` messages are understood; any other message
|
|
181
|
+
* type is ignored. Use `workerType: 'module'` when the script uses ES
|
|
182
|
+
* module imports (as in the example above); `'classic'` scripts must be
|
|
183
|
+
* pre-bundled, since static ES imports are not supported there.
|
|
184
|
+
*
|
|
185
|
+
* @param input HEIC image as a Blob, File, ArrayBuffer, or Uint8Array.
|
|
186
|
+
* @param options Conversion options plus the worker script URL.
|
|
187
|
+
* @returns A Promise resolving to the converted image as a Blob.
|
|
188
|
+
*/
|
|
189
|
+
declare function convertHeicInWorker(input: HeicInput, options: WorkerConvertOptions): Promise<Blob>;
|
|
190
|
+
|
|
79
191
|
/**
|
|
80
|
-
* Releases
|
|
81
|
-
*
|
|
192
|
+
* Releases any cached decoder resources.
|
|
193
|
+
*
|
|
194
|
+
* Decoders are now created and released per conversion, so there is no shared
|
|
195
|
+
* instance to free. This function is kept for API compatibility.
|
|
82
196
|
*/
|
|
83
197
|
declare function freeSharedDecoder(): void;
|
|
84
198
|
/**
|
|
85
|
-
* Converts HEIC image data to a standard web format (JPEG, PNG, or SVG).
|
|
199
|
+
* Converts HEIC image data to a standard web format (JPEG, PNG, WebP, or SVG).
|
|
86
200
|
*
|
|
87
201
|
* @param input HEIC image as a Blob, File, ArrayBuffer, or Uint8Array.
|
|
88
202
|
* @param options Conversion configuration options.
|
|
89
203
|
* @returns A Promise resolving to the converted image as a Blob.
|
|
90
204
|
*/
|
|
91
|
-
declare function convertHeic(input:
|
|
205
|
+
declare function convertHeic(input: HeicInput, options?: ConvertOptions): Promise<Blob>;
|
|
206
|
+
/**
|
|
207
|
+
* Converts multiple HEIC images to a standard web format.
|
|
208
|
+
*
|
|
209
|
+
* Conversions run with a bounded concurrency (default 4) and results are
|
|
210
|
+
* returned in the same order as the inputs. If any conversion fails, the
|
|
211
|
+
* returned promise rejects as soon as the failure is known (in-flight
|
|
212
|
+
* conversions are allowed to finish in the background) with an error that
|
|
213
|
+
* identifies the failing item index.
|
|
214
|
+
*
|
|
215
|
+
* @param inputs HEIC images as Blobs, Files, ArrayBuffers, or Uint8Arrays.
|
|
216
|
+
* @param options Batch conversion options.
|
|
217
|
+
* @returns A Promise resolving to the converted images as Blobs, in input order.
|
|
218
|
+
*/
|
|
219
|
+
declare function convertMany(inputs: HeicInput[], options?: ConvertManyOptions): Promise<Blob[]>;
|
|
92
220
|
|
|
93
|
-
export { type ConvertOptions, type DecodedImage, type IHeicDecoder, type ImageFormat, LibheifDecoder, type LibheifDecoderOptions, convertHeic, freeSharedDecoder };
|
|
221
|
+
export { type ConvertManyOptions, type ConvertOptions, type DecodedImage, type HeicInput, type IHeicDecoder, type ImageFormat, LibheifDecoder, type LibheifDecoderOptions, type ResizeOptions, type WorkerConvertOptions, convertHeic, convertHeicInWorker, convertMany, freeSharedDecoder };
|
package/dist/index.d.ts
CHANGED
|
@@ -19,8 +19,28 @@ interface IHeicDecoder {
|
|
|
19
19
|
*/
|
|
20
20
|
free(): void;
|
|
21
21
|
}
|
|
22
|
-
type ImageFormat = 'jpeg' | 'jpg' | 'png' | 'svg';
|
|
23
|
-
|
|
22
|
+
type ImageFormat = 'jpeg' | 'jpg' | 'png' | 'svg' | 'webp';
|
|
23
|
+
type HeicInput = Blob | File | ArrayBuffer | Uint8Array;
|
|
24
|
+
interface ResizeOptions {
|
|
25
|
+
/**
|
|
26
|
+
* Maximum width in pixels. The image is downscaled to fit within this
|
|
27
|
+
* bound while preserving the aspect ratio. Images smaller than the bound
|
|
28
|
+
* are never upscaled.
|
|
29
|
+
*/
|
|
30
|
+
maxWidth?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Maximum height in pixels. The image is downscaled to fit within this
|
|
33
|
+
* bound while preserving the aspect ratio. Images smaller than the bound
|
|
34
|
+
* are never upscaled.
|
|
35
|
+
*/
|
|
36
|
+
maxHeight?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Uniform scale factor applied to both dimensions (e.g. 0.5 halves the
|
|
39
|
+
* image). Takes precedence over `maxWidth` and `maxHeight` when set.
|
|
40
|
+
*/
|
|
41
|
+
scale?: number;
|
|
42
|
+
}
|
|
43
|
+
interface ConvertOptions extends ResizeOptions {
|
|
24
44
|
/**
|
|
25
45
|
* Target format for the conversion.
|
|
26
46
|
* @default 'jpeg'
|
|
@@ -28,7 +48,7 @@ interface ConvertOptions {
|
|
|
28
48
|
to?: ImageFormat;
|
|
29
49
|
/**
|
|
30
50
|
* Quality of the converted image (between 0.0 and 1.0).
|
|
31
|
-
*
|
|
51
|
+
* Applicable for 'jpeg', 'jpg', and 'webp' formats.
|
|
32
52
|
* @default 0.92
|
|
33
53
|
*/
|
|
34
54
|
quality?: number;
|
|
@@ -42,6 +62,25 @@ interface ConvertOptions {
|
|
|
42
62
|
*/
|
|
43
63
|
onProgress?: (percent: number) => void;
|
|
44
64
|
}
|
|
65
|
+
interface ConvertManyOptions extends Omit<ConvertOptions, 'onProgress'> {
|
|
66
|
+
/**
|
|
67
|
+
* Maximum number of conversions running concurrently.
|
|
68
|
+
* @default 4
|
|
69
|
+
*/
|
|
70
|
+
concurrency?: number;
|
|
71
|
+
/**
|
|
72
|
+
* Optional progress callback that receives the item index and its
|
|
73
|
+
* progress percentage (0 to 100) during decoding.
|
|
74
|
+
*/
|
|
75
|
+
onProgress?: (index: number, percent: number) => void;
|
|
76
|
+
/**
|
|
77
|
+
* Optional custom decoder implementation to inject. When provided, the
|
|
78
|
+
* same instance is shared by all concurrent conversions, so it must be
|
|
79
|
+
* safe for concurrent `decode()` calls. If not provided, a fresh
|
|
80
|
+
* default LibheifDecoder is created per item.
|
|
81
|
+
*/
|
|
82
|
+
decoder?: IHeicDecoder;
|
|
83
|
+
}
|
|
45
84
|
|
|
46
85
|
interface LibheifDecoderOptions {
|
|
47
86
|
/**
|
|
@@ -59,9 +98,15 @@ declare class LibheifDecoder implements IHeicDecoder {
|
|
|
59
98
|
private options?;
|
|
60
99
|
private module;
|
|
61
100
|
private decoderInstance;
|
|
101
|
+
/**
|
|
102
|
+
* Memoized module-loading promise so concurrent initialize()/decode() calls
|
|
103
|
+
* on the same instance never instantiate (or mutate) the module twice.
|
|
104
|
+
*/
|
|
105
|
+
private initPromise;
|
|
62
106
|
constructor(options?: LibheifDecoderOptions);
|
|
63
107
|
/**
|
|
64
108
|
* Initializes the WebAssembly module and instantiates the HEIC decoder.
|
|
109
|
+
* Safe to call concurrently: the module is loaded at most once per instance.
|
|
65
110
|
*/
|
|
66
111
|
initialize(): Promise<void>;
|
|
67
112
|
/**
|
|
@@ -72,22 +117,105 @@ declare class LibheifDecoder implements IHeicDecoder {
|
|
|
72
117
|
decode(data: Uint8Array, onProgress?: (percent: number) => void): Promise<DecodedImage>;
|
|
73
118
|
/**
|
|
74
119
|
* Cleans up the WebAssembly decoder instance and resources.
|
|
120
|
+
* Idempotent: safe to call multiple times. After free(), the instance must
|
|
121
|
+
* be re-initialized (createHeicDecoderModule runs again on the next call).
|
|
75
122
|
*/
|
|
76
123
|
free(): void;
|
|
77
124
|
}
|
|
78
125
|
|
|
126
|
+
interface WorkerConvertOptions extends ConvertOptions {
|
|
127
|
+
/**
|
|
128
|
+
* URL of the worker script that performs the conversion. Should be a
|
|
129
|
+
* compile-time constant (e.g. `new URL('./worker.js', import.meta.url)`
|
|
130
|
+
* under a bundler); the script runs with the page's privileges.
|
|
131
|
+
*/
|
|
132
|
+
workerUrl: string | URL;
|
|
133
|
+
/**
|
|
134
|
+
* Maximum time in milliseconds to wait for the worker result before
|
|
135
|
+
* rejecting with a timeout. Defaults to 60000 (60s). Set to 0 to
|
|
136
|
+
* disable the timeout.
|
|
137
|
+
*/
|
|
138
|
+
timeoutMs?: number;
|
|
139
|
+
/**
|
|
140
|
+
* Worker script type. Use `'module'` when the script uses ES module
|
|
141
|
+
* imports (e.g. `import { convertHeic } from ...`); `'classic'`
|
|
142
|
+
* scripts must be pre-bundled.
|
|
143
|
+
* @default 'classic'
|
|
144
|
+
*/
|
|
145
|
+
workerType?: 'classic' | 'module';
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Converts a HEIC image inside a Web Worker so the main thread stays
|
|
149
|
+
* responsive during the (potentially slow) WASM decode.
|
|
150
|
+
*
|
|
151
|
+
* The worker script is user-provided and must handle the following message
|
|
152
|
+
* protocol:
|
|
153
|
+
*
|
|
154
|
+
* ```js
|
|
155
|
+
* // converter.worker.js
|
|
156
|
+
* import { convertHeic } from '@keeratita/heic-converter';
|
|
157
|
+
*
|
|
158
|
+
* self.onmessage = async (event) => {
|
|
159
|
+
* const { input, options } = event.data;
|
|
160
|
+
* try {
|
|
161
|
+
* const blob = await convertHeic(input, {
|
|
162
|
+
* ...options,
|
|
163
|
+
* onProgress: (percent) => self.postMessage({ type: 'progress', percent }),
|
|
164
|
+
* });
|
|
165
|
+
* self.postMessage({ type: 'result', ok: true, blob });
|
|
166
|
+
* } catch (error) {
|
|
167
|
+
* self.postMessage({ type: 'result', ok: false, error: error?.stack ?? error?.message ?? String(error) });
|
|
168
|
+
* }
|
|
169
|
+
* };
|
|
170
|
+
* ```
|
|
171
|
+
*
|
|
172
|
+
* ```ts
|
|
173
|
+
* const jpegBlob = await convertHeicInWorker(heicBlob, {
|
|
174
|
+
* workerUrl: new URL('./converter.worker.js', import.meta.url),
|
|
175
|
+
* workerType: 'module',
|
|
176
|
+
* to: 'jpeg',
|
|
177
|
+
* });
|
|
178
|
+
* ```
|
|
179
|
+
*
|
|
180
|
+
* Only `progress` and `result` messages are understood; any other message
|
|
181
|
+
* type is ignored. Use `workerType: 'module'` when the script uses ES
|
|
182
|
+
* module imports (as in the example above); `'classic'` scripts must be
|
|
183
|
+
* pre-bundled, since static ES imports are not supported there.
|
|
184
|
+
*
|
|
185
|
+
* @param input HEIC image as a Blob, File, ArrayBuffer, or Uint8Array.
|
|
186
|
+
* @param options Conversion options plus the worker script URL.
|
|
187
|
+
* @returns A Promise resolving to the converted image as a Blob.
|
|
188
|
+
*/
|
|
189
|
+
declare function convertHeicInWorker(input: HeicInput, options: WorkerConvertOptions): Promise<Blob>;
|
|
190
|
+
|
|
79
191
|
/**
|
|
80
|
-
* Releases
|
|
81
|
-
*
|
|
192
|
+
* Releases any cached decoder resources.
|
|
193
|
+
*
|
|
194
|
+
* Decoders are now created and released per conversion, so there is no shared
|
|
195
|
+
* instance to free. This function is kept for API compatibility.
|
|
82
196
|
*/
|
|
83
197
|
declare function freeSharedDecoder(): void;
|
|
84
198
|
/**
|
|
85
|
-
* Converts HEIC image data to a standard web format (JPEG, PNG, or SVG).
|
|
199
|
+
* Converts HEIC image data to a standard web format (JPEG, PNG, WebP, or SVG).
|
|
86
200
|
*
|
|
87
201
|
* @param input HEIC image as a Blob, File, ArrayBuffer, or Uint8Array.
|
|
88
202
|
* @param options Conversion configuration options.
|
|
89
203
|
* @returns A Promise resolving to the converted image as a Blob.
|
|
90
204
|
*/
|
|
91
|
-
declare function convertHeic(input:
|
|
205
|
+
declare function convertHeic(input: HeicInput, options?: ConvertOptions): Promise<Blob>;
|
|
206
|
+
/**
|
|
207
|
+
* Converts multiple HEIC images to a standard web format.
|
|
208
|
+
*
|
|
209
|
+
* Conversions run with a bounded concurrency (default 4) and results are
|
|
210
|
+
* returned in the same order as the inputs. If any conversion fails, the
|
|
211
|
+
* returned promise rejects as soon as the failure is known (in-flight
|
|
212
|
+
* conversions are allowed to finish in the background) with an error that
|
|
213
|
+
* identifies the failing item index.
|
|
214
|
+
*
|
|
215
|
+
* @param inputs HEIC images as Blobs, Files, ArrayBuffers, or Uint8Arrays.
|
|
216
|
+
* @param options Batch conversion options.
|
|
217
|
+
* @returns A Promise resolving to the converted images as Blobs, in input order.
|
|
218
|
+
*/
|
|
219
|
+
declare function convertMany(inputs: HeicInput[], options?: ConvertManyOptions): Promise<Blob[]>;
|
|
92
220
|
|
|
93
|
-
export { type ConvertOptions, type DecodedImage, type IHeicDecoder, type ImageFormat, LibheifDecoder, type LibheifDecoderOptions, convertHeic, freeSharedDecoder };
|
|
221
|
+
export { type ConvertManyOptions, type ConvertOptions, type DecodedImage, type HeicInput, type IHeicDecoder, type ImageFormat, LibheifDecoder, type LibheifDecoderOptions, type ResizeOptions, type WorkerConvertOptions, convertHeic, convertHeicInWorker, convertMany, freeSharedDecoder };
|