@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
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 (2026-08-31)
|
|
4
|
+
|
|
5
|
+
- Added the standalone `@image-marker/web` package for browser rendering with
|
|
6
|
+
File, Blob, URL, data URL, and loaded image inputs.
|
|
7
|
+
- Added public `headless` and `editor-adapter` entries for custom editor UIs
|
|
8
|
+
without React Native dependencies.
|
|
9
|
+
- Added a packaged invisible-watermark Worker resource and Vite `?url` usage.
|
|
10
|
+
- Documented cooperative cancellation, host-owned termination, URL/CORS policy,
|
|
11
|
+
local file handling, and optional C2PA integration boundaries.
|
package/README.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# `@image-marker/web`
|
|
2
|
+
|
|
3
|
+
Standalone browser SDK for Image Marker. It runs with Canvas 2D and Web APIs and
|
|
4
|
+
does not install React Native, a Node sidecar, or native modules.
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
npm install @image-marker/web@0.1.0
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Quick start
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import Marker, { ImageFormat, Position } from '@image-marker/web';
|
|
14
|
+
|
|
15
|
+
const result = await Marker.markText({
|
|
16
|
+
backgroundImage: { src: file }, // File, Blob, data URL, URL, or loaded image
|
|
17
|
+
watermarkTexts: [
|
|
18
|
+
{
|
|
19
|
+
text: '© Acme',
|
|
20
|
+
position: { position: Position.bottomRight, X: 24, Y: 24 },
|
|
21
|
+
style: { color: '#FFFFFF', fontSize: 28 },
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
saveFormat: ImageFormat.png,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
preview.src = result.uri;
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The root entry exports the Web `Marker`, the existing public enums and types,
|
|
31
|
+
Recipe v2 helpers, visible watermark methods, and invisible watermark
|
|
32
|
+
embed/detect methods. Web results expose a data URL in `result.uri`.
|
|
33
|
+
|
|
34
|
+
## Public subpaths
|
|
35
|
+
|
|
36
|
+
- `@image-marker/web/headless` exports `ImageMarkerEditorController`, geometry,
|
|
37
|
+
projection, presets, and editor types without React or React Native UI.
|
|
38
|
+
- `@image-marker/web/editor-adapter` exports `createWebEditorAdapter` and
|
|
39
|
+
`resolveWebEditorSourceInfo`. The adapter connects a headless Recipe to Canvas
|
|
40
|
+
preview and original export.
|
|
41
|
+
- `@image-marker/web/worker` and
|
|
42
|
+
`@image-marker/web/worker/invisible-watermark.js` resolve to the packaged
|
|
43
|
+
invisible-watermark Worker. Vite can resolve it with
|
|
44
|
+
`import workerUrl from '@image-marker/web/worker?url'`.
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { ImageMarkerEditorController } from '@image-marker/web/headless';
|
|
48
|
+
import { createWebEditorAdapter } from '@image-marker/web/editor-adapter';
|
|
49
|
+
|
|
50
|
+
const controller = new ImageMarkerEditorController({
|
|
51
|
+
document: { schemaVersion: 2, layers, output: { saveFormat: 'png' } },
|
|
52
|
+
});
|
|
53
|
+
const adapter = createWebEditorAdapter(1024);
|
|
54
|
+
const preview = await adapter.renderPreview({
|
|
55
|
+
recipe: controller.exportRecipe(),
|
|
56
|
+
input: { backgroundImage: { src: file } },
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Cancellation and ownership
|
|
61
|
+
|
|
62
|
+
Pass an `AbortSignal` to Recipe jobs or Worker detection. Abort stops queued and
|
|
63
|
+
cooperative work; synchronous Canvas work already in progress may only be
|
|
64
|
+
discarded when it returns. A host that needs immediate CPU termination must stop
|
|
65
|
+
the Worker or task it owns. Promise timeouts and ignoring late values do not stop
|
|
66
|
+
work. Revoke object URLs and dispose controllers/listeners when their UI or task
|
|
67
|
+
ends.
|
|
68
|
+
|
|
69
|
+
For a Tauri or other isolated host, use `createWebMarker(options?)` to inject
|
|
70
|
+
`resources` (`createImage`, `createCanvas`, object-URL cleanup, and byte reads)
|
|
71
|
+
and an `execution` adapter. `execution.start()` returns the real task promise
|
|
72
|
+
plus optional `terminate`/`cancel` and `dispose` hooks. `marker.cancel()` and
|
|
73
|
+
`marker.dispose()` wait for the host acknowledgement; a timeout or ignored
|
|
74
|
+
late value is not termination. Inspect `marker.capabilities` to distinguish
|
|
75
|
+
cooperative Worker detection from default DOM work that requires host
|
|
76
|
+
termination.
|
|
77
|
+
|
|
78
|
+
Create Recipes from that instance when its cancellation boundary should include
|
|
79
|
+
the Recipe task:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import { createWebMarker } from '@image-marker/web';
|
|
83
|
+
|
|
84
|
+
const marker = createWebMarker();
|
|
85
|
+
const recipe = marker.createRecipe(recipeDocument, { resultType: 'blob' });
|
|
86
|
+
const pending = recipe.apply(input, { signal: abortController.signal });
|
|
87
|
+
const completion = pending.catch(() => undefined);
|
|
88
|
+
abortController.abort();
|
|
89
|
+
await marker.cancel();
|
|
90
|
+
await completion;
|
|
91
|
+
await marker.dispose();
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The packaged Worker handles invisible watermark detection only. It is not a
|
|
95
|
+
general Canvas image engine. Remote URLs remain supported when the remote server
|
|
96
|
+
allows CORS; local `File` and `Blob` inputs are recommended for desktop apps.
|
|
97
|
+
C2PA is an optional integration boundary and is not installed by this package.
|
|
98
|
+
|
|
99
|
+
See the [Web SDK guide](../../docs/web-sdk.md) and the runnable
|
|
100
|
+
[Vite example](../../examples/web-sdk/).
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
{
|
|
2
|
+
"packageExports": [
|
|
3
|
+
".",
|
|
4
|
+
"./headless",
|
|
5
|
+
"./editor-adapter",
|
|
6
|
+
"./worker",
|
|
7
|
+
"./worker/invisible-watermark.js",
|
|
8
|
+
"./package.json"
|
|
9
|
+
],
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@image-marker/recipe": "^0.1.0"
|
|
12
|
+
},
|
|
13
|
+
"peerDependencies": {},
|
|
14
|
+
"entries": {
|
|
15
|
+
".": {
|
|
16
|
+
"values": [
|
|
17
|
+
"INVISIBLE_WATERMARK_ALGORITHM",
|
|
18
|
+
"INVISIBLE_WATERMARK_MAX_PAYLOAD_BYTES",
|
|
19
|
+
"INVISIBLE_WATERMARK_MIN_HEIGHT",
|
|
20
|
+
"INVISIBLE_WATERMARK_MIN_KEY_BYTES",
|
|
21
|
+
"INVISIBLE_WATERMARK_MIN_WIDTH",
|
|
22
|
+
"INVISIBLE_WATERMARK_RESIZE_SCALES",
|
|
23
|
+
"ImageFormat",
|
|
24
|
+
"ImageMarkerError",
|
|
25
|
+
"Marker",
|
|
26
|
+
"Position",
|
|
27
|
+
"RotationCanvasMode",
|
|
28
|
+
"TextBackgroundType",
|
|
29
|
+
"WATERMARK_RECIPE_SCHEMA_VERSION",
|
|
30
|
+
"WatermarkRecipeValidationError",
|
|
31
|
+
"WebMarker",
|
|
32
|
+
"addWatermarkRecipeLayer",
|
|
33
|
+
"cloneRecipeValue",
|
|
34
|
+
"cloneWatermarkRecipe",
|
|
35
|
+
"cloneWatermarkRecipeLayer",
|
|
36
|
+
"createWatermarkRecipeDefinition",
|
|
37
|
+
"createWebMarker",
|
|
38
|
+
"default",
|
|
39
|
+
"duplicateWatermarkRecipeLayer",
|
|
40
|
+
"groupWatermarkRecipeLayers",
|
|
41
|
+
"materializeWatermarkRecipe",
|
|
42
|
+
"migrateWatermarkRecipe",
|
|
43
|
+
"parseEncodedImageInfo",
|
|
44
|
+
"parseWatermarkRecipe",
|
|
45
|
+
"recipeValidationPatterns",
|
|
46
|
+
"removeWatermarkRecipeLayer",
|
|
47
|
+
"reorderWatermarkRecipeLayer",
|
|
48
|
+
"resolveWatermarkTemplate",
|
|
49
|
+
"safeValidateWatermarkRecipe",
|
|
50
|
+
"serializeWatermarkRecipe",
|
|
51
|
+
"ungroupWatermarkRecipeLayers",
|
|
52
|
+
"updateWatermarkRecipeLayer",
|
|
53
|
+
"validateWatermarkRecipe",
|
|
54
|
+
"validateWatermarkTemplate"
|
|
55
|
+
],
|
|
56
|
+
"types": [
|
|
57
|
+
"BlendMode",
|
|
58
|
+
"ContentCredentialsAdapter",
|
|
59
|
+
"ContentCredentialsClaim",
|
|
60
|
+
"ContentCredentialsSignRequest",
|
|
61
|
+
"ContentCredentialsSignResult",
|
|
62
|
+
"ContentCredentialsVerificationResult",
|
|
63
|
+
"CornerRadius",
|
|
64
|
+
"DetectInvisibleWatermarkOptions",
|
|
65
|
+
"EmbedInvisibleWatermarkOptions",
|
|
66
|
+
"EmbedInvisibleWithCredentialsOptions",
|
|
67
|
+
"EmbedInvisibleWithCredentialsResult",
|
|
68
|
+
"ImageMarkOptions",
|
|
69
|
+
"ImageMarkerErrorCode",
|
|
70
|
+
"ImageOptions",
|
|
71
|
+
"ImageOrientation",
|
|
72
|
+
"ImageWatermarkLayer",
|
|
73
|
+
"InvisibleWatermarkCodec",
|
|
74
|
+
"InvisibleWatermarkDecodedImage",
|
|
75
|
+
"InvisibleWatermarkDetectionData",
|
|
76
|
+
"InvisibleWatermarkDetectionResult",
|
|
77
|
+
"InvisibleWatermarkImage",
|
|
78
|
+
"InvisibleWatermarkRuntime",
|
|
79
|
+
"InvisibleWatermarkRuntimeOptions",
|
|
80
|
+
"InvisibleWatermarkSearch",
|
|
81
|
+
"InvisibleWatermarkStrength",
|
|
82
|
+
"InvisibleWatermarkWorkerOptions",
|
|
83
|
+
"InvisibleWatermarkWorkerProgressPhase",
|
|
84
|
+
"LegacyWatermarkRecipeDefinition",
|
|
85
|
+
"MarkOptions",
|
|
86
|
+
"MarkerImageFormat",
|
|
87
|
+
"MarkerImageInfo",
|
|
88
|
+
"MarkerJobOptions",
|
|
89
|
+
"MarkerJobPhase",
|
|
90
|
+
"MarkerJobProgress",
|
|
91
|
+
"MarkerMetadataResult",
|
|
92
|
+
"MarkerOperation",
|
|
93
|
+
"MarkerOutputFormat",
|
|
94
|
+
"MarkerResult",
|
|
95
|
+
"MarkerResultDescriptor",
|
|
96
|
+
"PositionOptions",
|
|
97
|
+
"RadiusValue",
|
|
98
|
+
"RuntimeDetectInvisibleWatermarkOptions",
|
|
99
|
+
"RuntimeEmbedInvisibleWatermarkOptions",
|
|
100
|
+
"ShadowLayerStyle",
|
|
101
|
+
"TextBackgroundStyle",
|
|
102
|
+
"TextMarkOptions",
|
|
103
|
+
"TextOptions",
|
|
104
|
+
"TextStrokeStyle",
|
|
105
|
+
"TextStyle",
|
|
106
|
+
"TextWatermarkLayer",
|
|
107
|
+
"VerifyContentCredentialsOptions",
|
|
108
|
+
"WatermarkBatchAbortedResult",
|
|
109
|
+
"WatermarkBatchFulfilledResult",
|
|
110
|
+
"WatermarkBatchOptions",
|
|
111
|
+
"WatermarkBatchProgress",
|
|
112
|
+
"WatermarkBatchRejectedResult",
|
|
113
|
+
"WatermarkBatchResult",
|
|
114
|
+
"WatermarkBlobRecipeResultOptions",
|
|
115
|
+
"WatermarkImageOptions",
|
|
116
|
+
"WatermarkLayer",
|
|
117
|
+
"WatermarkLayout",
|
|
118
|
+
"WatermarkMarkerResultRecipeOptions",
|
|
119
|
+
"WatermarkRecipe",
|
|
120
|
+
"WatermarkRecipeCondition",
|
|
121
|
+
"WatermarkRecipeDefinition",
|
|
122
|
+
"WatermarkRecipeDefinitionLayer",
|
|
123
|
+
"WatermarkRecipeDocument",
|
|
124
|
+
"WatermarkRecipeInput",
|
|
125
|
+
"WatermarkRecipeLayer",
|
|
126
|
+
"WatermarkRecipeOptions",
|
|
127
|
+
"WatermarkRecipeOutputOptions",
|
|
128
|
+
"WatermarkRecipeResultOptions",
|
|
129
|
+
"WatermarkRecipeSchemaVersion",
|
|
130
|
+
"WatermarkRecipeValidationFailure",
|
|
131
|
+
"WatermarkRecipeValidationResult",
|
|
132
|
+
"WatermarkRecipeValidationSuccess",
|
|
133
|
+
"WatermarkRecipeVariable",
|
|
134
|
+
"WatermarkRecipeVariableContext",
|
|
135
|
+
"WatermarkTextDirection",
|
|
136
|
+
"WatermarkTextOverflow",
|
|
137
|
+
"WatermarkTextWrap",
|
|
138
|
+
"WebMarkerCancellationMode",
|
|
139
|
+
"WebMarkerCapabilities",
|
|
140
|
+
"WebMarkerExecutionAdapter",
|
|
141
|
+
"WebMarkerExecutionOperation",
|
|
142
|
+
"WebMarkerExecutionRequest",
|
|
143
|
+
"WebMarkerExecutionResultKind",
|
|
144
|
+
"WebMarkerExecutionTask",
|
|
145
|
+
"WebMarkerExecutionTermination",
|
|
146
|
+
"WebMarkerInstance",
|
|
147
|
+
"WebMarkerRuntimeOptions",
|
|
148
|
+
"WebResourceAdapter"
|
|
149
|
+
]
|
|
150
|
+
},
|
|
151
|
+
"./headless": {
|
|
152
|
+
"values": [
|
|
153
|
+
"ImageMarkerEditorController",
|
|
154
|
+
"addWatermarkRecipeLayer",
|
|
155
|
+
"angle",
|
|
156
|
+
"createBrandLogoLayer",
|
|
157
|
+
"createBrandTextLayer",
|
|
158
|
+
"createEditorBrandKit",
|
|
159
|
+
"createEditorTemplate",
|
|
160
|
+
"createEditorViewportProjection",
|
|
161
|
+
"distance",
|
|
162
|
+
"duplicateWatermarkRecipeLayer",
|
|
163
|
+
"fitEditorSizeWithinMax",
|
|
164
|
+
"groupWatermarkRecipeLayers",
|
|
165
|
+
"materializeEditorTemplate",
|
|
166
|
+
"normalizeSafeArea",
|
|
167
|
+
"projectEditorPoint",
|
|
168
|
+
"projectEditorRecipe",
|
|
169
|
+
"removeWatermarkRecipeLayer",
|
|
170
|
+
"reorderWatermarkRecipeLayer",
|
|
171
|
+
"snapLayerPosition",
|
|
172
|
+
"ungroupWatermarkRecipeLayers",
|
|
173
|
+
"unprojectEditorPoint",
|
|
174
|
+
"updateWatermarkRecipeLayer"
|
|
175
|
+
],
|
|
176
|
+
"types": [
|
|
177
|
+
"EditorAlignment",
|
|
178
|
+
"EditorAsset",
|
|
179
|
+
"EditorAutosaveOptions",
|
|
180
|
+
"EditorBrandKit",
|
|
181
|
+
"EditorClipboardDocument",
|
|
182
|
+
"EditorContentCredentialsExport",
|
|
183
|
+
"EditorDistribution",
|
|
184
|
+
"EditorExportOptions",
|
|
185
|
+
"EditorExportResult",
|
|
186
|
+
"EditorInvisibleExport",
|
|
187
|
+
"EditorKeyCommand",
|
|
188
|
+
"EditorLayerBounds",
|
|
189
|
+
"EditorPoint",
|
|
190
|
+
"EditorRect",
|
|
191
|
+
"EditorRenderRequest",
|
|
192
|
+
"EditorSafeArea",
|
|
193
|
+
"EditorSelectionMode",
|
|
194
|
+
"EditorSerializedState",
|
|
195
|
+
"EditorSize",
|
|
196
|
+
"EditorSnapContext",
|
|
197
|
+
"EditorSnapGuide",
|
|
198
|
+
"EditorState",
|
|
199
|
+
"EditorTemplate",
|
|
200
|
+
"EditorTextLayerPatch",
|
|
201
|
+
"EditorViewportProjection",
|
|
202
|
+
"EditorViewportState",
|
|
203
|
+
"ImageMarkerEditorControllerOptions",
|
|
204
|
+
"ImageMarkerEditorRenderAdapter",
|
|
205
|
+
"MarkerImageInfo",
|
|
206
|
+
"WatermarkBlendMode",
|
|
207
|
+
"WatermarkRecipeDefinition",
|
|
208
|
+
"WatermarkRecipeDefinitionLayer",
|
|
209
|
+
"WatermarkRecipeDocument",
|
|
210
|
+
"WatermarkRecipeInput",
|
|
211
|
+
"WatermarkTextStyle"
|
|
212
|
+
]
|
|
213
|
+
},
|
|
214
|
+
"./editor-adapter": {
|
|
215
|
+
"values": [
|
|
216
|
+
"createWebEditorAdapter",
|
|
217
|
+
"resolveWebEditorSourceInfo"
|
|
218
|
+
],
|
|
219
|
+
"types": [
|
|
220
|
+
"EditorMarkerEngine"
|
|
221
|
+
]
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface WatermarkBatchFulfilledResult<Result> {
|
|
2
|
+
status: 'fulfilled';
|
|
3
|
+
value: Result;
|
|
4
|
+
}
|
|
5
|
+
export interface WatermarkBatchRejectedResult {
|
|
6
|
+
status: 'rejected';
|
|
7
|
+
reason: unknown;
|
|
8
|
+
}
|
|
9
|
+
export interface WatermarkBatchAbortedResult {
|
|
10
|
+
status: 'aborted';
|
|
11
|
+
reason: Error;
|
|
12
|
+
}
|
|
13
|
+
export type WatermarkBatchResult<Result> = WatermarkBatchFulfilledResult<Result> | WatermarkBatchRejectedResult | WatermarkBatchAbortedResult;
|
|
14
|
+
export interface WatermarkBatchProgress<Result> {
|
|
15
|
+
total: number;
|
|
16
|
+
settled: number;
|
|
17
|
+
succeeded: number;
|
|
18
|
+
failed: number;
|
|
19
|
+
aborted: number;
|
|
20
|
+
/** Index of the item that produced this progress update. */
|
|
21
|
+
index: number;
|
|
22
|
+
result: WatermarkBatchResult<Result>;
|
|
23
|
+
}
|
|
24
|
+
export interface WatermarkBatchOptions<Result> {
|
|
25
|
+
/** Requested worker count. Web is capped at 4 and native targets at 1. */
|
|
26
|
+
concurrency?: number;
|
|
27
|
+
/** Stops new items from starting. Already-running items are allowed to finish. */
|
|
28
|
+
signal?: AbortSignal;
|
|
29
|
+
/** Called once when each item is fulfilled, rejected, or skipped after abort. */
|
|
30
|
+
onProgress?: (progress: WatermarkBatchProgress<Result>) => void;
|
|
31
|
+
}
|
|
32
|
+
export declare function runWatermarkBatch<Input, Result>(inputs: readonly Input[], task: (input: Input, index: number) => Promise<Result>, batchOptions?: WatermarkBatchOptions<Result>, maximumConcurrency?: number, inputLabel?: string): Promise<Array<WatermarkBatchResult<Result>>>;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runWatermarkBatch = runWatermarkBatch;
|
|
4
|
+
function abortResult() {
|
|
5
|
+
const reason = new Error('Batch item was not started because the operation was aborted.');
|
|
6
|
+
reason.name = 'AbortError';
|
|
7
|
+
return { status: 'aborted', reason };
|
|
8
|
+
}
|
|
9
|
+
async function runWatermarkBatch(inputs, task, batchOptions = {}, maximumConcurrency = 1, inputLabel = 'batch') {
|
|
10
|
+
if (!Array.isArray(inputs)) {
|
|
11
|
+
throw new Error(`${inputLabel} inputs must be an array.`);
|
|
12
|
+
}
|
|
13
|
+
const requestedConcurrency = batchOptions.concurrency ?? 1;
|
|
14
|
+
if (!Number.isFinite(requestedConcurrency) ||
|
|
15
|
+
!Number.isInteger(requestedConcurrency) ||
|
|
16
|
+
requestedConcurrency <= 0) {
|
|
17
|
+
throw new Error('concurrency must be a positive finite integer.');
|
|
18
|
+
}
|
|
19
|
+
if (inputs.length === 0) {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
const concurrency = Math.min(requestedConcurrency, maximumConcurrency, inputs.length);
|
|
23
|
+
const results = new Array(inputs.length);
|
|
24
|
+
let cursor = 0;
|
|
25
|
+
let settled = 0;
|
|
26
|
+
let succeeded = 0;
|
|
27
|
+
let failed = 0;
|
|
28
|
+
let aborted = 0;
|
|
29
|
+
const report = (index, result) => {
|
|
30
|
+
settled += 1;
|
|
31
|
+
if (result.status === 'fulfilled')
|
|
32
|
+
succeeded += 1;
|
|
33
|
+
else if (result.status === 'rejected')
|
|
34
|
+
failed += 1;
|
|
35
|
+
else
|
|
36
|
+
aborted += 1;
|
|
37
|
+
try {
|
|
38
|
+
batchOptions.onProgress?.({
|
|
39
|
+
total: inputs.length,
|
|
40
|
+
settled,
|
|
41
|
+
succeeded,
|
|
42
|
+
failed,
|
|
43
|
+
aborted,
|
|
44
|
+
index,
|
|
45
|
+
result,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Progress observers must not interrupt the batch or change results.
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const worker = async () => {
|
|
53
|
+
while (!batchOptions.signal?.aborted) {
|
|
54
|
+
const index = cursor;
|
|
55
|
+
cursor += 1;
|
|
56
|
+
if (index >= inputs.length) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (batchOptions.signal?.aborted) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
let result;
|
|
63
|
+
try {
|
|
64
|
+
result = {
|
|
65
|
+
status: 'fulfilled',
|
|
66
|
+
value: await task(inputs[index], index),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
catch (reason) {
|
|
70
|
+
result = { status: 'rejected', reason };
|
|
71
|
+
}
|
|
72
|
+
results[index] = result;
|
|
73
|
+
report(index, result);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
await Promise.all(Array.from({ length: concurrency }, worker));
|
|
77
|
+
for (let index = 0; index < inputs.length; index += 1) {
|
|
78
|
+
if (!results[index]) {
|
|
79
|
+
const result = abortResult();
|
|
80
|
+
results[index] = result;
|
|
81
|
+
report(index, result);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return results;
|
|
85
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { EmbedInvisibleWatermarkOptions } from './invisible-watermark.js';
|
|
2
|
+
import { INVISIBLE_WATERMARK_ALGORITHM } from './invisible-watermark.js';
|
|
3
|
+
import type { MarkerResult } from './result.js';
|
|
4
|
+
export interface ContentCredentialsClaim {
|
|
5
|
+
/** Human-readable asset title included in the signed manifest. */
|
|
6
|
+
title: string;
|
|
7
|
+
/** Encoded image type. WebP is supported when the selected adapter accepts it. */
|
|
8
|
+
format?: 'image/jpeg' | 'image/png' | 'image/webp';
|
|
9
|
+
/** Claim generator name shown by Content Credentials readers. */
|
|
10
|
+
generator?: string;
|
|
11
|
+
/** Adapter-specific public metadata. Never include signing secrets. */
|
|
12
|
+
metadata?: Readonly<Record<string, unknown>>;
|
|
13
|
+
}
|
|
14
|
+
export interface ContentCredentialsSignRequest {
|
|
15
|
+
/** Image already containing the invisible locator. */
|
|
16
|
+
image: string;
|
|
17
|
+
/** Locator used to correlate the signed record. */
|
|
18
|
+
locator: string;
|
|
19
|
+
/** Pixel algorithm that wrote the locator. */
|
|
20
|
+
algorithm: typeof INVISIBLE_WATERMARK_ALGORITHM;
|
|
21
|
+
claim: ContentCredentialsClaim;
|
|
22
|
+
}
|
|
23
|
+
export interface ContentCredentialsSignResult {
|
|
24
|
+
/** Image containing the signed Content Credential. */
|
|
25
|
+
image: string;
|
|
26
|
+
manifestId?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface ContentCredentialsVerificationResult {
|
|
29
|
+
valid: boolean;
|
|
30
|
+
manifestId?: string;
|
|
31
|
+
validationStatus?: readonly string[];
|
|
32
|
+
manifest?: unknown;
|
|
33
|
+
}
|
|
34
|
+
export interface ContentCredentialsAdapter {
|
|
35
|
+
sign(request: ContentCredentialsSignRequest): Promise<ContentCredentialsSignResult>;
|
|
36
|
+
verify(image: string): Promise<ContentCredentialsVerificationResult>;
|
|
37
|
+
}
|
|
38
|
+
export interface EmbedInvisibleWithCredentialsOptions {
|
|
39
|
+
watermark: EmbedInvisibleWatermarkOptions;
|
|
40
|
+
adapter: ContentCredentialsAdapter;
|
|
41
|
+
claim: ContentCredentialsClaim;
|
|
42
|
+
}
|
|
43
|
+
export interface EmbedInvisibleWithCredentialsResult {
|
|
44
|
+
watermarkedImage: MarkerResult;
|
|
45
|
+
signedImage: string;
|
|
46
|
+
manifestId?: string;
|
|
47
|
+
}
|
|
48
|
+
export interface VerifyContentCredentialsOptions {
|
|
49
|
+
image: string;
|
|
50
|
+
adapter: ContentCredentialsAdapter;
|
|
51
|
+
}
|
|
52
|
+
export declare function embedInvisibleWithCredentials(embed: (options: EmbedInvisibleWatermarkOptions) => Promise<MarkerResult>, options: EmbedInvisibleWithCredentialsOptions): Promise<EmbedInvisibleWithCredentialsResult>;
|
|
53
|
+
export declare function verifyContentCredentials(options: VerifyContentCredentialsOptions): Promise<ContentCredentialsVerificationResult>;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.embedInvisibleWithCredentials = embedInvisibleWithCredentials;
|
|
4
|
+
exports.verifyContentCredentials = verifyContentCredentials;
|
|
5
|
+
const invisible_watermark_js_1 = require("./invisible-watermark.js");
|
|
6
|
+
function validateAdapter(adapter) {
|
|
7
|
+
if (!adapter ||
|
|
8
|
+
typeof adapter.sign !== 'function' ||
|
|
9
|
+
typeof adapter.verify !== 'function') {
|
|
10
|
+
throw new Error('Content Credentials adapter must provide sign() and verify() functions.');
|
|
11
|
+
}
|
|
12
|
+
return adapter;
|
|
13
|
+
}
|
|
14
|
+
function snapshotClaim(claim) {
|
|
15
|
+
if (!claim || typeof claim.title !== 'string' || !claim.title.trim()) {
|
|
16
|
+
throw new Error('Content Credentials claim title must not be empty.');
|
|
17
|
+
}
|
|
18
|
+
if (claim.format !== undefined &&
|
|
19
|
+
claim.format !== 'image/jpeg' &&
|
|
20
|
+
claim.format !== 'image/png' &&
|
|
21
|
+
claim.format !== 'image/webp') {
|
|
22
|
+
throw new Error('Content Credentials claim format must be image/jpeg, image/png, or image/webp.');
|
|
23
|
+
}
|
|
24
|
+
if (claim.generator !== undefined &&
|
|
25
|
+
(typeof claim.generator !== 'string' || !claim.generator.trim())) {
|
|
26
|
+
throw new Error('Content Credentials claim generator must be a non-empty string.');
|
|
27
|
+
}
|
|
28
|
+
if (claim.metadata !== undefined &&
|
|
29
|
+
(!claim.metadata ||
|
|
30
|
+
typeof claim.metadata !== 'object' ||
|
|
31
|
+
Array.isArray(claim.metadata))) {
|
|
32
|
+
throw new Error('Content Credentials claim metadata must be an object.');
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
title: claim.title.trim(),
|
|
36
|
+
format: claim.format,
|
|
37
|
+
generator: claim.generator?.trim(),
|
|
38
|
+
metadata: claim.metadata ? { ...claim.metadata } : undefined,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function validateSignResult(result) {
|
|
42
|
+
if (!result || typeof result.image !== 'string' || !result.image) {
|
|
43
|
+
throw new Error('Content Credentials adapter returned an invalid signed image.');
|
|
44
|
+
}
|
|
45
|
+
if (result.manifestId !== undefined &&
|
|
46
|
+
typeof result.manifestId !== 'string') {
|
|
47
|
+
throw new Error('Content Credentials adapter returned an invalid manifest ID.');
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
function validateVerificationResult(result) {
|
|
52
|
+
if (!result || typeof result.valid !== 'boolean') {
|
|
53
|
+
throw new Error('Content Credentials adapter returned an invalid verification result.');
|
|
54
|
+
}
|
|
55
|
+
if (result.manifestId !== undefined &&
|
|
56
|
+
typeof result.manifestId !== 'string') {
|
|
57
|
+
throw new Error('Content Credentials adapter returned an invalid manifest ID.');
|
|
58
|
+
}
|
|
59
|
+
if (result.validationStatus !== undefined &&
|
|
60
|
+
(!Array.isArray(result.validationStatus) ||
|
|
61
|
+
result.validationStatus.some((status) => typeof status !== 'string'))) {
|
|
62
|
+
throw new Error('Content Credentials adapter returned invalid validation status data.');
|
|
63
|
+
}
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
async function embedInvisibleWithCredentials(embed, options) {
|
|
67
|
+
const adapter = validateAdapter(options?.adapter);
|
|
68
|
+
const claim = snapshotClaim(options?.claim);
|
|
69
|
+
const watermark = {
|
|
70
|
+
...options.watermark,
|
|
71
|
+
image: { ...options.watermark?.image },
|
|
72
|
+
};
|
|
73
|
+
const locator = watermark.payload;
|
|
74
|
+
const watermarkedImage = await embed(watermark);
|
|
75
|
+
const signed = validateSignResult(await adapter.sign({
|
|
76
|
+
image: watermarkedImage.uri,
|
|
77
|
+
locator,
|
|
78
|
+
algorithm: invisible_watermark_js_1.INVISIBLE_WATERMARK_ALGORITHM,
|
|
79
|
+
claim,
|
|
80
|
+
}));
|
|
81
|
+
const result = {
|
|
82
|
+
watermarkedImage,
|
|
83
|
+
signedImage: signed.image,
|
|
84
|
+
};
|
|
85
|
+
if (signed.manifestId !== undefined) {
|
|
86
|
+
result.manifestId = signed.manifestId;
|
|
87
|
+
}
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
async function verifyContentCredentials(options) {
|
|
91
|
+
const adapter = validateAdapter(options?.adapter);
|
|
92
|
+
if (typeof options?.image !== 'string' || !options.image) {
|
|
93
|
+
throw new Error('Content Credentials image must not be empty.');
|
|
94
|
+
}
|
|
95
|
+
return validateVerificationResult(await adapter.verify(options.image));
|
|
96
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export type ImageOrientation = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
|
|
2
|
+
export type MarkerImageFormat = 'jpeg' | 'png' | 'webp' | 'gif' | 'heif' | 'bmp' | 'unknown';
|
|
3
|
+
export interface MarkerImageInfo {
|
|
4
|
+
/** Display width after applying the encoded orientation. */
|
|
5
|
+
width: number;
|
|
6
|
+
/** Display height after applying the encoded orientation. */
|
|
7
|
+
height: number;
|
|
8
|
+
/** Width stored in the encoded pixel data before orientation is applied. */
|
|
9
|
+
encodedWidth: number;
|
|
10
|
+
/** Height stored in the encoded pixel data before orientation is applied. */
|
|
11
|
+
encodedHeight: number;
|
|
12
|
+
format: MarkerImageFormat;
|
|
13
|
+
mimeType?: string;
|
|
14
|
+
orientation: ImageOrientation;
|
|
15
|
+
rotationDegrees: 0 | 90 | 180 | 270;
|
|
16
|
+
mirrored: boolean;
|
|
17
|
+
requiresNormalization: boolean;
|
|
18
|
+
}
|
|
19
|
+
interface EncodedImageMetadata {
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
format: MarkerImageFormat;
|
|
23
|
+
mimeType?: string;
|
|
24
|
+
orientation?: ImageOrientation;
|
|
25
|
+
}
|
|
26
|
+
export declare function createMarkerImageInfo(metadata: EncodedImageMetadata): MarkerImageInfo;
|
|
27
|
+
/** Parse encoded metadata without decoding the image pixels. */
|
|
28
|
+
export declare function parseEncodedImageInfo(input: ArrayBuffer | ArrayBufferView): MarkerImageInfo;
|
|
29
|
+
export declare function parseNativeImageInfo(serialized: string): MarkerImageInfo;
|
|
30
|
+
export {};
|