@image-marker/web 0.1.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/CHANGELOG.md +11 -0
- package/README.md +100 -0
- package/api-contract.json +224 -0
- package/lib/commonjs/core/batch.d.ts +32 -0
- package/lib/commonjs/core/batch.js +85 -0
- package/lib/commonjs/core/content-credentials.d.ts +53 -0
- package/lib/commonjs/core/content-credentials.js +96 -0
- package/lib/commonjs/core/image-info.d.ts +30 -0
- package/lib/commonjs/core/image-info.js +348 -0
- package/lib/commonjs/core/index.d.ts +1014 -0
- package/lib/commonjs/core/index.js +92 -0
- package/lib/commonjs/core/invisible-watermark.d.ts +102 -0
- package/lib/commonjs/core/invisible-watermark.js +913 -0
- package/lib/commonjs/core/job.d.ts +57 -0
- package/lib/commonjs/core/job.js +151 -0
- package/lib/commonjs/core/layout.d.ts +19 -0
- package/lib/commonjs/core/layout.js +75 -0
- package/lib/commonjs/core/marker.d.ts +1 -0
- package/lib/commonjs/core/marker.js +11 -0
- package/lib/commonjs/core/recipe.d.ts +44 -0
- package/lib/commonjs/core/recipe.js +175 -0
- package/lib/commonjs/core/result.d.ts +34 -0
- package/lib/commonjs/core/result.js +41 -0
- package/lib/commonjs/core/trace-runtime.d.ts +43 -0
- package/lib/commonjs/core/trace-runtime.js +92 -0
- package/lib/commonjs/core/validate.d.ts +4 -0
- package/lib/commonjs/core/validate.js +192 -0
- package/lib/commonjs/core/web/browser.d.ts +105 -0
- package/lib/commonjs/core/web/browser.js +434 -0
- package/lib/commonjs/core/web/helpers.d.ts +41 -0
- package/lib/commonjs/core/web/helpers.js +264 -0
- package/lib/commonjs/core/web/index.d.ts +59 -0
- package/lib/commonjs/core/web/index.js +724 -0
- package/lib/commonjs/core/web/invisible-worker-client.d.ts +13 -0
- package/lib/commonjs/core/web/invisible-worker-client.js +140 -0
- package/lib/commonjs/core/web/renderer.d.ts +20 -0
- package/lib/commonjs/core/web/renderer.js +665 -0
- package/lib/commonjs/core/web/runtime.d.ts +106 -0
- package/lib/commonjs/core/web/runtime.js +2 -0
- package/lib/commonjs/editor/adapter.d.ts +26 -0
- package/lib/commonjs/editor/adapter.js +102 -0
- package/lib/commonjs/editor/controller.d.ts +106 -0
- package/lib/commonjs/editor/controller.js +919 -0
- package/lib/commonjs/editor/core-contract.d.ts +2 -0
- package/lib/commonjs/editor/core-contract.js +23 -0
- package/lib/commonjs/editor/geometry.d.ts +8 -0
- package/lib/commonjs/editor/geometry.js +109 -0
- package/lib/commonjs/editor/headless.d.ts +5 -0
- package/lib/commonjs/editor/headless.js +22 -0
- package/lib/commonjs/editor/presets.d.ts +19 -0
- package/lib/commonjs/editor/presets.js +64 -0
- package/lib/commonjs/editor/projection.d.ts +12 -0
- package/lib/commonjs/editor/projection.js +195 -0
- package/lib/commonjs/editor/types.d.ts +166 -0
- package/lib/commonjs/editor/types.js +2 -0
- package/lib/commonjs/editor-adapter.d.ts +7 -0
- package/lib/commonjs/editor-adapter.js +17 -0
- package/lib/commonjs/headless.d.ts +2 -0
- package/lib/commonjs/headless.js +26 -0
- package/lib/commonjs/index.d.ts +5 -0
- package/lib/commonjs/index.js +27 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/module/core/batch.d.ts +32 -0
- package/lib/module/core/batch.js +82 -0
- package/lib/module/core/content-credentials.d.ts +53 -0
- package/lib/module/core/content-credentials.js +92 -0
- package/lib/module/core/image-info.d.ts +30 -0
- package/lib/module/core/image-info.js +343 -0
- package/lib/module/core/index.d.ts +1014 -0
- package/lib/module/core/index.js +56 -0
- package/lib/module/core/invisible-watermark.d.ts +102 -0
- package/lib/module/core/invisible-watermark.js +897 -0
- package/lib/module/core/job.d.ts +57 -0
- package/lib/module/core/job.js +143 -0
- package/lib/module/core/layout.d.ts +19 -0
- package/lib/module/core/layout.js +70 -0
- package/lib/module/core/marker.d.ts +1 -0
- package/lib/module/core/marker.js +4 -0
- package/lib/module/core/recipe.d.ts +44 -0
- package/lib/module/core/recipe.js +169 -0
- package/lib/module/core/result.d.ts +34 -0
- package/lib/module/core/result.js +38 -0
- package/lib/module/core/trace-runtime.d.ts +43 -0
- package/lib/module/core/trace-runtime.js +89 -0
- package/lib/module/core/validate.d.ts +4 -0
- package/lib/module/core/validate.js +187 -0
- package/lib/module/core/web/browser.d.ts +105 -0
- package/lib/module/core/web/browser.js +427 -0
- package/lib/module/core/web/helpers.d.ts +41 -0
- package/lib/module/core/web/helpers.js +251 -0
- package/lib/module/core/web/index.d.ts +59 -0
- package/lib/module/core/web/index.js +709 -0
- package/lib/module/core/web/invisible-worker-client.d.ts +13 -0
- package/lib/module/core/web/invisible-worker-client.js +137 -0
- package/lib/module/core/web/renderer.d.ts +20 -0
- package/lib/module/core/web/renderer.js +661 -0
- package/lib/module/core/web/runtime.d.ts +106 -0
- package/lib/module/core/web/runtime.js +1 -0
- package/lib/module/editor/adapter.d.ts +26 -0
- package/lib/module/editor/adapter.js +99 -0
- package/lib/module/editor/controller.d.ts +106 -0
- package/lib/module/editor/controller.js +915 -0
- package/lib/module/editor/core-contract.d.ts +2 -0
- package/lib/module/editor/core-contract.js +2 -0
- package/lib/module/editor/geometry.d.ts +8 -0
- package/lib/module/editor/geometry.js +103 -0
- package/lib/module/editor/headless.d.ts +5 -0
- package/lib/module/editor/headless.js +4 -0
- package/lib/module/editor/presets.d.ts +19 -0
- package/lib/module/editor/presets.js +57 -0
- package/lib/module/editor/projection.d.ts +12 -0
- package/lib/module/editor/projection.js +188 -0
- package/lib/module/editor/types.d.ts +166 -0
- package/lib/module/editor/types.js +1 -0
- package/lib/module/editor-adapter.d.ts +7 -0
- package/lib/module/editor-adapter.js +10 -0
- package/lib/module/headless.d.ts +2 -0
- package/lib/module/headless.js +2 -0
- package/lib/module/index.d.ts +5 -0
- package/lib/module/index.js +3 -0
- package/lib/module/package.json +1 -0
- package/lib/source-manifest.json +34 -0
- package/lib/worker/invisible-watermark.js +1 -0
- package/package.json +85 -0
|
@@ -0,0 +1,724 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveSpreadValue = exports.resolveAnchoredPosition = exports.normalizeQuality = exports.normalizeOutputFormat = exports.getRotatedBounds = exports.getExpandedCanvasSize = exports.fitSizeWithinMax = exports.encodeCanvasToBlob = exports.encodeCanvas = exports.degreesToRadians = exports.WebMarker = exports.Marker = void 0;
|
|
4
|
+
exports.createWebMarker = createWebMarker;
|
|
5
|
+
const recipe_js_1 = require("../recipe.js");
|
|
6
|
+
const renderer_js_1 = require("./renderer.js");
|
|
7
|
+
const helpers_js_1 = require("./helpers.js");
|
|
8
|
+
const helpers_js_2 = require("./helpers.js");
|
|
9
|
+
const validate_js_1 = require("../validate.js");
|
|
10
|
+
const browser_js_1 = require("./browser.js");
|
|
11
|
+
const job_js_1 = require("../job.js");
|
|
12
|
+
const result_js_1 = require("../result.js");
|
|
13
|
+
const invisible_worker_client_js_1 = require("./invisible-worker-client.js");
|
|
14
|
+
const batch_js_1 = require("../batch.js");
|
|
15
|
+
const content_credentials_js_1 = require("../content-credentials.js");
|
|
16
|
+
const invisible_watermark_js_1 = require("../invisible-watermark.js");
|
|
17
|
+
function getPixelContext(canvas) {
|
|
18
|
+
const context = canvas.getContext('2d');
|
|
19
|
+
if (!context) {
|
|
20
|
+
throw new Error('The browser did not provide a 2D canvas context.');
|
|
21
|
+
}
|
|
22
|
+
return context;
|
|
23
|
+
}
|
|
24
|
+
function readImageData(canvas) {
|
|
25
|
+
try {
|
|
26
|
+
return getPixelContext(canvas).getImageData(0, 0, canvas.width, canvas.height);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
const errorName = error && typeof error === 'object' && 'name' in error
|
|
30
|
+
? String(error.name)
|
|
31
|
+
: '';
|
|
32
|
+
if (errorName === 'SecurityError') {
|
|
33
|
+
throw new Error('Unable to read image pixels because a remote image tainted the canvas. Configure CORS (Access-Control-Allow-Origin) or use a local file/data URL.');
|
|
34
|
+
}
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function createTextLayer(options) {
|
|
39
|
+
return { type: 'text', options };
|
|
40
|
+
}
|
|
41
|
+
function createImageLayer(options) {
|
|
42
|
+
return { type: 'image', options };
|
|
43
|
+
}
|
|
44
|
+
function createOrderedLayer(layer) {
|
|
45
|
+
if (layer.type === 'text') {
|
|
46
|
+
return createTextLayer(layer);
|
|
47
|
+
}
|
|
48
|
+
if (layer.type === 'image') {
|
|
49
|
+
return createImageLayer(layer);
|
|
50
|
+
}
|
|
51
|
+
throw new Error('watermark type must be either "text" or "image".');
|
|
52
|
+
}
|
|
53
|
+
function appendCompatibilityLayers(layers, options) {
|
|
54
|
+
if ('watermarkTexts' in options) {
|
|
55
|
+
options.watermarkTexts?.forEach((text) => layers.push(createTextLayer(text)));
|
|
56
|
+
}
|
|
57
|
+
options.watermarkImages?.forEach((image) => layers.push(createImageLayer(image)));
|
|
58
|
+
}
|
|
59
|
+
function createMarkLayers(options) {
|
|
60
|
+
if (!options?.backgroundImage?.src) {
|
|
61
|
+
throw new Error('please set image!');
|
|
62
|
+
}
|
|
63
|
+
const layers = (options.watermarks?.length ?? 0) > 0
|
|
64
|
+
? options.watermarks.map(createOrderedLayer)
|
|
65
|
+
: [];
|
|
66
|
+
if (layers.length === 0) {
|
|
67
|
+
appendCompatibilityLayers(layers, options);
|
|
68
|
+
}
|
|
69
|
+
if (layers.length === 0) {
|
|
70
|
+
throw new Error('please set watermark text or image!');
|
|
71
|
+
}
|
|
72
|
+
if (layers.some((layer) => layer.type === 'image' && !layer.options.src)) {
|
|
73
|
+
throw new Error('please set mark image!');
|
|
74
|
+
}
|
|
75
|
+
(0, validate_js_1.validateMarkOptions)(options);
|
|
76
|
+
return layers;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Public image-marking API shared by native and Web targets.
|
|
80
|
+
*
|
|
81
|
+
* Metro selects the native TurboModule implementation on iOS and Android.
|
|
82
|
+
* React Native Web and standard browser bundlers select this SSR-safe Canvas
|
|
83
|
+
* 2D implementation, which only touches DOM globals when a method is called.
|
|
84
|
+
*/
|
|
85
|
+
class Marker {
|
|
86
|
+
/** Read display dimensions, encoded dimensions, format, and orientation. */
|
|
87
|
+
static getImageInfo(source) {
|
|
88
|
+
return (0, browser_js_1.getWebImageInfo)(source);
|
|
89
|
+
}
|
|
90
|
+
/** Embed a locator first, then ask the supplied adapter to sign the result. */
|
|
91
|
+
static embedInvisibleWithCredentials(options, control) {
|
|
92
|
+
return (0, content_credentials_js_1.embedInvisibleWithCredentials)((watermark) => Marker.embedInvisible(watermark, control), options);
|
|
93
|
+
}
|
|
94
|
+
/** Verify Content Credentials through an application-supplied adapter. */
|
|
95
|
+
static verifyContentCredentials(options) {
|
|
96
|
+
return (0, content_credentials_js_1.verifyContentCredentials)(options);
|
|
97
|
+
}
|
|
98
|
+
/** Embed authenticated locators into many images while preserving input order. */
|
|
99
|
+
static embedInvisibleMany(inputs, options) {
|
|
100
|
+
const snapshots = Array.isArray(inputs)
|
|
101
|
+
? inputs.map((input) => ({ ...input, image: { ...input?.image } }))
|
|
102
|
+
: inputs;
|
|
103
|
+
return (0, batch_js_1.runWatermarkBatch)(snapshots, (input) => Marker.embedInvisible(input), options, 4, 'embedInvisibleMany');
|
|
104
|
+
}
|
|
105
|
+
/** Detect authenticated locators in many images while preserving input order. */
|
|
106
|
+
static detectInvisibleMany(inputs, options) {
|
|
107
|
+
const snapshots = Array.isArray(inputs)
|
|
108
|
+
? inputs.map((input) => ({ ...input, image: { ...input?.image } }))
|
|
109
|
+
: inputs;
|
|
110
|
+
return (0, batch_js_1.runWatermarkBatch)(snapshots, (input) => Marker.detectInvisible(input), options, 4, 'detectInvisibleMany');
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Embed a short, authenticated locator into the final image pixels.
|
|
114
|
+
*
|
|
115
|
+
* This Beta API supports distribution tracing. It is not DRM, encryption,
|
|
116
|
+
* or proof that the image was never edited.
|
|
117
|
+
*/
|
|
118
|
+
static async embedInvisible(options, control) {
|
|
119
|
+
return (0, job_js_1.runMarkerJob)({
|
|
120
|
+
descriptor: {
|
|
121
|
+
operation: 'embedInvisible',
|
|
122
|
+
saveFormat: options?.saveFormat,
|
|
123
|
+
filename: options?.filename,
|
|
124
|
+
},
|
|
125
|
+
control,
|
|
126
|
+
task: async () => {
|
|
127
|
+
(0, invisible_watermark_js_1.validateEmbedInvisibleOptions)(options);
|
|
128
|
+
const canvas = await (0, renderer_js_1.renderWebCompositionToCanvas)(options.image, [], {
|
|
129
|
+
quality: options.quality,
|
|
130
|
+
saveFormat: options.saveFormat,
|
|
131
|
+
maxSize: options.maxSize,
|
|
132
|
+
});
|
|
133
|
+
const context = getPixelContext(canvas);
|
|
134
|
+
const imageData = readImageData(canvas);
|
|
135
|
+
(0, invisible_watermark_js_1.embedInvisibleWatermarkPixels)({ data: imageData.data, width: canvas.width, height: canvas.height }, options);
|
|
136
|
+
context.putImageData(imageData, 0, 0);
|
|
137
|
+
return (0, helpers_js_2.encodeCanvas)(canvas, options.saveFormat, options.quality);
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
/** Detect and authenticate an invisible locator in an image without writing a file. */
|
|
142
|
+
static async detectInvisible(options, control) {
|
|
143
|
+
(0, invisible_watermark_js_1.validateDetectInvisibleOptions)(options);
|
|
144
|
+
const workerController = new AbortController();
|
|
145
|
+
const abortWorker = () => workerController.abort();
|
|
146
|
+
options.worker?.signal?.addEventListener('abort', abortWorker, {
|
|
147
|
+
once: true,
|
|
148
|
+
});
|
|
149
|
+
try {
|
|
150
|
+
const job = await (0, job_js_1.runControlledMarkerJob)({
|
|
151
|
+
operation: 'detectInvisible',
|
|
152
|
+
control,
|
|
153
|
+
workPhase: 'detecting',
|
|
154
|
+
cancel: abortWorker,
|
|
155
|
+
task: async () => {
|
|
156
|
+
const canvas = await (0, renderer_js_1.renderWebCompositionToCanvas)(options.image, [], {
|
|
157
|
+
saveFormat: undefined,
|
|
158
|
+
maxSize: options.maxSize,
|
|
159
|
+
});
|
|
160
|
+
const imageData = readImageData(canvas);
|
|
161
|
+
if (options.worker) {
|
|
162
|
+
return (0, invisible_worker_client_js_1.detectInvisibleWatermarkInWorker)({
|
|
163
|
+
data: imageData.data,
|
|
164
|
+
width: canvas.width,
|
|
165
|
+
height: canvas.height,
|
|
166
|
+
}, options, { ...options.worker, signal: workerController.signal });
|
|
167
|
+
}
|
|
168
|
+
return (0, invisible_watermark_js_1.detectInvisibleWatermarkPixelsAsync)({
|
|
169
|
+
data: imageData.data,
|
|
170
|
+
width: canvas.width,
|
|
171
|
+
height: canvas.height,
|
|
172
|
+
}, options);
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
return {
|
|
176
|
+
...job.value,
|
|
177
|
+
jobId: job.jobId,
|
|
178
|
+
operation: 'detectInvisible',
|
|
179
|
+
durationMs: job.durationMs,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
finally {
|
|
183
|
+
options.worker?.signal?.removeEventListener('abort', abortWorker);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/** Save ordered layers and output settings for reuse across one or many images. */
|
|
187
|
+
static createRecipe(options, resultOptions) {
|
|
188
|
+
if (resultOptions?.resultType === 'blob') {
|
|
189
|
+
return (0, recipe_js_1.createWatermarkRecipe)(options, async (markOptions) => {
|
|
190
|
+
const canvas = await (0, renderer_js_1.renderWebCompositionToCanvas)(markOptions.backgroundImage, createMarkLayers(markOptions), markOptions);
|
|
191
|
+
return (0, helpers_js_1.encodeCanvasToBlob)(canvas, markOptions.saveFormat, markOptions.quality);
|
|
192
|
+
}, 4);
|
|
193
|
+
}
|
|
194
|
+
if (resultOptions?.resultType !== undefined &&
|
|
195
|
+
resultOptions.resultType !== 'result') {
|
|
196
|
+
throw new Error(`Unsupported recipe result type: ${resultOptions.resultType}.`);
|
|
197
|
+
}
|
|
198
|
+
return (0, recipe_js_1.createWatermarkRecipe)(options, (markOptions, control) => Marker.mark(markOptions, control), 4);
|
|
199
|
+
}
|
|
200
|
+
/** Import a persisted Recipe v1/v2 document, migrating v1 to v2 first. */
|
|
201
|
+
static importRecipe(document, resultOptions) {
|
|
202
|
+
if (resultOptions?.resultType === 'blob') {
|
|
203
|
+
return (0, recipe_js_1.importWatermarkRecipe)(document, async (markOptions) => {
|
|
204
|
+
const canvas = await (0, renderer_js_1.renderWebCompositionToCanvas)(markOptions.backgroundImage, createMarkLayers(markOptions), markOptions);
|
|
205
|
+
return (0, helpers_js_1.encodeCanvasToBlob)(canvas, markOptions.saveFormat, markOptions.quality);
|
|
206
|
+
}, 4);
|
|
207
|
+
}
|
|
208
|
+
if (resultOptions?.resultType !== undefined &&
|
|
209
|
+
resultOptions.resultType !== 'result') {
|
|
210
|
+
throw new Error(`Unsupported recipe result type: ${resultOptions.resultType}.`);
|
|
211
|
+
}
|
|
212
|
+
return (0, recipe_js_1.importWatermarkRecipe)(document, (markOptions, control) => Marker.mark(markOptions, control), 4);
|
|
213
|
+
}
|
|
214
|
+
/** Render one or more text watermark layers. */
|
|
215
|
+
static async markText(options, control) {
|
|
216
|
+
return (0, job_js_1.runMarkerJob)({
|
|
217
|
+
descriptor: {
|
|
218
|
+
operation: 'markText',
|
|
219
|
+
saveFormat: options?.saveFormat,
|
|
220
|
+
filename: options?.filename,
|
|
221
|
+
},
|
|
222
|
+
control,
|
|
223
|
+
task: async () => {
|
|
224
|
+
if (!options?.backgroundImage?.src) {
|
|
225
|
+
throw new Error('please set image!');
|
|
226
|
+
}
|
|
227
|
+
if (!options.watermarkTexts || options.watermarkTexts.length === 0) {
|
|
228
|
+
throw new Error('please set watermark text!');
|
|
229
|
+
}
|
|
230
|
+
(0, validate_js_1.validateTextMarkOptions)(options);
|
|
231
|
+
return (0, renderer_js_1.renderWebComposition)(options.backgroundImage, options.watermarkTexts.map(createTextLayer), options);
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
/** Render one or more image watermark layers. */
|
|
236
|
+
static async markImage(options, control) {
|
|
237
|
+
return (0, job_js_1.runMarkerJob)({
|
|
238
|
+
descriptor: {
|
|
239
|
+
operation: 'markImage',
|
|
240
|
+
saveFormat: options?.saveFormat,
|
|
241
|
+
filename: options?.filename,
|
|
242
|
+
},
|
|
243
|
+
control,
|
|
244
|
+
task: async () => {
|
|
245
|
+
if (!options?.backgroundImage?.src) {
|
|
246
|
+
throw new Error('please set image!');
|
|
247
|
+
}
|
|
248
|
+
(0, validate_js_1.validateImageMarkOptions)(options);
|
|
249
|
+
const watermarkImages = options.watermarkImages ?? [];
|
|
250
|
+
if (watermarkImages.length === 0) {
|
|
251
|
+
throw new Error('please set mark image!');
|
|
252
|
+
}
|
|
253
|
+
if (watermarkImages.some((watermark) => !watermark.src)) {
|
|
254
|
+
throw new Error('please set mark image!');
|
|
255
|
+
}
|
|
256
|
+
const layers = [];
|
|
257
|
+
appendCompatibilityLayers(layers, options);
|
|
258
|
+
return (0, renderer_js_1.renderWebComposition)(options.backgroundImage, layers, options);
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
/** Render ordered mixed text and image watermark layers. */
|
|
263
|
+
static async mark(options, control) {
|
|
264
|
+
return (0, job_js_1.runMarkerJob)({
|
|
265
|
+
descriptor: {
|
|
266
|
+
operation: 'mark',
|
|
267
|
+
saveFormat: options?.saveFormat,
|
|
268
|
+
filename: options?.filename,
|
|
269
|
+
},
|
|
270
|
+
control,
|
|
271
|
+
task: async () => (0, renderer_js_1.renderWebComposition)(options.backgroundImage, createMarkLayers(options), options),
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
exports.Marker = Marker;
|
|
276
|
+
exports.WebMarker = Marker;
|
|
277
|
+
function runtimeAbortError(state) {
|
|
278
|
+
const stop = state.stop;
|
|
279
|
+
return new job_js_1.ImageMarkerError(stop?.code ?? 'ABORTED', stop?.message ?? 'Image marker operation was aborted.', {
|
|
280
|
+
jobId: state.taskId,
|
|
281
|
+
operation: state.operation === 'getImageInfo' ? 'mark' : state.operation,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
function reportRuntimeProgress(control, taskId, operation, phase, progress) {
|
|
285
|
+
try {
|
|
286
|
+
control?.onProgress?.({
|
|
287
|
+
jobId: taskId,
|
|
288
|
+
operation,
|
|
289
|
+
phase,
|
|
290
|
+
completed: phase === 'completed' ? 1 : 0,
|
|
291
|
+
total: 1,
|
|
292
|
+
progress,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
catch {
|
|
296
|
+
// Progress observers cannot interrupt a runtime task.
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
function validateRuntimeControl(control) {
|
|
300
|
+
const timeout = control?.timeoutMs;
|
|
301
|
+
if (timeout !== undefined &&
|
|
302
|
+
(!Number.isFinite(timeout) || !Number.isInteger(timeout) || timeout <= 0)) {
|
|
303
|
+
throw new job_js_1.ImageMarkerError('INVALID_OPTIONS', 'timeoutMs must be a positive finite integer.');
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
function throwIfRuntimeAborted(signal) {
|
|
307
|
+
if (!signal.aborted)
|
|
308
|
+
return;
|
|
309
|
+
const error = new Error('Web Marker operation was aborted.');
|
|
310
|
+
error.name = 'AbortError';
|
|
311
|
+
throw error;
|
|
312
|
+
}
|
|
313
|
+
function createWebMarkerCapabilities(hasHostAdapter) {
|
|
314
|
+
return Object.freeze({
|
|
315
|
+
visibleRendering: {
|
|
316
|
+
engine: 'canvas-2d',
|
|
317
|
+
cancellation: 'host-termination-required',
|
|
318
|
+
},
|
|
319
|
+
invisibleEmbedding: {
|
|
320
|
+
engine: 'canvas-2d-pixels',
|
|
321
|
+
cancellation: 'host-termination-required',
|
|
322
|
+
},
|
|
323
|
+
invisibleDetection: {
|
|
324
|
+
engine: 'dedicated-worker-when-configured-or-main-thread',
|
|
325
|
+
workerCancellation: 'cooperative',
|
|
326
|
+
mainThreadCancellation: 'host-termination-required',
|
|
327
|
+
},
|
|
328
|
+
execution: {
|
|
329
|
+
mode: hasHostAdapter
|
|
330
|
+
? 'host-adapter'
|
|
331
|
+
: 'default-dom',
|
|
332
|
+
supportsTerminationAcknowledgement: hasHostAdapter,
|
|
333
|
+
},
|
|
334
|
+
resources: {
|
|
335
|
+
localBlobInput: true,
|
|
336
|
+
blobOutput: true,
|
|
337
|
+
objectUrlCleanup: true,
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Create an isolated Web Marker instance. The legacy static `Marker` API
|
|
343
|
+
* remains the default DOM path; use this factory when task cancellation and
|
|
344
|
+
* resource ownership need to be scoped to a desktop application instance.
|
|
345
|
+
*/
|
|
346
|
+
function createWebMarker(options = {}) {
|
|
347
|
+
const activeTasks = new Set();
|
|
348
|
+
const resources = options.resources;
|
|
349
|
+
const execution = options.execution;
|
|
350
|
+
const capabilities = createWebMarkerCapabilities(Boolean(execution));
|
|
351
|
+
let isDisposed = false;
|
|
352
|
+
const disposeHostTask = async (state) => {
|
|
353
|
+
if (!state.hostTask || state.hostDisposed)
|
|
354
|
+
return;
|
|
355
|
+
state.hostDisposed = true;
|
|
356
|
+
await state.hostTask.dispose?.();
|
|
357
|
+
};
|
|
358
|
+
const requestStop = (state, stop) => {
|
|
359
|
+
if (!state.stop) {
|
|
360
|
+
state.stop = stop;
|
|
361
|
+
state.controller.abort();
|
|
362
|
+
}
|
|
363
|
+
if (!state.hostTask || state.termination) {
|
|
364
|
+
return state.termination ?? Promise.resolve();
|
|
365
|
+
}
|
|
366
|
+
const terminate = state.hostTask.terminate ?? state.hostTask.cancel;
|
|
367
|
+
state.termination = terminate
|
|
368
|
+
? new Promise((resolve, reject) => {
|
|
369
|
+
try {
|
|
370
|
+
resolve(terminate({ reason: state.stop.reason, taskId: state.taskId }));
|
|
371
|
+
}
|
|
372
|
+
catch (error) {
|
|
373
|
+
reject(error);
|
|
374
|
+
}
|
|
375
|
+
})
|
|
376
|
+
: Promise.resolve();
|
|
377
|
+
return state.termination;
|
|
378
|
+
};
|
|
379
|
+
const runRuntimeTask = async (operation, resultKind, input, control, workPhase, local) => {
|
|
380
|
+
validateRuntimeControl(control);
|
|
381
|
+
const taskId = (0, job_js_1.createMarkerJobId)(operation === 'getImageInfo' ? 'mark' : operation);
|
|
382
|
+
const state = {
|
|
383
|
+
taskId,
|
|
384
|
+
operation,
|
|
385
|
+
controller: new AbortController(),
|
|
386
|
+
hostDisposed: false,
|
|
387
|
+
completion: Promise.resolve(),
|
|
388
|
+
settleCompletion: () => undefined,
|
|
389
|
+
};
|
|
390
|
+
state.completion = new Promise((resolve) => {
|
|
391
|
+
state.settleCompletion = resolve;
|
|
392
|
+
});
|
|
393
|
+
if (isDisposed) {
|
|
394
|
+
throw new job_js_1.ImageMarkerError('ABORTED', 'Web Marker instance is disposed.', {
|
|
395
|
+
jobId: taskId,
|
|
396
|
+
operation: operation === 'getImageInfo' ? 'mark' : operation,
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
if (control?.signal?.aborted) {
|
|
400
|
+
throw new job_js_1.ImageMarkerError('ABORTED', 'Image marker operation was aborted before it started.', {
|
|
401
|
+
jobId: taskId,
|
|
402
|
+
operation: operation === 'getImageInfo' ? 'mark' : operation,
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
const stopFromSignal = () => {
|
|
406
|
+
requestStop(state, {
|
|
407
|
+
code: 'ABORTED',
|
|
408
|
+
message: 'Image marker operation was aborted.',
|
|
409
|
+
reason: 'signal',
|
|
410
|
+
}).catch(() => {
|
|
411
|
+
// The active task reports a host termination failure when it settles.
|
|
412
|
+
});
|
|
413
|
+
};
|
|
414
|
+
control?.signal?.addEventListener('abort', stopFromSignal, { once: true });
|
|
415
|
+
let timeout;
|
|
416
|
+
if (control?.timeoutMs !== undefined) {
|
|
417
|
+
timeout = setTimeout(() => {
|
|
418
|
+
requestStop(state, {
|
|
419
|
+
code: 'TIMEOUT',
|
|
420
|
+
message: `Image marker operation timed out after ${control.timeoutMs}ms.`,
|
|
421
|
+
reason: 'timeout',
|
|
422
|
+
}).catch(() => {
|
|
423
|
+
// The active task reports a host termination failure when it settles.
|
|
424
|
+
});
|
|
425
|
+
}, control.timeoutMs);
|
|
426
|
+
}
|
|
427
|
+
activeTasks.add(state);
|
|
428
|
+
const startedAt = Date.now();
|
|
429
|
+
const markerOperation = operation === 'getImageInfo' ? 'mark' : operation;
|
|
430
|
+
reportRuntimeProgress(control, taskId, markerOperation, 'queued', 0);
|
|
431
|
+
reportRuntimeProgress(control, taskId, markerOperation, 'validating', 0.1);
|
|
432
|
+
reportRuntimeProgress(control, taskId, markerOperation, workPhase, 0.25);
|
|
433
|
+
try {
|
|
434
|
+
let value;
|
|
435
|
+
if (execution) {
|
|
436
|
+
const task = execution.start({
|
|
437
|
+
taskId,
|
|
438
|
+
operation,
|
|
439
|
+
resultKind,
|
|
440
|
+
options: input,
|
|
441
|
+
signal: state.controller.signal,
|
|
442
|
+
});
|
|
443
|
+
if (!task || !task.result || typeof task.result.then !== 'function') {
|
|
444
|
+
throw new Error('Web Marker execution adapter must return a task with a result Promise.');
|
|
445
|
+
}
|
|
446
|
+
state.hostTask = task;
|
|
447
|
+
if (state.stop) {
|
|
448
|
+
requestStop(state, state.stop).catch(() => {
|
|
449
|
+
// The active task reports a host termination failure when it settles.
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
value = await task.result;
|
|
453
|
+
}
|
|
454
|
+
else {
|
|
455
|
+
value = await local(state.controller.signal);
|
|
456
|
+
}
|
|
457
|
+
if (state.stop) {
|
|
458
|
+
await state.termination;
|
|
459
|
+
throw runtimeAbortError(state);
|
|
460
|
+
}
|
|
461
|
+
reportRuntimeProgress(control, taskId, markerOperation, 'completed', 1);
|
|
462
|
+
return {
|
|
463
|
+
taskId,
|
|
464
|
+
durationMs: Math.max(0, Date.now() - startedAt),
|
|
465
|
+
value,
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
catch (error) {
|
|
469
|
+
if (state.stop) {
|
|
470
|
+
try {
|
|
471
|
+
await state.termination;
|
|
472
|
+
}
|
|
473
|
+
catch (terminationError) {
|
|
474
|
+
throw (0, job_js_1.normalizeMarkerError)(terminationError, {
|
|
475
|
+
jobId: taskId,
|
|
476
|
+
operation: markerOperation,
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
throw runtimeAbortError(state);
|
|
480
|
+
}
|
|
481
|
+
throw (0, job_js_1.normalizeMarkerError)(error, {
|
|
482
|
+
jobId: taskId,
|
|
483
|
+
operation: markerOperation,
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
finally {
|
|
487
|
+
if (timeout !== undefined)
|
|
488
|
+
clearTimeout(timeout);
|
|
489
|
+
control?.signal?.removeEventListener('abort', stopFromSignal);
|
|
490
|
+
try {
|
|
491
|
+
await disposeHostTask(state);
|
|
492
|
+
}
|
|
493
|
+
catch {
|
|
494
|
+
// The task result remains authoritative after its host resource cleanup.
|
|
495
|
+
}
|
|
496
|
+
finally {
|
|
497
|
+
activeTasks.delete(state);
|
|
498
|
+
state.settleCompletion();
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
const renderRuntime = (signal) => ({
|
|
503
|
+
resources,
|
|
504
|
+
signal,
|
|
505
|
+
});
|
|
506
|
+
const stopAndWait = async (state, stop) => {
|
|
507
|
+
let terminationError;
|
|
508
|
+
try {
|
|
509
|
+
await requestStop(state, stop);
|
|
510
|
+
}
|
|
511
|
+
catch (error) {
|
|
512
|
+
terminationError = error;
|
|
513
|
+
}
|
|
514
|
+
await state.completion;
|
|
515
|
+
if (terminationError)
|
|
516
|
+
throw terminationError;
|
|
517
|
+
};
|
|
518
|
+
const runMarkerOperation = async (operation, input, control, local) => {
|
|
519
|
+
if (execution) {
|
|
520
|
+
return (await runRuntimeTask(operation, 'marker-result', input, control, 'rendering', async () => {
|
|
521
|
+
throw new Error('Host execution should have handled this task.');
|
|
522
|
+
})).value;
|
|
523
|
+
}
|
|
524
|
+
const completed = await runRuntimeTask(operation, 'marker-result', input, control, 'rendering', local);
|
|
525
|
+
return (0, result_js_1.createMarkerResult)(completed.taskId, completed.value, completed.durationMs, {
|
|
526
|
+
operation,
|
|
527
|
+
saveFormat: input.saveFormat,
|
|
528
|
+
filename: input.filename,
|
|
529
|
+
});
|
|
530
|
+
};
|
|
531
|
+
const markText = (markOptions, control) => runMarkerOperation('markText', markOptions, control, async (signal) => {
|
|
532
|
+
throwIfRuntimeAborted(signal);
|
|
533
|
+
if (!markOptions?.backgroundImage?.src) {
|
|
534
|
+
throw new Error('please set image!');
|
|
535
|
+
}
|
|
536
|
+
if (!markOptions.watermarkTexts ||
|
|
537
|
+
markOptions.watermarkTexts.length === 0) {
|
|
538
|
+
throw new Error('please set watermark text!');
|
|
539
|
+
}
|
|
540
|
+
(0, validate_js_1.validateTextMarkOptions)(markOptions);
|
|
541
|
+
return (0, renderer_js_1.renderWebComposition)(markOptions.backgroundImage, markOptions.watermarkTexts.map(createTextLayer), markOptions, renderRuntime(signal));
|
|
542
|
+
});
|
|
543
|
+
const markImage = (markOptions, control) => runMarkerOperation('markImage', markOptions, control, async (signal) => {
|
|
544
|
+
throwIfRuntimeAborted(signal);
|
|
545
|
+
if (!markOptions?.backgroundImage?.src) {
|
|
546
|
+
throw new Error('please set image!');
|
|
547
|
+
}
|
|
548
|
+
(0, validate_js_1.validateImageMarkOptions)(markOptions);
|
|
549
|
+
const watermarkImages = markOptions.watermarkImages ?? [];
|
|
550
|
+
if (watermarkImages.length === 0 ||
|
|
551
|
+
watermarkImages.some((watermark) => !watermark.src)) {
|
|
552
|
+
throw new Error('please set mark image!');
|
|
553
|
+
}
|
|
554
|
+
const layers = [];
|
|
555
|
+
appendCompatibilityLayers(layers, markOptions);
|
|
556
|
+
return (0, renderer_js_1.renderWebComposition)(markOptions.backgroundImage, layers, markOptions, renderRuntime(signal));
|
|
557
|
+
});
|
|
558
|
+
const mark = (markOptions, control) => runMarkerOperation('mark', markOptions, control, (signal) => {
|
|
559
|
+
throwIfRuntimeAborted(signal);
|
|
560
|
+
return (0, renderer_js_1.renderWebComposition)(markOptions.backgroundImage, createMarkLayers(markOptions), markOptions, renderRuntime(signal));
|
|
561
|
+
});
|
|
562
|
+
const embedInvisible = (embedOptions, control) => runMarkerOperation('embedInvisible', embedOptions, control, async (signal) => {
|
|
563
|
+
throwIfRuntimeAborted(signal);
|
|
564
|
+
(0, invisible_watermark_js_1.validateEmbedInvisibleOptions)(embedOptions);
|
|
565
|
+
const canvas = await (0, renderer_js_1.renderWebCompositionToCanvas)(embedOptions.image, [], {
|
|
566
|
+
quality: embedOptions.quality,
|
|
567
|
+
saveFormat: embedOptions.saveFormat,
|
|
568
|
+
maxSize: embedOptions.maxSize,
|
|
569
|
+
}, renderRuntime(signal));
|
|
570
|
+
throwIfRuntimeAborted(signal);
|
|
571
|
+
const context = getPixelContext(canvas);
|
|
572
|
+
const imageData = readImageData(canvas);
|
|
573
|
+
(0, invisible_watermark_js_1.embedInvisibleWatermarkPixels)({ data: imageData.data, width: canvas.width, height: canvas.height }, embedOptions);
|
|
574
|
+
context.putImageData(imageData, 0, 0);
|
|
575
|
+
throwIfRuntimeAborted(signal);
|
|
576
|
+
return (0, helpers_js_2.encodeCanvas)(canvas, embedOptions.saveFormat, embedOptions.quality);
|
|
577
|
+
});
|
|
578
|
+
const detectInvisible = async (detectOptions, control) => {
|
|
579
|
+
if (execution) {
|
|
580
|
+
return (await runRuntimeTask('detectInvisible', 'detection', detectOptions, control, 'detecting', async () => {
|
|
581
|
+
throw new Error('Host execution should have handled this task.');
|
|
582
|
+
})).value;
|
|
583
|
+
}
|
|
584
|
+
const completed = await runRuntimeTask('detectInvisible', 'detection', detectOptions, control, 'detecting', async (signal) => {
|
|
585
|
+
throwIfRuntimeAborted(signal);
|
|
586
|
+
(0, invisible_watermark_js_1.validateDetectInvisibleOptions)(detectOptions);
|
|
587
|
+
const workerController = new AbortController();
|
|
588
|
+
const abortWorker = () => workerController.abort();
|
|
589
|
+
signal.addEventListener('abort', abortWorker, { once: true });
|
|
590
|
+
detectOptions.worker?.signal?.addEventListener('abort', abortWorker, {
|
|
591
|
+
once: true,
|
|
592
|
+
});
|
|
593
|
+
try {
|
|
594
|
+
const canvas = await (0, renderer_js_1.renderWebCompositionToCanvas)(detectOptions.image, [], { saveFormat: undefined, maxSize: detectOptions.maxSize }, renderRuntime(signal));
|
|
595
|
+
const imageData = readImageData(canvas);
|
|
596
|
+
if (detectOptions.worker) {
|
|
597
|
+
return (0, invisible_worker_client_js_1.detectInvisibleWatermarkInWorker)({
|
|
598
|
+
data: imageData.data,
|
|
599
|
+
width: canvas.width,
|
|
600
|
+
height: canvas.height,
|
|
601
|
+
}, detectOptions, { ...detectOptions.worker, signal: workerController.signal });
|
|
602
|
+
}
|
|
603
|
+
return (0, invisible_watermark_js_1.detectInvisibleWatermarkPixelsAsync)({
|
|
604
|
+
data: imageData.data,
|
|
605
|
+
width: canvas.width,
|
|
606
|
+
height: canvas.height,
|
|
607
|
+
}, detectOptions);
|
|
608
|
+
}
|
|
609
|
+
finally {
|
|
610
|
+
signal.removeEventListener('abort', abortWorker);
|
|
611
|
+
detectOptions.worker?.signal?.removeEventListener('abort', abortWorker);
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
return {
|
|
615
|
+
...completed.value,
|
|
616
|
+
jobId: completed.taskId,
|
|
617
|
+
operation: 'detectInvisible',
|
|
618
|
+
durationMs: completed.durationMs,
|
|
619
|
+
};
|
|
620
|
+
};
|
|
621
|
+
const renderBlobRecipe = async (markOptions, control) => {
|
|
622
|
+
if (execution) {
|
|
623
|
+
return (await runRuntimeTask('mark', 'blob', markOptions, control, 'rendering', async () => {
|
|
624
|
+
throw new Error('Host execution should have handled this task.');
|
|
625
|
+
})).value;
|
|
626
|
+
}
|
|
627
|
+
return (await runRuntimeTask('mark', 'blob', markOptions, control, 'rendering', async (signal) => {
|
|
628
|
+
const canvas = await (0, renderer_js_1.renderWebCompositionToCanvas)(markOptions.backgroundImage, createMarkLayers(markOptions), markOptions, renderRuntime(signal));
|
|
629
|
+
throwIfRuntimeAborted(signal);
|
|
630
|
+
return (0, helpers_js_1.encodeCanvasToBlob)(canvas, markOptions.saveFormat, markOptions.quality);
|
|
631
|
+
})).value;
|
|
632
|
+
};
|
|
633
|
+
const createRecipe = (recipeOptions, resultOptions) => {
|
|
634
|
+
if (resultOptions?.resultType === 'blob') {
|
|
635
|
+
return (0, recipe_js_1.createWatermarkRecipe)(recipeOptions, renderBlobRecipe, 4);
|
|
636
|
+
}
|
|
637
|
+
if (resultOptions?.resultType !== undefined &&
|
|
638
|
+
resultOptions.resultType !== 'result') {
|
|
639
|
+
throw new Error(`Unsupported recipe result type: ${resultOptions.resultType}.`);
|
|
640
|
+
}
|
|
641
|
+
return (0, recipe_js_1.createWatermarkRecipe)(recipeOptions, (markOptions, control) => mark(markOptions, control), 4);
|
|
642
|
+
};
|
|
643
|
+
const importRecipe = (document, resultOptions) => {
|
|
644
|
+
if (resultOptions?.resultType === 'blob') {
|
|
645
|
+
return (0, recipe_js_1.importWatermarkRecipe)(document, renderBlobRecipe, 4);
|
|
646
|
+
}
|
|
647
|
+
if (resultOptions?.resultType !== undefined &&
|
|
648
|
+
resultOptions.resultType !== 'result') {
|
|
649
|
+
throw new Error(`Unsupported recipe result type: ${resultOptions.resultType}.`);
|
|
650
|
+
}
|
|
651
|
+
return (0, recipe_js_1.importWatermarkRecipe)(document, (markOptions, control) => mark(markOptions, control), 4);
|
|
652
|
+
};
|
|
653
|
+
const instance = {
|
|
654
|
+
capabilities,
|
|
655
|
+
get disposed() {
|
|
656
|
+
return isDisposed;
|
|
657
|
+
},
|
|
658
|
+
async getImageInfo(source) {
|
|
659
|
+
if (execution) {
|
|
660
|
+
return (await runRuntimeTask('getImageInfo', 'image-info', source, undefined, 'rendering', async () => {
|
|
661
|
+
throw new Error('Host execution should have handled this task.');
|
|
662
|
+
})).value;
|
|
663
|
+
}
|
|
664
|
+
return (await runRuntimeTask('getImageInfo', 'image-info', source, undefined, 'rendering', (signal) => {
|
|
665
|
+
throwIfRuntimeAborted(signal);
|
|
666
|
+
return (0, browser_js_1.getWebImageInfo)(source, resources, signal);
|
|
667
|
+
})).value;
|
|
668
|
+
},
|
|
669
|
+
markText,
|
|
670
|
+
markImage,
|
|
671
|
+
mark,
|
|
672
|
+
embedInvisible,
|
|
673
|
+
detectInvisible,
|
|
674
|
+
embedInvisibleMany(inputs, batchOptions) {
|
|
675
|
+
const snapshots = Array.isArray(inputs)
|
|
676
|
+
? inputs.map((input) => ({ ...input, image: { ...input?.image } }))
|
|
677
|
+
: inputs;
|
|
678
|
+
return (0, batch_js_1.runWatermarkBatch)(snapshots, (input) => embedInvisible(input, { signal: batchOptions?.signal }), batchOptions, 4, 'embedInvisibleMany');
|
|
679
|
+
},
|
|
680
|
+
detectInvisibleMany(inputs, batchOptions) {
|
|
681
|
+
const snapshots = Array.isArray(inputs)
|
|
682
|
+
? inputs.map((input) => ({ ...input, image: { ...input?.image } }))
|
|
683
|
+
: inputs;
|
|
684
|
+
return (0, batch_js_1.runWatermarkBatch)(snapshots, (input) => detectInvisible(input, { signal: batchOptions?.signal }), batchOptions, 4, 'detectInvisibleMany');
|
|
685
|
+
},
|
|
686
|
+
createRecipe,
|
|
687
|
+
importRecipe,
|
|
688
|
+
embedInvisibleWithCredentials(credentialOptions, control) {
|
|
689
|
+
return (0, content_credentials_js_1.embedInvisibleWithCredentials)((watermark) => embedInvisible(watermark, control), credentialOptions);
|
|
690
|
+
},
|
|
691
|
+
verifyContentCredentials: content_credentials_js_1.verifyContentCredentials,
|
|
692
|
+
async cancel() {
|
|
693
|
+
await Promise.all(Array.from(activeTasks, (state) => stopAndWait(state, {
|
|
694
|
+
code: 'ABORTED',
|
|
695
|
+
message: 'Image marker operation was cancelled by its Web Marker instance.',
|
|
696
|
+
reason: 'cancel',
|
|
697
|
+
})));
|
|
698
|
+
},
|
|
699
|
+
async dispose() {
|
|
700
|
+
if (isDisposed && activeTasks.size === 0)
|
|
701
|
+
return;
|
|
702
|
+
isDisposed = true;
|
|
703
|
+
const tasks = Array.from(activeTasks);
|
|
704
|
+
await Promise.all(tasks.map((state) => stopAndWait(state, {
|
|
705
|
+
code: 'ABORTED',
|
|
706
|
+
message: 'Image marker operation was cancelled because its Web Marker instance was disposed.',
|
|
707
|
+
reason: 'dispose',
|
|
708
|
+
})));
|
|
709
|
+
},
|
|
710
|
+
};
|
|
711
|
+
return instance;
|
|
712
|
+
}
|
|
713
|
+
var helpers_js_3 = require("./helpers.js");
|
|
714
|
+
Object.defineProperty(exports, "degreesToRadians", { enumerable: true, get: function () { return helpers_js_3.degreesToRadians; } });
|
|
715
|
+
Object.defineProperty(exports, "encodeCanvas", { enumerable: true, get: function () { return helpers_js_3.encodeCanvas; } });
|
|
716
|
+
Object.defineProperty(exports, "encodeCanvasToBlob", { enumerable: true, get: function () { return helpers_js_3.encodeCanvasToBlob; } });
|
|
717
|
+
Object.defineProperty(exports, "fitSizeWithinMax", { enumerable: true, get: function () { return helpers_js_3.fitSizeWithinMax; } });
|
|
718
|
+
Object.defineProperty(exports, "getExpandedCanvasSize", { enumerable: true, get: function () { return helpers_js_3.getExpandedCanvasSize; } });
|
|
719
|
+
Object.defineProperty(exports, "getRotatedBounds", { enumerable: true, get: function () { return helpers_js_3.getRotatedBounds; } });
|
|
720
|
+
Object.defineProperty(exports, "normalizeOutputFormat", { enumerable: true, get: function () { return helpers_js_3.normalizeOutputFormat; } });
|
|
721
|
+
Object.defineProperty(exports, "normalizeQuality", { enumerable: true, get: function () { return helpers_js_3.normalizeQuality; } });
|
|
722
|
+
Object.defineProperty(exports, "resolveAnchoredPosition", { enumerable: true, get: function () { return helpers_js_3.resolveAnchoredPosition; } });
|
|
723
|
+
Object.defineProperty(exports, "resolveSpreadValue", { enumerable: true, get: function () { return helpers_js_3.resolveSpreadValue; } });
|
|
724
|
+
exports.default = Marker;
|