ag-psd 31.0.3 → 32.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 (138) hide show
  1. package/.v8-cache/v24.7.0-x64-ef5a0af0/00d0e0b4 +0 -0
  2. package/.v8-cache/v24.7.0-x64-ef5a0af0/05135617 +0 -0
  3. package/.v8-cache/v24.7.0-x64-ef5a0af0/0cc76730 +0 -0
  4. package/.v8-cache/v24.7.0-x64-ef5a0af0/1130885f +0 -0
  5. package/.v8-cache/v24.7.0-x64-ef5a0af0/20729831 +0 -0
  6. package/.v8-cache/v24.7.0-x64-ef5a0af0/214a593b +0 -0
  7. package/.v8-cache/v24.7.0-x64-ef5a0af0/226e2801 +0 -0
  8. package/.v8-cache/v24.7.0-x64-ef5a0af0/25f8b84a +0 -0
  9. package/.v8-cache/v24.7.0-x64-ef5a0af0/27518289 +0 -0
  10. package/.v8-cache/v24.7.0-x64-ef5a0af0/330ef2d9 +0 -0
  11. package/.v8-cache/v24.7.0-x64-ef5a0af0/37ff16b8 +0 -0
  12. package/.v8-cache/v24.7.0-x64-ef5a0af0/3f72a97c +0 -0
  13. package/.v8-cache/v24.7.0-x64-ef5a0af0/4511bacf +0 -0
  14. package/.v8-cache/v24.7.0-x64-ef5a0af0/68416cb2 +0 -0
  15. package/.v8-cache/v24.7.0-x64-ef5a0af0/71df9cb3 +0 -0
  16. package/.v8-cache/v24.7.0-x64-ef5a0af0/75efccd4 +0 -0
  17. package/.v8-cache/v24.7.0-x64-ef5a0af0/762f9305 +0 -0
  18. package/.v8-cache/v24.7.0-x64-ef5a0af0/7bcfc50a +0 -0
  19. package/.v8-cache/v24.7.0-x64-ef5a0af0/7ea06cea +0 -0
  20. package/.v8-cache/v24.7.0-x64-ef5a0af0/825e9a05 +0 -0
  21. package/.v8-cache/v24.7.0-x64-ef5a0af0/87372817 +0 -0
  22. package/.v8-cache/v24.7.0-x64-ef5a0af0/87cf2209 +0 -0
  23. package/.v8-cache/v24.7.0-x64-ef5a0af0/891a7c73 +0 -0
  24. package/.v8-cache/v24.7.0-x64-ef5a0af0/8b68a3a2 +0 -0
  25. package/.v8-cache/v24.7.0-x64-ef5a0af0/932f0770 +0 -0
  26. package/.v8-cache/v24.7.0-x64-ef5a0af0/a1d12a93 +0 -0
  27. package/.v8-cache/v24.7.0-x64-ef5a0af0/a7e80865 +0 -0
  28. package/.v8-cache/v24.7.0-x64-ef5a0af0/ac317c9c +0 -0
  29. package/.v8-cache/v24.7.0-x64-ef5a0af0/acc36e66 +0 -0
  30. package/.v8-cache/v24.7.0-x64-ef5a0af0/b3c2fab7 +0 -0
  31. package/.v8-cache/v24.7.0-x64-ef5a0af0/b8569d6b +0 -0
  32. package/.v8-cache/v24.7.0-x64-ef5a0af0/b8b073c4 +0 -0
  33. package/.v8-cache/v24.7.0-x64-ef5a0af0/bb489c89 +0 -0
  34. package/.v8-cache/v24.7.0-x64-ef5a0af0/c0b789d2 +0 -0
  35. package/.v8-cache/v24.7.0-x64-ef5a0af0/c314aece +0 -0
  36. package/.v8-cache/v24.7.0-x64-ef5a0af0/c46725e1 +0 -0
  37. package/.v8-cache/v24.7.0-x64-ef5a0af0/c794cb34 +0 -0
  38. package/.v8-cache/v24.7.0-x64-ef5a0af0/cd4ae980 +0 -0
  39. package/.v8-cache/v24.7.0-x64-ef5a0af0/cfc49f4f +0 -0
  40. package/.v8-cache/v24.7.0-x64-ef5a0af0/cff9434a +0 -0
  41. package/.v8-cache/v24.7.0-x64-ef5a0af0/d02dd6f7 +0 -0
  42. package/.v8-cache/v24.7.0-x64-ef5a0af0/dc86f000 +0 -0
  43. package/.v8-cache/v24.7.0-x64-ef5a0af0/e03e2acd +0 -0
  44. package/.v8-cache/v24.7.0-x64-ef5a0af0/e6735171 +0 -0
  45. package/.v8-cache/v24.7.0-x64-ef5a0af0/f5417276 +0 -0
  46. package/.v8-cache/v24.7.0-x64-ef5a0af0/f6fd8125 +0 -0
  47. package/.v8-cache/v24.7.0-x64-ef5a0af0/f74d3518 +0 -0
  48. package/.v8-cache/v24.7.0-x64-ef5a0af0/f886deac +0 -0
  49. package/.v8-cache/v24.7.0-x64-ef5a0af0/fd0a2987 +0 -0
  50. package/.v8-cache/v24.7.0-x64-ef5a0af0/fe349423 +0 -0
  51. package/CHANGELOG.md +29 -0
  52. package/dist/abr.js +12 -5
  53. package/dist/abr.js.map +1 -1
  54. package/dist/additionalInfo.d.ts +2 -0
  55. package/dist/additionalInfo.js +123 -68
  56. package/dist/additionalInfo.js.map +1 -1
  57. package/dist/bundle.js +1365 -632
  58. package/dist/descriptor.d.ts +15 -2
  59. package/dist/descriptor.js +329 -100
  60. package/dist/descriptor.js.map +1 -1
  61. package/dist/effectsHelpers.js +12 -12
  62. package/dist/effectsHelpers.js.map +1 -1
  63. package/dist/engineData.d.ts +3 -1
  64. package/dist/engineData.js +34 -13
  65. package/dist/engineData.js.map +1 -1
  66. package/dist/engineData2.js +50 -21
  67. package/dist/engineData2.js.map +1 -1
  68. package/dist/helpers.d.ts +5 -2
  69. package/dist/helpers.js +18 -4
  70. package/dist/helpers.js.map +1 -1
  71. package/dist/imageResources.js +45 -21
  72. package/dist/imageResources.js.map +1 -1
  73. package/dist/index.js +1 -0
  74. package/dist/index.js.map +1 -1
  75. package/dist/jpeg.js +27 -7
  76. package/dist/jpeg.js.map +1 -1
  77. package/dist/psd.d.ts +6 -1
  78. package/dist/psdReader.d.ts +2 -2
  79. package/dist/psdReader.js +457 -270
  80. package/dist/psdReader.js.map +1 -1
  81. package/dist/psdWriter.d.ts +2 -0
  82. package/dist/psdWriter.js +98 -76
  83. package/dist/psdWriter.js.map +1 -1
  84. package/dist/text.js +126 -23
  85. package/dist/text.js.map +1 -1
  86. package/dist/utf8.js +36 -15
  87. package/dist/utf8.js.map +1 -1
  88. package/dist-es/abr.js +12 -5
  89. package/dist-es/abr.js.map +1 -1
  90. package/dist-es/additionalInfo.d.ts +2 -0
  91. package/dist-es/additionalInfo.js +123 -68
  92. package/dist-es/additionalInfo.js.map +1 -1
  93. package/dist-es/descriptor.d.ts +15 -2
  94. package/dist-es/descriptor.js +329 -100
  95. package/dist-es/descriptor.js.map +1 -1
  96. package/dist-es/effectsHelpers.js +12 -12
  97. package/dist-es/effectsHelpers.js.map +1 -1
  98. package/dist-es/engineData.d.ts +3 -1
  99. package/dist-es/engineData.js +34 -13
  100. package/dist-es/engineData.js.map +1 -1
  101. package/dist-es/engineData2.js +50 -21
  102. package/dist-es/engineData2.js.map +1 -1
  103. package/dist-es/helpers.d.ts +5 -2
  104. package/dist-es/helpers.js +18 -4
  105. package/dist-es/helpers.js.map +1 -1
  106. package/dist-es/imageResources.js +45 -21
  107. package/dist-es/imageResources.js.map +1 -1
  108. package/dist-es/index.js +1 -0
  109. package/dist-es/index.js.map +1 -1
  110. package/dist-es/jpeg.js +27 -7
  111. package/dist-es/jpeg.js.map +1 -1
  112. package/dist-es/psd.d.ts +6 -1
  113. package/dist-es/psdReader.d.ts +2 -2
  114. package/dist-es/psdReader.js +457 -270
  115. package/dist-es/psdReader.js.map +1 -1
  116. package/dist-es/psdWriter.d.ts +2 -0
  117. package/dist-es/psdWriter.js +98 -76
  118. package/dist-es/psdWriter.js.map +1 -1
  119. package/dist-es/text.js +126 -23
  120. package/dist-es/text.js.map +1 -1
  121. package/dist-es/utf8.js +36 -15
  122. package/dist-es/utf8.js.map +1 -1
  123. package/package.json +1 -1
  124. package/src/abr.ts +14 -6
  125. package/src/additionalInfo.ts +127 -66
  126. package/src/descriptor.ts +347 -107
  127. package/src/effectsHelpers.ts +9 -9
  128. package/src/engineData.ts +36 -13
  129. package/src/engineData2.ts +468 -432
  130. package/src/helpers.ts +19 -4
  131. package/src/imageResources.ts +49 -22
  132. package/src/index.ts +1 -0
  133. package/src/jpeg.ts +24 -9
  134. package/src/psd.ts +3 -1
  135. package/src/psdReader.ts +477 -260
  136. package/src/psdWriter.ts +100 -83
  137. package/src/text.ts +122 -12
  138. package/src/utf8.ts +35 -17
package/dist/bundle.js CHANGED
@@ -7,6 +7,8 @@ const psdReader_1 = require("./psdReader");
7
7
  const dynamicsControl = ['off', 'fade', 'pen pressure', 'pen tilt', 'stylus wheel', 'initial direction', 'direction', 'initial rotation', 'rotation'];
8
8
  const dynamicBrushShapeShapes = ['round point', 'round blunt', 'round curve', 'round angle', 'round fan', 'flat point', 'flat blunt', 'flat curve', 'flat angle', 'flat fan'];
9
9
  const tipsBrushShapeShapes = ['erodible point', 'erodible flat', 'erodible round', 'erodible square', 'erodible triangle', 'custom'];
