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.
Files changed (103) hide show
  1. package/.v8-cache/v24.7.0-x64-ef5a0af0/03e19709 +0 -0
  2. package/.v8-cache/v24.7.0-x64-ef5a0af0/05135617 +0 -0
  3. package/.v8-cache/v24.7.0-x64-ef5a0af0/1130885f +0 -0
  4. package/.v8-cache/v24.7.0-x64-ef5a0af0/226e2801 +0 -0
  5. package/.v8-cache/v24.7.0-x64-ef5a0af0/2a19133e +0 -0
  6. package/.v8-cache/v24.7.0-x64-ef5a0af0/4511bacf +0 -0
  7. package/.v8-cache/v24.7.0-x64-ef5a0af0/68416cb2 +0 -0
  8. package/.v8-cache/v24.7.0-x64-ef5a0af0/71df9cb3 +0 -0
  9. package/.v8-cache/v24.7.0-x64-ef5a0af0/7ea06cea +0 -0
  10. package/.v8-cache/v24.7.0-x64-ef5a0af0/932f0770 +0 -0
  11. package/.v8-cache/v24.7.0-x64-ef5a0af0/acc36e66 +0 -0
  12. package/.v8-cache/v24.7.0-x64-ef5a0af0/b3c2fab7 +0 -0
  13. package/.v8-cache/v24.7.0-x64-ef5a0af0/c314aece +0 -0
  14. package/.v8-cache/v24.7.0-x64-ef5a0af0/cfc49f4f +0 -0
  15. package/.v8-cache/v24.7.0-x64-ef5a0af0/cff9434a +0 -0
  16. package/.v8-cache/v24.7.0-x64-ef5a0af0/dc86f000 +0 -0
  17. package/.v8-cache/v24.7.0-x64-ef5a0af0/e03e2acd +0 -0
  18. package/.v8-cache/v24.7.0-x64-ef5a0af0/e6745c2b +0 -0
  19. package/.v8-cache/v24.7.0-x64-ef5a0af0/e6b99945 +0 -0
  20. package/.v8-cache/v24.7.0-x64-ef5a0af0/eeeebdfe +0 -0
  21. package/.v8-cache/v24.7.0-x64-ef5a0af0/f6fd8125 +0 -0
  22. package/CHANGELOG.md +78 -0
  23. package/LICENSE +5 -0
  24. package/README.md +3 -6
  25. package/README_PSD.md +242 -81
  26. package/dist/abr.d.ts +11 -4
  27. package/dist/abr.js +357 -230
  28. package/dist/abr.js.map +1 -1
  29. package/dist/additionalInfo.d.ts +54 -2
  30. package/dist/additionalInfo.js +487 -306
  31. package/dist/additionalInfo.js.map +1 -1
  32. package/dist/ase.d.ts +6 -1
  33. package/dist/ase.js +61 -61
  34. package/dist/ase.js.map +1 -1
  35. package/dist/bundle.js +1937 -1115
  36. package/dist/descriptor.d.ts +14 -8
  37. package/dist/descriptor.js +202 -186
  38. package/dist/descriptor.js.map +1 -1
  39. package/dist/helpers.d.ts +1 -1
  40. package/dist/helpers.js +4 -2
  41. package/dist/helpers.js.map +1 -1
  42. package/dist/imageResources.d.ts +15 -3
  43. package/dist/imageResources.js +503 -142
  44. package/dist/imageResources.js.map +1 -1
  45. package/dist/jpeg.js +8 -8
  46. package/dist/jpeg.js.map +1 -1
  47. package/dist/psd.d.ts +148 -17
  48. package/dist/psd.js.map +1 -1
  49. package/dist/psdReader.d.ts +10 -0
  50. package/dist/psdReader.js +278 -166
  51. package/dist/psdReader.js.map +1 -1
  52. package/dist/psdWriter.d.ts +1 -0
  53. package/dist/psdWriter.js +77 -62
  54. package/dist/psdWriter.js.map +1 -1
  55. package/dist/text.js +17 -6
  56. package/dist/text.js.map +1 -1
  57. package/dist/utf8.js +0 -3
  58. package/dist/utf8.js.map +1 -1
  59. package/dist-es/abr.d.ts +11 -4
  60. package/dist-es/abr.js +357 -230
  61. package/dist-es/abr.js.map +1 -1
  62. package/dist-es/additionalInfo.d.ts +54 -2
  63. package/dist-es/additionalInfo.js +487 -306
  64. package/dist-es/additionalInfo.js.map +1 -1
  65. package/dist-es/ase.d.ts +6 -1
  66. package/dist-es/ase.js +61 -61
  67. package/dist-es/ase.js.map +1 -1
  68. package/dist-es/descriptor.d.ts +14 -8
  69. package/dist-es/descriptor.js +202 -186
  70. package/dist-es/descriptor.js.map +1 -1
  71. package/dist-es/helpers.d.ts +1 -1
  72. package/dist-es/helpers.js +4 -2
  73. package/dist-es/helpers.js.map +1 -1
  74. package/dist-es/imageResources.d.ts +15 -3
  75. package/dist-es/imageResources.js +503 -142
  76. package/dist-es/imageResources.js.map +1 -1
  77. package/dist-es/jpeg.js +8 -8
  78. package/dist-es/jpeg.js.map +1 -1
  79. package/dist-es/psd.d.ts +148 -17
  80. package/dist-es/psd.js.map +1 -1
  81. package/dist-es/psdReader.d.ts +10 -0
  82. package/dist-es/psdReader.js +278 -166
  83. package/dist-es/psdReader.js.map +1 -1
  84. package/dist-es/psdWriter.d.ts +1 -0
  85. package/dist-es/psdWriter.js +77 -62
  86. package/dist-es/psdWriter.js.map +1 -1
  87. package/dist-es/text.js +17 -6
  88. package/dist-es/text.js.map +1 -1
  89. package/dist-es/utf8.js +0 -3
  90. package/dist-es/utf8.js.map +1 -1
  91. package/package.json +2 -2
  92. package/src/abr.ts +375 -235
  93. package/src/additionalInfo.ts +663 -420
  94. package/src/ase.ts +66 -61
  95. package/src/descriptor.ts +199 -195
  96. package/src/helpers.ts +4 -1
  97. package/src/imageResources.ts +632 -168
  98. package/src/jpeg.ts +8 -8
  99. package/src/psd.ts +156 -30
  100. package/src/psdReader.ts +273 -172
  101. package/src/psdWriter.ts +95 -58
  102. package/src/text.ts +17 -6
  103. package/src/utf8.ts +0 -4
@@ -3,6 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.booleanOperations = exports.infoHandlersMap = exports.infoHandlers = void 0;
4
4
  exports.readBezierKnot = readBezierKnot;
5
5
  exports.readVectorMask = readVectorMask;
6
+ exports.writeVectorMask = writeVectorMask;
7
+ exports.parseKeyDescriptorList = parseKeyDescriptorList;
8
+ exports.serializeKeyDescriptorList = serializeKeyDescriptorList;
6
9
  exports.hasMultiEffects = hasMultiEffects;
7
10
  const base64_js_1 = require("base64-js");
8
11
  const effectsHelpers_1 = require("./effectsHelpers");
@@ -33,6 +36,7 @@ function readLength64(reader) {
33
36
  return (0, psdReader_1.readUint32)(reader);
34
37
  }
