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
@@ -11,14 +11,13 @@ var __rest = (this && this.__rest) || function (s, e) {
11
11
  return t;
12
12
  };
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.IntC = exports.IntE = exports.Drct = exports.WndM = exports.CntE = exports.FlCl = exports.ExtR = exports.ExtT = exports.DfsM = exports.blurType = exports.Lns = exports.MztT = exports.Chnl = exports.Dstr = exports.ZZTy = exports.Wvtp = exports.SphM = exports.RplS = exports.Cnvr = exports.UndA = exports.DspM = exports.SmBQ = exports.SmBM = exports.BlrQ = exports.BlrM = exports.strokeStyleLineAlignment = exports.strokeStyleLineJoinType = exports.strokeStyleLineCapType = exports.ESliceBGColorType = exports.ESliceOrigin = exports.ESliceVertAlign = exports.ESliceHorzAlign = exports.ESliceType = exports.FrFl = exports.FStl = exports.ClrS = exports.gradientInterpolationMethodType = exports.stdTrackID = exports.animInterpStyleEnum = exports.GrdT = exports.IGSr = exports.BETE = exports.BESs = exports.bvlT = exports.BESl = exports.BlnM = exports.warpStyle = exports.Annt = exports.Ornt = exports.textGridding = void 0;
15
- exports.presetKindType = exports.prjM = exports.FlMd = void 0;
14
+ exports.Drct = exports.WndM = exports.CntE = exports.FlCl = exports.ExtR = exports.ExtT = exports.DfsM = exports.blurType = exports.Lns = exports.MztT = exports.Chnl = exports.Dstr = exports.ZZTy = exports.Wvtp = exports.SphM = exports.RplS = exports.Cnvr = exports.UndA = exports.DspM = exports.SmBQ = exports.SmBM = exports.BlrQ = exports.BlrM = exports.strokeStyleLineAlignment = exports.strokeStyleLineJoinType = exports.strokeStyleLineCapType = exports.ESliceBGColorType = exports.ESliceOrigin = exports.ESliceVertAlign = exports.ESliceHorzAlign = exports.ESliceType = exports.FrFl = exports.FStl = exports.ClrS = exports.Clry = exports.gradientInterpolationMethodType = exports.stdTrackID = exports.animInterpStyleEnum = exports.GrdT = exports.IGSr = exports.BETE = exports.BESs = exports.bvlT = exports.BESl = exports.BlnM = exports.warpStyle = exports.Annt = exports.Ornt = exports.textGridding = exports.outputSettingsRoot = void 0;
15
+ exports.presetKindType = exports.prjM = exports.FlMd = exports.IntC = exports.IntE = void 0;
16
16
  exports.setLogErrors = setLogErrors;
17
17
  exports.readAsciiStringOrClassId = readAsciiStringOrClassId;
18
18
  exports.writeAsciiStringOrClassId = writeAsciiStringOrClassId;
19
19
  exports.readDescriptorStructure = readDescriptorStructure;
20
20
  exports.writeDescriptorStructure = writeDescriptorStructure;
21
- exports.createDescriptorReference = createDescriptorReference;
22
21
  exports.pathSelectionStateToReference = pathSelectionStateToReference;
23
22
  exports.writeClassStructure = writeClassStructure;
24
23
  exports.readVersionAndDescriptor = readVersionAndDescriptor;