10
+ // sanity limits for brush sample dimensions (Photoshop brushes are limited to 5000px)
11
+ const MAX_BRUSH_SIZE = 10000;
10
12
  const toBrushType = {
11
13
  _: 'brush',
12
14
  MixB: 'mixer brush',
@@ -113,7 +115,7 @@ function readAbr(buffer, options = {}) {
113
115
  while (reader.offset < reader.view.byteLength) {
114
116
  (0, psdReader_1.checkSignature)(reader, '8BIM');
115
117
  const type = (0, psdReader_1.readSignature)(reader);
116
- let size = (0, psdReader_1.readUint32)(reader);
118
+ const size = (0, psdReader_1.readUint32)(reader);
117
119
  const end = reader.offset + size;
118
120
  switch (type) {
119
121
  case 'samp': {
@@ -132,8 +134,14 @@ function readAbr(buffer, options = {}) {
132
134
  const w = (0, psdReader_1.readInt32)(reader) - x;
133
135
  if (w <= 0 || h <= 0)
134
136
  throw new Error('Invalid bounds');
137
+ if (w > MAX_BRUSH_SIZE || h > MAX_BRUSH_SIZE) {
138
+ throw new Error(`Brush sample too large (${w}x${h})`);
139
+ }
135
140
  const bithDepth = (0, psdReader_1.readInt16)(reader);
136
141
  const compression = (0, psdReader_1.readUint8)(reader); // 0 - raw, 1 - RLE
142
+ if (compression === 0 && w * h * (bithDepth === 16 ? 2 : 1) > brushEnd - reader.offset) {
143
+ throw new Error('Brush sample data out of bounds');
144
+ }
137
145
  const alpha = new Uint8Array(w * h);
138
146
  if (bithDepth === 8) {
139
147
  if (compression === 0) {
@@ -347,11 +355,10 @@ function readAbr(buffer, options = {}) {
347
355
  default:
348
356
  throw new Error(`Invalid brush type: ${type}`);
349
357
  }
350
- // align to 4 bytes
351
- while (size % 4) {
358
+ // skip any unread data in the section and align to 4 bytes
359
+ reader.offset = end;
360
+ while (reader.offset % 4)
352
361
  reader.offset++;
353
- size++;
354
- }
355
362
  }
356
363
  }
357
364
  else {
@@ -395,10 +402,6 @@ function readLength64(reader) {
395
402
  throw new Error(`Resource size above 4 GB limit at ${reader.offset.toString(16)}`);
396
403
  return (0, psdReader_1.readUint32)(reader);
397
404
  }
398
- function writeLength64(writer, length) {
399
- (0, psdWriter_1.writeUint32)(writer, 0);
400
- (0, psdWriter_1.writeUint32)(writer, length);
401
- }
402
405
  addHandler('TySh', hasKey('text'), (reader, target, leftBytes) => {
403
406
  if ((0, psdReader_1.readInt16)(reader) !== 1)
404
407
  throw new Error(`Invalid TySh version`);
@@ -437,7 +440,7 @@ addHandler('TySh', hasKey('text'), (reader, target, leftBytes) => {
437
440
  if (text.boundingBox)
438
441
  target.text.boundingBox = (0, descriptor_1.descBoundsToBounds)(text.boundingBox);
439
442
  if (text.EngineData) {
440
- const engineData = (0, engineData_1.parseEngineData)(text.EngineData);
443
+ const engineData = (0, engineData_1.parseEngineData)(text.EngineData, reader);
441
444
  const textData = (0, text_1.decodeEngineData)(engineData);
442
445
  // console.log(require('util').inspect(engineData, false, 99, false), 'utf8');
443
446
  // require('fs').writeFileSync(`layer-${target.name}.txt`, require('util').inspect(engineData, false, 99, false), 'utf8');
@@ -451,6 +454,7 @@ addHandler('TySh', hasKey('text'), (reader, target, leftBytes) => {
451
454
  }
452
455
  (0, psdReader_1.skipBytes)(reader, leftBytes());
453
456
  }, (writer, target) => {
457
+ var _a, _b, _c, _d;
454
458
  const text = target.text;
455
459
  const warp = text.warp || {};
456
460
  const transform = text.transform || [1, 0, 0, 1, 0, 0];
@@ -463,10 +467,10 @@ addHandler('TySh', hasKey('text'), (reader, target, leftBytes) => {
463
467
  (0, descriptor_1.writeVersionAndDescriptor)(writer, '', 'TxLr', textDescriptor, 'text');
464
468
  (0, psdWriter_1.writeInt16)(writer, 1); // warp version
465
469
  (0, descriptor_1.writeVersionAndDescriptor)(writer, '', 'warp', encodeWarp(warp));
466
- (0, psdWriter_1.writeFloat32)(writer, text.left);
467
- (0, psdWriter_1.writeFloat32)(writer, text.top);
468
- (0, psdWriter_1.writeFloat32)(writer, text.right);
469
- (0, psdWriter_1.writeFloat32)(writer, text.bottom);
470
+ (0, psdWriter_1.writeFloat32)(writer, (_a = text.left) !== null && _a !== void 0 ? _a : 0);
471
+ (0, psdWriter_1.writeFloat32)(writer, (_b = text.top) !== null && _b !== void 0 ? _b : 0);
472
+ (0, psdWriter_1.writeFloat32)(writer, (_c = text.right) !== null && _c !== void 0 ? _c : 0);
473
+ (0, psdWriter_1.writeFloat32)(writer, (_d = text.bottom) !== null && _d !== void 0 ? _d : 0);
470
474
  // writeZeros(writer, 2);
471
475
  });
472
476
  // vector fills
@@ -782,12 +786,50 @@ addHandler('luni', hasKey('name'), (reader, target, left) => {
782
786
  // writeUint16(writer, 0); // padding (but not extending string length)
783
787
  });
784
788
  addHandler('lnsr', hasKey('nameSource'), (reader, target) => target.nameSource = (0, psdReader_1.readSignature)(reader), (writer, target) => (0, psdWriter_1.writeSignature)(writer, target.nameSource));
785
- addHandler('lyid', hasKey('id'), (reader, target) => {
789
+ // these sections reference layer id
790
+ function layerNeedsId(target) {
791
+ return !!(target.animationFrames || target.timeline || target.comps);
792
+ }
793
+ function getExplicitLayerIds(psd, options) {
794
+ if (!options.explicitLayerIds) {
795
+ const ids = new Set();
796
+ const addIds = (layers) => {
797
+ for (const layer of layers !== null && layers !== void 0 ? layers : []) {
798
+ if (layer.id !== undefined)
799
+ ids.add(layer.id);
800
+ addIds(layer.children);
801
+ }
802
+ };
803
+ addIds(psd.children);
804
+ options.explicitLayerIds = ids;
805
+ }
806
+ return options.explicitLayerIds;
807
+ }
808
+ function getLayerId(target, options) {
809
+ var _a;
810
+ const id = (_a = options.layerToId.get(target)) !== null && _a !== void 0 ? _a : target.id;
811
+ if (id === undefined)
812
+ throw new Error('Missing layer id');
813
+ return id;
814
+ }
815
+ addHandler('lyid', target => target.id !== undefined || layerNeedsId(target), (reader, target) => {
786
816
  target.id = (0, psdReader_1.readUint32)(reader);
787
- }, (writer, target, _psd, options) => {
788
- let id = target.id;
789
- while (options.layerIds.has(id))
790
- id += 100; // make sure we don't have duplicate layer ids
817
+ }, (writer, target, psd, options) => {
818
+ var _a;
819
+ let id;
820
+ if (target.id !== undefined) {
821
+ id = target.id;
822
+ while (options.layerIds.has(id))
823
+ id += 100; // make sure we don't have duplicate layer ids
824
+ }
825
+ else {
826
+ // generate id for layers that don't have it, if it's referenced by other sections
827
+ const explicitIds = getExplicitLayerIds(psd, options);
828
+ id = (_a = options.nextGeneratedLayerId) !== null && _a !== void 0 ? _a : 1;
829
+ while (options.layerIds.has(id) || explicitIds.has(id))
830
+ id++;
831
+ options.nextGeneratedLayerId = id + 1;
832
+ }
791
833
  (0, psdWriter_1.writeUint32)(writer, id);
792
834
  options.layerIds.add(id);
793
835
  options.layerToId.set(target, id);
@@ -985,9 +1027,8 @@ target => target.timestamp !== undefined || target.animationFrames !== undefined
985
1027
  (0, psdWriter_1.writeUint8)(writer, 0); // copy (always false)
986
1028
  (0, psdWriter_1.writeZeros)(writer, 3);
987
1029
  (0, psdWriter_1.writeSection)(writer, 2, () => {
988
- var _a;
989
1030
  const desc = {
990
- LaID: (_a = target.id) !== null && _a !== void 0 ? _a : 0,
1031
+ LaID: getLayerId(target, options),
991
1032
  LaSt: [],
992
1033
  };
993
1034
  for (let i = 0; i < animationFrames.length; i++) {
@@ -1043,10 +1084,7 @@ target => target.timestamp !== undefined || target.animationFrames !== undefined
1043
1084
  if (timeline.tracks) {
1044
1085
  desc.trackList = (0, descriptor_1.serializeTrackList)(timeline.tracks);
1045
1086
  }
1046
- const id = options.layerToId.get(target) || target.id;
1047
- if (!id)
1048
- throw new Error('You need to provide layer.id value whan writing document with animations');
1049
- desc.LyrI = id;
1087
+ desc.LyrI = getLayerId(target, options);
1050
1088
  // console.log('WRITE:tmln', target.name, target.id, require('util').inspect(desc, false, 99, true));
1051
1089
  (0, descriptor_1.writeVersionAndDescriptor)(writer, '', 'null', desc, 'anim');
1052
1090
  }, true);
@@ -1069,14 +1107,11 @@ target => target.timestamp !== undefined || target.animationFrames !== undefined
1069
1107
  (0, psdWriter_1.writeUint8)(writer, 0); // copy (always false)
1070
1108
  (0, psdWriter_1.writeZeros)(writer, 3);
1071
1109
  (0, psdWriter_1.writeSection)(writer, 2, () => {
1072
- const id = options.layerToId.get(target) || target.id;
1073
- if (!id)
1074
- throw new Error('You need to provide layer.id value whan writing document with layer comps');
1075
1110
  const desc = {};
1076
1111
  if (comps.originalEffectsReferencePoint) {
1077
1112
  desc.origFXRefPoint = { Hrzn: comps.originalEffectsReferencePoint.x, Vrtc: comps.originalEffectsReferencePoint.y };
1078
1113
  }
1079
- desc.LyrI = id;
1114
+ desc.LyrI = getLayerId(target, options);
1080
1115
  desc.layerSettings = [];
1081
1116
  for (const item of comps.settings) {
1082
1117
  const t = {};
@@ -1094,7 +1129,7 @@ target => target.timestamp !== undefined || target.animationFrames !== undefined
1094
1129
  }, true);
1095
1130
  }
1096
1131
  });
1097
- addHandler('PxSc', () => false, (reader, target) => {
1132
+ addHandler('PxSc', hasKey('pixelSource'), (reader, target) => {
1098
1133
  const desc = (0, descriptor_1.readVersionAndDescriptor)(reader, true);
1099
1134
  // console.log('PxSc', require('util').inspect(desc, false, 99, true));
1100
1135
  if (desc.pixelSourceType === 1986285651) {
@@ -1129,7 +1164,7 @@ addHandler('PxSc', () => false, (reader, target) => {
1129
1164
  _classID: 'PixelSource',
1130
1165
  pixelSourceType: 1986285651, // vdP
1131
1166
  descVersion: 1,
1132
- origin: { Hrzn: source.origin.x, Vrtc: source.origin.y },
1167
+ origin: { Hrzn: source.origin.x, Vrtc: source.origin.y }, // written as 'null' class with 'long' values (see writeDescriptorStructure)
1133
1168
  interpretation: {
1134
1169
  _name: '',
1135
1170
  _classID: 'footageInterpretation',
@@ -1371,10 +1406,12 @@ addHandler('PlLd', hasKey('placedLayer'), (reader, target, left) => {
1371
1406
  (0, descriptor_1.writeVersionAndDescriptor)(writer, '', type, encodeWarp(warp), type);
1372
1407
  });
1373
1408
  function uint8ToFloat32(array) {
1374
- return new Float32Array(array.buffer.slice(array.byteOffset), 0, array.byteLength / 4);
1409
+ const length = Math.floor(array.byteLength / 4);
1410
+ return new Float32Array(array.buffer.slice(array.byteOffset, array.byteOffset + length * 4), 0, length);
1375
1411
  }
1376
1412
  function uint8ToUint32(array) {
1377
- return new Uint32Array(array.buffer.slice(array.byteOffset), 0, array.byteLength / 4);
1413
+ const length = Math.floor(array.byteLength / 4);
1414
+ return new Uint32Array(array.buffer.slice(array.byteOffset, array.byteOffset + length * 4), 0, length);
1378
1415
  }
1379
1416
  function toUint8(array) {
1380
1417
  return new Uint8Array(array.buffer, array.byteOffset, array.byteLength);
@@ -1643,7 +1680,7 @@ function parseFilterFXItem(f, options) {
1643
1680
  { x: f.Fltr.PuX2, y: f.Fltr.PuY2, },
1644
1681
  { x: f.Fltr.PuX3, y: f.Fltr.PuY3, },
1645
1682
  ],
1646
- puppetShapeList: f.Fltr.puppetShapeList.map(p => ({
1683
+ puppetShapeList: (f.Fltr.puppetShapeList || []).map(p => ({
1647
1684
  rigidType: p.rigidType,
1648
1685
  // TODO: VrsM
1649
1686
  // TODO: VrsN
@@ -2172,7 +2209,7 @@ function serializeFilterFXItem(f) {
2172
2209
  _classID: 'rigidTransform',
2173
2210
  'null': ['Ordn.Trgt'], // TODO: ???
2174
2211
  rigidType: f.filter.rigidType,
2175
- puppetShapeList: f.filter.puppetShapeList.map(p => ({
2212
+ puppetShapeList: (f.filter.puppetShapeList || []).map(p => ({
2176
2213
  _name: '',
2177
2214
  _classID: 'puppetShape',
2178
2215
  rigidType: p.rigidType,
@@ -2257,7 +2294,7 @@ function serializeFilterFXItem(f) {
2257
2294
  _classID: 'perspectiveWarpTransform',
2258
2295
  vertices: f.filter.vertices.map(pointToHrznVrtc),
2259
2296
  warpedVertices: f.filter.warpedVertices.map(pointToHrznVrtc),
2260
- quads: f.filter.quads.map(indices => ({ indices })),
2297
+ quads: f.filter.quads.map(indices => ({ _name: '', _classID: 'quadIndices', indices })),
2261
2298
  }, filterID: 442 });
2262
2299
  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 ? {
2263
2300
  Adjs: f.filter.adjustments.map(a => 'curve' in a ? {
@@ -2279,10 +2316,13 @@ function serializeFilterFXItem(f) {
2279
2316
  Cntr: f.filter.contrast,
2280
2317
  useLegacy: !!f.filter.useLegacy,
2281
2318
  }, filterID: 1114793795 });
2282
- // case 'hsb/hsl': return {
2283
- // TODO: ...
2284
- // };
2285
- default: throw new Error(`Unknow filter type: ${f.type}`);
2319
+ case 'hsb/hsl': return Object.assign(Object.assign({}, base), { Fltr: {
2320
+ _name: 'HSB/HSL',
2321
+ _classID: 'HsbP',
2322
+ Inpt: descriptor_1.ClrS.encode(f.filter.inputMode),
2323
+ Otpt: descriptor_1.ClrS.encode(f.filter.rowOrder),
2324
+ }, filterID: 1215521360 });
2325
+ default: throw new Error(`Unknown filter type: ${f.type}`);
2286
2326
  }
2287
2327
  }
2288
2328
  // let t: any;
@@ -2373,7 +2413,10 @@ addHandler('SoLd', hasKey('placedLayer'), (reader, target, left) => {
2373
2413
  if (!placed.id || typeof placed.id !== 'string' || !/^[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$/.test(placed.id)) {
2374
2414
  throw new Error('Placed layer ID must be in a GUID format (example: 20953ddb-9391-11ec-b4f1-c15674f50bc4)');
2375
2415
  }
2376
- 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: placedLayerTypes.indexOf(placed.type), Trnf: placed.transform, nonAffineTransform: (_b = placed.nonAffineTransform) !== null && _b !== void 0 ? _b : placed.transform,
2416
+ const placedType = placedLayerTypes.indexOf(placed.type);
2417
+ if (placedType === -1)
2418
+ throw new Error('Invalid placedLayer type');
2419
+ 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,
2377
2420
  // quiltWarp: {} as any,
2378
2421
  warp: encodeWarp(getWarpFromPlacedLayer(placed)), 'Sz ': {
2379
2422
  _name: '',
@@ -2451,7 +2494,7 @@ addHandler('Lr32', () => false, (reader, _target, _left, psd, imageResources) =>
2451
2494
  addHandler('LMsk', hasKey('userMask'), (reader, target) => {
2452
2495
  target.userMask = {
2453
2496
  colorSpace: (0, psdReader_1.readColor)(reader),
2454
- opacity: (0, psdReader_1.readUint16)(reader) / 0xff,
2497
+ opacity: (0, psdReader_1.readUint16)(reader) / 100,
2455
2498
  };
2456
2499
  const flag = (0, psdReader_1.readUint8)(reader);
2457
2500
  if (flag !== 128)
@@ -2460,7 +2503,7 @@ addHandler('LMsk', hasKey('userMask'), (reader, target) => {
2460
2503
  }, (writer, target) => {
2461
2504
  const userMask = target.userMask;
2462
2505
  (0, psdWriter_1.writeColor)(writer, userMask.colorSpace);
2463
- (0, psdWriter_1.writeUint16)(writer, (0, helpers_1.clamp)(userMask.opacity, 0, 1) * 0xff);
2506
+ (0, psdWriter_1.writeUint16)(writer, Math.round((0, helpers_1.clamp)(userMask.opacity, 0, 1) * 100));
2464
2507
  (0, psdWriter_1.writeUint8)(writer, 128);
2465
2508
  (0, psdWriter_1.writeZeros)(writer, 1);
2466
2509
  });
@@ -2580,7 +2623,8 @@ addHandler('Anno', target => target.annotations !== undefined, (reader, target,
2580
2623
  const count = (0, psdReader_1.readUint32)(reader);
2581
2624
  const annotations = [];
2582
2625
  for (let i = 0; i < count; i++) {
2583
- /*const length =*/ (0, psdReader_1.readUint32)(reader);
2626
+ const recordStart = reader.offset;
2627
+ const length = (0, psdReader_1.readUint32)(reader); // includes the length field itself
2584
2628
  const type = (0, psdReader_1.readSignature)(reader);
2585
2629
  const open = !!(0, psdReader_1.readUint8)(reader);
2586
2630
  /*const flags =*/ (0, psdReader_1.readUint8)(reader); // always 28
@@ -2596,11 +2640,12 @@ addHandler('Anno', target => target.annotations !== undefined, (reader, target,
2596
2640
  const dataLength = (0, psdReader_1.readUint32)(reader);
2597
2641
  let data;
2598
2642
  if (type === 'txtA') {
2643
+ const dataStart = reader.offset;
2599
2644
  if (dataLength >= 2 && (0, psdReader_1.readUint16)(reader) === 0xfeff) {
2600
- data = (0, psdReader_1.readUnicodeStringWithLength)(reader, (dataLength - 2) / 2);
2645
+ data = (0, psdReader_1.readUnicodeStringWithLength)(reader, Math.floor((dataLength - 2) / 2));
2601
2646
  }
2602
2647
  else {
2603
- reader.offset -= 2;
2648
+ reader.offset = dataStart;
2604
2649
  data = (0, psdReader_1.readAsciiString)(reader, dataLength);
2605
2650
  }
2606
2651
  data = data.replace(/\r/g, '\n');
@@ -2614,6 +2659,8 @@ addHandler('Anno', target => target.annotations !== undefined, (reader, target,
2614
2659
  annotations.push({
2615
2660
  type: type === 'txtA' ? 'text' : 'sound', open, iconLocation, popupLocation, color, author, name, date, data,
2616
2661
  });
2662
+ if (length >= (reader.offset - recordStart))
2663
+ reader.offset = recordStart + length;
2617
2664
  }
2618
2665
  target.annotations = annotations;
2619
2666
  (0, psdReader_1.skipBytes)(reader, left());
@@ -2741,11 +2788,12 @@ function createLnkHandler(tag) {
2741
2788
  }
2742
2789
  (0, psdReader_1.skipBytes)(reader, left()); // ?
2743
2790
  }, (writer, target) => {
2744
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
2791
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
2745
2792
  const psd = target;
2746
2793
  for (const file of psd.linkedFiles) {
2747
2794
  if ((tag === 'lnkE') !== !!file.linkedFile)
2748
2795
  continue;
2796
+ const external = tag === 'lnkE';
2749
2797
  let version = 2;
2750
2798
  if (file.assetLockedState != null)
2751
2799
  version = 7;
@@ -2753,11 +2801,13 @@ function createLnkHandler(tag) {
2753
2801
  version = 6;
2754
2802
  else if (file.childDocumentID != null)
2755
2803
  version = 5;
2756
- else if (tag === 'lnkE')
2757
- version = 3;
2758
- writeLength64(writer, 0);
2804
+ // liFE version 2 is the only one with embedded data, it has no date and its file size is the size of the data,
2805
+ // versions 5+ can't store the data, so it's not written in that case
2806
+ else if (external && !file.data)
2807
+ version = 4;
2808
+ (0, psdWriter_1.writeUint64)(writer, 0);
2759
2809
  const sizeOffset = writer.offset;
2760
- (0, psdWriter_1.writeSignature)(writer, (tag === 'lnkE') ? 'liFE' : (file.data ? 'liFD' : 'liFA'));
2810
+ (0, psdWriter_1.writeSignature)(writer, external ? 'liFE' : (file.data ? 'liFD' : 'liFA'));
2761
2811
  (0, psdWriter_1.writeInt32)(writer, version);
2762
2812
  if (!file.id || typeof file.id !== 'string' || !/^[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$/.test(file.id)) {
2763
2813
  throw new Error('Linked file ID must be in a GUID format (example: 20953ddb-9391-11ec-b4f1-c15674f50bc4)');
@@ -2766,7 +2816,7 @@ function createLnkHandler(tag) {
2766
2816
  (0, psdWriter_1.writeUnicodeStringWithPadding)(writer, file.name || '');
2767
2817
  (0, psdWriter_1.writeSignature)(writer, file.type ? `${file.type} `.substring(0, 4) : ' ');
2768
2818
  (0, psdWriter_1.writeSignature)(writer, file.creator ? `${file.creator} `.substring(0, 4) : '\0\0\0\0');
2769
- writeLength64(writer, file.data ? file.data.byteLength : 0);
2819
+ (0, psdWriter_1.writeUint64)(writer, (!external && file.data) ? file.data.byteLength : 0);
2770
2820
  if (file.descriptor && file.descriptor.compInfo) {
2771
2821
  const desc = {
2772
2822
  compInfo: {
@@ -2780,7 +2830,7 @@ function createLnkHandler(tag) {
2780
2830
  else {
2781
2831
  (0, psdWriter_1.writeUint8)(writer, 0);
2782
2832
  }
2783
- if (tag === 'lnkE') {
2833
+ if (external) {
2784
2834
  const desc = {
2785
2835
  descVersion: 2,
2786
2836
  'Nm ': (_b = (_a = file.linkedFile) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : '',
@@ -2789,6 +2839,8 @@ function createLnkHandler(tag) {
2789
2839
  relPath: (_h = (_g = file.linkedFile) === null || _g === void 0 ? void 0 : _g.relativePath) !== null && _h !== void 0 ? _h : '',
2790
2840
  };
2791
2841
  (0, descriptor_1.writeVersionAndDescriptor)(writer, '', 'ExternalFileLink', desc);
2842
+ }
2843
+ if (external && version > 3) {
2792
2844
  const time = file.time ? new Date(file.time) : new Date();
2793
2845
  (0, psdWriter_1.writeInt32)(writer, time.getUTCFullYear());
2794
2846
  (0, psdWriter_1.writeUint8)(writer, time.getUTCMonth());
@@ -2797,11 +2849,14 @@ function createLnkHandler(tag) {
2797
2849
  (0, psdWriter_1.writeUint8)(writer, time.getUTCMinutes());
2798
2850
  (0, psdWriter_1.writeFloat64)(writer, time.getUTCSeconds() + time.getUTCMilliseconds() / 1000);
2799
2851
  }
2800
- if (file.data) {
2801
- (0, psdWriter_1.writeBytes)(writer, file.data);
2852
+ if (external) {
2853
+ (0, psdWriter_1.writeUint64)(writer, (version === 2 ? (_j = file.data) === null || _j === void 0 ? void 0 : _j.byteLength : (_k = file.linkedFile) === null || _k === void 0 ? void 0 : _k.fileSize) || 0); // file size
2854
+ }
2855
+ else if (file.data) {
2856
+ (0, psdWriter_1.writeBytes)(writer, file.data); // liFD
2802
2857
  }
2803
2858
  else {
2804
- writeLength64(writer, ((_j = file.linkedFile) === null || _j === void 0 ? void 0 : _j.fileSize) || 0);
2859
+ (0, psdWriter_1.writeZeros)(writer, 8); // liFA
2805
2860
  }
2806
2861
  if (version >= 5)
2807
2862
  (0, psdWriter_1.writeUnicodeStringWithPadding)(writer, file.childDocumentID || '');
@@ -2809,6 +2864,8 @@ function createLnkHandler(tag) {
2809
2864
  (0, psdWriter_1.writeFloat64)(writer, file.assetModTime || 0);
2810
2865
  if (version >= 7)
2811
2866
  (0, psdWriter_1.writeUint8)(writer, file.assetLockedState || 0);
2867
+ if (external && version === 2 && file.data)
2868
+ (0, psdWriter_1.writeBytes)(writer, file.data);
2812
2869
  let size = writer.offset - sizeOffset;
2813
2870
  writer.view.setUint32(sizeOffset - 4, size, false); // write size
2814
2871
  while (size % 4) {
@@ -3181,10 +3238,15 @@ addHandler('phfl', adjustmentType('photo filter'), (reader, target, left) => {
3181
3238
  }
3182
3239
  else { // version 3
3183
3240
  // TODO: test this, this is probably wrong
3241
+ // values are stored as L (0..100), a, b (-128..127) multiplied by 100,
3242
+ // normalize them to the same scale as readColor() so they can be written back as version 2
3243
+ const l = (0, psdReader_1.readInt32)(reader) / 100;
3244
+ const a = (0, psdReader_1.readInt32)(reader) / 100;
3245
+ const b = (0, psdReader_1.readInt32)(reader) / 100;
3184
3246
  color = {
3185
- l: (0, psdReader_1.readInt32)(reader) / 100,
3186
- a: (0, psdReader_1.readInt32)(reader) / 100,
3187
- b: (0, psdReader_1.readInt32)(reader) / 100,
3247
+ l: l / 100,
3248
+ a: (0, helpers_1.normalizeLabAB)(a),
3249
+ b: (0, helpers_1.normalizeLabAB)(b),
3188
3250
  };
3189
3251
  }
3190
3252
  target.adjustment = {
@@ -3198,7 +3260,7 @@ addHandler('phfl', adjustmentType('photo filter'), (reader, target, left) => {
3198
3260
  const info = target.adjustment;
3199
3261
  (0, psdWriter_1.writeUint16)(writer, 2); // version
3200
3262
  (0, psdWriter_1.writeColor)(writer, info.color || { l: 0, a: 0, b: 0 });
3201
- (0, psdWriter_1.writeUint32)(writer, (info.density || 0) * 100);
3263
+ (0, psdWriter_1.writeUint32)(writer, Math.round(Math.max(0, info.density || 0) * 100));
3202
3264
  (0, psdWriter_1.writeUint8)(writer, info.preserveLuminosity ? 1 : 0);
3203
3265
  (0, psdWriter_1.writeZeros)(writer, 3);
3204
3266
  });
@@ -3401,10 +3463,11 @@ addHandler('grdm', adjustmentType('gradient map'), (reader, target, left) => {
3401
3463
  (0, psdReader_1.readUint16)(reader) / 0x8000,
3402
3464
  ];
3403
3465
  (0, psdReader_1.skipBytes)(reader, left());
3466
+ // stop locations are always in 0..4096 range, independent of smoothness
3404
3467
  for (const s of info.colorStops)
3405
- s.location /= interpolation;
3468
+ s.location /= 4096;
3406
3469
  for (const s of info.opacityStops)
3407
- s.location /= interpolation;
3470
+ s.location /= 4096;
3408
3471
  target.adjustment = info;
3409
3472
  }, (writer, target) => {
3410
3473
  var _a, _b, _c;
@@ -3417,18 +3480,18 @@ addHandler('grdm', adjustmentType('gradient map'), (reader, target, left) => {
3417
3480
  }
3418
3481
  (0, psdWriter_1.writeUnicodeStringWithPadding)(writer, info.name || '');
3419
3482
  (0, psdWriter_1.writeUint16)(writer, info.colorStops && info.colorStops.length || 0);
3420
- const interpolation = Math.round(((_a = info.smoothness) !== null && _a !== void 0 ? _a : 1) * 4096);
3483
+ const interpolation = Math.round((0, helpers_1.clamp)((_a = info.smoothness) !== null && _a !== void 0 ? _a : 1, 0, 1) * 4096);
3421
3484
  for (const s of info.colorStops || []) {
3422
- (0, psdWriter_1.writeUint32)(writer, Math.round(s.location * interpolation));
3485
+ (0, psdWriter_1.writeUint32)(writer, Math.round((0, helpers_1.clamp)(s.location, 0, 1) * 4096));
3423
3486
  (0, psdWriter_1.writeUint32)(writer, Math.round(s.midpoint * 100));
3424
3487
  (0, psdWriter_1.writeColor)(writer, s.color);
3425
3488
  (0, psdWriter_1.writeZeros)(writer, 2);
3426
3489
  }
3427
3490
  (0, psdWriter_1.writeUint16)(writer, info.opacityStops && info.opacityStops.length || 0);
3428
3491
  for (const s of info.opacityStops || []) {
3429
- (0, psdWriter_1.writeUint32)(writer, Math.round(s.location * interpolation));
3492
+ (0, psdWriter_1.writeUint32)(writer, Math.round((0, helpers_1.clamp)(s.location, 0, 1) * 4096));
3430
3493
  (0, psdWriter_1.writeUint32)(writer, Math.round(s.midpoint * 100));
3431
- (0, psdWriter_1.writeUint16)(writer, Math.round(s.opacity * 0xff));
3494
+ (0, psdWriter_1.writeUint16)(writer, Math.round((0, helpers_1.clamp)(s.opacity, 0, 1) * 0xff));
3432
3495
  }
3433
3496
  (0, psdWriter_1.writeUint16)(writer, 2); // expansion count
3434
3497
  (0, psdWriter_1.writeUint16)(writer, interpolation);
@@ -3590,7 +3653,7 @@ addHandler('Txt2', hasKey('engineData'), (reader, target, left, psd) => {
3590
3653
  const data = (0, psdReader_1.readBytes)(reader, left());
3591
3654
  target.engineData = (0, base64_js_1.fromByteArray)(data);
3592
3655
  const layersByIndex = getTextLayersSortedByIndex(psd);
3593
- const engineData = (0, engineData_1.parseEngineData)(data);
3656
+ const engineData = (0, engineData_1.parseEngineData)(data, reader);
3594
3657
  const engineData2 = (0, engineData2_1.decodeEngineData2)(engineData);
3595
3658
  const TextFrameSet = engineData2.ResourceDict.TextFrameSet;
3596
3659
  if (TextFrameSet) {
@@ -3672,7 +3735,6 @@ addHandler('FEid', hasKey('filterEffectsMasks'), (reader, target, leftBytes) =>
3672
3735
  }
3673
3736
  }
3674
3737
  }, (writer, target) => {
3675
- var _a;
3676
3738
  (0, psdWriter_1.writeInt32)(writer, 3); // version
3677
3739
  for (const mask of target.filterEffectsMasks) {
3678
3740
  (0, psdWriter_1.writeUint32)(writer, 0);
@@ -3701,7 +3763,7 @@ addHandler('FEid', hasKey('filterEffectsMasks'), (reader, target, leftBytes) =>
3701
3763
  }
3702
3764
  }
3703
3765
  writer.view.setUint32(length2Offset - 4, writer.offset - length2Offset, false);
3704
- const extra = (_a = target.filterEffectsMasks[target.filterEffectsMasks.length - 1]) === null || _a === void 0 ? void 0 : _a.extra;
3766
+ const extra = mask.extra;
3705
3767
  if (extra) {
3706
3768
  (0, psdWriter_1.writeUint8)(writer, 1);
3707
3769
  (0, psdWriter_1.writeInt32)(writer, extra.top);
@@ -3725,12 +3787,12 @@ addHandlerAlias('FXid', 'FEid');
3725
3787
  addHandler('FMsk', hasKey('filterMask'), (reader, target) => {
3726
3788
  target.filterMask = {
3727
3789
  colorSpace: (0, psdReader_1.readColor)(reader),
3728
- opacity: (0, psdReader_1.readUint16)(reader) / 0xff,
3790
+ opacity: (0, psdReader_1.readUint16)(reader) / 100,
3729
3791
  };
3730
3792
  }, (writer, target) => {
3731
3793
  var _a;
3732
3794
  (0, psdWriter_1.writeColor)(writer, target.filterMask.colorSpace);
3733
- (0, psdWriter_1.writeUint16)(writer, (0, helpers_1.clamp)((_a = target.filterMask.opacity) !== null && _a !== void 0 ? _a : 1, 0, 1) * 0xff);
3795
+ (0, psdWriter_1.writeUint16)(writer, Math.round((0, helpers_1.clamp)((_a = target.filterMask.opacity) !== null && _a !== void 0 ? _a : 1, 0, 1) * 100));
3734
3796
  });
3735
3797
  addHandler('artd', // document-wide artboard info
3736
3798
  // document-wide artboard info
@@ -3846,12 +3908,12 @@ addHandler('iOpa', hasKey('fillOpacity'), (reader, target) => {
3846
3908
  target.fillOpacity = (0, psdReader_1.readUint8)(reader) / 0xff;
3847
3909
  (0, psdReader_1.skipBytes)(reader, 3);
3848
3910
  }, (writer, target) => {
3849
- (0, psdWriter_1.writeUint8)(writer, target.fillOpacity * 0xff);
3911
+ (0, psdWriter_1.writeUint8)(writer, Math.round((0, helpers_1.clamp)(target.fillOpacity, 0, 1) * 0xff));
3850
3912
  (0, psdWriter_1.writeZeros)(writer, 3);
3851
3913
  });
3852
3914
  addHandler('brst', hasKey('channelBlendingRestrictions'), (reader, target, left) => {
3853
3915
  target.channelBlendingRestrictions = [];
3854
- while (left() > 4) {
3916
+ while (left() >= 4) {
3855
3917
  target.channelBlendingRestrictions.push((0, psdReader_1.readInt32)(reader));
3856
3918
  }
3857
3919
  }, (writer, target) => {
@@ -3922,8 +3984,12 @@ exports.IntC = exports.IntE = exports.Drct = exports.WndM = exports.CntE = expor
3922
3984
  exports.presetKindType = exports.prjM = exports.FlMd = void 0;
3923
3985
  exports.setLogErrors = setLogErrors;
3924
3986
  exports.readAsciiStringOrClassId = readAsciiStringOrClassId;
3987
+ exports.writeAsciiStringOrClassId = writeAsciiStringOrClassId;
3925
3988
  exports.readDescriptorStructure = readDescriptorStructure;
3926
3989
  exports.writeDescriptorStructure = writeDescriptorStructure;
3990
+ exports.createDescriptorReference = createDescriptorReference;
3991
+ exports.pathSelectionStateToReference = pathSelectionStateToReference;
3992
+ exports.writeClassStructure = writeClassStructure;
3927
3993
  exports.readVersionAndDescriptor = readVersionAndDescriptor;
3928
3994
  exports.writeVersionAndDescriptor = writeVersionAndDescriptor;
3929
3995
  exports.horzVrtcToXY = horzVrtcToXY;
@@ -4062,6 +4128,8 @@ const fieldToExtType = {
4062
4128
  origFXRefPoint: nullType,
4063
4129
  FXRefPoint: nullType,
4064
4130
  ClMg: makeType('', 'ClMg'),
4131
+ interpretation: makeType('', 'footageInterpretation'),
4132
+ bgColor: makeType('', 'NativeQuad'),
4065
4133
  };
4066
4134
  const fieldToArrayExtType = {
4067
4135
  'Crv ': makeType('', 'CrPt'),
@@ -4095,6 +4163,9 @@ const fieldToArrayExtType = {
4095
4163
  layerSettings: nullType,
4096
4164
  list: nullType,
4097
4165
  Adjs: makeType('', 'CrvA'),
4166
+ vertices: makeType('', 'Pnt '),
4167
+ warpedVertices: makeType('', 'Pnt '),
4168
+ quads: makeType('', 'quadIndices'),
4098
4169
  };
4099
4170
  const typeToField = {
4100
4171
  'TEXT': [
@@ -4120,6 +4191,7 @@ const typeToField = {
4120
4191
  'meshExpansion', 'meshRigidity', 'VrsM', 'VrsN', 'NmbG', 'WLMn', 'WLMx', 'AmMn', 'AmMx', 'SclH', 'SclV',
4121
4192
  'Lvl ', 'TlNm', 'TlOf', 'FlRs', 'Thsh', 'ShrS', 'ShrE', 'FlRs', 'Vrnc', 'Strg', 'ExtS', 'ExtD',
4122
4193
  'HrzS', 'VrtS', 'NmbR', 'EdgF', 'Ang1', 'Ang2', 'Ang3', 'Ang4', 'lastAppliedComp', 'capturedInfo',
4194
+ 'pixelSourceType',
4123
4195
  ],
4124
4196
  'enum': [
4125
4197
  'textGridding', 'Ornt', 'warpStyle', 'warpRotate', 'Inte', 'Bltn', 'ClrS', 'BlrQ',
@@ -4130,6 +4202,7 @@ const typeToField = {
4130
4202
  'enableCompCoreThreads', 'gs99', 'FrDs', 'trackID', 'animInterpStyle', 'horzAlign',
4131
4203
  'vertAlign', 'bgColorType', 'shapeOperation', 'UndA', 'Wvtp', 'Drct', 'WndM', 'Edg ', 'FlCl', 'IntE',
4132
4204
  'IntC', 'Cnvr', 'Fl ', 'Dstr', 'MztT', 'Lns ', 'ExtT', 'DspM', 'ExtR', 'ZZTy', 'SphM', 'SmBQ', 'placedLayerOCIOConversion', 'gradientsInterpolationMethod',
4205
+ 'interpretAlpha',
4133
4206
  ],
4134
4207
  'bool': [
4135
4208
  'PstS', 'printSixteenBit', 'masterFXSwitch', 'enab', 'uglg', 'antialiasGloss',
@@ -4142,14 +4215,15 @@ const typeToField = {
4142
4215
  'materialsDisclosed', 'hasMotion', 'muted', 'Effc', 'selected', 'autoScope', 'fillCanvas',
4143
4216
  'cellTextIsHTML', 'Smoo', 'Clsp', 'validAtPosition', 'rigidType', 'hasoptions', 'filterMaskEnable',
4144
4217
  'filterMaskLinked', 'filterMaskExtendWithWhite', 'removeJPEGArtifact', 'Mnch', 'ExtF', 'ExtM',
4145
- 'moreAccurate', 'GpuY', 'LIWy', 'Cnty',
4218
+ 'moreAccurate', 'GpuY', 'LIWy', 'Cnty', 'Lnkd', 'showAlteredVideo', 'lightingOn',
4146
4219
  ],
4147
4220
  'doub': [
4148
4221
  'warpValue', 'warpPerspective', 'warpPerspectiveOther', 'Intr', 'Wdth', 'Hght',
4149
4222
  'strokeStyleMiterLimit', 'strokeStyleResolution', 'layerTime', 'keyOriginResolution',
4150
4223
  'xx', 'xy', 'yx', 'yy', 'tx', 'ty', 'FrGA', 'frameRate', 'audioLevel', 'rotation',
4151
4224
  'X ', 'Y ', 'redFloat', 'greenFloat', 'blueFloat', 'imageResolution',
4152
- 'PuX0', 'PuX1', 'PuX2', 'PuX3', 'PuY0', 'PuY1', 'PuY2', 'PuY3'
4225
+ 'PuX0', 'PuX1', 'PuX2', 'PuX3', 'PuY0', 'PuY1', 'PuY2', 'PuY3',
4226
+ 'stylization', 'cleanliness', 'brushScale', 'microBrush', 'specularity',
4153
4227
  ],
4154
4228
  'UntF': [
4155
4229
  'sdwO', 'hglO', 'lagl', 'Lald', 'srgR', 'blur', 'Sftn', 'Opct', 'Dstn', 'Angl',
@@ -4165,14 +4239,57 @@ const typeToField = {
4165
4239
  'warpValues', 'selectedPin', 'Pts ', 'SbpL', 'pathComponents', 'pinOffsets', 'posFinalPins',
4166
4240
  'pinVertexIndices', 'PinP', 'PnRt', 'PnOv', 'PnDp', 'filterFXList', 'puppetShapeList', 'ShrP',
4167
4241
  'channelDenoise', 'Mtrx', 'layerSettings', 'list', 'compList', 'Adjs',
4242
+ 'vertices', 'warpedVertices', 'quads', 'indices', 'Mpng',
4168
4243
  ],
4169
4244
  'ObAr': ['meshPoints', 'quiltSliceX', 'quiltSliceY'],
4170
4245
  'obj ': ['null', 'Chnl'],
4171
4246
  'Pth ': ['DspF'],
4247
+ 'alis': ['alis'],
4172
4248
  };
4173
4249
  const channels = [
4174
4250
  'Rd ', 'Grn ', 'Bl ', 'Yllw', 'Ylw ', 'Cyn ', 'Mgnt', 'Blck', 'Gry ', 'Lmnc', 'A ', 'B ',
4175
4251
  ];
4252
+ // color classes whose channel values are written as doubles ('GRYC' and 'LABC' are kept for backwards compatibility)
4253
+ const doubleColorClasses = ['Grsc', 'LbCl', 'GRYC', 'LABC'];
4254
+ // Hidden (non-enumerable) markers used to remember how values were stored in the file, so they can be
4255
+ // written back the same way. They don't change the shape of the objects returned by the reader.
4256
+ const osTypeKey = typeof Symbol !== 'undefined' ? Symbol('agPsdOSType') : '__agPsdOSType';
4257
+ const obArInfoKey = typeof Symbol !== 'undefined' ? Symbol('agPsdObAr') : '__agPsdObAr';
4258
+ const referenceInfoKey = typeof Symbol !== 'undefined' ? Symbol('agPsdReference') : '__agPsdReference';
4259
+ function setHidden(target, key, value) {
4260
+ Object.defineProperty(target, key, { value, enumerable: false, configurable: true, writable: true });
4261
+ return target;
4262
+ }
4263
+ function getHidden(target, key) {
4264
+ return (target && typeof target === 'object') ? target[key] : undefined;
4265
+ }
4266
+ const osTypeFamilies = {
4267
+ UntF: 'UntF', UnFl: 'UntF',
4268
+ Objc: 'Objc', GlbO: 'Objc',
4269
+ type: 'type', GlbC: 'type',
4270
+ comp: 'comp',
4271
+ };
4272
+ // returns the OS type the value was originally read with, if it's compatible with the type we'd write it as
4273
+ // fallback for unknown keys, based on the type of the value
4274
+ function guessOSType(value) {
4275
+ if (typeof value === 'boolean')
4276
+ return 'bool';
4277
+ if (typeof value === 'number' && !Number.isInteger(value))
4278
+ return 'doub';
4279
+ if (Array.isArray(value))
4280
+ return 'VlLs';
4281
+ if (value && typeof value === 'object' && typeof value._classID === 'string')
4282
+ return 'Objc';
4283
+ return undefined;
4284
+ }
4285
+ function getRecordedOSType(value, type) {
4286
+ const recorded = getHidden(value, osTypeKey);
4287
+ if (!recorded)
4288
+ return type;
4289
+ if (!type || recorded === 'comp' || recorded === 'type' || recorded === 'GlbC')
4290
+ return recorded;
4291
+ return osTypeFamilies[recorded] === osTypeFamilies[type] ? recorded : type;
4292
+ }
4176
4293
  const fieldToArrayType = {
4177
4294
  'Mnm ': 'long',
4178
4295
  'Mxm ': 'long',
@@ -4212,6 +4329,8 @@ const fieldToArrayType = {
4212
4329
  Mtrx: 'long',
4213
4330
  compList: 'long',
4214
4331
  Chnl: 'enum',
4332
+ indices: 'long',
4333
+ Mpng: 'long',
4215
4334
  };
4216
4335
  const fieldToType = {};
4217
4336
  for (const type of Object.keys(typeToField)) {
@@ -4226,6 +4345,7 @@ for (const field of Object.keys(fieldToExtType)) {
4226
4345
  for (const field of Object.keys(fieldToArrayExtType)) {
4227
4346
  fieldToArrayType[field] = 'Objc';
4228
4347
  }
4348
+ const pixelSourceOriginRoot = 'PixelSource.origin';
4229
4349
  function getTypeByKey(key, value, root, parent) {
4230
4350
  if (key === 'presetKind') {
4231
4351
  return typeof value === 'string' ? 'enum' : 'long';
@@ -4245,7 +4365,7 @@ function getTypeByKey(key, value, root, parent) {
4245
4365
  else if (key === 'AntA') {
4246
4366
  return typeof value === 'string' ? 'enum' : 'bool';
4247
4367
  }
4248
- else if ((key === 'Hrzn' || key === 'Vrtc') && (parent.Type === 'keyType.Pstn' || parent._classID === 'Ofst')) {
4368
+ else if ((key === 'Hrzn' || key === 'Vrtc') && (parent.Type === 'keyType.Pstn' || parent._classID === 'Ofst' || root === pixelSourceOriginRoot)) {
4249
4369
  return 'long';
4250
4370
  }
4251
4371
  else if (key === 'Hrzn' || key === 'Vrtc' || key === 'Top ' || key === 'Left' || key === 'Btom' || key === 'Rght') {
@@ -4262,6 +4382,15 @@ function getTypeByKey(key, value, root, parent) {
4262
4382
  else if (key === 'Trnf') {
4263
4383
  return Array.isArray(value) ? 'VlLs' : 'Objc';
4264
4384
  }
4385
+ else if (key === 'alpha' && root === 'slices') {
4386
+ return 'long';
4387
+ }
4388
+ else if ((key === 'Inpt' || key === 'Otpt') && parent._classID === 'HsbP') {
4389
+ return 'enum';
4390
+ }
4391
+ else if (key === 'LghD' && parent._classID === 'oilPaint') {
4392
+ return 'long';
4393
+ }
4265
4394
  else {
4266
4395
  return fieldToType[key];
4267
4396
  }
@@ -4305,23 +4434,22 @@ function writeDescriptorStructure(writer, name, classId, value, root) {
4305
4434
  // write class structure
4306
4435
  (0, psdWriter_1.writeUnicodeStringWithPadding)(writer, name);
4307
4436
  writeAsciiStringOrClassId(writer, classId);
4308
- const keys = Object.keys(value);
4309
- let keyCount = keys.length;
4310
- if ('_name' in value)
4311
- keyCount--;
4312
- if ('_classID' in value)
4313
- keyCount--;
4314
- (0, psdWriter_1.writeUint32)(writer, keyCount);
4437
+ const allKeys = Object.keys(value);
4438
+ const keys = allKeys.filter(key => key !== '_name' && key !== '_classID' && value[key] !== undefined);
4439
+ (0, psdWriter_1.writeUint32)(writer, keys.length);
4315
4440
  for (const key of keys) {
4316
- if (key === '_name' || key === '_classID')
4317
- continue;
4318
4441
  let type = getTypeByKey(key, value[key], root, value);
4442
+ let childRoot = root;
4319
4443
  let extType = fieldToExtType[key];
4320
4444
  if (key === 'bounds' && root === 'text') {
4321
4445
  extType = makeType('', 'bounds');
4322
4446
  }
4323
4447
  else if (key === 'origin') {
4324
4448
  type = root === 'slices' ? 'enum' : 'Objc';
4449
+ if (classId === 'PixelSource') {
4450
+ extType = nullType; // stored as 'null' class with integer Hrzn/Vrtc
4451
+ childRoot = pixelSourceOriginRoot;
4452
+ }
4325
4453
  }
4326
4454
  else if ((key === 'Cyn ' || key === 'Mgnt' || key === 'Ylw ' || key === 'Blck') && value._classID === 'CMYC') {
4327
4455
  type = 'doub';
@@ -4389,10 +4517,14 @@ function writeDescriptorStructure(writer, name, classId, value, root) {
4389
4517
  extType = nullType;
4390
4518
  }
4391
4519
  else if (channels.indexOf(key) !== -1) {
4392
- type = (classId === 'RGBC' && root !== 'artd') ? 'doub' : 'long';
4520
+ type = ((classId === 'RGBC' && root !== 'artd') || doubleColorClasses.indexOf(classId) !== -1) ? 'doub' : 'long';
4393
4521
  }
4394
4522
  else if (key === 'profile') {
4395
- type = classId === 'printOutput' ? 'TEXT' : 'tdta';
4523
+ type = (classId === 'printOutput' || classId === 'proofSetup') ? 'TEXT' : 'tdta';
4524
+ }
4525
+ else if (key === 'alis' && typeof value[key] === 'string') {
4526
+ // 'alis' is read as a binary string (one char per byte), strings with wider chars can only be stored as TEXT
4527
+ type = /[^\x00-\xff]/.test(value[key]) ? 'TEXT' : 'alis';
4396
4528
  }
4397
4529
  else if (key === 'strokeStyleContent') {
4398
4530
  if (value[key]['Clr ']) {
@@ -4416,9 +4548,12 @@ function writeDescriptorStructure(writer, name, classId, value, root) {
4416
4548
  extType = { classID: 'HSBC', name: '' };
4417
4549
  // TODO: other color spaces
4418
4550
  }
4551
+ type = getRecordedOSType(value[key], type);
4552
+ if (!type)
4553
+ type = guessOSType(value[key]);
4419
4554
  writeAsciiStringOrClassId(writer, key);
4420
4555
  (0, psdWriter_1.writeSignature)(writer, type || 'long');
4421
- writeOSType(writer, type || 'long', value[key], key, extType, root);
4556
+ writeOSType(writer, type || 'long', value[key], key, extType, childRoot);
4422
4557
  if (logErrors && !type)
4423
4558
  console.log(`Missing descriptor field type for: '${key}' in`, value);
4424
4559
  }
@@ -4428,8 +4563,9 @@ function readOSType(reader, type, includeClass) {
4428
4563
  case 'obj ': // Reference
4429
4564
  return readReferenceStructure(reader);
4430
4565
  case 'Objc': // Descriptor
4431
- case 'GlbO': // GlobalObject same as Descriptor
4432
4566
  return readDescriptorStructure(reader, includeClass);
4567
+ case 'GlbO': // GlobalObject same as Descriptor
4568
+ return setHidden(readDescriptorStructure(reader, includeClass), osTypeKey, type);
4433
4569
  case 'VlLs': { // List
4434
4570
  const length = (0, psdReader_1.readInt32)(reader);
4435
4571
  const items = [];
@@ -4454,7 +4590,7 @@ function readOSType(reader, type, includeClass) {
4454
4590
  const value = (0, psdReader_1.readFloat32)(reader);
4455
4591
  if (!unitsMap[units])
4456
4592
  throw new Error(`Invalid units: ${units}`);
4457
- return { units: unitsMap[units], value };
4593
+ return setHidden({ units: unitsMap[units], value }, osTypeKey, type);
4458
4594
  }
4459
4595
  case 'TEXT': // String
4460
4596
  return (0, psdReader_1.readUnicodeString)(reader);
@@ -4468,13 +4604,13 @@ function readOSType(reader, type, includeClass) {
4468
4604
  case 'comp': { // Large Integer
4469
4605
  const low = (0, psdReader_1.readUint32)(reader);
4470
4606
  const high = (0, psdReader_1.readUint32)(reader);
4471
- return { low, high };
4607
+ return setHidden({ low, high }, osTypeKey, type);
4472
4608
  }
4473
4609
  case 'bool': // Boolean
4474
4610
  return !!(0, psdReader_1.readUint8)(reader);
4475
4611
  case 'type': // Class
4476
4612
  case 'GlbC': // Class
4477
- return readClassStructure(reader);
4613
+ return setHidden(readClassStructure(reader), osTypeKey, type);
4478
4614
  case 'alis': { // Alias
4479
4615
  const length = (0, psdReader_1.readInt32)(reader);
4480
4616
  return (0, psdReader_1.readAsciiString)(reader, length);
@@ -4486,21 +4622,21 @@ function readOSType(reader, type, includeClass) {
4486
4622
  case 'ObAr': { // Object array
4487
4623
  (0, psdReader_1.readInt32)(reader); // version: 16
4488
4624
  (0, psdReader_1.readUnicodeString)(reader); // name: ''
4489
- readAsciiStringOrClassId(reader); // 'rationalPoint'
4625
+ const classID = readAsciiStringOrClassId(reader); // 'rationalPoint'
4490
4626
  const length = (0, psdReader_1.readInt32)(reader);
4491
4627
  const items = [];
4492
4628
  for (let i = 0; i < length; i++) {
4493
4629
  const type1 = readAsciiStringOrClassId(reader); // type Hrzn | Vrtc
4494
- (0, psdReader_1.readSignature)(reader); // UnFl
4495
- (0, psdReader_1.readSignature)(reader); // units ? '#Pxl'
4630
+ const valueType = (0, psdReader_1.readSignature)(reader); // UnFl
4631
+ const units = (0, psdReader_1.readSignature)(reader); // units ? '#Pxl'
4496
4632
  const valuesCount = (0, psdReader_1.readInt32)(reader);
4497
4633
  const values = [];
4498
4634
  for (let j = 0; j < valuesCount; j++) {
4499
4635
  values.push((0, psdReader_1.readFloat64)(reader));
4500
4636
  }
4501
- items.push({ type: type1, values });
4637
+ items.push(setHidden({ type: type1, values }, obArInfoKey, { valueType, units }));
4502
4638
  }
4503
- return items;
4639
+ return setHidden(items, obArInfoKey, { classID });
4504
4640
  }
4505
4641
  case 'Pth ': { // File path
4506
4642
  /*const length =*/ (0, psdReader_1.readInt32)(reader); // total size of all fields below
@@ -4520,6 +4656,7 @@ const ObArTypes = {
4520
4656
  quiltSliceY: 'UntF',
4521
4657
  };
4522
4658
  function writeOSType(writer, type, value, key, extType, root) {
4659
+ var _a, _b, _c, _d, _e;
4523
4660
  switch (type) {
4524
4661
  case 'obj ': // Reference
4525
4662
  writeReferenceStructure(writer, key, value);
@@ -4528,9 +4665,9 @@ function writeOSType(writer, type, value, key, extType, root) {
4528
4665
  case 'GlbO': { // GlobalObject same as Descriptor
4529
4666
  if (typeof value !== 'object')
4530
4667
  throw new Error(`Invalid struct value: ${JSON.stringify(value)}, key: ${key}`);
4531
- if (!extType)
4668
+ if (!extType && !value._classID)
4532
4669
  throw new Error(`Missing ext type for: '${key}' (${JSON.stringify(value)})`);
4533
- const name = value._name || extType.name;
4670
+ const name = value._name || (extType === null || extType === void 0 ? void 0 : extType.name) || '';
4534
4671
  const classID = value._classID || extType.classID;
4535
4672
  writeDescriptorStructure(writer, name, classID, value, root);
4536
4673
  break;
@@ -4540,7 +4677,7 @@ function writeOSType(writer, type, value, key, extType, root) {
4540
4677
  throw new Error(`Invalid list value: ${JSON.stringify(value)}, key: ${key}`);
4541
4678
  (0, psdWriter_1.writeInt32)(writer, value.length);
4542
4679
  for (let i = 0; i < value.length; i++) {
4543
- const type = fieldToArrayType[key];
4680
+ const type = (_a = getRecordedOSType(value[i], fieldToArrayType[key])) !== null && _a !== void 0 ? _a : guessOSType(value[i]);
4544
4681
  (0, psdWriter_1.writeSignature)(writer, type || 'long');
4545
4682
  writeOSType(writer, type || 'long', value[i], `${key}[]`, fieldToArrayExtType[key], root);
4546
4683
  if (logErrors && !type)
@@ -4578,20 +4715,35 @@ function writeOSType(writer, type, value, key, extType, root) {
4578
4715
  case 'long': // Integer
4579
4716
  if (typeof value !== 'number')
4580
4717
  throw new Error(`Invalid integer value: ${JSON.stringify(value)}, key: ${key}`);
4581
- (0, psdWriter_1.writeInt32)(writer, value);
4718
+ (0, psdWriter_1.writeInt32)(writer, Math.round(value)); // round instead of truncating fractional values
4719
+ break;
4720
+ case 'comp': // Large Integer
4721
+ if (!value || typeof value !== 'object')
4722
+ throw new Error(`Invalid large integer value: ${JSON.stringify(value)}, key: ${key}`);
4723
+ (0, psdWriter_1.writeUint32)(writer, value.low >>> 0);
4724
+ (0, psdWriter_1.writeUint32)(writer, value.high >>> 0);
4582
4725
  break;
4583
- // case 'comp': // Large Integer
4584
- // writeLargeInteger(reader);
4585
4726
  case 'bool': // Boolean
4586
4727
  if (typeof value !== 'boolean')
4587
4728
  throw new Error(`Invalid boolean value: ${JSON.stringify(value)}, key: ${key}`);
4588
4729
  (0, psdWriter_1.writeUint8)(writer, value ? 1 : 0);
4589
4730
  break;
4590
- // case 'type': // Class
4591
- // case 'GlbC': // Class
4592
- // writeClassStructure(reader);
4593
- // case 'alis': // Alias
4594
- // writeAliasStructure(reader);
4731
+ case 'type': // Class
4732
+ case 'GlbC': // Class
4733
+ if (!value || typeof value !== 'object')
4734
+ throw new Error(`Invalid class value: ${JSON.stringify(value)}, key: ${key}`);
4735
+ (0, psdWriter_1.writeUnicodeStringWithPadding)(writer, value.name || '');
4736
+ writeAsciiStringOrClassId(writer, value.classID);
4737
+ break;
4738
+ case 'alis': { // Alias (read as a binary string, one char per byte)
4739
+ if (typeof value !== 'string')
4740
+ throw new Error(`Invalid alias value: ${JSON.stringify(value)}, key: ${key}`);
4741
+ (0, psdWriter_1.writeInt32)(writer, value.length);
4742
+ for (let i = 0; i < value.length; i++) {
4743
+ (0, psdWriter_1.writeUint8)(writer, value.charCodeAt(i) & 0xff);
4744
+ }
4745
+ break;
4746
+ }
4595
4747
  case 'tdta': // Raw Data
4596
4748
  (0, psdWriter_1.writeInt32)(writer, value.byteLength);
4597
4749
  (0, psdWriter_1.writeBytes)(writer, value);
@@ -4599,15 +4751,16 @@ function writeOSType(writer, type, value, key, extType, root) {
4599
4751
  case 'ObAr': { // Object array
4600
4752
  (0, psdWriter_1.writeInt32)(writer, 16); // version
4601
4753
  (0, psdWriter_1.writeUnicodeStringWithPadding)(writer, ''); // name
4602
- const type = ObArTypes[key];
4754
+ const type = (_c = (_b = getHidden(value, obArInfoKey)) === null || _b === void 0 ? void 0 : _b.classID) !== null && _c !== void 0 ? _c : ObArTypes[key];
4603
4755
  if (!type)
4604
4756
  throw new Error(`Not implemented ObArType for: ${key}`);
4605
4757
  writeAsciiStringOrClassId(writer, type);
4606
4758
  (0, psdWriter_1.writeInt32)(writer, value.length);
4607
4759
  for (let i = 0; i < value.length; i++) {
4760
+ const info = getHidden(value[i], obArInfoKey);
4608
4761
  writeAsciiStringOrClassId(writer, value[i].type); // Hrzn | Vrtc
4609
- (0, psdWriter_1.writeSignature)(writer, 'UnFl');
4610
- (0, psdWriter_1.writeSignature)(writer, '#Pxl');
4762
+ (0, psdWriter_1.writeSignature)(writer, (_d = info === null || info === void 0 ? void 0 : info.valueType) !== null && _d !== void 0 ? _d : 'UnFl');
4763
+ (0, psdWriter_1.writeSignature)(writer, (_e = info === null || info === void 0 ? void 0 : info.units) !== null && _e !== void 0 ? _e : '#Pxl');
4611
4764
  (0, psdWriter_1.writeInt32)(writer, value[i].values.length);
4612
4765
  for (let j = 0; j < value[i].values.length; j++) {
4613
4766
  (0, psdWriter_1.writeFloat64)(writer, value[i].values[j]);
@@ -4628,85 +4781,144 @@ function writeOSType(writer, type, value, key, extType, root) {
4628
4781
  throw new Error(`Not implemented descriptor OSType: ${type}`);
4629
4782
  }
4630
4783
  }
4784
+ function referenceItemToValue(item) {
4785
+ switch (item.type) {
4786
+ case 'prop': return item.keyID;
4787
+ case 'Clss': return { name: item.name || '', classID: item.classID };
4788
+ case 'Enmr': return `${item.typeID}.${item.enumValue}`;
4789
+ default: return item.value;
4790
+ }
4791
+ }
4792
+ function referenceItemMatches(item, value) {
4793
+ if (!item)
4794
+ return false;
4795
+ const expected = referenceItemToValue(item);
4796
+ if (expected && typeof expected === 'object') {
4797
+ return !!value && typeof value === 'object' && value.name === expected.name && value.classID === expected.classID;
4798
+ }
4799
+ return expected === value;
4800
+ }
4801
+ // Attaches information about how each item of reference should be written (class IDs, reference types),
4802
+ // the public value of the array stays the same (array of strings/numbers/class objects)
4803
+ function createDescriptorReference(items) {
4804
+ return setHidden(items.map(referenceItemToValue), referenceInfoKey, items);
4805
+ }
4631
4806
  function readReferenceStructure(reader) {
4632
4807
  const itemsCount = (0, psdReader_1.readInt32)(reader);
4633
- const items = [];
4808
+ const infos = [];
4634
4809
  for (let i = 0; i < itemsCount; i++) {
4635
4810
  const type = (0, psdReader_1.readSignature)(reader);
4636
4811
  switch (type) {
4637
4812
  case 'prop': { // Property
4638
- readClassStructure(reader);
4813
+ const { name, classID } = readClassStructure(reader);
4639
4814
  const keyID = readAsciiStringOrClassId(reader);
4640
- items.push(keyID);
4815
+ infos.push({ type, name, classID, keyID });
4641
4816
  break;
4642
4817
  }
4643
- case 'Clss': // Class
4644
- items.push(readClassStructure(reader));
4818
+ case 'Clss': { // Class
4819
+ const { name, classID } = readClassStructure(reader);
4820
+ infos.push({ type, name, classID });
4645
4821
  break;
4822
+ }
4646
4823
  case 'Enmr': { // Enumerated Reference
4647
- readClassStructure(reader);
4824
+ const { name, classID } = readClassStructure(reader);
4648
4825
  const typeID = readAsciiStringOrClassId(reader);
4649
- const value = readAsciiStringOrClassId(reader);
4650
- items.push(`${typeID}.${value}`);
4826
+ const enumValue = readAsciiStringOrClassId(reader);
4827
+ infos.push({ type, name, classID, typeID, enumValue });
4651
4828
  break;
4652
4829
  }
4653
4830
  case 'rele': { // Offset
4654
- // const { name, classID } =
4655
- readClassStructure(reader);
4656
- items.push((0, psdReader_1.readUint32)(reader));
4831
+ const { name, classID } = readClassStructure(reader);
4832
+ infos.push({ type, name, classID, value: (0, psdReader_1.readUint32)(reader) });
4657
4833
  break;
4658
4834
  }
4659
4835
  case 'Idnt': // Identifier
4660
- items.push((0, psdReader_1.readInt32)(reader));
4661
- break;
4662
4836
  case 'indx': // Index
4663
- items.push((0, psdReader_1.readInt32)(reader));
4837
+ infos.push({ type, value: (0, psdReader_1.readInt32)(reader) });
4664
4838
  break;
4665
4839
  case 'name': { // Name
4666
- readClassStructure(reader);
4667
- items.push((0, psdReader_1.readUnicodeString)(reader));
4840
+ const { name, classID } = readClassStructure(reader);
4841
+ infos.push({ type, name, classID, value: (0, psdReader_1.readUnicodeString)(reader) });
4668
4842
  break;
4669
4843
  }
4670
4844
  default:
4671
4845
  throw new Error(`Invalid descriptor reference type: ${type}`);
4672
4846
  }
4673
4847
  }
4674
- return items;
4848
+ return createDescriptorReference(infos);
4849
+ }
4850
+ function guessReferenceItem(value) {
4851
+ if (typeof value === 'string') {
4852
+ // enumerated references are read as `${typeID}.${enumValue}` where typeID is a 4 character ID or a string ID
4853
+ // (values like 'My.Path' or 'Logo.v2' can't be typeID/enum value, so they are written as name references)
4854
+ const match = /^([a-z ]{4}|[a-z]{5,})\.([a-z ]+)$/i.exec(value);
4855
+ if (match) {
4856
+ return { type: 'Enmr', name: '', classID: match[1], typeID: match[1], enumValue: match[2] };
4857
+ }
4858
+ else {
4859
+ return { type: 'name', name: '', classID: 'Lyr ', value };
4860
+ }
4861
+ }
4862
+ else if (typeof value === 'number') {
4863
+ return { type: 'Idnt', value };
4864
+ }
4865
+ else if (value && typeof value === 'object' && typeof value.classID === 'string') {
4866
+ return { type: 'Clss', name: value.name || '', classID: value.classID };
4867
+ }
4868
+ else {
4869
+ throw new Error(`Invalid descriptor reference value: ${JSON.stringify(value)}`);
4870
+ }
4871
+ }
4872
+ // path selection state (image resource 1088) is a reference to paths, enumerated references there use 'Path' class
4873
+ // (name references use 'Lyr ' class, they reference layer of the vector mask)
4874
+ function pathSelectionStateToReference(items) {
4875
+ const infos = getHidden(items, referenceInfoKey);
4876
+ return createDescriptorReference(items.map((value, i) => {
4877
+ if (referenceItemMatches(infos === null || infos === void 0 ? void 0 : infos[i], value))
4878
+ return infos[i]; // keep information read from the file
4879
+ const item = guessReferenceItem(value);
4880
+ if (item.type === 'Enmr')
4881
+ item.classID = 'Path';
4882
+ return item;
4883
+ }));
4675
4884
  }
4676
4885
  function writeReferenceStructure(writer, _key, items) {
4886
+ if (!Array.isArray(items))
4887
+ throw new Error(`Invalid reference value: ${JSON.stringify(items)}, key: ${_key}`);
4888
+ const infos = getHidden(items, referenceInfoKey);
4677
4889
  (0, psdWriter_1.writeInt32)(writer, items.length);
4678
4890
  for (let i = 0; i < items.length; i++) {
4679
4891
  const value = items[i];
4680
- let type = 'unknown';
4681
- if (typeof value === 'string') {
4682
- if (/^[a-z ]+\.[a-z ]+$/i.test(value)) {
4683
- type = 'Enmr';
4684
- }
4685
- else {
4686
- type = 'name';
4687
- }
4688
- }
4689
- (0, psdWriter_1.writeSignature)(writer, type);
4690
- switch (type) {
4691
- // case 'prop': // Property
4692
- // case 'Clss': // Class
4693
- case 'Enmr': { // Enumerated Reference
4694
- const [typeID, enumValue] = value.split('.');
4695
- writeClassStructure(writer, '\0', typeID);
4696
- writeAsciiStringOrClassId(writer, typeID);
4697
- writeAsciiStringOrClassId(writer, enumValue);
4892
+ const recorded = infos === null || infos === void 0 ? void 0 : infos[i];
4893
+ const item = referenceItemMatches(recorded, value) ? recorded : guessReferenceItem(value);
4894
+ (0, psdWriter_1.writeSignature)(writer, item.type);
4895
+ switch (item.type) {
4896
+ case 'prop': // Property
4897
+ writeClassStructure(writer, item.name || '', item.classID);
4898
+ writeAsciiStringOrClassId(writer, item.keyID);
4698
4899
  break;
4699
- }
4700
- // case 'rele': // Offset
4701
- // case 'Idnt': // Identifier
4702
- // case 'indx': // Index
4703
- case 'name': { // Name
4704
- writeClassStructure(writer, '\0', 'Lyr ');
4705
- (0, psdWriter_1.writeUnicodeString)(writer, value + '\0');
4900
+ case 'Clss': // Class
4901
+ writeClassStructure(writer, item.name || '', item.classID);
4902
+ break;
4903
+ case 'Enmr': // Enumerated Reference
4904
+ writeClassStructure(writer, item.name || '', item.classID);
4905
+ writeAsciiStringOrClassId(writer, item.typeID);
4906
+ writeAsciiStringOrClassId(writer, item.enumValue);
4907
+ break;
4908
+ case 'rele': // Offset
4909
+ writeClassStructure(writer, item.name || '', item.classID);
4910
+ (0, psdWriter_1.writeUint32)(writer, item.value);
4911
+ break;
4912
+ case 'Idnt': // Identifier
4913
+ case 'indx': // Index
4914
+ (0, psdWriter_1.writeInt32)(writer, item.value);
4915
+ break;
4916
+ case 'name': // Name
4917
+ writeClassStructure(writer, item.name || '', item.classID);
4918
+ (0, psdWriter_1.writeUnicodeStringWithPadding)(writer, `${item.value}`);
4706
4919
  break;
4707
- }
4708
4920
  default:
4709
- throw new Error(`Invalid descriptor reference type: ${type}`);
4921
+ throw new Error(`Invalid descriptor reference type: ${item.type}`);
4710
4922
  }
4711
4923
  }
4712
4924
  return items;
@@ -4717,7 +4929,7 @@ function readClassStructure(reader) {
4717
4929
  return { name, classID };
4718
4930
  }
4719
4931
  function writeClassStructure(writer, name, classID) {
4720
- (0, psdWriter_1.writeUnicodeString)(writer, name);
4932
+ (0, psdWriter_1.writeUnicodeStringWithPadding)(writer, name);
4721
4933
  writeAsciiStringOrClassId(writer, classID);
4722
4934
  }
4723
4935
  function readVersionAndDescriptor(reader, includeClass = false) {
@@ -4849,9 +5061,11 @@ function serializeEffects(e, log, multi) {
4849
5061
  info.numModifyingFX = 0;
4850
5062
  for (const key of Object.keys(e)) {
4851
5063
  const value = e[key];
5064
+ if (value == null || typeof value !== 'object')
5065
+ continue;
4852
5066
  if (Array.isArray(value)) {
4853
5067
  for (const effect of value) {
4854
- if (effect.enabled)
5068
+ if (effect === null || effect === void 0 ? void 0 : effect.enabled)
4855
5069
  info.numModifyingFX++;
4856
5070
  }
4857
5071
  }
@@ -5000,7 +5214,7 @@ function serializeTrackList(tracks) {
5000
5214
  const t = tracks[i];
5001
5215
  trackList.push(Object.assign(Object.assign({ trackID: exports.stdTrackID.encode(t.type), Vrsn: 1, enab: !!t.enabled, Effc: !!t.effectParams }, (t.effectParams ? {
5002
5216
  effectParams: {
5003
- keyList: serializeKeyList(t.keys),
5217
+ keyList: serializeKeyList(t.effectParams.keys),
5004
5218
  fillCanvas: t.effectParams.fillCanvas,
5005
5219
  zoomOrigin: t.effectParams.zoomOrigin,
5006
5220
  }
@@ -5243,7 +5457,7 @@ function serializeEffectObject(obj, objName, reportErrors) {
5243
5457
  result.sdwO = unitsPercent(val);
5244
5458
  break;
5245
5459
  case 'angle':
5246
- if (objName === 'gradientOverlay' || objName === 'patternFill') {
5460
+ if (objName === 'gradientOverlay' || objName === 'patternOverlay') {
5247
5461
  result.Angl = unitsAngle(val);
5248
5462
  }
5249
5463
  else {
@@ -5315,7 +5529,8 @@ function serializeEffectObject(obj, objName, reportErrors) {
5315
5529
  }
5316
5530
  function parseGradient(grad) {
5317
5531
  if (grad.GrdF === 'GrdF.CstS') {
5318
- const samples = grad.Intr || 4096;
5532
+ // stop locations are always in 0..4096 range, independent of smoothness (Intr)
5533
+ const samples = 4096;
5319
5534
  return {
5320
5535
  type: 'solid',
5321
5536
  name: grad['Nm '],
@@ -5349,11 +5564,12 @@ function parseGradient(grad) {
5349
5564
  function serializeGradient(grad) {
5350
5565
  var _a, _b;
5351
5566
  if (grad.type === 'solid') {
5352
- const samples = Math.round(((_a = grad.smoothness) !== null && _a !== void 0 ? _a : 1) * 4096);
5567
+ // stop locations are always in 0..4096 range, independent of smoothness (Intr)
5568
+ const samples = 4096;
5353
5569
  return {
5354
5570
  'Nm ': grad.name || '',
5355
5571
  GrdF: 'GrdF.CstS',
5356
- Intr: samples,
5572
+ Intr: Math.round(((_a = grad.smoothness) !== null && _a !== void 0 ? _a : 1) * 4096),
5357
5573
  Clrs: grad.colorStops.map(s => {
5358
5574
  var _a;
5359
5575
  return ({
@@ -5382,8 +5598,8 @@ function serializeGradient(grad) {
5382
5598
  ClrS: exports.ClrS.encode(grad.colorModel),
5383
5599
  RndS: grad.randomSeed || 0,
5384
5600
  Smth: Math.round(((_b = grad.roughness) !== null && _b !== void 0 ? _b : 1) * 4096),
5385
- 'Mnm ': (grad.min || [0, 0, 0, 0]).map(x => x * 100),
5386
- 'Mxm ': (grad.max || [1, 1, 1, 1]).map(x => x * 100),
5601
+ 'Mnm ': (grad.min || [0, 0, 0, 0]).map(x => Math.round(x * 100)),
5602
+ 'Mxm ': (grad.max || [1, 1, 1, 1]).map(x => Math.round(x * 100)),
5387
5603
  };
5388
5604
  }
5389
5605
  }
@@ -5483,21 +5699,65 @@ function serializeVectorContent(content) {
5483
5699
  return { key: 'GdFl', descriptor: serializeGradientContent(content) };
5484
5700
  }
5485
5701
  }
5702
+ // Descriptor color values: HSB saturation/brightness 0..100, CMYK 0..100, Gray 0..100, Lab L 0..100, a/b -128..127
5703
+ // 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)
5704
+ const colorSourceKey = typeof Symbol !== 'undefined' ? Symbol('agPsdColorSource') : '__agPsdColorSource';
5705
+ const colorConversions = {
5706
+ // key: [descriptor -> color, color -> descriptor]
5707
+ Strt: [x => x / 100, x => x * 100],
5708
+ Brgh: [x => x / 100, x => x * 100],
5709
+ 'Cyn ': [x => x * 255 / 100, x => x * 100 / 255],
5710
+ Mgnt: [x => x * 255 / 100, x => x * 100 / 255],
5711
+ 'Ylw ': [x => x * 255 / 100, x => x * 100 / 255],
5712
+ Blck: [x => x * 255 / 100, x => x * 100 / 255],
5713
+ 'Gry ': [x => x * 255 / 100, x => x * 100 / 255],
5714
+ Lmnc: [x => x / 100, x => x * 100],
5715
+ 'A ': [helpers_1.normalizeLabAB, helpers_1.denormalizeLabAB],
5716
+ 'B ': [helpers_1.normalizeLabAB, helpers_1.denormalizeLabAB],
5717
+ };
5718
+ function colorToDescriptorValue(key, value) {
5719
+ const conversion = colorConversions[key];
5720
+ if (!conversion)
5721
+ return value;
5722
+ const [from, to] = conversion;
5723
+ const result = to(value);
5724
+ if (!isFinite(result))
5725
+ return result;
5726
+ // prefer shortest number that converts back to the same value (avoids values like 30.200000000000003)
5727
+ for (let precision = 1; precision < 17; precision++) {
5728
+ const candidate = +result.toPrecision(precision);
5729
+ if (from(candidate) === value)
5730
+ return candidate;
5731
+ }
5732
+ return result;
5733
+ }
5734
+ function shallowEqual(a, b) {
5735
+ const keys = Object.keys(a);
5736
+ if (keys.length !== Object.keys(b).length)
5737
+ return false;
5738
+ return keys.every(key => a[key] === b[key]);
5739
+ }
5486
5740
  function parseColor(color) {
5741
+ let result;
5487
5742
  if ('H ' in color) {
5488
- return { h: parsePercentOrAngle(color['H ']), s: color.Strt, b: color.Brgh };
5743
+ result = { h: parsePercentOrAngle(color['H ']), s: color.Strt / 100, b: color.Brgh / 100 };
5489
5744
  }
5490
5745
  else if ('Rd ' in color) {
5491
5746
  return { r: color['Rd '], g: color['Grn '], b: color['Bl '] };
5492
5747
  }
5493
5748
  else if ('Cyn ' in color) {
5494
- return { c: color['Cyn '], m: color.Mgnt, y: color['Ylw '], k: color.Blck };
5749
+ result = {
5750
+ c: color['Cyn '] * 255 / 100,
5751
+ m: color.Mgnt * 255 / 100,
5752
+ y: color['Ylw '] * 255 / 100,
5753
+ k: color.Blck * 255 / 100,
5754
+ };
5495
5755
  }
5496
5756
  else if ('Gry ' in color) {
5497
- return { k: color['Gry '] };
5757
+ result = { k: color['Gry '] * 255 / 100 };
5498
5758
  }
5499
5759
  else if ('Lmnc' in color) {
5500
- return { l: color.Lmnc, a: color['A '], b: color['B '] };
5760
+ result = { l: color.Lmnc / 100, a: (0, helpers_1.normalizeLabAB)(color['A ']), b: (0, helpers_1.normalizeLabAB)(color['B ']) };
5501
5761
  }
5502
5762
  else if ('redFloat' in color) {
5503
5763
  return { fr: color.redFloat, fg: color.greenFloat, fb: color.blueFloat };
@@ -5505,8 +5765,10 @@ function parseColor(color) {
5505
5765
  else {
5506
5766
  throw new Error('Unsupported color descriptor');
5507
5767
  }
5768
+ // remember original descriptor values, so unmodified colors can be written back without conversion errors
5769
+ return setHidden(result, colorSourceKey, { color: Object.assign({}, result), descriptor: color });
5508
5770
  }
5509
- function serializeColor(color) {
5771
+ function serializeColorValues(color) {
5510
5772
  if (!color) {
5511
5773
  return { _name: '', _classID: 'RGBC', 'Rd ': 0, 'Grn ': 0, 'Bl ': 0 };
5512
5774
  }
@@ -5517,21 +5779,50 @@ function serializeColor(color) {
5517
5779
  return { _name: '', _classID: 'RGBC', redFloat: color.fr, greenFloat: color.fg, blueFloat: color.fb };
5518
5780
  }
5519
5781
  else if ('h' in color) {
5520
- return { _name: '', _classID: 'HSBC', 'H ': unitsAngle(color.h * 360), Strt: color.s || 0, Brgh: color.b || 0 };
5782
+ return {
5783
+ _name: '', _classID: 'HSBC',
5784
+ 'H ': unitsAngle((color.h || 0) * 360),
5785
+ Strt: colorToDescriptorValue('Strt', color.s || 0),
5786
+ Brgh: colorToDescriptorValue('Brgh', color.b || 0),
5787
+ };
5521
5788
  }
5522
5789
  else if ('c' in color) {
5523
- return { _name: '', _classID: 'CMYC', 'Cyn ': color.c || 0, Mgnt: color.m || 0, 'Ylw ': color.y || 0, Blck: color.k || 0 };
5790
+ return {
5791
+ _name: '', _classID: 'CMYC',
5792
+ 'Cyn ': colorToDescriptorValue('Cyn ', color.c || 0),
5793
+ Mgnt: colorToDescriptorValue('Mgnt', color.m || 0),
5794
+ 'Ylw ': colorToDescriptorValue('Ylw ', color.y || 0),
5795
+ Blck: colorToDescriptorValue('Blck', color.k || 0),
5796
+ };
5524
5797
  }
5525
5798
  else if ('l' in color) {
5526
- return { _name: '', _classID: 'LABC', Lmnc: color.l || 0, 'A ': color.a || 0, 'B ': color.b || 0 };
5799
+ return {
5800
+ _name: '', _classID: 'LbCl',
5801
+ Lmnc: colorToDescriptorValue('Lmnc', color.l || 0),
5802
+ 'A ': colorToDescriptorValue('A ', color.a || 0),
5803
+ 'B ': colorToDescriptorValue('B ', color.b || 0),
5804
+ };
5527
5805
  }
5528
5806
  else if ('k' in color) {
5529
- return { _name: '', _classID: 'GRYC', 'Gry ': color.k };
5807
+ return { _name: '', _classID: 'Grsc', 'Gry ': colorToDescriptorValue('Gry ', color.k || 0) };
5530
5808
  }
5531
5809
  else {
5532
5810
  throw new Error('Invalid color value');
5533
5811
  }
5534
5812
  }
5813
+ function serializeColor(color) {
5814
+ const result = serializeColorValues(color);
5815
+ const source = getHidden(color, colorSourceKey);
5816
+ if (source && shallowEqual(source.color, color)) {
5817
+ // color was not modified since it was read, use exact original values
5818
+ for (const key of Object.keys(result)) {
5819
+ if (colorConversions[key] && typeof source.descriptor[key] === 'number') {
5820
+ result[key] = source.descriptor[key];
5821
+ }
5822
+ }
5823
+ }
5824
+ return result;
5825
+ }
5535
5826
  function parseAngle(x) {
5536
5827
  if (x === undefined)
5537
5828
  return 0;
@@ -5948,9 +6239,9 @@ function readEffects(reader) {
5948
6239
  case 'cmnS': { // common state (see See Effects layer, common state info)
5949
6240
  const size = (0, psdReader_1.readUint32)(reader);
5950
6241
  const version = (0, psdReader_1.readUint32)(reader);
5951
- const visible = !!(0, psdReader_1.readUint8)(reader);
6242
+ (0, psdReader_1.readUint8)(reader); // visible (not reliable, Photoshop writes 1 even if effects are disabled)
5952
6243
  (0, psdReader_1.skipBytes)(reader, 2);
5953
- if (size !== 7 || version !== 0 || !visible)
6244
+ if (size !== 7 || version !== 0)
5954
6245
  throw new Error(`Invalid effects common state`);
5955
6246
  break;
5956
6247
  }
@@ -6098,7 +6389,7 @@ function writeShadowInfo(writer, shadow) {
6098
6389
  (0, psdWriter_1.writeColor)(writer, shadow.color); // native color
6099
6390
  }
6100
6391
  function writeEffects(writer, effects) {
6101
- var _a, _b, _c, _d, _e, _f;
6392
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
6102
6393
  const dropShadow = (_a = effects.dropShadow) === null || _a === void 0 ? void 0 : _a[0];
6103
6394
  const innerShadow = (_b = effects.innerShadow) === null || _b === void 0 ? void 0 : _b[0];
6104
6395
  const outerGlow = effects.outerGlow;
@@ -6124,7 +6415,7 @@ function writeEffects(writer, effects) {
6124
6415
  (0, psdWriter_1.writeSignature)(writer, 'cmnS');
6125
6416
  (0, psdWriter_1.writeUint32)(writer, 7); // size
6126
6417
  (0, psdWriter_1.writeUint32)(writer, 0); // version
6127
- (0, psdWriter_1.writeUint8)(writer, 1); // visible
6418
+ (0, psdWriter_1.writeUint8)(writer, 1); // visible (Photoshop writes 1 here even if effects are disabled)
6128
6419
  (0, psdWriter_1.writeZeros)(writer, 2);
6129
6420
  if (dropShadow) {
6130
6421
  (0, psdWriter_1.writeSignature)(writer, '8BIM');
@@ -6146,7 +6437,7 @@ function writeEffects(writer, effects) {
6146
6437
  (0, psdWriter_1.writeColor)(writer, outerGlow.color);
6147
6438
  writeBlendMode(writer, outerGlow.blendMode);
6148
6439
  (0, psdWriter_1.writeUint8)(writer, outerGlow.enabled ? 1 : 0);
6149
- writeFixedPoint8(writer, outerGlow.opacity || 0);
6440
+ writeFixedPoint8(writer, (_e = outerGlow.opacity) !== null && _e !== void 0 ? _e : 1);
6150
6441
  (0, psdWriter_1.writeColor)(writer, outerGlow.color);
6151
6442
  }
6152
6443
  if (innerGlow) {
@@ -6154,12 +6445,12 @@ function writeEffects(writer, effects) {
6154
6445
  (0, psdWriter_1.writeSignature)(writer, 'iglw');
6155
6446
  (0, psdWriter_1.writeUint32)(writer, 43);
6156
6447
  (0, psdWriter_1.writeUint32)(writer, 2);
6157
- (0, psdWriter_1.writeFixedPoint32)(writer, ((_e = innerGlow.size) === null || _e === void 0 ? void 0 : _e.value) || 0);
6448
+ (0, psdWriter_1.writeFixedPoint32)(writer, ((_f = innerGlow.size) === null || _f === void 0 ? void 0 : _f.value) || 0);
6158
6449
  (0, psdWriter_1.writeFixedPoint32)(writer, 0); // intensity
6159
6450
  (0, psdWriter_1.writeColor)(writer, innerGlow.color);
6160
6451
  writeBlendMode(writer, innerGlow.blendMode);
6161
6452
  (0, psdWriter_1.writeUint8)(writer, innerGlow.enabled ? 1 : 0);
6162
- writeFixedPoint8(writer, innerGlow.opacity || 0);
6453
+ writeFixedPoint8(writer, (_g = innerGlow.opacity) !== null && _g !== void 0 ? _g : 1);
6163
6454
  (0, psdWriter_1.writeUint8)(writer, 0); // inverted
6164
6455
  (0, psdWriter_1.writeColor)(writer, innerGlow.color);
6165
6456
  }
@@ -6170,15 +6461,15 @@ function writeEffects(writer, effects) {
6170
6461
  (0, psdWriter_1.writeUint32)(writer, 2);
6171
6462
  (0, psdWriter_1.writeFixedPoint32)(writer, bevel.angle || 0);
6172
6463
  (0, psdWriter_1.writeFixedPoint32)(writer, bevel.strength || 0);
6173
- (0, psdWriter_1.writeFixedPoint32)(writer, ((_f = bevel.size) === null || _f === void 0 ? void 0 : _f.value) || 0);
6464
+ (0, psdWriter_1.writeFixedPoint32)(writer, ((_h = bevel.size) === null || _h === void 0 ? void 0 : _h.value) || 0);
6174
6465
  writeBlendMode(writer, bevel.highlightBlendMode);
6175
6466
  writeBlendMode(writer, bevel.shadowBlendMode);
6176
6467
  (0, psdWriter_1.writeColor)(writer, bevel.highlightColor);
6177
6468
  (0, psdWriter_1.writeColor)(writer, bevel.shadowColor);
6178
6469
  const style = bevelStyles.indexOf(bevel.style);
6179
- (0, psdWriter_1.writeUint8)(writer, style <= 0 ? 1 : style);
6180
- writeFixedPoint8(writer, bevel.highlightOpacity || 0);
6181
- writeFixedPoint8(writer, bevel.shadowOpacity || 0);
6470
+ (0, psdWriter_1.writeUint8)(writer, style <= 0 ? bevelStyles.indexOf('inner bevel') : style); // reader defaults to inner bevel
6471
+ writeFixedPoint8(writer, (_j = bevel.highlightOpacity) !== null && _j !== void 0 ? _j : 1);
6472
+ writeFixedPoint8(writer, (_k = bevel.shadowOpacity) !== null && _k !== void 0 ? _k : 1);
6182
6473
  (0, psdWriter_1.writeUint8)(writer, bevel.enabled ? 1 : 0);
6183
6474
  (0, psdWriter_1.writeUint8)(writer, bevel.useGlobalLight ? 1 : 0);
6184
6475
  (0, psdWriter_1.writeUint8)(writer, bevel.direction === 'down' ? 1 : 0);
@@ -6192,7 +6483,7 @@ function writeEffects(writer, effects) {
6192
6483
  (0, psdWriter_1.writeUint32)(writer, 2);
6193
6484
  writeBlendMode(writer, solidFill.blendMode);
6194
6485
  (0, psdWriter_1.writeColor)(writer, solidFill.color);
6195
- writeFixedPoint8(writer, solidFill.opacity || 0);
6486
+ writeFixedPoint8(writer, (_l = solidFill.opacity) !== null && _l !== void 0 ? _l : 1);
6196
6487
  (0, psdWriter_1.writeUint8)(writer, solidFill.enabled ? 1 : 0);
6197
6488
  (0, psdWriter_1.writeColor)(writer, solidFill.color);
6198
6489
  }
@@ -6201,6 +6492,7 @@ function writeEffects(writer, effects) {
6201
6492
  },{"./helpers":8,"./psdReader":13,"./psdWriter":14}],6:[function(require,module,exports){
6202
6493
  "use strict";
6203
6494
  Object.defineProperty(exports, "__esModule", { value: true });
6495
+ exports.setProperty = setProperty;
6204
6496
  exports.parseEngineData = parseEngineData;
6205
6497
  exports.serializeEngineData = serializeEngineData;
6206
6498
  function isWhitespace(char) {
@@ -6211,7 +6503,15 @@ function isNumber(char) {
6211
6503
  // 0123456789.-
6212
6504
  return (char >= 48 && char <= 57) || char === 46 || char === 45;
6213
6505
  }
6214
- function parseEngineData(data) {
6506
+ function setProperty(target, key, value) {
6507
+ if (key === '__proto__') { // avoid changing prototype of the object
6508
+ Object.defineProperty(target, key, { value, enumerable: true, configurable: true, writable: true });
6509
+ }
6510
+ else {
6511
+ target[key] = value;
6512
+ }
6513
+ }
6514
+ function parseEngineData(data, reader) {
6215
6515
  let index = 0;
6216
6516
  function skipWhitespace() {
6217
6517
  while (index < data.length && isWhitespace(data[index])) {
@@ -6270,7 +6570,8 @@ function parseEngineData(data) {
6270
6570
  throw new Error('Invalid data');
6271
6571
  const top = stack[stack.length - 1];
6272
6572
  if (typeof top === 'string') {
6273
- stack[stack.length - 2][top] = value;
6573
+ const target = stack[stack.length - 2];
6574
+ setProperty(target, top, value);
6274
6575
  pop();
6275
6576
  }
6276
6577
  else if (Array.isArray(top)) {
@@ -6284,7 +6585,8 @@ function parseEngineData(data) {
6284
6585
  if (!stack.length)
6285
6586
  pushContainer({});
6286
6587
  const top = stack[stack.length - 1];
6287
- if (top && typeof top === 'string') {
6588
+ if (typeof top === 'string' || Array.isArray(top)) {
6589
+ // name used as a value (either property value or array item)
6288
6590
  if (name === 'nil') {
6289
6591
  pushValue(null);
6290
6592
  }
@@ -6306,6 +6608,8 @@ function parseEngineData(data) {
6306
6608
  }
6307
6609
  skipWhitespace();
6308
6610
  let dataLength = data.length;
6611
+ let invalidTokens = 0;
6612
+ let firstInvalidToken = '';
6309
6613
  while (dataLength > 0 && data[dataLength - 1] === 0)
6310
6614
  dataLength--; // trim 0 bytes from end
6311
6615
  while (index < dataLength) {
@@ -6365,14 +6669,16 @@ function parseEngineData(data) {
6365
6669
  }
6366
6670
  else {
6367
6671
  index += 1;
6368
- console.log(`Invalid token '${String.fromCharCode(char)}' (${char}) at ${index}`
6369
- // + ` near '${String.fromCharCode.apply(null, data.slice(index - 10, index + 20) as any)}'`
6370
- // + ` data [${Array.from(data.slice(index - 10, index + 20)).join(', ')}]`
6371
- );
6672
+ if (!invalidTokens)
6673
+ firstInvalidToken = `'${String.fromCharCode(char)}' (${char}) at ${index}`;
6674
+ invalidTokens++;
6372
6675
  // throw new Error(`Invalid token ${String.fromCharCode(char)} at ${index}`);
6373
6676
  }
6374
6677
  skipWhitespace();
6375
6678
  }
6679
+ if (invalidTokens) {
6680
+ reader.log(`Invalid tokens in engine data (${invalidTokens} in total, first: ${firstInvalidToken})`);
6681
+ }
6376
6682
  return root;
6377
6683
  }
6378
6684
  const floatKeys = [
@@ -6382,7 +6688,6 @@ const floatKeys = [
6382
6688
  'OutlineWidth', 'AutoLeading',
6383
6689
  ];
6384
6690
  const intArrays = ['RunLengthArray'];
6385
- // TODO: handle /nil
6386
6691
  function serializeEngineData(data, condensed = false) {
6387
6692
  let buffer = new Uint8Array(1024);
6388
6693
  let offset = 0;
@@ -6418,16 +6723,21 @@ function serializeEngineData(data, condensed = false) {
6418
6723
  if (!condensed)
6419
6724
  writeString('\n');
6420
6725
  }
6726
+ // NaN and Infinity can't be represented in engine data, write them as 0
6727
+ function finite(value) {
6728
+ return Number.isFinite(value) ? value : 0;
6729
+ }
6421
6730
  function serializeInt(value) {
6422
- return value.toString();
6731
+ return finite(value).toString();
6423
6732
  }
6424
6733
  function serializeFloat(value) {
6425
- return value.toFixed(5)
6734
+ return finite(value).toFixed(5)
6426
6735
  .replace(/(\d)0+$/g, '$1')
6427
6736
  .replace(/^0+\.([1-9])/g, '.$1')
6428
6737
  .replace(/^-0+\.0(\d)/g, '-.0$1');
6429
6738
  }
6430
6739
  function serializeNumber(value, key) {
6740
+ value = finite(value);
6431
6741
  const isFloat = (key && floatKeys.indexOf(key) !== -1) || (value | 0) !== value;
6432
6742
  return isFloat ? serializeFloat(value) : serializeInt(value);
6433
6743
  }
@@ -6454,7 +6764,7 @@ function serializeEngineData(data, condensed = false) {
6454
6764
  writeIndent();
6455
6765
  }
6456
6766
  }
6457
- if (value === null) {
6767
+ if (value === null || value === undefined) { // undefined is only written for array items, properties are skipped
6458
6768
  writePrefix();
6459
6769
  writeString(condensed ? '/nil' : 'null');
6460
6770
  }
@@ -6516,7 +6826,8 @@ function serializeEngineData(data, condensed = false) {
6516
6826
  writeString('\n');
6517
6827
  indent++;
6518
6828
  for (const key of getKeys(value)) {
6519
- writeProperty(key, value[key]);
6829
+ if (value[key] !== undefined)
6830
+ writeProperty(key, value[key]);
6520
6831
  }
6521
6832
  indent--;
6522
6833
  writeIndent();
@@ -6527,7 +6838,8 @@ function serializeEngineData(data, condensed = false) {
6527
6838
  if (condensed) {
6528
6839
  if (typeof data === 'object') {
6529
6840
  for (const key of getKeys(data)) {
6530
- writeProperty(key, data[key]);
6841
+ if (data[key] !== undefined)
6842
+ writeProperty(key, data[key]);
6531
6843
  }
6532
6844
  }
6533
6845
  }
@@ -6544,6 +6856,7 @@ function serializeEngineData(data, condensed = false) {
6544
6856
  // /test/engineData2.json:1109 is character codes
6545
6857
  Object.defineProperty(exports, "__esModule", { value: true });
6546
6858
  exports.decodeEngineData2 = decodeEngineData2;
6859
+ const engineData_1 = require("./engineData");
6547
6860
  const keysColor = {
6548
6861
  '0': {
6549
6862
  uproot: true,
@@ -6914,48 +7227,78 @@ const keysRoot = {
6914
7227
  },
6915
7228
  },
6916
7229
  };
6917
- function decodeObj(obj, keys) {
7230
+ const MAX_DEPTH = 256;
7231
+ function getKey(keys, key) {
7232
+ return Object.prototype.hasOwnProperty.call(keys, key) ? keys[key] : undefined;
7233
+ }
7234
+ function decodeObj(obj, keys, depth) {
6918
7235
  var _a, _b;
6919
- if (obj === null)
7236
+ if (depth > MAX_DEPTH)
7237
+ throw new Error(`Engine data is nested too deeply (more than ${MAX_DEPTH} levels)`);
7238
+ if (obj === null || obj === undefined)
6920
7239
  return obj;
6921
7240
  if (Array.isArray(obj))
6922
- return obj.map(x => decodeObj(x, keys));
7241
+ return obj.map(x => decodeObj(x, keys, depth + 1));
6923
7242
  if (typeof obj !== 'object')
6924
7243
  return obj;
6925
- let result = {};
6926
- for (const key of Object.keys(obj)) {
6927
- if (keys[key]) {
6928
- if (keys[key].uproot) {
6929
- if (key !== '99')
6930
- result = decodeObj(obj[key], (_a = keys[key].children) !== null && _a !== void 0 ? _a : {});
6931
- if (obj['99'])
6932
- result._type = obj['99'];
6933
- break;
6934
- }
6935
- else {
6936
- result[keys[key].name || key] = decodeObj(obj[key], (_b = keys[key].children) !== null && _b !== void 0 ? _b : {});
6937
- }
6938
- }
6939
- else if (key === '99') {
6940
- result._type = obj[key];
7244
+ const objKeys = Object.keys(obj);
7245
+ const uprootKey = objKeys.find(key => { var _a; return key !== '99' && ((_a = getKey(keys, key)) === null || _a === void 0 ? void 0 : _a.uproot); });
7246
+ let result;
7247
+ if (uprootKey !== undefined) {
7248
+ // replace the object with the value of the uprooted key
7249
+ result = decodeObj(obj[uprootKey], (_a = getKey(keys, uprootKey).children) !== null && _a !== void 0 ? _a : {}, depth + 1);
7250
+ // primitive values can't hold extra properties (type and sibling keys are dropped)
7251
+ if (result === null || typeof result !== 'object')
7252
+ return result;
7253
+ if (obj['99'])
7254
+ result._type = obj['99'];
7255
+ if (Array.isArray(result))
7256
+ return result;
7257
+ }
7258
+ else {
7259
+ result = {};
7260
+ }
7261
+ for (const key of objKeys) {
7262
+ if (key === uprootKey)
7263
+ continue;
7264
+ const info = getKey(keys, key);
7265
+ if (key === '99') {
7266
+ if (uprootKey === undefined)
7267
+ result._type = obj[key];
6941
7268
  }
6942
7269
  else {
6943
- result[key] = decodeObj(obj[key], {});
7270
+ const name = (info === null || info === void 0 ? void 0 : info.name) || key;
7271
+ // don't overwrite fields of uprooted object with sibling keys
7272
+ if (uprootKey === undefined || !(name in result)) {
7273
+ (0, engineData_1.setProperty)(result, name, decodeObj(obj[key], (_b = info === null || info === void 0 ? void 0 : info.children) !== null && _b !== void 0 ? _b : {}, depth + 1));
7274
+ }
6944
7275
  }
6945
7276
  }
6946
7277
  return result;
6947
7278
  }
7279
+ function emptyResourceDict() {
7280
+ return { FontSet: [], StyleSheetSet: [], ParagraphSheetSet: [] };
7281
+ }
6948
7282
  function decodeEngineData2(data) {
6949
- return decodeObj(data, keysRoot);
7283
+ const result = decodeObj(data, keysRoot, 0);
7284
+ if (!result || typeof result !== 'object' || Array.isArray(result)) {
7285
+ return { ResourceDict: emptyResourceDict(), EngineDict: {} };
7286
+ }
7287
+ if (!result.ResourceDict || typeof result.ResourceDict !== 'object') {
7288
+ result.ResourceDict = emptyResourceDict();
7289
+ }
7290
+ return result;
6950
7291
  }
6951
7292
 
6952
- },{}],8:[function(require,module,exports){
7293
+ },{"./engineData":6}],8:[function(require,module,exports){
6953
7294
  "use strict";
6954
7295
  Object.defineProperty(exports, "__esModule", { value: true });
6955
7296
  exports.createImageData = exports.createCanvas = exports.MaskParams = exports.LayerMaskFlags = exports.ColorSpace = exports.largeAdditionalInfoKeys = exports.layerColors = exports.toBlendMode = exports.fromBlendMode = exports.RAW_IMAGE_DATA = exports.MOCK_HANDLERS = void 0;
6956
7297
  exports.revMap = revMap;
6957
7298
  exports.createEnum = createEnum;
6958
7299
  exports.offsetForChannel = offsetForChannel;
7300
+ exports.normalizeLabAB = normalizeLabAB;
7301
+ exports.denormalizeLabAB = denormalizeLabAB;
6959
7302
  exports.clamp = clamp;
6960
7303
  exports.hasAlpha = hasAlpha;
6961
7304
  exports.resetImageData = resetImageData;
@@ -7075,6 +7418,13 @@ function offsetForChannel(channelId, cmyk) {
7075
7418
  default: return channelId + 1;
7076
7419
  }
7077
7420
  }
7421
+ // Lab a/b components are stored in -128..127 range (multiplied by scale), normalized values are in -1..1 range
7422
+ function normalizeLabAB(value, scale = 1) {
7423
+ return value < 0 ? (value / (128 * scale)) : (value / (127 * scale));
7424
+ }
7425
+ function denormalizeLabAB(value, scale = 1) {
7426
+ return value < 0 ? (value * (128 * scale)) : (value * (127 * scale));
7427
+ }
7078
7428
  function clamp(value, min, max) {
7079
7429
  return value < min ? min : (value > max ? max : value);
7080
7430
  }
@@ -7150,9 +7500,10 @@ function writeDataRaw(data, offset, width, height) {
7150
7500
  }
7151
7501
  return array;
7152
7502
  }
7503
+ // returns view into the buffer, the buffer has to fit worst case size (see getRLEChannelBound), throws if it doesn't
7153
7504
  function writeDataRLE(buffer, { data, width, height }, offsets, large) {
7154
7505
  if (!width || !height)
7155
- return undefined;
7506
+ return buffer.subarray(0, 0);
7156
7507
  const stride = (4 * width) | 0;
7157
7508
  let ol = 0;
7158
7509
  let o = (offsets.length * (large ? 4 : 2) * height) | 0;
@@ -7229,6 +7580,9 @@ function writeDataRLE(buffer, { data, width, height }, offsets, large) {
7229
7580
  buffer[o++] = data[p];
7230
7581
  }
7231
7582
  }
7583
+ // writes past the end of typed array are ignored, so it's enough to check the size after each row
7584
+ if (o > buffer.length)
7585
+ throw new Error(`RLE buffer too small (${buffer.length})`);
7232
7586
  const length = o - startOffset;
7233
7587
  if (large) {
7234
7588
  buffer[ol++] = (length >> 24) & 0xff;
@@ -7238,7 +7592,8 @@ function writeDataRLE(buffer, { data, width, height }, offsets, large) {
7238
7592
  buffer[ol++] = length & 0xff;
7239
7593
  }
7240
7594
  }
7241
- return buffer.slice(0, o);
7595
+ // returns view of the buffer, copy the data if the buffer is going to be reused
7596
+ return buffer.subarray(0, o);
7242
7597
  }
7243
7598
  function writeDataZipWithoutPrediction({ data, width, height }, offsets) {
7244
7599
  const size = width * height;
@@ -7266,7 +7621,7 @@ function writeDataZipWithoutPrediction({ data, width, height }, offsets) {
7266
7621
  return buffers[0];
7267
7622
  }
7268
7623
  }
7269
- function createCanvasFromData(data) {
7624
+ function createCanvasFromData(data, reader) {
7270
7625
  const canvas = (0, exports.createCanvas)(100, 100);
7271
7626
  try {
7272
7627
  const context = canvas.getContext('2d');
@@ -7276,7 +7631,7 @@ function createCanvasFromData(data) {
7276
7631
  context.putImageData(imageData, 0, 0);
7277
7632
  }
7278
7633
  catch (e) {
7279
- console.error('JPEG decompression error', e.message);
7634
+ reader.log('JPEG decompression error', e.message);
7280
7635
  }
7281
7636
  return canvas;
7282
7637
  }
@@ -7343,6 +7698,18 @@ function writeUtf8String(writer, value) {
7343
7698
  const buffer = (0, utf8_1.encodeString)(value);
7344
7699
  (0, psdWriter_1.writeBytes)(writer, buffer);
7345
7700
  }
7701
+ let gbkDecoder;
7702
+ function getGbkDecoder() {
7703
+ if (gbkDecoder === undefined) {
7704
+ try {
7705
+ gbkDecoder = new TextDecoder('gbk');
7706
+ }
7707
+ catch (_a) {
7708
+ gbkDecoder = null;
7709
+ }
7710
+ }
7711
+ return gbkDecoder;
7712
+ }
7346
7713
  function readEncodedString(reader) {
7347
7714
  const length = (0, psdReader_1.readUint8)(reader);
7348
7715
  const buffer = (0, psdReader_1.readBytes)(reader, length);
@@ -7354,19 +7721,26 @@ function readEncodedString(reader) {
7354
7721
  }
7355
7722
  }
7356
7723
  if (notAscii) {
7357
- try {
7358
- const decoder = new TextDecoder('gbk');
7359
- return decoder.decode(buffer);
7724
+ const decoder = getGbkDecoder();
7725
+ if (decoder) {
7726
+ try {
7727
+ return decoder.decode(buffer);
7728
+ }
7729
+ catch (_a) { }
7360
7730
  }
7361
- catch (_a) { }
7362
7731
  }
7363
7732
  return (0, utf8_1.decodeString)(buffer);
7364
7733
  }
7365
7734
  function writeEncodedString(writer, value) {
7366
7735
  let ascii = '';
7367
- for (let i = 0, code = value.codePointAt(i++); code !== undefined; code = value.codePointAt(i++)) {
7368
- ascii += code > 0x7f ? '?' : String.fromCodePoint(code);
7736
+ for (let i = 0; i < value.length;) {
7737
+ const code = value.codePointAt(i);
7738
+ ascii += code > 0x7f ? '?' : String.fromCharCode(code);
7739
+ i += code > 0xffff ? 2 : 1; // skip low surrogate of astral characters
7369
7740
  }
7741
+ // length is stored in a single byte
7742
+ if (ascii.length > 255)
7743
+ ascii = ascii.substring(0, 255);
7370
7744
  const buffer = (0, utf8_1.encodeString)(ascii);
7371
7745
  (0, psdWriter_1.writeUint8)(writer, buffer.byteLength);
7372
7746
  (0, psdWriter_1.writeBytes)(writer, buffer);
@@ -7707,7 +8081,7 @@ addHandler(1032, target => target.gridAndGuidesInformation !== undefined, (reade
7707
8081
  };
7708
8082
  for (let i = 0; i < count; i++) {
7709
8083
  target.gridAndGuidesInformation.guides.push({
7710
- location: (0, psdReader_1.readUint32)(reader) / 32,
8084
+ location: (0, psdReader_1.readInt32)(reader) / 32,
7711
8085
  direction: (0, psdReader_1.readUint8)(reader) ? 'horizontal' : 'vertical'
7712
8086
  });
7713
8087
  }
@@ -7716,11 +8090,11 @@ addHandler(1032, target => target.gridAndGuidesInformation !== undefined, (reade
7716
8090
  const grid = info.grid || { horizontal: 18 * 32, vertical: 18 * 32 };
7717
8091
  const guides = info.guides || [];
7718
8092
  (0, psdWriter_1.writeUint32)(writer, 1);
7719
- (0, psdWriter_1.writeUint32)(writer, grid.horizontal);
7720
- (0, psdWriter_1.writeUint32)(writer, grid.vertical);
8093
+ (0, psdWriter_1.writeUint32)(writer, Math.round(grid.horizontal));
8094
+ (0, psdWriter_1.writeUint32)(writer, Math.round(grid.vertical));
7721
8095
  (0, psdWriter_1.writeUint32)(writer, guides.length);
7722
8096
  for (const g of guides) {
7723
- (0, psdWriter_1.writeUint32)(writer, g.location * 32);
8097
+ (0, psdWriter_1.writeInt32)(writer, Math.round(g.location * 32));
7724
8098
  (0, psdWriter_1.writeUint8)(writer, g.direction === 'horizontal' ? 1 : 0);
7725
8099
  }
7726
8100
  });
@@ -7736,10 +8110,10 @@ target => target.layerComps !== undefined, (reader, target) => {
7736
8110
  name: item['Nm '],
7737
8111
  capturedInfo: item.capturedInfo,
7738
8112
  });
7739
- if ('comment' in item)
8113
+ if (item.comment !== undefined)
7740
8114
  target.layerComps.list[target.layerComps.list.length - 1].comment = item.comment;
7741
8115
  }
7742
- if ('lastAppliedComp' in desc)
8116
+ if (desc.lastAppliedComp !== undefined)
7743
8117
  target.layerComps.lastApplied = desc.lastAppliedComp;
7744
8118
  }, (writer, target) => {
7745
8119
  const layerComps = target.layerComps;
@@ -7748,13 +8122,13 @@ target => target.layerComps !== undefined, (reader, target) => {
7748
8122
  const t = {};
7749
8123
  t._classID = 'Comp';
7750
8124
  t['Nm '] = item.name;
7751
- if ('comment' in item)
8125
+ if (item.comment !== undefined)
7752
8126
  t.comment = item.comment;
7753
8127
  t.compID = item.id;
7754
8128
  t.capturedInfo = item.capturedInfo;
7755
8129
  desc.list.push(t);
7756
8130
  }
7757
- if ('lastApplied' in layerComps)
8131
+ if (layerComps.lastApplied !== undefined)
7758
8132
  desc.lastAppliedComp = layerComps.lastApplied;
7759
8133
  // console.log('CompList', require('util').inspect(desc, false, 99, true));
7760
8134
  (0, descriptor_1.writeVersionAndDescriptor)(writer, '', 'CompList', desc);
@@ -7796,8 +8170,8 @@ target => target.onionSkins !== undefined, (reader, target) => {
7796
8170
  numBefore: onionSkins.framesBefore,
7797
8171
  numAfter: onionSkins.framesAfter,
7798
8172
  Spcn: onionSkins.frameSpacing,
7799
- minOpacity: (onionSkins.minOpacity * 100) | 0,
7800
- maxOpacity: (onionSkins.maxOpacity * 100) | 0,
8173
+ minOpacity: Math.round(onionSkins.minOpacity * 100),
8174
+ maxOpacity: Math.round(onionSkins.maxOpacity * 100),
7801
8175
  BlnM: Math.max(0, onionSkinsBlendModes.indexOf(onionSkins.blendMode)),
7802
8176
  };
7803
8177
  (0, descriptor_1.writeVersionAndDescriptor)(writer, '', 'null', desc);
@@ -7858,7 +8232,7 @@ target => target.timelineInformation !== undefined, (reader, target) => {
7858
8232
  LCnt: timeline.repeats,
7859
8233
  globalTrackList: (0, descriptor_1.serializeTrackList)(timeline.globalTracks),
7860
8234
  audioClipGroupList: {
7861
- audioClipGroupList: (_a = timeline.audioClipGroups) === null || _a === void 0 ? void 0 : _a.map(a => ({
8235
+ audioClipGroupList: ((_a = timeline.audioClipGroups) !== null && _a !== void 0 ? _a : []).map(a => ({
7862
8236
  groupID: a.id,
7863
8237
  muted: a.muted,
7864
8238
  audioClipList: a.audioClips.map(c => ({
@@ -8001,8 +8375,13 @@ target => target.slices ? target.slices.length : 0, (reader, target, leftBytes)
8001
8375
  }
8002
8376
  if (leftBytes()) {
8003
8377
  const desc = (0, descriptor_1.readVersionAndDescriptor)(reader);
8378
+ const slicesById = new Map();
8379
+ for (const s of slices) {
8380
+ if (!slicesById.has(s.id))
8381
+ slicesById.set(s.id, s);
8382
+ }
8004
8383
  desc.slices.forEach(d => {
8005
- const slice = slices.find(s => d.sliceID == s.id);
8384
+ const slice = slicesById.get(d.sliceID);
8006
8385
  if (slice) {
8007
8386
  slice.topOutset = d.topOutset;
8008
8387
  slice.leftOutset = d.leftOutset;
@@ -8129,7 +8508,7 @@ addHandler(1036, target => target.thumbnail !== undefined || target.thumbnailRaw
8129
8508
  target.thumbnailRaw = { width, height, data };
8130
8509
  }
8131
8510
  else if (data.byteLength) {
8132
- target.thumbnail = (0, helpers_1.createCanvasFromData)(data);
8511
+ target.thumbnail = (0, helpers_1.createCanvasFromData)(data, reader);
8133
8512
  }
8134
8513
  }, (writer, target) => {
8135
8514
  var _a;
@@ -8208,7 +8587,7 @@ addHandler(1088, target => target.pathSelectionState !== undefined, (reader, tar
8208
8587
  const desc = (0, descriptor_1.readVersionAndDescriptor)(reader);
8209
8588
  target.pathSelectionState = desc['null'];
8210
8589
  }, (writer, target) => {
8211
- const desc = { 'null': target.pathSelectionState };
8590
+ const desc = { 'null': (0, descriptor_1.pathSelectionStateToReference)(target.pathSelectionState) };
8212
8591
  (0, descriptor_1.writeVersionAndDescriptor)(writer, '', 'null', desc);
8213
8592
  });
8214
8593
  helpers_1.MOCK_HANDLERS && addHandler(1025, target => target._ir1025 !== undefined, (reader, target, left) => {
@@ -8290,7 +8669,7 @@ target => target.animations !== undefined, (reader, target, left) => {
8290
8669
  FrID: f.id,
8291
8670
  };
8292
8671
  if (f.delay)
8293
- frame.FrDl = (f.delay * 100) | 0;
8672
+ frame.FrDl = Math.round(f.delay * 100);
8294
8673
  frame.FrDs = FrmD.encode(f.dispose);
8295
8674
  // if (i === 0) frame.FrGA = 30; // ???
8296
8675
  desc.FrIn.push(frame);
@@ -8414,6 +8793,7 @@ function writePsdBuffer(psd, options) {
8414
8793
  if (typeof Buffer === 'undefined') {
8415
8794
  throw new Error('Buffer not supported on this platform');
8416
8795
  }
8796
+ // copy, so the buffer doesn't hold on to the unused part of the writer's buffer
8417
8797
  return Buffer.from(writePsdUint8Array(psd, options));
8418
8798
  }
8419
8799
 
@@ -8467,6 +8847,8 @@ const maxResolutionInMP = 100; // Don't decode more than 100 megapixels
8467
8847
  const maxMemoryUsageBytes = 64 * 1024 * 1024; // Don't decode if memory footprint is more than 64MB
8468
8848
  let totalBytesAllocated = 0; // avoid unexpected OOMs from untrusted content.
8469
8849
  function requestMemoryAllocation(increaseAmount) {
8850
+ if (!isFinite(increaseAmount) || increaseAmount < 0)
8851
+ throw new Error(`Invalid memory allocation size (${increaseAmount})`);
8470
8852
  const totalMemoryImpactBytes = totalBytesAllocated + increaseAmount;
8471
8853
  if (totalMemoryImpactBytes > maxMemoryUsageBytes) {
8472
8854
  const exceededAmount = Math.ceil((totalMemoryImpactBytes - maxMemoryUsageBytes) / 1024 / 1024);
@@ -8532,6 +8914,8 @@ function decodeScan(data, offset, frame, components, resetInterval, spectralStar
8532
8914
  return bitsData >>> 7;
8533
8915
  }
8534
8916
  function decodeHuffman(tree) {
8917
+ if (!tree)
8918
+ throw new Error('Missing Huffman table');
8535
8919
  let node = tree;
8536
8920
  while (true) {
8537
8921
  node = node[readBit()];
@@ -8714,6 +9098,8 @@ function decodeScan(data, offset, frame, components, resetInterval, spectralStar
8714
9098
  else {
8715
9099
  mcuExpected = mcusPerLine * frame.mcusPerColumn;
8716
9100
  }
9101
+ if (!isFinite(mcuExpected) || mcuExpected < 0)
9102
+ throw new Error('Invalid number of MCUs');
8717
9103
  if (!resetInterval)
8718
9104
  resetInterval = mcuExpected;
8719
9105
  let h;
@@ -8729,6 +9115,9 @@ function decodeScan(data, offset, frame, components, resetInterval, spectralStar
8729
9115
  for (let n = 0; n < resetInterval; n++) {
8730
9116
  decodeBlock(component, decodeFn, mcu);
8731
9117
  mcu++;
9118
+ // If we've reached our expected MCU's, stop decoding
9119
+ if (mcu === mcuExpected)
9120
+ break;
8732
9121
  }
8733
9122
  }
8734
9123
  else {
@@ -8789,6 +9178,8 @@ function buildComponentData(component) {
8789
9178
  // 988-991.
8790
9179
  function quantizeAndInverse(zz, dataOut, dataIn) {
8791
9180
  const qt = component.quantizationTable;
9181
+ if (!qt)
9182
+ throw new Error('Missing quantization table');
8792
9183
  const p = dataIn;
8793
9184
  // dequant
8794
9185
  for (let i = 0; i < 64; i++) {
@@ -9138,10 +9529,14 @@ function parse(data) {
9138
9529
  throw new Error(`maxResolutionInMP limit exceeded by ${exceededAmount}MP`);
9139
9530
  }
9140
9531
  const componentsCount = data[offset++];
9532
+ if (!componentsCount)
9533
+ throw new Error('SOF: no components in frame');
9141
9534
  for (let i = 0; i < componentsCount; i++) {
9142
9535
  const componentId = data[offset];
9143
9536
  const h = data[offset + 1] >> 4;
9144
9537
  const v = data[offset + 1] & 15;
9538
+ if (h < 1 || h > 4 || v < 1 || v > 4)
9539
+ throw new Error(`SOF: invalid sampling factors (${h}x${v})`);
9145
9540
  const qId = data[offset + 2];
9146
9541
  frame.componentsOrder.push(componentId);
9147
9542
  frame.components[componentId] = {
@@ -9189,11 +9584,16 @@ function parse(data) {
9189
9584
  readUint16(); // Ignore this data since it represents the image height
9190
9585
  break;
9191
9586
  case 0xFFDA: { // SOS (Start of Scan)
9587
+ if (!frame)
9588
+ throw new Error('SOS: scan before frame header (SOF)');
9192
9589
  readUint16(); // skip data length
9193
9590
  const selectorsCount = data[offset++];
9194
9591
  const components = [];
9195
9592
  for (let i = 0; i < selectorsCount; i++) {
9196
- const component = frame.components[data[offset++]];
9593
+ const componentId = data[offset++];
9594
+ const component = frame.components[componentId];
9595
+ if (!component)
9596
+ throw new Error(`SOS: component ${componentId} not found in frame`);
9197
9597
  const tableSpec = data[offset++];
9198
9598
  component.huffmanTableDC = huffmanTablesDC[tableSpec >> 4];
9199
9599
  component.huffmanTableAC = huffmanTablesAC[tableSpec & 15];
@@ -9242,7 +9642,10 @@ function parse(data) {
9242
9642
  for (let i = 0; i < frames.length; i++) {
9243
9643
  const cp = frames[i].components;
9244
9644
  for (let j in cp) { // TODO: don't use `in`
9245
- cp[j].quantizationTable = quantizationTables[cp[j].quantizationIdx];
9645
+ const table = quantizationTables[cp[j].quantizationIdx];
9646
+ if (!table)
9647
+ throw new Error(`Missing quantization table ${cp[j].quantizationIdx}`);
9648
+ cp[j].quantizationTable = table;
9246
9649
  delete cp[j].quantizationIdx; // TODO: why ???
9247
9650
  }
9248
9651
  }
@@ -9298,11 +9701,8 @@ function getData(decoded) {
9298
9701
  break;
9299
9702
  }
9300
9703
  case 3: {
9301
- // The default transform for three components is true
9302
- colorTransform = true;
9303
- // The adobe transform marker overrides any previous setting
9304
- if (decoded.adobe && decoded.adobe.transformCode)
9305
- colorTransform = true;
9704
+ // The default transform for three components is true, the adobe transform marker overrides it
9705
+ colorTransform = decoded.adobe ? !!decoded.adobe.transformCode : true;
9306
9706
  const component1 = decoded.components[0];
9307
9707
  const component2 = decoded.components[1];
9308
9708
  const component3 = decoded.components[2];
@@ -9629,22 +10029,24 @@ function readFixedPoint32(reader) {
9629
10029
  function readFixedPointPath32(reader) {
9630
10030
  return readInt32(reader) / (1 << 24);
9631
10031
  }
10032
+ // NOTE: returned array may alias the reader's underlying buffer, never write into it
9632
10033
  function readBytes(reader, length) {
9633
- const start = reader.view.byteOffset + reader.offset;
10034
+ const start = reader.offset;
10035
+ const viewLength = reader.view.byteLength;
9634
10036
  reader.offset += length;
9635
- if ((start + length) > reader.view.buffer.byteLength) {
10037
+ if ((start + length) > viewLength) {
9636
10038
  // fix for broken PSD files that are missing part of file at the end
9637
10039
  warnOrThrow(reader, 'Reading bytes exceeding buffer length');
9638
10040
  if (length > (100 * 1024 * 1024))
9639
10041
  throw new Error('Reading past end of file'); // limit to 100MB
9640
10042
  const result = new Uint8Array(length);
9641
- const len = Math.min(length, reader.view.byteLength - start);
10043
+ const len = Math.min(length, viewLength - start);
9642
10044
  if (len > 0)
9643
- result.set(new Uint8Array(reader.view.buffer, start, len));
10045
+ result.set(new Uint8Array(reader.view.buffer, reader.view.byteOffset + start, len));
9644
10046
  return result;
9645
10047
  }
9646
10048
  else {
9647
- return new Uint8Array(reader.view.buffer, start, length);
10049
+ return new Uint8Array(reader.view.buffer, reader.view.byteOffset + start, length);
9648
10050
  }
9649
10051
  }
9650
10052
  function readSignature(reader) {
@@ -9669,31 +10071,52 @@ function readUnicodeString(reader) {
9669
10071
  const length = readUint32(reader);
9670
10072
  return readUnicodeStringWithLength(reader, length);
9671
10073
  }
9672
- function readUnicodeStringWithLength(reader, length) {
10074
+ const STRING_CHUNK_SIZE = 0x2000;
10075
+ function checkReadBounds(reader, size) {
10076
+ if (reader.offset < 0 || (reader.offset + size) > reader.view.byteLength) {
10077
+ throw new RangeError('Offset is outside the bounds of the DataView');
10078
+ }
10079
+ }
10080
+ function readUtf16String(reader, length, littleEndian) {
10081
+ if (!(length > 0))
10082
+ return '';
10083
+ checkReadBounds(reader, length * 2);
10084
+ const view = reader.view;
10085
+ const codes = new Uint16Array(Math.min(length, STRING_CHUNK_SIZE));
10086
+ let offset = reader.offset;
9673
10087
  let text = '';
9674
- while (length--) {
9675
- const value = readUint16(reader);
9676
- if (value || length > 0) { // remove trailing \0
9677
- text += String.fromCharCode(value);
10088
+ for (let i = 0; i < length; i += STRING_CHUNK_SIZE) {
10089
+ const count = Math.min(STRING_CHUNK_SIZE, length - i);
10090
+ for (let j = 0; j < count; j++, offset += 2) {
10091
+ codes[j] = view.getUint16(offset, littleEndian);
9678
10092
  }
10093
+ text += String.fromCharCode.apply(null, (count === codes.length ? codes : codes.subarray(0, count)));
9679
10094
  }
10095
+ reader.offset = offset;
10096
+ // remove trailing \0
10097
+ if (text.charCodeAt(text.length - 1) === 0)
10098
+ text = text.substring(0, text.length - 1);
9680
10099
  return text;
9681
10100
  }
9682
- function readUnicodeStringWithLengthLE(reader, length) {
10101
+ function bytesToString(bytes) {
9683
10102
  let text = '';
9684
- while (length--) {
9685
- const value = readUint16LE(reader);
9686
- if (value || length > 0) { // remove trailing \0
9687
- text += String.fromCharCode(value);
9688
- }
10103
+ for (let i = 0; i < bytes.length; i += STRING_CHUNK_SIZE) {
10104
+ text += String.fromCharCode.apply(null, bytes.subarray(i, i + STRING_CHUNK_SIZE));
9689
10105
  }
9690
10106
  return text;
9691
10107
  }
10108
+ function readUnicodeStringWithLength(reader, length) {
10109
+ return readUtf16String(reader, length, false);
10110
+ }
10111
+ function readUnicodeStringWithLengthLE(reader, length) {
10112
+ return readUtf16String(reader, length, true);
10113
+ }
9692
10114
  function readAsciiString(reader, length) {
9693
- let text = '';
9694
- while (length--) {
9695
- text += String.fromCharCode(readUint8(reader));
9696
- }
10115
+ if (!(length > 0))
10116
+ return '';
10117
+ checkReadBounds(reader, length);
10118
+ const text = bytesToString(new Uint8Array(reader.view.buffer, reader.view.byteOffset + reader.offset, length));
10119
+ reader.offset += length;
9697
10120
  return text;
9698
10121
  }
9699
10122
  function skipBytes(reader, count) {
@@ -9707,12 +10130,7 @@ function checkSignature(reader, a, b) {
9707
10130
  }
9708
10131
  }
9709
10132
  function readShortString(reader, length) {
9710
- const buffer = readBytes(reader, length);
9711
- let result = '';
9712
- for (let i = 0; i < buffer.length; i++) {
9713
- result += String.fromCharCode(buffer[i]);
9714
- }
9715
- return result;
10133
+ return bytesToString(readBytes(reader, length));
9716
10134
  }
9717
10135
  function isValidSignature(sig) {
9718
10136
  return sig === '8BIM' || sig === 'MeSa' || sig === 'AgHg' || sig === 'PHUT' || sig === 'DCSR';
@@ -9733,7 +10151,7 @@ function readPsd(reader, readOptions = {}) {
9733
10151
  const maxSize = version === 1 ? 30000 : 300000;
9734
10152
  if (width > maxSize || height > maxSize)
9735
10153
  throw new Error(`Invalid size: ${width}x${height}`);
9736
- if (channels > 16)
10154
+ if (channels < 1 || channels > 56)
9737
10155
  throw new Error(`Invalid channel count: ${channels}`);
9738
10156
  if (![1, 8, 16, 32].includes(bitsPerChannel))
9739
10157
  throw new Error(`Invalid bitsPerChannel: ${bitsPerChannel}`);
@@ -9835,7 +10253,10 @@ function readPsd(reader, readOptions = {}) {
9835
10253
  const skipComposite = reader.skipCompositeImageData && (reader.skipLayerImageData || hasChildren);
9836
10254
  if (!skipComposite) {
9837
10255
  if (reader.useRawData) {
9838
- psd.rawCompositeData = new Uint8Array(reader.view.buffer, reader.view.byteOffset + reader.offset);
10256
+ const rawOffset = Math.min(reader.offset, reader.view.byteLength);
10257
+ const rawCompositeData = new Uint8Array(reader.view.buffer, reader.view.byteOffset + rawOffset, reader.view.byteLength - rawOffset);
10258
+ psd.rawCompositeData = rawCompositeData;
10259
+ psd.rawCompositeDataInfo = { large: reader.large, globalAlpha: reader.globalAlpha };
9839
10260
  }
9840
10261
  else {
9841
10262
  const imageData = readImageData(reader, psd);
@@ -9890,7 +10311,9 @@ function readLayerInfo(reader, psd, imageResources) {
9890
10311
  stack.push(l);
9891
10312
  }
9892
10313
  else if (type === 3 /* SectionDividerType.BoundingSectionDivider */) {
9893
- stack.pop();
10314
+ // ignore unmatched section dividers, never pop the root
10315
+ if (stack.length > 1)
10316
+ stack.pop();
9894
10317
  // this was workaround because I didn't know what `lsdk` section was, now it's probably not needed anymore
9895
10318
  // } else if (l.name === '</Layer group>' && !l.sectionDivider && !l.top && !l.left && !l.bottom && !l.right) {
9896
10319
  // // sometimes layer group terminator doesn't have sectionDivider, so we just guess here (PS bug ?)
@@ -9976,7 +10399,7 @@ function readLayerMaskData(reader, layer) {
9976
10399
  mask.positionRelativeToLayer = (flags & 1 /* LayerMaskFlags.PositionRelativeToLayer */) !== 0;
9977
10400
  mask.disabled = (flags & 2 /* LayerMaskFlags.LayerMaskDisabled */) !== 0;
9978
10401
  mask.fromVectorData = (flags & 8 /* LayerMaskFlags.LayerMaskFromRenderingOtherData */) !== 0;
9979
- if (left() >= 18) {
10402
+ if (hasRealMaskData(reader, flags, left())) {
9980
10403
  const realMask = {};
9981
10404
  layer.realMask = realMask;
9982
10405
  const realFlags = readUint8(reader);
@@ -10005,6 +10428,43 @@ function readLayerMaskData(reader, layer) {
10005
10428
  skipBytes(reader, left());
10006
10429
  });
10007
10430
  }
10431
+ function maskParamsSize(params) {
10432
+ return 1 +
10433
+ ((params & 1 /* MaskParams.UserMaskDensity */) ? 1 : 0) +
10434
+ ((params & 2 /* MaskParams.UserMaskFeather */) ? 8 : 0) +
10435
+ ((params & 4 /* MaskParams.VectorMaskDensity */) ? 1 : 0) +
10436
+ ((params & 8 /* MaskParams.VectorMaskFeather */) ? 8 : 0);
10437
+ }
10438
+ // checks if data at offset is mask parameters followed by up to 3 bytes of zero padding, ending exactly at offset + left
10439
+ function isMaskParamsLayout(view, offset, left) {
10440
+ if (left < 1)
10441
+ return false;
10442
+ const params = view.getUint8(offset);
10443
+ if (params === 0 || (params & ~0x0f))
10444
+ return false; // only 4 parameter flags exist
10445
+ const size = maskParamsSize(params);
10446
+ if (left < size || left - size > 3)
10447
+ return false;
10448
+ for (let i = size; i < left; i++) {
10449
+ if (view.getUint8(offset + i) !== 0)
10450
+ return false;
10451
+ }
10452
+ return true;
10453
+ }
10454
+ // real mask data (18 bytes) is followed by mask parameters (if flag is set) and padding
10455
+ function hasRealMaskData(reader, flags, left) {
10456
+ if (left < 18)
10457
+ return false;
10458
+ if (!(flags & 16 /* LayerMaskFlags.MaskHasParametersAppliedToIt */))
10459
+ return true;
10460
+ const { view, offset } = reader;
10461
+ if (isMaskParamsLayout(view, offset, left))
10462
+ return false;
10463
+ if (isMaskParamsLayout(view, offset + 18, left - 18))
10464
+ return true;
10465
+ // unknown layout, assume real mask if there's enough data for it (and mask parameters)
10466
+ return left === 18 || left - 18 >= maskParamsSize(view.getUint8(offset + 18));
10467
+ }
10008
10468
  function readBlendingRange(reader) {
10009
10469
  return [readUint8(reader), readUint8(reader), readUint8(reader), readUint8(reader)];
10010
10470
  }
@@ -10071,8 +10531,12 @@ function getCompositeImageData(psd) {
10071
10531
  if (!data)
10072
10532
  return undefined;
10073
10533
  const reader = createReader(data.buffer, data.byteOffset, data.byteLength);
10074
- const imageData = readImageData(reader, psd);
10075
- return imageData;
10534
+ const info = psd.rawCompositeDataInfo;
10535
+ if (info) {
10536
+ reader.large = info.large;
10537
+ reader.globalAlpha = info.globalAlpha;
10538
+ }
10539
+ return readImageData(reader, psd);
10076
10540
  }
10077
10541
  function getCompositeCanvas(psd) {
10078
10542
  return imageDataToCanvasSafe(getCompositeImageData(psd));
@@ -10114,18 +10578,18 @@ function decodeLayerPixels(layer, useImageData) {
10114
10578
  function decodeLayerImageData(layer, options) {
10115
10579
  var _a, _b, _c;
10116
10580
  let { throwForMissingFeatures, useImageData } = options;
10117
- const imageData = getDataFromLayer(layer, LayerDataType.Layer, throwForMissingFeatures, options.totalMemoryLimit);
10581
+ const imageData = getDataFromLayer(layer, LayerDataType.Layer, throwForMissingFeatures, options.totalMemoryLimit, options);
10118
10582
  setImageDataOrCanvas(layer, imageData, useImageData);
10119
10583
  if (options.totalMemoryLimit !== undefined)
10120
10584
  options.totalMemoryLimit -= (_a = imageData === null || imageData === void 0 ? void 0 : imageData.data.byteLength) !== null && _a !== void 0 ? _a : 0;
10121
10585
  if (layer.mask) {
10122
- const maskData = getDataFromLayer(layer, LayerDataType.Mask, throwForMissingFeatures, options.totalMemoryLimit);
10586
+ const maskData = getDataFromLayer(layer, LayerDataType.Mask, throwForMissingFeatures, options.totalMemoryLimit, options);
10123
10587
  setImageDataOrCanvas(layer.mask, maskData, useImageData);
10124
10588
  if (options.totalMemoryLimit !== undefined)
10125
10589
  options.totalMemoryLimit -= (_b = maskData === null || maskData === void 0 ? void 0 : maskData.data.byteLength) !== null && _b !== void 0 ? _b : 0;
10126
10590
  }
10127
10591
  if (layer.realMask) {
10128
- const maskData = getDataFromLayer(layer, LayerDataType.RealMask, throwForMissingFeatures, options.totalMemoryLimit);
10592
+ const maskData = getDataFromLayer(layer, LayerDataType.RealMask, throwForMissingFeatures, options.totalMemoryLimit, options);
10129
10593
  setImageDataOrCanvas(layer.realMask, maskData, useImageData);
10130
10594
  if (options.totalMemoryLimit !== undefined)
10131
10595
  options.totalMemoryLimit -= (_c = maskData === null || maskData === void 0 ? void 0 : maskData.data.byteLength) !== null && _c !== void 0 ? _c : 0;
@@ -10138,7 +10602,8 @@ var LayerDataType;
10138
10602
  LayerDataType[LayerDataType["Mask"] = 1] = "Mask";
10139
10603
  LayerDataType[LayerDataType["RealMask"] = 2] = "RealMask";
10140
10604
  })(LayerDataType || (LayerDataType = {}));
10141
- function getDataFromLayer(layer, read, throwForMissingFeatures, memoryLimit) {
10605
+ function getDataFromLayer(layer, read, throwForMissingFeatures, memoryLimit, logOptions) {
10606
+ var _a;
10142
10607
  if (!layer.rawData)
10143
10608
  return undefined;
10144
10609
  const { colorMode, bitsPerChannel, channels, large } = layer.rawData;
@@ -10151,7 +10616,7 @@ function getDataFromLayer(layer, read, throwForMissingFeatures, memoryLimit) {
10151
10616
  if (layerWidth && layerHeight && read === LayerDataType.Layer) {
10152
10617
  if (cmyk) {
10153
10618
  if (bitsPerChannel !== 8)
10154
- throw new Error('bitsPerChannel Not supproted');
10619
+ throw new Error('bitsPerChannel not supported');
10155
10620
  imageData = { width: layerWidth, height: layerHeight, data: new Uint8ClampedArray(layerWidth * layerHeight * 5) };
10156
10621
  }
10157
10622
  else {
@@ -10166,6 +10631,11 @@ function getDataFromLayer(layer, read, throwForMissingFeatures, memoryLimit) {
10166
10631
  if (!data)
10167
10632
  continue;
10168
10633
  const dataReader = createReader(data.buffer, data.byteOffset, data.byteLength);
10634
+ dataReader.debug = !!(logOptions === null || logOptions === void 0 ? void 0 : logOptions.debug);
10635
+ if (logOptions === null || logOptions === void 0 ? void 0 : logOptions.log)
10636
+ dataReader.log = logOptions.log;
10637
+ if (memoryLimit !== undefined)
10638
+ dataReader.totalMemoryLimit = Math.max(0, memoryLimit - ((_a = imageData === null || imageData === void 0 ? void 0 : imageData.data.byteLength) !== null && _a !== void 0 ? _a : 0));
10169
10639
  if (id === -2 /* ChannelID.UserMask */ || id === -3 /* ChannelID.RealUserMask */) {
10170
10640
  if (id === -2 /* ChannelID.UserMask */ && read !== LayerDataType.Mask)
10171
10641
  continue;
@@ -10178,6 +10648,8 @@ function getDataFromLayer(layer, read, throwForMissingFeatures, memoryLimit) {
10178
10648
  const maskHeight = Math.max(0, (mask.bottom || 0) - (mask.top || 0));
10179
10649
  if (maskWidth && maskHeight) {
10180
10650
  maskData = createImageDataBitDepth(maskWidth, maskHeight, bitsPerChannel, 4, memoryLimit);
10651
+ if (memoryLimit !== undefined)
10652
+ dataReader.totalMemoryLimit = Math.max(0, dataReader.totalMemoryLimit - maskData.data.byteLength);
10181
10653
  readData(dataReader, data.byteLength, maskData, compression, maskWidth, maskHeight, bitsPerChannel, 0, large, 4);
10182
10654
  if (helpers_1.RAW_IMAGE_DATA) { // TODO: use layer.rawData instead
10183
10655
  if (id === -2 /* ChannelID.UserMask */) {
@@ -10209,15 +10681,14 @@ function getDataFromLayer(layer, read, throwForMissingFeatures, memoryLimit) {
10209
10681
  layer.imageDataRawCompression[id] = compression;
10210
10682
  layer.imageDataRaw[id] = data;
10211
10683
  }
10212
- if (targetData && colorMode === 1 /* ColorMode.Grayscale */) {
10213
- setupGrayscale(targetData);
10214
- }
10215
10684
  }
10216
10685
  if (id === -1 /* ChannelID.Transparency */) {
10217
10686
  initializedAlpha = true;
10218
10687
  }
10219
10688
  }
10220
10689
  if (imageData) {
10690
+ if (colorMode === 1 /* ColorMode.Grayscale */)
10691
+ setupGrayscale(imageData);
10221
10692
  if (!initializedAlpha)
10222
10693
  resetAlpha(imageData, cmyk);
10223
10694
  if (cmyk) {
@@ -10233,7 +10704,7 @@ function readData(reader, length, pixels, compression, width, height, bitDepth,
10233
10704
  return;
10234
10705
  if (compression === 0 /* Compression.RawData */) {
10235
10706
  if (length !== (width * height * Math.floor(bitDepth / 8))) {
10236
- reader.log(`Invalid length (${length}, ${width * height * Math.floor(bitDepth / 8)})`);
10707
+ warnOrThrow(reader, `Invalid length (${length}, ${width * height * Math.floor(bitDepth / 8)})`);
10237
10708
  }
10238
10709
  const data = readBytes(reader, length);
10239
10710
  readDataRaw(data, pixels, bitDepth, step, offset);
@@ -10244,11 +10715,11 @@ function readData(reader, length, pixels, compression, width, height, bitDepth,
10244
10715
  }
10245
10716
  else if (compression === 2 /* Compression.ZipWithoutPrediction */) {
10246
10717
  const data = readBytes(reader, length);
10247
- readDataZip(data, pixels, width, height, bitDepth, step, offset, false);
10718
+ readDataZip(data, pixels, width, height, bitDepth, step, offset, false, reader);
10248
10719
  }
10249
10720
  else if (compression === 3 /* Compression.ZipWithPrediction */) {
10250
10721
  const data = readBytes(reader, length);
10251
- readDataZip(data, pixels, width, height, bitDepth, step, offset, true);
10722
+ readDataZip(data, pixels, width, height, bitDepth, step, offset, true, reader);
10252
10723
  }
10253
10724
  else {
10254
10725
  throw new Error(`Invalid Compression type: ${compression}`);
@@ -10263,7 +10734,7 @@ function readGlobalLayerMaskInfo(reader) {
10263
10734
  const colorSpace2 = readUint16(reader);
10264
10735
  const colorSpace3 = readUint16(reader);
10265
10736
  const colorSpace4 = readUint16(reader);
10266
- const opacity = readUint16(reader) / 0xff;
10737
+ const opacity = readUint16(reader) / 100; // 0 - 100
10267
10738
  const kind = readUint8(reader);
10268
10739
  skipBytes(reader, left()); // 3 bytes of padding ?
10269
10740
  return { overlayColorSpace, colorSpace1, colorSpace2, colorSpace3, colorSpace4, opacity, kind };
@@ -10343,11 +10814,12 @@ function readImageData(reader, psd) {
10343
10814
  var _a;
10344
10815
  const compression = readUint16(reader);
10345
10816
  const bitsPerChannel = (_a = psd.bitsPerChannel) !== null && _a !== void 0 ? _a : 8;
10817
+ const { width, height } = psd;
10346
10818
  if (exports.supportedColorModes.indexOf(psd.colorMode) === -1)
10347
10819
  throw new Error(`Color mode not supported: ${psd.colorMode}`);
10348
10820
  if (compression !== 0 /* Compression.RawData */ && compression !== 1 /* Compression.RleCompressed */)
10349
10821
  throw new Error(`Compression type not supported: ${compression}`);
10350
- const imageData = createImageDataBitDepth(psd.width, psd.height, bitsPerChannel, 4, reader.totalMemoryLimit);
10822
+ const imageData = createImageDataBitDepth(width, height, bitsPerChannel, 4, reader.totalMemoryLimit);
10351
10823
  if (reader.totalMemoryLimit !== undefined)
10352
10824
  reader.totalMemoryLimit -= imageData.data.byteLength;
10353
10825
  (0, helpers_1.resetImageData)(imageData);
@@ -10355,46 +10827,26 @@ function readImageData(reader, psd) {
10355
10827
  case 0 /* ColorMode.Bitmap */: {
10356
10828
  if (bitsPerChannel !== 1)
10357
10829
  throw new Error('Invalid bitsPerChannel for bitmap color mode');
10830
+ const rowBytes = Math.ceil(width / 8);
10358
10831
  let bytes;
10359
10832
  if (compression === 0 /* Compression.RawData */) {
10360
- bytes = readBytes(reader, Math.ceil(psd.width / 8) * psd.height);
10361
- }
10362
- else if (compression === 1 /* Compression.RleCompressed */) {
10363
- bytes = new Uint8Array(psd.width * psd.height);
10364
- readDataRLE(reader, { data: bytes, width: psd.width, height: psd.height }, psd.width, psd.height, 8, 1, [0], reader.large);
10833
+ bytes = readBytes(reader, rowBytes * height);
10365
10834
  }
10366
10835
  else {
10367
- throw new Error(`Compression not supported: ${compression}`);
10836
+ bytes = new Uint8Array(rowBytes * height);
10837
+ readDataRLE(reader, { data: bytes, width: rowBytes, height }, rowBytes, height, 8, 1, [0], reader.large);
10368
10838
  }
10369
- (0, helpers_1.decodeBitmap)(bytes, imageData.data, psd.width, psd.height);
10839
+ (0, helpers_1.decodeBitmap)(bytes, imageData.data, width, height);
10370
10840
  break;
10371
10841
  }
10372
10842
  case 3 /* ColorMode.RGB */:
10373
10843
  case 1 /* ColorMode.Grayscale */: {
10374
- const channels = psd.colorMode === 1 /* ColorMode.Grayscale */ ? [0] : [0, 1, 2];
10375
- if (psd.channels && psd.channels > 3) {
10376
- for (let i = 3; i < psd.channels; i++) {
10377
- // TODO: store these channels in additional image data
10378
- channels.push(i);
10379
- }
10380
- }
10381
- else if (reader.globalAlpha) {
10382
- channels.push(3);
10383
- }
10384
- if (compression === 0 /* Compression.RawData */) {
10385
- for (let i = 0; i < channels.length; i++) {
10386
- const data = readBytes(reader, psd.width * psd.height * Math.floor(bitsPerChannel / 8));
10387
- readDataRaw(data, imageData, bitsPerChannel, 4, channels[i]);
10388
- }
10389
- }
10390
- else if (compression === 1 /* Compression.RleCompressed */) {
10391
- const start = reader.offset;
10392
- readDataRLE(reader, imageData, psd.width, psd.height, bitsPerChannel, 4, channels, reader.large);
10393
- if (helpers_1.RAW_IMAGE_DATA)
10394
- psd.imageDataRaw = new Uint8Array(reader.view.buffer, reader.view.byteOffset + start, reader.offset - start);
10395
- }
10396
- else {
10397
- throw new Error(`Compression not supported: ${compression}`);
10844
+ const colorChannels = psd.colorMode === 1 /* ColorMode.Grayscale */ ? 1 : 3;
10845
+ const offsets = compositeChannelOffsets(reader, psd, colorChannels, 3);
10846
+ const start = reader.offset;
10847
+ readCompositeChannels(reader, compression, imageData, bitsPerChannel, 4, offsets);
10848
+ if (helpers_1.RAW_IMAGE_DATA && compression === 1 /* Compression.RleCompressed */) {
10849
+ psd.imageDataRaw = new Uint8Array(reader.view.buffer, reader.view.byteOffset + start, reader.offset - start);
10398
10850
  }
10399
10851
  if (psd.colorMode === 1 /* ColorMode.Grayscale */) {
10400
10852
  setupGrayscale(imageData);
@@ -10403,57 +10855,25 @@ function readImageData(reader, psd) {
10403
10855
  }
10404
10856
  case 2 /* ColorMode.Indexed */: {
10405
10857
  if (bitsPerChannel !== 8)
10406
- throw new Error('bitsPerChannel Not supproted');
10407
- if (psd.channels !== 1)
10408
- throw new Error('Invalid channel count');
10858
+ throw new Error('bitsPerChannel not supported');
10409
10859
  if (!psd.palette)
10410
10860
  throw new Error('Missing color palette');
10411
- if (compression === 0 /* Compression.RawData */) {
10412
- throw new Error(`Compression not supported: ${compression}`);
10413
- }
10414
- else if (compression === 1 /* Compression.RleCompressed */) {
10415
- const indexedImageData = {
10416
- width: imageData.width,
10417
- height: imageData.height,
10418
- data: new Uint8Array(imageData.width * imageData.height),
10419
- };
10420
- readDataRLE(reader, indexedImageData, psd.width, psd.height, bitsPerChannel, 1, [0], reader.large);
10421
- indexedToRgb(indexedImageData, imageData, psd.palette);
10422
- }
10423
- else {
10424
- throw new Error(`Compression not supported: ${compression}`);
10425
- }
10861
+ const offsets = compositeChannelOffsets(reader, psd, 1, -1);
10862
+ const indexedImageData = { width, height, data: new Uint8Array(width * height) };
10863
+ readCompositeChannels(reader, compression, indexedImageData, 8, 1, offsets);
10864
+ indexedToRgb(indexedImageData, imageData, psd.palette);
10426
10865
  break;
10427
10866
  }
10428
10867
  case 4 /* ColorMode.CMYK */: {
10429
10868
  if (bitsPerChannel !== 8)
10430
- throw new Error('bitsPerChannel Not supproted');
10431
- if (psd.channels !== 4)
10432
- throw new Error(`Invalid channel count`);
10433
- const channels = [0, 1, 2, 3];
10434
- if (reader.globalAlpha)
10435
- channels.push(4);
10436
- if (compression === 0 /* Compression.RawData */) {
10437
- throw new Error(`Compression not supported: ${compression}`);
10438
- // TODO: ...
10439
- // for (let i = 0; i < channels.length; i++) {
10440
- // readDataRaw(reader, imageData, channels[i], psd.width, psd.height);
10441
- // }
10442
- }
10443
- else if (compression === 1 /* Compression.RleCompressed */) {
10444
- const cmykImageData = {
10445
- width: imageData.width,
10446
- height: imageData.height,
10447
- data: new Uint8Array(imageData.width * imageData.height * 5),
10448
- };
10449
- const start = reader.offset;
10450
- readDataRLE(reader, cmykImageData, psd.width, psd.height, bitsPerChannel, 5, channels, reader.large);
10451
- cmykToRgb(cmykImageData, imageData, true);
10452
- if (helpers_1.RAW_IMAGE_DATA)
10453
- psd.imageDataRaw = new Uint8Array(reader.view.buffer, reader.view.byteOffset + start, reader.offset - start);
10454
- }
10455
- else {
10456
- throw new Error(`Compression not supported: ${compression}`);
10869
+ throw new Error('bitsPerChannel not supported');
10870
+ const offsets = compositeChannelOffsets(reader, psd, 4, 4);
10871
+ const cmykImageData = { width, height, data: new Uint8Array(width * height * 5) };
10872
+ const start = reader.offset;
10873
+ readCompositeChannels(reader, compression, cmykImageData, 8, 5, offsets);
10874
+ cmykToRgb(cmykImageData, imageData, true);
10875
+ if (helpers_1.RAW_IMAGE_DATA && compression === 1 /* Compression.RleCompressed */) {
10876
+ psd.imageDataRaw = new Uint8Array(reader.view.buffer, reader.view.byteOffset + start, reader.offset - start);
10457
10877
  }
10458
10878
  break;
10459
10879
  }
@@ -10461,23 +10881,98 @@ function readImageData(reader, psd) {
10461
10881
  }
10462
10882
  // remove weird white matte
10463
10883
  if (reader.globalAlpha) {
10464
- if (psd.bitsPerChannel !== 8)
10465
- throw new Error('bitsPerChannel Not supproted');
10466
- const p = imageData.data;
10467
- const size = imageData.width * imageData.height * 4;
10884
+ removeWhiteMatte(imageData);
10885
+ }
10886
+ return imageData;
10887
+ }
10888
+ // returns target offset for each of the composite image channels stored in the file, -1 for channels that are skipped
10889
+ function compositeChannelOffsets(reader, psd, colorChannels, alphaOffset) {
10890
+ var _a, _b, _c, _d;
10891
+ const channelCount = (_a = psd.channels) !== null && _a !== void 0 ? _a : (colorChannels + (reader.globalAlpha ? 1 : 0));
10892
+ if (channelCount < colorChannels)
10893
+ throw new Error(`Invalid channel count: ${channelCount}`);
10894
+ // First extra channel is transparency if document has global alpha (negative layer count). Some non-Photoshop
10895
+ // applications write transparency channel without setting global alpha, in that case extra channel is not
10896
+ // listed in alpha channel names (Photoshop names all user alpha channels / saved selections).
10897
+ const namedChannels = (_d = (_c = (_b = psd.imageResources) === null || _b === void 0 ? void 0 : _b.alphaChannelNames) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0;
10898
+ const hasTransparency = reader.globalAlpha || (channelCount - colorChannels) > namedChannels;
10899
+ const offsets = [];
10900
+ for (let i = 0; i < channelCount; i++) {
10901
+ if (i < colorChannels) {
10902
+ offsets.push(i);
10903
+ }
10904
+ else if (i === colorChannels && hasTransparency && alphaOffset >= 0) {
10905
+ offsets.push(alphaOffset);
10906
+ }
10907
+ else {
10908
+ // TODO: store these channels (alpha channels / saved selections, spot colors) in additional image data
10909
+ offsets.push(-1);
10910
+ }
10911
+ }
10912
+ return offsets;
10913
+ }
10914
+ function readCompositeChannels(reader, compression, pixelData, bitsPerChannel, step, offsets) {
10915
+ if (compression === 0 /* Compression.RawData */) {
10916
+ const channelSize = pixelData.width * pixelData.height * bytesPerValue(bitsPerChannel);
10917
+ for (const offset of offsets) {
10918
+ if (offset < 0) {
10919
+ skipBytes(reader, channelSize);
10920
+ }
10921
+ else {
10922
+ readDataRaw(readBytes(reader, channelSize), pixelData, bitsPerChannel, step, offset);
10923
+ }
10924
+ }
10925
+ }
10926
+ else if (compression === 1 /* Compression.RleCompressed */) {
10927
+ readDataRLE(reader, pixelData, pixelData.width, pixelData.height, bitsPerChannel, step, offsets, reader.large);
10928
+ }
10929
+ else {
10930
+ throw new Error(`Compression not supported: ${compression}`);
10931
+ }
10932
+ }
10933
+ // un-premultiplies colors from white matte, used for composite image of documents with global alpha
10934
+ function removeWhiteMatte({ data, width, height }) {
10935
+ const size = width * height * 4;
10936
+ if (data instanceof Float32Array) {
10468
10937
  for (let i = 0; i < size; i += 4) {
10469
- const pa = p[i + 3];
10938
+ const a = data[i + 3];
10939
+ if (a > 0 && a < 1) {
10940
+ const ra = 1 / a;
10941
+ const invA = 1 - ra;
10942
+ for (let j = 0; j < 3; j++) {
10943
+ const v = data[i + j] * ra + invA;
10944
+ data[i + j] = v < 0 ? 0 : v;
10945
+ }
10946
+ }
10947
+ }
10948
+ }
10949
+ else if (data instanceof Uint16Array) {
10950
+ for (let i = 0; i < size; i += 4) {
10951
+ const pa = data[i + 3];
10952
+ if (pa !== 0 && pa !== 0xffff) {
10953
+ const ra = 0xffff / pa;
10954
+ const invA = 0xffff * (1 - ra);
10955
+ for (let j = 0; j < 3; j++) {
10956
+ const v = Math.round(data[i + j] * ra + invA);
10957
+ data[i + j] = v < 0 ? 0 : (v > 0xffff ? 0xffff : v);
10958
+ }
10959
+ }
10960
+ }
10961
+ }
10962
+ else {
10963
+ // Uint8ClampedArray clamps and rounds values on assignment
10964
+ for (let i = 0; i < size; i += 4) {
10965
+ const pa = data[i + 3];
10470
10966
  if (pa != 0 && pa != 255) {
10471
10967
  const a = pa / 255;
10472
10968
  const ra = 1 / a;
10473
10969
  const invA = 255 * (1 - ra);
10474
- p[i + 0] = p[i + 0] * ra + invA;
10475
- p[i + 1] = p[i + 1] * ra + invA;
10476
- p[i + 2] = p[i + 2] * ra + invA;
10970
+ data[i + 0] = data[i + 0] * ra + invA;
10971
+ data[i + 1] = data[i + 1] * ra + invA;
10972
+ data[i + 2] = data[i + 2] * ra + invA;
10477
10973
  }
10478
10974
  }
10479
10975
  }
10480
- return imageData;
10481
10976
  }
10482
10977
  function cmykToRgb(cmyk, rgb, reverseAlpha) {
10483
10978
  const size = rgb.width * rgb.height * 4;
@@ -10516,175 +11011,267 @@ function indexedToRgb(indexed, rgb, palette) {
10516
11011
  dstData[dst + 3] = 255;
10517
11012
  }
10518
11013
  }
10519
- function verifyCompatible(a, b) {
10520
- if ((a.byteLength / a.length) !== (b.byteLength / b.length)) {
10521
- throw new Error('Invalid array types');
11014
+ // number of bytes used to store single channel value, 1-bit data is handled as bytes
11015
+ function bytesPerValue(bitDepth) {
11016
+ switch (bitDepth) {
11017
+ case 1:
11018
+ case 8: return 1;
11019
+ case 16: return 2;
11020
+ case 32: return 4;
11021
+ default: throw new Error(`Invalid bitDepth (${bitDepth})`);
10522
11022
  }
10523
11023
  }
10524
- function bytesToArray(bytes, bitDepth) {
10525
- if (bitDepth === 8) {
10526
- return bytes;
11024
+ function verifyArrayType(data, bytesPerValue) {
11025
+ if (data.BYTES_PER_ELEMENT !== bytesPerValue) {
11026
+ throw new Error('Invalid array types');
10527
11027
  }
10528
- else if (bitDepth === 16) {
10529
- // PSD files store 16-bit channel data in big-endian byte order.
10530
- // Swap each pair of bytes so that Uint16Array (native-endian) reads the correct values.
10531
- for (let i = 0; i < bytes.byteLength; i += 2) {
10532
- const tmp = bytes[i];
10533
- bytes[i] = bytes[i + 1];
10534
- bytes[i + 1] = tmp;
10535
- }
10536
- if (bytes.byteOffset % 2) {
10537
- const result = new Uint16Array(bytes.byteLength / 2);
10538
- new Uint8Array(result.buffer, result.byteOffset, result.byteLength).set(bytes);
10539
- return result;
10540
- }
10541
- else {
10542
- return new Uint16Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 2);
11028
+ }
11029
+ // NOTE: `buffer` may alias input file data, it must not be modified here
11030
+ function readDataRaw(buffer, pixelData, bitDepth, step, offset) {
11031
+ const bpv = bytesPerValue(bitDepth);
11032
+ if (!pixelData || offset < 0 || offset >= step)
11033
+ return;
11034
+ const data = pixelData.data;
11035
+ verifyArrayType(data, bpv);
11036
+ const size = Math.min(pixelData.width * pixelData.height, Math.floor(buffer.byteLength / bpv)) | 0;
11037
+ if (bpv === 1) {
11038
+ for (let i = 0, p = offset | 0; i < size; i++, p = (p + step) | 0) {
11039
+ data[p] = buffer[i];
10543
11040
  }
10544
11041
  }
10545
- else if (bitDepth === 32) {
10546
- if (bytes.byteOffset % 4) {
10547
- const result = new Float32Array(bytes.byteLength / 4);
10548
- new Uint8Array(result.buffer, result.byteOffset, result.byteLength).set(bytes);
10549
- return result;
10550
- }
10551
- else {
10552
- return new Float32Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 4);
11042
+ else if (bpv === 2) {
11043
+ for (let i = 0, j = 0, p = offset | 0; i < size; i++, j += 2, p = (p + step) | 0) {
11044
+ data[p] = (buffer[j] << 8) | buffer[j + 1];
10553
11045
  }
10554
11046
  }
10555
11047
  else {
10556
- throw new Error(`Invalid bitDepth (${bitDepth})`);
10557
- }
10558
- }
10559
- function copyChannelToPixelData(pixelData, channel, offset, step) {
10560
- verifyCompatible(pixelData.data, channel);
10561
- const size = pixelData.width * pixelData.height;
10562
- const data = pixelData.data;
10563
- for (let i = 0, p = offset | 0; i < size; i++, p = (p + step) | 0) {
10564
- data[p] = channel[i];
10565
- }
10566
- }
10567
- function readDataRaw(buffer, pixelData, bitDepth, step, offset) {
10568
- if (bitDepth == 32) {
10569
- for (let i = 0; i < buffer.byteLength; i += 4) {
10570
- const a = buffer[i + 0];
10571
- const b = buffer[i + 1];
10572
- const c = buffer[i + 2];
10573
- const d = buffer[i + 3];
10574
- buffer[i + 0] = d;
10575
- buffer[i + 1] = c;
10576
- buffer[i + 2] = b;
10577
- buffer[i + 3] = a;
11048
+ const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
11049
+ for (let i = 0, j = 0, p = offset | 0; i < size; i++, j += 4, p = (p + step) | 0) {
11050
+ data[p] = view.getFloat32(j, false);
10578
11051
  }
10579
11052
  }
10580
- const array = bytesToArray(buffer, bitDepth);
10581
- if (pixelData && offset < step) {
10582
- copyChannelToPixelData(pixelData, array, offset, step);
10583
- }
10584
11053
  }
10585
- function decodePredicted(data, width, height, mod) {
11054
+ function decodePredicted(data, width, height) {
10586
11055
  for (let y = 0; y < height; y++) {
10587
11056
  const offset = y * width;
10588
11057
  for (let x = 1, o = offset + 1; x < width; x++, o++) {
10589
- data[o] = (data[o - 1] + data[o]) % mod;
11058
+ data[o] = data[o - 1] + data[o]; // Uint8Array wraps values on assignment
11059
+ }
11060
+ }
11061
+ }
11062
+ const zipOverflow = new Error('Decompressed data exceeds expected size');
11063
+ // inflates data, stopping when output exceeds expected size (protects against zip bombs)
11064
+ function inflateLimited(compressed, expectedSize, reader) {
11065
+ const output = new Uint8Array(expectedSize);
11066
+ let position = 0;
11067
+ let overflow = false;
11068
+ const inflator = new pako_1.Inflate({ chunkSize: Math.max(1024, Math.min(expectedSize, 0x10000)) });
11069
+ inflator.onData = (chunk) => {
11070
+ const data = chunk;
11071
+ const count = Math.min(data.length, expectedSize - position);
11072
+ if (count > 0)
11073
+ output.set(count === data.length ? data : data.subarray(0, count), position);
11074
+ position += count;
11075
+ if (count < data.length) {
11076
+ overflow = true;
11077
+ throw zipOverflow;
10590
11078
  }
11079
+ };
11080
+ inflator.onEnd = (status) => {
11081
+ var _a;
11082
+ // default handler collects all the chunks into result, we only need the error status
11083
+ inflator.err = status;
11084
+ inflator.msg = ((_a = inflator.strm) === null || _a === void 0 ? void 0 : _a.msg) || '';
11085
+ };
11086
+ try {
11087
+ inflator.push(compressed, true);
11088
+ }
11089
+ catch (e) {
11090
+ if (e !== zipOverflow)
11091
+ throw e;
11092
+ }
11093
+ if (overflow) {
11094
+ if (reader)
11095
+ warnOrThrow(reader, zipOverflow.message);
11096
+ }
11097
+ else if (inflator.err) {
11098
+ throw new Error(`Invalid zip data: ${inflator.msg}`);
10591
11099
  }
11100
+ return output;
10592
11101
  }
10593
- function readDataZip(compressed, pixelData, width, height, bitDepth, step, offset, prediction) {
10594
- const decompressed = (0, pako_1.inflate)(compressed);
10595
- if (pixelData && offset < step) {
10596
- const array = bytesToArray(decompressed, bitDepth);
10597
- if (bitDepth === 8) {
10598
- if (prediction)
10599
- decodePredicted(decompressed, width, height, 0x100);
10600
- copyChannelToPixelData(pixelData, decompressed, offset, step);
10601
- }
10602
- else if (bitDepth === 16) {
10603
- if (prediction)
10604
- decodePredicted(array, width, height, 0x10000);
10605
- copyChannelToPixelData(pixelData, array, offset, step);
11102
+ function readDataZip(compressed, pixelData, width, height, bitDepth, step, offset, prediction, reader) {
11103
+ const bpv = bytesPerValue(bitDepth);
11104
+ if (!pixelData || offset < 0 || offset >= step)
11105
+ return;
11106
+ const data = pixelData.data;
11107
+ verifyArrayType(data, bpv);
11108
+ const expectedSize = width * height * bpv;
11109
+ if (reader)
11110
+ consumeMemory(reader, expectedSize);
11111
+ try {
11112
+ const decompressed = inflateLimited(compressed, expectedSize, reader);
11113
+ if (bpv === 1) {
11114
+ for (let y = 0, i = 0; y < height; y++) {
11115
+ let prev = 0;
11116
+ for (let x = 0, p = offset + y * width * step; x < width; x++, i++, p += step) {
11117
+ let v = decompressed[i];
11118
+ if (prediction)
11119
+ v = prev = (prev + v) & 0xff;
11120
+ data[p] = v;
11121
+ }
11122
+ }
10606
11123
  }
10607
- else if (bitDepth === 32) {
10608
- if (prediction)
10609
- decodePredicted(decompressed, width * 4, height, 0x100);
10610
- let di = offset;
10611
- const dst = new Uint32Array(pixelData.data.buffer, pixelData.data.byteOffset, pixelData.data.length);
10612
- for (let y = 0; y < height; y++) {
10613
- let a = width * 4 * y;
10614
- for (let x = 0; x < width; x++, a++, di += step) {
10615
- const b = a + width;
10616
- const c = b + width;
10617
- const d = c + width;
10618
- dst[di] = ((decompressed[a] << 24) | (decompressed[b] << 16) | (decompressed[c] << 8) | decompressed[d]) >>> 0;
11124
+ else if (bpv === 2) {
11125
+ for (let y = 0, i = 0; y < height; y++) {
11126
+ let prev = 0;
11127
+ for (let x = 0, p = offset + y * width * step; x < width; x++, i += 2, p += step) {
11128
+ let v = (decompressed[i] << 8) | decompressed[i + 1];
11129
+ if (prediction)
11130
+ v = prev = (prev + v) & 0xffff;
11131
+ data[p] = v;
10619
11132
  }
10620
11133
  }
10621
11134
  }
10622
11135
  else {
10623
- throw new Error('Invalid bitDepth');
11136
+ const dst = new Uint32Array(data.buffer, data.byteOffset, data.length);
11137
+ if (prediction) {
11138
+ // bytes are delta encoded per row and stored in planar order (all 1st bytes, all 2nd bytes, ...)
11139
+ decodePredicted(decompressed, width * 4, height);
11140
+ for (let y = 0, di = offset; y < height; y++) {
11141
+ let a = width * 4 * y;
11142
+ for (let x = 0; x < width; x++, a++, di += step) {
11143
+ const b = a + width;
11144
+ const c = b + width;
11145
+ const d = c + width;
11146
+ dst[di] = ((decompressed[a] << 24) | (decompressed[b] << 16) | (decompressed[c] << 8) | decompressed[d]) >>> 0;
11147
+ }
11148
+ }
11149
+ }
11150
+ else {
11151
+ const view = new DataView(decompressed.buffer, decompressed.byteOffset, decompressed.byteLength);
11152
+ for (let i = 0, j = 0, di = offset, size = width * height; i < size; i++, j += 4, di += step) {
11153
+ dst[di] = view.getUint32(j, false);
11154
+ }
11155
+ }
10624
11156
  }
10625
11157
  }
11158
+ finally {
11159
+ if (reader)
11160
+ recoverMemory(reader, expectedSize);
11161
+ }
10626
11162
  }
10627
- function readDataRLE(reader, pixelData, width, height, _bitDepth, step, offsets, large) {
10628
- const data = pixelData && pixelData.data;
10629
- let lengths;
10630
- if (large) {
10631
- consumeMemory(reader, offsets.length * height * 4);
10632
- lengths = new Uint32Array(offsets.length * height);
10633
- for (let o = 0, li = 0; o < offsets.length; o++) {
10634
- for (let y = 0; y < height; y++, li++) {
10635
- lengths[li] = readUint32(reader);
11163
+ // decodes single PackBits row, returns number of decoded values
11164
+ function decodeRLERow(src, i, end, dst, p, step, count) {
11165
+ let x = 0;
11166
+ while (i < end && x < count) {
11167
+ const header = src[i++];
11168
+ if (header > 128) {
11169
+ if (i >= end)
11170
+ break;
11171
+ const value = src[i++];
11172
+ let n = 257 - header;
11173
+ if (n > count - x)
11174
+ n = count - x;
11175
+ x += n;
11176
+ for (; n > 0; n--, p += step) {
11177
+ dst[p] = value;
11178
+ }
11179
+ }
11180
+ else if (header < 128) {
11181
+ let n = header + 1;
11182
+ if (n > count - x)
11183
+ n = count - x;
11184
+ if (n > end - i)
11185
+ n = end - i;
11186
+ x += n;
11187
+ for (; n > 0; n--, p += step) {
11188
+ dst[p] = src[i++];
10636
11189
  }
10637
11190
  }
11191
+ else {
11192
+ // ignore 128
11193
+ }
11194
+ }
11195
+ return x;
11196
+ }
11197
+ // converts big-endian 16/32-bit values from bytes and writes them to `data` starting at `p` with `step`
11198
+ function scatterValues(bytes, count, bpv, data, p, step) {
11199
+ if (bpv === 2) {
11200
+ for (let x = 0, i = 0; x < count; x++, i += 2, p += step) {
11201
+ data[p] = (bytes[i] << 8) | bytes[i + 1];
11202
+ }
10638
11203
  }
10639
11204
  else {
10640
- consumeMemory(reader, offsets.length * height * 2);
10641
- lengths = new Uint16Array(offsets.length * height);
10642
- for (let o = 0, li = 0; o < offsets.length; o++) {
10643
- for (let y = 0; y < height; y++, li++) {
10644
- lengths[li] = readUint16(reader);
10645
- }
11205
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
11206
+ for (let x = 0, i = 0; x < count; x++, i += 4, p += step) {
11207
+ data[p] = view.getFloat32(i, false);
10646
11208
  }
10647
11209
  }
10648
- // if (bitDepth !== 1 && bitDepth !== 8) throw new Error(`Invalid bit depth (${bitDepth})`);
10649
- const extraLimit = (step - 1) | 0; // 3 for rgb, 4 for cmyk
10650
- for (let c = 0, li = 0; c < offsets.length; c++) {
10651
- const offset = offsets[c] | 0;
10652
- const extra = c > extraLimit || offset > extraLimit;
10653
- if (!data || extra) {
10654
- for (let y = 0; y < height; y++, li++) {
10655
- skipBytes(reader, lengths[li]);
10656
- }
11210
+ }
11211
+ // decodes RLE rows of single channel, each row starts at the beginning of the image row
11212
+ function decodeRLEChannel(src, lengths, li, pos, width, height, data, offset, step, bpv, row) {
11213
+ const srcEnd = src.length;
11214
+ const rowSize = width * bpv;
11215
+ for (let y = 0; y < height; y++, li++) {
11216
+ const rowStart = pos < srcEnd ? pos : srcEnd;
11217
+ pos += lengths[li];
11218
+ const rowEnd = pos < srcEnd ? pos : srcEnd;
11219
+ if (rowStart >= rowEnd)
11220
+ continue;
11221
+ if (bpv === 1) {
11222
+ decodeRLERow(src, rowStart, rowEnd, data, offset + y * width * step, step, width);
10657
11223
  }
10658
11224
  else {
10659
- for (let y = 0, p = offset | 0; y < height; y++, li++) {
10660
- const length = lengths[li];
10661
- const buffer = readBytes(reader, length);
10662
- for (let i = 0, x = 0; i < length; i++) {
10663
- let header = buffer[i];
10664
- if (header > 128) {
10665
- const value = buffer[++i];
10666
- header = (256 - header) | 0;
10667
- for (let j = 0; j <= header && x < width; j = (j + 1) | 0, x = (x + 1) | 0) {
10668
- data[p] = value;
10669
- p = (p + step) | 0;
10670
- }
10671
- }
10672
- else if (header < 128) {
10673
- for (let j = 0; j <= header && x < width; j = (j + 1) | 0, x = (x + 1) | 0) {
10674
- data[p] = buffer[++i];
10675
- p = (p + step) | 0;
10676
- }
10677
- }
10678
- else {
10679
- // ignore 128
10680
- }
10681
- // This showed up on some images from non-photoshop programs, ignoring it seems to work just fine.
10682
- // if (i >= length) throw new Error(`Invalid RLE data: exceeded buffer size ${i}/${length}`);
10683
- }
11225
+ const decoded = decodeRLERow(src, rowStart, rowEnd, row, 0, 1, rowSize);
11226
+ scatterValues(row, Math.floor(decoded / bpv), bpv, data, offset + y * width * step, step);
11227
+ }
11228
+ }
11229
+ }
11230
+ function readDataRLE(reader, pixelData, width, height, bitDepth, step, offsets, large) {
11231
+ const data = pixelData && pixelData.data;
11232
+ const rows = offsets.length * height;
11233
+ const lengthSize = large ? 4 : 2;
11234
+ consumeMemory(reader, rows * lengthSize);
11235
+ try {
11236
+ if ((reader.offset + rows * lengthSize) > reader.view.byteLength) {
11237
+ throw new Error('RLE row lengths exceed buffer length');
11238
+ }
11239
+ const lengths = large ? new Uint32Array(rows) : new Uint16Array(rows);
11240
+ let total = 0;
11241
+ if (large) {
11242
+ for (let i = 0; i < rows; i++)
11243
+ total += (lengths[i] = readUint32(reader));
11244
+ }
11245
+ else {
11246
+ for (let i = 0; i < rows; i++)
11247
+ total += (lengths[i] = readUint16(reader));
11248
+ }
11249
+ const start = reader.offset;
11250
+ const srcEnd = reader.view.byteLength;
11251
+ if (total > (srcEnd - start)) {
11252
+ // broken or truncated file, decode only data that is available
11253
+ warnOrThrow(reader, 'RLE data exceeds buffer length');
11254
+ }
11255
+ const bpv = bytesPerValue(bitDepth);
11256
+ if (data)
11257
+ verifyArrayType(data, bpv);
11258
+ const src = new Uint8Array(reader.view.buffer, reader.view.byteOffset, srcEnd);
11259
+ const row = (data && bpv > 1) ? new Uint8Array(width * bpv) : undefined;
11260
+ let pos = start;
11261
+ for (let c = 0, li = 0; c < offsets.length; c++, li += height) {
11262
+ const offset = offsets[c] | 0;
11263
+ const channelStart = pos;
11264
+ for (let y = 0; y < height; y++)
11265
+ pos += lengths[li + y];
11266
+ if (data && offset >= 0 && offset < step) {
11267
+ decodeRLEChannel(src, lengths, li, channelStart, width, height, data, offset, step, bpv, row);
10684
11268
  }
10685
11269
  }
11270
+ reader.offset = pos;
11271
+ }
11272
+ finally {
11273
+ recoverMemory(reader, rows * lengthSize);
10686
11274
  }
10687
- recoverMemory(reader, lengths.byteLength);
10688
11275
  }
10689
11276
  function readSection(reader, round, func, skipEmpty = true, eightBytes = false) {
10690
11277
  let length = readUint32(reader);
@@ -10743,8 +11330,8 @@ function readColor(reader) {
10743
11330
  const l = readInt16(reader) / 10000;
10744
11331
  const ta = readInt16(reader);
10745
11332
  const tb = readInt16(reader);
10746
- const a = ta < 0 ? (ta / 12800) : (ta / 12700);
10747
- const b = tb < 0 ? (tb / 12800) : (tb / 12700);
11333
+ const a = (0, helpers_1.normalizeLabAB)(ta, 100);
11334
+ const b = (0, helpers_1.normalizeLabAB)(tb, 100);
10748
11335
  skipBytes(reader, 2);
10749
11336
  return { l, a, b };
10750
11337
  }
@@ -10944,6 +11531,8 @@ exports.writeUnicodeStringWithoutLength = writeUnicodeStringWithoutLength;
10944
11531
  exports.writeUnicodeStringWithoutLengthLE = writeUnicodeStringWithoutLengthLE;
10945
11532
  exports.writeUnicodeString = writeUnicodeString;
10946
11533
  exports.writeUnicodeStringWithPadding = writeUnicodeStringWithPadding;
11534
+ exports.getRLEChannelBound = getRLEChannelBound;
11535
+ exports.writeUint64 = writeUint64;
10947
11536
  exports.writeSection = writeSection;
10948
11537
  exports.writePsd = writePsd;
10949
11538
  exports.writeColor = writeColor;
@@ -11067,59 +11656,71 @@ function writeUnicodeStringWithPadding(writer, text) {
11067
11656
  }
11068
11657
  writeUint16(writer, 0);
11069
11658
  }
11070
- function getLargestLayerSize(layers = []) {
11659
+ // Worst-case size of writeDataRLE output for a single channel: per row the byte count (2 bytes, 4 for PSB)
11660
+ // plus PackBits data, which in the worst case is all literal runs of up to 128 bytes, each with 1 header byte.
11661
+ function getRLEChannelBound(width, height, large) {
11662
+ return height * ((large ? 4 : 2) + width + Math.ceil(width / 128));
11663
+ }
11664
+ function getLargestLayerSize(layers = [], large) {
11071
11665
  let max = 0;
11072
11666
  for (const layer of layers) {
11073
- const { width, height } = getLayerDimentions(layer);
11074
- max = Math.max(max, 2 * height + 2 * width * height);
11075
- if (layer.mask) {
11076
- const { width, height } = getLayerDimentions(layer.mask);
11077
- max = Math.max(max, 2 * height + 2 * width * height);
11078
- }
11079
- if (layer.realMask) {
11080
- const { width, height } = getLayerDimentions(layer.realMask);
11081
- max = Math.max(max, 2 * height + 2 * width * height);
11667
+ for (const target of [layer, layer.mask, layer.realMask]) {
11668
+ if (target) {
11669
+ const { width, height } = getLayerDimentions(target);
11670
+ max = Math.max(max, getRLEChannelBound(width, height, large));
11671
+ }
11082
11672
  }
11083
11673
  if (layer.children) {
11084
- max = Math.max(max, getLargestLayerSize(layer.children));
11674
+ max = Math.max(max, getLargestLayerSize(layer.children, large));
11085
11675
  }
11086
11676
  }
11087
11677
  return max;
11088
11678
  }
11679
+ function writeUint64(writer, value) {
11680
+ writeUint32(writer, Math.floor(value / 0x100000000));
11681
+ writeUint32(writer, value >>> 0);
11682
+ }
11089
11683
  function writeSection(writer, round, func, writeTotalLength = false, large = false) {
11684
+ const offset = writer.offset;
11090
11685
  if (large)
11091
11686
  writeUint32(writer, 0);
11092
- const offset = writer.offset;
11093
11687
  writeUint32(writer, 0);
11688
+ const start = writer.offset;
11094
11689
  func();
11095
- let length = writer.offset - offset - 4;
11690
+ let length = writer.offset - start;
11096
11691
  let len = length;
11097
11692
  while (len % round) {
11098
11693
  writeUint8(writer, 0);
11099
11694
  len++;
11100
11695
  }
11101
- // while (writer.offset % round) {
11102
- // writeUint8(writer, 0);
11103
- // len++;
11104
- // }
11105
11696
  if (writeTotalLength) {
11106
11697
  length = len;
11107
11698
  }
11108
- writer.view.setUint32(offset, length, false);
11699
+ if (large) {
11700
+ writer.view.setUint32(offset, Math.floor(length / 0x100000000), false);
11701
+ writer.view.setUint32(offset + 4, length >>> 0, false);
11702
+ }
11703
+ else {
11704
+ if (length > 0xffffffff)
11705
+ throw new Error('Section too large, use PSB format instead');
11706
+ writer.view.setUint32(offset, length, false);
11707
+ }
11708
+ }
11709
+ function verifyPixelData(data, name) {
11710
+ // check element size instead of instanceof, so arrays from other realms (iframes, workers, vm) are accepted
11711
+ const bytesPerElement = data && data.data.BYTES_PER_ELEMENT;
11712
+ if (bytesPerElement !== undefined && bytesPerElement !== 1) {
11713
+ throw new Error(`${name} has incorrect bitDepth (only 8-bit Uint8Array or Uint8ClampedArray data is supported for writing)`);
11714
+ }
11109
11715
  }
11110
11716
  function verifyBitCount(target) {
11111
- var _a;
11717
+ var _a, _b, _c;
11112
11718
  (_a = target.children) === null || _a === void 0 ? void 0 : _a.forEach(verifyBitCount);
11113
- const data = target.imageData;
11114
- if (data && (data.data instanceof Uint32Array || data.data instanceof Uint16Array)) {
11115
- throw new Error('imageData has incorrect bitDepth');
11116
- }
11117
- if ('mask' in target && target.mask) {
11118
- const data = target.mask.imageData;
11119
- if (data && (data.data instanceof Uint32Array || data.data instanceof Uint16Array)) {
11120
- throw new Error('mask imageData has incorrect bitDepth');
11121
- }
11122
- }
11719
+ verifyPixelData(target.imageData, 'imageData');
11720
+ if ('mask' in target)
11721
+ verifyPixelData((_b = target.mask) === null || _b === void 0 ? void 0 : _b.imageData, 'mask imageData');
11722
+ if ('realMask' in target)
11723
+ verifyPixelData((_c = target.realMask) === null || _c === void 0 ? void 0 : _c.imageData, 'realMask imageData');
11123
11724
  }
11124
11725
  function writePsd(writer, psd, options = {}) {
11125
11726
  var _a;
@@ -11130,6 +11731,10 @@ function writePsd(writer, psd, options = {}) {
11130
11731
  const bitsPerChannel = (_a = psd.bitsPerChannel) !== null && _a !== void 0 ? _a : 8;
11131
11732
  if (bitsPerChannel !== 8)
11132
11733
  throw new Error('bitsPerChannel other than 8 are not supported for writing');
11734
+ const layers = [];
11735
+ addChildren(layers, psd.children);
11736
+ if (!layers.length)
11737
+ layers.push({});
11133
11738
  verifyBitCount(psd);
11134
11739
  const imageResources = Object.assign({}, psd.imageResources);
11135
11740
  const opt = Object.assign(Object.assign({}, options), { layerIds: new Set(), layerToId: new Map() });
@@ -11143,8 +11748,8 @@ function writePsd(writer, psd, options = {}) {
11143
11748
  if (imageData && (psd.width !== imageData.width || psd.height !== imageData.height))
11144
11749
  throw new Error('Document canvas must have the same size as document');
11145
11750
  const globalAlpha = !!imageData && (0, helpers_1.hasAlpha)(imageData);
11146
- const maxBufferSize = Math.max(getLargestLayerSize(psd.children), 4 * 2 * psd.width * psd.height + 2 * psd.height);
11147
- writer.tempBuffer = new Uint8Array(maxBufferSize);
11751
+ // only used for encoding a single layer channel at a time, composite image is encoded directly into the writer buffer
11752
+ writer.tempBuffer = new Uint8Array(getLargestLayerSize(psd.children, !!options.psb));
11148
11753
  // header
11149
11754
  writeSignature(writer, '8BPS');
11150
11755
  writeUint16(writer, options.psb ? 2 : 1); // version
@@ -11167,10 +11772,6 @@ function writePsd(writer, psd, options = {}) {
11167
11772
  }
11168
11773
  // TODO: other data?
11169
11774
  });
11170
- const layers = [];
11171
- addChildren(layers, psd.children);
11172
- if (!layers.length)
11173
- layers.push({});
11174
11775
  // image resources
11175
11776
  imageResources.layersGroup = layers.map(l => l.linkGroup || 0);
11176
11777
  imageResources.layerGroupsEnabledId = layers.map(l => l.linkGroupEnabled == false ? 0 : 1);
@@ -11192,36 +11793,45 @@ function writePsd(writer, psd, options = {}) {
11192
11793
  writeGlobalLayerMaskInfo(writer, psd.globalLayerMaskInfo);
11193
11794
  writeAdditionalLayerInfo(writer, psd, psd, opt);
11194
11795
  }, undefined, !!opt.psb);
11796
+ writer.tempBuffer = undefined;
11195
11797
  // image data
11196
- const channels = globalAlpha ? [0, 1, 2, 3] : [0, 1, 2];
11197
- const width = imageData ? imageData.width : psd.width;
11198
- const height = imageData ? imageData.height : psd.height;
11199
- const data = { data: new Uint8Array(width * height * 4), width, height };
11200
11798
  writeUint16(writer, 1 /* Compression.RleCompressed */); // Photoshop doesn't support zip compression of composite image data
11201
11799
  if (helpers_1.RAW_IMAGE_DATA && psd.imageDataRaw) {
11202
11800
  console.log('writing raw image data');
11203
11801
  writeBytes(writer, psd.imageDataRaw);
11204
11802
  }
11205
11803
  else {
11206
- if (imageData)
11207
- data.data.set(new Uint8Array(imageData.data.buffer, imageData.data.byteOffset, imageData.data.byteLength));
11208
- // add weird white matte
11209
- if (globalAlpha) {
11210
- const size = data.width * data.height * 4;
11211
- const p = data.data;
11212
- for (let i = 0; i < size; i += 4) {
11213
- const pa = p[i + 3];
11214
- if (pa != 0 && pa != 255) {
11215
- const a = pa / 255;
11216
- const ra = 255 * (1 - a);
11217
- p[i + 0] = p[i + 0] * a + ra;
11218
- p[i + 1] = p[i + 1] * a + ra;
11219
- p[i + 2] = p[i + 2] * a + ra;
11220
- }
11804
+ const channels = globalAlpha ? [0, 1, 2, 3] : [0, 1, 2];
11805
+ const data = getCompositeData(imageData, psd.width, psd.height, globalAlpha);
11806
+ // encode directly into the writer buffer
11807
+ const maxSize = channels.length * getRLEChannelBound(data.width, data.height, !!options.psb);
11808
+ ensureSize(writer, writer.offset + maxSize);
11809
+ const buffer = new Uint8Array(writer.buffer, writer.offset, maxSize);
11810
+ writer.offset += (0, helpers_1.writeDataRLE)(buffer, data, channels, !!options.psb).length;
11811
+ }
11812
+ }
11813
+ function getCompositeData(imageData, width, height, globalAlpha) {
11814
+ if (!imageData)
11815
+ return { data: new Uint8Array(width * height * 4), width, height };
11816
+ const size = width * height * 4;
11817
+ const src = imageData.data;
11818
+ let dst = undefined;
11819
+ // add weird white matte (copy the data only if there are semi-transparent pixels)
11820
+ if (globalAlpha) {
11821
+ for (let i = 0; i < size; i += 4) {
11822
+ const pa = src[i + 3];
11823
+ if (pa != 0 && pa != 255) {
11824
+ if (!dst)
11825
+ dst = new Uint8Array(src.buffer, src.byteOffset, size).slice();
11826
+ const a = pa / 255;
11827
+ const ra = 255 * (1 - a);
11828
+ dst[i + 0] = src[i + 0] * a + ra;
11829
+ dst[i + 1] = src[i + 1] * a + ra;
11830
+ dst[i + 2] = src[i + 2] * a + ra;
11221
11831
  }
11222
11832
  }
11223
- writeBytes(writer, (0, helpers_1.writeDataRLE)(writer.tempBuffer, data, channels, !!options.psb));
11224
11833
  }
11834
+ return dst ? { data: dst, width, height } : imageData;
11225
11835
  }
11226
11836
  function writeLayerInfo(writer, layers, psd, globalAlpha, options) {
11227
11837
  writeSection(writer, 4, () => {
@@ -11238,9 +11848,12 @@ function writeLayerInfo(writer, layers, psd, globalAlpha, options) {
11238
11848
  writeUint16(writer, channels.length);
11239
11849
  for (const c of channels) {
11240
11850
  writeInt16(writer, c.id);
11241
- if (options.psb)
11242
- writeUint32(writer, 0);
11243
- writeUint32(writer, c.length);
11851
+ if (options.psb) {
11852
+ writeUint64(writer, c.length);
11853
+ }
11854
+ else {
11855
+ writeUint32(writer, c.length);
11856
+ }
11244
11857
  }
11245
11858
  writeSignature(writer, '8BIM');
11246
11859
  writeSignature(writer, helpers_1.fromBlendMode[layer.blendMode] || 'norm');
@@ -11277,7 +11890,7 @@ function writeLayerInfo(writer, layers, psd, globalAlpha, options) {
11277
11890
  }, true, options.psb);
11278
11891
  }
11279
11892
  function writeLayerMaskData(writer, { mask, realMask }, layerData) {
11280
- writeSection(writer, 1, () => {
11893
+ writeSection(writer, 4, () => {
11281
11894
  if (!mask && !realMask)
11282
11895
  return;
11283
11896
  let params = 0, flags = 0, realFlags = 0;
@@ -11332,8 +11945,7 @@ function writeLayerMaskData(writer, { mask, realMask }, layerData) {
11332
11945
  if (mask.vectorMaskFeather !== undefined)
11333
11946
  writeFloat64(writer, mask.vectorMaskFeather);
11334
11947
  }
11335
- writeZeros(writer, 2);
11336
- });
11948
+ }, true); // photoshop pads mask data to multiple of 4 bytes and includes padding in the length
11337
11949
  }
11338
11950
  function writerBlendingRange(writer, range) {
11339
11951
  writeUint8(writer, range[0]);
@@ -11362,7 +11974,7 @@ function writeGlobalLayerMaskInfo(writer, info) {
11362
11974
  writeUint16(writer, info.colorSpace2);
11363
11975
  writeUint16(writer, info.colorSpace3);
11364
11976
  writeUint16(writer, info.colorSpace4);
11365
- writeUint16(writer, Math.round(info.opacity * 0xff));
11977
+ writeUint16(writer, Math.round((0, helpers_1.clamp)(info.opacity, 0, 1) * 100)); // 0-100
11366
11978
  writeUint8(writer, info.kind);
11367
11979
  writeZeros(writer, 3);
11368
11980
  }
@@ -11430,10 +12042,8 @@ function addChildren(layers, children) {
11430
12042
  }
11431
12043
  }
11432
12044
  function resizeBuffer(writer, size) {
11433
- let newLength = writer.buffer.byteLength;
11434
- do {
11435
- newLength *= 2;
11436
- } while (size > newLength);
12045
+ // grow at least 2x to keep small writes fast, but don't overshoot large reservations (like composite image data)
12046
+ const newLength = Math.max(writer.buffer.byteLength * 2, size);
11437
12047
  const newBuffer = new ArrayBuffer(newLength);
11438
12048
  const newBytes = new Uint8Array(newBuffer);
11439
12049
  const oldBytes = new Uint8Array(writer.buffer);
@@ -11456,12 +12066,12 @@ function createThumbnail(psd) {
11456
12066
  let scale = 1;
11457
12067
  if (psd.width > psd.height) {
11458
12068
  canvas.width = 160;
11459
- canvas.height = Math.floor(psd.height * (canvas.width / psd.width));
12069
+ canvas.height = Math.max(1, Math.floor(psd.height * (canvas.width / psd.width)));
11460
12070
  scale = canvas.width / psd.width;
11461
12071
  }
11462
12072
  else {
11463
12073
  canvas.height = 160;
11464
- canvas.width = Math.floor(psd.width * (canvas.height / psd.height));
12074
+ canvas.width = Math.max(1, Math.floor(psd.width * (canvas.height / psd.height)));
11465
12075
  scale = canvas.height / psd.height;
11466
12076
  }
11467
12077
  const context = canvas.getContext('2d');
@@ -11504,7 +12114,7 @@ function getMaskChannels(tempBuffer, layerData, layer, mask, options, realMask)
11504
12114
  compression = 2 /* Compression.ZipWithoutPrediction */;
11505
12115
  }
11506
12116
  else {
11507
- buffer = (0, helpers_1.writeDataRLE)(tempBuffer, imageData, [0], !!options.psb);
12117
+ buffer = (0, helpers_1.writeDataRLE)(tempBuffer, imageData, [0], !!options.psb).slice(); // copy, temp buffer is reused
11508
12118
  compression = 1 /* Compression.RleCompressed */;
11509
12119
  }
11510
12120
  layerData.channels.push({ id: realMask ? -3 /* ChannelID.RealUserMask */ : -2 /* ChannelID.UserMask */, compression, data: buffer, length: 2 + buffer.length });
@@ -11612,7 +12222,7 @@ function getLayerChannels(tempBuffer, layer, background, options) {
11612
12222
  compression = 2 /* Compression.ZipWithoutPrediction */;
11613
12223
  }
11614
12224
  else {
11615
- data = (0, helpers_1.writeDataRLE)(tempBuffer, imageData, [offset], !!options.psb);
12225
+ data = (0, helpers_1.writeDataRLE)(tempBuffer, imageData, [offset], !!options.psb).slice(); // copy, temp buffer is reused
11616
12226
  compression = 1 /* Compression.RleCompressed */;
11617
12227
  }
11618
12228
  return { id, compression, data, length: 2 + data.length };
@@ -11676,8 +12286,8 @@ function writeColor(writer, color) {
11676
12286
  else if ('l' in color) {
11677
12287
  writeUint16(writer, 7 /* ColorSpace.Lab */);
11678
12288
  writeInt16(writer, Math.round(color.l * 10000));
11679
- writeInt16(writer, Math.round(color.a < 0 ? (color.a * 12800) : (color.a * 12700)));
11680
- writeInt16(writer, Math.round(color.b < 0 ? (color.b * 12800) : (color.b * 12700)));
12289
+ writeInt16(writer, Math.round((0, helpers_1.denormalizeLabAB)(color.a, 100)));
12290
+ writeInt16(writer, Math.round((0, helpers_1.denormalizeLabAB)(color.b, 100)));
11681
12291
  writeUint16(writer, 0);
11682
12292
  }
11683
12293
  else if ('h' in color) {
@@ -11727,6 +12337,9 @@ function writePattern(writer, pattern) {
11727
12337
  writeUint32(writer, bottom);
11728
12338
  writeUint32(writer, right);
11729
12339
  writeUint32(writer, 24); // channels count
12340
+ // ponytail: pattern channels always use small (2-byte) RLE scanline headers, matching
12341
+ // readPattern which reads them as non-large
12342
+ const buffer = new Uint8Array(getRLEChannelBound(width, height, false));
11730
12343
  // channels: RGB at indices 0,1,2 and alpha at index 25
11731
12344
  for (let i = 0; i < 24 + 2; i++) {
11732
12345
  const offset = i < 3 ? i : (i === 25 ? 3 : -1);
@@ -11734,10 +12347,6 @@ function writePattern(writer, pattern) {
11734
12347
  writeUint32(writer, 0); // has
11735
12348
  continue;
11736
12349
  }
11737
- // worst-case RLE size for a single channel: width + 1 per scanline, plus scanline length headers
11738
- const buffer = new Uint8Array(width * height + 2 * height + 2 * width + 16);
11739
- // ponytail: pattern channels always use small (2-byte) RLE scanline headers, matching
11740
- // readPattern which reads them as non-large; `large` here only affects the header width.
11741
12350
  const data = (0, helpers_1.writeDataRLE)(buffer, pixelData, [offset], false);
11742
12351
  writeUint32(writer, 1); // has
11743
12352
  writeUint32(writer, data.length + 4 + 16 + 2 + 1); // length
@@ -11765,6 +12374,7 @@ function writePattern(writer, pattern) {
11765
12374
  Object.defineProperty(exports, "__esModule", { value: true });
11766
12375
  exports.decodeEngineData = decodeEngineData;
11767
12376
  exports.encodeEngineData = encodeEngineData;
12377
+ const helpers_1 = require("./helpers");
11768
12378
  const defaultFont = {
11769
12379
  name: 'MyriadPro-Regular',
11770
12380
  script: 0,
@@ -11877,6 +12487,26 @@ function decodeColor(color) {
11877
12487
  default: throw new Error('Unknown color type in text layer');
11878
12488
  }
11879
12489
  }
12490
+ // h, s, b in 0..1 range, returns r, g, b in 0..1 range
12491
+ function hsbToRgb(h, s, v) {
12492
+ h = (0, helpers_1.clamp)(h || 0, 0, 1);
12493
+ s = (0, helpers_1.clamp)(s || 0, 0, 1);
12494
+ v = (0, helpers_1.clamp)(v || 0, 0, 1);
12495
+ const h6 = (h * 6) % 6;
12496
+ const i = Math.floor(h6);
12497
+ const f = h6 - i;
12498
+ const p = v * (1 - s);
12499
+ const q = v * (1 - s * f);
12500
+ const t = v * (1 - s * (1 - f));
12501
+ switch (i) {
12502
+ case 0: return [v, t, p];
12503
+ case 1: return [q, v, p];
12504
+ case 2: return [p, v, t];
12505
+ case 3: return [p, q, v];
12506
+ case 4: return [t, p, v];
12507
+ default: return [v, p, q];
12508
+ }
12509
+ }
11880
12510
  function encodeColor(color) {
11881
12511
  if (!color) {
11882
12512
  return { Type: 1, Values: [0, 0, 0, 0] };
@@ -11884,9 +12514,19 @@ function encodeColor(color) {
11884
12514
  else if ('r' in color) {
11885
12515
  return { Type: 1, Values: ['a' in color ? color.a / 255 : 1, color.r / 255, color.g / 255, color.b / 255] };
11886
12516
  }
12517
+ else if ('fr' in color) {
12518
+ return { Type: 1, Values: [1, (0, helpers_1.clamp)(color.fr || 0, 0, 1), (0, helpers_1.clamp)(color.fg || 0, 0, 1), (0, helpers_1.clamp)(color.fb || 0, 0, 1)] };
12519
+ }
12520
+ else if ('h' in color) {
12521
+ const [r, g, b] = hsbToRgb(color.h, color.s, color.b);
12522
+ return { Type: 1, Values: [1, r, g, b] };
12523
+ }
11887
12524
  else if ('c' in color) {
11888
12525
  return { Type: 2, Values: [1, color.c / 255, color.m / 255, color.y / 255, color.k / 255] };
11889
12526
  }
12527
+ else if ('l' in color) {
12528
+ throw new Error('LAB colors are not supported in text layers, use RGB, CMYK or grayscale color instead');
12529
+ }
11890
12530
  else if ('k' in color) {
11891
12531
  return { Type: 0, Values: [1, color.k / 255] };
11892
12532
  }
@@ -11924,16 +12564,19 @@ function findOrAddFont(fonts, font) {
11924
12564
  return fonts.length - 1;
11925
12565
  }
11926
12566
  function decodeObject(obj, keys, fonts) {
12567
+ var _a;
11927
12568
  const result = {};
11928
12569
  for (const key of keys) {
11929
12570
  const Key = upperFirst(key);
11930
12571
  if (obj[Key] === undefined)
11931
12572
  continue;
11932
12573
  if (key === 'justification') {
11933
- result[key] = justification[obj[Key]];
12574
+ result[key] = (_a = justification[obj[Key]]) !== null && _a !== void 0 ? _a : 'left';
11934
12575
  }
11935
12576
  else if (key === 'font') {
11936
- result[key] = fonts[obj[Key]];
12577
+ const font = fonts[obj[Key]];
12578
+ if (font)
12579
+ result[key] = font; // invalid font index, leave the font unset so it falls back to default
11937
12580
  }
11938
12581
  else if (key === 'fillColor' || key === 'strokeColor') {
11939
12582
  result[key] = decodeColor(obj[Key]);
@@ -11952,7 +12595,7 @@ function encodeObject(obj, keys, fonts) {
11952
12595
  if (obj[key] === undefined)
11953
12596
  continue;
11954
12597
  if (key === 'justification') {
11955
- result[Key] = justification.indexOf((_a = obj[key]) !== null && _a !== void 0 ? _a : 'left');
12598
+ result[Key] = Math.max(0, justification.indexOf((_a = obj[key]) !== null && _a !== void 0 ? _a : 'left')); // default to 'left' for unknown values
11956
12599
  }
11957
12600
  else if (key === 'font') {
11958
12601
  result[Key] = findOrAddFont(fonts, obj[key]);
@@ -12020,8 +12663,38 @@ function deduplicateValues(base, runs, keys) {
12020
12663
  runs.length = 0;
12021
12664
  }
12022
12665
  }
12666
+ function isDefaultGridColor(color) {
12667
+ // Photoshop writes default grid colors as blue with 0 alpha, ignore alpha when comparing
12668
+ return !!color && 'r' in color && color.r === 0 && color.g === 0 && color.b === 255;
12669
+ }
12670
+ function decodeGridColor(color, defaultColor) {
12671
+ try {
12672
+ return color ? decodeColor(color) : defaultColor;
12673
+ }
12674
+ catch (_a) {
12675
+ return defaultColor; // don't fail whole text layer because of invalid grid color
12676
+ }
12677
+ }
12678
+ function decodeGridInfo(grid) {
12679
+ var _a, _b;
12680
+ if (!grid || typeof grid !== 'object')
12681
+ return undefined;
12682
+ const result = {
12683
+ isOn: !!grid.GridIsOn,
12684
+ show: !!grid.ShowGrid,
12685
+ size: (_a = grid.GridSize) !== null && _a !== void 0 ? _a : defaultGridInfo.size,
12686
+ leading: (_b = grid.GridLeading) !== null && _b !== void 0 ? _b : defaultGridInfo.leading,
12687
+ color: decodeGridColor(grid.GridColor, defaultGridInfo.color),
12688
+ leadingFillColor: decodeGridColor(grid.GridLeadingFillColor, defaultGridInfo.leadingFillColor),
12689
+ alignLineHeightToGridFlags: !!grid.AlignLineHeightToGridFlags,
12690
+ };
12691
+ const isDefault = !result.isOn && !result.show && result.size === defaultGridInfo.size &&
12692
+ result.leading === defaultGridInfo.leading && !result.alignLineHeightToGridFlags &&
12693
+ isDefaultGridColor(result.color) && isDefaultGridColor(result.leadingFillColor);
12694
+ return isDefault ? undefined : result;
12695
+ }
12023
12696
  function decodeEngineData(engineData) {
12024
- var _a, _b, _c, _d, _e, _f;
12697
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
12025
12698
  // console.log('engineData', require('util').inspect(engineData, false, 99, true));
12026
12699
  const engineDict = engineData.EngineDict;
12027
12700
  const resourceDict = engineData.ResourceDict;
@@ -12033,9 +12706,11 @@ function decodeEngineData(engineData) {
12033
12706
  }));
12034
12707
  let text = engineDict.Editor.Text.replace(/\r/g, '\n');
12035
12708
  let removedCharacters = 0;
12036
- while (/\n$/.test(text)) {
12709
+ // Photoshop always appends exactly one trailing \r (and encoder adds one back), any additional
12710
+ // trailing new lines are empty paragraphs that are part of the text
12711
+ if (/\n$/.test(text)) {
12037
12712
  text = text.substring(0, text.length - 1);
12038
- removedCharacters++;
12713
+ removedCharacters = 1;
12039
12714
  }
12040
12715
  const result = {
12041
12716
  text,
@@ -12056,6 +12731,10 @@ function decodeEngineData(engineData) {
12056
12731
  if (photoshop.BoxBounds)
12057
12732
  result.boxBounds = photoshop.BoxBounds;
12058
12733
  }
12734
+ // grid info (only included when it differs from defaults, to avoid noise in common case)
12735
+ const gridInfo = decodeGridInfo(engineDict.GridInfo);
12736
+ if (gridInfo)
12737
+ result.gridInfo = gridInfo;
12059
12738
  // paragraph style
12060
12739
  // const theNormalParagraphSheet = resourceDict.TheNormalParagraphSheet;
12061
12740
  // const paragraphSheetSet = resourceDict.ParagraphSheetSet;
@@ -12086,13 +12765,17 @@ function decodeEngineData(engineData) {
12086
12765
  // const styleSheetSet = resourceDict.StyleSheetSet;
12087
12766
  // const styleSheetData = styleSheetSet[theNormalStyleSheet].StyleSheetData;
12088
12767
  const styleRun = engineDict.StyleRun;
12768
+ // style runs without a font use the font of the normal style sheet
12769
+ const normalStyleSheet = (_j = (_g = resourceDict.StyleSheetSet) === null || _g === void 0 ? void 0 : _g[(_h = resourceDict.TheNormalStyleSheet) !== null && _h !== void 0 ? _h : 0]) === null || _j === void 0 ? void 0 : _j.StyleSheetData;
12770
+ const normalFontIndex = normalStyleSheet === null || normalStyleSheet === void 0 ? void 0 : normalStyleSheet.Font;
12771
+ const normalFont = (_k = (normalFontIndex !== undefined ? fonts[normalFontIndex] : undefined)) !== null && _k !== void 0 ? _k : fonts[0];
12089
12772
  result.style = {}; // decodeStyle(styleSheetData, fonts);
12090
12773
  result.styleRuns = [];
12091
12774
  for (let i = 0; i < styleRun.RunArray.length; i++) {
12092
12775
  const length = styleRun.RunLengthArray[i];
12093
12776
  const style = decodeStyle(styleRun.RunArray[i].StyleSheet.StyleSheetData, fonts);
12094
- if (!style.font)
12095
- style.font = fonts[0];
12777
+ if (!style.font && normalFont)
12778
+ style.font = normalFont;
12096
12779
  result.styleRuns.push({ length, style });
12097
12780
  }
12098
12781
  for (let counter = removedCharacters; result.styleRuns.length && counter > 0; counter--) {
@@ -12105,16 +12788,42 @@ function decodeEngineData(engineData) {
12105
12788
  delete result.styleRuns;
12106
12789
  return result;
12107
12790
  }
12791
+ // Adjusts run lengths (specified in terms of the original text) for characters removed from the text
12792
+ // (\r characters of \r\n pairs that are collapsed into single \r)
12793
+ function adjustRunLengths(runs, removed) {
12794
+ if (!runs || !removed.length)
12795
+ return runs;
12796
+ let start = 0;
12797
+ let removedIndex = 0;
12798
+ return runs.map(run => {
12799
+ const length = Math.max(0, run.length || 0);
12800
+ const end = start + length;
12801
+ let count = 0;
12802
+ while (removedIndex < removed.length && removed[removedIndex] < end) {
12803
+ if (removed[removedIndex] >= start)
12804
+ count++;
12805
+ removedIndex++;
12806
+ }
12807
+ start = end;
12808
+ return Object.assign(Object.assign({}, run), { length: length - count });
12809
+ });
12810
+ }
12108
12811
  function encodeEngineData(data) {
12109
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
12110
- const text = `${(data.text || '').replace(/\r?\n/g, '\r')}\r`;
12812
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
12813
+ const sourceText = data.text || '';
12814
+ const removedIndices = []; // indices of \r characters removed from \r\n pairs
12815
+ for (let i = 0; i < sourceText.length - 1; i++) {
12816
+ if (sourceText.charCodeAt(i) === 13 && sourceText.charCodeAt(i + 1) === 10)
12817
+ removedIndices.push(i);
12818
+ }
12819
+ const text = `${sourceText.replace(/\r?\n/g, '\r')}\r`;
12111
12820
  const fonts = [
12112
12821
  { name: 'AdobeInvisFont', script: 0, type: 0, synthetic: 0 },
12113
12822
  ];
12114
12823
  const defFont = ((_a = data.style) === null || _a === void 0 ? void 0 : _a.font) || ((_c = (_b = data.styleRuns) === null || _b === void 0 ? void 0 : _b.find(s => s.style.font)) === null || _c === void 0 ? void 0 : _c.style.font) || defaultFont;
12115
12824
  const paragraphRunArray = [];
12116
12825
  const paragraphRunLengthArray = [];
12117
- const paragraphRuns = data.paragraphStyleRuns;
12826
+ const paragraphRuns = adjustRunLengths(data.paragraphStyleRuns, removedIndices);
12118
12827
  if (paragraphRuns && paragraphRuns.length) {
12119
12828
  let leftLength = text.length;
12120
12829
  for (const run of paragraphRuns) {
@@ -12163,7 +12872,9 @@ function encodeEngineData(data) {
12163
12872
  }
12164
12873
  }
12165
12874
  const styleSheetData = encodeStyle(Object.assign(Object.assign({}, defaultStyle), { font: defFont }), fonts);
12166
- const styleRuns = data.styleRuns || [{ length: text.length, style: data.style || {} }];
12875
+ const styleRuns = ((_d = data.styleRuns) === null || _d === void 0 ? void 0 : _d.length) ?
12876
+ adjustRunLengths(data.styleRuns, removedIndices) :
12877
+ [{ length: text.length, style: data.style || {} }];
12167
12878
  const styleRunArray = [];
12168
12879
  const styleRunLengthArray = [];
12169
12880
  let leftLength = text.length;
@@ -12185,7 +12896,7 @@ function encodeEngineData(data) {
12185
12896
  });
12186
12897
  }
12187
12898
  // add extra run to the end if existing ones didn't fill it up
12188
- if (leftLength && styleRuns.length) {
12899
+ if (leftLength) {
12189
12900
  styleRunLengthArray.push(leftLength);
12190
12901
  styleRunArray.push({
12191
12902
  StyleSheet: {
@@ -12194,6 +12905,7 @@ function encodeEngineData(data) {
12194
12905
  });
12195
12906
  }
12196
12907
  const gridInfo = Object.assign(Object.assign({}, defaultGridInfo), data.gridInfo);
12908
+ const antiAliasIndex = antialias.indexOf((_e = data.antiAlias) !== null && _e !== void 0 ? _e : 'sharp');
12197
12909
  const WritingDirection = data.orientation === 'vertical' ? 2 : 0;
12198
12910
  const Procession = data.orientation === 'vertical' ? 1 : 0;
12199
12911
  const ShapeType = data.shapeType === 'box' ? 1 : 0;
@@ -12257,11 +12969,11 @@ function encodeEngineData(data) {
12257
12969
  FontType: f.type || 0,
12258
12970
  Synthetic: f.synthetic || 0,
12259
12971
  })),
12260
- SuperscriptSize: (_d = data.superscriptSize) !== null && _d !== void 0 ? _d : 0.583,
12261
- SuperscriptPosition: (_e = data.superscriptPosition) !== null && _e !== void 0 ? _e : 0.333,
12262
- SubscriptSize: (_f = data.subscriptSize) !== null && _f !== void 0 ? _f : 0.583,
12263
- SubscriptPosition: (_g = data.subscriptPosition) !== null && _g !== void 0 ? _g : 0.333,
12264
- SmallCapSize: (_h = data.smallCapSize) !== null && _h !== void 0 ? _h : 0.7,
12972
+ SuperscriptSize: (_f = data.superscriptSize) !== null && _f !== void 0 ? _f : 0.583,
12973
+ SuperscriptPosition: (_g = data.superscriptPosition) !== null && _g !== void 0 ? _g : 0.333,
12974
+ SubscriptSize: (_h = data.subscriptSize) !== null && _h !== void 0 ? _h : 0.583,
12975
+ SubscriptPosition: (_j = data.subscriptPosition) !== null && _j !== void 0 ? _j : 0.333,
12976
+ SmallCapSize: (_k = data.smallCapSize) !== null && _k !== void 0 ? _k : 0.7,
12265
12977
  };
12266
12978
  const engineData = {
12267
12979
  EngineDict: {
@@ -12284,14 +12996,14 @@ function encodeEngineData(data) {
12284
12996
  GridInfo: {
12285
12997
  GridIsOn: !!gridInfo.isOn,
12286
12998
  ShowGrid: !!gridInfo.show,
12287
- GridSize: (_j = gridInfo.size) !== null && _j !== void 0 ? _j : 18,
12288
- GridLeading: (_k = gridInfo.leading) !== null && _k !== void 0 ? _k : 22,
12999
+ GridSize: (_l = gridInfo.size) !== null && _l !== void 0 ? _l : 18,
13000
+ GridLeading: (_m = gridInfo.leading) !== null && _m !== void 0 ? _m : 22,
12289
13001
  GridColor: encodeColor(gridInfo.color),
12290
- GridLeadingFillColor: encodeColor(gridInfo.color),
13002
+ GridLeadingFillColor: encodeColor(gridInfo.leadingFillColor),
12291
13003
  AlignLineHeightToGridFlags: !!gridInfo.alignLineHeightToGridFlags,
12292
13004
  },
12293
- AntiAlias: antialias.indexOf((_l = data.antiAlias) !== null && _l !== void 0 ? _l : 'sharp'),
12294
- UseFractionalGlyphWidths: (_m = data.useFractionalGlyphWidths) !== null && _m !== void 0 ? _m : true,
13005
+ AntiAlias: antiAliasIndex === -1 ? antialias.indexOf('sharp') : antiAliasIndex,
13006
+ UseFractionalGlyphWidths: (_o = data.useFractionalGlyphWidths) !== null && _o !== void 0 ? _o : true,
12295
13007
  Rendered: {
12296
13008
  Version: 1,
12297
13009
  Shapes: {
@@ -12314,7 +13026,7 @@ function encodeEngineData(data) {
12314
13026
  return engineData;
12315
13027
  }
12316
13028
 
12317
- },{}],16:[function(require,module,exports){
13029
+ },{"./helpers":8}],16:[function(require,module,exports){
12318
13030
  "use strict";
12319
13031
  Object.defineProperty(exports, "__esModule", { value: true });
12320
13032
  exports.stringLengthInBytes = stringLengthInBytes;
@@ -12335,30 +13047,44 @@ function charLengthInBytes(code) {
12335
13047
  return 4;
12336
13048
  }
12337
13049
  }
12338
- // Reads a scalar value at index `i`, replacing unpaired surrogates with U+FFFD,
13050
+ // Returns scalar value at index `i` (combining surrogate pairs), replacing unpaired surrogates with U+FFFD,
12339
13051
  // matching how TextEncoder converts a string to a sequence of Unicode scalar values.
13052
+ // Values above 0xffff always take 2 UTF-16 code units.
12340
13053
  function codePointAt(value, i) {
12341
13054
  const code = value.charCodeAt(i);
13055
+ if (code < 0xd800 || code > 0xdfff)
13056
+ return code;
12342
13057
  // high surrogate
12343
- if (code >= 0xd800 && code <= 0xdbff && (i + 1) < value.length) {
13058
+ if (code <= 0xdbff && (i + 1) < value.length) {
12344
13059
  const extra = value.charCodeAt(i + 1);
12345
13060
  // low surrogate
12346
13061
  if (extra >= 0xdc00 && extra <= 0xdfff) {
12347
- return { code: ((code & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000, size: 2 };
13062
+ return ((code & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000;
12348
13063
  }
12349
13064
  }
12350
13065
  // unpaired surrogate (lone high or lone low)
12351
- if (code >= 0xd800 && code <= 0xdfff) {
12352
- return { code: 0xfffd, size: 1 };
12353
- }
12354
- return { code, size: 1 };
13066
+ return 0xfffd;
12355
13067
  }
12356
13068
  function stringLengthInBytes(value) {
12357
13069
  let result = 0;
12358
- for (let i = 0; i < value.length;) {
12359
- const { code, size } = codePointAt(value, i);
12360
- result += charLengthInBytes(code);
12361
- i += size;
13070
+ for (let i = 0; i < value.length; i++) {
13071
+ const code = value.charCodeAt(i);
13072
+ if (code < 0x80) {
13073
+ result += 1;
13074
+ }
13075
+ else if (code < 0x800) {
13076
+ result += 2;
13077
+ }
13078
+ else {
13079
+ const scalar = codePointAt(value, i);
13080
+ if (scalar > 0xffff) {
13081
+ result += 4;
13082
+ i++;
13083
+ }
13084
+ else {
13085
+ result += 3;
13086
+ }
13087
+ }
12362
13088
  }
12363
13089
  return result;
12364
13090
  }
@@ -12387,10 +13113,17 @@ function writeCharacter(buffer, offset, code) {
12387
13113
  return length;
12388
13114
  }
12389
13115
  function encodeStringTo(buffer, offset, value) {
12390
- for (let i = 0; i < value.length;) {
12391
- const { code, size } = codePointAt(value, i);
12392
- offset += writeCharacter(buffer, offset, code);
12393
- i += size;
13116
+ for (let i = 0; i < value.length; i++) {
13117
+ const code = value.charCodeAt(i);
13118
+ if (code < 0x80) {
13119
+ buffer[offset++] = code;
13120
+ }
13121
+ else {
13122
+ const scalar = codePointAt(value, i);
13123
+ if (scalar > 0xffff)
13124
+ i++;
13125
+ offset += writeCharacter(buffer, offset, scalar);
13126
+ }
12394
13127
  }
12395
13128
  return offset;
12396
13129
  }