35
38
  addHandler('TySh', hasKey('text'), (reader, target, leftBytes) => {
39
+ var _a;
36
40
  if ((0, psdReader_1.readInt16)(reader) !== 1)
37
41
  throw new Error(`Invalid TySh version`);
38
42
  const transform = [];
@@ -79,7 +83,8 @@ addHandler('TySh', hasKey('text'), (reader, target, leftBytes) => {
79
83
  // require('fs').writeFileSync('before.txt', require('util').inspect(before, false, 99, false), 'utf8');
80
84
  // require('fs').writeFileSync('after.txt', require('util').inspect(after, false, 99, false), 'utf8');
81
85
  // console.log(require('util').inspect(parseEngineData(text.EngineData), false, 99, true));
82
- target.text = Object.assign(Object.assign({}, target.text), textData);
86
+ // descriptor anti-alias value is more reliable than the one from engine data
87
+ target.text = Object.assign(Object.assign(Object.assign({}, target.text), textData), { antiAlias: (_a = target.text.antiAlias) !== null && _a !== void 0 ? _a : textData.antiAlias });
83
88
  // console.log(require('util').inspect(target.text, false, 99, true));
84
89
  }
85
90
  (0, psdReader_1.skipBytes)(reader, leftBytes());
@@ -209,25 +214,8 @@ function readVectorMask(reader, vectorMask, width, height, size) {
209
214
  }
210
215
  return paths;
211
216
  }
212
- addHandler('vmsk', hasKey('vectorMask'), (reader, target, left, { width, height }) => {
213
- if ((0, psdReader_1.readUint32)(reader) !== 3)
214
- throw new Error('Invalid vmsk version');
215
- target.vectorMask = { paths: [] };
216
- const vectorMask = target.vectorMask;
217
- const flags = (0, psdReader_1.readUint32)(reader);
218
- vectorMask.invert = (flags & 1) !== 0;
219
- vectorMask.notLink = (flags & 2) !== 0;
220
- vectorMask.disable = (flags & 4) !== 0;
221
- readVectorMask(reader, vectorMask, width, height, left());
222
- // drawBezierPaths(vectorMask.paths, width, height, 'out.png');
223
- (0, psdReader_1.skipBytes)(reader, left());
224
- }, (writer, target, { width, height }) => {
225
- const vectorMask = target.vectorMask;
226
- const flags = (vectorMask.invert ? 1 : 0) |
227
- (vectorMask.notLink ? 2 : 0) |
228
- (vectorMask.disable ? 4 : 0);
229
- (0, psdWriter_1.writeUint32)(writer, 3); // version
230
- (0, psdWriter_1.writeUint32)(writer, flags);
217
+ // componentIndices - write index of path component for each subpath (Photoshop writes them for layer vector masks)
218
+ function writeVectorMask(writer, vectorMask, width, height, componentIndices = false) {
231
219
  // initial entry
232
220
  (0, psdWriter_1.writeUint16)(writer, 6);
233
221
  (0, psdWriter_1.writeZeros)(writer, 24);
@@ -244,12 +232,18 @@ addHandler('vmsk', hasKey('vectorMask'), (reader, target, left, { width, height
244
232
  (0, psdWriter_1.writeUint16)(writer, 8);
245
233
  (0, psdWriter_1.writeUint16)(writer, vectorMask.fillStartsWithAllPixels ? 1 : 0);
246
234
  (0, psdWriter_1.writeZeros)(writer, 22);
235
+ let componentIndex = -1;
247
236
  for (const path of vectorMask.paths) {
237
+ // subpaths without operation are part of the same path component as previous subpath
238
+ if (path.operation || componentIndex < 0)
239
+ componentIndex++;
248
240
  (0, psdWriter_1.writeUint16)(writer, path.open ? 3 : 0);
249
241
  (0, psdWriter_1.writeUint16)(writer, path.knots.length);
250
242
  (0, psdWriter_1.writeUint16)(writer, path.operation ? exports.booleanOperations.indexOf(path.operation) : -1); // -1 for undefined
251
243
  (0, psdWriter_1.writeUint16)(writer, path.fillRule === 'non-zero' ? 2 : 1);
252
- (0, psdWriter_1.writeZeros)(writer, 18); // TODO: these are sometimes non-zero
244
+ (0, psdWriter_1.writeZeros)(writer, 4);
245
+ (0, psdWriter_1.writeUint32)(writer, componentIndices ? componentIndex : 0);
246
+ (0, psdWriter_1.writeZeros)(writer, 10);
253
247
  const linkedKnot = path.open ? 4 : 1;
254
248
  const unlinkedKnot = path.open ? 5 : 2;
255
249
  for (const { linked, points } of path.knots) {
@@ -257,8 +251,29 @@ addHandler('vmsk', hasKey('vectorMask'), (reader, target, left, { width, height
257
251
  writeBezierKnot(writer, points, width, height);
258
252
  }
259
253
  }
254
+ }
255
+ addHandler('vmsk', hasKey('vectorMask'), (reader, target, left, { width, height }) => {
256
+ if ((0, psdReader_1.readUint32)(reader) !== 3)
257
+ throw new Error('Invalid vmsk version');
258
+ target.vectorMask = { paths: [] };
259
+ const vectorMask = target.vectorMask;
260
+ const flags = (0, psdReader_1.readUint32)(reader);
261
+ vectorMask.invert = (flags & 1) !== 0;
262
+ vectorMask.notLink = (flags & 2) !== 0;
263
+ vectorMask.disable = (flags & 4) !== 0;
264
+ readVectorMask(reader, vectorMask, width, height, left());
265
+ // drawBezierPaths(vectorMask.paths, width, height, 'out.png');
266
+ (0, psdReader_1.skipBytes)(reader, left());
267
+ }, (writer, target, { width, height }) => {
268
+ const vectorMask = target.vectorMask;
269
+ const flags = (vectorMask.invert ? 1 : 0) |
270
+ (vectorMask.notLink ? 2 : 0) |
271
+ (vectorMask.disable ? 4 : 0);
272
+ (0, psdWriter_1.writeUint32)(writer, 3); // version
273
+ (0, psdWriter_1.writeUint32)(writer, flags);
274
+ writeVectorMask(writer, vectorMask, width, height, true);
260
275
  });
261
- // TODO: need to write vmsk if has outline ?
276
+ // written instead of 'vmsk' for layers with vector stroke ('vscg' section)
262
277
  addHandlerAlias('vsms', 'vmsk');
263
278
  // addHandlerAlias('vmsk', 'vsms');
264
279
  addHandler('vowv', // something with vectors?
@@ -267,12 +282,8 @@ hasKey('vowv'), (reader, target) => {
267
282
  }, (writer, target) => {
268
283
  (0, psdWriter_1.writeUint32)(writer, target.vowv);
269
284
  });
270
- addHandler('vogk', hasKey('vectorOrigination'), (reader, target, left) => {
271
- if ((0, psdReader_1.readInt32)(reader) !== 1)
272
- throw new Error(`Invalid vogk version`);
273
- const desc = (0, descriptor_1.readVersionAndDescriptor)(reader);
274
- // console.log(require('util').inspect(desc, false, 99, true));
275
- target.vectorOrigination = { keyDescriptorList: [] };
285
+ function parseKeyDescriptorList(desc) {
286
+ const keyDescriptorList = [];
276
287
  for (const i of desc.keyDescriptorList) {
277
288
  const item = {};
278
289
  if (i.keyShapeInvalidated != null)
@@ -311,15 +322,14 @@ addHandler('vogk', hasKey('vectorOrigination'), (reader, target, left) => {
311
322
  if (trnf) {
312
323
  item.transform = [trnf.xx, trnf.xy, trnf.yx, trnf.yy, trnf.tx, trnf.ty];
313
324
  }
314
- target.vectorOrigination.keyDescriptorList.push(item);
325
+ keyDescriptorList.push(item);
315
326
  }
316
- (0, psdReader_1.skipBytes)(reader, left());
317
- }, (writer, target) => {
318
- target;
319
- const orig = target.vectorOrigination;
327
+ return keyDescriptorList;
328
+ }
329
+ function serializeKeyDescriptorList(keyDescriptorList) {
320
330
  const desc = { keyDescriptorList: [] };
321
- for (let i = 0; i < orig.keyDescriptorList.length; i++) {
322
- const item = orig.keyDescriptorList[i];
331
+ for (let i = 0; i < keyDescriptorList.length; i++) {
332
+ const item = keyDescriptorList[i];
323
333
  desc.keyDescriptorList.push({}); // we're adding keyOriginIndex at the end
324
334
  const out = desc.keyDescriptorList[desc.keyDescriptorList.length - 1];
325
335
  if (item.keyOriginType != null)
@@ -370,10 +380,21 @@ addHandler('vogk', hasKey('vectorOrigination'), (reader, target, left) => {
370
380
  out.keyShapeInvalidated = item.keyShapeInvalidated;
371
381
  out.keyOriginIndex = i;
372
382
  }
383
+ return desc;
384
+ }
385
+ addHandler('vogk', hasKey('vectorOrigination'), (reader, target, left) => {
386
+ if ((0, psdReader_1.readInt32)(reader) !== 1)
387
+ throw new Error(`Invalid vogk version`);
388
+ const desc = (0, descriptor_1.readVersionAndDescriptor)(reader);
389
+ // console.log(require('util').inspect(desc, false, 99, true));
390
+ target.vectorOrigination = { keyDescriptorList: parseKeyDescriptorList(desc) };
391
+ (0, psdReader_1.skipBytes)(reader, left());
392
+ }, (writer, target) => {
393
+ const desc = serializeKeyDescriptorList(target.vectorOrigination.keyDescriptorList);
373
394
  (0, psdWriter_1.writeInt32)(writer, 1); // version
374
395
  (0, descriptor_1.writeVersionAndDescriptor)(writer, '', 'null', desc);
375
396
  });
376
- addHandler('lmfx', target => target.effects !== undefined && hasMultiEffects(target.effects), (reader, target, left) => {
397
+ addHandler('lmfx', target => target.effects !== undefined && !isGroupLayer(target) && hasMultiEffects(target.effects), (reader, target, left) => {
377
398
  const version = (0, psdReader_1.readUint32)(reader);
378
399
  if (version !== 0)
379
400
  throw new Error('Invalid lmfx version');
@@ -449,8 +470,12 @@ addHandler('lyid', target => target.id !== undefined || layerNeedsId(target), (r
449
470
  let id;
450
471
  if (target.id !== undefined) {
451
472
  id = target.id;
452
- while (options.layerIds.has(id))
453
- id += 100; // make sure we don't have duplicate layer ids
473
+ if (options.layerIds.has(id)) {
474
+ // make sure we don't have duplicate layer ids (and don't take id explicitly set on another layer)
475
+ const explicitIds = getExplicitLayerIds(psd, options);
476
+ while (options.layerIds.has(id) || explicitIds.has(id))
477
+ id += 100;
478
+ }
454
479
  }
455
480
  else {
456
481
  // generate id for layers that don't have it, if it's referenced by other sections
@@ -523,12 +548,15 @@ addHandler('lspf', hasKey('protected'), (reader, target) => {
523
548
  };
524
549
  if (flags & 0x08)
525
550
  target.protected.artboards = true;
551
+ if (flags & 0x80000000)
552
+ target.protected.all = true;
526
553
  }, (writer, target) => {
527
554
  const flags = (target.protected.transparency ? 0x01 : 0) |
528
555
  (target.protected.composite ? 0x02 : 0) |
529
556
  (target.protected.position ? 0x04 : 0) |
530
- (target.protected.artboards ? 0x08 : 0);
531
- (0, psdWriter_1.writeUint32)(writer, flags);
557
+ (target.protected.artboards ? 0x08 : 0) |
558
+ (target.protected.all ? 0x80000000 : 0);
559
+ (0, psdWriter_1.writeUint32)(writer, flags >>> 0);
532
560
  });
533
561
  addHandler('lclr', hasKey('layerColor'), (reader, target) => {
534
562
  const color = (0, psdReader_1.readUint16)(reader);
@@ -539,106 +567,160 @@ addHandler('lclr', hasKey('layerColor'), (reader, target) => {
539
567
  (0, psdWriter_1.writeUint16)(writer, index === -1 ? 0 : index);
540
568
  (0, psdWriter_1.writeZeros)(writer, 6);
541
569
  });
570
+ function parseGeneratorSettings(settings) {
571
+ const result = {};
572
+ if (settings.exportAs) {
573
+ result.exportAs = {};
574
+ if (settings.exportAs.exportOption !== undefined)
575
+ result.exportAs.exportOption = settings.exportAs.exportOption;
576
+ if (settings.exportAs.scaleSettings !== undefined)
577
+ result.exportAs.scaleSettings = settings.exportAs.scaleSettings;
578
+ }
579
+ if (settings.generator_45_assets)
580
+ result.assets = settings.generator_45_assets.json;
581
+ if (settings.layerTime !== undefined)
582
+ result.layerTime = settings.layerTime;
583
+ if (settings.crema)
584
+ result.crema = settings.crema.json;
585
+ return result;
586
+ }
587
+ function serializeGeneratorSettings(generatorSettings) {
588
+ const settings = {
589
+ _name: '',
590
+ _classID: generatorSettings.assets !== undefined ? 'generatorSettings' : 'null',
591
+ };
592
+ if (generatorSettings.exportAs) {
593
+ settings.exportAs = { _name: '', _classID: 'null' };
594
+ if (generatorSettings.exportAs.exportOption !== undefined)
595
+ settings.exportAs.exportOption = generatorSettings.exportAs.exportOption;
596
+ if (generatorSettings.exportAs.scaleSettings !== undefined)
597
+ settings.exportAs.scaleSettings = generatorSettings.exportAs.scaleSettings;
598
+ }
599
+ if (generatorSettings.assets !== undefined)
600
+ settings.generator_45_assets = { _name: '', _classID: 'generatorSettings', json: generatorSettings.assets };
601
+ if (generatorSettings.layerTime !== undefined)
602
+ settings.layerTime = generatorSettings.layerTime;
603
+ if (generatorSettings.crema !== undefined)
604
+ settings.crema = { _name: '', _classID: 'null', json: generatorSettings.crema };
605
+ return { generatorSettings: settings };
606
+ }
607
+ // document (Psd object) stores generator settings in 'extn' section instead of 'shmd' section
608
+ function isDocument(target) {
609
+ return target.width !== undefined;
610
+ }
542
611
  addHandler('shmd', // Metadata setting
543
612
  // Metadata setting
544
- target => target.timestamp !== undefined || target.animationFrames !== undefined || target.animationFrameFlags !== undefined || target.timeline !== undefined || target.comps !== undefined, (reader, target, left) => {
613
+ target => target.timestamp !== undefined || target.animationFrames !== undefined || target.animationFrameFlags !== undefined || target.timeline !== undefined || target.comps !== undefined ||
614
+ (target.generatorSettings !== undefined && !isDocument(target)), (reader, target, left) => {
545
615
  const count = (0, psdReader_1.readUint32)(reader);
546
616
  for (let i = 0; i < count; i++) {
547
- (0, psdReader_1.checkSignature)(reader, '8BIM');
617
+ const signature = (0, psdReader_1.readSignature)(reader);
618
+ if (signature !== '8BIM' && signature !== '8ELE')
619
+ throw new Error(`Invalid shmd signature: '${signature}'`); // 8ELE in Photoshop Elements
548
620
  const key = (0, psdReader_1.readSignature)(reader);
549
621
  (0, psdReader_1.readUint8)(reader); // copy
550
622
  (0, psdReader_1.skipBytes)(reader, 3);
551
- (0, psdReader_1.readSection)(reader, 1, left => {
552
- if (key === 'cust') {
553
- const desc = (0, descriptor_1.readVersionAndDescriptor)(reader);
554
- // console.log('cust', target.name, require('util').inspect(desc, false, 99, true));
555
- if (desc.layerTime !== undefined)
556
- target.timestamp = desc.layerTime;
557
- }
558
- else if (key === 'mlst') {
559
- const desc = (0, descriptor_1.readVersionAndDescriptor)(reader);
560
- // console.log('mlst', target.name, require('util').inspect(desc, false, 99, true));
561
- target.animationFrames = [];
562
- for (let i = 0; i < desc.LaSt.length; i++) {
563
- const f = desc.LaSt[i];
564
- const frame = { frames: f.FrLs };
565
- if (f.enab !== undefined)
566
- frame.enable = f.enab;
567
- if (f.Ofst)
568
- frame.offset = (0, descriptor_1.horzVrtcToXY)(f.Ofst);
569
- if (f.FXRf)
570
- frame.referencePoint = (0, descriptor_1.horzVrtcToXY)(f.FXRf);
571
- if (f.Lefx)
572
- frame.effects = (0, descriptor_1.parseEffects)(f.Lefx, !!reader.logMissingFeatures);
573
- if (f.blendOptions && f.blendOptions.Opct)
574
- frame.opacity = (0, descriptor_1.parsePercent)(f.blendOptions.Opct);
575
- target.animationFrames.push(frame);
623
+ const end = reader.offset + 4 + reader.view.getUint32(reader.offset);
624
+ try {
625
+ (0, psdReader_1.readSection)(reader, 1, left => {
626
+ if (key === 'cust') {
627
+ const desc = (0, descriptor_1.readVersionAndDescriptor)(reader);
628
+ // console.log('cust', target.name, require('util').inspect(desc, false, 99, true));
629
+ if (desc.layerTime !== undefined)
630
+ target.timestamp = desc.layerTime;
576
631
  }
577
- }
578
- else if (key === 'mdyn') {
579
- // frame flags
580
- (0, psdReader_1.readUint16)(reader); // unknown
581
- const propagate = (0, psdReader_1.readUint8)(reader);
582
- const flags = (0, psdReader_1.readUint8)(reader);
583
- target.animationFrameFlags = {
584
- propagateFrameOne: !propagate,
585
- unifyLayerPosition: (flags & 1) !== 0,
586
- unifyLayerStyle: (flags & 2) !== 0,
587
- unifyLayerVisibility: (flags & 4) !== 0,
588
- };
589
- }
590
- else if (key === 'tmln') {
591
- const desc = (0, descriptor_1.readVersionAndDescriptor)(reader);
592
- const timeScope = desc.timeScope;
593
- // console.log('tmln', target.name, target.id, require('util').inspect(desc, false, 99, true));
594
- const timeline = {
595
- start: (0, descriptor_1.frac)(timeScope.Strt),
596
- duration: (0, descriptor_1.frac)(timeScope.duration),
597
- inTime: (0, descriptor_1.frac)(timeScope.inTime),
598
- outTime: (0, descriptor_1.frac)(timeScope.outTime),
599
- autoScope: desc.autoScope,
600
- audioLevel: desc.audioLevel,
601
- };
602
- if (desc.trackList) {
603
- timeline.tracks = (0, descriptor_1.parseTrackList)(desc.trackList, !!reader.logMissingFeatures);
632
+ else if (key === 'mlst') {
633
+ const desc = (0, descriptor_1.readVersionAndDescriptor)(reader);
634
+ // console.log('mlst', target.name, require('util').inspect(desc, false, 99, true));
635
+ target.animationFrames = [];
636
+ for (let i = 0; i < desc.LaSt.length; i++) {
637
+ const f = desc.LaSt[i];
638
+ const frame = { frames: f.FrLs };
639
+ if (f.enab !== undefined)
640
+ frame.enable = f.enab;
641
+ if (f.Ofst)
642
+ frame.offset = (0, descriptor_1.horzVrtcToXY)(f.Ofst);
643
+ if (f.FXRf)
644
+ frame.referencePoint = (0, descriptor_1.horzVrtcToXY)(f.FXRf);
645
+ if (f.Lefx)
646
+ frame.effects = (0, descriptor_1.parseEffects)(f.Lefx, !!reader.logMissingFeatures);
647
+ if (f.blendOptions && f.blendOptions.Opct)
648
+ frame.opacity = (0, descriptor_1.parsePercent)(f.blendOptions.Opct);
649
+ target.animationFrames.push(frame);
650
+ }
604
651
  }
605
- target.timeline = timeline;
606
- // console.log('tmln:result', target.name, target.id, require('util').inspect(timeline, false, 99, true));
607
- }
608
- else if (key === 'cmls') {
609
- const desc = (0, descriptor_1.readVersionAndDescriptor)(reader);
610
- // console.log('cmls', require('util').inspect(desc, false, 99, true));
611
- target.comps = {
612
- settings: [],
613
- };
614
- if (desc.origFXRefPoint)
615
- target.comps.originalEffectsReferencePoint = { x: desc.origFXRefPoint.Hrzn, y: desc.origFXRefPoint.Vrtc };
616
- for (const item of desc.layerSettings) {
617
- target.comps.settings.push({ compList: item.compList });
618
- const t = target.comps.settings[target.comps.settings.length - 1];
619
- if ('enab' in item)
620
- t.enabled = item.enab;
621
- if (item.Ofst)
622
- t.offset = { x: item.Ofst.Hrzn, y: item.Ofst.Vrtc };
623
- if (item.FXRefPoint)
624
- t.effectsReferencePoint = { x: item.FXRefPoint.Hrzn, y: item.FXRefPoint.Vrtc };
652
+ else if (key === 'mdyn') {
653
+ // frame flags
654
+ (0, psdReader_1.readUint16)(reader); // unknown
655
+ const propagate = (0, psdReader_1.readUint8)(reader);
656
+ const flags = (0, psdReader_1.readUint8)(reader);
657
+ target.animationFrameFlags = {
658
+ propagateFrameOne: !propagate,
659
+ unifyLayerPosition: (flags & 1) !== 0,
660
+ unifyLayerStyle: (flags & 2) !== 0,
661
+ unifyLayerVisibility: (flags & 4) !== 0,
662
+ };
625
663
  }
626
- }
627
- else if (key === 'extn') {
628
- const desc = (0, descriptor_1.readVersionAndDescriptor)(reader);
629
- // console.log(require('util').inspect(desc, false, 99, true));
630
- desc; // TODO: save this
631
- reader.logMissingFeatures && reader.log('Unhandled "shmd" section key', key);
632
- }
633
- else {
634
- reader.logMissingFeatures && reader.log('Unhandled "shmd" section key', key);
635
- }
636
- (0, psdReader_1.skipBytes)(reader, left());
637
- });
664
+ else if (key === 'tmln') {
665
+ const desc = (0, descriptor_1.readVersionAndDescriptor)(reader);
666
+ const timeScope = desc.timeScope;
667
+ // console.log('tmln', target.name, target.id, require('util').inspect(desc, false, 99, true));
668
+ const timeline = {
669
+ start: (0, descriptor_1.frac)(timeScope.Strt),
670
+ duration: (0, descriptor_1.frac)(timeScope.duration),
671
+ inTime: (0, descriptor_1.frac)(timeScope.inTime),
672
+ outTime: (0, descriptor_1.frac)(timeScope.outTime),
673
+ autoScope: desc.autoScope,
674
+ audioLevel: desc.audioLevel,
675
+ };
676
+ if (desc.trackList) {
677
+ timeline.tracks = (0, descriptor_1.parseTrackList)(desc.trackList, !!reader.logMissingFeatures);
678
+ }
679
+ target.timeline = timeline;
680
+ // console.log('tmln:result', target.name, target.id, require('util').inspect(timeline, false, 99, true));
681
+ }
682
+ else if (key === 'cmls') {
683
+ const desc = (0, descriptor_1.readVersionAndDescriptor)(reader);
684
+ // console.log('cmls', require('util').inspect(desc, false, 99, true));
685
+ target.comps = {
686
+ settings: [],
687
+ };
688
+ if (desc.origFXRefPoint)
689
+ target.comps.originalEffectsReferencePoint = { x: desc.origFXRefPoint.Hrzn, y: desc.origFXRefPoint.Vrtc };
690
+ for (const item of desc.layerSettings) {
691
+ target.comps.settings.push({ compList: item.compList });
692
+ const t = target.comps.settings[target.comps.settings.length - 1];
693
+ if ('enab' in item)
694
+ t.enabled = item.enab;
695
+ if (item.Ofst)
696
+ t.offset = { x: item.Ofst.Hrzn, y: item.Ofst.Vrtc };
697
+ if (item.FXRefPoint)
698
+ t.effectsReferencePoint = { x: item.FXRefPoint.Hrzn, y: item.FXRefPoint.Vrtc };
699
+ }
700
+ }
701
+ else if (key === 'extn') {
702
+ const desc = (0, descriptor_1.readVersionAndDescriptor)(reader);
703
+ if (desc.generatorSettings)
704
+ target.generatorSettings = parseGeneratorSettings(desc.generatorSettings);
705
+ }
706
+ else {
707
+ reader.logMissingFeatures && reader.log('Unhandled "shmd" section key', key);
708
+ }
709
+ (0, psdReader_1.skipBytes)(reader, left());
710
+ });
711
+ }
712
+ catch (e) {
713
+ // failure to read one item shouldn't drop all other items
714
+ if (reader.throwForMissingFeatures)
715
+ throw e;
716
+ reader.logMissingFeatures && reader.log(`Failed to read "shmd" section key ${key}: ${e.message}`);
717
+ reader.offset = end;
718
+ }
638
719
  }
639
720
  (0, psdReader_1.skipBytes)(reader, left());
640
721
  }, (writer, target, _, options) => {
641
722
  const { animationFrames, animationFrameFlags, timestamp, timeline, comps } = target;
723
+ const generatorSettings = isDocument(target) ? undefined : target.generatorSettings;
642
724
  let count = 0;
643
725
  if (animationFrames)
644
726
  count++;
@@ -650,6 +732,8 @@ target => target.timestamp !== undefined || target.animationFrames !== undefined
650
732
  count++;
651
733
  if (comps)
652
734
  count++;
735
+ if (generatorSettings)
736
+ count++;
653
737
  (0, psdWriter_1.writeUint32)(writer, count);
654
738
  if (animationFrames) {
655
739
  (0, psdWriter_1.writeSignature)(writer, '8BIM');
@@ -672,9 +756,9 @@ target => target.timestamp !== undefined || target.animationFrames !== undefined
672
756
  if (f.referencePoint)
673
757
  frame.FXRf = (0, descriptor_1.xyToHorzVrtc)(f.referencePoint);
674
758
  if (f.effects)
675
- frame.Lefx = (0, descriptor_1.serializeEffects)(f.effects, false, false);
759
+ frame.Lefx = (0, descriptor_1.serializeEffects)(f.effects, false, hasMultiEffects(f.effects));
676
760
  if (f.opacity !== undefined)
677
- frame.blendOptions = { Opct: (0, descriptor_1.unitsPercent)(f.opacity) };
761
+ frame.blendOptions = { Opct: (0, descriptor_1.unitsPercentF)(f.opacity) };
678
762
  desc.LaSt.push(frame);
679
763
  }
680
764
  (0, descriptor_1.writeVersionAndDescriptor)(writer, '', 'null', desc);
@@ -758,6 +842,15 @@ target => target.timestamp !== undefined || target.animationFrames !== undefined
758
842
  (0, descriptor_1.writeVersionAndDescriptor)(writer, '', 'null', desc);
759
843
  }, true);
760
844
  }
845
+ if (generatorSettings) {
846
+ (0, psdWriter_1.writeSignature)(writer, '8BIM');
847
+ (0, psdWriter_1.writeSignature)(writer, 'extn');
848
+ (0, psdWriter_1.writeUint8)(writer, 0); // copy (always false)
849
+ (0, psdWriter_1.writeZeros)(writer, 3);
850
+ (0, psdWriter_1.writeSection)(writer, 2, () => {
851
+ (0, descriptor_1.writeVersionAndDescriptor)(writer, '', 'null', serializeGeneratorSettings(generatorSettings));
852
+ }, true);
853
+ }
761
854
  });
762
855
  addHandler('PxSc', hasKey('pixelSource'), (reader, target) => {
763
856
  const desc = (0, descriptor_1.readVersionAndDescriptor)(reader, true);
@@ -837,7 +930,7 @@ addHandler('vstk', hasKey('vectorStroke'), (reader, target, left) => {
837
930
  lineAlignment: descriptor_1.strokeStyleLineAlignment.decode(desc.strokeStyleLineAlignment),
838
931
  scaleLock: desc.strokeStyleScaleLock,
839
932
  strokeAdjust: desc.strokeStyleStrokeAdjust,
840
- lineDashSet: desc.strokeStyleLineDashSet.map(descriptor_1.parseUnits),
933
+ lineDashSet: (desc.strokeStyleLineDashSet || []).map(descriptor_1.parseUnits),
841
934
  blendMode: descriptor_1.BlnM.decode(desc.strokeStyleBlendMode),
842
935
  opacity: (0, descriptor_1.parsePercent)(desc.strokeStyleOpacity),
843
936
  content: (0, descriptor_1.parseVectorContent)(desc.strokeStyleContent),
@@ -875,9 +968,11 @@ hasKey('artboard'), (reader, target, left) => {
875
968
  rect: { top: rect['Top '], left: rect.Left, bottom: rect.Btom, right: rect.Rght },
876
969
  guideIndices: desc.guideIndeces,
877
970
  presetName: desc.artboardPresetName,
878
- color: (0, descriptor_1.parseColor)(desc['Clr ']),
879
- backgroundType: desc.artboardBackgroundType,
880
971
  };
972
+ if (desc['Clr '])
973
+ target.artboard.color = (0, descriptor_1.parseColor)(desc['Clr ']);
974
+ if (desc.artboardBackgroundType !== undefined)
975
+ target.artboard.backgroundType = desc.artboardBackgroundType;
881
976
  (0, psdReader_1.skipBytes)(reader, left());
882
977
  }, (writer, target) => {
883
978
  var _a;
@@ -983,14 +1078,15 @@ function encodeWarp(warp) {
983
1078
  }
984
1079
  return desc;
985
1080
  }
986
- addHandler('PlLd', hasKey('placedLayer'), (reader, target, left) => {
1081
+ addHandler('PlLd', target => !!target.placedLayer && !target.placedLayer.external, // linked smart objects are stored in SoLE
1082
+ (reader, target, left) => {
987
1083
  if ((0, psdReader_1.readSignature)(reader) !== 'plcL')
988
1084
  throw new Error(`Invalid PlLd signature`);
989
1085
  if ((0, psdReader_1.readInt32)(reader) !== 3)
990
1086
  throw new Error(`Invalid PlLd version`);
991
1087
  const id = (0, psdReader_1.readPascalString)(reader, 1);
992
1088
  const pageNumber = (0, psdReader_1.readInt32)(reader);
993
- const totalPages = (0, psdReader_1.readInt32)(reader); // TODO: check how this works ?
1089
+ const totalPages = (0, psdReader_1.readInt32)(reader); // for multi-page documents (PDF)
994
1090
  (0, psdReader_1.readInt32)(reader); // anitAliasPolicy 16
995
1091
  const placedLayerType = (0, psdReader_1.readInt32)(reader); // 0 = unknown, 1 = vector, 2 = raster, 3 = image stack
996
1092
  if (!placedLayerTypes[placedLayerType])
@@ -1035,6 +1131,8 @@ addHandler('PlLd', hasKey('placedLayer'), (reader, target, left) => {
1035
1131
  const type = isQuilt ? 'quiltWarp' : 'warp';
1036
1132
  (0, descriptor_1.writeVersionAndDescriptor)(writer, '', type, encodeWarp(warp), type);
1037
1133
  });
1134
+ const levelsFilterKeys = ['_name', '_classID', 'presetKind', 'Adjs'];
1135
+ const levelsFilterAdjustmentKeys = ['_name', '_classID', 'Chnl', 'Inpt', 'Otpt', 'Gmm '];
1038
1136
  function uint8ToFloat32(array) {
1039
1137
  const length = Math.floor(array.byteLength / 4);
1040
1138
  return new Float32Array(array.buffer.slice(array.byteOffset, array.byteOffset + length * 4), 0, length);
@@ -1077,7 +1175,7 @@ function pointToHrznVrtc(point) {
1077
1175
  Vrtc: (0, descriptor_1.unitsValue)(point.y, 'y'),
1078
1176
  };
1079
1177
  }
1080
- function parseFilterFXItem(f, options) {
1178
+ function parseFilterFXItem(f, reader) {
1081
1179
  const base = {
1082
1180
  name: f['Nm '],
1083
1181
  opacity: (0, descriptor_1.parsePercent)(f.blendOptions.Opct),
@@ -1125,7 +1223,7 @@ function parseFilterFXItem(f, options) {
1125
1223
  undefinedAreas: descriptor_1.UndA.decode(f.Fltr.UndA),
1126
1224
  displacementFile: {
1127
1225
  signature: f.Fltr.DspF.sig,
1128
- path: f.Fltr.DspF.path, // TODO: this is decoded incorrectly ???
1226
+ path: f.Fltr.DspF.path,
1129
1227
  },
1130
1228
  } });
1131
1229
  case 'Pnch': return Object.assign(Object.assign({}, base), { type: 'pinch', filter: {
@@ -1312,8 +1410,7 @@ function parseFilterFXItem(f, options) {
1312
1410
  ],
1313
1411
  puppetShapeList: (f.Fltr.puppetShapeList || []).map(p => ({
1314
1412
  rigidType: p.rigidType,
1315
- // TODO: VrsM
1316
- // TODO: VrsN
1413
+ // VrsM / VrsN - version (always 1.0)
1317
1414
  originalVertexArray: uint8ToPoints(p.originalVertexArray),
1318
1415
  deformedVertexArray: uint8ToPoints(p.deformedVertexArray),
1319
1416
  indexArray: Array.from(uint8ToUint32(p.indexArray)),
@@ -1410,7 +1507,7 @@ function parseFilterFXItem(f, options) {
1410
1507
  ;
1411
1508
  case 'Crvs':
1412
1509
  {
1413
- return Object.assign(Object.assign({}, base), { type: 'curves', filter: Object.assign({ presetKind: descriptor_1.presetKindType.decode(f.Fltr.presetKind) }, (f.Fltr.Adjs ? {
1510
+ return Object.assign(Object.assign({}, base), { type: 'curves', filter: Object.assign(Object.assign({}, (f.Fltr.presetKind !== undefined ? { presetKind: descriptor_1.presetKindType.decode(f.Fltr.presetKind) } : {})), (f.Fltr.Adjs ? {
1414
1511
  adjustments: f.Fltr.Adjs.map(a => {
1415
1512
  const channels = a.Chnl.map(descriptor_1.Chnl.decode);
1416
1513
  if (a['Crv ']) {
@@ -1434,6 +1531,35 @@ function parseFilterFXItem(f, options) {
1434
1531
  } : {})) });
1435
1532
  }
1436
1533
  ;
1534
+ case 'Lvls':
1535
+ {
1536
+ const adjustments = f.Fltr.Adjs;
1537
+ const isKnown = (obj, keys) => Object.keys(obj).every(k => keys.indexOf(k) !== -1);
1538
+ // skip filter with settings we don't know about
1539
+ if (!isKnown(f.Fltr, levelsFilterKeys) || (adjustments && !adjustments.every(a => isKnown(a, levelsFilterAdjustmentKeys)))) {
1540
+ if (reader.throwForMissingFeatures)
1541
+ throw new Error('Unknown levels filter settings');
1542
+ reader.logMissingFeatures && reader.log(`Skipped levels smart filter with unknown settings`);
1543
+ return undefined;
1544
+ }
1545
+ return Object.assign(Object.assign({}, base), { type: 'levels', filter: Object.assign(Object.assign({}, (f.Fltr.presetKind !== undefined ? { presetKind: descriptor_1.presetKindType.decode(f.Fltr.presetKind) } : {})), (adjustments ? {
1546
+ adjustments: adjustments.map(a => {
1547
+ const result = { channels: a.Chnl.map(descriptor_1.Chnl.decode) };
1548
+ if (a.Inpt) {
1549
+ result.shadowInput = a.Inpt[0];
1550
+ result.highlightInput = a.Inpt[1];
1551
+ }
1552
+ if (a.Otpt) {
1553
+ result.shadowOutput = a.Otpt[0];
1554
+ result.highlightOutput = a.Otpt[1];
1555
+ }
1556
+ if (a['Gmm '] !== undefined)
1557
+ result.midtoneInput = a['Gmm '];
1558
+ return result;
1559
+ }),
1560
+ } : {})) });
1561
+ }
1562
+ ;
1437
1563
  case 'BrgC':
1438
1564
  {
1439
1565
  return Object.assign(Object.assign({}, base), { type: 'brightness/contrast', filter: {
@@ -1444,10 +1570,11 @@ function parseFilterFXItem(f, options) {
1444
1570
  }
1445
1571
  ;
1446
1572
  default:
1447
- if (options.throwForMissingFeatures) {
1573
+ if (reader.throwForMissingFeatures) {
1448
1574
  // console.log('FILTER', require('util').inspect(f, false, 99, true));
1449
1575
  throw new Error(`Unknown filter classId: ${f.Fltr._classID}`);
1450
1576
  }
1577
+ reader.logMissingFeatures && reader.log(`Unhandled smart filter: ${f.Fltr._classID} (${f['Nm ']})`);
1451
1578
  return undefined;
1452
1579
  }
1453
1580
  }
@@ -1467,21 +1594,23 @@ function parseFilterFXItem(f, options) {
1467
1594
  case 1314149187: return Object.assign(Object.assign({}, base), { type: 'ntsc colors' });
1468
1595
  case 1231976050: return Object.assign(Object.assign({}, base), { type: 'invert' });
1469
1596
  default:
1470
- if (options.throwForMissingFeatures) {
1597
+ if (reader.throwForMissingFeatures) {
1471
1598
  // console.log('FILTER', require('util').inspect(f, false, 99, true));
1472
1599
  throw new Error(`Unknown filterID: ${f.filterID}`);
1473
1600
  }
1601
+ reader.logMissingFeatures && reader.log(`Unhandled smart filter: ${f.filterID} (${f['Nm ']})`);
1602
+ return undefined;
1474
1603
  }
1475
1604
  }
1476
1605
  }
1477
- function parseFilterFX(desc, options) {
1606
+ function parseFilterFX(desc, reader) {
1478
1607
  return {
1479
1608
  enabled: desc.enab,
1480
1609
  validAtPosition: desc.validAtPosition,
1481
1610
  maskEnabled: desc.filterMaskEnable,
1482
1611
  maskLinked: desc.filterMaskLinked,
1483
1612
  maskExtendWithWhite: desc.filterMaskExtendWithWhite,
1484
- list: desc.filterFXList.map(x => parseFilterFXItem(x, options)).filter((x) => !!x),
1613
+ list: desc.filterFXList.map(x => parseFilterFXItem(x, reader)).filter((x) => !!x),
1485
1614
  };
1486
1615
  }
1487
1616
  function uvRadius(t) {
@@ -1837,14 +1966,14 @@ function serializeFilterFXItem(f) {
1837
1966
  case 'puppet': return Object.assign(Object.assign({}, base), { Fltr: {
1838
1967
  _name: 'Rigid Transform',
1839
1968
  _classID: 'rigidTransform',
1840
- 'null': ['Ordn.Trgt'], // TODO: ???
1969
+ 'null': ['Ordn.Trgt'], // target layer reference
1841
1970
  rigidType: f.filter.rigidType,
1842
1971
  puppetShapeList: (f.filter.puppetShapeList || []).map(p => ({
1843
1972
  _name: '',
1844
1973
  _classID: 'puppetShape',
1845
1974
  rigidType: p.rigidType,
1846
- VrsM: 1, // TODO: ???
1847
- VrsN: 0, // TODO: ???
1975
+ VrsM: 1, // version major
1976
+ VrsN: 0, // version minor
1848
1977
  originalVertexArray: toUint8(new Float32Array(pointsToArray(p.originalVertexArray))),
1849
1978
  deformedVertexArray: toUint8(new Float32Array(pointsToArray(p.deformedVertexArray))),
1850
1979
  indexArray: toUint8(new Uint32Array(p.indexArray)),
@@ -1926,7 +2055,7 @@ function serializeFilterFXItem(f) {
1926
2055
  warpedVertices: f.filter.warpedVertices.map(pointToHrznVrtc),
1927
2056
  quads: f.filter.quads.map(indices => ({ _name: '', _classID: 'quadIndices', indices })),
1928
2057
  }, filterID: 442 });
1929
- case 'curves': return Object.assign(Object.assign({}, base), { Fltr: Object.assign({ _name: 'Curves', _classID: 'Crvs', presetKind: descriptor_1.presetKindType.encode(f.filter.presetKind) }, (f.filter.adjustments ? {
2058
+ case 'curves': return Object.assign(Object.assign({}, base), { Fltr: Object.assign(Object.assign({ _name: 'Curves', _classID: 'Crvs' }, (f.filter.presetKind !== undefined ? { presetKind: descriptor_1.presetKindType.encode(f.filter.presetKind) } : {})), (f.filter.adjustments ? {
1930
2059
  Adjs: f.filter.adjustments.map(a => 'curve' in a ? {
1931
2060
  _name: '',
1932
2061
  _classID: 'CrvA',
@@ -1939,6 +2068,21 @@ function serializeFilterFXItem(f) {
1939
2068
  Mpng: a.values,
1940
2069
  })
1941
2070
  } : {})), filterID: 1131574899 });
2071
+ case 'levels': return Object.assign(Object.assign({}, base), { Fltr: Object.assign(Object.assign({ _name: 'Levels', _classID: 'Lvls' }, (f.filter.presetKind !== undefined ? { presetKind: descriptor_1.presetKindType.encode(f.filter.presetKind) } : {})), (f.filter.adjustments ? {
2072
+ Adjs: f.filter.adjustments.map(a => {
2073
+ var _a, _b, _c, _d;
2074
+ const adjustment = { _name: '', _classID: 'LvlA', Chnl: a.channels.map(descriptor_1.Chnl.encode) };
2075
+ if (a.shadowInput !== undefined || a.highlightInput !== undefined) {
2076
+ adjustment.Inpt = [Math.round((_a = a.shadowInput) !== null && _a !== void 0 ? _a : 0), Math.round((_b = a.highlightInput) !== null && _b !== void 0 ? _b : 255)];
2077
+ }
2078
+ if (a.shadowOutput !== undefined || a.highlightOutput !== undefined) {
2079
+ adjustment.Otpt = [Math.round((_c = a.shadowOutput) !== null && _c !== void 0 ? _c : 0), Math.round((_d = a.highlightOutput) !== null && _d !== void 0 ? _d : 255)];
2080
+ }
2081
+ if (a.midtoneInput !== undefined)
2082
+ adjustment['Gmm '] = a.midtoneInput;
2083
+ return adjustment;
2084
+ }),
2085
+ } : {})), filterID: 1282829427 });
1942
2086
  case 'brightness/contrast': return Object.assign(Object.assign({}, base), { Fltr: {
1943
2087
  _name: 'Brightness/Contrast',
1944
2088
  _classID: 'BrgC',
@@ -1989,7 +2133,8 @@ function getWarpFromPlacedLayer(placed) {
1989
2133
  },
1990
2134
  };
1991
2135
  }
1992
- addHandler('SoLd', hasKey('placedLayer'), (reader, target, left) => {
2136
+ addHandler('SoLd', target => !!target.placedLayer && !target.placedLayer.external, // linked smart objects are stored in SoLE
2137
+ (reader, target, left) => {
1993
2138
  if ((0, psdReader_1.readSignature)(reader) !== 'soLD')
1994
2139
  throw new Error(`Invalid SoLd type`);
1995
2140
  const version = (0, psdReader_1.readInt32)(reader);
@@ -2046,13 +2191,18 @@ addHandler('SoLd', hasKey('placedLayer'), (reader, target, left) => {
2046
2191
  const placedType = placedLayerTypes.indexOf(placed.type);
2047
2192
  if (placedType === -1)
2048
2193
  throw new Error('Invalid placedLayer type');
2194
+ const warp = getWarpFromPlacedLayer(placed);
2195
+ // size of linked image matches warp bounds if it's not provided
2196
+ const bounds = warp.bounds;
2197
+ const width = placed.width || (bounds ? bounds.right.value - bounds.left.value : 0);
2198
+ const height = placed.height || (bounds ? bounds.bottom.value - bounds.top.value : 0);
2049
2199
  const desc = Object.assign(Object.assign({ Idnt: placed.id, placed: (_a = placed.placed) !== null && _a !== void 0 ? _a : placed.id, PgNm: placed.pageNumber || 1, totalPages: placed.totalPages || 1 }, (placed.crop ? { Crop: placed.crop } : {})), { frameStep: placed.frameStep || { numerator: 0, denominator: 600 }, duration: placed.duration || { numerator: 0, denominator: 600 }, frameCount: placed.frameCount || 0, Annt: 16, Type: placedType, Trnf: placed.transform, nonAffineTransform: (_b = placed.nonAffineTransform) !== null && _b !== void 0 ? _b : placed.transform,
2050
2200
  // quiltWarp: {} as any,
2051
- warp: encodeWarp(getWarpFromPlacedLayer(placed)), 'Sz ': {
2201
+ warp: encodeWarp(warp), 'Sz ': {
2052
2202
  _name: '',
2053
2203
  _classID: 'Pnt ',
2054
- Wdth: placed.width || 0, // TODO: find size ?
2055
- Hght: placed.height || 0, // TODO: find size ?
2204
+ Wdth: width,
2205
+ Hght: height,
2056
2206
  }, Rslt: placed.resolution ? (0, descriptor_1.unitsValue)(placed.resolution, 'resolution') : { units: 'Density', value: 72 } });
2057
2207
  if (placed.filter) {
2058
2208
  desc.filterFX = {
@@ -2066,14 +2216,6 @@ addHandler('SoLd', hasKey('placedLayer'), (reader, target, left) => {
2066
2216
  filterFXList: placed.filter.list.map(f => serializeFilterFXItem(f)),
2067
2217
  };
2068
2218
  }
2069
- // TODO:
2070
- // desc.comp = -1;
2071
- // desc.compInfo = { _name: '', _classID: 'null', compID: -1, originalCompID: -1 } as any;
2072
- // desc.ClMg = {
2073
- // _name: '',
2074
- // _classID: 'ClMg',
2075
- // placedLayerOCIOConversion: 'placedLayerOCIOConversion.placedLayerOCIOConvertEmbedded'
2076
- // } as any;
2077
2219
  // if (JSON.stringify(t) !== JSON.stringify(desc)) {
2078
2220
  // console.log('read', require('util').inspect(t, false, 99, true));
2079
2221
  // console.log('write', require('util').inspect(desc, false, 99, true));
@@ -2103,7 +2245,13 @@ addHandler('SoLd', hasKey('placedLayer'), (reader, target, left) => {
2103
2245
  desc.compInfo = placed.compInfo;
2104
2246
  (0, descriptor_1.writeVersionAndDescriptor)(writer, '', 'null', desc, desc.quiltWarp ? 'quiltWarp' : 'warp');
2105
2247
  });
2106
- addHandlerAlias('SoLE', 'SoLd');
2248
+ const soLdHandler = exports.infoHandlersMap['SoLd'];
2249
+ addHandler('SoLE', // linked smart object (Photoshop doesn't write PlLd for these)
2250
+ // linked smart object (Photoshop doesn't write PlLd for these)
2251
+ target => { var _a; return !!((_a = target.placedLayer) === null || _a === void 0 ? void 0 : _a.external); }, (reader, target, left, psd, imageResources) => {
2252
+ soLdHandler.read(reader, target, left, psd, imageResources);
2253
+ target.placedLayer.external = true;
2254
+ }, soLdHandler.write);
2107
2255
  addHandler('fxrp', hasKey('referencePoint'), (reader, target) => {
2108
2256
  target.referencePoint = {
2109
2257
  x: (0, psdReader_1.readFloat64)(reader),
@@ -2113,14 +2261,37 @@ addHandler('fxrp', hasKey('referencePoint'), (reader, target) => {
2113
2261
  (0, psdWriter_1.writeFloat64)(writer, target.referencePoint.x);
2114
2262
  (0, psdWriter_1.writeFloat64)(writer, target.referencePoint.y);
2115
2263
  });
2116
- addHandler('Lr16', () => false, (reader, _target, _left, psd, imageResources) => {
2117
- (0, psdReader_1.readLayerInfo)(reader, psd, imageResources);
2118
- }, (_writer, _target) => {
2119
- });
2120
- addHandler('Lr32', () => false, (reader, _target, _left, psd, imageResources) => {
2121
- (0, psdReader_1.readLayerInfo)(reader, psd, imageResources);
2122
- }, (_writer, _target) => {
2123
- });
2264
+ function createGlobalLayerInfoHandler(key, bitsPerChannel) {
2265
+ addHandler(key, () => false, (reader, _target, left, psd, imageResources) => {
2266
+ var _a;
2267
+ // Photoshop writes empty (or 8bit compatibility) layer info section alongside Lr16/Lr32 in 16/32bit documents,
2268
+ // layers from global info with matching bit depth replace layers from that section, other ones are only used
2269
+ // if the section is empty
2270
+ if (!reader.layersInGlobalInfo && (psd.bitsPerChannel === bitsPerChannel || !((_a = psd.children) === null || _a === void 0 ? void 0 : _a.length))) {
2271
+ const sectionLayers = psd.children;
2272
+ psd.children = [];
2273
+ try {
2274
+ (0, psdReader_1.readLayerInfo)(reader, psd, imageResources);
2275
+ }
2276
+ catch (e) {
2277
+ psd.children = sectionLayers; // keep layers from layer info section if global info is broken
2278
+ throw e;
2279
+ }
2280
+ reader.layersInGlobalInfo = true;
2281
+ }
2282
+ (0, psdReader_1.skipBytes)(reader, left());
2283
+ }, () => { });
2284
+ }
2285
+ createGlobalLayerInfoHandler('Lr16', 16);
2286
+ createGlobalLayerInfoHandler('Lr32', 32);
2287
+ createGlobalLayerInfoHandler('Layr', undefined);
2288
+ // merged transparency markers, first extra channel of composite image is transparency
2289
+ for (const key of ['Mtrn', 'Mt16', 'Mt32']) {
2290
+ addHandler(key, () => false, (reader, _target, left) => {
2291
+ reader.globalAlpha = true;
2292
+ (0, psdReader_1.skipBytes)(reader, left());
2293
+ }, () => { });
2294
+ }
2124
2295
  addHandler('LMsk', hasKey('userMask'), (reader, target) => {
2125
2296
  target.userMask = {
2126
2297
  colorSpace: (0, psdReader_1.readColor)(reader),
@@ -2144,11 +2315,13 @@ if (helpers_1.MOCK_HANDLERS) {
2144
2315
  }, (writer, target) => false && (0, psdWriter_1.writeBytes)(writer, target._Patt));
2145
2316
  }
2146
2317
  else {
2147
- addHandler('Patt', // TODO: handle also Pat2 & Pat3
2148
- // TODO: handle also Pat2 & Pat3
2318
+ addHandler('Patt', // 'Pat2' and 'Pat3' (16 and 32 bit documents) are aliases, patterns are always written as 8 bit
2319
+ // 'Pat2' and 'Pat3' (16 and 32 bit documents) are aliases, patterns are always written as 8 bit
2149
2320
  target => !!(target.patterns && target.patterns.length > 0), (reader, target, left) => {
2150
2321
  while (left() > 0) {
2151
- const pattern = (0, psdReader_1.readPattern)(reader);
2322
+ const pattern = (0, psdReader_1.readPatternSafe)(reader);
2323
+ if (!pattern)
2324
+ continue;
2152
2325
  if (target.patterns === undefined)
2153
2326
  target.patterns = [];
2154
2327
  target.patterns.push(pattern);
@@ -2336,6 +2509,10 @@ addHandler('Anno', target => target.annotations !== undefined, (reader, target,
2336
2509
  writer.view.setUint32(dataOffset - 4, writer.offset - dataOffset, false);
2337
2510
  }
2338
2511
  });
2512
+ function readFileTypeSignature(reader) {
2513
+ const value = (0, psdReader_1.readSignature)(reader);
2514
+ return /^[\0 ]*$/.test(value) ? '' : value.trim(); // empty value is stored as spaces or zeros
2515
+ }
2339
2516
  function createLnkHandler(tag) {
2340
2517
  addHandler(tag, (target) => {
2341
2518
  const psd = target;
@@ -2357,8 +2534,8 @@ function createLnkHandler(tag) {
2357
2534
  const version = (0, psdReader_1.readInt32)(reader);
2358
2535
  const id = (0, psdReader_1.readPascalString)(reader, 1);
2359
2536
  const name = (0, psdReader_1.readUnicodeString)(reader);
2360
- const fileType = (0, psdReader_1.readSignature)(reader).trim(); // ' ' if empty
2361
- const fileCreator = (0, psdReader_1.readSignature)(reader).trim(); // ' ' or '\0\0\0\0' if empty
2537
+ const fileType = readFileTypeSignature(reader); // ' ' if empty
2538
+ const fileCreator = readFileTypeSignature(reader); // ' ' or '\0\0\0\0' if empty
2362
2539
  const dataSize = readLength64(reader);
2363
2540
  const hasFileOpenDescriptor = (0, psdReader_1.readUint8)(reader);
2364
2541
  const fileOpenDescriptor = hasFileOpenDescriptor ? (0, descriptor_1.readVersionAndDescriptor)(reader) : undefined;
@@ -2509,28 +2686,40 @@ createLnkHandler('lnk2');
2509
2686
  createLnkHandler('lnkE');
2510
2687
  addHandlerAlias('lnkD', 'lnk2');
2511
2688
  addHandlerAlias('lnk3', 'lnk2');
2512
- addHandler('pths', hasKey('pathList'), (reader, target) => {
2689
+ // unicode names of saved paths (path data is stored in image resources 2000-2998)
2690
+ addHandler('pths', target => { var _a; return ((_a = target.imageResources) === null || _a === void 0 ? void 0 : _a.savedPaths) !== undefined; }, (reader, _target, left, psd) => {
2691
+ var _a, _b;
2513
2692
  const desc = (0, descriptor_1.readVersionAndDescriptor)(reader, true);
2514
- // console.log(require('util').inspect(desc, false, 99, true));
2515
- // if (options.throwForMissingFeatures && desc?.pathList?.length) throw new Error('non-empty pathList in `pths`');
2516
- desc;
2517
- target.pathList = []; // TODO: read paths
2518
- }, (writer, _target) => {
2693
+ const imageResources = (_a = psd.imageResources) !== null && _a !== void 0 ? _a : (psd.imageResources = {});
2694
+ const savedPaths = (_b = imageResources.savedPaths) !== null && _b !== void 0 ? _b : (imageResources.savedPaths = []);
2695
+ for (let i = 0; i < desc.pathList.length && i < savedPaths.length; i++) {
2696
+ const { pathUnicodeName, pathSymmetryClass } = desc.pathList[i];
2697
+ const path = savedPaths[i];
2698
+ // clipping path is referenced by (non-unicode) name
2699
+ if (imageResources.clippingPath && imageResources.clippingPath.name === path.name) {
2700
+ imageResources.clippingPath.name = pathUnicodeName;
2701
+ }
2702
+ path.name = pathUnicodeName;
2703
+ if (pathSymmetryClass)
2704
+ path.symmetryMode = pathSymmetryClass.pathSymmetryMode.split('.')[1];
2705
+ }
2706
+ (0, psdReader_1.skipBytes)(reader, left());
2707
+ }, (writer, _target, psd) => {
2519
2708
  const desc = {
2520
- pathList: [], // TODO: write paths
2709
+ pathList: psd.imageResources.savedPaths.map(path => (Object.assign({ _name: '', _classID: 'pathInfoClass', pathUnicodeName: path.name }, (path.symmetryMode ? {
2710
+ pathSymmetryClass: {
2711
+ _name: '',
2712
+ _classID: 'pathSymmetryClass',
2713
+ pathSymmetryMode: `pathSymmetryModeEnum.${path.symmetryMode}`,
2714
+ },
2715
+ } : {})))),
2521
2716
  };
2522
2717
  (0, descriptor_1.writeVersionAndDescriptor)(writer, '', 'pathsDataClass', desc);
2523
2718
  });
2524
2719
  addHandler('lyvr', hasKey('version'), (reader, target) => target.version = (0, psdReader_1.readUint32)(reader), (writer, target) => (0, psdWriter_1.writeUint32)(writer, target.version));
2525
- addHandler('lfxs', () => false, // TODO: not sure when we actually need to write this section
2526
- // NOTE: this might be insufficient
2527
- // target => target.effects !== undefined && (
2528
- // !!target.effects.dropShadow?.some(e => e.choke) ||
2529
- // !!target.effects.innerShadow?.some(e => e.choke) ||
2530
- // !!target.effects.outerGlow?.choke ||
2531
- // !!target.effects.innerGlow?.choke
2532
- // ),
2533
- (reader, target, left) => {
2720
+ addHandler('lfxs', // effects of group layers (instead of 'lfx2' / 'lmfx')
2721
+ // effects of group layers (instead of 'lfx2' / 'lmfx')
2722
+ target => target.effects !== undefined && isGroupLayer(target), (reader, target, left) => {
2534
2723
  const version = (0, psdReader_1.readUint32)(reader);
2535
2724
  if (version !== 0)
2536
2725
  throw new Error(`Invalid lfxs version`);
@@ -2574,6 +2763,10 @@ function readLevelsChannel(reader) {
2574
2763
  const midtoneInput = (0, psdReader_1.readInt16)(reader) / 100;
2575
2764
  return { shadowInput, highlightInput, shadowOutput, highlightOutput, midtoneInput };
2576
2765
  }
2766
+ const defaultLevelsChannel = { shadowInput: 0, highlightInput: 255, shadowOutput: 0, highlightOutput: 255, midtoneInput: 1 };
2767
+ function isDefaultLevelsChannel(c) {
2768
+ return c.shadowInput === 0 && c.highlightInput === 255 && c.shadowOutput === 0 && c.highlightOutput === 255 && c.midtoneInput === 1;
2769
+ }
2577
2770
  function writeLevelsChannel(writer, channel) {
2578
2771
  (0, psdWriter_1.writeInt16)(writer, channel.shadowInput);
2579
2772
  (0, psdWriter_1.writeInt16)(writer, channel.highlightInput);
@@ -2584,48 +2777,36 @@ function writeLevelsChannel(writer, channel) {
2584
2777
  addHandler('levl', adjustmentType('levels'), (reader, target, left) => {
2585
2778
  if ((0, psdReader_1.readUint16)(reader) !== 2)
2586
2779
  throw new Error('Invalid levl version');
2587
- const levels = [];
2588
- for (let i = 0; i < 29 && left(); i++) {
2589
- levels.push(readLevelsChannel(reader));
2590
- }
2591
- const [rgb, red, green, blue] = levels;
2592
- target.adjustment = Object.assign(Object.assign({}, target.adjustment), { type: 'levels', rgb,
2593
- red,
2594
- green,
2595
- blue });
2596
- if (left()) {
2780
+ const channels = [];
2781
+ for (let i = 0; i < 29 && left() >= 10; i++) {
2782
+ channels.push(readLevelsChannel(reader));
2783
+ }
2784
+ if (left() >= 8) {
2597
2785
  if ((0, psdReader_1.readSignature)(reader) !== 'Lvls')
2598
2786
  throw new Error(`Invalid levl signature`);
2599
2787
  if ((0, psdReader_1.readUint16)(reader) !== 3)
2600
2788
  throw new Error(`Invalid levl version`);
2601
2789
  const totalChannelRecords = (0, psdReader_1.readUint16)(reader);
2602
- for (let i = 0; i < (totalChannelRecords - levels.length) && left(); i++) {
2603
- levels.push(readLevelsChannel(reader));
2790
+ while (channels.length < totalChannelRecords && left() >= 10) {
2791
+ channels.push(readLevelsChannel(reader));
2604
2792
  }
2605
2793
  }
2794
+ // records for all possible channels are stored, skip unused ones
2795
+ while (channels.length && isDefaultLevelsChannel(channels[channels.length - 1]))
2796
+ channels.pop();
2797
+ target.adjustment = Object.assign(Object.assign({}, target.adjustment), { type: 'levels', channels });
2606
2798
  (0, psdReader_1.skipBytes)(reader, left());
2607
2799
  }, (writer, target) => {
2608
- const info = target.adjustment;
2609
- const defaultChannel = {
2610
- shadowInput: 0,
2611
- highlightInput: 255,
2612
- shadowOutput: 0,
2613
- highlightOutput: 255,
2614
- midtoneInput: 1,
2615
- };
2800
+ const channels = target.adjustment.channels || [];
2616
2801
  (0, psdWriter_1.writeUint16)(writer, 2); // version
2617
- writeLevelsChannel(writer, info.rgb || defaultChannel);
2618
- writeLevelsChannel(writer, info.red || defaultChannel);
2619
- writeLevelsChannel(writer, info.green || defaultChannel);
2620
- writeLevelsChannel(writer, info.blue || defaultChannel);
2621
- for (let i = 0; i < (29 - 4); i++) {
2622
- writeLevelsChannel(writer, defaultChannel);
2802
+ for (let i = 0; i < 29; i++) {
2803
+ writeLevelsChannel(writer, channels[i] || defaultLevelsChannel);
2623
2804
  }
2624
2805
  (0, psdWriter_1.writeSignature)(writer, 'Lvls');
2625
2806
  (0, psdWriter_1.writeUint16)(writer, 3); // version
2626
2807
  (0, psdWriter_1.writeUint16)(writer, 62); // total count
2627
- for (let i = 0; i < (62 - 29); i++) {
2628
- writeLevelsChannel(writer, defaultChannel);
2808
+ for (let i = 29; i < 62; i++) {
2809
+ writeLevelsChannel(writer, channels[i] || defaultLevelsChannel);
2629
2810
  }
2630
2811
  });
2631
2812
  function readCurveChannel(reader) {
@@ -2650,17 +2831,16 @@ addHandler('curv', adjustmentType('curves'), (reader, target, left) => {
2650
2831
  if ((0, psdReader_1.readUint16)(reader) !== 1)
2651
2832
  throw new Error('Invalid curv version');
2652
2833
  (0, psdReader_1.readUint16)(reader);
2653
- const channels = (0, psdReader_1.readUint16)(reader);
2654
- const info = { type: 'curves' };
2655
- if (channels & 1)
2656
- info.rgb = readCurveChannel(reader);
2657
- if (channels & 2)
2658
- info.red = readCurveChannel(reader);
2659
- if (channels & 4)
2660
- info.green = readCurveChannel(reader);
2661
- if (channels & 8)
2662
- info.blue = readCurveChannel(reader);
2663
- target.adjustment = Object.assign(Object.assign({}, target.adjustment), info);
2834
+ const channelsMask = (0, psdReader_1.readUint16)(reader);
2835
+ const channels = [];
2836
+ for (let i = 0; i < 16; i++) {
2837
+ if (channelsMask & (1 << i)) {
2838
+ while (channels.length < i)
2839
+ channels.push([]);
2840
+ channels.push(readCurveChannel(reader));
2841
+ }
2842
+ }
2843
+ target.adjustment = Object.assign(Object.assign({}, target.adjustment), { type: 'curves', channels });
2664
2844
  // ignoring, duplicate information
2665
2845
  // checkSignature(reader, 'Crv ');
2666
2846
  // const cVersion = readUint16(reader);
@@ -2676,57 +2856,29 @@ addHandler('curv', adjustmentType('curves'), (reader, target, left) => {
2676
2856
  // }
2677
2857
  (0, psdReader_1.skipBytes)(reader, left());
2678
2858
  }, (writer, target) => {
2679
- const info = target.adjustment;
2680
- const { rgb, red, green, blue } = info;
2681
- let channels = 0;
2682
- let channelCount = 0;
2683
- if (rgb && rgb.length) {
2684
- channels |= 1;
2685
- channelCount++;
2686
- }
2687
- if (red && red.length) {
2688
- channels |= 2;
2689
- channelCount++;
2690
- }
2691
- if (green && green.length) {
2692
- channels |= 4;
2693
- channelCount++;
2694
- }
2695
- if (blue && blue.length) {
2696
- channels |= 8;
2697
- channelCount++;
2859
+ var _a;
2860
+ const channels = (target.adjustment.channels || []).slice(0, 16);
2861
+ const used = [];
2862
+ let channelsMask = 0;
2863
+ for (let i = 0; i < channels.length; i++) {
2864
+ if ((_a = channels[i]) === null || _a === void 0 ? void 0 : _a.length) {
2865
+ channelsMask |= 1 << i;
2866
+ used.push(i);
2867
+ }
2698
2868
  }
2699
2869
  (0, psdWriter_1.writeUint8)(writer, 0);
2700
2870
  (0, psdWriter_1.writeUint16)(writer, 1); // version
2701
2871
  (0, psdWriter_1.writeUint16)(writer, 0);
2702
- (0, psdWriter_1.writeUint16)(writer, channels);
2703
- if (rgb && rgb.length)
2704
- writeCurveChannel(writer, rgb);
2705
- if (red && red.length)
2706
- writeCurveChannel(writer, red);
2707
- if (green && green.length)
2708
- writeCurveChannel(writer, green);
2709
- if (blue && blue.length)
2710
- writeCurveChannel(writer, blue);
2872
+ (0, psdWriter_1.writeUint16)(writer, channelsMask);
2873
+ for (const i of used)
2874
+ writeCurveChannel(writer, channels[i]);
2711
2875
  (0, psdWriter_1.writeSignature)(writer, 'Crv ');
2712
2876
  (0, psdWriter_1.writeUint16)(writer, 4); // version
2713
2877
  (0, psdWriter_1.writeUint16)(writer, 0);
2714
- (0, psdWriter_1.writeUint16)(writer, channelCount);
2715
- if (rgb && rgb.length) {
2716
- (0, psdWriter_1.writeUint16)(writer, 0);
2717
- writeCurveChannel(writer, rgb);
2718
- }
2719
- if (red && red.length) {
2720
- (0, psdWriter_1.writeUint16)(writer, 1);
2721
- writeCurveChannel(writer, red);
2722
- }
2723
- if (green && green.length) {
2724
- (0, psdWriter_1.writeUint16)(writer, 2);
2725
- writeCurveChannel(writer, green);
2726
- }
2727
- if (blue && blue.length) {
2728
- (0, psdWriter_1.writeUint16)(writer, 3);
2729
- writeCurveChannel(writer, blue);
2878
+ (0, psdWriter_1.writeUint16)(writer, used.length);
2879
+ for (const i of used) {
2880
+ (0, psdWriter_1.writeUint16)(writer, i);
2881
+ writeCurveChannel(writer, channels[i]);
2730
2882
  }
2731
2883
  });
2732
2884
  addHandler('expA', adjustmentType('exposure'), (reader, target, left) => {
@@ -2783,12 +2935,34 @@ function writeHueChannel(writer, channel) {
2783
2935
  addHandler('hue2', adjustmentType('hue/saturation'), (reader, target, left) => {
2784
2936
  if ((0, psdReader_1.readUint16)(reader) !== 2)
2785
2937
  throw new Error('Invalid hue2 version');
2786
- target.adjustment = Object.assign(Object.assign({}, target.adjustment), { type: 'hue/saturation', master: readHueChannel(reader), reds: readHueChannel(reader), yellows: readHueChannel(reader), greens: readHueChannel(reader), cyans: readHueChannel(reader), blues: readHueChannel(reader), magentas: readHueChannel(reader) });
2938
+ const colorize = !!(0, psdReader_1.readUint8)(reader);
2939
+ (0, psdReader_1.skipBytes)(reader, 1);
2940
+ const colorizeHue = (0, psdReader_1.readInt16)(reader); // stored as -180..180
2941
+ const colorizeSaturation = (0, psdReader_1.readInt16)(reader);
2942
+ const colorizeLightness = (0, psdReader_1.readInt16)(reader);
2943
+ target.adjustment = Object.assign(Object.assign({}, target.adjustment), { type: 'hue/saturation', colorize, colorization: {
2944
+ hue: colorizeHue < 0 ? colorizeHue + 360 : colorizeHue,
2945
+ saturation: colorizeSaturation,
2946
+ lightness: colorizeLightness,
2947
+ }, master: {
2948
+ hue: (0, psdReader_1.readInt16)(reader),
2949
+ saturation: (0, psdReader_1.readInt16)(reader),
2950
+ lightness: (0, psdReader_1.readInt16)(reader),
2951
+ }, reds: readHueChannel(reader), yellows: readHueChannel(reader), greens: readHueChannel(reader), cyans: readHueChannel(reader), blues: readHueChannel(reader), magentas: readHueChannel(reader) });
2787
2952
  (0, psdReader_1.skipBytes)(reader, left());
2788
2953
  }, (writer, target) => {
2954
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
2789
2955
  const info = target.adjustment;
2790
2956
  (0, psdWriter_1.writeUint16)(writer, 2); // version
2791
- writeHueChannel(writer, info.master);
2957
+ (0, psdWriter_1.writeUint8)(writer, info.colorize ? 1 : 0);
2958
+ (0, psdWriter_1.writeUint8)(writer, 0);
2959
+ const colorizeHue = Math.round((_b = (_a = info.colorization) === null || _a === void 0 ? void 0 : _a.hue) !== null && _b !== void 0 ? _b : 0);
2960
+ (0, psdWriter_1.writeInt16)(writer, colorizeHue > 180 ? colorizeHue - 360 : colorizeHue);
2961
+ (0, psdWriter_1.writeInt16)(writer, Math.round((_d = (_c = info.colorization) === null || _c === void 0 ? void 0 : _c.saturation) !== null && _d !== void 0 ? _d : 0));
2962
+ (0, psdWriter_1.writeInt16)(writer, Math.round((_f = (_e = info.colorization) === null || _e === void 0 ? void 0 : _e.lightness) !== null && _f !== void 0 ? _f : 0));
2963
+ (0, psdWriter_1.writeInt16)(writer, Math.round((_h = (_g = info.master) === null || _g === void 0 ? void 0 : _g.hue) !== null && _h !== void 0 ? _h : 0));
2964
+ (0, psdWriter_1.writeInt16)(writer, Math.round((_k = (_j = info.master) === null || _j === void 0 ? void 0 : _j.saturation) !== null && _k !== void 0 ? _k : 0));
2965
+ (0, psdWriter_1.writeInt16)(writer, Math.round((_m = (_l = info.master) === null || _l === void 0 ? void 0 : _l.lightness) !== null && _m !== void 0 ? _m : 0));
2792
2966
  writeHueChannel(writer, info.reds);
2793
2967
  writeHueChannel(writer, info.yellows);
2794
2968
  writeHueChannel(writer, info.greens);
@@ -2867,16 +3041,18 @@ addHandler('phfl', adjustmentType('photo filter'), (reader, target, left) => {
2867
3041
  color = (0, psdReader_1.readColor)(reader);
2868
3042
  }
2869
3043
  else { // version 3
2870
- // TODO: test this, this is probably wrong
2871
- // values are stored as L (0..100), a, b (-128..127) multiplied by 100,
2872
- // normalize them to the same scale as readColor() so they can be written back as version 2
2873
- const l = (0, psdReader_1.readInt32)(reader) / 100;
2874
- const a = (0, psdReader_1.readInt32)(reader) / 100;
2875
- const b = (0, psdReader_1.readInt32)(reader) / 100;
3044
+ // XYZ color relative to D50 white point, 32768 = 1.0 (not documented, matches colors of Photoshop presets),
3045
+ // converted to Lab (same scale as readColor()) so it can be written back as version 2
3046
+ const x = (0, psdReader_1.readInt32)(reader) / 32768;
3047
+ const y = (0, psdReader_1.readInt32)(reader) / 32768;
3048
+ const z = (0, psdReader_1.readInt32)(reader) / 32768;
3049
+ if (!(x >= 0 && y >= 0 && z >= 0 && x <= 2 && y <= 2 && z <= 2))
3050
+ throw new Error('Invalid phfl XYZ color');
3051
+ const f = (t) => t > 216 / 24389 ? Math.cbrt(t) : (24389 / 27 * t + 16) / 116;
2876
3052
  color = {
2877
- l: l / 100,
2878
- a: (0, helpers_1.normalizeLabAB)(a),
2879
- b: (0, helpers_1.normalizeLabAB)(b),
3053
+ l: Math.max(0, 116 * f(y) - 16) / 100,
3054
+ a: (0, helpers_1.normalizeLabAB)(500 * (f(x) - f(y))),
3055
+ b: (0, helpers_1.normalizeLabAB)(200 * (f(y) - f(z))),
2880
3056
  };
2881
3057
  }
2882
3058
  target.adjustment = {
@@ -3044,7 +3220,7 @@ addHandler('grdm', adjustmentType('gradient map'), (reader, target, left) => {
3044
3220
  reader.offset--;
3045
3221
  if (hasMethod) {
3046
3222
  const method = (0, psdReader_1.readSignature)(reader);
3047
- info.method = descriptor_1.gradientInterpolationMethodType.decode(method);
3223
+ info.method = descriptor_1.gradientInterpolationMethodType.decode(`gradientInterpolationMethodType.${method}`);
3048
3224
  }
3049
3225
  info.name = (0, psdReader_1.readUnicodeString)(reader);
3050
3226
  info.colorStops = [];
@@ -3106,7 +3282,7 @@ addHandler('grdm', adjustmentType('gradient map'), (reader, target, left) => {
3106
3282
  (0, psdWriter_1.writeUint8)(writer, info.reverse ? 1 : 0);
3107
3283
  (0, psdWriter_1.writeUint8)(writer, info.dither ? 1 : 0);
3108
3284
  if (info.method !== undefined) {
3109
- (0, psdWriter_1.writeSignature)(writer, descriptor_1.gradientInterpolationMethodType.encode(info.method));
3285
+ (0, psdWriter_1.writeSignature)(writer, descriptor_1.gradientInterpolationMethodType.encode(info.method).split('.')[1]);
3110
3286
  }
3111
3287
  (0, psdWriter_1.writeUnicodeStringWithPadding)(writer, info.name || '');
3112
3288
  (0, psdWriter_1.writeUint16)(writer, info.colorStops && info.colorStops.length || 0);
@@ -3428,17 +3604,22 @@ addHandler('artd', // document-wide artboard info
3428
3604
  // document-wide artboard info
3429
3605
  target => target.artboards !== undefined, (reader, target, left) => {
3430
3606
  const desc = (0, descriptor_1.readVersionAndDescriptor)(reader);
3431
- target.artboards = {
3432
- count: desc['Cnt '],
3433
- autoExpandOffset: { horizontal: desc.autoExpandOffset.Hrzn, vertical: desc.autoExpandOffset.Vrtc },
3434
- origin: { horizontal: desc.origin.Hrzn, vertical: desc.origin.Vrtc },
3435
- autoExpandEnabled: desc.autoExpandEnabled,
3436
- autoNestEnabled: desc.autoNestEnabled,
3437
- autoPositionEnabled: desc.autoPositionEnabled,
3438
- shrinkwrapOnSaveEnabled: !!desc.shrinkwrapOnSaveEnabled,
3439
- docDefaultNewArtboardBackgroundColor: (0, descriptor_1.parseColor)(desc.docDefaultNewArtboardBackgroundColor),
3440
- docDefaultNewArtboardBackgroundType: desc.docDefaultNewArtboardBackgroundType,
3441
- };
3607
+ const artboards = target.artboards = { count: desc['Cnt '] };
3608
+ if (desc.autoExpandOffset)
3609
+ artboards.autoExpandOffset = { horizontal: desc.autoExpandOffset.Hrzn, vertical: desc.autoExpandOffset.Vrtc };
3610
+ if (desc.origin)
3611
+ artboards.origin = { horizontal: desc.origin.Hrzn, vertical: desc.origin.Vrtc };
3612
+ if (desc.autoExpandEnabled !== undefined)
3613
+ artboards.autoExpandEnabled = desc.autoExpandEnabled;
3614
+ if (desc.autoNestEnabled !== undefined)
3615
+ artboards.autoNestEnabled = desc.autoNestEnabled;
3616
+ if (desc.autoPositionEnabled !== undefined)
3617
+ artboards.autoPositionEnabled = desc.autoPositionEnabled;
3618
+ artboards.shrinkwrapOnSaveEnabled = !!desc.shrinkwrapOnSaveEnabled;
3619
+ if (desc.docDefaultNewArtboardBackgroundColor)
3620
+ artboards.docDefaultNewArtboardBackgroundColor = (0, descriptor_1.parseColor)(desc.docDefaultNewArtboardBackgroundColor);
3621
+ if (desc.docDefaultNewArtboardBackgroundType !== undefined)
3622
+ artboards.docDefaultNewArtboardBackgroundType = desc.docDefaultNewArtboardBackgroundType;
3442
3623
  (0, psdReader_1.skipBytes)(reader, left());
3443
3624
  }, (writer, target) => {
3444
3625
  var _a, _b, _c, _d, _e;
@@ -3456,17 +3637,19 @@ target => target.artboards !== undefined, (reader, target, left) => {
3456
3637
  };
3457
3638
  (0, descriptor_1.writeVersionAndDescriptor)(writer, '', 'null', desc, 'artd');
3458
3639
  });
3640
+ function isGroupLayer(target) {
3641
+ return !!target.children;
3642
+ }
3459
3643
  function hasMultiEffects(effects) {
3460
3644
  return Object.keys(effects).map(key => effects[key]).some(v => Array.isArray(v) && v.length > 1);
3461
3645
  }
3462
- addHandler('lfx2', target => target.effects !== undefined && !hasMultiEffects(target.effects), (reader, target, left) => {
3646
+ addHandler('lfx2', target => target.effects !== undefined && !isGroupLayer(target) && !hasMultiEffects(target.effects), (reader, target, left) => {
3463
3647
  const version = (0, psdReader_1.readUint32)(reader);
3464
3648
  if (version !== 0)
3465
3649
  throw new Error(`Invalid lfx2 version`);
3466
3650
  const desc = (0, descriptor_1.readVersionAndDescriptor)(reader);
3467
3651
  // console.log('READ', require('util').inspect(desc, false, 99, true));
3468
- // TODO: don't discard if we got it from lmfx
3469
- // discard if read in 'lrFX' section
3652
+ // discard if read in 'lrFX' section ('lmfx' and 'lfx2' are never stored together)
3470
3653
  target.effects = (0, descriptor_1.parseEffects)(desc, !!reader.logMissingFeatures);
3471
3654
  (0, psdReader_1.skipBytes)(reader, left());
3472
3655
  }, (writer, target, _, options) => {
@@ -3523,16 +3706,14 @@ addHandler('cinf', hasKey('compositorUsed'), (reader, target, left) => {
3523
3706
  desc.compCoreGPUSupport = `reason.${cinf.compCoreGPUSupport}`;
3524
3707
  (0, descriptor_1.writeVersionAndDescriptor)(writer, '', 'null', desc);
3525
3708
  });
3526
- // extension settings ?, ignore it
3527
- addHandler('extn', target => target._extn !== undefined, (reader, target) => {
3709
+ // generator settings of the document (layers store them in 'shmd' section)
3710
+ addHandler('extn', target => target.generatorSettings !== undefined && isDocument(target), (reader, target, left) => {
3528
3711
  const desc = (0, descriptor_1.readVersionAndDescriptor)(reader);
3529
- // console.log(require('util').inspect(desc, false, 99, true));
3530
- if (helpers_1.MOCK_HANDLERS)
3531
- target._extn = desc;
3712
+ if (desc.generatorSettings)
3713
+ target.generatorSettings = parseGeneratorSettings(desc.generatorSettings);
3714
+ (0, psdReader_1.skipBytes)(reader, left());
3532
3715
  }, (writer, target) => {
3533
- // TODO: need to add correct types for desc fields (resources/src.psd)
3534
- if (helpers_1.MOCK_HANDLERS)
3535
- (0, descriptor_1.writeVersionAndDescriptor)(writer, '', 'null', target._extn);
3716
+ (0, descriptor_1.writeVersionAndDescriptor)(writer, '', 'null', serializeGeneratorSettings(target.generatorSettings));
3536
3717
  });
3537
3718
  addHandler('iOpa', hasKey('fillOpacity'), (reader, target) => {
3538
3719
  target.fillOpacity = (0, psdReader_1.readUint8)(reader) / 0xff;