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/src/helpers.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { deflate as deflateSync } from 'pako';
2
2
  import { Layer, BlendMode, LayerColor, PixelData, PixelArray, ChannelID, Compression } from './psd';
3
3
  import { decodeJpeg } from './jpeg';
4
+ import { PsdReader } from './psdReader';
4
5
 
5
6
  export const MOCK_HANDLERS = false;
6
7
  export const RAW_IMAGE_DATA = false;
@@ -146,6 +147,15 @@ export function offsetForChannel(channelId: ChannelID, cmyk: boolean) {
146
147
  }
147
148
  }
148
149
 
150
+ // Lab a/b components are stored in -128..127 range (multiplied by scale), normalized values are in -1..1 range
151
+ export function normalizeLabAB(value: number, scale = 1) {
152
+ return value < 0 ? (value / (128 * scale)) : (value / (127 * scale));
153
+ }
154
+
155
+ export function denormalizeLabAB(value: number, scale = 1) {
156
+ return value < 0 ? (value * (128 * scale)) : (value * (127 * scale));
157
+ }
158
+
149
159
  export function clamp(value: number, min: number, max: number) {
150
160
  return value < min ? min : (value > max ? max : value);
151
161
  }
@@ -236,8 +246,9 @@ export function writeDataRaw(data: PixelData, offset: number, width: number, hei
236
246
  return array;
237
247
  }
238
248
 
249
+ // returns view into the buffer, the buffer has to fit worst case size (see getRLEChannelBound), throws if it doesn't
239
250
  export function writeDataRLE(buffer: Uint8Array, { data, width, height }: PixelData, offsets: number[], large: boolean) {
240
- if (!width || !height) return undefined;
251
+ if (!width || !height) return buffer.subarray(0, 0);
241
252
 
242
253
  const stride = (4 * width) | 0;
243
254
 
@@ -320,6 +331,9 @@ export function writeDataRLE(buffer: Uint8Array, { data, width, height }: PixelD
320
331
  }
321
332
  }
322
333
 
334
+ // writes past the end of typed array are ignored, so it's enough to check the size after each row
335
+ if (o > buffer.length) throw new Error(`RLE buffer too small (${buffer.length})`);
336
+
323
337
  const length = o - startOffset;
324
338
 
325
339
  if (large) {
@@ -332,7 +346,8 @@ export function writeDataRLE(buffer: Uint8Array, { data, width, height }: PixelD
332
346
  }
333
347
  }
334
348
 
335
- return buffer.slice(0, o);
349
+ // returns view of the buffer, copy the data if the buffer is going to be reused
350
+ return buffer.subarray(0, o);
336
351
  }
337
352
 
338
353
  export function writeDataZipWithoutPrediction({ data, width, height }: PixelData, offsets: number[]) {
@@ -366,7 +381,7 @@ export function writeDataZipWithoutPrediction({ data, width, height }: PixelData
366
381
  }
367
382
  }
368
383
 
