ag-psd 32.0.0 → 34.0.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/.v8-cache/v24.7.0-x64-ef5a0af0/03e19709 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/05135617 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/1130885f +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/226e2801 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/2a19133e +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/4511bacf +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/68416cb2 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/71df9cb3 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/7ea06cea +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/87cf2209 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/932f0770 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/acc36e66 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/b3c2fab7 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/c0b789d2 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/c314aece +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/cfc49f4f +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/cff9434a +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/d13d7610 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/dc86f000 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/e03e2acd +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/e6745c2b +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/e6b99945 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/eeeebdfe +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/f6fd8125 +0 -0
- package/CHANGELOG.md +100 -0
- package/LICENSE +5 -0
- package/README.md +4 -7
- package/README_PSD.md +261 -84
- package/TODO.md +13 -84
- package/dist/abr.d.ts +11 -4
- package/dist/abr.js +362 -230
- package/dist/abr.js.map +1 -1
- package/dist/additionalInfo.d.ts +56 -2
- package/dist/additionalInfo.js +828 -361
- package/dist/additionalInfo.js.map +1 -1
- package/dist/ase.d.ts +6 -1
- package/dist/ase.js +61 -61
- package/dist/ase.js.map +1 -1
- package/dist/bundle.js +3321 -1364
- package/dist/descriptor.d.ts +28 -13
- package/dist/descriptor.js +295 -241
- package/dist/descriptor.js.map +1 -1
- package/dist/helpers.d.ts +7 -1
- package/dist/helpers.js +246 -4
- package/dist/helpers.js.map +1 -1
- package/dist/imageResources.d.ts +15 -3
- package/dist/imageResources.js +586 -144
- package/dist/imageResources.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/jpeg.js +8 -8
- package/dist/jpeg.js.map +1 -1
- package/dist/psd.d.ts +292 -19
- package/dist/psd.js.map +1 -1
- package/dist/psdReader.d.ts +10 -0
- package/dist/psdReader.js +424 -216
- package/dist/psdReader.js.map +1 -1
- package/dist/psdWriter.d.ts +2 -0
- package/dist/psdWriter.js +373 -113
- package/dist/psdWriter.js.map +1 -1
- package/dist/text.d.ts +32 -1
- package/dist/text.js +75 -6
- package/dist/text.js.map +1 -1
- package/dist/utf8.js +0 -3
- package/dist/utf8.js.map +1 -1
- package/dist-es/abr.d.ts +11 -4
- package/dist-es/abr.js +362 -230
- package/dist-es/abr.js.map +1 -1
- package/dist-es/additionalInfo.d.ts +56 -2
- package/dist-es/additionalInfo.js +828 -361
- package/dist-es/additionalInfo.js.map +1 -1
- package/dist-es/ase.d.ts +6 -1
- package/dist-es/ase.js +61 -61
- package/dist-es/ase.js.map +1 -1
- package/dist-es/descriptor.d.ts +28 -13
- package/dist-es/descriptor.js +295 -241
- package/dist-es/descriptor.js.map +1 -1
- package/dist-es/helpers.d.ts +7 -1
- package/dist-es/helpers.js +246 -4
- package/dist-es/helpers.js.map +1 -1
- package/dist-es/imageResources.d.ts +15 -3
- package/dist-es/imageResources.js +586 -144
- package/dist-es/imageResources.js.map +1 -1
- package/dist-es/index.d.ts +1 -0
- package/dist-es/index.js +1 -0
- package/dist-es/index.js.map +1 -1
- package/dist-es/jpeg.js +8 -8
- package/dist-es/jpeg.js.map +1 -1
- package/dist-es/psd.d.ts +292 -19
- package/dist-es/psd.js.map +1 -1
- package/dist-es/psdReader.d.ts +10 -0
- package/dist-es/psdReader.js +424 -216
- package/dist-es/psdReader.js.map +1 -1
- package/dist-es/psdWriter.d.ts +2 -0
- package/dist-es/psdWriter.js +373 -113
- package/dist-es/psdWriter.js.map +1 -1
- package/dist-es/text.d.ts +32 -1
- package/dist-es/text.js +75 -6
- package/dist-es/text.js.map +1 -1
- package/dist-es/utf8.js +0 -3
- package/dist-es/utf8.js.map +1 -1
- package/package.json +2 -2
- package/perf.js +312 -9
- package/src/abr.ts +382 -235
- package/src/additionalInfo.ts +1071 -482
- package/src/ase.ts +66 -61
- package/src/descriptor.ts +291 -242
- package/src/helpers.ts +266 -3
- package/src/imageResources.ts +736 -169
- package/src/index.ts +1 -0
- package/src/jpeg.ts +8 -8
- package/src/psd.ts +271 -32
- package/src/psdReader.ts +440 -221
- package/src/psdWriter.ts +402 -110
- package/src/text.ts +93 -7
- package/src/utf8.ts +0 -4
package/src/imageResources.ts
CHANGED
|
@@ -1,21 +1,36 @@
|
|
|
1
1
|
import { toByteArray } from 'base64-js';
|
|
2
|
-
import { BlendMode, ImageResources, RenderingIntent } from './psd';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
2
|
+
import { BlendMode, Color, HalftoneScreen, ImageResources, KeyDescriptorItem, PrintTransferFunction, RenderingIntent, SavedPath } from './psd';
|
|
3
|
+
import { KeyDescriptorListDescriptor, parseKeyDescriptorList, readVectorMask, serializeKeyDescriptorList, writeVectorMask } from './additionalInfo';
|
|
4
|
+
import { PsdReader, readUnicodeString, readUint32, readUint16, readUint8, readFloat64, readBytes, skipBytes, readFloat32, readInt16, readFixedPoint32, readSignature, checkSignature, readSection, readColor, readInt32, readAsciiString, decodeUtf8, getUtf8StrictDecoder, getGbkStrictDecoder } from './psdReader';
|
|
5
|
+
import { PsdWriter, writeUnicodeString, writeUint32, writeUint8, writeFloat64, writeUint16, writeBytes, writeInt16, writeFloat32, writeFixedPoint32, writeUnicodeStringWithPadding, writeColor, writeSignature, writeSection, writeInt32, writeAsciiString, writeZeros } from './psdWriter';
|
|
6
|
+
import { clamp, ColorSpace, createCanvasFromData, createEnum, createImageData, imageDataToCanvas, MOCK_HANDLERS } from './helpers';
|
|
7
|
+
import { encodeString } from './utf8';
|
|
8
|
+
import { ESliceBGColorType, ESliceHorzAlign, ESliceOrigin, ESliceType, ESliceVertAlign, frac, FractionDescriptor, parseTrackList, pathSelectionStateToReference, DescriptorReferenceItem, readVersionAndDescriptor, serializeTrackList, TimelineTrackDescriptor, TimeScopeDescriptor, writeVersionAndDescriptor, DescriptorColor, DescriptorUnitsValue, parseColor, serializeColor, parseUnits, unitsValue, parsePercent, unitsPercentF , outputSettingsRoot } from './descriptor';
|
|
8
9
|
|
|
9
10
|
export interface InternalImageResources extends ImageResources {
|
|
10
11
|
layersGroup?: number[];
|
|
11
12
|
layerGroupsEnabledId?: number[];
|
|
13
|
+
savedPathsOrigination?: KeyDescriptorItem[][];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ResourceDocumentInfo {
|
|
17
|
+
width: number;
|
|
18
|
+
height: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ResourceInfo extends ResourceDocumentInfo {
|
|
22
|
+
signature: string; // '8BIM', or 'MeSa' etc. for resources from other applications
|
|
23
|
+
id: number;
|
|
24
|
+
name: Uint8Array; // raw pascal string bytes
|
|
12
25
|
}
|
|
13
26
|
|
|
14
27
|
export interface ResourceHandler {
|
|
15
28
|
key: number;
|
|
29
|
+
keyCount: number; // number of consecutive resource ids handled, starting at `key` (index is added to `key` when writing)
|
|
16
30
|
has: (target: InternalImageResources) => boolean | number;
|
|
17
|
-
read: (reader: PsdReader, target: InternalImageResources, left: () => number) => void;
|
|
18
|
-
write: (writer: PsdWriter, target: InternalImageResources, index: number) => void;
|
|
31
|
+
read: (reader: PsdReader, target: InternalImageResources, left: () => number, info: ResourceInfo) => void;
|
|
32
|
+
write: (writer: PsdWriter, target: InternalImageResources, index: number, document: ResourceDocumentInfo) => void;
|
|
33
|
+
name?: (target: InternalImageResources, index: number) => string;
|
|
19
34
|
}
|
|
20
35
|
|
|
21
36
|
export const resourceHandlers: ResourceHandler[] = [];
|
|
@@ -24,12 +39,13 @@ export const resourceHandlersMap: { [key: number]: ResourceHandler } = {};
|
|
|
24
39
|
function addHandler(
|
|
25
40
|
key: number,
|
|
26
41
|
has: (target: InternalImageResources) => boolean | number,
|
|
27
|
-
read: (reader: PsdReader, target: InternalImageResources, left: () => number) => void,
|
|
28
|
-
write: (writer: PsdWriter, target: InternalImageResources, index: number) => void,
|
|
42
|
+
read: (reader: PsdReader, target: InternalImageResources, left: () => number, info: ResourceInfo) => void,
|
|
43
|
+
write: (writer: PsdWriter, target: InternalImageResources, index: number, document: ResourceDocumentInfo) => void,
|
|
44
|
+
options: { keyCount?: number; name?: (target: InternalImageResources, index: number) => string; } = {},
|
|
29
45
|
) {
|
|
30
|
-
const handler: ResourceHandler = { key, has, read, write };
|
|
46
|
+
const handler: ResourceHandler = { key, keyCount: options.keyCount ?? 1, has, read, write, name: options.name };
|
|
31
47
|
resourceHandlers.push(handler);
|
|
32
|
-
resourceHandlersMap[handler.key] = handler;
|
|
48
|
+
for (let i = 0; i < handler.keyCount; i++) resourceHandlersMap[handler.key + i] = handler;
|
|
33
49
|
}
|
|
34
50
|
|
|
35
51
|
const LOG_MOCK_HANDLERS = false;
|
|
@@ -49,7 +65,7 @@ function byteAt(value: string, index: number) {
|
|
|
49
65
|
|
|
50
66
|
function readUtf8String(reader: PsdReader, length: number) {
|
|
51
67
|
const buffer = readBytes(reader, length);
|
|
52
|
-
return
|
|
68
|
+
return decodeUtf8(reader, buffer);
|
|
53
69
|
}
|
|
54
70
|
|
|
55
71
|
function writeUtf8String(writer: PsdWriter, value: string) {
|
|
@@ -57,24 +73,13 @@ function writeUtf8String(writer: PsdWriter, value: string) {
|
|
|
57
73
|
writeBytes(writer, buffer);
|
|
58
74
|
}
|
|
59
75
|
|
|
60
|
-
let gbkDecoder: TextDecoder | null | undefined;
|
|
61
|
-
|
|
62
|
-
function getGbkDecoder() {
|
|
63
|
-
if (gbkDecoder === undefined) {
|
|
64
|
-
try {
|
|
65
|
-
gbkDecoder = new TextDecoder('gbk');
|
|
66
|
-
} catch {
|
|
67
|
-
gbkDecoder = null;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return gbkDecoder;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
76
|
function readEncodedString(reader: PsdReader) {
|
|
75
77
|
const length = readUint8(reader);
|
|
76
78
|
const buffer = readBytes(reader, length);
|
|
79
|
+
return decodeEncodedString(reader, buffer);
|
|
80
|
+
}
|
|
77
81
|
|
|
82
|
+
function decodeEncodedString(reader: PsdReader, buffer: Uint8Array) {
|
|
78
83
|
let notAscii = false;
|
|
79
84
|
for (let i = 0; i < buffer.byteLength; i++) {
|
|
80
85
|
if (buffer[i] & 0x80) {
|
|
@@ -84,19 +89,21 @@ function readEncodedString(reader: PsdReader) {
|
|
|
84
89
|
}
|
|
85
90
|
|
|
86
91
|
if (notAscii) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
// encoding is ambiguous, many GBK strings are also valid UTF-8 and vice versa, GBK is preferred,
|
|
93
|
+
// UTF-8 is only used if the string is not valid GBK
|
|
94
|
+
for (const decoder of [getGbkStrictDecoder(reader), getUtf8StrictDecoder(reader)]) {
|
|
95
|
+
if (decoder) {
|
|
96
|
+
try {
|
|
97
|
+
return decoder.decode(buffer);
|
|
98
|
+
} catch { }
|
|
99
|
+
}
|
|
93
100
|
}
|
|
94
101
|
}
|
|
95
102
|
|
|
96
|
-
return
|
|
103
|
+
return decodeUtf8(reader, buffer);
|
|
97
104
|
}
|
|
98
105
|
|
|
99
|
-
function
|
|
106
|
+
function toShortAsciiString(value: string) {
|
|
100
107
|
let ascii = '';
|
|
101
108
|
|
|
102
109
|
for (let i = 0; i < value.length;) {
|
|
@@ -106,22 +113,23 @@ function writeEncodedString(writer: PsdWriter, value: string) {
|
|
|
106
113
|
}
|
|
107
114
|
|
|
108
115
|
// length is stored in a single byte
|
|
109
|
-
|
|
116
|
+
return ascii.length > 255 ? ascii.substring(0, 255) : ascii;
|
|
117
|
+
}
|
|
110
118
|
|
|
111
|
-
|
|
119
|
+
function writeEncodedString(writer: PsdWriter, value: string) {
|
|
120
|
+
const buffer = encodeString(toShortAsciiString(value));
|
|
112
121
|
writeUint8(writer, buffer.byteLength);
|
|
113
122
|
writeBytes(writer, buffer);
|
|
114
123
|
}
|
|
115
124
|
|
|
116
|
-
|
|
125
|
+
addHandler(
|
|
117
126
|
1028, // IPTC-NAA record
|
|
118
|
-
target =>
|
|
127
|
+
target => target.iptcData !== undefined,
|
|
119
128
|
(reader, target, left) => {
|
|
120
|
-
|
|
121
|
-
(target as any)._ir1028 = readBytes(reader, left());
|
|
129
|
+
target.iptcData = readBytes(reader, left());
|
|
122
130
|
},
|
|
123
131
|
(writer, target) => {
|
|
124
|
-
writeBytes(writer,
|
|
132
|
+
writeBytes(writer, target.iptcData!);
|
|
125
133
|
},
|
|
126
134
|
);
|
|
127
135
|
|
|
@@ -183,14 +191,14 @@ interface PrintInformationDescriptor {
|
|
|
183
191
|
};
|
|
184
192
|
}
|
|
185
193
|
|
|
186
|
-
|
|
194
|
+
addHandler(
|
|
187
195
|
1085, // Windows DEVMODE. Variable OS specific info for Windows.
|
|
188
|
-
target =>
|
|
196
|
+
target => target.windowsDevMode !== undefined,
|
|
189
197
|
(reader, target, left) => {
|
|
190
|
-
|
|
198
|
+
target.windowsDevMode = readBytes(reader, left());
|
|
191
199
|
},
|
|
192
200
|
(writer, target) => {
|
|
193
|
-
writeBytes(writer,
|
|
201
|
+
writeBytes(writer, target.windowsDevMode!);
|
|
194
202
|
}
|
|
195
203
|
);
|
|
196
204
|
|
|
@@ -208,6 +216,7 @@ addHandler(
|
|
|
208
216
|
const info = target.printInformation;
|
|
209
217
|
|
|
210
218
|
if (desc.PstS !== undefined) info.printerManagesColors = desc.PstS;
|
|
219
|
+
if (desc.ClrS === 'ClrS.Grys') info.printerProfileColorSpace = 'grayscale';
|
|
211
220
|
if (desc['Nm '] !== undefined) info.printerProfile = desc['Nm '];
|
|
212
221
|
if (desc.MpBl !== undefined) info.blackPointCompensation = desc.MpBl;
|
|
213
222
|
if (desc.printSixteenBit !== undefined) info.printSixteenBit = desc.printSixteenBit;
|
|
@@ -233,13 +242,13 @@ addHandler(
|
|
|
233
242
|
desc.PstS = true;
|
|
234
243
|
} else {
|
|
235
244
|
if (info.hardProof !== undefined) desc.hardProof = !!info.hardProof;
|
|
236
|
-
desc.ClrS = 'ClrS.
|
|
237
|
-
desc['Nm '] = info.printerProfile
|
|
245
|
+
desc.ClrS = info.printerProfileColorSpace === 'grayscale' ? 'ClrS.Grys' : 'ClrS.RGBC';
|
|
246
|
+
if (info.printerProfile !== undefined) desc['Nm '] = info.printerProfile;
|
|
238
247
|
}
|
|
239
248
|
|
|
240
249
|
desc.Inte = Inte.encode(info.renderingIntent);
|
|
241
250
|
|
|
242
|
-
if (!info.printerManagesColors) desc.MpBl = !!info.blackPointCompensation;
|
|
251
|
+
if (!info.printerManagesColors && info.blackPointCompensation !== undefined) desc.MpBl = !!info.blackPointCompensation;
|
|
243
252
|
|
|
244
253
|
desc.printSixteenBit = !!info.printSixteenBit;
|
|
245
254
|
desc.printerName = info.printerName || '';
|
|
@@ -251,9 +260,9 @@ addHandler(
|
|
|
251
260
|
MpBl: !!info.proofSetup.blackPointCompensation,
|
|
252
261
|
paperWhite: !!info.proofSetup.paperWhite,
|
|
253
262
|
};
|
|
254
|
-
} else {
|
|
263
|
+
} else if (info.proofSetup) {
|
|
255
264
|
desc.printProofSetup = {
|
|
256
|
-
Bltn:
|
|
265
|
+
Bltn: `builtinProof.${info.proofSetup.builtin || 'proofCMYK'}`,
|
|
257
266
|
};
|
|
258
267
|
}
|
|
259
268
|
|
|
@@ -261,19 +270,101 @@ addHandler(
|
|
|
261
270
|
},
|
|
262
271
|
);
|
|
263
272
|
|
|
264
|
-
|
|
273
|
+
interface PrintStyleDescriptor {
|
|
274
|
+
Cptn: boolean;
|
|
275
|
+
Clbr: boolean;
|
|
276
|
+
RgsM: boolean;
|
|
277
|
+
CrnC: boolean;
|
|
278
|
+
CntC: boolean;
|
|
279
|
+
Lbls: boolean;
|
|
280
|
+
Ngtv: boolean;
|
|
281
|
+
EmlD: boolean;
|
|
282
|
+
Intr: boolean;
|
|
283
|
+
Bckg: DescriptorColor;
|
|
284
|
+
BrdT: DescriptorUnitsValue;
|
|
285
|
+
'Bld ': DescriptorUnitsValue;
|
|
286
|
+
Rslt: DescriptorUnitsValue;
|
|
287
|
+
vectorData: boolean;
|
|
288
|
+
PgPs: string;
|
|
289
|
+
Left: DescriptorUnitsValue;
|
|
290
|
+
'Top ': DescriptorUnitsValue;
|
|
291
|
+
'Scl ': DescriptorUnitsValue;
|
|
292
|
+
cropWhenPrinting?: boolean;
|
|
293
|
+
cropRectBottom?: number;
|
|
294
|
+
cropRectLeft?: number;
|
|
295
|
+
cropRectRight?: number;
|
|
296
|
+
cropRectTop?: number;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
addHandler(
|
|
265
300
|
1083, // Print style
|
|
266
|
-
target =>
|
|
267
|
-
(reader, target
|
|
268
|
-
|
|
269
|
-
|
|
301
|
+
target => target.printStyle !== undefined,
|
|
302
|
+
(reader, target) => {
|
|
303
|
+
const desc: PrintStyleDescriptor = readVersionAndDescriptor(reader);
|
|
304
|
+
|
|
305
|
+
target.printStyle = {
|
|
306
|
+
caption: desc.Cptn,
|
|
307
|
+
calibrationBars: desc.Clbr,
|
|
308
|
+
registrationMarks: desc.RgsM,
|
|
309
|
+
cornerCropMarks: desc.CrnC,
|
|
310
|
+
centerCropMarks: desc.CntC,
|
|
311
|
+
labels: desc.Lbls,
|
|
312
|
+
negative: desc.Ngtv,
|
|
313
|
+
emulsionDown: desc.EmlD,
|
|
314
|
+
interpolate: desc.Intr,
|
|
315
|
+
backgroundColor: parseColor(desc.Bckg),
|
|
316
|
+
border: parseUnits(desc.BrdT),
|
|
317
|
+
bleed: parseUnits(desc['Bld ']),
|
|
318
|
+
resolution: parseUnits(desc.Rslt),
|
|
319
|
+
vectorData: desc.vectorData,
|
|
320
|
+
pagePosition: desc.PgPs === 'PgPs.PgPC' ? 'centered' : desc.PgPs,
|
|
321
|
+
left: parseUnits(desc.Left),
|
|
322
|
+
top: parseUnits(desc['Top ']),
|
|
323
|
+
scale: parsePercent(desc['Scl ']),
|
|
324
|
+
};
|
|
270
325
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
326
|
+
if (desc.cropWhenPrinting !== undefined) {
|
|
327
|
+
target.printStyle.cropWhenPrinting = desc.cropWhenPrinting;
|
|
328
|
+
target.printStyle.cropRect = {
|
|
329
|
+
top: desc.cropRectTop || 0,
|
|
330
|
+
left: desc.cropRectLeft || 0,
|
|
331
|
+
bottom: desc.cropRectBottom || 0,
|
|
332
|
+
right: desc.cropRectRight || 0,
|
|
333
|
+
};
|
|
334
|
+
}
|
|
274
335
|
},
|
|
275
336
|
(writer, target) => {
|
|
276
|
-
|
|
337
|
+
const style = target.printStyle!;
|
|
338
|
+
const desc: PrintStyleDescriptor = {
|
|
339
|
+
Cptn: !!style.caption,
|
|
340
|
+
Clbr: !!style.calibrationBars,
|
|
341
|
+
RgsM: !!style.registrationMarks,
|
|
342
|
+
CrnC: !!style.cornerCropMarks,
|
|
343
|
+
CntC: !!style.centerCropMarks,
|
|
344
|
+
Lbls: !!style.labels,
|
|
345
|
+
Ngtv: !!style.negative,
|
|
346
|
+
EmlD: !!style.emulsionDown,
|
|
347
|
+
Intr: !!style.interpolate,
|
|
348
|
+
Bckg: serializeColor(style.backgroundColor || { r: 255, g: 255, b: 255 }),
|
|
349
|
+
BrdT: unitsValue(style.border || { units: 'Distance', value: 0 }, 'border'),
|
|
350
|
+
'Bld ': unitsValue(style.bleed || { units: 'Distance', value: 0 }, 'bleed'),
|
|
351
|
+
Rslt: unitsValue(style.resolution || { units: 'Pixels', value: 72 }, 'resolution'),
|
|
352
|
+
vectorData: style.vectorData ?? true,
|
|
353
|
+
PgPs: (!style.pagePosition || style.pagePosition === 'centered') ? 'PgPs.PgPC' : style.pagePosition,
|
|
354
|
+
Left: unitsValue(style.left || { units: 'Distance', value: 0 }, 'left'),
|
|
355
|
+
'Top ': unitsValue(style.top || { units: 'Distance', value: 0 }, 'top'),
|
|
356
|
+
'Scl ': unitsPercentF(style.scale ?? 1),
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
if (style.cropWhenPrinting !== undefined || style.cropRect) {
|
|
360
|
+
desc.cropWhenPrinting = !!style.cropWhenPrinting;
|
|
361
|
+
desc.cropRectBottom = style.cropRect?.bottom ?? 0;
|
|
362
|
+
desc.cropRectLeft = style.cropRect?.left ?? 0;
|
|
363
|
+
desc.cropRectRight = style.cropRect?.right ?? 0;
|
|
364
|
+
desc.cropRectTop = style.cropRect?.top ?? 0;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
writeVersionAndDescriptor(writer, '', 'printOutputOptions', desc);
|
|
277
368
|
},
|
|
278
369
|
);
|
|
279
370
|
|
|
@@ -372,15 +463,72 @@ addHandler(
|
|
|
372
463
|
},
|
|
373
464
|
);
|
|
374
465
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
466
|
+
const displayInfoKinds = ['selected', 'protected', 'spot'] as const;
|
|
467
|
+
const supportedColorSpaces: number[] = [ColorSpace.RGB, ColorSpace.HSB, ColorSpace.CMYK, ColorSpace.Lab, ColorSpace.Grayscale];
|
|
468
|
+
|
|
469
|
+
function readDisplayInfo(reader: PsdReader) {
|
|
470
|
+
const info: NonNullable<ImageResources['alphaChannelDisplayInfo']>[number] = { opacity: 0, kind: 'selected' };
|
|
471
|
+
const colorSpace = reader.view.getUint16(reader.offset);
|
|
472
|
+
|
|
473
|
+
if (supportedColorSpaces.indexOf(colorSpace) !== -1) {
|
|
474
|
+
info.color = readColor(reader);
|
|
475
|
+
} else {
|
|
476
|
+
// keep color in unknown color space as it is, so the channel info is not lost or misaligned
|
|
477
|
+
skipBytes(reader, 2);
|
|
478
|
+
info.rawColor = { colorSpace, values: [readUint16(reader), readUint16(reader), readUint16(reader), readUint16(reader)] };
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
info.opacity = readUint16(reader) / 100;
|
|
482
|
+
const kind = displayInfoKinds[readUint8(reader)];
|
|
483
|
+
if (!kind) throw new Error('Invalid display info kind');
|
|
484
|
+
info.kind = kind;
|
|
485
|
+
return info;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
addHandler(
|
|
489
|
+
1007, // Display info (obsolete, replaced by 1077), written back as 1077
|
|
490
|
+
() => false,
|
|
378
491
|
(reader, target, left) => {
|
|
379
|
-
|
|
380
|
-
|
|
492
|
+
const info: NonNullable<ImageResources['alphaChannelDisplayInfo']> = [];
|
|
493
|
+
|
|
494
|
+
while (left() >= 14) {
|
|
495
|
+
info.push(readDisplayInfo(reader));
|
|
496
|
+
skipBytes(reader, 1); // padding
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// 1077 resource has more precise data and is used if present
|
|
500
|
+
if (!target.alphaChannelDisplayInfo) target.alphaChannelDisplayInfo = info;
|
|
501
|
+
},
|
|
502
|
+
() => { },
|
|
503
|
+
);
|
|
504
|
+
|
|
505
|
+
addHandler(
|
|
506
|
+
1077, // Display info (alpha and spot channels)
|
|
507
|
+
target => target.alphaChannelDisplayInfo !== undefined,
|
|
508
|
+
(reader, target, left) => {
|
|
509
|
+
const version = readUint32(reader);
|
|
510
|
+
if (version !== 1) throw new Error(`Invalid display info version: ${version}`);
|
|
511
|
+
|
|
512
|
+
target.alphaChannelDisplayInfo = [];
|
|
513
|
+
|
|
514
|
+
while (left() >= 13) {
|
|
515
|
+
target.alphaChannelDisplayInfo.push(readDisplayInfo(reader));
|
|
516
|
+
}
|
|
381
517
|
},
|
|
382
518
|
(writer, target) => {
|
|
383
|
-
|
|
519
|
+
writeUint32(writer, 1); // version
|
|
520
|
+
|
|
521
|
+
for (const info of target.alphaChannelDisplayInfo!) {
|
|
522
|
+
if (!info.color && info.rawColor) {
|
|
523
|
+
writeUint16(writer, info.rawColor.colorSpace);
|
|
524
|
+
for (let i = 0; i < 4; i++) writeUint16(writer, info.rawColor.values[i] | 0);
|
|
525
|
+
} else {
|
|
526
|
+
writeColor(writer, info.color || { r: 255, g: 0, b: 0 });
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
writeUint16(writer, Math.round(clamp(info.opacity, 0, 1) * 100));
|
|
530
|
+
writeUint8(writer, Math.max(0, displayInfoKinds.indexOf(info.kind)));
|
|
531
|
+
}
|
|
384
532
|
},
|
|
385
533
|
);
|
|
386
534
|
|
|
@@ -474,42 +622,176 @@ addHandler(
|
|
|
474
622
|
},
|
|
475
623
|
);
|
|
476
624
|
|
|
477
|
-
|
|
478
|
-
10000, // Print flags
|
|
479
|
-
target =>
|
|
480
|
-
(reader, target
|
|
481
|
-
|
|
482
|
-
(
|
|
625
|
+
addHandler(
|
|
626
|
+
10000, // Print flags information
|
|
627
|
+
target => target.printFlagsInfo !== undefined,
|
|
628
|
+
(reader, target) => {
|
|
629
|
+
const version = readUint16(reader);
|
|
630
|
+
if (version !== 1) throw new Error(`Invalid print flags information version: ${version}`);
|
|
631
|
+
const centerCropMarks = !!readUint8(reader);
|
|
632
|
+
skipBytes(reader, 1);
|
|
633
|
+
const bleedWidth = readUint32(reader);
|
|
634
|
+
const bleedWidthScale = readUint16(reader);
|
|
635
|
+
target.printFlagsInfo = { centerCropMarks, bleedWidth, bleedWidthScale };
|
|
483
636
|
},
|
|
484
637
|
(writer, target) => {
|
|
485
|
-
|
|
638
|
+
const info = target.printFlagsInfo!;
|
|
639
|
+
writeUint16(writer, 1); // version
|
|
640
|
+
writeUint8(writer, info.centerCropMarks ? 1 : 0);
|
|
641
|
+
writeUint8(writer, 0);
|
|
642
|
+
writeUint32(writer, info.bleedWidth | 0);
|
|
643
|
+
writeUint16(writer, info.bleedWidthScale | 0);
|
|
486
644
|
},
|
|
487
645
|
);
|
|
488
646
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
target =>
|
|
492
|
-
(reader, target
|
|
493
|
-
|
|
494
|
-
(target as any)._ir1013 = readBytes(reader, left());
|
|
647
|
+
addHandler(
|
|
648
|
+
1046, // Indexed color table count
|
|
649
|
+
target => target.indexedColorCount !== undefined,
|
|
650
|
+
(reader, target) => {
|
|
651
|
+
target.indexedColorCount = readUint16(reader);
|
|
495
652
|
},
|
|
496
653
|
(writer, target) => {
|
|
497
|
-
|
|
654
|
+
writeUint16(writer, target.indexedColorCount!);
|
|
498
655
|
},
|
|
499
656
|
);
|
|
500
657
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
target =>
|
|
658
|
+
addHandler(
|
|
659
|
+
1047, // Transparency index
|
|
660
|
+
target => target.transparencyIndex !== undefined,
|
|
661
|
+
(reader, target) => {
|
|
662
|
+
target.transparencyIndex = readUint16(reader);
|
|
663
|
+
},
|
|
664
|
+
(writer, target) => {
|
|
665
|
+
writeUint16(writer, target.transparencyIndex!);
|
|
666
|
+
},
|
|
667
|
+
);
|
|
668
|
+
|
|
669
|
+
const halftoneFrequencyUnits: HalftoneScreen['frequencyUnits'][] = ['lines/inch', 'lines/cm']; // 1, 2
|
|
670
|
+
const halftoneShapes: (HalftoneScreen['shape'] | undefined)[] = ['round', 'ellipse', 'line', 'square', 'cross', undefined, 'diamond']; // 0 - 6
|
|
671
|
+
|
|
672
|
+
function readHalftoneScreens(reader: PsdReader, left: () => number) {
|
|
673
|
+
const screens: HalftoneScreen[] = [];
|
|
674
|
+
|
|
675
|
+
while (left() >= 18) {
|
|
676
|
+
const frequency = readFixedPoint32(reader);
|
|
677
|
+
const frequencyUnits = halftoneFrequencyUnits[readUint16(reader) - 1];
|
|
678
|
+
if (!frequencyUnits) throw new Error('Invalid halftone screen frequency units');
|
|
679
|
+
const angle = readFixedPoint32(reader);
|
|
680
|
+
const shape = halftoneShapes[readInt16(reader)];
|
|
681
|
+
if (!shape) throw new Error('Invalid halftone screen shape');
|
|
682
|
+
skipBytes(reader, 4);
|
|
683
|
+
const accurateScreens = !!readUint8(reader);
|
|
684
|
+
const printerDefaultScreens = !!readUint8(reader);
|
|
685
|
+
screens.push({ frequency, frequencyUnits, angle, shape, accurateScreens, printerDefaultScreens });
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
return screens;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
function writeHalftoneScreens(writer: PsdWriter, screens: HalftoneScreen[]) {
|
|
692
|
+
for (const screen of screens) {
|
|
693
|
+
writeFixedPoint32(writer, screen.frequency);
|
|
694
|
+
writeUint16(writer, Math.max(0, halftoneFrequencyUnits.indexOf(screen.frequencyUnits)) + 1);
|
|
695
|
+
writeFixedPoint32(writer, screen.angle);
|
|
696
|
+
writeInt16(writer, Math.max(0, halftoneShapes.indexOf(screen.shape)));
|
|
697
|
+
writeZeros(writer, 4);
|
|
698
|
+
writeUint8(writer, screen.accurateScreens ? 1 : 0);
|
|
699
|
+
writeUint8(writer, screen.printerDefaultScreens ? 1 : 0);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
addHandler(
|
|
704
|
+
1012, // Grayscale and multichannel halftoning information
|
|
705
|
+
target => target.grayscaleHalftoneScreens !== undefined,
|
|
706
|
+
(reader, target, left) => target.grayscaleHalftoneScreens = readHalftoneScreens(reader, left),
|
|
707
|
+
(writer, target) => writeHalftoneScreens(writer, target.grayscaleHalftoneScreens!),
|
|
708
|
+
);
|
|
709
|
+
|
|
710
|
+
addHandler(
|
|
711
|
+
1013, // Color halftoning information
|
|
712
|
+
target => target.colorHalftoneScreens !== undefined,
|
|
713
|
+
(reader, target, left) => target.colorHalftoneScreens = readHalftoneScreens(reader, left),
|
|
714
|
+
(writer, target) => writeHalftoneScreens(writer, target.colorHalftoneScreens!),
|
|
715
|
+
);
|
|
716
|
+
|
|
717
|
+
function readTransferFunctions(reader: PsdReader, left: () => number) {
|
|
718
|
+
const functions: PrintTransferFunction[] = [];
|
|
719
|
+
|
|
720
|
+
while (left() >= 28) {
|
|
721
|
+
const curve: number[] = [];
|
|
722
|
+
for (let i = 0; i < 13; i++) curve.push(readInt16(reader));
|
|
723
|
+
const override = !!readUint16(reader);
|
|
724
|
+
functions.push({ curve, override });
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
return functions;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
function writeTransferFunctions(writer: PsdWriter, functions: PrintTransferFunction[]) {
|
|
731
|
+
for (const f of functions) {
|
|
732
|
+
for (let i = 0; i < 13; i++) writeInt16(writer, f.curve[i] ?? -1);
|
|
733
|
+
writeUint16(writer, f.override ? 1 : 0);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
addHandler(
|
|
738
|
+
1014, // Duotone halftoning information
|
|
739
|
+
target => target.duotoneHalftoneScreens !== undefined,
|
|
740
|
+
(reader, target, left) => target.duotoneHalftoneScreens = readHalftoneScreens(reader, left),
|
|
741
|
+
(writer, target) => writeHalftoneScreens(writer, target.duotoneHalftoneScreens!),
|
|
742
|
+
);
|
|
743
|
+
|
|
744
|
+
addHandler(
|
|
745
|
+
1017, // Duotone transfer functions
|
|
746
|
+
target => target.duotoneTransferFunctions !== undefined,
|
|
747
|
+
(reader, target, left) => target.duotoneTransferFunctions = readTransferFunctions(reader, left),
|
|
748
|
+
(writer, target) => writeTransferFunctions(writer, target.duotoneTransferFunctions!),
|
|
749
|
+
);
|
|
750
|
+
|
|
751
|
+
addHandler(
|
|
752
|
+
1018, // Duotone image information
|
|
753
|
+
target => target.duotoneImageInformation !== undefined,
|
|
754
|
+
(reader, target, left) => target.duotoneImageInformation = readBytes(reader, left()),
|
|
755
|
+
(writer, target) => writeBytes(writer, target.duotoneImageInformation),
|
|
756
|
+
);
|
|
757
|
+
|
|
758
|
+
addHandler(
|
|
759
|
+
1066, // Alternate duotone colors
|
|
760
|
+
target => target.alternateDuotoneColors !== undefined,
|
|
504
761
|
(reader, target, left) => {
|
|
505
|
-
|
|
506
|
-
|
|
762
|
+
if (readUint16(reader) !== 1) throw new Error('Invalid alternate duotone colors version');
|
|
763
|
+
const colors: Color[] = [];
|
|
764
|
+
const count = readUint16(reader);
|
|
765
|
+
for (let i = 0; i < count && left() >= 10; i++) colors.push(readColor(reader));
|
|
766
|
+
const labCount = readUint16(reader);
|
|
767
|
+
if (labCount * 3 > left()) throw new Error('Invalid alternate duotone colors Lab table size');
|
|
768
|
+
const labColors = readBytes(reader, labCount * 3);
|
|
769
|
+
target.alternateDuotoneColors = { colors, labColors };
|
|
507
770
|
},
|
|
508
771
|
(writer, target) => {
|
|
509
|
-
|
|
772
|
+
const { colors, labColors } = target.alternateDuotoneColors!;
|
|
773
|
+
writeUint16(writer, 1); // version
|
|
774
|
+
writeUint16(writer, colors.length);
|
|
775
|
+
for (const color of colors) writeColor(writer, color);
|
|
776
|
+
writeUint16(writer, Math.floor(labColors.byteLength / 3));
|
|
777
|
+
writeBytes(writer, labColors.subarray(0, Math.floor(labColors.byteLength / 3) * 3));
|
|
510
778
|
},
|
|
511
779
|
);
|
|
512
780
|
|
|
781
|
+
addHandler(
|
|
782
|
+
1015, // Grayscale and multichannel transfer function
|
|
783
|
+
target => target.grayscaleTransferFunctions !== undefined,
|
|
784
|
+
(reader, target, left) => target.grayscaleTransferFunctions = readTransferFunctions(reader, left),
|
|
785
|
+
(writer, target) => writeTransferFunctions(writer, target.grayscaleTransferFunctions!),
|
|
786
|
+
);
|
|
787
|
+
|
|
788
|
+
addHandler(
|
|
789
|
+
1016, // Color transfer functions
|
|
790
|
+
target => target.colorTransferFunctions !== undefined,
|
|
791
|
+
(reader, target, left) => target.colorTransferFunctions = readTransferFunctions(reader, left),
|
|
792
|
+
(writer, target) => writeTransferFunctions(writer, target.colorTransferFunctions!),
|
|
793
|
+
);
|
|
794
|
+
|
|
513
795
|
interface CountInformationDesc {
|
|
514
796
|
Vrsn: 1;
|
|
515
797
|
countGroupList: {
|
|
@@ -631,17 +913,20 @@ addHandler(
|
|
|
631
913
|
|
|
632
914
|
if (version !== 1) throw new Error(`Invalid 1032 resource version: ${version}`);
|
|
633
915
|
|
|
634
|
-
target.gridAndGuidesInformation
|
|
635
|
-
|
|
636
|
-
guides: [],
|
|
637
|
-
};
|
|
916
|
+
const previous = target.gridAndGuidesInformation?.guides; // in case guides were already read from 1092
|
|
917
|
+
const guides: Guide[] = [];
|
|
638
918
|
|
|
639
919
|
for (let i = 0; i < count; i++) {
|
|
640
|
-
|
|
920
|
+
const guide: Guide = {
|
|
641
921
|
location: readInt32(reader) / 32,
|
|
642
922
|
direction: readUint8(reader) ? 'horizontal' : 'vertical'
|
|
643
|
-
}
|
|
923
|
+
};
|
|
924
|
+
if (previous?.length === count && previous[i].color) guide.color = previous[i].color;
|
|
925
|
+
if (previous?.length === count && previous[i].id) guide.id = previous[i].id;
|
|
926
|
+
guides.push(guide);
|
|
644
927
|
}
|
|
928
|
+
|
|
929
|
+
target.gridAndGuidesInformation = { grid: { horizontal, vertical }, guides };
|
|
645
930
|
},
|
|
646
931
|
(writer, target) => {
|
|
647
932
|
const info = target.gridAndGuidesInformation!;
|
|
@@ -660,6 +945,91 @@ addHandler(
|
|
|
660
945
|
},
|
|
661
946
|
);
|
|
662
947
|
|
|
948
|
+
type Guide = NonNullable<NonNullable<ImageResources['gridAndGuidesInformation']>['guides']>[number];
|
|
949
|
+
|
|
950
|
+
const defaultGuideColor = { r: 74, g: 255, b: 255 }; // Photoshop default (cyan)
|
|
951
|
+
|
|
952
|
+
addHandler(
|
|
953
|
+
1092, // Grid and guides information with guide colors (same as 1032 with color of each guide)
|
|
954
|
+
target => target.gridAndGuidesInformation !== undefined,
|
|
955
|
+
(reader, target) => {
|
|
956
|
+
const version = readUint32(reader);
|
|
957
|
+
if (version !== 2) throw new Error(`Invalid 1092 resource version: ${version}`);
|
|
958
|
+
const horizontal = readUint32(reader);
|
|
959
|
+
const vertical = readUint32(reader);
|
|
960
|
+
const count = readUint32(reader);
|
|
961
|
+
const guides: Guide[] = [];
|
|
962
|
+
|
|
963
|
+
for (let i = 0; i < count; i++) {
|
|
964
|
+
const location = readInt32(reader) / 32;
|
|
965
|
+
const direction = readUint8(reader) ? 'horizontal' : 'vertical';
|
|
966
|
+
skipBytes(reader, 1);
|
|
967
|
+
const color = { r: readUint8(reader), g: readUint8(reader), b: readUint8(reader) };
|
|
968
|
+
guides.push({ location, direction, color });
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
const existing = target.gridAndGuidesInformation?.guides;
|
|
972
|
+
|
|
973
|
+
if (existing && existing.length === count) {
|
|
974
|
+
existing.forEach((guide, i) => guide.color = guides[i].color);
|
|
975
|
+
} else {
|
|
976
|
+
target.gridAndGuidesInformation = { grid: { horizontal, vertical }, guides };
|
|
977
|
+
}
|
|
978
|
+
},
|
|
979
|
+
(writer, target) => {
|
|
980
|
+
const info = target.gridAndGuidesInformation!;
|
|
981
|
+
const grid = info.grid || { horizontal: 18 * 32, vertical: 18 * 32 };
|
|
982
|
+
const guides = info.guides || [];
|
|
983
|
+
|
|
984
|
+
writeUint32(writer, 2); // version
|
|
985
|
+
writeUint32(writer, Math.round(grid.horizontal));
|
|
986
|
+
writeUint32(writer, Math.round(grid.vertical));
|
|
987
|
+
writeUint32(writer, guides.length);
|
|
988
|
+
|
|
989
|
+
for (const g of guides) {
|
|
990
|
+
const color = g.color || defaultGuideColor;
|
|
991
|
+
writeInt32(writer, Math.round(g.location * 32));
|
|
992
|
+
writeUint8(writer, g.direction === 'horizontal' ? 1 : 0);
|
|
993
|
+
writeUint8(writer, 0);
|
|
994
|
+
writeUint8(writer, Math.round(color.r));
|
|
995
|
+
writeUint8(writer, Math.round(color.g));
|
|
996
|
+
writeUint8(writer, Math.round(color.b));
|
|
997
|
+
}
|
|
998
|
+
},
|
|
999
|
+
);
|
|
1000
|
+
|
|
1001
|
+
const emptyGuid = '00000000-0000-0000-0000-000000000000';
|
|
1002
|
+
|
|
1003
|
+
addHandler(
|
|
1004
|
+
1097, // Guide ids
|
|
1005
|
+
target => !!target.gridAndGuidesInformation && (target.gridAndGuidesInformation.guides || []).every(g => g.id),
|
|
1006
|
+
(reader, target) => {
|
|
1007
|
+
const count = readUint32(reader);
|
|
1008
|
+
const ids: string[] = [];
|
|
1009
|
+
|
|
1010
|
+
for (let i = 0; i < count; i++) {
|
|
1011
|
+
ids.push(readAsciiString(reader, readUint32(reader)));
|
|
1012
|
+
readUint32(reader); // unknown, always 0
|
|
1013
|
+
readAsciiString(reader, readUint32(reader)); // unknown, always empty guid
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
const guides = target.gridAndGuidesInformation?.guides;
|
|
1017
|
+
if (guides && guides.length === count) guides.forEach((guide, i) => guide.id = ids[i]);
|
|
1018
|
+
},
|
|
1019
|
+
(writer, target) => {
|
|
1020
|
+
const guides = target.gridAndGuidesInformation!.guides || [];
|
|
1021
|
+
writeUint32(writer, guides.length);
|
|
1022
|
+
|
|
1023
|
+
for (const g of guides) {
|
|
1024
|
+
writeUint32(writer, g.id!.length);
|
|
1025
|
+
writeAsciiString(writer, g.id!);
|
|
1026
|
+
writeUint32(writer, 0);
|
|
1027
|
+
writeUint32(writer, emptyGuid.length);
|
|
1028
|
+
writeAsciiString(writer, emptyGuid);
|
|
1029
|
+
}
|
|
1030
|
+
},
|
|
1031
|
+
);
|
|
1032
|
+
|
|
663
1033
|
interface LayerCompsDescriptor {
|
|
664
1034
|
list: {
|
|
665
1035
|
_classID: 'Comp';
|
|
@@ -835,7 +1205,7 @@ addHandler(
|
|
|
835
1205
|
workOutTime: frac(desc.workOutTime),
|
|
836
1206
|
repeats: desc.LCnt,
|
|
837
1207
|
hasMotion: desc.hasMotion,
|
|
838
|
-
globalTracks: parseTrackList(desc.globalTrackList,
|
|
1208
|
+
globalTracks: parseTrackList(desc.globalTrackList, reader),
|
|
839
1209
|
};
|
|
840
1210
|
|
|
841
1211
|
if (desc.audioClipGroupList?.audioClipGroupList?.length) {
|
|
@@ -875,7 +1245,7 @@ addHandler(
|
|
|
875
1245
|
workInTime: timeline.workInTime,
|
|
876
1246
|
workOutTime: timeline.workOutTime,
|
|
877
1247
|
LCnt: timeline.repeats,
|
|
878
|
-
globalTrackList: serializeTrackList(timeline.globalTracks),
|
|
1248
|
+
globalTrackList: serializeTrackList(timeline.globalTracks, writer),
|
|
879
1249
|
audioClipGroupList: {
|
|
880
1250
|
audioClipGroupList: (timeline.audioClipGroups ?? []).map(a => ({
|
|
881
1251
|
groupID: a.id,
|
|
@@ -1254,16 +1624,14 @@ addHandler(
|
|
|
1254
1624
|
},
|
|
1255
1625
|
);
|
|
1256
1626
|
|
|
1257
|
-
|
|
1627
|
+
addHandler(
|
|
1258
1628
|
1039, // ICC Profile
|
|
1259
|
-
target =>
|
|
1629
|
+
target => target.iccProfile !== undefined,
|
|
1260
1630
|
(reader, target, left) => {
|
|
1261
|
-
|
|
1262
|
-
LOG_MOCK_HANDLERS && console.log('image resource 1039', left());
|
|
1263
|
-
(target as any)._ir1039 = readBytes(reader, left());
|
|
1631
|
+
target.iccProfile = readBytes(reader, left());
|
|
1264
1632
|
},
|
|
1265
1633
|
(writer, target) => {
|
|
1266
|
-
writeBytes(writer,
|
|
1634
|
+
writeBytes(writer, target.iccProfile!);
|
|
1267
1635
|
},
|
|
1268
1636
|
);
|
|
1269
1637
|
|
|
@@ -1274,6 +1642,106 @@ addHandler(
|
|
|
1274
1642
|
(writer, target) => writeUint32(writer, target.idsSeedNumber!),
|
|
1275
1643
|
);
|
|
1276
1644
|
|
|
1645
|
+
addHandler(
|
|
1646
|
+
1038, // Color samplers (Photoshop 5.0, replaced by 1073 in CS3)
|
|
1647
|
+
target => target.colorSamplers !== undefined && !target.colorSamplers.some(s => s.depth !== undefined),
|
|
1648
|
+
(reader, target, left) => {
|
|
1649
|
+
const version = readUint32(reader);
|
|
1650
|
+
if (version !== 1) throw new Error(`Invalid color samplers version: ${version}`);
|
|
1651
|
+
if (target.colorSamplers?.some(s => s.depth !== undefined)) return skipBytes(reader, left()); // already read from 1073
|
|
1652
|
+
const count = readUint32(reader);
|
|
1653
|
+
target.colorSamplers = [];
|
|
1654
|
+
|
|
1655
|
+
for (let i = 0; i < count && left() >= 10; i++) {
|
|
1656
|
+
// vertical and horizontal position in 1/16 pixel (inferred from sample files, the specification describes
|
|
1657
|
+
// these values as horizontal and vertical fixed point numbers)
|
|
1658
|
+
const y = readInt32(reader) / 16;
|
|
1659
|
+
const x = readInt32(reader) / 16;
|
|
1660
|
+
const colorSpace = readInt16(reader);
|
|
1661
|
+
target.colorSamplers.push({ x, y, colorSpace });
|
|
1662
|
+
}
|
|
1663
|
+
},
|
|
1664
|
+
(writer, target) => {
|
|
1665
|
+
writeUint32(writer, 1); // version
|
|
1666
|
+
writeUint32(writer, target.colorSamplers!.length);
|
|
1667
|
+
|
|
1668
|
+
for (const sampler of target.colorSamplers!) {
|
|
1669
|
+
writeInt32(writer, Math.round(sampler.y * 16));
|
|
1670
|
+
writeInt32(writer, Math.round(sampler.x * 16));
|
|
1671
|
+
writeInt16(writer, sampler.colorSpace);
|
|
1672
|
+
}
|
|
1673
|
+
},
|
|
1674
|
+
);
|
|
1675
|
+
|
|
1676
|
+
addHandler(
|
|
1677
|
+
1073, // Color samplers (Photoshop CS3)
|
|
1678
|
+
target => target.colorSamplers !== undefined && target.colorSamplers.some(s => s.depth !== undefined),
|
|
1679
|
+
(reader, target, left) => {
|
|
1680
|
+
const version = readUint32(reader);
|
|
1681
|
+
if (version < 1 || version > 3) throw new Error(`Invalid color samplers version: ${version}`);
|
|
1682
|
+
const count = readUint32(reader);
|
|
1683
|
+
target.colorSamplers = [];
|
|
1684
|
+
|
|
1685
|
+
for (let i = 0; i < count && left() >= 10; i++) {
|
|
1686
|
+
// position is fixed point value for sampler version 1 (stored the same way as in 1038 resource) or float for version 2
|
|
1687
|
+
const samplerVersion = version === 3 ? readUint32(reader) : version;
|
|
1688
|
+
const y = samplerVersion === 1 ? readInt32(reader) / 16 : readFloat32(reader);
|
|
1689
|
+
const x = samplerVersion === 1 ? readInt32(reader) / 16 : readFloat32(reader);
|
|
1690
|
+
const colorSpace = readInt16(reader);
|
|
1691
|
+
const depth = version >= 2 ? readInt16(reader) : 1;
|
|
1692
|
+
target.colorSamplers.push({ x, y, colorSpace, depth });
|
|
1693
|
+
}
|
|
1694
|
+
},
|
|
1695
|
+
(writer, target) => {
|
|
1696
|
+
writeUint32(writer, 3); // version
|
|
1697
|
+
writeUint32(writer, target.colorSamplers!.length);
|
|
1698
|
+
|
|
1699
|
+
for (const sampler of target.colorSamplers!) {
|
|
1700
|
+
writeUint32(writer, 1); // sampler version
|
|
1701
|
+
writeInt32(writer, Math.round(sampler.y * 16));
|
|
1702
|
+
writeInt32(writer, Math.round(sampler.x * 16));
|
|
1703
|
+
writeInt16(writer, sampler.colorSpace);
|
|
1704
|
+
writeInt16(writer, sampler.depth ?? 1);
|
|
1705
|
+
}
|
|
1706
|
+
},
|
|
1707
|
+
);
|
|
1708
|
+
|
|
1709
|
+
addHandler(
|
|
1710
|
+
1022, // Quick Mask information
|
|
1711
|
+
target => target.quickMaskInfo !== undefined,
|
|
1712
|
+
(reader, target) => {
|
|
1713
|
+
const channelId = readUint16(reader);
|
|
1714
|
+
const initiallyEmpty = !!readUint8(reader);
|
|
1715
|
+
target.quickMaskInfo = { channelId, initiallyEmpty };
|
|
1716
|
+
},
|
|
1717
|
+
(writer, target) => {
|
|
1718
|
+
writeUint16(writer, target.quickMaskInfo!.channelId);
|
|
1719
|
+
writeUint8(writer, target.quickMaskInfo!.initiallyEmpty ? 1 : 0);
|
|
1720
|
+
},
|
|
1721
|
+
);
|
|
1722
|
+
|
|
1723
|
+
addHandler(
|
|
1724
|
+
1074, // Measurement scale
|
|
1725
|
+
target => target.measurementScale !== undefined,
|
|
1726
|
+
(reader, target, left) => {
|
|
1727
|
+
target.measurementScale = readBytes(reader, left());
|
|
1728
|
+
},
|
|
1729
|
+
(writer, target) => {
|
|
1730
|
+
writeBytes(writer, target.measurementScale);
|
|
1731
|
+
},
|
|
1732
|
+
);
|
|
1733
|
+
|
|
1734
|
+
addHandler(
|
|
1735
|
+
1042, // Effects visible (only present when effects are hidden)
|
|
1736
|
+
target => target.effectsVisible !== undefined,
|
|
1737
|
+
(reader, target) => {
|
|
1738
|
+
target.effectsVisible = !!readUint8(reader);
|
|
1739
|
+
},
|
|
1740
|
+
(writer, target) => {
|
|
1741
|
+
writeUint8(writer, target.effectsVisible ? 1 : 0);
|
|
1742
|
+
},
|
|
1743
|
+
);
|
|
1744
|
+
|
|
1277
1745
|
addHandler(
|
|
1278
1746
|
1036,
|
|
1279
1747
|
target => target.thumbnail !== undefined || target.thumbnailRaw !== undefined,
|
|
@@ -1281,14 +1749,39 @@ addHandler(
|
|
|
1281
1749
|
const format = readUint32(reader); // 1 = kJpegRGB, 0 = kRawRGB
|
|
1282
1750
|
const width = readUint32(reader);
|
|
1283
1751
|
const height = readUint32(reader);
|
|
1284
|
-
readUint32(reader); // widthBytes = (width * bits_per_pixel + 31) / 32 * 4.
|
|
1752
|
+
const widthBytes = readUint32(reader); // widthBytes = (width * bits_per_pixel + 31) / 32 * 4.
|
|
1285
1753
|
readUint32(reader); // totalSize = widthBytes * height * planes
|
|
1286
1754
|
readUint32(reader); // sizeAfterCompression
|
|
1287
1755
|
const bitsPerPixel = readUint16(reader); // 24
|
|
1288
|
-
|
|
1756
|
+
readUint16(reader); // planes, should be 1 (some applications write 3)
|
|
1757
|
+
|
|
1758
|
+
if (format === 0 && bitsPerPixel === 24 && widthBytes >= width * 3 && widthBytes * height <= left()) {
|
|
1759
|
+
// raw RGB data, rows are padded to 4 bytes
|
|
1760
|
+
if (reader.useRawThumbnail) {
|
|
1761
|
+
reader.logMissingFeatures && reader.log('Raw thumbnail data is not supported with useRawThumbnail option');
|
|
1762
|
+
skipBytes(reader, left());
|
|
1763
|
+
return;
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
const data = readBytes(reader, widthBytes * height);
|
|
1767
|
+
const imageData = createImageData(width, height);
|
|
1768
|
+
|
|
1769
|
+
for (let y = 0, dst = 0; y < height; y++) {
|
|
1770
|
+
for (let x = 0, src = y * widthBytes; x < width; x++, src += 3, dst += 4) {
|
|
1771
|
+
imageData.data[dst + 0] = data[src + 0];
|
|
1772
|
+
imageData.data[dst + 1] = data[src + 1];
|
|
1773
|
+
imageData.data[dst + 2] = data[src + 2];
|
|
1774
|
+
imageData.data[dst + 3] = 255;
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
target.thumbnail = imageDataToCanvas(imageData);
|
|
1779
|
+
skipBytes(reader, left());
|
|
1780
|
+
return;
|
|
1781
|
+
}
|
|
1289
1782
|
|
|
1290
|
-
if (format !== 1 || bitsPerPixel !== 24
|
|
1291
|
-
reader.logMissingFeatures && reader.log(`Invalid thumbnail data (format: ${format}, bitsPerPixel: ${bitsPerPixel}
|
|
1783
|
+
if (format !== 1 || bitsPerPixel !== 24) {
|
|
1784
|
+
reader.logMissingFeatures && reader.log(`Invalid thumbnail data (format: ${format}, bitsPerPixel: ${bitsPerPixel})`);
|
|
1292
1785
|
skipBytes(reader, left());
|
|
1293
1786
|
return;
|
|
1294
1787
|
}
|
|
@@ -1367,15 +1860,14 @@ addHandler(
|
|
|
1367
1860
|
},
|
|
1368
1861
|
);
|
|
1369
1862
|
|
|
1370
|
-
|
|
1371
|
-
1058, // EXIF data 1
|
|
1372
|
-
target =>
|
|
1863
|
+
addHandler(
|
|
1864
|
+
1058, // EXIF data 1
|
|
1865
|
+
target => target.exifData !== undefined,
|
|
1373
1866
|
(reader, target, left) => {
|
|
1374
|
-
|
|
1375
|
-
(target as any)._ir1058 = readBytes(reader, left());
|
|
1867
|
+
target.exifData = readBytes(reader, left());
|
|
1376
1868
|
},
|
|
1377
1869
|
(writer, target) => {
|
|
1378
|
-
writeBytes(writer,
|
|
1870
|
+
writeBytes(writer, target.exifData!);
|
|
1379
1871
|
},
|
|
1380
1872
|
);
|
|
1381
1873
|
|
|
@@ -1401,32 +1893,116 @@ addHandler(
|
|
|
1401
1893
|
},
|
|
1402
1894
|
);
|
|
1403
1895
|
|
|
1404
|
-
interface
|
|
1896
|
+
interface Descriptor1088Read {
|
|
1405
1897
|
'null': string[];
|
|
1406
1898
|
}
|
|
1407
1899
|
|
|
1900
|
+
interface Descriptor1088Write {
|
|
1901
|
+
'null': DescriptorReferenceItem[];
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1408
1904
|
addHandler(
|
|
1409
1905
|
1088,
|
|
1410
1906
|
target => target.pathSelectionState !== undefined,
|
|
1411
1907
|
(reader, target, _left) => {
|
|
1412
|
-
const desc:
|
|
1908
|
+
const desc: Descriptor1088Read = readVersionAndDescriptor(reader);
|
|
1413
1909
|
target.pathSelectionState = desc['null'];
|
|
1414
1910
|
},
|
|
1415
1911
|
(writer, target) => {
|
|
1416
|
-
const desc:
|
|
1912
|
+
const desc: Descriptor1088Write = { 'null': pathSelectionStateToReference(target.pathSelectionState!) };
|
|
1417
1913
|
writeVersionAndDescriptor(writer, '', 'null', desc);
|
|
1418
1914
|
},
|
|
1419
1915
|
);
|
|
1420
1916
|
|
|
1421
|
-
|
|
1422
|
-
1025,
|
|
1423
|
-
target =>
|
|
1917
|
+
addHandler(
|
|
1918
|
+
1025, // Work path
|
|
1919
|
+
target => target.workPath !== undefined,
|
|
1920
|
+
(reader, target, left, { width, height }) => {
|
|
1921
|
+
target.workPath = { paths: [] };
|
|
1922
|
+
readVectorMask(reader, target.workPath, width, height, left());
|
|
1923
|
+
skipBytes(reader, left());
|
|
1924
|
+
},
|
|
1925
|
+
(writer, target, _index, { width, height }) => {
|
|
1926
|
+
writeVectorMask(writer, target.workPath!, width, height);
|
|
1927
|
+
},
|
|
1928
|
+
);
|
|
1929
|
+
|
|
1930
|
+
const MAX_SAVED_PATHS = 999; // ids 2000-2998
|
|
1931
|
+
|
|
1932
|
+
addHandler(
|
|
1933
|
+
2000, // Saved paths (one resource for each path, named after the path)
|
|
1934
|
+
target => target.savedPaths ? Math.min(target.savedPaths.length, MAX_SAVED_PATHS) : 0,
|
|
1935
|
+
(reader, target, left, { signature, id, name, width, height }) => {
|
|
1936
|
+
if (signature !== '8BIM') { // ImageReady ('MeSa') resources use the same ids for other data
|
|
1937
|
+
reader.logMissingFeatures && reader.log(`Unhandled image resource: ${signature} ${id}`);
|
|
1938
|
+
skipBytes(reader, left());
|
|
1939
|
+
return;
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
const savedPaths = target.savedPaths ??= [];
|
|
1943
|
+
const path: SavedPath = { name: decodeEncodedString(reader, name), paths: [] };
|
|
1944
|
+
readVectorMask(reader, path, width, height, left());
|
|
1945
|
+
skipBytes(reader, left());
|
|
1946
|
+
|
|
1947
|
+
const keyDescriptorList = target.savedPathsOrigination?.[savedPaths.length];
|
|
1948
|
+
if (keyDescriptorList) path.vectorOrigination = { keyDescriptorList };
|
|
1949
|
+
|
|
1950
|
+
savedPaths.push(path);
|
|
1951
|
+
},
|
|
1952
|
+
(writer, target, index, { width, height }) => {
|
|
1953
|
+
writeVectorMask(writer, target.savedPaths![index], width, height);
|
|
1954
|
+
},
|
|
1955
|
+
{ keyCount: MAX_SAVED_PATHS, name: (target, index) => toShortAsciiString(target.savedPaths![index].name) },
|
|
1956
|
+
);
|
|
1957
|
+
|
|
1958
|
+
interface OriginPathInfoDescriptor {
|
|
1959
|
+
keyRootDescriptorList: KeyDescriptorListDescriptor[];
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
addHandler(
|
|
1963
|
+
3000, // Origin path info (origination data of saved paths)
|
|
1964
|
+
target => !!target.savedPaths?.some(path => path.vectorOrigination),
|
|
1424
1965
|
(reader, target, left) => {
|
|
1425
|
-
|
|
1426
|
-
|
|
1966
|
+
const desc = readVersionAndDescriptor(reader) as OriginPathInfoDescriptor;
|
|
1967
|
+
|
|
1968
|
+
desc.keyRootDescriptorList.forEach((item, i) => {
|
|
1969
|
+
const keyDescriptorList = parseKeyDescriptorList(item);
|
|
1970
|
+
if (!keyDescriptorList.length) return; // written for paths without origination data
|
|
1971
|
+
|
|
1972
|
+
const path = target.savedPaths?.[i];
|
|
1973
|
+
|
|
1974
|
+
if (path) {
|
|
1975
|
+
path.vectorOrigination = { keyDescriptorList };
|
|
1976
|
+
} else { // saved paths are stored after this resource
|
|
1977
|
+
(target.savedPathsOrigination ??= [])[i] = keyDescriptorList;
|
|
1978
|
+
}
|
|
1979
|
+
});
|
|
1980
|
+
|
|
1981
|
+
skipBytes(reader, left());
|
|
1427
1982
|
},
|
|
1428
1983
|
(writer, target) => {
|
|
1429
|
-
|
|
1984
|
+
const desc: OriginPathInfoDescriptor = {
|
|
1985
|
+
keyRootDescriptorList: target.savedPaths!.slice(0, MAX_SAVED_PATHS)
|
|
1986
|
+
.map(path => serializeKeyDescriptorList(path.vectorOrigination?.keyDescriptorList ?? [])),
|
|
1987
|
+
};
|
|
1988
|
+
writeVersionAndDescriptor(writer, '', 'null', desc);
|
|
1989
|
+
},
|
|
1990
|
+
{ name: () => 'OriginDataIRB' },
|
|
1991
|
+
);
|
|
1992
|
+
|
|
1993
|
+
addHandler(
|
|
1994
|
+
2999, // Clipping path
|
|
1995
|
+
target => target.clippingPath !== undefined,
|
|
1996
|
+
(reader, target, left) => {
|
|
1997
|
+
const name = readEncodedString(reader);
|
|
1998
|
+
const flatness = readFixedPoint32(reader);
|
|
1999
|
+
skipBytes(reader, left()); // fill rule (ignored by Photoshop)
|
|
2000
|
+
target.clippingPath = { name, flatness };
|
|
2001
|
+
},
|
|
2002
|
+
(writer, target) => {
|
|
2003
|
+
writeEncodedString(writer, target.clippingPath!.name);
|
|
2004
|
+
writeFixedPoint32(writer, target.clippingPath!.flatness);
|
|
2005
|
+
writeUint16(writer, 1); // fill rule (ignored by Photoshop)
|
|
1430
2006
|
},
|
|
1431
2007
|
);
|
|
1432
2008
|
|
|
@@ -1456,13 +2032,28 @@ interface AnimationsDescriptor {
|
|
|
1456
2032
|
FSts: AnimationDescriptor[];
|
|
1457
2033
|
}
|
|
1458
2034
|
|
|
2035
|
+
// plug-in resources are written with consecutive ids starting at 4000, in the same order as Photoshop writes them
|
|
2036
|
+
function pluginResourceKeys(target: InternalImageResources) {
|
|
2037
|
+
const keys: string[] = [];
|
|
2038
|
+
if (target.outputSettings !== undefined) keys.push('mset');
|
|
2039
|
+
if (target.animations !== undefined) keys.push('mani', 'mfri');
|
|
2040
|
+
return keys;
|
|
2041
|
+
}
|
|
2042
|
+
|
|
1459
2043
|
addHandler(
|
|
1460
|
-
4000, // Plug-In resource(s)
|
|
1461
|
-
target => target.
|
|
2044
|
+
4000, // Plug-In resource(s), identified by key stored in the data (ids 4000-4999)
|
|
2045
|
+
target => pluginResourceKeys(target).length,
|
|
1462
2046
|
(reader, target, left) => {
|
|
1463
2047
|
const key = readSignature(reader);
|
|
1464
2048
|
|
|
1465
|
-
if (key === '
|
|
2049
|
+
if (key === 'mset') {
|
|
2050
|
+
// Save for Web output settings
|
|
2051
|
+
target.outputSettings = readVersionAndDescriptor(reader, true);
|
|
2052
|
+
skipBytes(reader, left());
|
|
2053
|
+
} else if (key === 'mfri') {
|
|
2054
|
+
// frame count, active frame and animation count, same data as in animations (mani)
|
|
2055
|
+
skipBytes(reader, left());
|
|
2056
|
+
} else if (key === 'mani') {
|
|
1466
2057
|
checkSignature(reader, 'IRFR');
|
|
1467
2058
|
readSection(reader, 1, left => {
|
|
1468
2059
|
while (left() > 0) {
|
|
@@ -1490,24 +2081,43 @@ addHandler(
|
|
|
1490
2081
|
|
|
1491
2082
|
// console.log('#4000 AnDs', require('util').inspect(desc, false, 99, true));
|
|
1492
2083
|
// console.log('#4000 AnDs:result', require('util').inspect(target.animations, false, 99, true));
|
|
2084
|
+
skipBytes(reader, left()); // padding
|
|
1493
2085
|
} else if (key === 'Roll') {
|
|
1494
2086
|
const bytes = readBytes(reader, left());
|
|
1495
2087
|
reader.logDevFeatures && reader.log('#4000 Roll', bytes);
|
|
1496
2088
|
} else {
|
|
1497
2089
|
reader.logMissingFeatures && reader.log('Unhandled subsection in #4000', key);
|
|
2090
|
+
skipBytes(reader, left());
|
|
1498
2091
|
}
|
|
1499
2092
|
});
|
|
1500
2093
|
}
|
|
1501
2094
|
});
|
|
2095
|
+
|
|
2096
|
+
skipBytes(reader, left()); // unknown data after animations section
|
|
1502
2097
|
} else if (key === 'mopt') {
|
|
1503
2098
|
const bytes = readBytes(reader, left());
|
|
1504
2099
|
reader.logDevFeatures && reader.log('#4000 mopt', bytes);
|
|
1505
2100
|
} else {
|
|
1506
|
-
reader.logMissingFeatures && reader.log(
|
|
2101
|
+
reader.logMissingFeatures && reader.log(`Unhandled plug-in resource key: ${key}`);
|
|
2102
|
+
skipBytes(reader, left());
|
|
1507
2103
|
}
|
|
1508
2104
|
},
|
|
1509
|
-
(writer, target) => {
|
|
1510
|
-
|
|
2105
|
+
(writer, target, index) => {
|
|
2106
|
+
const key = pluginResourceKeys(target)[index];
|
|
2107
|
+
|
|
2108
|
+
if (key === 'mset') {
|
|
2109
|
+
writeSignature(writer, 'mset');
|
|
2110
|
+
writeVersionAndDescriptor(writer, '', 'null', target.outputSettings, outputSettingsRoot);
|
|
2111
|
+
} else if (key === 'mfri') {
|
|
2112
|
+
const animations = target.animations!;
|
|
2113
|
+
writeSignature(writer, 'mfri');
|
|
2114
|
+
writeUint32(writer, 2); // version
|
|
2115
|
+
writeUint32(writer, 16); // length
|
|
2116
|
+
writeUint32(writer, animations.frames.length);
|
|
2117
|
+
writeUint32(writer, animations.animations[0]?.activeFrame ?? 0);
|
|
2118
|
+
writeUint32(writer, animations.animations.length);
|
|
2119
|
+
writeUint32(writer, 0);
|
|
2120
|
+
} else if (key === 'mani') {
|
|
1511
2121
|
writeSignature(writer, 'mani');
|
|
1512
2122
|
writeSignature(writer, 'IRFR');
|
|
1513
2123
|
writeSection(writer, 1, () => {
|
|
@@ -1553,49 +2163,6 @@ addHandler(
|
|
|
1553
2163
|
});
|
|
1554
2164
|
}
|
|
1555
2165
|
},
|
|
2166
|
+
{ keyCount: 1000 },
|
|
1556
2167
|
);
|
|
1557
2168
|
|
|
1558
|
-
// TODO: Unfinished
|
|
1559
|
-
MOCK_HANDLERS && addHandler(
|
|
1560
|
-
4001, // Plug-In resource(s)
|
|
1561
|
-
target => (target as any)._ir4001 !== undefined,
|
|
1562
|
-
(reader, target, left) => {
|
|
1563
|
-
if (MOCK_HANDLERS) {
|
|
1564
|
-
LOG_MOCK_HANDLERS && console.log('image resource 4001', left());
|
|
1565
|
-
(target as any)._ir4001 = readBytes(reader, left());
|
|
1566
|
-
return;
|
|
1567
|
-
}
|
|
1568
|
-
|
|
1569
|
-
const key = readSignature(reader);
|
|
1570
|
-
|
|
1571
|
-
if (key === 'mfri') {
|
|
1572
|
-
const version = readUint32(reader);
|
|
1573
|
-
if (version !== 2) throw new Error('Invalid mfri version');
|
|
1574
|
-
|
|
1575
|
-
const length = readUint32(reader);
|
|
1576
|
-
const bytes = readBytes(reader, length);
|
|
1577
|
-
reader.logDevFeatures && reader.log('mfri', bytes);
|
|
1578
|
-
} else if (key === 'mset') {
|
|
1579
|
-
const desc = readVersionAndDescriptor(reader);
|
|
1580
|
-
reader.logDevFeatures && reader.log('mset', desc);
|
|
1581
|
-
} else {
|
|
1582
|
-
reader.logMissingFeatures && reader.log('Unhandled key in #4001', key);
|
|
1583
|
-
}
|
|
1584
|
-
},
|
|
1585
|
-
(writer, target) => {
|
|
1586
|
-
writeBytes(writer, (target as any)._ir4001);
|
|
1587
|
-
},
|
|
1588
|
-
);
|
|
1589
|
-
|
|
1590
|
-
// TODO: Unfinished
|
|
1591
|
-
MOCK_HANDLERS && addHandler(
|
|
1592
|
-
4002, // Plug-In resource(s)
|
|
1593
|
-
target => (target as any)._ir4002 !== undefined,
|
|
1594
|
-
(reader, target, left) => {
|
|
1595
|
-
LOG_MOCK_HANDLERS && console.log('image resource 4002', left());
|
|
1596
|
-
(target as any)._ir4002 = readBytes(reader, left());
|
|
1597
|
-
},
|
|
1598
|
-
(writer, target) => {
|
|
1599
|
-
writeBytes(writer, (target as any)._ir4002);
|
|
1600
|
-
},
|
|
1601
|
-
);
|