@@ -167,6 +166,7 @@ const fieldToArrayExtType = {
167
166
  Clrs: makeType('', 'Clrt'),
168
167
  Trns: makeType('', 'TrnS'),
169
168
  keyDescriptorList: nullType,
169
+ keyRootDescriptorList: nullType,
170
170
  solidFillMulti: makeType('', 'SoFi'),
171
171
  gradientFillMulti: makeType('', 'GrFl'),
172
172
  dropShadowMulti: makeType('', 'DrSh'),
@@ -200,17 +200,23 @@ const fieldToArrayExtType = {
200
200
  };
201
201
  const typeToField = {
202
202
  'TEXT': [
203
- 'Txt ', 'printerName', 'Nm ', 'Idnt', 'blackAndWhitePresetFileName', 'LUT3DFileName',
203
+ 'Txt ', 'printerName', 'Nm ', 'Idnt', 'blackAndWhitePresetFileName', 'LUT3DFileName', 'exportOption', 'scaleSettings',
204
+ 'BackgroundImagePath', 'ImageSubfolderName',
204
205
  'presetFileName', 'curvesPresetFileName', 'mixerPresetFileName', 'placed', 'description', 'reason',
205
206
  'artboardPresetName', 'json', 'clipID', 'relPath', 'fullPath', 'mediaDescriptor', 'Msge',
206
- 'altTag', 'url', 'cellText', 'preset', 'KnNm', 'FPth', 'comment', 'originalPath',
207
+ 'altTag', 'url', 'cellText', 'preset', 'KnNm', 'FPth', 'comment', 'originalPath', 'Bk ', 'pathUnicodeName',
207
208
  ],
208
209
  'tdta': [
209
210
  'EngineData', 'LUT3DFileData', 'indexArray', 'originalVertexArray', 'deformedVertexArray',
210
- 'LqMe',
211
+ 'LqMe', 'bookKey',
211
212
  ],
212
213
  'long': [
213
- 'TextIndex', 'RndS', 'Mdpn', 'Smth', 'Lctn', 'strokeStyleVersion', 'LaID', 'Vrsn', 'Cnt ',
214
+ 'TextIndex', 'RndS', 'Mdpn', 'Smth', 'Lctn', 'strokeStyleVersion', 'LaID', 'Vrsn', 'Cnt ', 'bookID',
215
+ 'cropRectBottom', 'cropRectLeft', 'cropRectRight', 'cropRectTop',
216
+ // save for web output settings
217
+ 'Version', 'BackgroundColorBlue', 'BackgroundColorGreen', 'BackgroundColorRed', 'BackgroundColorState', 'AttributeCase', 'Encoding',
218
+ 'DuplicateFileNameBehavior', 'ImageMapLocation', 'ImageMapType', 'Indent', 'LineEndings', 'SpacersEmptyCells',
219
+ 'SpacersHorizontal', 'SpacersVertical', 'StylesFormat', 'TDWidthHeight', 'TagCase', 'ColorPolicy', 'MetadataPolicy',
214
220
  'Brgh', 'Cntr', 'means', 'vibrance', 'Strt', 'bwPresetKind', 'comp', 'compID', 'originalCompID',
215
221
  'curvesPresetKind', 'mixerPresetKind', 'uOrder', 'vOrder', 'PgNm', 'totalPages', 'Crop',
216
222
  'numerator', 'denominator', 'frameCount', 'Annt', 'keyOriginType', 'unitValueQuadVersion',
@@ -232,7 +238,7 @@ const typeToField = {
232
238
  'tableOrder', 'enableCompCore', 'enableCompCoreGPU', 'compCoreSupport', 'compCoreGPUSupport', 'Engn',
233
239
  'enableCompCoreThreads', 'gs99', 'FrDs', 'trackID', 'animInterpStyle', 'horzAlign',
234
240
  'vertAlign', 'bgColorType', 'shapeOperation', 'UndA', 'Wvtp', 'Drct', 'WndM', 'Edg ', 'FlCl', 'IntE',
235
- 'IntC', 'Cnvr', 'Fl ', 'Dstr', 'MztT', 'Lns ', 'ExtT', 'DspM', 'ExtR', 'ZZTy', 'SphM', 'SmBQ', 'placedLayerOCIOConversion', 'gradientsInterpolationMethod',
241
+ 'IntC', 'Cnvr', 'Fl ', 'Dstr', 'MztT', 'Lns ', 'ExtT', 'DspM', 'ExtR', 'ZZTy', 'SphM', 'SmBQ', 'placedLayerOCIOConversion', 'gradientsInterpolationMethod', 'pathSymmetryMode',
236
242
  'interpretAlpha',
237
243
  ],
238
244
  'bool': [
@@ -249,7 +255,7 @@ const typeToField = {
249
255
  'moreAccurate', 'GpuY', 'LIWy', 'Cnty', 'Lnkd', 'showAlteredVideo', 'lightingOn',
250
256
  ],
251
257
  'doub': [
252
- 'warpValue', 'warpPerspective', 'warpPerspectiveOther', 'Intr', 'Wdth', 'Hght',
258
+ 'warpValue', 'warpPerspective', 'warpPerspectiveOther', 'Intr', 'Wdth', 'Hght', 'Gmm ',
253
259
  'strokeStyleMiterLimit', 'strokeStyleResolution', 'layerTime', 'keyOriginResolution',
254
260
  'xx', 'xy', 'yx', 'yy', 'tx', 'ty', 'FrGA', 'frameRate', 'audioLevel', 'rotation',
255
261
  'X ', 'Y ', 'redFloat', 'greenFloat', 'blueFloat', 'imageResolution',
@@ -263,7 +269,7 @@ const typeToField = {
263
269
  'topRight', 'topLeft', 'bottomLeft', 'bottomRight', 'ClNs', 'Shrp',
264
270
  ],
265
271
  'VlLs': [
266
- 'Crv ', 'Clrs', 'Mnm ', 'Mxm ', 'Trns', 'pathList', 'strokeStyleLineDashSet', 'FrLs', 'slices',
272
+ 'Crv ', 'Clrs', 'Mnm ', 'Mxm ', 'Trns', 'pathList', 'keyRootDescriptorList', 'strokeStyleLineDashSet', 'FrLs', 'slices',
267
273
  'LaSt', 'Trnf', 'nonAffineTransform', 'keyDescriptorList', 'guideIndeces', 'gradientFillMulti',
268
274
  'solidFillMulti', 'frameFXMulti', 'innerShadowMulti', 'dropShadowMulti', 'FrIn', 'FSts', 'FsFr',
269
275
  'sheetTimelineOptions', 'audioClipList', 'trackList', 'globalTrackList', 'keyList', 'audioClipList',
@@ -282,25 +288,6 @@ const channels = [
282
288
  ];
283
289
  // color classes whose channel values are written as doubles ('GRYC' and 'LABC' are kept for backwards compatibility)
284
290
  const doubleColorClasses = ['Grsc', 'LbCl', 'GRYC', 'LABC'];
285
- // Hidden (non-enumerable) markers used to remember how values were stored in the file, so they can be
286
- // written back the same way. They don't change the shape of the objects returned by the reader.
287
- const osTypeKey = typeof Symbol !== 'undefined' ? Symbol('agPsdOSType') : '__agPsdOSType';
288
- const obArInfoKey = typeof Symbol !== 'undefined' ? Symbol('agPsdObAr') : '__agPsdObAr';
289
- const referenceInfoKey = typeof Symbol !== 'undefined' ? Symbol('agPsdReference') : '__agPsdReference';
290
- function setHidden(target, key, value) {
291
- Object.defineProperty(target, key, { value, enumerable: false, configurable: true, writable: true });
292
- return target;
293
- }
294
- function getHidden(target, key) {
295
- return (target && typeof target === 'object') ? target[key] : undefined;
296
- }
297
- const osTypeFamilies = {
298
- UntF: 'UntF', UnFl: 'UntF',
299
- Objc: 'Objc', GlbO: 'Objc',
300
- type: 'type', GlbC: 'type',
301
- comp: 'comp',
302
- };
303
- // returns the OS type the value was originally read with, if it's compatible with the type we'd write it as
304
291
  // fallback for unknown keys, based on the type of the value
305
292
  function guessOSType(value) {
306
293
  if (typeof value === 'boolean')
@@ -309,26 +296,33 @@ function guessOSType(value) {
309
296
  return 'doub';
310
297
  if (Array.isArray(value))
311
298
  return 'VlLs';
312
- if (value && typeof value === 'object' && typeof value._classID === 'string')
313
- return 'Objc';
299
+ if (value && typeof value === 'object') {
300
+ if (typeof value._classID === 'string')
301
+ return 'Objc';
302
+ if (typeof value.classID === 'string')
303
+ return 'type'; // class read from 'type' or 'GlbC'
304
+ if (typeof value.low === 'number' && typeof value.high === 'number')
305
+ return 'comp';
306
+ if (typeof value.units === 'string' && typeof value.value === 'number')
307
+ return 'UntF';
308
+ }
314
309
  return undefined;
315
310
  }
316
- function getRecordedOSType(value, type) {
317
- const recorded = getHidden(value, osTypeKey);
318
- if (!recorded)
319
- return type;
320
- if (!type || recorded === 'comp' || recorded === 'type' || recorded === 'GlbC')
321
- return recorded;
322
- return osTypeFamilies[recorded] === osTypeFamilies[type] ? recorded : type;
323
- }
324
311
  const fieldToArrayType = {
325
312
  'Mnm ': 'long',
326
313
  'Mxm ': 'long',
314
+ HtmlFileNameComponents: 'long', // save for web output settings
315
+ SavingFileNameComponents: 'long',
316
+ SliceFileNameComponents: 'long',
317
+ Inpt: 'long', // levels filter
318
+ Otpt: 'long', // levels filter
327
319
  FrLs: 'long',
328
320
  strokeStyleLineDashSet: 'UntF',
329
321
  Trnf: 'doub',
330
322
  nonAffineTransform: 'doub',
331
323
  keyDescriptorList: 'Objc',
324
+ keyRootDescriptorList: 'Objc',
325
+ pathList: 'Objc',
332
326
  gradientFillMulti: 'Objc',
333
327
  solidFillMulti: 'Objc',
334
328
  frameFXMulti: 'Objc',
@@ -419,6 +413,15 @@ function getTypeByKey(key, value, root, parent) {
419
413
  else if ((key === 'Inpt' || key === 'Otpt') && parent._classID === 'HsbP') {
420
414
  return 'enum';
421
415
  }
416
+ else if ((key === 'Inpt' || key === 'Otpt') && parent._classID === 'LvlA') {
417
+ return 'VlLs';
418
+ }
419
+ else if (key === 'Intr' && typeof value === 'boolean') {
420
+ return 'bool'; // interpolation in print style
421
+ }
422
+ else if (key === 'PgPs') {
423
+ return 'enum';
424
+ }
422
425
  else if (key === 'LghD' && parent._classID === 'oilPaint') {
423
426
  return 'long';
424
427
  }
@@ -431,7 +434,7 @@ function readAsciiStringOrClassId(reader) {
431
434
  return (0, psdReader_1.readAsciiString)(reader, length || 4);
432
435
  }
433
436
  function writeAsciiStringOrClassId(writer, value) {
434
- if (value.length === 4 && value !== 'warp' && value !== 'time' && value !== 'hold' && value !== 'list') {
437
+ if (value.length === 4 && value !== 'warp' && value !== 'time' && value !== 'hold' && value !== 'list' && value !== 'json') {
435
438
  // write classId
436
439
  (0, psdWriter_1.writeInt32)(writer, 0);
437
440
  (0, psdWriter_1.writeSignature)(writer, value);
@@ -459,12 +462,20 @@ function readDescriptorStructure(reader, includeClass) {
459
462
  }
460
463
  return object;
461
464
  }
465
+ exports.outputSettingsRoot = 'outputSettings';
462
466
  function writeDescriptorStructure(writer, name, classId, value, root) {
463
467
  if (logErrors && !classId)
464
468
  console.log('Missing classId for: ', name, classId, value);
465
469
  // write class structure
466
470
  (0, psdWriter_1.writeUnicodeStringWithPadding)(writer, name);
467
- writeAsciiStringOrClassId(writer, classId);
471
+ if (root === exports.outputSettingsRoot && classId === 'null') {
472
+ // Photoshop writes 'null' class id as 4 character string in save for web output settings
473
+ (0, psdWriter_1.writeInt32)(writer, 4);
474
+ (0, psdWriter_1.writeSignature)(writer, classId);
475
+ }
476
+ else {
477
+ writeAsciiStringOrClassId(writer, classId);
478
+ }
468
479
  const allKeys = Object.keys(value);
469
480
  const keys = allKeys.filter(key => key !== '_name' && key !== '_classID' && value[key] !== undefined);
470
481
  (0, psdWriter_1.writeUint32)(writer, keys.length);
@@ -574,12 +585,17 @@ function writeDescriptorStructure(writer, name, classId, value, root) {
574
585
  else if (key === 'bounds' && root === 'quiltWarp') {
575
586
  extType = makeType('', 'classFloatRect');
576
587
  }
588
+ // guess color class from its fields (colors created by serializeColor always have _classID set)
577
589
  if (extType && extType.classID === 'RGBC') {
578
590
  if ('H ' in value[key])
579
591
  extType = { classID: 'HSBC', name: '' };
580
- // TODO: other color spaces
592
+ else if ('Cyn ' in value[key])
593
+ extType = { classID: 'CMYC', name: '' };
594
+ else if ('Lmnc' in value[key])
595
+ extType = { classID: 'LbCl', name: '' };
596
+ else if ('Gry ' in value[key])
597
+ extType = { classID: 'Grsc', name: '' };
581
598
  }
582
- type = getRecordedOSType(value[key], type);
583
599
  if (!type)
584
600
  type = guessOSType(value[key]);
585
601
  writeAsciiStringOrClassId(writer, key);
@@ -594,9 +610,8 @@ function readOSType(reader, type, includeClass) {
594
610
  case 'obj ': // Reference
595
611
  return readReferenceStructure(reader);
596
612
  case 'Objc': // Descriptor
597
- return readDescriptorStructure(reader, includeClass);
598
613
  case 'GlbO': // GlobalObject same as Descriptor
599
- return setHidden(readDescriptorStructure(reader, includeClass), osTypeKey, type);
614
+ return readDescriptorStructure(reader, includeClass);
600
615
  case 'VlLs': { // List
601
616
  const length = (0, psdReader_1.readInt32)(reader);
602
617
  const items = [];
@@ -621,7 +636,7 @@ function readOSType(reader, type, includeClass) {
621
636
  const value = (0, psdReader_1.readFloat32)(reader);
622
637
  if (!unitsMap[units])
623
638
  throw new Error(`Invalid units: ${units}`);
624
- return setHidden({ units: unitsMap[units], value }, osTypeKey, type);
639
+ return { units: unitsMap[units], value };
625
640
  }
626
641
  case 'TEXT': // String
627
642
  return (0, psdReader_1.readUnicodeString)(reader);
@@ -635,13 +650,13 @@ function readOSType(reader, type, includeClass) {
635
650
  case 'comp': { // Large Integer
636
651
  const low = (0, psdReader_1.readUint32)(reader);
637
652
  const high = (0, psdReader_1.readUint32)(reader);
638
- return setHidden({ low, high }, osTypeKey, type);
653
+ return { low, high };
639
654
  }
640
655
  case 'bool': // Boolean
641
656
  return !!(0, psdReader_1.readUint8)(reader);
642
657
  case 'type': // Class
643
658
  case 'GlbC': // Class
644
- return setHidden(readClassStructure(reader), osTypeKey, type);
659
+ return readClassStructure(reader);
645
660
  case 'alis': { // Alias
646
661
  const length = (0, psdReader_1.readInt32)(reader);
647
662
  return (0, psdReader_1.readAsciiString)(reader, length);
@@ -653,21 +668,21 @@ function readOSType(reader, type, includeClass) {
653
668
  case 'ObAr': { // Object array
654
669
  (0, psdReader_1.readInt32)(reader); // version: 16
655
670
  (0, psdReader_1.readUnicodeString)(reader); // name: ''
656
- const classID = readAsciiStringOrClassId(reader); // 'rationalPoint'
671
+ readAsciiStringOrClassId(reader); // classID: 'rationalPoint'
657
672
  const length = (0, psdReader_1.readInt32)(reader);
658
673
  const items = [];
659
674
  for (let i = 0; i < length; i++) {
660
675
  const type1 = readAsciiStringOrClassId(reader); // type Hrzn | Vrtc
661
- const valueType = (0, psdReader_1.readSignature)(reader); // UnFl
662
- const units = (0, psdReader_1.readSignature)(reader); // units ? '#Pxl'
676
+ (0, psdReader_1.readSignature)(reader); // UnFl
677
+ (0, psdReader_1.readSignature)(reader); // units ? '#Pxl'
663
678
  const valuesCount = (0, psdReader_1.readInt32)(reader);
664
679
  const values = [];
665
680
  for (let j = 0; j < valuesCount; j++) {
666
681
  values.push((0, psdReader_1.readFloat64)(reader));
667
682
  }
668
- items.push(setHidden({ type: type1, values }, obArInfoKey, { valueType, units }));
683
+ items.push({ type: type1, values });
669
684
  }
670
- return setHidden(items, obArInfoKey, { classID });
685
+ return items;
671
686
  }
672
687
  case 'Pth ': { // File path
673
688
  /*const length =*/ (0, psdReader_1.readInt32)(reader); // total size of all fields below
@@ -687,7 +702,7 @@ const ObArTypes = {
687
702
  quiltSliceY: 'UntF',
688
703
  };
689
704
  function writeOSType(writer, type, value, key, extType, root) {
690
- var _a, _b, _c, _d, _e;
705
+ var _a;
691
706
  switch (type) {
692
707
  case 'obj ': // Reference
693
708
  writeReferenceStructure(writer, key, value);
@@ -708,7 +723,7 @@ function writeOSType(writer, type, value, key, extType, root) {
708
723
  throw new Error(`Invalid list value: ${JSON.stringify(value)}, key: ${key}`);
709
724
  (0, psdWriter_1.writeInt32)(writer, value.length);
710
725
  for (let i = 0; i < value.length; i++) {
711
- const type = (_a = getRecordedOSType(value[i], fieldToArrayType[key])) !== null && _a !== void 0 ? _a : guessOSType(value[i]);
726
+ const type = (_a = fieldToArrayType[key]) !== null && _a !== void 0 ? _a : guessOSType(value[i]);
712
727
  (0, psdWriter_1.writeSignature)(writer, type || 'long');
713
728
  writeOSType(writer, type || 'long', value[i], `${key}[]`, fieldToArrayExtType[key], root);
714
729
  if (logErrors && !type)
@@ -782,16 +797,15 @@ function writeOSType(writer, type, value, key, extType, root) {
782
797
  case 'ObAr': { // Object array
783
798
  (0, psdWriter_1.writeInt32)(writer, 16); // version
784
799
  (0, psdWriter_1.writeUnicodeStringWithPadding)(writer, ''); // name
785
- const type = (_c = (_b = getHidden(value, obArInfoKey)) === null || _b === void 0 ? void 0 : _b.classID) !== null && _c !== void 0 ? _c : ObArTypes[key];
800
+ const type = ObArTypes[key];
786
801
  if (!type)
787
802
  throw new Error(`Not implemented ObArType for: ${key}`);
788
803
  writeAsciiStringOrClassId(writer, type);
789
804
  (0, psdWriter_1.writeInt32)(writer, value.length);
790
805
  for (let i = 0; i < value.length; i++) {
791
- const info = getHidden(value[i], obArInfoKey);
792
806
  writeAsciiStringOrClassId(writer, value[i].type); // Hrzn | Vrtc
793
- (0, psdWriter_1.writeSignature)(writer, (_d = info === null || info === void 0 ? void 0 : info.valueType) !== null && _d !== void 0 ? _d : 'UnFl');
794
- (0, psdWriter_1.writeSignature)(writer, (_e = info === null || info === void 0 ? void 0 : info.units) !== null && _e !== void 0 ? _e : '#Pxl');
807
+ (0, psdWriter_1.writeSignature)(writer, 'UnFl');
808
+ (0, psdWriter_1.writeSignature)(writer, '#Pxl');
795
809
  (0, psdWriter_1.writeInt32)(writer, value[i].values.length);
796
810
  for (let j = 0; j < value[i].values.length; j++) {
797
811
  (0, psdWriter_1.writeFloat64)(writer, value[i].values[j]);
@@ -800,83 +814,60 @@ function writeOSType(writer, type, value, key, extType, root) {
800
814
  break;
801
815
  }
802
816
  case 'Pth ': { // File path
803
- const length = 4 + 4 + 4 + value.path.length * 2;
817
+ const path = value.path + '\0'; // Photoshop stores trailing null character (stripped when reading)
818
+ const length = 4 + 4 + 4 + path.length * 2;
804
819
  (0, psdWriter_1.writeInt32)(writer, length);
805
820
  (0, psdWriter_1.writeSignature)(writer, value.sig);
806
821
  (0, psdWriter_1.writeInt32LE)(writer, length);
807
- (0, psdWriter_1.writeInt32LE)(writer, value.path.length);
808
- (0, psdWriter_1.writeUnicodeStringWithoutLengthLE)(writer, value.path);
822
+ (0, psdWriter_1.writeInt32LE)(writer, path.length);
823
+ (0, psdWriter_1.writeUnicodeStringWithoutLengthLE)(writer, path);
809
824
  break;
810
825
  }
811
826
  default:
812
827
  throw new Error(`Not implemented descriptor OSType: ${type}`);
813
828
  }
814
829
  }
815
- function referenceItemToValue(item) {
816
- switch (item.type) {
817
- case 'prop': return item.keyID;
818
- case 'Clss': return { name: item.name || '', classID: item.classID };
819
- case 'Enmr': return `${item.typeID}.${item.enumValue}`;
820
- default: return item.value;
821
- }
822
- }
823
- function referenceItemMatches(item, value) {
824
- if (!item)
825
- return false;
826
- const expected = referenceItemToValue(item);
827
- if (expected && typeof expected === 'object') {
828
- return !!value && typeof value === 'object' && value.name === expected.name && value.classID === expected.classID;
829
- }
830
- return expected === value;
831
- }
832
- // Attaches information about how each item of reference should be written (class IDs, reference types),
833
- // the public value of the array stays the same (array of strings/numbers/class objects)
834
- function createDescriptorReference(items) {
835
- return setHidden(items.map(referenceItemToValue), referenceInfoKey, items);
830
+ const referenceTypes = ['prop', 'Clss', 'Enmr', 'rele', 'Idnt', 'indx', 'name'];
831
+ function isReferenceItem(value) {
832
+ return !!value && typeof value === 'object' && referenceTypes.indexOf(value.type) !== -1;
836
833
  }
837
834
  function readReferenceStructure(reader) {
838
835
  const itemsCount = (0, psdReader_1.readInt32)(reader);
839
- const infos = [];
836
+ const items = [];
840
837
  for (let i = 0; i < itemsCount; i++) {
841
838
  const type = (0, psdReader_1.readSignature)(reader);
842
839
  switch (type) {
843
- case 'prop': { // Property
844
- const { name, classID } = readClassStructure(reader);
845
- const keyID = readAsciiStringOrClassId(reader);
846
- infos.push({ type, name, classID, keyID });
840
+ case 'prop': // Property
841
+ readClassStructure(reader);
842
+ items.push(readAsciiStringOrClassId(reader));
847
843
  break;
848
- }
849
- case 'Clss': { // Class
850
- const { name, classID } = readClassStructure(reader);
851
- infos.push({ type, name, classID });
844
+ case 'Clss': // Class
845
+ items.push(readClassStructure(reader));
852
846
  break;
853
- }
854
847
  case 'Enmr': { // Enumerated Reference
855
- const { name, classID } = readClassStructure(reader);
848
+ readClassStructure(reader);
856
849
  const typeID = readAsciiStringOrClassId(reader);
857
850
  const enumValue = readAsciiStringOrClassId(reader);
858
- infos.push({ type, name, classID, typeID, enumValue });
851
+ items.push(`${typeID}.${enumValue}`);
859
852
  break;
860
853
  }
861
- case 'rele': { // Offset
862
- const { name, classID } = readClassStructure(reader);
863
- infos.push({ type, name, classID, value: (0, psdReader_1.readUint32)(reader) });
854
+ case 'rele': // Offset
855
+ readClassStructure(reader);
856
+ items.push((0, psdReader_1.readUint32)(reader));
864
857
  break;
865
- }
866
858
  case 'Idnt': // Identifier
867
859
  case 'indx': // Index
868
- infos.push({ type, value: (0, psdReader_1.readInt32)(reader) });
860
+ items.push((0, psdReader_1.readInt32)(reader));
869
861
  break;
870
- case 'name': { // Name
871
- const { name, classID } = readClassStructure(reader);
872
- infos.push({ type, name, classID, value: (0, psdReader_1.readUnicodeString)(reader) });
862
+ case 'name': // Name
863
+ readClassStructure(reader);
864
+ items.push((0, psdReader_1.readUnicodeString)(reader));
873
865
  break;
874
- }
875
866
  default:
876
867
  throw new Error(`Invalid descriptor reference type: ${type}`);
877
868
  }
878
869
  }
879
- return createDescriptorReference(infos);
870
+ return items;
880
871
  }
881
872
  function guessReferenceItem(value) {
882
873
  if (typeof value === 'string') {
@@ -884,17 +875,17 @@ function guessReferenceItem(value) {
884
875
  // (values like 'My.Path' or 'Logo.v2' can't be typeID/enum value, so they are written as name references)
885
876
  const match = /^([a-z ]{4}|[a-z]{5,})\.([a-z ]+)$/i.exec(value);
886
877
  if (match) {
887
- return { type: 'Enmr', name: '', classID: match[1], typeID: match[1], enumValue: match[2] };
878
+ return { type: 'Enmr', _name: '', _classID: match[1], typeID: match[1], enumValue: match[2] };
888
879
  }
889
880
  else {
890
- return { type: 'name', name: '', classID: 'Lyr ', value };
881
+ return { type: 'name', _name: '', _classID: 'Lyr ', value };
891
882
  }
892
883
  }
893
884
  else if (typeof value === 'number') {
894
885
  return { type: 'Idnt', value };
895
886
  }
896
887
  else if (value && typeof value === 'object' && typeof value.classID === 'string') {
897
- return { type: 'Clss', name: value.name || '', classID: value.classID };
888
+ return { type: 'Clss', _name: value.name || '', _classID: value.classID };
898
889
  }
899
890
  else {
900
891
  throw new Error(`Invalid descriptor reference value: ${JSON.stringify(value)}`);
@@ -903,41 +894,35 @@ function guessReferenceItem(value) {
903
894
  // path selection state (image resource 1088) is a reference to paths, enumerated references there use 'Path' class
904
895
  // (name references use 'Lyr ' class, they reference layer of the vector mask)
905
896
  function pathSelectionStateToReference(items) {
906
- const infos = getHidden(items, referenceInfoKey);
907
- return createDescriptorReference(items.map((value, i) => {
908
- if (referenceItemMatches(infos === null || infos === void 0 ? void 0 : infos[i], value))
909
- return infos[i]; // keep information read from the file
897
+ return items.map(value => {
910
898
  const item = guessReferenceItem(value);
911
899
  if (item.type === 'Enmr')
912
- item.classID = 'Path';
900
+ item._classID = 'Path';
913
901
  return item;
914
- }));
902
+ });
915
903
  }
916
904
  function writeReferenceStructure(writer, _key, items) {
917
905
  if (!Array.isArray(items))
918
906
  throw new Error(`Invalid reference value: ${JSON.stringify(items)}, key: ${_key}`);
919
- const infos = getHidden(items, referenceInfoKey);
920
907
  (0, psdWriter_1.writeInt32)(writer, items.length);
921
- for (let i = 0; i < items.length; i++) {
922
- const value = items[i];
923
- const recorded = infos === null || infos === void 0 ? void 0 : infos[i];
924
- const item = referenceItemMatches(recorded, value) ? recorded : guessReferenceItem(value);
908
+ for (const value of items) {
909
+ const item = isReferenceItem(value) ? value : guessReferenceItem(value);
925
910
  (0, psdWriter_1.writeSignature)(writer, item.type);
926
911
  switch (item.type) {
927
912
  case 'prop': // Property
928
- writeClassStructure(writer, item.name || '', item.classID);
913
+ writeClassStructure(writer, item._name || '', item._classID);
929
914
  writeAsciiStringOrClassId(writer, item.keyID);
930
915
  break;
931
916
  case 'Clss': // Class
932
- writeClassStructure(writer, item.name || '', item.classID);
917
+ writeClassStructure(writer, item._name || '', item._classID);
933
918
  break;
934
919
  case 'Enmr': // Enumerated Reference
935
- writeClassStructure(writer, item.name || '', item.classID);
920
+ writeClassStructure(writer, item._name || '', item._classID);
936
921
  writeAsciiStringOrClassId(writer, item.typeID);
937
922
  writeAsciiStringOrClassId(writer, item.enumValue);
938
923
  break;
939
924
  case 'rele': // Offset
940
- writeClassStructure(writer, item.name || '', item.classID);
925
+ writeClassStructure(writer, item._name || '', item._classID);
941
926
  (0, psdWriter_1.writeUint32)(writer, item.value);
942
927
  break;
943
928
  case 'Idnt': // Identifier
@@ -945,7 +930,7 @@ function writeReferenceStructure(writer, _key, items) {
945
930
  (0, psdWriter_1.writeInt32)(writer, item.value);
946
931
  break;
947
932
  case 'name': // Name
948
- writeClassStructure(writer, item.name || '', item.classID);
933
+ writeClassStructure(writer, item._name || '', item._classID);
949
934
  (0, psdWriter_1.writeUnicodeStringWithPadding)(writer, `${item.value}`);
950
935
  break;
951
936
  default:
@@ -1004,7 +989,7 @@ function parseFxObject(fx) {
1004
989
  fillType: exports.FrFl.decode(fx.PntT),
1005
990
  blendMode: exports.BlnM.decode(fx['Md ']),
1006
991
  opacity: parsePercent(fx.Opct),
1007
- size: parseUnits(fx['Sz ']),
992
+ size: parseUnitsOrNumber(fx['Sz ']), // stored as number (pixels) in old versions
1008
993
  };
1009
994
  if (fx.present !== undefined)
1010
995
  stroke.present = fx.present;
@@ -1109,11 +1094,11 @@ function serializeEffects(e, log, multi) {
1109
1094
  }
1110
1095
  function parseEffects(info, log) {
1111
1096
  const effects = {};
1112
- const { masterFXSwitch, DrSh, dropShadowMulti, IrSh, innerShadowMulti, OrGl, IrGl, ebbl, SoFi, solidFillMulti, patternFill, GrFl, gradientFillMulti, ChFX, FrFX, frameFXMulti, numModifyingFX } = info, rest = __rest(info, ["masterFXSwitch", "DrSh", "dropShadowMulti", "IrSh", "innerShadowMulti", "OrGl", "IrGl", "ebbl", "SoFi", "solidFillMulti", "patternFill", "GrFl", "gradientFillMulti", "ChFX", "FrFX", "frameFXMulti", "numModifyingFX"]);
1097
+ const _a = info, { _name, _classID, 'Scl ': scale, masterFXSwitch, DrSh, dropShadowMulti, IrSh, innerShadowMulti, OrGl, IrGl, ebbl, SoFi, solidFillMulti, patternFill, GrFl, gradientFillMulti, ChFX, FrFX, frameFXMulti, numModifyingFX } = _a, rest = __rest(_a, ["_name", "_classID", 'Scl ', "masterFXSwitch", "DrSh", "dropShadowMulti", "IrSh", "innerShadowMulti", "OrGl", "IrGl", "ebbl", "SoFi", "solidFillMulti", "patternFill", "GrFl", "gradientFillMulti", "ChFX", "FrFX", "frameFXMulti", "numModifyingFX"]);
1113
1098
  if (!masterFXSwitch)
1114
1099
  effects.disabled = true;
1115
- if (info['Scl '])
1116
- effects.scale = parsePercent(info['Scl ']);
1100
+ if (scale)
1101
+ effects.scale = parsePercent(scale);
1117
1102
  if (DrSh)
1118
1103
  effects.dropShadow = [parseEffectObject(DrSh, log)];
1119
1104
  if (dropShadowMulti)
@@ -1127,7 +1112,7 @@ function parseEffects(info, log) {
1127
1112
  if (IrGl)
1128
1113
  effects.innerGlow = parseEffectObject(IrGl, log);
1129
1114
  if (ebbl)
1130
- effects.bevel = parseEffectObject(ebbl, log);
1115
+ effects.bevel = parseEffectObject(ebbl, log, 'bevel');
1131
1116
  if (SoFi)
1132
1117
  effects.solidFill = [parseEffectObject(SoFi, log)];
1133
1118
  if (solidFillMulti)
@@ -1144,7 +1129,7 @@ function parseEffects(info, log) {
1144
1129
  effects.stroke = [parseFxObject(FrFX)];
1145
1130
  if (frameFXMulti)
1146
1131
  effects.stroke = frameFXMulti.map(i => parseFxObject(i));
1147
- if (log && Object.keys(rest).length > 1)
1132
+ if (log && Object.keys(rest).length)
1148
1133
  console.log('Unhandled effect keys:', rest);
1149
1134
  return effects;
1150
1135
  }
@@ -1253,7 +1238,7 @@ function serializeTrackList(tracks) {
1253
1238
  }
1254
1239
  return trackList;
1255
1240
  }
1256
- function parseEffectObject(obj, reportErrors) {
1241
+ function parseEffectObject(obj, reportErrors, objName) {
1257
1242
  const result = {};
1258
1243
  for (const key of Object.keys(obj)) {
1259
1244
  const val = obj[key];
@@ -1318,7 +1303,8 @@ function parseEffectObject(obj, reportErrors) {
1318
1303
  case 'Type':
1319
1304
  result.type = exports.GrdT.decode(val);
1320
1305
  break;
1321
- case 'gs99':
1306
+ case 'gs99': // gradient overlay
1307
+ case 'gradientsInterpolationMethod': // glows
1322
1308
  result.interpolationMethod = exports.gradientInterpolationMethodType.decode(val);
1323
1309
  break;
1324
1310
  case 'Opct':
@@ -1370,18 +1356,26 @@ function parseEffectObject(obj, reportErrors) {
1370
1356
  result.pattern = { name: val['Nm '], id: val.Idnt };
1371
1357
  break;
1372
1358
  case 'phase':
1373
- result.phase = { x: val.Hrzn, y: val.Vrtc };
1359
+ result.phase = parsePhase(val);
1374
1360
  break;
1375
1361
  case 'Ofst':
1376
1362
  result.offset = { x: parsePercent(val.Hrzn), y: parsePercent(val.Vrtc) };
1377
1363
  break;
1378
1364
  case 'MpgS':
1379
- case 'TrnS':
1380
- result.contour = {
1365
+ case 'TrnS': {
1366
+ const contour = {
1381
1367
  name: val['Nm '],
1382
1368
  curve: val['Crv '].map(p => ({ x: p.Hrzn, y: p.Vrtc })),
1383
1369
  };
1370
+ // bevel can have both: gloss contour (TrnS) and contour of the shape (MpgS)
1371
+ if (objName === 'bevel' && key === 'MpgS') {
1372
+ result.shapeContour = contour;
1373
+ }
1374
+ else {
1375
+ result.contour = contour;
1376
+ }
1384
1377
  break;
1378
+ }
1385
1379
  case 'Grad':
1386
1380
  result.gradient = parseGradient(val);
1387
1381
  break;
@@ -1476,7 +1470,7 @@ function serializeEffectObject(obj, objName, reportErrors) {
1476
1470
  result.Type = exports.GrdT.encode(val);
1477
1471
  break;
1478
1472
  case 'interpolationMethod':
1479
- result.gs99 = exports.gradientInterpolationMethodType.encode(val);
1473
+ result[(objName === 'outerGlow' || objName === 'innerGlow') ? 'gradientsInterpolationMethod' : 'gs99'] = exports.gradientInterpolationMethodType.encode(val);
1480
1474
  break;
1481
1475
  case 'opacity':
1482
1476
  result.Opct = unitsPercent(val);
@@ -1532,15 +1526,14 @@ function serializeEffectObject(obj, objName, reportErrors) {
1532
1526
  result.phase = { Hrzn: val.x, Vrtc: val.y };
1533
1527
  break;
1534
1528
  case 'offset':
1535
- result.Ofst = { Hrzn: unitsPercent(val.x), Vrtc: unitsPercent(val.y) };
1529
+ result.Ofst = { Hrzn: unitsPercentF(val.x), Vrtc: unitsPercentF(val.y) };
1536
1530
  break;
1537
- case 'contour': {
1538
- result[objName === 'satin' ? 'MpgS' : 'TrnS'] = {
1539
- 'Nm ': val.name,
1540
- 'Crv ': val.curve.map(p => ({ Hrzn: p.x, Vrtc: p.y })),
1541
- };
1531
+ case 'contour':
1532
+ result[objName === 'satin' ? 'MpgS' : 'TrnS'] = serializeContour(val);
1533
+ break;
1534
+ case 'shapeContour':
1535
+ result.MpgS = serializeContour(val);
1542
1536
  break;
1543
- }
1544
1537
  case 'gradient':
1545
1538
  result.Grad = serializeGradient(val);
1546
1539
  break;
@@ -1558,6 +1551,12 @@ function serializeEffectObject(obj, objName, reportErrors) {
1558
1551
  }
1559
1552
  return result;
1560
1553
  }
1554
+ function serializeContour(contour) {
1555
+ return {
1556
+ 'Nm ': contour.name,
1557
+ 'Crv ': contour.curve.map(p => ({ Hrzn: p.x, Vrtc: p.y })),
1558
+ };
1559
+ }
1561
1560
  function parseGradient(grad) {
1562
1561
  if (grad.GrdF === 'GrdF.CstS') {
1563
1562
  // stop locations are always in 0..4096 range, independent of smoothness (Intr)
@@ -1566,11 +1565,22 @@ function parseGradient(grad) {
1566
1565
  type: 'solid',
1567
1566
  name: grad['Nm '],
1568
1567
  smoothness: grad.Intr / 4096,
1569
- colorStops: grad.Clrs.map(s => ({
1570
- color: parseColor(s['Clr ']),
1571
- location: s.Lctn / samples,
1572
- midpoint: s.Mdpn / 100,
1573
- })),
1568
+ colorStops: grad.Clrs.map(s => {
1569
+ const stop = {
1570
+ color: s['Clr '] ? parseColor(s['Clr ']) : { r: 0, g: 0, b: 0 },
1571
+ location: s.Lctn / samples,
1572
+ midpoint: s.Mdpn / 100,
1573
+ };
1574
+ if (s.Type) {
1575
+ try {
1576
+ const type = exports.Clry.decode(s.Type);
1577
+ if (type !== 'user')
1578
+ stop.type = type;
1579
+ }
1580
+ catch (_a) { } // unknown stop type, read as user color
1581
+ }
1582
+ return stop;
1583
+ }),
1574
1584
  opacityStops: grad.Trns.map(s => ({
1575
1585
  opacity: parsePercent(s.Opct),
1576
1586
  location: s.Lctn / samples,
@@ -1602,18 +1612,18 @@ function serializeGradient(grad) {
1602
1612
  GrdF: 'GrdF.CstS',
1603
1613
  Intr: Math.round(((_a = grad.smoothness) !== null && _a !== void 0 ? _a : 1) * 4096),
1604
1614
  Clrs: grad.colorStops.map(s => {
1605
- var _a;
1615
+ var _a, _b;
1606
1616
  return ({
1607
1617
  'Clr ': serializeColor(s.color),
1608
- Type: 'Clry.UsrS',
1618
+ Type: exports.Clry.encode((_a = s.type) !== null && _a !== void 0 ? _a : 'user'),
1609
1619
  Lctn: Math.round(s.location * samples),
1610
- Mdpn: Math.round(((_a = s.midpoint) !== null && _a !== void 0 ? _a : 0.5) * 100),
1620
+ Mdpn: Math.round(((_b = s.midpoint) !== null && _b !== void 0 ? _b : 0.5) * 100),
1611
1621
  });
1612
1622
  }),
1613
1623
  Trns: grad.opacityStops.map(s => {
1614
1624
  var _a;
1615
1625
  return ({
1616
- Opct: unitsPercent(s.opacity),
1626
+ Opct: unitsPercentF(s.opacity),
1617
1627
  Lctn: Math.round(s.location * samples),
1618
1628
  Mdpn: Math.round(((_a = s.midpoint) !== null && _a !== void 0 ? _a : 0.5) * 100),
1619
1629
  });
@@ -1657,6 +1667,13 @@ function parseGradientContent(descriptor) {
1657
1667
  }
1658
1668
  return result;
1659
1669
  }
1670
+ // phase is usually stored as plain numbers, but some files store it as unit values
1671
+ function parsePhase({ Hrzn, Vrtc }) {
1672
+ return {
1673
+ x: typeof Hrzn === 'number' ? Hrzn : Hrzn.value,
1674
+ y: typeof Vrtc === 'number' ? Vrtc : Vrtc.value,
1675
+ };
1676
+ }
1660
1677
  function parsePatternContent(descriptor) {
1661
1678
  const result = {
1662
1679
  name: descriptor.Ptrn['Nm '],
@@ -1665,7 +1682,11 @@ function parsePatternContent(descriptor) {
1665
1682
  if (descriptor.Lnkd !== undefined)
1666
1683
  result.linked = descriptor.Lnkd;
1667
1684
  if (descriptor.phase !== undefined)
1668
- result.phase = { x: descriptor.phase.Hrzn, y: descriptor.phase.Vrtc };
1685
+ result.phase = parsePhase(descriptor.phase);
1686
+ if (descriptor['Scl '] !== undefined)
1687
+ result.scale = parsePercent(descriptor['Scl ']);
1688
+ if (descriptor.Angl !== undefined)
1689
+ result.angle = parseAngle(descriptor.Angl);
1669
1690
  return result;
1670
1691
  }
1671
1692
  function parseVectorContent(descriptor) {
@@ -1696,11 +1717,11 @@ function serializeGradientContent(content) {
1696
1717
  if (content.align !== undefined)
1697
1718
  result.Algn = content.align;
1698
1719
  if (content.scale !== undefined)
1699
- result['Scl '] = unitsPercent(content.scale);
1720
+ result['Scl '] = unitsPercentF(content.scale);
1700
1721
  if (content.offset) {
1701
1722
  result.Ofst = {
1702
- Hrzn: unitsPercent(content.offset.x),
1703
- Vrtc: unitsPercent(content.offset.y),
1723
+ Hrzn: unitsPercentF(content.offset.x),
1724
+ Vrtc: unitsPercentF(content.offset.y),
1704
1725
  };
1705
1726
  }
1706
1727
  result.Grad = serializeGradient(content);
@@ -1717,6 +1738,10 @@ function serializePatternContent(content) {
1717
1738
  result.Lnkd = !!content.linked;
1718
1739
  if (content.phase !== undefined)
1719
1740
  result.phase = { Hrzn: content.phase.x, Vrtc: content.phase.y };
1741
+ if (content.scale !== undefined)
1742
+ result['Scl '] = unitsPercentF(content.scale);
1743
+ if (content.angle !== undefined)
1744
+ result.Angl = unitsAngle(content.angle);
1720
1745
  return result;
1721
1746
  }
1722
1747
  function serializeVectorContent(content) {
@@ -1732,9 +1757,9 @@ function serializeVectorContent(content) {
1732
1757
  }
1733
1758
  // Descriptor color values: HSB saturation/brightness 0..100, CMYK 0..100, Gray 0..100, Lab L 0..100, a/b -128..127
1734
1759
  // Color values (see psd.ts): HSB 0..1, CMYK 0..255, Gray 0..255, Lab L 0..1, a/b -1..1 (same as readColor/writeColor)
1735
- const colorSourceKey = typeof Symbol !== 'undefined' ? Symbol('agPsdColorSource') : '__agPsdColorSource';
1736
1760
  const colorConversions = {
1737
1761
  // key: [descriptor -> color, color -> descriptor]
1762
+ 'H ': [x => x / 360, x => x * 360], // angle
1738
1763
  Strt: [x => x / 100, x => x * 100],
1739
1764
  Brgh: [x => x / 100, x => x * 100],
1740
1765
  'Cyn ': [x => x * 255 / 100, x => x * 100 / 255],
@@ -1762,22 +1787,18 @@ function colorToDescriptorValue(key, value) {
1762
1787
  }
1763
1788
  return result;
1764
1789
  }
1765
- function shallowEqual(a, b) {
1766
- const keys = Object.keys(a);
1767
- if (keys.length !== Object.keys(b).length)
1768
- return false;
1769
- return keys.every(key => a[key] === b[key]);
1790
+ function isBookColor(color) {
1791
+ return color._classID === 'BkCl' || 'Bk ' in color || 'bookID' in color;
1770
1792
  }
1771
1793
  function parseColor(color) {
1772
- let result;
1773
1794
  if ('H ' in color) {
1774
- result = { h: parsePercentOrAngle(color['H ']), s: color.Strt / 100, b: color.Brgh / 100 };
1795
+ return { h: parsePercentOrAngle(color['H ']), s: color.Strt / 100, b: color.Brgh / 100 };
1775
1796
  }
1776
1797
  else if ('Rd ' in color) {
1777
1798
  return { r: color['Rd '], g: color['Grn '], b: color['Bl '] };
1778
1799
  }
1779
1800
  else if ('Cyn ' in color) {
1780
- result = {
1801
+ return {
1781
1802
  c: color['Cyn '] * 255 / 100,
1782
1803
  m: color.Mgnt * 255 / 100,
1783
1804
  y: color['Ylw '] * 255 / 100,
@@ -1785,21 +1806,22 @@ function parseColor(color) {
1785
1806
  };
1786
1807
  }
1787
1808
  else if ('Gry ' in color) {
1788
- result = { k: color['Gry '] * 255 / 100 };
1809
+ return { k: color['Gry '] * 255 / 100 };
1789
1810
  }
1790
1811
  else if ('Lmnc' in color) {
1791
- result = { l: color.Lmnc / 100, a: (0, helpers_1.normalizeLabAB)(color['A ']), b: (0, helpers_1.normalizeLabAB)(color['B ']) };
1812
+ return { l: color.Lmnc / 100, a: (0, helpers_1.normalizeLabAB)(color['A ']), b: (0, helpers_1.normalizeLabAB)(color['B ']) };
1792
1813
  }
1793
1814
  else if ('redFloat' in color) {
1794
1815
  return { fr: color.redFloat, fg: color.greenFloat, fb: color.blueFloat };
1795
1816
  }
1817
+ else if (isBookColor(color)) {
1818
+ return { r: 0, g: 0, b: 0 }; // book (Pantone) colors are not supported
1819
+ }
1796
1820
  else {
1797
1821
  throw new Error('Unsupported color descriptor');
1798
1822
  }
1799
- // remember original descriptor values, so unmodified colors can be written back without conversion errors
1800
- return setHidden(result, colorSourceKey, { color: Object.assign({}, result), descriptor: color });
1801
1823
  }
1802
- function serializeColorValues(color) {
1824
+ function serializeColor(color) {
1803
1825
  if (!color) {
1804
1826
  return { _name: '', _classID: 'RGBC', 'Rd ': 0, 'Grn ': 0, 'Bl ': 0 };
1805
1827
  }
@@ -1812,7 +1834,7 @@ function serializeColorValues(color) {
1812
1834
  else if ('h' in color) {
1813
1835
  return {
1814
1836
  _name: '', _classID: 'HSBC',
1815
- 'H ': unitsAngle((color.h || 0) * 360),
1837
+ 'H ': unitsAngle(colorToDescriptorValue('H ', color.h || 0)),
1816
1838
  Strt: colorToDescriptorValue('Strt', color.s || 0),
1817
1839
  Brgh: colorToDescriptorValue('Brgh', color.b || 0),
1818
1840
  };
@@ -1841,19 +1863,6 @@ function serializeColorValues(color) {
1841
1863
  throw new Error('Invalid color value');
1842
1864
  }
1843
1865
  }
1844
- function serializeColor(color) {
1845
- const result = serializeColorValues(color);
1846
- const source = getHidden(color, colorSourceKey);
1847
- if (source && shallowEqual(source.color, color)) {
1848
- // color was not modified since it was read, use exact original values
1849
- for (const key of Object.keys(result)) {
1850
- if (colorConversions[key] && typeof source.descriptor[key] === 'number') {
1851
- result[key] = source.descriptor[key];
1852
- }
1853
- }
1854
- }
1855
- return result;
1856
- }
1857
1866
  function parseAngle(x) {
1858
1867
  if (x === undefined)
1859
1868
  return 0;
@@ -1879,7 +1888,8 @@ function parsePercentOrAngle(x) {
1879
1888
  }
1880
1889
  function parseUnits({ units, value }) {
1881
1890
  if (units !== 'Pixels' && units !== 'Millimeters' && units !== 'Points' && units !== 'None' &&
1882
- units !== 'Picas' && units !== 'Inches' && units !== 'Centimeters' && units !== 'Density') {
1891
+ units !== 'Picas' && units !== 'Inches' && units !== 'Centimeters' && units !== 'Density' &&
1892
+ units !== 'Distance') {
1883
1893
  throw new Error(`Invalid units: ${JSON.stringify({ units, value })}`);
1884
1894
  }
1885
1895
  return { value, units };
@@ -1912,7 +1922,8 @@ function unitsValue(x, key) {
1912
1922
  if (typeof value !== 'number')
1913
1923
  throw new Error(`Invalid value in ${JSON.stringify(x)} (key: ${key})`);
1914
1924
  if (units !== 'Pixels' && units !== 'Millimeters' && units !== 'Points' && units !== 'None' &&
1915
- units !== 'Picas' && units !== 'Inches' && units !== 'Centimeters' && units !== 'Density') {
1925
+ units !== 'Picas' && units !== 'Inches' && units !== 'Centimeters' && units !== 'Density' &&
1926
+ units !== 'Distance') {
1916
1927
  throw new Error(`Invalid units in ${JSON.stringify(x)} (key: ${key})`);
1917
1928
  }
1918
1929
  return { units, value };
@@ -2040,6 +2051,11 @@ exports.gradientInterpolationMethodType = (0, helpers_1.createEnum)('gradientInt
2040
2051
  smooth: 'Smoo',
2041
2052
  // TODO: stripes
2042
2053
  });
2054
+ exports.Clry = (0, helpers_1.createEnum)('Clry', 'user', {
2055
+ user: 'UsrS',
2056
+ foreground: 'FrgC',
2057
+ background: 'BckC',
2058
+ });
2043
2059
  exports.ClrS = (0, helpers_1.createEnum)('ClrS', 'rgb', {
2044
2060
  rgb: 'RGBC',
2045
2061
  hsb: 'HSBl',