ag-psd 32.0.0 → 33.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/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/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/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 +78 -0
- package/LICENSE +5 -0
- package/README.md +3 -6
- package/README_PSD.md +242 -81
- package/dist/abr.d.ts +11 -4
- package/dist/abr.js +357 -230
- package/dist/abr.js.map +1 -1
- package/dist/additionalInfo.d.ts +54 -2
- package/dist/additionalInfo.js +487 -306
- 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 +1937 -1115
- package/dist/descriptor.d.ts +14 -8
- package/dist/descriptor.js +202 -186
- package/dist/descriptor.js.map +1 -1
- package/dist/helpers.d.ts +1 -1
- package/dist/helpers.js +4 -2
- package/dist/helpers.js.map +1 -1
- package/dist/imageResources.d.ts +15 -3
- package/dist/imageResources.js +503 -142
- package/dist/imageResources.js.map +1 -1
- package/dist/jpeg.js +8 -8
- package/dist/jpeg.js.map +1 -1
- package/dist/psd.d.ts +148 -17
- package/dist/psd.js.map +1 -1
- package/dist/psdReader.d.ts +10 -0
- package/dist/psdReader.js +278 -166
- package/dist/psdReader.js.map +1 -1
- package/dist/psdWriter.d.ts +1 -0
- package/dist/psdWriter.js +77 -62
- package/dist/psdWriter.js.map +1 -1
- package/dist/text.js +17 -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 +357 -230
- package/dist-es/abr.js.map +1 -1
- package/dist-es/additionalInfo.d.ts +54 -2
- package/dist-es/additionalInfo.js +487 -306
- 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 +14 -8
- package/dist-es/descriptor.js +202 -186
- package/dist-es/descriptor.js.map +1 -1
- package/dist-es/helpers.d.ts +1 -1
- package/dist-es/helpers.js +4 -2
- package/dist-es/helpers.js.map +1 -1
- package/dist-es/imageResources.d.ts +15 -3
- package/dist-es/imageResources.js +503 -142
- package/dist-es/imageResources.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 +148 -17
- package/dist-es/psd.js.map +1 -1
- package/dist-es/psdReader.d.ts +10 -0
- package/dist-es/psdReader.js +278 -166
- package/dist-es/psdReader.js.map +1 -1
- package/dist-es/psdWriter.d.ts +1 -0
- package/dist-es/psdWriter.js +77 -62
- package/dist-es/psdWriter.js.map +1 -1
- package/dist-es/text.js +17 -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/src/abr.ts +375 -235
- package/src/additionalInfo.ts +663 -420
- package/src/ase.ts +66 -61
- package/src/descriptor.ts +199 -195
- package/src/helpers.ts +4 -1
- package/src/imageResources.ts +632 -168
- package/src/jpeg.ts +8 -8
- package/src/psd.ts +156 -30
- package/src/psdReader.ts +273 -172
- package/src/psdWriter.ts +95 -58
- package/src/text.ts +17 -6
- 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, 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,132 @@ 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 =>
|
|
504
|
-
(reader, target
|
|
505
|
-
|
|
506
|
-
(target as any)._ir1016 = readBytes(reader, left());
|
|
658
|
+
addHandler(
|
|
659
|
+
1047, // Transparency index
|
|
660
|
+
target => target.transparencyIndex !== undefined,
|
|
661
|
+
(reader, target) => {
|
|
662
|
+
target.transparencyIndex = readUint16(reader);
|
|
507
663
|
},
|
|
508
664
|
(writer, target) => {
|
|
509
|
-
|
|
665
|
+
writeUint16(writer, target.transparencyIndex!);
|
|
510
666
|
},
|
|
511
667
|
);
|
|
512
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
|
+
1015, // Grayscale and multichannel transfer function
|
|
739
|
+
target => target.grayscaleTransferFunctions !== undefined,
|
|
740
|
+
(reader, target, left) => target.grayscaleTransferFunctions = readTransferFunctions(reader, left),
|
|
741
|
+
(writer, target) => writeTransferFunctions(writer, target.grayscaleTransferFunctions!),
|
|
742
|
+
);
|
|
743
|
+
|
|
744
|
+
addHandler(
|
|
745
|
+
1016, // Color transfer functions
|
|
746
|
+
target => target.colorTransferFunctions !== undefined,
|
|
747
|
+
(reader, target, left) => target.colorTransferFunctions = readTransferFunctions(reader, left),
|
|
748
|
+
(writer, target) => writeTransferFunctions(writer, target.colorTransferFunctions!),
|
|
749
|
+
);
|
|
750
|
+
|
|
513
751
|
interface CountInformationDesc {
|
|
514
752
|
Vrsn: 1;
|
|
515
753
|
countGroupList: {
|
|
@@ -631,17 +869,20 @@ addHandler(
|
|
|
631
869
|
|
|
632
870
|
if (version !== 1) throw new Error(`Invalid 1032 resource version: ${version}`);
|
|
633
871
|
|
|
634
|
-
target.gridAndGuidesInformation
|
|
635
|
-
|
|
636
|
-
guides: [],
|
|
637
|
-
};
|
|
872
|
+
const previous = target.gridAndGuidesInformation?.guides; // in case guides were already read from 1092
|
|
873
|
+
const guides: Guide[] = [];
|
|
638
874
|
|
|
639
875
|
for (let i = 0; i < count; i++) {
|
|
640
|
-
|
|
876
|
+
const guide: Guide = {
|
|
641
877
|
location: readInt32(reader) / 32,
|
|
642
878
|
direction: readUint8(reader) ? 'horizontal' : 'vertical'
|
|
643
|
-
}
|
|
879
|
+
};
|
|
880
|
+
if (previous?.length === count && previous[i].color) guide.color = previous[i].color;
|
|
881
|
+
if (previous?.length === count && previous[i].id) guide.id = previous[i].id;
|
|
882
|
+
guides.push(guide);
|
|
644
883
|
}
|
|
884
|
+
|
|
885
|
+
target.gridAndGuidesInformation = { grid: { horizontal, vertical }, guides };
|
|
645
886
|
},
|
|
646
887
|
(writer, target) => {
|
|
647
888
|
const info = target.gridAndGuidesInformation!;
|
|
@@ -660,6 +901,91 @@ addHandler(
|
|
|
660
901
|
},
|
|
661
902
|
);
|
|
662
903
|
|
|
904
|
+
type Guide = NonNullable<NonNullable<ImageResources['gridAndGuidesInformation']>['guides']>[number];
|
|
905
|
+
|
|
906
|
+
const defaultGuideColor = { r: 74, g: 255, b: 255 }; // Photoshop default (cyan)
|
|
907
|
+
|
|
908
|
+
addHandler(
|
|
909
|
+
1092, // Grid and guides information with guide colors (same as 1032 with color of each guide)
|
|
910
|
+
target => target.gridAndGuidesInformation !== undefined,
|
|
911
|
+
(reader, target) => {
|
|
912
|
+
const version = readUint32(reader);
|
|
913
|
+
if (version !== 2) throw new Error(`Invalid 1092 resource version: ${version}`);
|
|
914
|
+
const horizontal = readUint32(reader);
|
|
915
|
+
const vertical = readUint32(reader);
|
|
916
|
+
const count = readUint32(reader);
|
|
917
|
+
const guides: Guide[] = [];
|
|
918
|
+
|
|
919
|
+
for (let i = 0; i < count; i++) {
|
|
920
|
+
const location = readInt32(reader) / 32;
|
|
921
|
+
const direction = readUint8(reader) ? 'horizontal' : 'vertical';
|
|
922
|
+
skipBytes(reader, 1);
|
|
923
|
+
const color = { r: readUint8(reader), g: readUint8(reader), b: readUint8(reader) };
|
|
924
|
+
guides.push({ location, direction, color });
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
const existing = target.gridAndGuidesInformation?.guides;
|
|
928
|
+
|
|
929
|
+
if (existing && existing.length === count) {
|
|
930
|
+
existing.forEach((guide, i) => guide.color = guides[i].color);
|
|
931
|
+
} else {
|
|
932
|
+
target.gridAndGuidesInformation = { grid: { horizontal, vertical }, guides };
|
|
933
|
+
}
|
|
934
|
+
},
|
|
935
|
+
(writer, target) => {
|
|
936
|
+
const info = target.gridAndGuidesInformation!;
|
|
937
|
+
const grid = info.grid || { horizontal: 18 * 32, vertical: 18 * 32 };
|
|
938
|
+
const guides = info.guides || [];
|
|
939
|
+
|
|
940
|
+
writeUint32(writer, 2); // version
|
|
941
|
+
writeUint32(writer, Math.round(grid.horizontal));
|
|
942
|
+
writeUint32(writer, Math.round(grid.vertical));
|
|
943
|
+
writeUint32(writer, guides.length);
|
|
944
|
+
|
|
945
|
+
for (const g of guides) {
|
|
946
|
+
const color = g.color || defaultGuideColor;
|
|
947
|
+
writeInt32(writer, Math.round(g.location * 32));
|
|
948
|
+
writeUint8(writer, g.direction === 'horizontal' ? 1 : 0);
|
|
949
|
+
writeUint8(writer, 0);
|
|
950
|
+
writeUint8(writer, Math.round(color.r));
|
|
951
|
+
writeUint8(writer, Math.round(color.g));
|
|
952
|
+
writeUint8(writer, Math.round(color.b));
|
|
953
|
+
}
|
|
954
|
+
},
|
|
955
|
+
);
|
|
956
|
+
|
|
957
|
+
const emptyGuid = '00000000-0000-0000-0000-000000000000';
|
|
958
|
+
|
|
959
|
+
addHandler(
|
|
960
|
+
1097, // Guide ids
|
|
961
|
+
target => !!target.gridAndGuidesInformation && (target.gridAndGuidesInformation.guides || []).every(g => g.id),
|
|
962
|
+
(reader, target) => {
|
|
963
|
+
const count = readUint32(reader);
|
|
964
|
+
const ids: string[] = [];
|
|
965
|
+
|
|
966
|
+
for (let i = 0; i < count; i++) {
|
|
967
|
+
ids.push(readAsciiString(reader, readUint32(reader)));
|
|
968
|
+
readUint32(reader); // unknown, always 0
|
|
969
|
+
readAsciiString(reader, readUint32(reader)); // unknown, always empty guid
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
const guides = target.gridAndGuidesInformation?.guides;
|
|
973
|
+
if (guides && guides.length === count) guides.forEach((guide, i) => guide.id = ids[i]);
|
|
974
|
+
},
|
|
975
|
+
(writer, target) => {
|
|
976
|
+
const guides = target.gridAndGuidesInformation!.guides || [];
|
|
977
|
+
writeUint32(writer, guides.length);
|
|
978
|
+
|
|
979
|
+
for (const g of guides) {
|
|
980
|
+
writeUint32(writer, g.id!.length);
|
|
981
|
+
writeAsciiString(writer, g.id!);
|
|
982
|
+
writeUint32(writer, 0);
|
|
983
|
+
writeUint32(writer, emptyGuid.length);
|
|
984
|
+
writeAsciiString(writer, emptyGuid);
|
|
985
|
+
}
|
|
986
|
+
},
|
|
987
|
+
);
|
|
988
|
+
|
|
663
989
|
interface LayerCompsDescriptor {
|
|
664
990
|
list: {
|
|
665
991
|
_classID: 'Comp';
|
|
@@ -1254,16 +1580,14 @@ addHandler(
|
|
|
1254
1580
|
},
|
|
1255
1581
|
);
|
|
1256
1582
|
|
|
1257
|
-
|
|
1583
|
+
addHandler(
|
|
1258
1584
|
1039, // ICC Profile
|
|
1259
|
-
target =>
|
|
1585
|
+
target => target.iccProfile !== undefined,
|
|
1260
1586
|
(reader, target, left) => {
|
|
1261
|
-
|
|
1262
|
-
LOG_MOCK_HANDLERS && console.log('image resource 1039', left());
|
|
1263
|
-
(target as any)._ir1039 = readBytes(reader, left());
|
|
1587
|
+
target.iccProfile = readBytes(reader, left());
|
|
1264
1588
|
},
|
|
1265
1589
|
(writer, target) => {
|
|
1266
|
-
writeBytes(writer,
|
|
1590
|
+
writeBytes(writer, target.iccProfile!);
|
|
1267
1591
|
},
|
|
1268
1592
|
);
|
|
1269
1593
|
|
|
@@ -1274,6 +1598,47 @@ addHandler(
|
|
|
1274
1598
|
(writer, target) => writeUint32(writer, target.idsSeedNumber!),
|
|
1275
1599
|
);
|
|
1276
1600
|
|
|
1601
|
+
addHandler(
|
|
1602
|
+
1038, // Color samplers (Photoshop 5.0, replaced by 1073 in CS3)
|
|
1603
|
+
target => target.colorSamplers !== undefined,
|
|
1604
|
+
(reader, target, left) => {
|
|
1605
|
+
const version = readUint32(reader);
|
|
1606
|
+
if (version !== 1) throw new Error(`Invalid color samplers version: ${version}`);
|
|
1607
|
+
const count = readUint32(reader);
|
|
1608
|
+
target.colorSamplers = [];
|
|
1609
|
+
|
|
1610
|
+
for (let i = 0; i < count && left() >= 10; i++) {
|
|
1611
|
+
// vertical and horizontal position in 1/16 pixel (inferred from sample files, the specification describes
|
|
1612
|
+
// these values as horizontal and vertical fixed point numbers)
|
|
1613
|
+
const y = readInt32(reader) / 16;
|
|
1614
|
+
const x = readInt32(reader) / 16;
|
|
1615
|
+
const colorSpace = readInt16(reader);
|
|
1616
|
+
target.colorSamplers.push({ x, y, colorSpace });
|
|
1617
|
+
}
|
|
1618
|
+
},
|
|
1619
|
+
(writer, target) => {
|
|
1620
|
+
writeUint32(writer, 1); // version
|
|
1621
|
+
writeUint32(writer, target.colorSamplers!.length);
|
|
1622
|
+
|
|
1623
|
+
for (const sampler of target.colorSamplers!) {
|
|
1624
|
+
writeInt32(writer, Math.round(sampler.y * 16));
|
|
1625
|
+
writeInt32(writer, Math.round(sampler.x * 16));
|
|
1626
|
+
writeInt16(writer, sampler.colorSpace);
|
|
1627
|
+
}
|
|
1628
|
+
},
|
|
1629
|
+
);
|
|
1630
|
+
|
|
1631
|
+
addHandler(
|
|
1632
|
+
1042, // Effects visible (only present when effects are hidden)
|
|
1633
|
+
target => target.effectsVisible !== undefined,
|
|
1634
|
+
(reader, target) => {
|
|
1635
|
+
target.effectsVisible = !!readUint8(reader);
|
|
1636
|
+
},
|
|
1637
|
+
(writer, target) => {
|
|
1638
|
+
writeUint8(writer, target.effectsVisible ? 1 : 0);
|
|
1639
|
+
},
|
|
1640
|
+
);
|
|
1641
|
+
|
|
1277
1642
|
addHandler(
|
|
1278
1643
|
1036,
|
|
1279
1644
|
target => target.thumbnail !== undefined || target.thumbnailRaw !== undefined,
|
|
@@ -1281,14 +1646,39 @@ addHandler(
|
|
|
1281
1646
|
const format = readUint32(reader); // 1 = kJpegRGB, 0 = kRawRGB
|
|
1282
1647
|
const width = readUint32(reader);
|
|
1283
1648
|
const height = readUint32(reader);
|
|
1284
|
-
readUint32(reader); // widthBytes = (width * bits_per_pixel + 31) / 32 * 4.
|
|
1649
|
+
const widthBytes = readUint32(reader); // widthBytes = (width * bits_per_pixel + 31) / 32 * 4.
|
|
1285
1650
|
readUint32(reader); // totalSize = widthBytes * height * planes
|
|
1286
1651
|
readUint32(reader); // sizeAfterCompression
|
|
1287
1652
|
const bitsPerPixel = readUint16(reader); // 24
|
|
1288
|
-
|
|
1653
|
+
readUint16(reader); // planes, should be 1 (some applications write 3)
|
|
1654
|
+
|
|
1655
|
+
if (format === 0 && bitsPerPixel === 24 && widthBytes >= width * 3 && widthBytes * height <= left()) {
|
|
1656
|
+
// raw RGB data, rows are padded to 4 bytes
|
|
1657
|
+
if (reader.useRawThumbnail) {
|
|
1658
|
+
reader.logMissingFeatures && reader.log('Raw thumbnail data is not supported with useRawThumbnail option');
|
|
1659
|
+
skipBytes(reader, left());
|
|
1660
|
+
return;
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
const data = readBytes(reader, widthBytes * height);
|
|
1664
|
+
const imageData = createImageData(width, height);
|
|
1665
|
+
|
|
1666
|
+
for (let y = 0, dst = 0; y < height; y++) {
|
|
1667
|
+
for (let x = 0, src = y * widthBytes; x < width; x++, src += 3, dst += 4) {
|
|
1668
|
+
imageData.data[dst + 0] = data[src + 0];
|
|
1669
|
+
imageData.data[dst + 1] = data[src + 1];
|
|
1670
|
+
imageData.data[dst + 2] = data[src + 2];
|
|
1671
|
+
imageData.data[dst + 3] = 255;
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
target.thumbnail = imageDataToCanvas(imageData);
|
|
1676
|
+
skipBytes(reader, left());
|
|
1677
|
+
return;
|
|
1678
|
+
}
|
|
1289
1679
|
|
|
1290
|
-
if (format !== 1 || bitsPerPixel !== 24
|
|
1291
|
-
reader.logMissingFeatures && reader.log(`Invalid thumbnail data (format: ${format}, bitsPerPixel: ${bitsPerPixel}
|
|
1680
|
+
if (format !== 1 || bitsPerPixel !== 24) {
|
|
1681
|
+
reader.logMissingFeatures && reader.log(`Invalid thumbnail data (format: ${format}, bitsPerPixel: ${bitsPerPixel})`);
|
|
1292
1682
|
skipBytes(reader, left());
|
|
1293
1683
|
return;
|
|
1294
1684
|
}
|
|
@@ -1367,15 +1757,14 @@ addHandler(
|
|
|
1367
1757
|
},
|
|
1368
1758
|
);
|
|
1369
1759
|
|
|
1370
|
-
|
|
1371
|
-
1058, // EXIF data 1
|
|
1372
|
-
target =>
|
|
1760
|
+
addHandler(
|
|
1761
|
+
1058, // EXIF data 1
|
|
1762
|
+
target => target.exifData !== undefined,
|
|
1373
1763
|
(reader, target, left) => {
|
|
1374
|
-
|
|
1375
|
-
(target as any)._ir1058 = readBytes(reader, left());
|
|
1764
|
+
target.exifData = readBytes(reader, left());
|
|
1376
1765
|
},
|
|
1377
1766
|
(writer, target) => {
|
|
1378
|
-
writeBytes(writer,
|
|
1767
|
+
writeBytes(writer, target.exifData!);
|
|
1379
1768
|
},
|
|
1380
1769
|
);
|
|
1381
1770
|
|
|
@@ -1401,32 +1790,116 @@ addHandler(
|
|
|
1401
1790
|
},
|
|
1402
1791
|
);
|
|
1403
1792
|
|
|
1404
|
-
interface
|
|
1793
|
+
interface Descriptor1088Read {
|
|
1405
1794
|
'null': string[];
|
|
1406
1795
|
}
|
|
1407
1796
|
|
|
1797
|
+
interface Descriptor1088Write {
|
|
1798
|
+
'null': DescriptorReferenceItem[];
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1408
1801
|
addHandler(
|
|
1409
1802
|
1088,
|
|
1410
1803
|
target => target.pathSelectionState !== undefined,
|
|
1411
1804
|
(reader, target, _left) => {
|
|
1412
|
-
const desc:
|
|
1805
|
+
const desc: Descriptor1088Read = readVersionAndDescriptor(reader);
|
|
1413
1806
|
target.pathSelectionState = desc['null'];
|
|
1414
1807
|
},
|
|
1415
1808
|
(writer, target) => {
|
|
1416
|
-
const desc:
|
|
1809
|
+
const desc: Descriptor1088Write = { 'null': pathSelectionStateToReference(target.pathSelectionState!) };
|
|
1417
1810
|
writeVersionAndDescriptor(writer, '', 'null', desc);
|
|
1418
1811
|
},
|
|
1419
1812
|
);
|
|
1420
1813
|
|
|
1421
|
-
|
|
1422
|
-
1025,
|
|
1423
|
-
target =>
|
|
1814
|
+
addHandler(
|
|
1815
|
+
1025, // Work path
|
|
1816
|
+
target => target.workPath !== undefined,
|
|
1817
|
+
(reader, target, left, { width, height }) => {
|
|
1818
|
+
target.workPath = { paths: [] };
|
|
1819
|
+
readVectorMask(reader, target.workPath, width, height, left());
|
|
1820
|
+
skipBytes(reader, left());
|
|
1821
|
+
},
|
|
1822
|
+
(writer, target, _index, { width, height }) => {
|
|
1823
|
+
writeVectorMask(writer, target.workPath!, width, height);
|
|
1824
|
+
},
|
|
1825
|
+
);
|
|
1826
|
+
|
|
1827
|
+
const MAX_SAVED_PATHS = 999; // ids 2000-2998
|
|
1828
|
+
|
|
1829
|
+
addHandler(
|
|
1830
|
+
2000, // Saved paths (one resource for each path, named after the path)
|
|
1831
|
+
target => target.savedPaths ? Math.min(target.savedPaths.length, MAX_SAVED_PATHS) : 0,
|
|
1832
|
+
(reader, target, left, { signature, id, name, width, height }) => {
|
|
1833
|
+
if (signature !== '8BIM') { // ImageReady ('MeSa') resources use the same ids for other data
|
|
1834
|
+
reader.logMissingFeatures && reader.log(`Unhandled image resource: ${signature} ${id}`);
|
|
1835
|
+
skipBytes(reader, left());
|
|
1836
|
+
return;
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
const savedPaths = target.savedPaths ??= [];
|
|
1840
|
+
const path: SavedPath = { name: decodeEncodedString(reader, name), paths: [] };
|
|
1841
|
+
readVectorMask(reader, path, width, height, left());
|
|
1842
|
+
skipBytes(reader, left());
|
|
1843
|
+
|
|
1844
|
+
const keyDescriptorList = target.savedPathsOrigination?.[savedPaths.length];
|
|
1845
|
+
if (keyDescriptorList) path.vectorOrigination = { keyDescriptorList };
|
|
1846
|
+
|
|
1847
|
+
savedPaths.push(path);
|
|
1848
|
+
},
|
|
1849
|
+
(writer, target, index, { width, height }) => {
|
|
1850
|
+
writeVectorMask(writer, target.savedPaths![index], width, height);
|
|
1851
|
+
},
|
|
1852
|
+
{ keyCount: MAX_SAVED_PATHS, name: (target, index) => toShortAsciiString(target.savedPaths![index].name) },
|
|
1853
|
+
);
|
|
1854
|
+
|
|
1855
|
+
interface OriginPathInfoDescriptor {
|
|
1856
|
+
keyRootDescriptorList: KeyDescriptorListDescriptor[];
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
addHandler(
|
|
1860
|
+
3000, // Origin path info (origination data of saved paths)
|
|
1861
|
+
target => !!target.savedPaths?.some(path => path.vectorOrigination),
|
|
1424
1862
|
(reader, target, left) => {
|
|
1425
|
-
|
|
1426
|
-
|
|
1863
|
+
const desc = readVersionAndDescriptor(reader) as OriginPathInfoDescriptor;
|
|
1864
|
+
|
|
1865
|
+
desc.keyRootDescriptorList.forEach((item, i) => {
|
|
1866
|
+
const keyDescriptorList = parseKeyDescriptorList(item);
|
|
1867
|
+
if (!keyDescriptorList.length) return; // written for paths without origination data
|
|
1868
|
+
|
|
1869
|
+
const path = target.savedPaths?.[i];
|
|
1870
|
+
|
|
1871
|
+
if (path) {
|
|
1872
|
+
path.vectorOrigination = { keyDescriptorList };
|
|
1873
|
+
} else { // saved paths are stored after this resource
|
|
1874
|
+
(target.savedPathsOrigination ??= [])[i] = keyDescriptorList;
|
|
1875
|
+
}
|
|
1876
|
+
});
|
|
1877
|
+
|
|
1878
|
+
skipBytes(reader, left());
|
|
1427
1879
|
},
|
|
1428
1880
|
(writer, target) => {
|
|
1429
|
-
|
|
1881
|
+
const desc: OriginPathInfoDescriptor = {
|
|
1882
|
+
keyRootDescriptorList: target.savedPaths!.slice(0, MAX_SAVED_PATHS)
|
|
1883
|
+
.map(path => serializeKeyDescriptorList(path.vectorOrigination?.keyDescriptorList ?? [])),
|
|
1884
|
+
};
|
|
1885
|
+
writeVersionAndDescriptor(writer, '', 'null', desc);
|
|
1886
|
+
},
|
|
1887
|
+
{ name: () => 'OriginDataIRB' },
|
|
1888
|
+
);
|
|
1889
|
+
|
|
1890
|
+
addHandler(
|
|
1891
|
+
2999, // Clipping path
|
|
1892
|
+
target => target.clippingPath !== undefined,
|
|
1893
|
+
(reader, target, left) => {
|
|
1894
|
+
const name = readEncodedString(reader);
|
|
1895
|
+
const flatness = readFixedPoint32(reader);
|
|
1896
|
+
skipBytes(reader, left()); // fill rule (ignored by Photoshop)
|
|
1897
|
+
target.clippingPath = { name, flatness };
|
|
1898
|
+
},
|
|
1899
|
+
(writer, target) => {
|
|
1900
|
+
writeEncodedString(writer, target.clippingPath!.name);
|
|
1901
|
+
writeFixedPoint32(writer, target.clippingPath!.flatness);
|
|
1902
|
+
writeUint16(writer, 1); // fill rule (ignored by Photoshop)
|
|
1430
1903
|
},
|
|
1431
1904
|
);
|
|
1432
1905
|
|
|
@@ -1456,13 +1929,28 @@ interface AnimationsDescriptor {
|
|
|
1456
1929
|
FSts: AnimationDescriptor[];
|
|
1457
1930
|
}
|
|
1458
1931
|
|
|
1932
|
+
// plug-in resources are written with consecutive ids starting at 4000, in the same order as Photoshop writes them
|
|
1933
|
+
function pluginResourceKeys(target: InternalImageResources) {
|
|
1934
|
+
const keys: string[] = [];
|
|
1935
|
+
if (target.outputSettings !== undefined) keys.push('mset');
|
|
1936
|
+
if (target.animations !== undefined) keys.push('mani', 'mfri');
|
|
1937
|
+
return keys;
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1459
1940
|
addHandler(
|
|
1460
|
-
4000, // Plug-In resource(s)
|
|
1461
|
-
target => target.
|
|
1941
|
+
4000, // Plug-In resource(s), identified by key stored in the data (ids 4000-4999)
|
|
1942
|
+
target => pluginResourceKeys(target).length,
|
|
1462
1943
|
(reader, target, left) => {
|
|
1463
1944
|
const key = readSignature(reader);
|
|
1464
1945
|
|
|
1465
|
-
if (key === '
|
|
1946
|
+
if (key === 'mset') {
|
|
1947
|
+
// Save for Web output settings
|
|
1948
|
+
target.outputSettings = readVersionAndDescriptor(reader, true);
|
|
1949
|
+
skipBytes(reader, left());
|
|
1950
|
+
} else if (key === 'mfri') {
|
|
1951
|
+
// frame count, active frame and animation count, same data as in animations (mani)
|
|
1952
|
+
skipBytes(reader, left());
|
|
1953
|
+
} else if (key === 'mani') {
|
|
1466
1954
|
checkSignature(reader, 'IRFR');
|
|
1467
1955
|
readSection(reader, 1, left => {
|
|
1468
1956
|
while (left() > 0) {
|
|
@@ -1490,24 +1978,43 @@ addHandler(
|
|
|
1490
1978
|
|
|
1491
1979
|
// console.log('#4000 AnDs', require('util').inspect(desc, false, 99, true));
|
|
1492
1980
|
// console.log('#4000 AnDs:result', require('util').inspect(target.animations, false, 99, true));
|
|
1981
|
+
skipBytes(reader, left()); // padding
|
|
1493
1982
|
} else if (key === 'Roll') {
|
|
1494
1983
|
const bytes = readBytes(reader, left());
|
|
1495
1984
|
reader.logDevFeatures && reader.log('#4000 Roll', bytes);
|
|
1496
1985
|
} else {
|
|
1497
1986
|
reader.logMissingFeatures && reader.log('Unhandled subsection in #4000', key);
|
|
1987
|
+
skipBytes(reader, left());
|
|
1498
1988
|
}
|
|
1499
1989
|
});
|
|
1500
1990
|
}
|
|
1501
1991
|
});
|
|
1992
|
+
|
|
1993
|
+
skipBytes(reader, left()); // unknown data after animations section
|
|
1502
1994
|
} else if (key === 'mopt') {
|
|
1503
1995
|
const bytes = readBytes(reader, left());
|
|
1504
1996
|
reader.logDevFeatures && reader.log('#4000 mopt', bytes);
|
|
1505
1997
|
} else {
|
|
1506
|
-
reader.logMissingFeatures && reader.log(
|
|
1998
|
+
reader.logMissingFeatures && reader.log(`Unhandled plug-in resource key: ${key}`);
|
|
1999
|
+
skipBytes(reader, left());
|
|
1507
2000
|
}
|
|
1508
2001
|
},
|
|
1509
|
-
(writer, target) => {
|
|
1510
|
-
|
|
2002
|
+
(writer, target, index) => {
|
|
2003
|
+
const key = pluginResourceKeys(target)[index];
|
|
2004
|
+
|
|
2005
|
+
if (key === 'mset') {
|
|
2006
|
+
writeSignature(writer, 'mset');
|
|
2007
|
+
writeVersionAndDescriptor(writer, '', 'null', target.outputSettings, outputSettingsRoot);
|
|
2008
|
+
} else if (key === 'mfri') {
|
|
2009
|
+
const animations = target.animations!;
|
|
2010
|
+
writeSignature(writer, 'mfri');
|
|
2011
|
+
writeUint32(writer, 2); // version
|
|
2012
|
+
writeUint32(writer, 16); // length
|
|
2013
|
+
writeUint32(writer, animations.frames.length);
|
|
2014
|
+
writeUint32(writer, animations.animations[0]?.activeFrame ?? 0);
|
|
2015
|
+
writeUint32(writer, animations.animations.length);
|
|
2016
|
+
writeUint32(writer, 0);
|
|
2017
|
+
} else if (key === 'mani') {
|
|
1511
2018
|
writeSignature(writer, 'mani');
|
|
1512
2019
|
writeSignature(writer, 'IRFR');
|
|
1513
2020
|
writeSection(writer, 1, () => {
|
|
@@ -1553,49 +2060,6 @@ addHandler(
|
|
|
1553
2060
|
});
|
|
1554
2061
|
}
|
|
1555
2062
|
},
|
|
2063
|
+
{ keyCount: 1000 },
|
|
1556
2064
|
);
|
|
1557
2065
|
|
|
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
|
-
);
|