imagescript 3.2.0 → 3.3.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 +8 -3
- package/THIRD_PARTY_NOTICES.md +131 -0
- package/dist/animation.d.ts +7 -0
- package/dist/animation.js +22 -12
- package/dist/codecs/crc.js +5 -0
- package/dist/codecs/gif.js +30 -52
- package/dist/codecs/jpeg.js +9 -27
- package/dist/codecs/png.js +22 -10
- package/dist/codecs/tga.js +3 -1
- package/dist/codecs/tiff.js +7 -2
- package/dist/codecs/vp8.js +3 -1
- package/dist/codecs/vp8l.js +126 -58
- package/dist/codecs/webp.js +11 -9
- package/dist/codecs/zlib.js +81 -69
- package/dist/frame.d.ts +1 -0
- package/dist/frame.js +4 -2
- package/dist/gif.d.ts +1 -0
- package/dist/gif.js +2 -2
- package/dist/image.d.ts +42 -7
- package/dist/image.js +224 -113
- package/dist/index.js +4 -4
- package/dist/render/bidi.js +350 -0
- package/dist/render/buffer.js +362 -0
- package/dist/render/color.js +164 -0
- package/dist/render/colr.js +523 -0
- package/dist/render/font.js +173 -241
- package/dist/render/hinting.js +12 -4
- package/dist/render/layout.js +1553 -0
- package/dist/render/linebreak.js +210 -0
- package/dist/render/segment.js +106 -0
- package/dist/render/shape.js +984 -0
- package/dist/render/shapers/arabic.js +284 -0
- package/dist/render/shapers/hangul.js +142 -0
- package/dist/render/shapers/hebrew.js +49 -0
- package/dist/render/shapers/index.js +52 -0
- package/dist/render/shapers/indic.js +579 -0
- package/dist/render/shapers/khmer.js +104 -0
- package/dist/render/shapers/machine.js +129 -0
- package/dist/render/shapers/myanmar.js +121 -0
- package/dist/render/shapers/syllabic.js +100 -0
- package/dist/render/shapers/thai.js +125 -0
- package/dist/render/shapers/use.js +235 -0
- package/dist/render/svg.js +101 -22
- package/dist/render/text.js +373 -103
- package/dist/render/unicode-data.js +63 -0
- package/dist/render/unicode.js +58 -0
- package/dist/text-layout.d.ts +4 -1
- package/dist/text-layout.js +2 -2
- package/dist/util/alpha.js +43 -0
- package/dist/util/blur.js +80 -5
- package/dist/util/bytes.js +31 -0
- package/dist/util/limits.js +34 -0
- package/dist/util/options.js +7 -0
- package/dist/util/resample.js +359 -79
- package/dist/util/rotate.js +43 -0
- package/dist/version.js +1 -1
- package/licenses/HarfBuzz.txt +42 -0
- package/licenses/Unicode-3.0.txt +39 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12,11 +12,11 @@ export { Animation } from "./animation.js";
|
|
|
12
12
|
export { TextLayout } from "./text-layout.js";
|
|
13
13
|
export { ImageType } from "./image-type.js";
|
|
14
14
|
export function decode(data, onlyFirstFrameOrOptions = false) {
|
|
15
|
-
const { onlyFirstFrame = false, applyOrientation } = isOptions(onlyFirstFrameOrOptions) ? onlyFirstFrameOrOptions : { onlyFirstFrame: onlyFirstFrameOrOptions };
|
|
15
|
+
const { onlyFirstFrame = false, applyOrientation, maxPixels } = isOptions(onlyFirstFrameOrOptions) ? onlyFirstFrameOrOptions : { onlyFirstFrame: onlyFirstFrameOrOptions };
|
|
16
16
|
const type = ImageType.getType(data);
|
|
17
17
|
if (type === 'gif')
|
|
18
|
-
return GIF.decode(data, { onlyFirstFrame });
|
|
18
|
+
return GIF.decode(data, { onlyFirstFrame, maxPixels });
|
|
19
19
|
if (type === 'webp' && webp.isAnimated(view(data)))
|
|
20
|
-
return Animation.decode(data, { onlyFirstFrame });
|
|
21
|
-
return Image.decode(data, { applyOrientation });
|
|
20
|
+
return Animation.decode(data, { onlyFirstFrame, maxPixels });
|
|
21
|
+
return Image.decode(data, { applyOrientation, maxPixels });
|
|
22
22
|
}
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { bidiClass, brackets as bracketData, mirrors as mirrorData } from "./unicode-data.js";
|
|
2
|
+
import { valueIndex } from "./unicode.js";
|
|
3
|
+
const NAMES = ['L', 'R', 'AL', 'EN', 'ES', 'ET', 'AN', 'CS', 'NSM', 'BN', 'B', 'S', 'WS', 'ON', 'LRE', 'LRO', 'RLE', 'RLO', 'PDF', 'LRI', 'RLI', 'FSI', 'PDI'];
|
|
4
|
+
export const BIDI_CLASSES = Object.fromEntries(NAMES.map((name, i) => [name, i]));
|
|
5
|
+
const fromTable = Uint8Array.from(bidiClass.names, name => BIDI_CLASSES[name]);
|
|
6
|
+
export function classOf(codepoint) {
|
|
7
|
+
return fromTable[valueIndex(bidiClass, codepoint)];
|
|
8
|
+
}
|
|
9
|
+
let bracketMap = null;
|
|
10
|
+
let mirrorMap = null;
|
|
11
|
+
export function mirror(codepoint) {
|
|
12
|
+
if (!mirrorMap) {
|
|
13
|
+
mirrorMap = new Map();
|
|
14
|
+
for (let i = 0; i < mirrorData.length; i += 2)
|
|
15
|
+
mirrorMap.set(mirrorData[i], mirrorData[i + 1]);
|
|
16
|
+
}
|
|
17
|
+
return mirrorMap.get(codepoint) ?? codepoint;
|
|
18
|
+
}
|
|
19
|
+
function bracket(codepoint) {
|
|
20
|
+
if (!bracketMap) {
|
|
21
|
+
bracketMap = new Map();
|
|
22
|
+
for (let i = 0; i < bracketData.length; i += 3)
|
|
23
|
+
bracketMap.set(bracketData[i], { pair: bracketData[i + 1], opening: bracketData[i + 2] === 1 });
|
|
24
|
+
}
|
|
25
|
+
return bracketMap.get(codepoint);
|
|
26
|
+
}
|
|
27
|
+
const canonical = (codepoint) => codepoint === 0x2329 ? 0x3008 : codepoint === 0x232a ? 0x3009 : codepoint;
|
|
28
|
+
const MAX_DEPTH = 125;
|
|
29
|
+
const isIsolateInitiator = (t) => t === 19 || t === 20 || t === 21;
|
|
30
|
+
const isRemoved = (t) => t === 9 || t >= 14 && t <= 18;
|
|
31
|
+
export function resolve(classes, codepoints, direction = -1) {
|
|
32
|
+
const n = classes.length;
|
|
33
|
+
const original = Uint8Array.from(classes);
|
|
34
|
+
const types = Uint8Array.from(classes);
|
|
35
|
+
const matching = new Int32Array(n).fill(-1);
|
|
36
|
+
{
|
|
37
|
+
const open = [];
|
|
38
|
+
for (let i = 0; i < n; i++) {
|
|
39
|
+
if (isIsolateInitiator(original[i]))
|
|
40
|
+
open.push(i);
|
|
41
|
+
else if (original[i] === 22 && open.length)
|
|
42
|
+
matching[open.pop()] = i;
|
|
43
|
+
else if (original[i] === 10)
|
|
44
|
+
open.length = 0;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const firstStrong = (start, end) => {
|
|
48
|
+
for (let i = start; i < end; i++) {
|
|
49
|
+
const t = original[i];
|
|
50
|
+
if (t === 0)
|
|
51
|
+
return 0;
|
|
52
|
+
if (t === 1 || t === 2)
|
|
53
|
+
return 1;
|
|
54
|
+
if (isIsolateInitiator(t)) {
|
|
55
|
+
if (matching[i] < 0)
|
|
56
|
+
return -1;
|
|
57
|
+
i = matching[i];
|
|
58
|
+
}
|
|
59
|
+
else if (t === 10)
|
|
60
|
+
return -1;
|
|
61
|
+
}
|
|
62
|
+
return -1;
|
|
63
|
+
};
|
|
64
|
+
const paragraphLevel = direction >= 0 ? direction : Math.max(0, firstStrong(0, n));
|
|
65
|
+
const levels = new Int8Array(n);
|
|
66
|
+
const stack = [{ level: paragraphLevel, override: -1, isolate: false }];
|
|
67
|
+
let overflowIsolates = 0, overflowEmbeddings = 0, validIsolates = 0;
|
|
68
|
+
for (let i = 0; i < n; i++) {
|
|
69
|
+
const t = original[i], top = stack[stack.length - 1];
|
|
70
|
+
switch (t) {
|
|
71
|
+
case 16:
|
|
72
|
+
case 14:
|
|
73
|
+
case 17:
|
|
74
|
+
case 15: {
|
|
75
|
+
const rtl = t === 16 || t === 17;
|
|
76
|
+
const level = rtl ? top.level + 1 | 1 : top.level + 2 & ~1;
|
|
77
|
+
if (level <= MAX_DEPTH && !overflowIsolates && !overflowEmbeddings)
|
|
78
|
+
stack.push({ level, override: t === 17 ? 1 : t === 15 ? 0 : -1, isolate: false });
|
|
79
|
+
else if (!overflowIsolates)
|
|
80
|
+
overflowEmbeddings++;
|
|
81
|
+
levels[i] = top.level;
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
case 20:
|
|
85
|
+
case 19:
|
|
86
|
+
case 21: {
|
|
87
|
+
levels[i] = top.level;
|
|
88
|
+
if (top.override >= 0)
|
|
89
|
+
types[i] = top.override;
|
|
90
|
+
const rtl = t === 20 || t === 21 && firstStrong(i + 1, matching[i] >= 0 ? matching[i] : n) === 1;
|
|
91
|
+
const level = rtl ? top.level + 1 | 1 : top.level + 2 & ~1;
|
|
92
|
+
if (level <= MAX_DEPTH && !overflowIsolates && !overflowEmbeddings) {
|
|
93
|
+
validIsolates++;
|
|
94
|
+
stack.push({ level, override: -1, isolate: true });
|
|
95
|
+
}
|
|
96
|
+
else
|
|
97
|
+
overflowIsolates++;
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
case 22:
|
|
101
|
+
if (overflowIsolates)
|
|
102
|
+
overflowIsolates--;
|
|
103
|
+
else if (validIsolates) {
|
|
104
|
+
overflowEmbeddings = 0;
|
|
105
|
+
while (!stack[stack.length - 1].isolate)
|
|
106
|
+
stack.pop();
|
|
107
|
+
stack.pop();
|
|
108
|
+
validIsolates--;
|
|
109
|
+
}
|
|
110
|
+
levels[i] = stack[stack.length - 1].level;
|
|
111
|
+
if (stack[stack.length - 1].override >= 0)
|
|
112
|
+
types[i] = stack[stack.length - 1].override;
|
|
113
|
+
break;
|
|
114
|
+
case 18:
|
|
115
|
+
if (overflowIsolates) {
|
|
116
|
+
}
|
|
117
|
+
else if (overflowEmbeddings)
|
|
118
|
+
overflowEmbeddings--;
|
|
119
|
+
else if (!top.isolate && stack.length > 1)
|
|
120
|
+
stack.pop();
|
|
121
|
+
levels[i] = top.level;
|
|
122
|
+
break;
|
|
123
|
+
case 10:
|
|
124
|
+
levels[i] = paragraphLevel;
|
|
125
|
+
break;
|
|
126
|
+
case 9:
|
|
127
|
+
levels[i] = top.level;
|
|
128
|
+
break;
|
|
129
|
+
default:
|
|
130
|
+
levels[i] = top.level;
|
|
131
|
+
if (top.override >= 0)
|
|
132
|
+
types[i] = top.override;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const runs = [];
|
|
136
|
+
{
|
|
137
|
+
let run = [];
|
|
138
|
+
for (let i = 0; i < n; i++) {
|
|
139
|
+
if (isRemoved(original[i]))
|
|
140
|
+
continue;
|
|
141
|
+
if (run.length && levels[i] !== levels[run[run.length - 1]]) {
|
|
142
|
+
runs.push(run);
|
|
143
|
+
run = [];
|
|
144
|
+
}
|
|
145
|
+
run.push(i);
|
|
146
|
+
}
|
|
147
|
+
if (run.length)
|
|
148
|
+
runs.push(run);
|
|
149
|
+
}
|
|
150
|
+
const matched = new Uint8Array(n);
|
|
151
|
+
for (let i = 0; i < n; i++)
|
|
152
|
+
if (matching[i] >= 0)
|
|
153
|
+
matched[matching[i]] = 1;
|
|
154
|
+
const runOf = new Int32Array(n).fill(-1);
|
|
155
|
+
runs.forEach((run, r) => runOf[run[0]] = r);
|
|
156
|
+
const sequences = [];
|
|
157
|
+
for (const run of runs) {
|
|
158
|
+
const first = run[0];
|
|
159
|
+
if (original[first] === 22 && matched[first])
|
|
160
|
+
continue;
|
|
161
|
+
const sequence = [...run];
|
|
162
|
+
for (let last = sequence[sequence.length - 1]; isIsolateInitiator(original[last]) && matching[last] >= 0;) {
|
|
163
|
+
const next = runOf[matching[last]];
|
|
164
|
+
if (next < 0)
|
|
165
|
+
break;
|
|
166
|
+
sequence.push(...runs[next]);
|
|
167
|
+
last = sequence[sequence.length - 1];
|
|
168
|
+
}
|
|
169
|
+
sequences.push(sequence);
|
|
170
|
+
}
|
|
171
|
+
const embeddingLevels = levels.slice();
|
|
172
|
+
const levelOfNeighbor = (index, step) => {
|
|
173
|
+
for (let i = index + step; i >= 0 && i < n; i += step)
|
|
174
|
+
if (!isRemoved(original[i]))
|
|
175
|
+
return embeddingLevels[i];
|
|
176
|
+
return paragraphLevel;
|
|
177
|
+
};
|
|
178
|
+
for (const sequence of sequences) {
|
|
179
|
+
const level = embeddingLevels[sequence[0]], m = sequence.length;
|
|
180
|
+
const embedding = level & 1 ? 1 : 0;
|
|
181
|
+
const first = sequence[0], last = sequence[m - 1];
|
|
182
|
+
const sos = Math.max(level, levelOfNeighbor(first, -1)) & 1 ? 1 : 0;
|
|
183
|
+
const eos = Math.max(level, isIsolateInitiator(original[last]) ? paragraphLevel : levelOfNeighbor(last, 1)) & 1 ? 1 : 0;
|
|
184
|
+
const t = sequence.map(i => types[i]);
|
|
185
|
+
for (let k = 0; k < m; k++)
|
|
186
|
+
if (t[k] === 8)
|
|
187
|
+
t[k] = k === 0 ? sos : isIsolateInitiator(t[k - 1]) || t[k - 1] === 22 ? 13 : t[k - 1];
|
|
188
|
+
for (let k = 0, strong = sos; k < m; k++) {
|
|
189
|
+
if (t[k] === 0 || t[k] === 1 || t[k] === 2)
|
|
190
|
+
strong = t[k];
|
|
191
|
+
else if (t[k] === 3 && strong === 2)
|
|
192
|
+
t[k] = 6;
|
|
193
|
+
}
|
|
194
|
+
for (let k = 0; k < m; k++)
|
|
195
|
+
if (t[k] === 2)
|
|
196
|
+
t[k] = 1;
|
|
197
|
+
for (let k = 1; k + 1 < m; k++) {
|
|
198
|
+
if (t[k] === 4 && t[k - 1] === 3 && t[k + 1] === 3)
|
|
199
|
+
t[k] = 3;
|
|
200
|
+
else if (t[k] === 7 && (t[k - 1] === 3 || t[k - 1] === 6) && t[k + 1] === t[k - 1])
|
|
201
|
+
t[k] = t[k - 1];
|
|
202
|
+
}
|
|
203
|
+
for (let k = 0; k < m; k++) {
|
|
204
|
+
if (t[k] !== 5)
|
|
205
|
+
continue;
|
|
206
|
+
let end = k;
|
|
207
|
+
while (end < m && t[end] === 5)
|
|
208
|
+
end++;
|
|
209
|
+
if (k > 0 && t[k - 1] === 3 || end < m && t[end] === 3)
|
|
210
|
+
for (let j = k; j < end; j++)
|
|
211
|
+
t[j] = 3;
|
|
212
|
+
k = end - 1;
|
|
213
|
+
}
|
|
214
|
+
for (let k = 0; k < m; k++)
|
|
215
|
+
if (t[k] === 4 || t[k] === 5 || t[k] === 7)
|
|
216
|
+
t[k] = 13;
|
|
217
|
+
for (let k = 0, strong = sos; k < m; k++) {
|
|
218
|
+
if (t[k] === 0 || t[k] === 1)
|
|
219
|
+
strong = t[k];
|
|
220
|
+
else if (t[k] === 3 && strong === 0)
|
|
221
|
+
t[k] = 0;
|
|
222
|
+
}
|
|
223
|
+
if (codepoints) {
|
|
224
|
+
const pairs = [];
|
|
225
|
+
const open = [];
|
|
226
|
+
for (let k = 0; k < m; k++) {
|
|
227
|
+
if (t[k] !== 13)
|
|
228
|
+
continue;
|
|
229
|
+
const cp = codepoints[sequence[k]], b = bracket(cp);
|
|
230
|
+
if (!b)
|
|
231
|
+
continue;
|
|
232
|
+
if (b.opening) {
|
|
233
|
+
if (open.length === 63)
|
|
234
|
+
break;
|
|
235
|
+
open.push({ k, pair: canonical(b.pair) });
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
for (let j = open.length - 1; j >= 0; j--)
|
|
239
|
+
if (open[j].pair === canonical(cp)) {
|
|
240
|
+
pairs.push([open[j].k, k]);
|
|
241
|
+
open.length = j;
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
pairs.sort((a, b) => a[0] - b[0]);
|
|
247
|
+
const strongOf = (type) => type === 0 ? 0 : type === 1 || type === 3 || type === 6 ? 1 : -1;
|
|
248
|
+
for (const [o, c] of pairs) {
|
|
249
|
+
let inside = -1;
|
|
250
|
+
for (let k = o + 1; k < c; k++) {
|
|
251
|
+
const s = strongOf(t[k]);
|
|
252
|
+
if (s === embedding) {
|
|
253
|
+
inside = embedding;
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
if (s >= 0)
|
|
257
|
+
inside = s;
|
|
258
|
+
}
|
|
259
|
+
if (inside < 0)
|
|
260
|
+
continue;
|
|
261
|
+
let resolved = embedding;
|
|
262
|
+
if (inside !== embedding) {
|
|
263
|
+
let before = sos;
|
|
264
|
+
for (let k = o - 1; k >= 0; k--) {
|
|
265
|
+
const s = strongOf(t[k]);
|
|
266
|
+
if (s >= 0) {
|
|
267
|
+
before = s;
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
resolved = before === inside ? inside : embedding;
|
|
272
|
+
}
|
|
273
|
+
t[o] = t[c] = resolved;
|
|
274
|
+
for (const b of [o, c])
|
|
275
|
+
for (let k = b + 1; k < m && original[sequence[k]] === 8; k++)
|
|
276
|
+
t[k] = resolved;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
const isNeutral = (type) => type === 10 || type === 11 || type === 12 || type === 13 || isIsolateInitiator(type) || type === 22;
|
|
280
|
+
for (let k = 0; k < m; k++) {
|
|
281
|
+
if (!isNeutral(t[k]))
|
|
282
|
+
continue;
|
|
283
|
+
let end = k;
|
|
284
|
+
while (end < m && isNeutral(t[end]))
|
|
285
|
+
end++;
|
|
286
|
+
const before = k === 0 ? sos : t[k - 1] === 0 ? 0 : 1;
|
|
287
|
+
const after = end === m ? eos : t[end] === 0 ? 0 : 1;
|
|
288
|
+
const resolved = before === after ? before : embedding;
|
|
289
|
+
for (let j = k; j < end; j++)
|
|
290
|
+
t[j] = resolved;
|
|
291
|
+
k = end - 1;
|
|
292
|
+
}
|
|
293
|
+
for (let k = 0; k < m; k++) {
|
|
294
|
+
const i = sequence[k];
|
|
295
|
+
if (level & 1) {
|
|
296
|
+
if (t[k] === 0 || t[k] === 3 || t[k] === 6)
|
|
297
|
+
levels[i]++;
|
|
298
|
+
}
|
|
299
|
+
else if (t[k] === 1)
|
|
300
|
+
levels[i]++;
|
|
301
|
+
else if (t[k] === 6 || t[k] === 3)
|
|
302
|
+
levels[i] += 2;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
for (let i = 0; i < n; i++)
|
|
306
|
+
if (isRemoved(original[i]))
|
|
307
|
+
levels[i] = -1;
|
|
308
|
+
return { level: paragraphLevel, levels, classes: original };
|
|
309
|
+
}
|
|
310
|
+
export function lineLevels(paragraph, start, end) {
|
|
311
|
+
const levels = paragraph.levels.slice(start, end), classes = paragraph.classes;
|
|
312
|
+
const trailing = (t) => t === 12 || isIsolateInitiator(t) || t === 22 || isRemoved(t);
|
|
313
|
+
for (let i = end - 1, atEnd = true; i >= start; i--) {
|
|
314
|
+
const t = classes[i];
|
|
315
|
+
if (t === 11 || t === 10) {
|
|
316
|
+
levels[i - start] = paragraph.level;
|
|
317
|
+
atEnd = true;
|
|
318
|
+
}
|
|
319
|
+
else if (atEnd && trailing(t)) {
|
|
320
|
+
if (!isRemoved(t))
|
|
321
|
+
levels[i - start] = paragraph.level;
|
|
322
|
+
}
|
|
323
|
+
else
|
|
324
|
+
atEnd = false;
|
|
325
|
+
}
|
|
326
|
+
return levels;
|
|
327
|
+
}
|
|
328
|
+
export function visualOrder(levels) {
|
|
329
|
+
const order = [];
|
|
330
|
+
for (let i = 0; i < levels.length; i++)
|
|
331
|
+
if (levels[i] >= 0)
|
|
332
|
+
order.push(i);
|
|
333
|
+
let highest = 0, lowestOdd = Infinity;
|
|
334
|
+
for (const i of order) {
|
|
335
|
+
highest = Math.max(highest, levels[i]);
|
|
336
|
+
if (levels[i] & 1)
|
|
337
|
+
lowestOdd = Math.min(lowestOdd, levels[i]);
|
|
338
|
+
}
|
|
339
|
+
for (let level = highest; level >= lowestOdd; level--)
|
|
340
|
+
for (let k = 0; k < order.length; k++) {
|
|
341
|
+
if (levels[order[k]] < level)
|
|
342
|
+
continue;
|
|
343
|
+
let end = k;
|
|
344
|
+
while (end < order.length && levels[order[end]] >= level)
|
|
345
|
+
end++;
|
|
346
|
+
order.splice(k, end - k, ...order.slice(k, end).reverse());
|
|
347
|
+
k = end - 1;
|
|
348
|
+
}
|
|
349
|
+
return order;
|
|
350
|
+
}
|