369
- export function createCanvasFromData(data: Uint8Array): HTMLCanvasElement {
384
+ export function createCanvasFromData(data: Uint8Array, reader: Pick<PsdReader, 'log'>): HTMLCanvasElement {
370
385
  const canvas = createCanvas(100, 100);
371
386
 
372
387
  try {
@@ -376,7 +391,7 @@ export function createCanvasFromData(data: Uint8Array): HTMLCanvasElement {
376
391
  canvas.height = imageData.height;
377
392
  context.putImageData(imageData, 0, 0);
378
393
  } catch (e: any) {
379
- console.error('JPEG decompression error', e.message);
394
+ reader.log('JPEG decompression error', e.message);
380
395
  }
381
396
 
382
397
  return canvas;
@@ -4,7 +4,7 @@ import { PsdReader, readUnicodeString, readUint32, readUint16, readUint8, readFl
4
4
  import { PsdWriter, writeUnicodeString, writeUint32, writeUint8, writeFloat64, writeUint16, writeBytes, writeInt16, writeFloat32, writeFixedPoint32, writeUnicodeStringWithPadding, writeColor, writeSignature, writeSection, writeInt32, writeAsciiString, } from './psdWriter';
5
5
  import { createCanvasFromData, createEnum, MOCK_HANDLERS } from './helpers';
6
6
  import { decodeString, encodeString } from './utf8';
7
- import { ESliceBGColorType, ESliceHorzAlign, ESliceOrigin, ESliceType, ESliceVertAlign, frac, FractionDescriptor, parseTrackList, readVersionAndDescriptor, serializeTrackList, TimelineTrackDescriptor, TimeScopeDescriptor, writeVersionAndDescriptor } from './descriptor';
7
+ import { ESliceBGColorType, ESliceHorzAlign, ESliceOrigin, ESliceType, ESliceVertAlign, frac, FractionDescriptor, parseTrackList, pathSelectionStateToReference, readVersionAndDescriptor, serializeTrackList, TimelineTrackDescriptor, TimeScopeDescriptor, writeVersionAndDescriptor } from './descriptor';
8
8
 
9
9
  export interface InternalImageResources extends ImageResources {
10
10
  layersGroup?: number[];
@@ -57,6 +57,20 @@ function writeUtf8String(writer: PsdWriter, value: string) {
57
57
  writeBytes(writer, buffer);
58
58
  }
59
59
 
60
+ let gbkDecoder: TextDecoder | null | undefined;
61
+
62
+ function getGbkDecoder() {
63
+ if (gbkDecoder === undefined) {
64
+ try {
65
+ gbkDecoder = new TextDecoder('gbk');
66
+ } catch {
67
+ gbkDecoder = null;
68
+ }
69
+ }
70
+
71
+ return gbkDecoder;
72
+ }
73
+
60
74
  function readEncodedString(reader: PsdReader) {
61
75
  const length = readUint8(reader);
62
76
  const buffer = readBytes(reader, length);
@@ -70,10 +84,13 @@ function readEncodedString(reader: PsdReader) {
70
84
  }
71
85
 
72
86
  if (notAscii) {
73
- try {
74
- const decoder = new TextDecoder('gbk');
75
- return decoder.decode(buffer)
76
- } catch { }
87
+ const decoder = getGbkDecoder();
88
+
89
+ if (decoder) {
90
+ try {
91
+ return decoder.decode(buffer);
92
+ } catch { }
93
+ }
77
94
  }
78
95
 
79
96
  return decodeString(buffer);
@@ -82,10 +99,15 @@ function readEncodedString(reader: PsdReader) {
82
99
  function writeEncodedString(writer: PsdWriter, value: string) {
83
100
  let ascii = '';
84
101
 
85
- for (let i = 0, code = value.codePointAt(i++); code !== undefined; code = value.codePointAt(i++)) {
86
- ascii += code > 0x7f ? '?' : String.fromCodePoint(code);
102
+ for (let i = 0; i < value.length;) {
103
+ const code = value.codePointAt(i)!;
104
+ ascii += code > 0x7f ? '?' : String.fromCharCode(code);
105
+ i += code > 0xffff ? 2 : 1; // skip low surrogate of astral characters
87
106
  }
88
107
 
108
+ // length is stored in a single byte
109
+ if (ascii.length > 255) ascii = ascii.substring(0, 255);
110
+
89
111
  const buffer = encodeString(ascii);
90
112
  writeUint8(writer, buffer.byteLength);
91
113
  writeBytes(writer, buffer);
@@ -616,7 +638,7 @@ addHandler(
616
638
 
617
639
  for (let i = 0; i < count; i++) {
618
640
  target.gridAndGuidesInformation.guides!.push({
619
- location: readUint32(reader) / 32,
641
+ location: readInt32(reader) / 32,
620
642
  direction: readUint8(reader) ? 'horizontal' : 'vertical'
621
643
  });
622
644
  }
@@ -627,12 +649,12 @@ addHandler(
627
649
  const guides = info.guides || [];
628
650
 
629
651
  writeUint32(writer, 1);
630
- writeUint32(writer, grid.horizontal);
631
- writeUint32(writer, grid.vertical);
652
+ writeUint32(writer, Math.round(grid.horizontal));
653
+ writeUint32(writer, Math.round(grid.vertical));
632
654
  writeUint32(writer, guides.length);
633
655
 
634
656
  for (const g of guides) {
635
- writeUint32(writer, g.location * 32);
657
+ writeInt32(writer, Math.round(g.location * 32));
636
658
  writeUint8(writer, g.direction === 'horizontal' ? 1 : 0);
637
659
  }
638
660
  },
@@ -665,10 +687,10 @@ addHandler(
665
687
  capturedInfo: item.capturedInfo,
666
688
  });
667
689
 
668
- if ('comment' in item) target.layerComps.list[target.layerComps.list.length - 1].comment = item.comment;
690
+ if (item.comment !== undefined) target.layerComps.list[target.layerComps.list.length - 1].comment = item.comment;
669
691
  }
670
692
 
671
- if ('lastAppliedComp' in desc) target.layerComps.lastApplied = desc.lastAppliedComp;
693
+ if (desc.lastAppliedComp !== undefined) target.layerComps.lastApplied = desc.lastAppliedComp;
672
694
  },
673
695
  (writer, target) => {
674
696
  const layerComps = target.layerComps!;
@@ -678,13 +700,13 @@ addHandler(
678
700
  const t: LayerCompsDescriptor['list'][0] = {} as any;
679
701
  t._classID = 'Comp';
680
702
  t['Nm '] = item.name;
681
- if ('comment' in item) t.comment = item.comment;
703
+ if (item.comment !== undefined) t.comment = item.comment;
682
704
  t.compID = item.id;
683
705
  t.capturedInfo = item.capturedInfo;
684
706
  desc.list.push(t);
685
707
  }
686
708
 
687
- if ('lastApplied' in layerComps) desc.lastAppliedComp = layerComps.lastApplied;
709
+ if (layerComps.lastApplied !== undefined) desc.lastAppliedComp = layerComps.lastApplied;
688
710
 
689
711
  // console.log('CompList', require('util').inspect(desc, false, 99, true));
690
712
  writeVersionAndDescriptor(writer, '', 'CompList', desc);
@@ -747,8 +769,8 @@ addHandler(
747
769
  numBefore: onionSkins.framesBefore,
748
770
  numAfter: onionSkins.framesAfter,
749
771
  Spcn: onionSkins.frameSpacing,
750
- minOpacity: (onionSkins.minOpacity * 100) | 0,
751
- maxOpacity: (onionSkins.maxOpacity * 100) | 0,
772
+ minOpacity: Math.round(onionSkins.minOpacity * 100),
773
+ maxOpacity: Math.round(onionSkins.maxOpacity * 100),
752
774
  BlnM: Math.max(0, onionSkinsBlendModes.indexOf(onionSkins.blendMode)),
753
775
  };
754
776
 
@@ -855,7 +877,7 @@ addHandler(
855
877
  LCnt: timeline.repeats,
856
878
  globalTrackList: serializeTrackList(timeline.globalTracks),
857
879
  audioClipGroupList: {
858
- audioClipGroupList: timeline.audioClipGroups?.map(a => ({
880
+ audioClipGroupList: (timeline.audioClipGroups ?? []).map(a => ({
859
881
  groupID: a.id,
860
882
  muted: a.muted,
861
883
  audioClipList: a.audioClips.map<TimelineAudioClipDescriptor>(c => ({
@@ -1072,8 +1094,13 @@ addHandler(
1072
1094
  }
1073
1095
  if (leftBytes()) {
1074
1096
  const desc = readVersionAndDescriptor(reader) as SlicesDesc;
1097
+ const slicesById = new Map<number, typeof slices[0]>();
1098
+ for (const s of slices) {
1099
+ if (!slicesById.has(s.id)) slicesById.set(s.id, s);
1100
+ }
1101
+
1075
1102
  desc.slices.forEach(d => {
1076
- const slice = slices.find(s => d.sliceID == s.id);
1103
+ const slice = slicesById.get(d.sliceID);
1077
1104
  if (slice) {
1078
1105
  slice.topOutset = d.topOutset;
1079
1106
  slice.leftOutset = d.leftOutset;
@@ -1272,7 +1299,7 @@ addHandler(
1272
1299
  if (reader.useRawThumbnail) {
1273
1300
  target.thumbnailRaw = { width, height, data };
1274
1301
  } else if (data.byteLength) {
1275
- target.thumbnail = createCanvasFromData(data);
1302
+ target.thumbnail = createCanvasFromData(data, reader);
1276
1303
  }
1277
1304
  },
1278
1305
  (writer, target) => {
@@ -1386,7 +1413,7 @@ addHandler(
1386
1413
  target.pathSelectionState = desc['null'];
1387
1414
  },
1388
1415
  (writer, target) => {
1389
- const desc: Descriptor1088 = { 'null': target.pathSelectionState! };
1416
+ const desc: Descriptor1088 = { 'null': pathSelectionStateToReference(target.pathSelectionState!) };
1390
1417
  writeVersionAndDescriptor(writer, '', 'null', desc);
1391
1418
  },
1392
1419
  );
@@ -1498,7 +1525,7 @@ addHandler(
1498
1525
  const frame: AnimationFrameDescriptor = {
1499
1526
  FrID: f.id,
1500
1527
  } as any;
1501
- if (f.delay) frame.FrDl = (f.delay * 100) | 0;
1528
+ if (f.delay) frame.FrDl = Math.round(f.delay * 100);
1502
1529
  frame.FrDs = FrmD.encode(f.dispose);
1503
1530
  // if (i === 0) frame.FrGA = 30; // ???
1504
1531
  desc.FrIn.push(frame);
package/src/index.ts CHANGED
@@ -43,5 +43,6 @@ export function writePsdBuffer(psd: Psd, options?: WriteOptions): Buffer {
43
43
  throw new Error('Buffer not supported on this platform');
44
44
  }
45
45
 
46
+ // copy, so the buffer doesn't hold on to the unused part of the writer's buffer
46
47
  return Buffer.from(writePsdUint8Array(psd, options));
47
48
  }
package/src/jpeg.ts CHANGED
@@ -88,6 +88,8 @@ const maxMemoryUsageBytes = 64 * 1024 * 1024; // Don't decode if memory footprin
88
88
  let totalBytesAllocated = 0; // avoid unexpected OOMs from untrusted content.
89
89
 
90
90
  function requestMemoryAllocation(increaseAmount: number) {
91
+ if (!isFinite(increaseAmount) || increaseAmount < 0) throw new Error(`Invalid memory allocation size (${increaseAmount})`);
92
+
91
93
  const totalMemoryImpactBytes = totalBytesAllocated + increaseAmount;
92
94
  if (totalMemoryImpactBytes > maxMemoryUsageBytes) {
93
95
  const exceededAmount = Math.ceil((totalMemoryImpactBytes - maxMemoryUsageBytes) / 1024 / 1024);
@@ -169,7 +171,9 @@ function decodeScan(
169
171
  return bitsData >>> 7;
170
172
  }
171
173
 
172
- function decodeHuffman(tree: number[] | number[][]) {
174
+ function decodeHuffman(tree: number[] | number[][] | undefined) {
175
+ if (!tree) throw new Error('Missing Huffman table');
176
+
173
177
  let node: number | number[] | number[][] = tree;
174
178
 
175
179
  while (true) {
@@ -359,6 +363,8 @@ function decodeScan(
359
363
  mcuExpected = mcusPerLine * frame.mcusPerColumn;
360
364
  }
361
365
 
366
+ if (!isFinite(mcuExpected) || mcuExpected < 0) throw new Error('Invalid number of MCUs');
367
+
362
368
  if (!resetInterval) resetInterval = mcuExpected;
363
369
 
364
370
  let h: number;
@@ -375,6 +381,9 @@ function decodeScan(
375
381
  for (let n = 0; n < resetInterval; n++) {
376
382
  decodeBlock(component, decodeFn, mcu);
377
383
  mcu++;
384
+
385
+ // If we've reached our expected MCU's, stop decoding
386
+ if (mcu === mcuExpected) break;
378
387
  }
379
388
  } else {
380
389
  for (let n = 0; n < resetInterval; n++) {
@@ -438,7 +447,8 @@ function buildComponentData(component: Component) {
438
447
  // IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989,
439
448
  // 988-991.
440
449
  function quantizeAndInverse(zz: Int32Array, dataOut: Uint8Array, dataIn: Int32Array) {
441
- const qt = component.quantizationTable!;
450
+ const qt = component.quantizationTable;
451
+ if (!qt) throw new Error('Missing quantization table');
442
452
  const p = dataIn;
443
453
 
444
454
  // dequant
@@ -823,11 +833,13 @@ function parse(data: Uint8Array) {
823
833
  }
824
834
 
825
835
  const componentsCount = data[offset++];
836
+ if (!componentsCount) throw new Error('SOF: no components in frame');
826
837
 
827
838
  for (let i = 0; i < componentsCount; i++) {
828
839
  const componentId = data[offset];
829
840
  const h = data[offset + 1] >> 4;
830
841
  const v = data[offset + 1] & 15;
842
+ if (h < 1 || h > 4 || v < 1 || v > 4) throw new Error(`SOF: invalid sampling factors (${h}x${v})`);
831
843
  const qId = data[offset + 2];
832
844
  frame!.componentsOrder.push(componentId);
833
845
  frame!.components[componentId] = {
@@ -881,11 +893,14 @@ function parse(data: Uint8Array) {
881
893
  readUint16() // Ignore this data since it represents the image height
882
894
  break;
883
895
  case 0xFFDA: { // SOS (Start of Scan)
896
+ if (!frame) throw new Error('SOS: scan before frame header (SOF)');
884
897
  readUint16(); // skip data length
885
898
  const selectorsCount = data[offset++];
886
899
  const components: Component[] = [];
887
900
  for (let i = 0; i < selectorsCount; i++) {
888
- const component = frame!.components[data[offset++]];
901
+ const componentId = data[offset++];
902
+ const component = frame.components[componentId];
903
+ if (!component) throw new Error(`SOS: component ${componentId} not found in frame`);
889
904
  const tableSpec = data[offset++];
890
905
  component.huffmanTableDC = huffmanTablesDC[tableSpec >> 4];
891
906
  component.huffmanTableAC = huffmanTablesAC[tableSpec & 15];
@@ -895,7 +910,7 @@ function parse(data: Uint8Array) {
895
910
  const spectralEnd = data[offset++];
896
911
  const successiveApproximation = data[offset++];
897
912
  const processed = decodeScan(
898
- data, offset, frame!, components, resetInterval, spectralStart, spectralEnd,
913
+ data, offset, frame, components, resetInterval, spectralStart, spectralEnd,
899
914
  successiveApproximation >> 4, successiveApproximation & 15);
900
915
  offset += processed;
901
916
  break;
@@ -938,7 +953,9 @@ function parse(data: Uint8Array) {
938
953
  for (let i = 0; i < frames.length; i++) {
939
954
  const cp = frames[i].components;
940
955
  for (let j in cp) { // TODO: don't use `in`
941
- cp[j].quantizationTable = quantizationTables[cp[j].quantizationIdx!];
956
+ const table = quantizationTables[cp[j].quantizationIdx!];
957
+ if (!table) throw new Error(`Missing quantization table ${cp[j].quantizationIdx}`);
958
+ cp[j].quantizationTable = table;
942
959
  delete cp[j].quantizationIdx; // TODO: why ???
943
960
  }
944
961
  }
@@ -1004,10 +1021,8 @@ function getData(decoded: Decoded) {
1004
1021
  break;
1005
1022
  }
1006
1023
  case 3: {
1007
- // The default transform for three components is true
1008
- colorTransform = true;
1009
- // The adobe transform marker overrides any previous setting
1010
- if (decoded.adobe && decoded.adobe.transformCode) colorTransform = true;
1024
+ // The default transform for three components is true, the adobe transform marker overrides it
1025
+ colorTransform = decoded.adobe ? !!decoded.adobe.transformCode : true;
1011
1026
 
1012
1027
  const component1 = decoded.components[0];
1013
1028
  const component2 = decoded.components[1];
package/src/psd.ts CHANGED
@@ -411,7 +411,7 @@ export interface TextPath {
411
411
  textRange: number[];
412
412
  rowGutter?: number;
413
413
  columnGutter?: number;
414
- BaselineAlignment?: {
414
+ baselineAlignment?: {
415
415
  flag?: number;
416
416
  min?: number;
417
417
  };
@@ -1793,6 +1793,8 @@ export interface Psd extends LayerAdditionalInfo {
1793
1793
  canvas?: HTMLCanvasElement;
1794
1794
  imageData?: PixelData;
1795
1795
  rawCompositeData?: Uint8Array;
1796
+ /** Information needed to decode `rawCompositeData` (see `getCompositeImageData`) */
1797
+ rawCompositeDataInfo?: { large: boolean; globalAlpha: boolean; };
1796
1798
  imageResources?: ImageResources;
1797
1799
  linkedFiles?: LinkedFile[]; // used in smart objects
1798
1800
  artboards?: {