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/additionalInfo.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { fromByteArray, toByteArray } from 'base64-js';
|
|
2
2
|
import { readEffects, writeEffects } from './effectsHelpers';
|
|
3
3
|
import { clamp, createEnum, layerColors, MOCK_HANDLERS, normalizeLabAB } from './helpers';
|
|
4
|
-
import { LayerAdditionalInfo, BezierPath, Psd, BrightnessAdjustment, ExposureAdjustment, VibranceAdjustment, ColorBalanceAdjustment, BlackAndWhiteAdjustment, PhotoFilterAdjustment, ChannelMixerChannel, ChannelMixerAdjustment, PosterizeAdjustment, ThresholdAdjustment, GradientMapAdjustment, CMYK, SelectiveColorAdjustment, ColorLookupAdjustment, LevelsAdjustmentChannel, LevelsAdjustment, CurvesAdjustment, CurvesAdjustmentChannel, HueSaturationAdjustment, HueSaturationAdjustmentChannel, PresetInfo, Color, ColorBalanceValues, WriteOptions, LinkedFile, PlacedLayerType, Warp, KeyDescriptorItem, BooleanOperation, LayerEffectsInfo, Annotation,
|
|
5
|
-
import { PsdReader, readSignature, readUnicodeString, skipBytes, readUint32, readUint8, readFloat64, readUint16, readBytes, readInt16, checkSignature, readFloat32, readFixedPointPath32, readSection, readColor, readInt32, readPascalString, readUnicodeStringWithLength, readAsciiString,
|
|
4
|
+
import { LayerAdditionalInfo, LevelsFilterAdjustment, BezierPath, Psd, BrightnessAdjustment, ExposureAdjustment, VibranceAdjustment, ColorBalanceAdjustment, BlackAndWhiteAdjustment, PhotoFilterAdjustment, ChannelMixerChannel, ChannelMixerAdjustment, PosterizeAdjustment, ThresholdAdjustment, GradientMapAdjustment, CMYK, SelectiveColorAdjustment, ColorLookupAdjustment, LevelsAdjustmentChannel, LevelsAdjustment, CurvesAdjustment, CurvesAdjustmentChannel, HueSaturationAdjustment, HueSaturationAdjustmentChannel, PresetInfo, Color, ColorBalanceValues, WriteOptions, LinkedFile, PlacedLayerType, Warp, KeyDescriptorItem, BooleanOperation, LayerEffectsInfo, Annotation, VectorPathData, AnimationFrame, Timeline, PlacedLayerFilter, UnitsValue, Filter, PlacedLayer, Layer } from './psd';
|
|
5
|
+
import { PsdReader, readSignature, readUnicodeString, skipBytes, readUint32, readUint8, readFloat64, readUint16, readBytes, readInt16, checkSignature, readFloat32, readFixedPointPath32, readSection, readColor, readInt32, readPascalString, readUnicodeStringWithLength, readAsciiString, readPatternSafe, readLayerInfo } from './psdReader';
|
|
6
6
|
import { PsdWriter, writeZeros, writeSignature, writeBytes, writeUint32, writeUint16, writeFloat64, writeUint8, writeInt16, writeFloat32, writeFixedPointPath32, writeUnicodeString, writeSection, writeUnicodeStringWithPadding, writeColor, writePascalString, writeInt32, writePattern, writeUint64 } from './psdWriter';
|
|
7
7
|
import { Annt, BlnM, DescriptorColor, DescriptorUnitsValue, parsePercent, parseUnits, parseUnitsOrNumber, QuiltWarpDescriptor, strokeStyleLineAlignment, strokeStyleLineCapType, strokeStyleLineJoinType, TextDescriptor, textGridding, unitsPercent, unitsValue, WarpDescriptor, warpStyle, writeVersionAndDescriptor, readVersionAndDescriptor, StrokeDescriptor, Ornt, horzVrtcToXY, LmfxDescriptor, Lfx2Descriptor, FrameListDescriptor, TimelineDescriptor, FrameDescriptor, xyToHorzVrtc, serializeEffects, parseEffects, parseColor, serializeColor, serializeVectorContent, parseVectorContent, parseTrackList, serializeTrackList, FractionDescriptor, BlrM, BlrQ, SmBQ, SmBM, DspM, UndA, Cnvr, RplS, SphM, Wvtp, ZZTy, Dstr, Chnl, MztT, Lns, blurType, DfsM, ExtT, ExtR, FlCl, CntE, WndM, Drct, IntE, IntC, FlMd, unitsPercentF, frac, ClrS, descBoundsToBounds, boundsToDescBounds, presetKindType, gradientInterpolationMethodType, PxScDescriptor } from './descriptor';
|
|
8
8
|
import { serializeEngineData, parseEngineData } from './engineData';
|
|
@@ -103,7 +103,8 @@ addHandler(
|
|
|
103
103
|
// require('fs').writeFileSync('after.txt', require('util').inspect(after, false, 99, false), 'utf8');
|
|
104
104
|
|
|
105
105
|
// console.log(require('util').inspect(parseEngineData(text.EngineData), false, 99, true));
|
|
106
|
-
|
|
106
|
+
// descriptor anti-alias value is more reliable than the one from engine data
|
|
107
|
+
target.text = { ...target.text, ...textData, antiAlias: target.text!.antiAlias ?? textData.antiAlias };
|
|
107
108
|
// console.log(require('util').inspect(target.text, false, 99, true));
|
|
108
109
|
}
|
|
109
110
|
|
|
@@ -228,7 +229,7 @@ function writeBezierKnot(writer: PsdWriter, points: number[], width: number, hei
|
|
|
228
229
|
|
|
229
230
|
export const booleanOperations: BooleanOperation[] = ['exclude', 'combine', 'subtract', 'intersect'];
|
|
230
231
|
|
|
231
|
-
export function readVectorMask(reader: PsdReader, vectorMask:
|
|
232
|
+
export function readVectorMask(reader: PsdReader, vectorMask: VectorPathData, width: number, height: number, size: number) {
|
|
232
233
|
const end = reader.offset + size;
|
|
233
234
|
const paths = vectorMask.paths;
|
|
234
235
|
let path: BezierPath | undefined = undefined;
|
|
@@ -283,6 +284,51 @@ export function readVectorMask(reader: PsdReader, vectorMask: LayerVectorMask, w
|
|
|
283
284
|
return paths;
|
|
284
285
|
}
|
|
285
286
|
|
|
287
|
+
// componentIndices - write index of path component for each subpath (Photoshop writes them for layer vector masks)
|
|
288
|
+
export function writeVectorMask(writer: PsdWriter, vectorMask: VectorPathData, width: number, height: number, componentIndices = false) {
|
|
289
|
+
// initial entry
|
|
290
|
+
writeUint16(writer, 6);
|
|
291
|
+
writeZeros(writer, 24);
|
|
292
|
+
|
|
293
|
+
const clipboard = vectorMask.clipboard;
|
|
294
|
+
if (clipboard) {
|
|
295
|
+
writeUint16(writer, 7);
|
|
296
|
+
writeFixedPointPath32(writer, clipboard.top);
|
|
297
|
+
writeFixedPointPath32(writer, clipboard.left);
|
|
298
|
+
writeFixedPointPath32(writer, clipboard.bottom);
|
|
299
|
+
writeFixedPointPath32(writer, clipboard.right);
|
|
300
|
+
writeFixedPointPath32(writer, clipboard.resolution);
|
|
301
|
+
writeZeros(writer, 4);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
writeUint16(writer, 8);
|
|
305
|
+
writeUint16(writer, vectorMask.fillStartsWithAllPixels ? 1 : 0);
|
|
306
|
+
writeZeros(writer, 22);
|
|
307
|
+
|
|
308
|
+
let componentIndex = -1;
|
|
309
|
+
|
|
310
|
+
for (const path of vectorMask.paths) {
|
|
311
|
+
// subpaths without operation are part of the same path component as previous subpath
|
|
312
|
+
if (path.operation || componentIndex < 0) componentIndex++;
|
|
313
|
+
|
|
314
|
+
writeUint16(writer, path.open ? 3 : 0);
|
|
315
|
+
writeUint16(writer, path.knots.length);
|
|
316
|
+
writeUint16(writer, path.operation ? booleanOperations.indexOf(path.operation) : -1); // -1 for undefined
|
|
317
|
+
writeUint16(writer, path.fillRule === 'non-zero' ? 2 : 1);
|
|
318
|
+
writeZeros(writer, 4);
|
|
319
|
+
writeUint32(writer, componentIndices ? componentIndex : 0);
|
|
320
|
+
writeZeros(writer, 10);
|
|
321
|
+
|
|
322
|
+
const linkedKnot = path.open ? 4 : 1;
|
|
323
|
+
const unlinkedKnot = path.open ? 5 : 2;
|
|
324
|
+
|
|
325
|
+
for (const { linked, points } of path.knots) {
|
|
326
|
+
writeUint16(writer, linked ? linkedKnot : unlinkedKnot);
|
|
327
|
+
writeBezierKnot(writer, points, width, height);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
286
332
|
addHandler(
|
|
287
333
|
'vmsk',
|
|
288
334
|
hasKey('vectorMask'),
|
|
@@ -312,45 +358,11 @@ addHandler(
|
|
|
312
358
|
|
|
313
359
|
writeUint32(writer, 3); // version
|
|
314
360
|
writeUint32(writer, flags);
|
|
315
|
-
|
|
316
|
-
// initial entry
|
|
317
|
-
writeUint16(writer, 6);
|
|
318
|
-
writeZeros(writer, 24);
|
|
319
|
-
|
|
320
|
-
const clipboard = vectorMask.clipboard;
|
|
321
|
-
if (clipboard) {
|
|
322
|
-
writeUint16(writer, 7);
|
|
323
|
-
writeFixedPointPath32(writer, clipboard.top);
|
|
324
|
-
writeFixedPointPath32(writer, clipboard.left);
|
|
325
|
-
writeFixedPointPath32(writer, clipboard.bottom);
|
|
326
|
-
writeFixedPointPath32(writer, clipboard.right);
|
|
327
|
-
writeFixedPointPath32(writer, clipboard.resolution);
|
|
328
|
-
writeZeros(writer, 4);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
writeUint16(writer, 8);
|
|
332
|
-
writeUint16(writer, vectorMask.fillStartsWithAllPixels ? 1 : 0);
|
|
333
|
-
writeZeros(writer, 22);
|
|
334
|
-
|
|
335
|
-
for (const path of vectorMask.paths) {
|
|
336
|
-
writeUint16(writer, path.open ? 3 : 0);
|
|
337
|
-
writeUint16(writer, path.knots.length);
|
|
338
|
-
writeUint16(writer, path.operation ? booleanOperations.indexOf(path.operation) : -1); // -1 for undefined
|
|
339
|
-
writeUint16(writer, path.fillRule === 'non-zero' ? 2 : 1);
|
|
340
|
-
writeZeros(writer, 18); // TODO: these are sometimes non-zero
|
|
341
|
-
|
|
342
|
-
const linkedKnot = path.open ? 4 : 1;
|
|
343
|
-
const unlinkedKnot = path.open ? 5 : 2;
|
|
344
|
-
|
|
345
|
-
for (const { linked, points } of path.knots) {
|
|
346
|
-
writeUint16(writer, linked ? linkedKnot : unlinkedKnot);
|
|
347
|
-
writeBezierKnot(writer, points, width, height);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
361
|
+
writeVectorMask(writer, vectorMask, width, height, true);
|
|
350
362
|
},
|
|
351
363
|
);
|
|
352
364
|
|
|
353
|
-
//
|
|
365
|
+
// written instead of 'vmsk' for layers with vector stroke ('vscg' section)
|
|
354
366
|
addHandlerAlias('vsms', 'vmsk');
|
|
355
367
|
// addHandlerAlias('vmsk', 'vsms');
|
|
356
368
|
|
|
@@ -365,7 +377,7 @@ addHandler(
|
|
|
365
377
|
},
|
|
366
378
|
);
|
|
367
379
|
|
|
368
|
-
interface
|
|
380
|
+
export interface KeyDescriptorListDescriptor {
|
|
369
381
|
keyDescriptorList: {
|
|
370
382
|
keyShapeInvalidated?: boolean;
|
|
371
383
|
keyOriginType?: number;
|
|
@@ -395,121 +407,128 @@ interface VogkDescriptor {
|
|
|
395
407
|
}[];
|
|
396
408
|
}
|
|
397
409
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
(
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
410
|
+
export function parseKeyDescriptorList(desc: KeyDescriptorListDescriptor) {
|
|
411
|
+
const keyDescriptorList: KeyDescriptorItem[] = [];
|
|
412
|
+
|
|
413
|
+
for (const i of desc.keyDescriptorList) {
|
|
414
|
+
const item: KeyDescriptorItem = {};
|
|
415
|
+
|
|
416
|
+
if (i.keyShapeInvalidated != null) item.keyShapeInvalidated = i.keyShapeInvalidated;
|
|
417
|
+
if (i.keyOriginType != null) item.keyOriginType = i.keyOriginType;
|
|
418
|
+
if (i.keyOriginResolution != null) item.keyOriginResolution = i.keyOriginResolution;
|
|
419
|
+
if (i.keyOriginShapeBBox) {
|
|
420
|
+
item.keyOriginShapeBoundingBox = {
|
|
421
|
+
top: parseUnitsOrNumber(i.keyOriginShapeBBox['Top ']),
|
|
422
|
+
left: parseUnitsOrNumber(i.keyOriginShapeBBox.Left),
|
|
423
|
+
bottom: parseUnitsOrNumber(i.keyOriginShapeBBox.Btom),
|
|
424
|
+
right: parseUnitsOrNumber(i.keyOriginShapeBBox.Rght),
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
const rectRadii = i.keyOriginRRectRadii;
|
|
428
|
+
if (rectRadii) {
|
|
429
|
+
item.keyOriginRRectRadii = {
|
|
430
|
+
topRight: parseUnits(rectRadii.topRight),
|
|
431
|
+
topLeft: parseUnits(rectRadii.topLeft),
|
|
432
|
+
bottomLeft: parseUnits(rectRadii.bottomLeft),
|
|
433
|
+
bottomRight: parseUnits(rectRadii.bottomRight),
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
const corners = i.keyOriginBoxCorners;
|
|
437
|
+
if (corners) {
|
|
438
|
+
item.keyOriginBoxCorners = [
|
|
439
|
+
{ x: corners.rectangleCornerA.Hrzn, y: corners.rectangleCornerA.Vrtc },
|
|
440
|
+
{ x: corners.rectangleCornerB.Hrzn, y: corners.rectangleCornerB.Vrtc },
|
|
441
|
+
{ x: corners.rectangleCornerC.Hrzn, y: corners.rectangleCornerC.Vrtc },
|
|
442
|
+
{ x: corners.rectangleCornerD.Hrzn, y: corners.rectangleCornerD.Vrtc },
|
|
443
|
+
];
|
|
444
|
+
}
|
|
445
|
+
const trnf = i.Trnf;
|
|
446
|
+
if (trnf) {
|
|
447
|
+
item.transform = [trnf.xx, trnf.xy, trnf.yx, trnf.yy, trnf.tx, trnf.ty];
|
|
448
|
+
}
|
|
407
449
|
|
|
408
|
-
|
|
409
|
-
|
|
450
|
+
keyDescriptorList.push(item);
|
|
451
|
+
}
|
|
410
452
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
if (i.keyOriginResolution != null) item.keyOriginResolution = i.keyOriginResolution;
|
|
414
|
-
if (i.keyOriginShapeBBox) {
|
|
415
|
-
item.keyOriginShapeBoundingBox = {
|
|
416
|
-
top: parseUnitsOrNumber(i.keyOriginShapeBBox['Top ']),
|
|
417
|
-
left: parseUnitsOrNumber(i.keyOriginShapeBBox.Left),
|
|
418
|
-
bottom: parseUnitsOrNumber(i.keyOriginShapeBBox.Btom),
|
|
419
|
-
right: parseUnitsOrNumber(i.keyOriginShapeBBox.Rght),
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
const rectRadii = i.keyOriginRRectRadii;
|
|
423
|
-
if (rectRadii) {
|
|
424
|
-
item.keyOriginRRectRadii = {
|
|
425
|
-
topRight: parseUnits(rectRadii.topRight),
|
|
426
|
-
topLeft: parseUnits(rectRadii.topLeft),
|
|
427
|
-
bottomLeft: parseUnits(rectRadii.bottomLeft),
|
|
428
|
-
bottomRight: parseUnits(rectRadii.bottomRight),
|
|
429
|
-
};
|
|
430
|
-
}
|
|
431
|
-
const corners = i.keyOriginBoxCorners;
|
|
432
|
-
if (corners) {
|
|
433
|
-
item.keyOriginBoxCorners = [
|
|
434
|
-
{ x: corners.rectangleCornerA.Hrzn, y: corners.rectangleCornerA.Vrtc },
|
|
435
|
-
{ x: corners.rectangleCornerB.Hrzn, y: corners.rectangleCornerB.Vrtc },
|
|
436
|
-
{ x: corners.rectangleCornerC.Hrzn, y: corners.rectangleCornerC.Vrtc },
|
|
437
|
-
{ x: corners.rectangleCornerD.Hrzn, y: corners.rectangleCornerD.Vrtc },
|
|
438
|
-
];
|
|
439
|
-
}
|
|
440
|
-
const trnf = i.Trnf;
|
|
441
|
-
if (trnf) {
|
|
442
|
-
item.transform = [trnf.xx, trnf.xy, trnf.yx, trnf.yy, trnf.tx, trnf.ty];
|
|
443
|
-
}
|
|
453
|
+
return keyDescriptorList;
|
|
454
|
+
}
|
|
444
455
|
|
|
445
|
-
|
|
446
|
-
|
|
456
|
+
export function serializeKeyDescriptorList(keyDescriptorList: KeyDescriptorItem[]) {
|
|
457
|
+
const desc: KeyDescriptorListDescriptor = { keyDescriptorList: [] };
|
|
447
458
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
(writer, target) => {
|
|
451
|
-
target;
|
|
452
|
-
const orig = target.vectorOrigination!;
|
|
453
|
-
const desc: VogkDescriptor = { keyDescriptorList: [] };
|
|
459
|
+
for (let i = 0; i < keyDescriptorList.length; i++) {
|
|
460
|
+
const item = keyDescriptorList[i];
|
|
454
461
|
|
|
455
|
-
|
|
456
|
-
const item = orig.keyDescriptorList[i];
|
|
462
|
+
desc.keyDescriptorList.push({} as any); // we're adding keyOriginIndex at the end
|
|
457
463
|
|
|
458
|
-
|
|
464
|
+
const out = desc.keyDescriptorList[desc.keyDescriptorList.length - 1];
|
|
459
465
|
|
|
460
|
-
|
|
466
|
+
if (item.keyOriginType != null) out.keyOriginType = item.keyOriginType;
|
|
467
|
+
if (item.keyOriginResolution != null) out.keyOriginResolution = item.keyOriginResolution;
|
|
461
468
|
|
|
462
|
-
|
|
463
|
-
|
|
469
|
+
const radii = item.keyOriginRRectRadii;
|
|
470
|
+
if (radii) {
|
|
471
|
+
out.keyOriginRRectRadii = {
|
|
472
|
+
unitValueQuadVersion: 1,
|
|
473
|
+
topRight: unitsValue(radii.topRight, 'topRight'),
|
|
474
|
+
topLeft: unitsValue(radii.topLeft, 'topLeft'),
|
|
475
|
+
bottomLeft: unitsValue(radii.bottomLeft, 'bottomLeft'),
|
|
476
|
+
bottomRight: unitsValue(radii.bottomRight, 'bottomRight'),
|
|
477
|
+
};
|
|
478
|
+
}
|
|
464
479
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
480
|
+
const box = item.keyOriginShapeBoundingBox;
|
|
481
|
+
if (box) {
|
|
482
|
+
out.keyOriginShapeBBox = {
|
|
483
|
+
unitValueQuadVersion: 1,
|
|
484
|
+
'Top ': unitsValue(box.top, 'top'),
|
|
485
|
+
Left: unitsValue(box.left, 'left'),
|
|
486
|
+
Btom: unitsValue(box.bottom, 'bottom'),
|
|
487
|
+
Rght: unitsValue(box.right, 'right'),
|
|
488
|
+
};
|
|
489
|
+
}
|
|
475
490
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
}
|
|
491
|
+
const corners = item.keyOriginBoxCorners;
|
|
492
|
+
if (corners && corners.length === 4) {
|
|
493
|
+
out.keyOriginBoxCorners = {
|
|
494
|
+
rectangleCornerA: { Hrzn: corners[0].x, Vrtc: corners[0].y },
|
|
495
|
+
rectangleCornerB: { Hrzn: corners[1].x, Vrtc: corners[1].y },
|
|
496
|
+
rectangleCornerC: { Hrzn: corners[2].x, Vrtc: corners[2].y },
|
|
497
|
+
rectangleCornerD: { Hrzn: corners[3].x, Vrtc: corners[3].y },
|
|
498
|
+
};
|
|
499
|
+
}
|
|
486
500
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
501
|
+
const transform = item.transform;
|
|
502
|
+
if (transform && transform.length === 6) {
|
|
503
|
+
out.Trnf = {
|
|
504
|
+
xx: transform[0],
|
|
505
|
+
xy: transform[1],
|
|
506
|
+
yx: transform[2],
|
|
507
|
+
yy: transform[3],
|
|
508
|
+
tx: transform[4],
|
|
509
|
+
ty: transform[5],
|
|
510
|
+
};
|
|
511
|
+
}
|
|
496
512
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
xx: transform[0],
|
|
501
|
-
xy: transform[1],
|
|
502
|
-
yx: transform[2],
|
|
503
|
-
yy: transform[3],
|
|
504
|
-
tx: transform[4],
|
|
505
|
-
ty: transform[5],
|
|
506
|
-
};
|
|
507
|
-
}
|
|
513
|
+
if (item.keyShapeInvalidated != null) out.keyShapeInvalidated = item.keyShapeInvalidated;
|
|
514
|
+
out.keyOriginIndex = i;
|
|
515
|
+
}
|
|
508
516
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
}
|
|
517
|
+
return desc;
|
|
518
|
+
}
|
|
512
519
|
|
|
520
|
+
addHandler(
|
|
521
|
+
'vogk',
|
|
522
|
+
hasKey('vectorOrigination'),
|
|
523
|
+
(reader, target, left) => {
|
|
524
|
+
if (readInt32(reader) !== 1) throw new Error(`Invalid vogk version`);
|
|
525
|
+
const desc = readVersionAndDescriptor(reader) as KeyDescriptorListDescriptor;
|
|
526
|
+
// console.log(require('util').inspect(desc, false, 99, true));
|
|
527
|
+
target.vectorOrigination = { keyDescriptorList: parseKeyDescriptorList(desc) };
|
|
528
|
+
skipBytes(reader, left());
|
|
529
|
+
},
|
|
530
|
+
(writer, target) => {
|
|
531
|
+
const desc = serializeKeyDescriptorList(target.vectorOrigination!.keyDescriptorList);
|
|
513
532
|
writeInt32(writer, 1); // version
|
|
514
533
|
writeVersionAndDescriptor(writer, '', 'null', desc);
|
|
515
534
|
}
|
|
@@ -517,7 +536,7 @@ addHandler(
|
|
|
517
536
|
|
|
518
537
|
addHandler(
|
|
519
538
|
'lmfx',
|
|
520
|
-
target => target.effects !== undefined && hasMultiEffects(target.effects),
|
|
539
|
+
target => target.effects !== undefined && !isGroupLayer(target) && hasMultiEffects(target.effects),
|
|
521
540
|
(reader, target, left) => {
|
|
522
541
|
const version = readUint32(reader);
|
|
523
542
|
if (version !== 0) throw new Error('Invalid lmfx version');
|
|
@@ -621,7 +640,12 @@ addHandler(
|
|
|
621
640
|
|
|
622
641
|
if (target.id !== undefined) {
|
|
623
642
|
id = target.id;
|
|
624
|
-
|
|
643
|
+
|
|
644
|
+
if (options.layerIds.has(id)) {
|
|
645
|
+
// make sure we don't have duplicate layer ids (and don't take id explicitly set on another layer)
|
|
646
|
+
const explicitIds = getExplicitLayerIds(psd, options);
|
|
647
|
+
while (options.layerIds.has(id) || explicitIds.has(id)) id += 100;
|
|
648
|
+
}
|
|
625
649
|
} else {
|
|
626
650
|
// generate id for layers that don't have it, if it's referenced by other sections
|
|
627
651
|
const explicitIds = getExplicitLayerIds(psd, options);
|
|
@@ -733,15 +757,17 @@ addHandler(
|
|
|
733
757
|
};
|
|
734
758
|
|
|
735
759
|
if (flags & 0x08) target.protected.artboards = true;
|
|
760
|
+
if (flags & 0x80000000) target.protected.all = true;
|
|
736
761
|
},
|
|
737
762
|
(writer, target) => {
|
|
738
763
|
const flags =
|
|
739
764
|
(target.protected!.transparency ? 0x01 : 0) |
|
|
740
765
|
(target.protected!.composite ? 0x02 : 0) |
|
|
741
766
|
(target.protected!.position ? 0x04 : 0) |
|
|
742
|
-
(target.protected!.artboards ? 0x08 : 0)
|
|
767
|
+
(target.protected!.artboards ? 0x08 : 0) |
|
|
768
|
+
(target.protected!.all ? 0x80000000 : 0);
|
|
743
769
|
|
|
744
|
-
writeUint32(writer, flags);
|
|
770
|
+
writeUint32(writer, flags >>> 0);
|
|
745
771
|
},
|
|
746
772
|
);
|
|
747
773
|
|
|
@@ -764,6 +790,62 @@ interface CustomDescriptor {
|
|
|
764
790
|
layerTime?: number;
|
|
765
791
|
}
|
|
766
792
|
|
|
793
|
+
interface GeneratorSettingsDescriptor {
|
|
794
|
+
generatorSettings?: {
|
|
795
|
+
_name: '';
|
|
796
|
+
_classID: string; // 'null' or 'generatorSettings'
|
|
797
|
+
exportAs?: {
|
|
798
|
+
_name: '';
|
|
799
|
+
_classID: 'null';
|
|
800
|
+
exportOption?: string; // JSON
|
|
801
|
+
scaleSettings?: string; // JSON
|
|
802
|
+
};
|
|
803
|
+
generator_45_assets?: { _name: ''; _classID: string; json: string; };
|
|
804
|
+
layerTime?: number;
|
|
805
|
+
crema?: { _name: ''; _classID: string; json: string; };
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
function parseGeneratorSettings(settings: NonNullable<GeneratorSettingsDescriptor['generatorSettings']>) {
|
|
810
|
+
const result: NonNullable<LayerAdditionalInfo['generatorSettings']> = {};
|
|
811
|
+
|
|
812
|
+
if (settings.exportAs) {
|
|
813
|
+
result.exportAs = {};
|
|
814
|
+
if (settings.exportAs.exportOption !== undefined) result.exportAs.exportOption = settings.exportAs.exportOption;
|
|
815
|
+
if (settings.exportAs.scaleSettings !== undefined) result.exportAs.scaleSettings = settings.exportAs.scaleSettings;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
if (settings.generator_45_assets) result.assets = settings.generator_45_assets.json;
|
|
819
|
+
if (settings.layerTime !== undefined) result.layerTime = settings.layerTime;
|
|
820
|
+
if (settings.crema) result.crema = settings.crema.json;
|
|
821
|
+
|
|
822
|
+
return result;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
function serializeGeneratorSettings(generatorSettings: NonNullable<LayerAdditionalInfo['generatorSettings']>): GeneratorSettingsDescriptor {
|
|
826
|
+
const settings: GeneratorSettingsDescriptor['generatorSettings'] = {
|
|
827
|
+
_name: '',
|
|
828
|
+
_classID: generatorSettings.assets !== undefined ? 'generatorSettings' : 'null',
|
|
829
|
+
};
|
|
830
|
+
|
|
831
|
+
if (generatorSettings.exportAs) {
|
|
832
|
+
settings.exportAs = { _name: '', _classID: 'null' };
|
|
833
|
+
if (generatorSettings.exportAs.exportOption !== undefined) settings.exportAs.exportOption = generatorSettings.exportAs.exportOption;
|
|
834
|
+
if (generatorSettings.exportAs.scaleSettings !== undefined) settings.exportAs.scaleSettings = generatorSettings.exportAs.scaleSettings;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
if (generatorSettings.assets !== undefined) settings.generator_45_assets = { _name: '', _classID: 'generatorSettings', json: generatorSettings.assets };
|
|
838
|
+
if (generatorSettings.layerTime !== undefined) settings.layerTime = generatorSettings.layerTime;
|
|
839
|
+
if (generatorSettings.crema !== undefined) settings.crema = { _name: '', _classID: 'null', json: generatorSettings.crema };
|
|
840
|
+
|
|
841
|
+
return { generatorSettings: settings };
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
// document (Psd object) stores generator settings in 'extn' section instead of 'shmd' section
|
|
845
|
+
function isDocument(target: LayerAdditionalInfo) {
|
|
846
|
+
return (target as Psd).width !== undefined;
|
|
847
|
+
}
|
|
848
|
+
|
|
767
849
|
interface CmlsDescriptor {
|
|
768
850
|
origFXRefPoint?: { Hrzn: number; Vrtc: number; };
|
|
769
851
|
LyrI: number;
|
|
@@ -777,112 +859,113 @@ interface CmlsDescriptor {
|
|
|
777
859
|
|
|
778
860
|
addHandler(
|
|
779
861
|
'shmd', // Metadata setting
|
|
780
|
-
target => target.timestamp !== undefined || target.animationFrames !== undefined || target.animationFrameFlags !== undefined || target.timeline !== undefined || target.comps !== undefined
|
|
862
|
+
target => target.timestamp !== undefined || target.animationFrames !== undefined || target.animationFrameFlags !== undefined || target.timeline !== undefined || target.comps !== undefined ||
|
|
863
|
+
(target.generatorSettings !== undefined && !isDocument(target)),
|
|
781
864
|
(reader, target, left) => {
|
|
782
865
|
const count = readUint32(reader);
|
|
783
866
|
|
|
784
867
|
for (let i = 0; i < count; i++) {
|
|
785
|
-
|
|
868
|
+
const signature = readSignature(reader);
|
|
869
|
+
if (signature !== '8BIM' && signature !== '8ELE') throw new Error(`Invalid shmd signature: '${signature}'`); // 8ELE in Photoshop Elements
|
|
786
870
|
const key = readSignature(reader);
|
|
787
871
|
readUint8(reader); // copy
|
|
788
872
|
skipBytes(reader, 3);
|
|
789
873
|
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
if (
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
874
|
+
const end = reader.offset + 4 + reader.view.getUint32(reader.offset);
|
|
875
|
+
|
|
876
|
+
try {
|
|
877
|
+
readSection(reader, 1, left => {
|
|
878
|
+
if (key === 'cust') {
|
|
879
|
+
const desc = readVersionAndDescriptor(reader) as CustomDescriptor;
|
|
880
|
+
// console.log('cust', target.name, require('util').inspect(desc, false, 99, true));
|
|
881
|
+
if (desc.layerTime !== undefined) target.timestamp = desc.layerTime;
|
|
882
|
+
} else if (key === 'mlst') {
|
|
883
|
+
const desc = readVersionAndDescriptor(reader) as FrameListDescriptor;
|
|
884
|
+
// console.log('mlst', target.name, require('util').inspect(desc, false, 99, true));
|
|
885
|
+
|
|
886
|
+
target.animationFrames = [];
|
|
887
|
+
|
|
888
|
+
for (let i = 0; i < desc.LaSt.length; i++) {
|
|
889
|
+
const f = desc.LaSt[i];
|
|
890
|
+
const frame: AnimationFrame = { frames: f.FrLs };
|
|
891
|
+
if (f.enab !== undefined) frame.enable = f.enab;
|
|
892
|
+
if (f.Ofst) frame.offset = horzVrtcToXY(f.Ofst);
|
|
893
|
+
if (f.FXRf) frame.referencePoint = horzVrtcToXY(f.FXRf);
|
|
894
|
+
if (f.Lefx) frame.effects = parseEffects(f.Lefx, !!reader.logMissingFeatures);
|
|
895
|
+
if (f.blendOptions && f.blendOptions.Opct) frame.opacity = parsePercent(f.blendOptions.Opct);
|
|
896
|
+
target.animationFrames.push(frame);
|
|
897
|
+
}
|
|
898
|
+
} else if (key === 'mdyn') {
|
|
899
|
+
// frame flags
|
|
900
|
+
readUint16(reader); // unknown
|
|
901
|
+
const propagate = readUint8(reader);
|
|
902
|
+
const flags = readUint8(reader);
|
|
903
|
+
|
|
904
|
+
target.animationFrameFlags = {
|
|
905
|
+
propagateFrameOne: !propagate,
|
|
906
|
+
unifyLayerPosition: (flags & 1) !== 0,
|
|
907
|
+
unifyLayerStyle: (flags & 2) !== 0,
|
|
908
|
+
unifyLayerVisibility: (flags & 4) !== 0,
|
|
909
|
+
};
|
|
910
|
+
} else if (key === 'tmln') {
|
|
911
|
+
const desc = readVersionAndDescriptor(reader) as TimelineDescriptor;
|
|
912
|
+
const timeScope = desc.timeScope;
|
|
913
|
+
// console.log('tmln', target.name, target.id, require('util').inspect(desc, false, 99, true));
|
|
914
|
+
|
|
915
|
+
const timeline: Timeline = {
|
|
916
|
+
start: frac(timeScope.Strt),
|
|
917
|
+
duration: frac(timeScope.duration),
|
|
918
|
+
inTime: frac(timeScope.inTime),
|
|
919
|
+
outTime: frac(timeScope.outTime),
|
|
920
|
+
autoScope: desc.autoScope,
|
|
921
|
+
audioLevel: desc.audioLevel,
|
|
922
|
+
};
|
|
836
923
|
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
924
|
+
if (desc.trackList) {
|
|
925
|
+
timeline.tracks = parseTrackList(desc.trackList, !!reader.logMissingFeatures);
|
|
926
|
+
}
|
|
840
927
|
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
928
|
+
target.timeline = timeline;
|
|
929
|
+
// console.log('tmln:result', target.name, target.id, require('util').inspect(timeline, false, 99, true));
|
|
930
|
+
} else if (key === 'cmls') {
|
|
931
|
+
const desc = readVersionAndDescriptor(reader) as CmlsDescriptor;
|
|
932
|
+
// console.log('cmls', require('util').inspect(desc, false, 99, true));
|
|
846
933
|
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
934
|
+
target.comps = {
|
|
935
|
+
settings: [],
|
|
936
|
+
};
|
|
850
937
|
|
|
851
|
-
|
|
938
|
+
if (desc.origFXRefPoint) target.comps.originalEffectsReferencePoint = { x: desc.origFXRefPoint.Hrzn, y: desc.origFXRefPoint.Vrtc };
|
|
852
939
|
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
generatorSettings
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
};
|
|
866
|
-
layerTime: number;
|
|
867
|
-
};
|
|
940
|
+
for (const item of desc.layerSettings) {
|
|
941
|
+
target.comps.settings.push({ compList: item.compList });
|
|
942
|
+
const t = target.comps.settings[target.comps.settings.length - 1];
|
|
943
|
+
if ('enab' in item) t.enabled = item.enab;
|
|
944
|
+
if (item.Ofst) t.offset = { x: item.Ofst.Hrzn, y: item.Ofst.Vrtc };
|
|
945
|
+
if (item.FXRefPoint) t.effectsReferencePoint = { x: item.FXRefPoint.Hrzn, y: item.FXRefPoint.Vrtc };
|
|
946
|
+
}
|
|
947
|
+
} else if (key === 'extn') {
|
|
948
|
+
const desc = readVersionAndDescriptor(reader) as GeneratorSettingsDescriptor;
|
|
949
|
+
if (desc.generatorSettings) target.generatorSettings = parseGeneratorSettings(desc.generatorSettings);
|
|
950
|
+
} else {
|
|
951
|
+
reader.logMissingFeatures && reader.log('Unhandled "shmd" section key', key);
|
|
868
952
|
}
|
|
869
953
|
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
skipBytes(reader, left());
|
|
879
|
-
});
|
|
954
|
+
skipBytes(reader, left());
|
|
955
|
+
});
|
|
956
|
+
} catch (e) {
|
|
957
|
+
// failure to read one item shouldn't drop all other items
|
|
958
|
+
if (reader.throwForMissingFeatures) throw e;
|
|
959
|
+
reader.logMissingFeatures && reader.log(`Failed to read "shmd" section key ${key}: ${(e as Error).message}`);
|
|
960
|
+
reader.offset = end;
|
|
961
|
+
}
|
|
880
962
|
}
|
|
881
963
|
|
|
882
964
|
skipBytes(reader, left());
|
|
883
965
|
},
|
|
884
966
|
(writer, target, _, options) => {
|
|
885
967
|
const { animationFrames, animationFrameFlags, timestamp, timeline, comps } = target;
|
|
968
|
+
const generatorSettings = isDocument(target) ? undefined : target.generatorSettings;
|
|
886
969
|
|
|
887
970
|
let count = 0;
|
|
888
971
|
if (animationFrames) count++;
|
|
@@ -890,6 +973,7 @@ addHandler(
|
|
|
890
973
|
if (timeline) count++;
|
|
891
974
|
if (timestamp !== undefined) count++;
|
|
892
975
|
if (comps) count++;
|
|
976
|
+
if (generatorSettings) count++;
|
|
893
977
|
writeUint32(writer, count);
|
|
894
978
|
|
|
895
979
|
if (animationFrames) {
|
|
@@ -910,8 +994,8 @@ addHandler(
|
|
|
910
994
|
frame.FrLs = f.frames;
|
|
911
995
|
if (f.offset) frame.Ofst = xyToHorzVrtc(f.offset);
|
|
912
996
|
if (f.referencePoint) frame.FXRf = xyToHorzVrtc(f.referencePoint);
|
|
913
|
-
if (f.effects) frame.Lefx = serializeEffects(f.effects, false,
|
|
914
|
-
if (f.opacity !== undefined) frame.blendOptions = { Opct:
|
|
997
|
+
if (f.effects) frame.Lefx = serializeEffects(f.effects, false, hasMultiEffects(f.effects));
|
|
998
|
+
if (f.opacity !== undefined) frame.blendOptions = { Opct: unitsPercentF(f.opacity) };
|
|
915
999
|
desc.LaSt.push(frame);
|
|
916
1000
|
}
|
|
917
1001
|
|
|
@@ -1005,6 +1089,16 @@ addHandler(
|
|
|
1005
1089
|
writeVersionAndDescriptor(writer, '', 'null', desc);
|
|
1006
1090
|
}, true);
|
|
1007
1091
|
}
|
|
1092
|
+
|
|
1093
|
+
if (generatorSettings) {
|
|
1094
|
+
writeSignature(writer, '8BIM');
|
|
1095
|
+
writeSignature(writer, 'extn');
|
|
1096
|
+
writeUint8(writer, 0); // copy (always false)
|
|
1097
|
+
writeZeros(writer, 3);
|
|
1098
|
+
writeSection(writer, 2, () => {
|
|
1099
|
+
writeVersionAndDescriptor(writer, '', 'null', serializeGeneratorSettings(generatorSettings));
|
|
1100
|
+
}, true);
|
|
1101
|
+
}
|
|
1008
1102
|
},
|
|
1009
1103
|
);
|
|
1010
1104
|
|
|
@@ -1096,7 +1190,7 @@ addHandler(
|
|
|
1096
1190
|
lineAlignment: strokeStyleLineAlignment.decode(desc.strokeStyleLineAlignment),
|
|
1097
1191
|
scaleLock: desc.strokeStyleScaleLock,
|
|
1098
1192
|
strokeAdjust: desc.strokeStyleStrokeAdjust,
|
|
1099
|
-
lineDashSet: desc.strokeStyleLineDashSet.map(parseUnits),
|
|
1193
|
+
lineDashSet: (desc.strokeStyleLineDashSet || []).map(parseUnits),
|
|
1100
1194
|
blendMode: BlnM.decode(desc.strokeStyleBlendMode),
|
|
1101
1195
|
opacity: parsePercent(desc.strokeStyleOpacity),
|
|
1102
1196
|
content: parseVectorContent(desc.strokeStyleContent),
|
|
@@ -1135,8 +1229,8 @@ interface ArtbDescriptor {
|
|
|
1135
1229
|
artboardRect: { 'Top ': number; Left: number; Btom: number; Rght: number; };
|
|
1136
1230
|
guideIndeces: any[];
|
|
1137
1231
|
artboardPresetName: string;
|
|
1138
|
-
'Clr '
|
|
1139
|
-
artboardBackgroundType
|
|
1232
|
+
'Clr '?: DescriptorColor;
|
|
1233
|
+
artboardBackgroundType?: number;
|
|
1140
1234
|
}
|
|
1141
1235
|
|
|
1142
1236
|
addHandler(
|
|
@@ -1149,10 +1243,11 @@ addHandler(
|
|
|
1149
1243
|
rect: { top: rect['Top '], left: rect.Left, bottom: rect.Btom, right: rect.Rght },
|
|
1150
1244
|
guideIndices: desc.guideIndeces,
|
|
1151
1245
|
presetName: desc.artboardPresetName,
|
|
1152
|
-
color: parseColor(desc['Clr ']),
|
|
1153
|
-
backgroundType: desc.artboardBackgroundType,
|
|
1154
1246
|
};
|
|
1155
1247
|
|
|
1248
|
+
if (desc['Clr ']) target.artboard.color = parseColor(desc['Clr ']);
|
|
1249
|
+
if (desc.artboardBackgroundType !== undefined) target.artboard.backgroundType = desc.artboardBackgroundType;
|
|
1250
|
+
|
|
1156
1251
|
skipBytes(reader, left());
|
|
1157
1252
|
},
|
|
1158
1253
|
(writer, target) => {
|
|
@@ -1299,13 +1394,13 @@ function encodeWarp(warp: Warp): WarpDescriptor {
|
|
|
1299
1394
|
|
|
1300
1395
|
addHandler(
|
|
1301
1396
|
'PlLd',
|
|
1302
|
-
|
|
1397
|
+
target => !!target.placedLayer && !target.placedLayer.external, // linked smart objects are stored in SoLE
|
|
1303
1398
|
(reader, target, left) => {
|
|
1304
1399
|
if (readSignature(reader) !== 'plcL') throw new Error(`Invalid PlLd signature`);
|
|
1305
1400
|
if (readInt32(reader) !== 3) throw new Error(`Invalid PlLd version`);
|
|
1306
1401
|
const id = readPascalString(reader, 1);
|
|
1307
1402
|
const pageNumber = readInt32(reader);
|
|
1308
|
-
const totalPages = readInt32(reader); //
|
|
1403
|
+
const totalPages = readInt32(reader); // for multi-page documents (PDF)
|
|
1309
1404
|
readInt32(reader); // anitAliasPolicy 16
|
|
1310
1405
|
const placedLayerType = readInt32(reader); // 0 = unknown, 1 = vector, 2 = raster, 3 = image stack
|
|
1311
1406
|
if (!placedLayerTypes[placedLayerType]) throw new Error('Invalid PlLd type');
|
|
@@ -1857,7 +1952,7 @@ type SoLdDescriptorFilterItem = {
|
|
|
1857
1952
|
'Fl ': string;
|
|
1858
1953
|
};
|
|
1859
1954
|
} | {
|
|
1860
|
-
filterID: 991 | 943; //
|
|
1955
|
+
filterID: 991 | 943; // 943 in older Photoshop versions (2021), 991 in newer ones, settings are the same
|
|
1861
1956
|
Fltr: {
|
|
1862
1957
|
_name: 'Rigid Transform';
|
|
1863
1958
|
_classID: 'rigidTransform';
|
|
@@ -2036,7 +2131,7 @@ type SoLdDescriptorFilterItem = {
|
|
|
2036
2131
|
Fltr: {
|
|
2037
2132
|
_name: 'Curves';
|
|
2038
2133
|
_classID: 'Crvs';
|
|
2039
|
-
presetKind
|
|
2134
|
+
presetKind?: string; // 'presetKindType.presetKindCustom';
|
|
2040
2135
|
Adjs?: {
|
|
2041
2136
|
_name: '';
|
|
2042
2137
|
_classID: 'CrvA';
|
|
@@ -2045,6 +2140,14 @@ type SoLdDescriptorFilterItem = {
|
|
|
2045
2140
|
Mpng?: number[];
|
|
2046
2141
|
}[];
|
|
2047
2142
|
};
|
|
2143
|
+
} | {
|
|
2144
|
+
filterID: 1282829427;
|
|
2145
|
+
Fltr: {
|
|
2146
|
+
_name: 'Levels';
|
|
2147
|
+
_classID: 'Lvls';
|
|
2148
|
+
presetKind?: string; // 'presetKindType.presetKindCustom';
|
|
2149
|
+
Adjs?: FilterLevelsAdjustment[];
|
|
2150
|
+
};
|
|
2048
2151
|
} | {
|
|
2049
2152
|
filterID: 1231976050;
|
|
2050
2153
|
} | {
|
|
@@ -2066,6 +2169,18 @@ interface FilterCurvesCurvePoint {
|
|
|
2066
2169
|
Cnty?: boolean;
|
|
2067
2170
|
}
|
|
2068
2171
|
|
|
2172
|
+
interface FilterLevelsAdjustment {
|
|
2173
|
+
_name: '';
|
|
2174
|
+
_classID: 'LvlA';
|
|
2175
|
+
Chnl: string[]; // 'Chnl.Cmps' | 'Chnl.Rd ' | 'Chnl.Grn ' | 'Chnl.Bl '
|
|
2176
|
+
Inpt?: number[]; // shadow, highlight
|
|
2177
|
+
Otpt?: number[]; // shadow, highlight
|
|
2178
|
+
'Gmm '?: number;
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
const levelsFilterKeys = ['_name', '_classID', 'presetKind', 'Adjs'];
|
|
2182
|
+
const levelsFilterAdjustmentKeys = ['_name', '_classID', 'Chnl', 'Inpt', 'Otpt', 'Gmm '];
|
|
2183
|
+
|
|
2069
2184
|
interface SoLdDescriptorFilter {
|
|
2070
2185
|
_name: '',
|
|
2071
2186
|
_classID: 'filterFXStyle',
|
|
@@ -2129,7 +2244,7 @@ function pointToHrznVrtc(point: { x: UnitsValue; y: UnitsValue; }): HrznVrtcDesc
|
|
|
2129
2244
|
};
|
|
2130
2245
|
}
|
|
2131
2246
|
|
|
2132
|
-
function parseFilterFXItem(f: SoLdDescriptorFilterItem,
|
|
2247
|
+
function parseFilterFXItem(f: SoLdDescriptorFilterItem, reader: PsdReader): Filter | undefined {
|
|
2133
2248
|
const base: Omit<Filter, 'type' | 'filter'> = {
|
|
2134
2249
|
name: f['Nm '],
|
|
2135
2250
|
opacity: parsePercent(f.blendOptions.Opct),
|
|
@@ -2209,7 +2324,7 @@ function parseFilterFXItem(f: SoLdDescriptorFilterItem, options: ReadOptions): F
|
|
|
2209
2324
|
undefinedAreas: UndA.decode(f.Fltr.UndA),
|
|
2210
2325
|
displacementFile: {
|
|
2211
2326
|
signature: f.Fltr.DspF.sig,
|
|
2212
|
-
path: f.Fltr.DspF.path,
|
|
2327
|
+
path: f.Fltr.DspF.path,
|
|
2213
2328
|
},
|
|
2214
2329
|
},
|
|
2215
2330
|
};
|
|
@@ -2544,8 +2659,7 @@ function parseFilterFXItem(f: SoLdDescriptorFilterItem, options: ReadOptions): F
|
|
|
2544
2659
|
],
|
|
2545
2660
|
puppetShapeList: (f.Fltr.puppetShapeList || []).map(p => ({
|
|
2546
2661
|
rigidType: p.rigidType,
|
|
2547
|
-
//
|
|
2548
|
-
// TODO: VrsN
|
|
2662
|
+
// VrsM / VrsN - version (always 1.0)
|
|
2549
2663
|
originalVertexArray: uint8ToPoints(p.originalVertexArray),
|
|
2550
2664
|
deformedVertexArray: uint8ToPoints(p.deformedVertexArray),
|
|
2551
2665
|
indexArray: Array.from(uint8ToUint32(p.indexArray)),
|
|
@@ -2664,7 +2778,8 @@ function parseFilterFXItem(f: SoLdDescriptorFilterItem, options: ReadOptions): F
|
|
|
2664
2778
|
...base,
|
|
2665
2779
|
type: 'curves',
|
|
2666
2780
|
filter: {
|
|
2667
|
-
|
|
2781
|
+
// Photoshop doesn't write preset kind for filters with explicit values
|
|
2782
|
+
...(f.Fltr.presetKind !== undefined ? { presetKind: presetKindType.decode(f.Fltr.presetKind) } : {}),
|
|
2668
2783
|
...(f.Fltr.Adjs ? {
|
|
2669
2784
|
adjustments: f.Fltr.Adjs.map(a => {
|
|
2670
2785
|
const channels = a.Chnl.map(Chnl.decode);
|
|
@@ -2687,6 +2802,40 @@ function parseFilterFXItem(f: SoLdDescriptorFilterItem, options: ReadOptions): F
|
|
|
2687
2802
|
},
|
|
2688
2803
|
};
|
|
2689
2804
|
};
|
|
2805
|
+
case 'Lvls': {
|
|
2806
|
+
const adjustments = f.Fltr.Adjs;
|
|
2807
|
+
const isKnown = (obj: object, keys: string[]) => Object.keys(obj).every(k => keys.indexOf(k) !== -1);
|
|
2808
|
+
|
|
2809
|
+
// skip filter with settings we don't know about
|
|
2810
|
+
if (!isKnown(f.Fltr, levelsFilterKeys) || (adjustments && !adjustments.every(a => isKnown(a, levelsFilterAdjustmentKeys)))) {
|
|
2811
|
+
if (reader.throwForMissingFeatures) throw new Error('Unknown levels filter settings');
|
|
2812
|
+
reader.logMissingFeatures && reader.log(`Skipped levels smart filter with unknown settings`);
|
|
2813
|
+
return undefined;
|
|
2814
|
+
}
|
|
2815
|
+
|
|
2816
|
+
return {
|
|
2817
|
+
...base,
|
|
2818
|
+
type: 'levels',
|
|
2819
|
+
filter: {
|
|
2820
|
+
...(f.Fltr.presetKind !== undefined ? { presetKind: presetKindType.decode(f.Fltr.presetKind) } : {}),
|
|
2821
|
+
...(adjustments ? {
|
|
2822
|
+
adjustments: adjustments.map(a => {
|
|
2823
|
+
const result: LevelsFilterAdjustment = { channels: a.Chnl.map(Chnl.decode) };
|
|
2824
|
+
if (a.Inpt) {
|
|
2825
|
+
result.shadowInput = a.Inpt[0];
|
|
2826
|
+
result.highlightInput = a.Inpt[1];
|
|
2827
|
+
}
|
|
2828
|
+
if (a.Otpt) {
|
|
2829
|
+
result.shadowOutput = a.Otpt[0];
|
|
2830
|
+
result.highlightOutput = a.Otpt[1];
|
|
2831
|
+
}
|
|
2832
|
+
if (a['Gmm '] !== undefined) result.midtoneInput = a['Gmm '];
|
|
2833
|
+
return result;
|
|
2834
|
+
}),
|
|
2835
|
+
} : {}),
|
|
2836
|
+
},
|
|
2837
|
+
};
|
|
2838
|
+
};
|
|
2690
2839
|
case 'BrgC': {
|
|
2691
2840
|
return {
|
|
2692
2841
|
...base,
|
|
@@ -2699,10 +2848,11 @@ function parseFilterFXItem(f: SoLdDescriptorFilterItem, options: ReadOptions): F
|
|
|
2699
2848
|
};
|
|
2700
2849
|
};
|
|
2701
2850
|
default:
|
|
2702
|
-
if (
|
|
2851
|
+
if (reader.throwForMissingFeatures) {
|
|
2703
2852
|
// console.log('FILTER', require('util').inspect(f, false, 99, true));
|
|
2704
2853
|
throw new Error(`Unknown filter classId: ${(f as any).Fltr._classID}`);
|
|
2705
2854
|
}
|
|
2855
|
+
reader.logMissingFeatures && reader.log(`Unhandled smart filter: ${(f as any).Fltr._classID} (${f['Nm ']})`);
|
|
2706
2856
|
return undefined;
|
|
2707
2857
|
}
|
|
2708
2858
|
} else {
|
|
@@ -2721,22 +2871,24 @@ function parseFilterFXItem(f: SoLdDescriptorFilterItem, options: ReadOptions): F
|
|
|
2721
2871
|
case 1314149187: return { ...base, type: 'ntsc colors' };
|
|
2722
2872
|
case 1231976050: return { ...base, type: 'invert' }
|
|
2723
2873
|
default:
|
|
2724
|
-
if (
|
|
2874
|
+
if (reader.throwForMissingFeatures) {
|
|
2725
2875
|
// console.log('FILTER', require('util').inspect(f, false, 99, true));
|
|
2726
2876
|
throw new Error(`Unknown filterID: ${(f as any).filterID}`);
|
|
2727
2877
|
}
|
|
2878
|
+
reader.logMissingFeatures && reader.log(`Unhandled smart filter: ${(f as any).filterID} (${f['Nm ']})`);
|
|
2879
|
+
return undefined;
|
|
2728
2880
|
}
|
|
2729
2881
|
}
|
|
2730
2882
|
}
|
|
2731
2883
|
|
|
2732
|
-
function parseFilterFX(desc: SoLdDescriptorFilter,
|
|
2884
|
+
function parseFilterFX(desc: SoLdDescriptorFilter, reader: PsdReader): PlacedLayerFilter {
|
|
2733
2885
|
return {
|
|
2734
2886
|
enabled: desc.enab,
|
|
2735
2887
|
validAtPosition: desc.validAtPosition,
|
|
2736
2888
|
maskEnabled: desc.filterMaskEnable,
|
|
2737
2889
|
maskLinked: desc.filterMaskLinked,
|
|
2738
2890
|
maskExtendWithWhite: desc.filterMaskExtendWithWhite,
|
|
2739
|
-
list: desc.filterFXList.map(x => parseFilterFXItem(x,
|
|
2891
|
+
list: desc.filterFXList.map(x => parseFilterFXItem(x, reader)).filter((x): x is Filter => !!x),
|
|
2740
2892
|
};
|
|
2741
2893
|
}
|
|
2742
2894
|
|
|
@@ -3275,14 +3427,14 @@ function serializeFilterFXItem(f: Filter): SoLdDescriptorFilterItem {
|
|
|
3275
3427
|
Fltr: {
|
|
3276
3428
|
_name: 'Rigid Transform',
|
|
3277
3429
|
_classID: 'rigidTransform',
|
|
3278
|
-
'null': ['Ordn.Trgt'], //
|
|
3430
|
+
'null': ['Ordn.Trgt'], // target layer reference
|
|
3279
3431
|
rigidType: f.filter.rigidType,
|
|
3280
3432
|
puppetShapeList: (f.filter.puppetShapeList || []).map(p => ({
|
|
3281
3433
|
_name: '',
|
|
3282
3434
|
_classID: 'puppetShape',
|
|
3283
3435
|
rigidType: p.rigidType,
|
|
3284
|
-
VrsM: 1, //
|
|
3285
|
-
VrsN: 0, //
|
|
3436
|
+
VrsM: 1, // version major
|
|
3437
|
+
VrsN: 0, // version minor
|
|
3286
3438
|
originalVertexArray: toUint8(new Float32Array(pointsToArray(p.originalVertexArray))),
|
|
3287
3439
|
deformedVertexArray: toUint8(new Float32Array(pointsToArray(p.deformedVertexArray))),
|
|
3288
3440
|
indexArray: toUint8(new Uint32Array(p.indexArray)),
|
|
@@ -3397,7 +3549,7 @@ function serializeFilterFXItem(f: Filter): SoLdDescriptorFilterItem {
|
|
|
3397
3549
|
Fltr: {
|
|
3398
3550
|
_name: 'Curves',
|
|
3399
3551
|
_classID: 'Crvs',
|
|
3400
|
-
presetKind: presetKindType.encode(f.filter.presetKind),
|
|
3552
|
+
...(f.filter.presetKind !== undefined ? { presetKind: presetKindType.encode(f.filter.presetKind) } : {}),
|
|
3401
3553
|
...(f.filter.adjustments ? {
|
|
3402
3554
|
Adjs: f.filter.adjustments.map(a => 'curve' in a ? {
|
|
3403
3555
|
_name: '',
|
|
@@ -3420,6 +3572,28 @@ function serializeFilterFXItem(f: Filter): SoLdDescriptorFilterItem {
|
|
|
3420
3572
|
},
|
|
3421
3573
|
filterID: 1131574899,
|
|
3422
3574
|
};
|
|
3575
|
+
case 'levels': return {
|
|
3576
|
+
...base,
|
|
3577
|
+
Fltr: {
|
|
3578
|
+
_name: 'Levels',
|
|
3579
|
+
_classID: 'Lvls',
|
|
3580
|
+
...(f.filter.presetKind !== undefined ? { presetKind: presetKindType.encode(f.filter.presetKind) } : {}),
|
|
3581
|
+
...(f.filter.adjustments ? {
|
|
3582
|
+
Adjs: f.filter.adjustments.map(a => {
|
|
3583
|
+
const adjustment: FilterLevelsAdjustment = { _name: '', _classID: 'LvlA', Chnl: a.channels.map(Chnl.encode) };
|
|
3584
|
+
if (a.shadowInput !== undefined || a.highlightInput !== undefined) {
|
|
3585
|
+
adjustment.Inpt = [Math.round(a.shadowInput ?? 0), Math.round(a.highlightInput ?? 255)];
|
|
3586
|
+
}
|
|
3587
|
+
if (a.shadowOutput !== undefined || a.highlightOutput !== undefined) {
|
|
3588
|
+
adjustment.Otpt = [Math.round(a.shadowOutput ?? 0), Math.round(a.highlightOutput ?? 255)];
|
|
3589
|
+
}
|
|
3590
|
+
if (a.midtoneInput !== undefined) adjustment['Gmm '] = a.midtoneInput;
|
|
3591
|
+
return adjustment;
|
|
3592
|
+
}),
|
|
3593
|
+
} : {}),
|
|
3594
|
+
},
|
|
3595
|
+
filterID: 1282829427,
|
|
3596
|
+
};
|
|
3423
3597
|
case 'brightness/contrast': return {
|
|
3424
3598
|
...base,
|
|
3425
3599
|
Fltr: {
|
|
@@ -3463,8 +3637,8 @@ interface SoLdDescriptor {
|
|
|
3463
3637
|
'Sz ': { _name: '', _classID: 'Pnt ', Wdth: number; Hght: number; };
|
|
3464
3638
|
Rslt: DescriptorUnitsValue;
|
|
3465
3639
|
filterFX?: SoLdDescriptorFilter;
|
|
3466
|
-
comp?: number;
|
|
3467
|
-
compInfo?: { compID: number; originalCompID: number; };
|
|
3640
|
+
comp?: number;
|
|
3641
|
+
compInfo?: { compID: number; originalCompID: number; };
|
|
3468
3642
|
Impr?: {}; // ???
|
|
3469
3643
|
ClMg?: { // TODO: support this ?
|
|
3470
3644
|
_name: '';
|
|
@@ -3512,7 +3686,7 @@ function getWarpFromPlacedLayer(placed: PlacedLayer): Warp {
|
|
|
3512
3686
|
|
|
3513
3687
|
addHandler(
|
|
3514
3688
|
'SoLd',
|
|
3515
|
-
|
|
3689
|
+
target => !!target.placedLayer && !target.placedLayer.external, // linked smart objects are stored in SoLE
|
|
3516
3690
|
(reader, target, left) => {
|
|
3517
3691
|
if (readSignature(reader) !== 'soLD') throw new Error(`Invalid SoLd type`);
|
|
3518
3692
|
const version = readInt32(reader);
|
|
@@ -3573,6 +3747,12 @@ addHandler(
|
|
|
3573
3747
|
const placedType = placedLayerTypes.indexOf(placed.type);
|
|
3574
3748
|
if (placedType === -1) throw new Error('Invalid placedLayer type');
|
|
3575
3749
|
|
|
3750
|
+
const warp = getWarpFromPlacedLayer(placed);
|
|
3751
|
+
// size of linked image matches warp bounds if it's not provided
|
|
3752
|
+
const bounds = warp.bounds;
|
|
3753
|
+
const width = placed.width || (bounds ? bounds.right.value - bounds.left.value : 0);
|
|
3754
|
+
const height = placed.height || (bounds ? bounds.bottom.value - bounds.top.value : 0);
|
|
3755
|
+
|
|
3576
3756
|
const desc: SoLdDescriptor = {
|
|
3577
3757
|
Idnt: placed.id,
|
|
3578
3758
|
placed: placed.placed ?? placed.id,
|
|
@@ -3587,12 +3767,12 @@ addHandler(
|
|
|
3587
3767
|
Trnf: placed.transform,
|
|
3588
3768
|
nonAffineTransform: placed.nonAffineTransform ?? placed.transform,
|
|
3589
3769
|
// quiltWarp: {} as any,
|
|
3590
|
-
warp: encodeWarp(
|
|
3770
|
+
warp: encodeWarp(warp),
|
|
3591
3771
|
'Sz ': {
|
|
3592
3772
|
_name: '',
|
|
3593
3773
|
_classID: 'Pnt ',
|
|
3594
|
-
Wdth:
|
|
3595
|
-
Hght:
|
|
3774
|
+
Wdth: width,
|
|
3775
|
+
Hght: height,
|
|
3596
3776
|
},
|
|
3597
3777
|
Rslt: placed.resolution ? unitsValue(placed.resolution, 'resolution') : { units: 'Density', value: 72 },
|
|
3598
3778
|
};
|
|
@@ -3610,15 +3790,6 @@ addHandler(
|
|
|
3610
3790
|
};
|
|
3611
3791
|
}
|
|
3612
3792
|
|
|
3613
|
-
// TODO:
|
|
3614
|
-
// desc.comp = -1;
|
|
3615
|
-
// desc.compInfo = { _name: '', _classID: 'null', compID: -1, originalCompID: -1 } as any;
|
|
3616
|
-
// desc.ClMg = {
|
|
3617
|
-
// _name: '',
|
|
3618
|
-
// _classID: 'ClMg',
|
|
3619
|
-
// placedLayerOCIOConversion: 'placedLayerOCIOConversion.placedLayerOCIOConvertEmbedded'
|
|
3620
|
-
// } as any;
|
|
3621
|
-
|
|
3622
3793
|
// if (JSON.stringify(t) !== JSON.stringify(desc)) {
|
|
3623
3794
|
// console.log('read', require('util').inspect(t, false, 99, true));
|
|
3624
3795
|
// console.log('write', require('util').inspect(desc, false, 99, true));
|
|
@@ -3650,7 +3821,17 @@ addHandler(
|
|
|
3650
3821
|
},
|
|
3651
3822
|
);
|
|
3652
3823
|
|
|
3653
|
-
|
|
3824
|
+
const soLdHandler = infoHandlersMap['SoLd'];
|
|
3825
|
+
|
|
3826
|
+
addHandler(
|
|
3827
|
+
'SoLE', // linked smart object (Photoshop doesn't write PlLd for these)
|
|
3828
|
+
target => !!target.placedLayer?.external,
|
|
3829
|
+
(reader, target, left, psd, imageResources) => {
|
|
3830
|
+
soLdHandler.read(reader, target, left, psd, imageResources);
|
|
3831
|
+
target.placedLayer!.external = true;
|
|
3832
|
+
},
|
|
3833
|
+
soLdHandler.write,
|
|
3834
|
+
);
|
|
3654
3835
|
|
|
3655
3836
|
addHandler(
|
|
3656
3837
|
'fxrp',
|
|
@@ -3667,25 +3848,50 @@ addHandler(
|
|
|
3667
3848
|
},
|
|
3668
3849
|
);
|
|
3669
3850
|
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
)
|
|
3851
|
+
function createGlobalLayerInfoHandler(key: string, bitsPerChannel: number | undefined) {
|
|
3852
|
+
addHandler(
|
|
3853
|
+
key,
|
|
3854
|
+
() => false,
|
|
3855
|
+
(reader, _target, left, psd, imageResources) => {
|
|
3856
|
+
// Photoshop writes empty (or 8bit compatibility) layer info section alongside Lr16/Lr32 in 16/32bit documents,
|
|
3857
|
+
// layers from global info with matching bit depth replace layers from that section, other ones are only used
|
|
3858
|
+
// if the section is empty
|
|
3859
|
+
if (!reader.layersInGlobalInfo && (psd.bitsPerChannel === bitsPerChannel || !psd.children?.length)) {
|
|
3860
|
+
const sectionLayers = psd.children;
|
|
3861
|
+
psd.children = [];
|
|
3862
|
+
|
|
3863
|
+
try {
|
|
3864
|
+
readLayerInfo(reader, psd, imageResources);
|
|
3865
|
+
} catch (e) {
|
|
3866
|
+
psd.children = sectionLayers; // keep layers from layer info section if global info is broken
|
|
3867
|
+
throw e;
|
|
3868
|
+
}
|
|
3679
3869
|
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3870
|
+
reader.layersInGlobalInfo = true;
|
|
3871
|
+
}
|
|
3872
|
+
|
|
3873
|
+
skipBytes(reader, left());
|
|
3874
|
+
},
|
|
3875
|
+
() => { },
|
|
3876
|
+
);
|
|
3877
|
+
}
|
|
3878
|
+
|
|
3879
|
+
createGlobalLayerInfoHandler('Lr16', 16);
|
|
3880
|
+
createGlobalLayerInfoHandler('Lr32', 32);
|
|
3881
|
+
createGlobalLayerInfoHandler('Layr', undefined);
|
|
3882
|
+
|
|
3883
|
+
// merged transparency markers, first extra channel of composite image is transparency
|
|
3884
|
+
for (const key of ['Mtrn', 'Mt16', 'Mt32']) {
|
|
3885
|
+
addHandler(
|
|
3886
|
+
key,
|
|
3887
|
+
() => false,
|
|
3888
|
+
(reader, _target, left) => {
|
|
3889
|
+
reader.globalAlpha = true;
|
|
3890
|
+
skipBytes(reader, left());
|
|
3891
|
+
},
|
|
3892
|
+
() => { },
|
|
3893
|
+
);
|
|
3894
|
+
}
|
|
3689
3895
|
|
|
3690
3896
|
addHandler(
|
|
3691
3897
|
'LMsk',
|
|
@@ -3720,11 +3926,12 @@ if (MOCK_HANDLERS) {
|
|
|
3720
3926
|
);
|
|
3721
3927
|
} else {
|
|
3722
3928
|
addHandler(
|
|
3723
|
-
'Patt', //
|
|
3929
|
+
'Patt', // 'Pat2' and 'Pat3' (16 and 32 bit documents) are aliases, patterns are always written as 8 bit
|
|
3724
3930
|
target => !!(target.patterns && target.patterns.length > 0),
|
|
3725
3931
|
(reader, target, left) => {
|
|
3726
3932
|
while (left() > 0) {
|
|
3727
|
-
const pattern =
|
|
3933
|
+
const pattern = readPatternSafe(reader);
|
|
3934
|
+
if (!pattern) continue;
|
|
3728
3935
|
if (target.patterns === undefined) target.patterns = [];
|
|
3729
3936
|
target.patterns.push(pattern);
|
|
3730
3937
|
}
|
|
@@ -3954,6 +4161,11 @@ interface LinkedFileDescriptor {
|
|
|
3954
4161
|
relPath: string;
|
|
3955
4162
|
}
|
|
3956
4163
|
|
|
4164
|
+
function readFileTypeSignature(reader: PsdReader) {
|
|
4165
|
+
const value = readSignature(reader);
|
|
4166
|
+
return /^[\0 ]*$/.test(value) ? '' : value.trim(); // empty value is stored as spaces or zeros
|
|
4167
|
+
}
|
|
4168
|
+
|
|
3957
4169
|
function createLnkHandler(tag: string) {
|
|
3958
4170
|
addHandler(
|
|
3959
4171
|
tag,
|
|
@@ -3978,8 +4190,8 @@ function createLnkHandler(tag: string) {
|
|
|
3978
4190
|
const id = readPascalString(reader, 1);
|
|
3979
4191
|
const name = readUnicodeString(reader);
|
|
3980
4192
|
|
|
3981
|
-
const fileType =
|
|
3982
|
-
const fileCreator =
|
|
4193
|
+
const fileType = readFileTypeSignature(reader); // ' ' if empty
|
|
4194
|
+
const fileCreator = readFileTypeSignature(reader); // ' ' or '\0\0\0\0' if empty
|
|
3983
4195
|
const dataSize = readLength64(reader);
|
|
3984
4196
|
const hasFileOpenDescriptor = readUint8(reader);
|
|
3985
4197
|
const fileOpenDescriptor = hasFileOpenDescriptor ? readVersionAndDescriptor(reader) as FileOpenDescriptor : undefined;
|
|
@@ -4137,28 +4349,55 @@ addHandlerAlias('lnk3', 'lnk2');
|
|
|
4137
4349
|
|
|
4138
4350
|
interface PthsDescriptor {
|
|
4139
4351
|
pathList: {
|
|
4352
|
+
_name: '';
|
|
4140
4353
|
_classID: 'pathInfoClass';
|
|
4141
4354
|
pathUnicodeName: string;
|
|
4142
|
-
pathSymmetryClass
|
|
4355
|
+
pathSymmetryClass?: {
|
|
4356
|
+
_name: '';
|
|
4143
4357
|
_classID: 'pathSymmetryClass';
|
|
4144
4358
|
pathSymmetryMode: string; // 'pathSymmetryModeEnum.pathSymmetryModeBasicPath'
|
|
4145
4359
|
};
|
|
4146
4360
|
}[];
|
|
4147
4361
|
}
|
|
4148
4362
|
|
|
4363
|
+
// unicode names of saved paths (path data is stored in image resources 2000-2998)
|
|
4149
4364
|
addHandler(
|
|
4150
4365
|
'pths',
|
|
4151
|
-
|
|
4152
|
-
(reader,
|
|
4366
|
+
target => (target as Psd).imageResources?.savedPaths !== undefined,
|
|
4367
|
+
(reader, _target, left, psd) => {
|
|
4153
4368
|
const desc = readVersionAndDescriptor(reader, true) as PthsDescriptor;
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4369
|
+
const imageResources = psd.imageResources ??= {};
|
|
4370
|
+
const savedPaths = imageResources.savedPaths ??= [];
|
|
4371
|
+
|
|
4372
|
+
for (let i = 0; i < desc.pathList.length && i < savedPaths.length; i++) {
|
|
4373
|
+
const { pathUnicodeName, pathSymmetryClass } = desc.pathList[i];
|
|
4374
|
+
const path = savedPaths[i];
|
|
4375
|
+
|
|
4376
|
+
// clipping path is referenced by (non-unicode) name
|
|
4377
|
+
if (imageResources.clippingPath && imageResources.clippingPath.name === path.name) {
|
|
4378
|
+
imageResources.clippingPath.name = pathUnicodeName;
|
|
4379
|
+
}
|
|
4380
|
+
|
|
4381
|
+
path.name = pathUnicodeName;
|
|
4382
|
+
if (pathSymmetryClass) path.symmetryMode = pathSymmetryClass.pathSymmetryMode.split('.')[1];
|
|
4383
|
+
}
|
|
4384
|
+
|
|
4385
|
+
skipBytes(reader, left());
|
|
4158
4386
|
},
|
|
4159
|
-
(writer, _target) => {
|
|
4387
|
+
(writer, _target, psd) => {
|
|
4160
4388
|
const desc: PthsDescriptor = {
|
|
4161
|
-
pathList:
|
|
4389
|
+
pathList: psd.imageResources!.savedPaths!.map(path => ({
|
|
4390
|
+
_name: '',
|
|
4391
|
+
_classID: 'pathInfoClass',
|
|
4392
|
+
pathUnicodeName: path.name,
|
|
4393
|
+
...(path.symmetryMode ? {
|
|
4394
|
+
pathSymmetryClass: {
|
|
4395
|
+
_name: '',
|
|
4396
|
+
_classID: 'pathSymmetryClass',
|
|
4397
|
+
pathSymmetryMode: `pathSymmetryModeEnum.${path.symmetryMode}`,
|
|
4398
|
+
},
|
|
4399
|
+
} : {}),
|
|
4400
|
+
})),
|
|
4162
4401
|
};
|
|
4163
4402
|
|
|
4164
4403
|
writeVersionAndDescriptor(writer, '', 'pathsDataClass', desc);
|
|
@@ -4173,15 +4412,8 @@ addHandler(
|
|
|
4173
4412
|
);
|
|
4174
4413
|
|
|
4175
4414
|
addHandler(
|
|
4176
|
-
'lfxs',
|
|
4177
|
-
|
|
4178
|
-
// NOTE: this might be insufficient
|
|
4179
|
-
// target => target.effects !== undefined && (
|
|
4180
|
-
// !!target.effects.dropShadow?.some(e => e.choke) ||
|
|
4181
|
-
// !!target.effects.innerShadow?.some(e => e.choke) ||
|
|
4182
|
-
// !!target.effects.outerGlow?.choke ||
|
|
4183
|
-
// !!target.effects.innerGlow?.choke
|
|
4184
|
-
// ),
|
|
4415
|
+
'lfxs', // effects of group layers (instead of 'lfx2' / 'lmfx')
|
|
4416
|
+
target => target.effects !== undefined && isGroupLayer(target),
|
|
4185
4417
|
(reader, target, left) => {
|
|
4186
4418
|
const version = readUint32(reader);
|
|
4187
4419
|
if (version !== 0) throw new Error(`Invalid lfxs version`);
|
|
@@ -4238,6 +4470,12 @@ function readLevelsChannel(reader: PsdReader): LevelsAdjustmentChannel {
|
|
|
4238
4470
|
return { shadowInput, highlightInput, shadowOutput, highlightOutput, midtoneInput };
|
|
4239
4471
|
}
|
|
4240
4472
|
|
|
4473
|
+
const defaultLevelsChannel: LevelsAdjustmentChannel = { shadowInput: 0, highlightInput: 255, shadowOutput: 0, highlightOutput: 255, midtoneInput: 1 };
|
|
4474
|
+
|
|
4475
|
+
function isDefaultLevelsChannel(c: LevelsAdjustmentChannel) {
|
|
4476
|
+
return c.shadowInput === 0 && c.highlightInput === 255 && c.shadowOutput === 0 && c.highlightOutput === 255 && c.midtoneInput === 1;
|
|
4477
|
+
}
|
|
4478
|
+
|
|
4241
4479
|
function writeLevelsChannel(writer: PsdWriter, channel: LevelsAdjustmentChannel) {
|
|
4242
4480
|
writeInt16(writer, channel.shadowInput);
|
|
4243
4481
|
writeInt16(writer, channel.highlightInput);
|
|
@@ -4252,62 +4490,49 @@ addHandler(
|
|
|
4252
4490
|
(reader, target, left) => {
|
|
4253
4491
|
if (readUint16(reader) !== 2) throw new Error('Invalid levl version');
|
|
4254
4492
|
|
|
4255
|
-
const
|
|
4493
|
+
const channels: LevelsAdjustmentChannel[] = [];
|
|
4256
4494
|
|
|
4257
|
-
for (let i = 0; i < 29 && left(); i++) {
|
|
4258
|
-
|
|
4495
|
+
for (let i = 0; i < 29 && left() >= 10; i++) {
|
|
4496
|
+
channels.push(readLevelsChannel(reader));
|
|
4259
4497
|
}
|
|
4260
4498
|
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
target.adjustment = {
|
|
4264
|
-
...target.adjustment as PresetInfo,
|
|
4265
|
-
type: 'levels',
|
|
4266
|
-
rgb,
|
|
4267
|
-
red,
|
|
4268
|
-
green,
|
|
4269
|
-
blue,
|
|
4270
|
-
};
|
|
4271
|
-
|
|
4272
|
-
if (left()) {
|
|
4499
|
+
if (left() >= 8) {
|
|
4273
4500
|
if (readSignature(reader) !== 'Lvls') throw new Error(`Invalid levl signature`);
|
|
4274
4501
|
if (readUint16(reader) !== 3) throw new Error(`Invalid levl version`);
|
|
4275
4502
|
|
|
4276
4503
|
const totalChannelRecords = readUint16(reader);
|
|
4277
4504
|
|
|
4278
|
-
|
|
4279
|
-
|
|
4505
|
+
while (channels.length < totalChannelRecords && left() >= 10) {
|
|
4506
|
+
channels.push(readLevelsChannel(reader));
|
|
4280
4507
|
}
|
|
4281
4508
|
}
|
|
4282
4509
|
|
|
4510
|
+
// records for all possible channels are stored, skip unused ones
|
|
4511
|
+
while (channels.length && isDefaultLevelsChannel(channels[channels.length - 1])) channels.pop();
|
|
4512
|
+
|
|
4513
|
+
target.adjustment = {
|
|
4514
|
+
...target.adjustment as PresetInfo,
|
|
4515
|
+
type: 'levels',
|
|
4516
|
+
channels,
|
|
4517
|
+
};
|
|
4518
|
+
|
|
4283
4519
|
skipBytes(reader, left());
|
|
4284
4520
|
},
|
|
4285
4521
|
(writer, target) => {
|
|
4286
|
-
const
|
|
4287
|
-
const defaultChannel = {
|
|
4288
|
-
shadowInput: 0,
|
|
4289
|
-
highlightInput: 255,
|
|
4290
|
-
shadowOutput: 0,
|
|
4291
|
-
highlightOutput: 255,
|
|
4292
|
-
midtoneInput: 1,
|
|
4293
|
-
};
|
|
4522
|
+
const channels = (target.adjustment as LevelsAdjustment).channels || [];
|
|
4294
4523
|
|
|
4295
4524
|
writeUint16(writer, 2); // version
|
|
4296
|
-
writeLevelsChannel(writer, info.rgb || defaultChannel);
|
|
4297
|
-
writeLevelsChannel(writer, info.red || defaultChannel);
|
|
4298
|
-
writeLevelsChannel(writer, info.green || defaultChannel);
|
|
4299
|
-
writeLevelsChannel(writer, info.blue || defaultChannel);
|
|
4300
4525
|
|
|
4301
|
-
for (let i = 0; i <
|
|
4302
|
-
writeLevelsChannel(writer,
|
|
4526
|
+
for (let i = 0; i < 29; i++) {
|
|
4527
|
+
writeLevelsChannel(writer, channels[i] || defaultLevelsChannel);
|
|
4303
4528
|
}
|
|
4304
4529
|
|
|
4305
4530
|
writeSignature(writer, 'Lvls');
|
|
4306
4531
|
writeUint16(writer, 3); // version
|
|
4307
4532
|
writeUint16(writer, 62); // total count
|
|
4308
4533
|
|
|
4309
|
-
for (let i =
|
|
4310
|
-
writeLevelsChannel(writer,
|
|
4534
|
+
for (let i = 29; i < 62; i++) {
|
|
4535
|
+
writeLevelsChannel(writer, channels[i] || defaultLevelsChannel);
|
|
4311
4536
|
}
|
|
4312
4537
|
},
|
|
4313
4538
|
);
|
|
@@ -4341,17 +4566,20 @@ addHandler(
|
|
|
4341
4566
|
readUint8(reader);
|
|
4342
4567
|
if (readUint16(reader) !== 1) throw new Error('Invalid curv version');
|
|
4343
4568
|
readUint16(reader);
|
|
4344
|
-
const
|
|
4345
|
-
const
|
|
4569
|
+
const channelsMask = readUint16(reader);
|
|
4570
|
+
const channels: CurvesAdjustmentChannel[] = [];
|
|
4346
4571
|
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4572
|
+
for (let i = 0; i < 16; i++) {
|
|
4573
|
+
if (channelsMask & (1 << i)) {
|
|
4574
|
+
while (channels.length < i) channels.push([]);
|
|
4575
|
+
channels.push(readCurveChannel(reader));
|
|
4576
|
+
}
|
|
4577
|
+
}
|
|
4351
4578
|
|
|
4352
4579
|
target.adjustment = {
|
|
4353
4580
|
...target.adjustment as PresetInfo,
|
|
4354
|
-
|
|
4581
|
+
type: 'curves',
|
|
4582
|
+
channels,
|
|
4355
4583
|
};
|
|
4356
4584
|
|
|
4357
4585
|
// ignoring, duplicate information
|
|
@@ -4374,35 +4602,33 @@ addHandler(
|
|
|
4374
4602
|
skipBytes(reader, left());
|
|
4375
4603
|
},
|
|
4376
4604
|
(writer, target) => {
|
|
4377
|
-
const
|
|
4378
|
-
const
|
|
4379
|
-
let
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4605
|
+
const channels = ((target.adjustment as CurvesAdjustment).channels || []).slice(0, 16);
|
|
4606
|
+
const used: number[] = [];
|
|
4607
|
+
let channelsMask = 0;
|
|
4608
|
+
|
|
4609
|
+
for (let i = 0; i < channels.length; i++) {
|
|
4610
|
+
if (channels[i]?.length) {
|
|
4611
|
+
channelsMask |= 1 << i;
|
|
4612
|
+
used.push(i);
|
|
4613
|
+
}
|
|
4614
|
+
}
|
|
4386
4615
|
|
|
4387
4616
|
writeUint8(writer, 0);
|
|
4388
4617
|
writeUint16(writer, 1); // version
|
|
4389
4618
|
writeUint16(writer, 0);
|
|
4390
|
-
writeUint16(writer,
|
|
4619
|
+
writeUint16(writer, channelsMask);
|
|
4391
4620
|
|
|
4392
|
-
|
|
4393
|
-
if (red && red.length) writeCurveChannel(writer, red);
|
|
4394
|
-
if (green && green.length) writeCurveChannel(writer, green);
|
|
4395
|
-
if (blue && blue.length) writeCurveChannel(writer, blue);
|
|
4621
|
+
for (const i of used) writeCurveChannel(writer, channels[i]);
|
|
4396
4622
|
|
|
4397
4623
|
writeSignature(writer, 'Crv ');
|
|
4398
4624
|
writeUint16(writer, 4); // version
|
|
4399
4625
|
writeUint16(writer, 0);
|
|
4400
|
-
writeUint16(writer,
|
|
4626
|
+
writeUint16(writer, used.length);
|
|
4401
4627
|
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4628
|
+
for (const i of used) {
|
|
4629
|
+
writeUint16(writer, i);
|
|
4630
|
+
writeCurveChannel(writer, channels[i]);
|
|
4631
|
+
}
|
|
4406
4632
|
},
|
|
4407
4633
|
);
|
|
4408
4634
|
|
|
@@ -4487,10 +4713,26 @@ addHandler(
|
|
|
4487
4713
|
(reader, target, left) => {
|
|
4488
4714
|
if (readUint16(reader) !== 2) throw new Error('Invalid hue2 version');
|
|
4489
4715
|
|
|
4716
|
+
const colorize = !!readUint8(reader);
|
|
4717
|
+
skipBytes(reader, 1);
|
|
4718
|
+
const colorizeHue = readInt16(reader); // stored as -180..180
|
|
4719
|
+
const colorizeSaturation = readInt16(reader);
|
|
4720
|
+
const colorizeLightness = readInt16(reader);
|
|
4721
|
+
|
|
4490
4722
|
target.adjustment = {
|
|
4491
4723
|
...target.adjustment as PresetInfo,
|
|
4492
4724
|
type: 'hue/saturation',
|
|
4493
|
-
|
|
4725
|
+
colorize,
|
|
4726
|
+
colorization: {
|
|
4727
|
+
hue: colorizeHue < 0 ? colorizeHue + 360 : colorizeHue,
|
|
4728
|
+
saturation: colorizeSaturation,
|
|
4729
|
+
lightness: colorizeLightness,
|
|
4730
|
+
},
|
|
4731
|
+
master: {
|
|
4732
|
+
hue: readInt16(reader),
|
|
4733
|
+
saturation: readInt16(reader),
|
|
4734
|
+
lightness: readInt16(reader),
|
|
4735
|
+
},
|
|
4494
4736
|
reds: readHueChannel(reader),
|
|
4495
4737
|
yellows: readHueChannel(reader),
|
|
4496
4738
|
greens: readHueChannel(reader),
|
|
@@ -4505,7 +4747,15 @@ addHandler(
|
|
|
4505
4747
|
const info = target.adjustment as HueSaturationAdjustment;
|
|
4506
4748
|
|
|
4507
4749
|
writeUint16(writer, 2); // version
|
|
4508
|
-
|
|
4750
|
+
writeUint8(writer, info.colorize ? 1 : 0);
|
|
4751
|
+
writeUint8(writer, 0);
|
|
4752
|
+
const colorizeHue = Math.round(info.colorization?.hue ?? 0);
|
|
4753
|
+
writeInt16(writer, colorizeHue > 180 ? colorizeHue - 360 : colorizeHue);
|
|
4754
|
+
writeInt16(writer, Math.round(info.colorization?.saturation ?? 0));
|
|
4755
|
+
writeInt16(writer, Math.round(info.colorization?.lightness ?? 0));
|
|
4756
|
+
writeInt16(writer, Math.round(info.master?.hue ?? 0));
|
|
4757
|
+
writeInt16(writer, Math.round(info.master?.saturation ?? 0));
|
|
4758
|
+
writeInt16(writer, Math.round(info.master?.lightness ?? 0));
|
|
4509
4759
|
writeHueChannel(writer, info.reds);
|
|
4510
4760
|
writeHueChannel(writer, info.yellows);
|
|
4511
4761
|
writeHueChannel(writer, info.greens);
|
|
@@ -4619,16 +4869,17 @@ addHandler(
|
|
|
4619
4869
|
if (version === 2) {
|
|
4620
4870
|
color = readColor(reader);
|
|
4621
4871
|
} else { // version 3
|
|
4622
|
-
//
|
|
4623
|
-
//
|
|
4624
|
-
|
|
4625
|
-
const
|
|
4626
|
-
const
|
|
4627
|
-
|
|
4872
|
+
// XYZ color relative to D50 white point, 32768 = 1.0 (not documented, matches colors of Photoshop presets),
|
|
4873
|
+
// converted to Lab (same scale as readColor()) so it can be written back as version 2
|
|
4874
|
+
const x = readInt32(reader) / 32768;
|
|
4875
|
+
const y = readInt32(reader) / 32768;
|
|
4876
|
+
const z = readInt32(reader) / 32768;
|
|
4877
|
+
if (!(x >= 0 && y >= 0 && z >= 0 && x <= 2 && y <= 2 && z <= 2)) throw new Error('Invalid phfl XYZ color');
|
|
4878
|
+
const f = (t: number) => t > 216 / 24389 ? Math.cbrt(t) : (24389 / 27 * t + 16) / 116;
|
|
4628
4879
|
color = {
|
|
4629
|
-
l:
|
|
4630
|
-
a: normalizeLabAB(
|
|
4631
|
-
b: normalizeLabAB(
|
|
4880
|
+
l: Math.max(0, 116 * f(y) - 16) / 100,
|
|
4881
|
+
a: normalizeLabAB(500 * (f(x) - f(y))),
|
|
4882
|
+
b: normalizeLabAB(200 * (f(y) - f(z))),
|
|
4632
4883
|
};
|
|
4633
4884
|
}
|
|
4634
4885
|
|
|
@@ -4844,7 +5095,7 @@ addHandler(
|
|
|
4844
5095
|
const hasMethod = !!readUint8(reader); reader.offset--;
|
|
4845
5096
|
if (hasMethod) {
|
|
4846
5097
|
const method = readSignature(reader);
|
|
4847
|
-
info.method = gradientInterpolationMethodType.decode(method);
|
|
5098
|
+
info.method = gradientInterpolationMethodType.decode(`gradientInterpolationMethodType.${method}`);
|
|
4848
5099
|
}
|
|
4849
5100
|
|
|
4850
5101
|
info.name = readUnicodeString(reader);
|
|
@@ -4917,7 +5168,7 @@ addHandler(
|
|
|
4917
5168
|
writeUint8(writer, info.dither ? 1 : 0);
|
|
4918
5169
|
|
|
4919
5170
|
if (info.method !== undefined) {
|
|
4920
|
-
writeSignature(writer, gradientInterpolationMethodType.encode(info.method));
|
|
5171
|
+
writeSignature(writer, gradientInterpolationMethodType.encode(info.method).split('.')[1]);
|
|
4921
5172
|
}
|
|
4922
5173
|
|
|
4923
5174
|
writeUnicodeStringWithPadding(writer, info.name || '');
|
|
@@ -5334,14 +5585,14 @@ addHandler(
|
|
|
5334
5585
|
|
|
5335
5586
|
interface ArtdDescriptor {
|
|
5336
5587
|
'Cnt ': number;
|
|
5337
|
-
autoExpandOffset
|
|
5338
|
-
origin
|
|
5339
|
-
autoExpandEnabled
|
|
5340
|
-
autoNestEnabled
|
|
5341
|
-
autoPositionEnabled
|
|
5588
|
+
autoExpandOffset?: { Hrzn: number; Vrtc: number; };
|
|
5589
|
+
origin?: { Hrzn: number; Vrtc: number; };
|
|
5590
|
+
autoExpandEnabled?: boolean;
|
|
5591
|
+
autoNestEnabled?: boolean;
|
|
5592
|
+
autoPositionEnabled?: boolean;
|
|
5342
5593
|
shrinkwrapOnSaveEnabled?: boolean;
|
|
5343
|
-
docDefaultNewArtboardBackgroundColor
|
|
5344
|
-
docDefaultNewArtboardBackgroundType
|
|
5594
|
+
docDefaultNewArtboardBackgroundColor?: DescriptorColor;
|
|
5595
|
+
docDefaultNewArtboardBackgroundType?: number;
|
|
5345
5596
|
}
|
|
5346
5597
|
|
|
5347
5598
|
addHandler(
|
|
@@ -5349,17 +5600,15 @@ addHandler(
|
|
|
5349
5600
|
target => (target as Psd).artboards !== undefined,
|
|
5350
5601
|
(reader, target, left) => {
|
|
5351
5602
|
const desc = readVersionAndDescriptor(reader) as ArtdDescriptor;
|
|
5352
|
-
(target as Psd).artboards = {
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
docDefaultNewArtboardBackgroundType: desc.docDefaultNewArtboardBackgroundType,
|
|
5362
|
-
};
|
|
5603
|
+
const artboards: Psd['artboards'] = (target as Psd).artboards = { count: desc['Cnt '] };
|
|
5604
|
+
if (desc.autoExpandOffset) artboards.autoExpandOffset = { horizontal: desc.autoExpandOffset.Hrzn, vertical: desc.autoExpandOffset.Vrtc };
|
|
5605
|
+
if (desc.origin) artboards.origin = { horizontal: desc.origin.Hrzn, vertical: desc.origin.Vrtc };
|
|
5606
|
+
if (desc.autoExpandEnabled !== undefined) artboards.autoExpandEnabled = desc.autoExpandEnabled;
|
|
5607
|
+
if (desc.autoNestEnabled !== undefined) artboards.autoNestEnabled = desc.autoNestEnabled;
|
|
5608
|
+
if (desc.autoPositionEnabled !== undefined) artboards.autoPositionEnabled = desc.autoPositionEnabled;
|
|
5609
|
+
artboards.shrinkwrapOnSaveEnabled = !!desc.shrinkwrapOnSaveEnabled;
|
|
5610
|
+
if (desc.docDefaultNewArtboardBackgroundColor) artboards.docDefaultNewArtboardBackgroundColor = parseColor(desc.docDefaultNewArtboardBackgroundColor);
|
|
5611
|
+
if (desc.docDefaultNewArtboardBackgroundType !== undefined) artboards.docDefaultNewArtboardBackgroundType = desc.docDefaultNewArtboardBackgroundType;
|
|
5363
5612
|
|
|
5364
5613
|
skipBytes(reader, left());
|
|
5365
5614
|
},
|
|
@@ -5380,13 +5629,17 @@ addHandler(
|
|
|
5380
5629
|
},
|
|
5381
5630
|
);
|
|
5382
5631
|
|
|
5632
|
+
function isGroupLayer(target: LayerAdditionalInfo) {
|
|
5633
|
+
return !!(target as Layer).children;
|
|
5634
|
+
}
|
|
5635
|
+
|
|
5383
5636
|
export function hasMultiEffects(effects: LayerEffectsInfo) {
|
|
5384
5637
|
return Object.keys(effects).map(key => (effects as any)[key]).some(v => Array.isArray(v) && v.length > 1);
|
|
5385
5638
|
}
|
|
5386
5639
|
|
|
5387
5640
|
addHandler(
|
|
5388
5641
|
'lfx2',
|
|
5389
|
-
target => target.effects !== undefined && !hasMultiEffects(target.effects),
|
|
5642
|
+
target => target.effects !== undefined && !isGroupLayer(target) && !hasMultiEffects(target.effects),
|
|
5390
5643
|
(reader, target, left) => {
|
|
5391
5644
|
const version = readUint32(reader);
|
|
5392
5645
|
if (version !== 0) throw new Error(`Invalid lfx2 version`);
|
|
@@ -5394,8 +5647,7 @@ addHandler(
|
|
|
5394
5647
|
const desc: Lfx2Descriptor & LmfxDescriptor = readVersionAndDescriptor(reader);
|
|
5395
5648
|
// console.log('READ', require('util').inspect(desc, false, 99, true));
|
|
5396
5649
|
|
|
5397
|
-
//
|
|
5398
|
-
// discard if read in 'lrFX' section
|
|
5650
|
+
// discard if read in 'lrFX' section ('lmfx' and 'lfx2' are never stored together)
|
|
5399
5651
|
target.effects = parseEffects(desc, !!reader.logMissingFeatures);
|
|
5400
5652
|
|
|
5401
5653
|
skipBytes(reader, left());
|
|
@@ -5469,26 +5721,17 @@ addHandler(
|
|
|
5469
5721
|
},
|
|
5470
5722
|
);
|
|
5471
5723
|
|
|
5472
|
-
|
|
5473
|
-
generatorSettings: {
|
|
5474
|
-
generator_45_assets: { json: string; };
|
|
5475
|
-
layerTime: number;
|
|
5476
|
-
};
|
|
5477
|
-
}
|
|
5478
|
-
|
|
5479
|
-
// extension settings ?, ignore it
|
|
5724
|
+
// generator settings of the document (layers store them in 'shmd' section)
|
|
5480
5725
|
addHandler(
|
|
5481
5726
|
'extn',
|
|
5482
|
-
target =>
|
|
5483
|
-
(reader, target) => {
|
|
5484
|
-
const desc
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
if (MOCK_HANDLERS) (target as any)._extn = desc;
|
|
5727
|
+
target => target.generatorSettings !== undefined && isDocument(target),
|
|
5728
|
+
(reader, target, left) => {
|
|
5729
|
+
const desc = readVersionAndDescriptor(reader) as GeneratorSettingsDescriptor;
|
|
5730
|
+
if (desc.generatorSettings) target.generatorSettings = parseGeneratorSettings(desc.generatorSettings);
|
|
5731
|
+
skipBytes(reader, left());
|
|
5488
5732
|
},
|
|
5489
5733
|
(writer, target) => {
|
|
5490
|
-
|
|
5491
|
-
if (MOCK_HANDLERS) writeVersionAndDescriptor(writer, '', 'null', (target as any)._extn);
|
|
5734
|
+
writeVersionAndDescriptor(writer, '', 'null', serializeGeneratorSettings(target.generatorSettings!));
|
|
5492
5735
|
},
|
|
5493
5736
|
);
|
|
5494
5737
|
|