ag-psd 31.0.3 → 33.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/.v8-cache/v24.7.0-x64-ef5a0af0/00d0e0b4 +0 -0
  2. package/.v8-cache/v24.7.0-x64-ef5a0af0/03e19709 +0 -0
  3. package/.v8-cache/v24.7.0-x64-ef5a0af0/05135617 +0 -0
  4. package/.v8-cache/v24.7.0-x64-ef5a0af0/0cc76730 +0 -0
  5. package/.v8-cache/v24.7.0-x64-ef5a0af0/1130885f +0 -0
  6. package/.v8-cache/v24.7.0-x64-ef5a0af0/20729831 +0 -0
  7. package/.v8-cache/v24.7.0-x64-ef5a0af0/214a593b +0 -0
  8. package/.v8-cache/v24.7.0-x64-ef5a0af0/226e2801 +0 -0
  9. package/.v8-cache/v24.7.0-x64-ef5a0af0/25f8b84a +0 -0
  10. package/.v8-cache/v24.7.0-x64-ef5a0af0/27518289 +0 -0
  11. package/.v8-cache/v24.7.0-x64-ef5a0af0/2a19133e +0 -0
  12. package/.v8-cache/v24.7.0-x64-ef5a0af0/330ef2d9 +0 -0
  13. package/.v8-cache/v24.7.0-x64-ef5a0af0/37ff16b8 +0 -0
  14. package/.v8-cache/v24.7.0-x64-ef5a0af0/3f72a97c +0 -0
  15. package/.v8-cache/v24.7.0-x64-ef5a0af0/4511bacf +0 -0
  16. package/.v8-cache/v24.7.0-x64-ef5a0af0/68416cb2 +0 -0
  17. package/.v8-cache/v24.7.0-x64-ef5a0af0/71df9cb3 +0 -0
  18. package/.v8-cache/v24.7.0-x64-ef5a0af0/75efccd4 +0 -0
  19. package/.v8-cache/v24.7.0-x64-ef5a0af0/762f9305 +0 -0
  20. package/.v8-cache/v24.7.0-x64-ef5a0af0/7bcfc50a +0 -0
  21. package/.v8-cache/v24.7.0-x64-ef5a0af0/7ea06cea +0 -0
  22. package/.v8-cache/v24.7.0-x64-ef5a0af0/825e9a05 +0 -0
  23. package/.v8-cache/v24.7.0-x64-ef5a0af0/87372817 +0 -0
  24. package/.v8-cache/v24.7.0-x64-ef5a0af0/87cf2209 +0 -0
  25. package/.v8-cache/v24.7.0-x64-ef5a0af0/891a7c73 +0 -0
  26. package/.v8-cache/v24.7.0-x64-ef5a0af0/8b68a3a2 +0 -0
  27. package/.v8-cache/v24.7.0-x64-ef5a0af0/932f0770 +0 -0
  28. package/.v8-cache/v24.7.0-x64-ef5a0af0/a1d12a93 +0 -0
  29. package/.v8-cache/v24.7.0-x64-ef5a0af0/a7e80865 +0 -0
  30. package/.v8-cache/v24.7.0-x64-ef5a0af0/ac317c9c +0 -0
  31. package/.v8-cache/v24.7.0-x64-ef5a0af0/acc36e66 +0 -0
  32. package/.v8-cache/v24.7.0-x64-ef5a0af0/b3c2fab7 +0 -0
  33. package/.v8-cache/v24.7.0-x64-ef5a0af0/b8569d6b +0 -0
  34. package/.v8-cache/v24.7.0-x64-ef5a0af0/b8b073c4 +0 -0
  35. package/.v8-cache/v24.7.0-x64-ef5a0af0/bb489c89 +0 -0
  36. package/.v8-cache/v24.7.0-x64-ef5a0af0/c0b789d2 +0 -0
  37. package/.v8-cache/v24.7.0-x64-ef5a0af0/c314aece +0 -0
  38. package/.v8-cache/v24.7.0-x64-ef5a0af0/c46725e1 +0 -0
  39. package/.v8-cache/v24.7.0-x64-ef5a0af0/c794cb34 +0 -0
  40. package/.v8-cache/v24.7.0-x64-ef5a0af0/cd4ae980 +0 -0
  41. package/.v8-cache/v24.7.0-x64-ef5a0af0/cfc49f4f +0 -0
  42. package/.v8-cache/v24.7.0-x64-ef5a0af0/cff9434a +0 -0
  43. package/.v8-cache/v24.7.0-x64-ef5a0af0/d02dd6f7 +0 -0
  44. package/.v8-cache/v24.7.0-x64-ef5a0af0/dc86f000 +0 -0
  45. package/.v8-cache/v24.7.0-x64-ef5a0af0/e03e2acd +0 -0
  46. package/.v8-cache/v24.7.0-x64-ef5a0af0/e6735171 +0 -0
  47. package/.v8-cache/v24.7.0-x64-ef5a0af0/e6745c2b +0 -0
  48. package/.v8-cache/v24.7.0-x64-ef5a0af0/e6b99945 +0 -0
  49. package/.v8-cache/v24.7.0-x64-ef5a0af0/eeeebdfe +0 -0
  50. package/.v8-cache/v24.7.0-x64-ef5a0af0/f5417276 +0 -0
  51. package/.v8-cache/v24.7.0-x64-ef5a0af0/f6fd8125 +0 -0
  52. package/.v8-cache/v24.7.0-x64-ef5a0af0/f74d3518 +0 -0
  53. package/.v8-cache/v24.7.0-x64-ef5a0af0/f886deac +0 -0
  54. package/.v8-cache/v24.7.0-x64-ef5a0af0/fd0a2987 +0 -0
  55. package/.v8-cache/v24.7.0-x64-ef5a0af0/fe349423 +0 -0
  56. package/CHANGELOG.md +107 -0
  57. package/LICENSE +5 -0
  58. package/README.md +3 -6
  59. package/README_PSD.md +242 -81
  60. package/dist/abr.d.ts +11 -4
  61. package/dist/abr.js +363 -229
  62. package/dist/abr.js.map +1 -1
  63. package/dist/additionalInfo.d.ts +56 -2
  64. package/dist/additionalInfo.js +600 -364
  65. package/dist/additionalInfo.js.map +1 -1
  66. package/dist/ase.d.ts +6 -1
  67. package/dist/ase.js +61 -61
  68. package/dist/ase.js.map +1 -1
  69. package/dist/bundle.js +3096 -1541
  70. package/dist/descriptor.d.ts +25 -6
  71. package/dist/descriptor.js +380 -135
  72. package/dist/descriptor.js.map +1 -1
  73. package/dist/effectsHelpers.js +12 -12
  74. package/dist/effectsHelpers.js.map +1 -1
  75. package/dist/engineData.d.ts +3 -1
  76. package/dist/engineData.js +34 -13
  77. package/dist/engineData.js.map +1 -1
  78. package/dist/engineData2.js +50 -21
  79. package/dist/engineData2.js.map +1 -1
  80. package/dist/helpers.d.ts +6 -3
  81. package/dist/helpers.js +22 -6
  82. package/dist/helpers.js.map +1 -1
  83. package/dist/imageResources.d.ts +15 -3
  84. package/dist/imageResources.js +529 -144
  85. package/dist/imageResources.js.map +1 -1
  86. package/dist/index.js +1 -0
  87. package/dist/index.js.map +1 -1
  88. package/dist/jpeg.js +30 -10
  89. package/dist/jpeg.js.map +1 -1
  90. package/dist/psd.d.ts +154 -18
  91. package/dist/psd.js.map +1 -1
  92. package/dist/psdReader.d.ts +12 -2
  93. package/dist/psdReader.js +720 -421
  94. package/dist/psdReader.js.map +1 -1
  95. package/dist/psdWriter.d.ts +3 -0
  96. package/dist/psdWriter.js +167 -130
  97. package/dist/psdWriter.js.map +1 -1
  98. package/dist/text.js +143 -29
  99. package/dist/text.js.map +1 -1
  100. package/dist/utf8.js +36 -18
  101. package/dist/utf8.js.map +1 -1
  102. package/dist-es/abr.d.ts +11 -4
  103. package/dist-es/abr.js +363 -229
  104. package/dist-es/abr.js.map +1 -1
  105. package/dist-es/additionalInfo.d.ts +56 -2
  106. package/dist-es/additionalInfo.js +600 -364
  107. package/dist-es/additionalInfo.js.map +1 -1
  108. package/dist-es/ase.d.ts +6 -1
  109. package/dist-es/ase.js +61 -61
  110. package/dist-es/ase.js.map +1 -1
  111. package/dist-es/descriptor.d.ts +25 -6
  112. package/dist-es/descriptor.js +380 -135
  113. package/dist-es/descriptor.js.map +1 -1
  114. package/dist-es/effectsHelpers.js +12 -12
  115. package/dist-es/effectsHelpers.js.map +1 -1
  116. package/dist-es/engineData.d.ts +3 -1
  117. package/dist-es/engineData.js +34 -13
  118. package/dist-es/engineData.js.map +1 -1
  119. package/dist-es/engineData2.js +50 -21
  120. package/dist-es/engineData2.js.map +1 -1
  121. package/dist-es/helpers.d.ts +6 -3
  122. package/dist-es/helpers.js +22 -6
  123. package/dist-es/helpers.js.map +1 -1
  124. package/dist-es/imageResources.d.ts +15 -3
  125. package/dist-es/imageResources.js +529 -144
  126. package/dist-es/imageResources.js.map +1 -1
  127. package/dist-es/index.js +1 -0
  128. package/dist-es/index.js.map +1 -1
  129. package/dist-es/jpeg.js +30 -10
  130. package/dist-es/jpeg.js.map +1 -1
  131. package/dist-es/psd.d.ts +154 -18
  132. package/dist-es/psd.js.map +1 -1
  133. package/dist-es/psdReader.d.ts +12 -2
  134. package/dist-es/psdReader.js +720 -421
  135. package/dist-es/psdReader.js.map +1 -1
  136. package/dist-es/psdWriter.d.ts +3 -0
  137. package/dist-es/psdWriter.js +167 -130
  138. package/dist-es/psdWriter.js.map +1 -1
  139. package/dist-es/text.js +143 -29
  140. package/dist-es/text.js.map +1 -1
  141. package/dist-es/utf8.js +36 -18
  142. package/dist-es/utf8.js.map +1 -1
  143. package/package.json +2 -2
  144. package/src/abr.ts +380 -232
  145. package/src/additionalInfo.ts +781 -477
  146. package/src/ase.ts +66 -61
  147. package/src/descriptor.ts +386 -142
  148. package/src/effectsHelpers.ts +9 -9
  149. package/src/engineData.ts +36 -13
  150. package/src/engineData2.ts +468 -432
  151. package/src/helpers.ts +23 -5
  152. package/src/imageResources.ts +658 -167
  153. package/src/index.ts +1 -0
  154. package/src/jpeg.ts +27 -12
  155. package/src/psd.ts +158 -30
  156. package/src/psdReader.ts +732 -414
  157. package/src/psdWriter.ts +186 -132
  158. package/src/text.ts +139 -18
  159. package/src/utf8.ts +35 -21
@@ -12,6 +12,10 @@ var __rest = (this && this.__rest) || function (s, e) {
12
12
  };
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
14
  exports.supportedColorModes = void 0;
15
+ exports.getUtf8Decoder = getUtf8Decoder;
16
+ exports.getUtf8StrictDecoder = getUtf8StrictDecoder;
17
+ exports.getGbkStrictDecoder = getGbkStrictDecoder;
18
+ exports.decodeUtf8 = decodeUtf8;
15
19
  exports.createReader = createReader;
16
20
  exports.warnOrThrow = warnOrThrow;
17
21
  exports.readUint8 = readUint8;
@@ -53,12 +57,15 @@ exports.readDataZip = readDataZip;
53
57
  exports.readDataRLE = readDataRLE;
54
58
  exports.readSection = readSection;
55
59
  exports.readColor = readColor;
60
+ exports.readPatternSafe = readPatternSafe;
56
61
  exports.readPattern = readPattern;
62
+ exports.consumeMemory = consumeMemory;
57
63
  const pako_1 = require("pako");
58
64
  const helpers_1 = require("./helpers");
59
65
  const additionalInfo_1 = require("./additionalInfo");
66
+ const utf8_1 = require("./utf8");
60
67
  const imageResources_1 = require("./imageResources");
61
- exports.supportedColorModes = [0 /* ColorMode.Bitmap */, 1 /* ColorMode.Grayscale */, 3 /* ColorMode.RGB */, 2 /* ColorMode.Indexed */];
68
+ exports.supportedColorModes = [0 /* ColorMode.Bitmap */, 1 /* ColorMode.Grayscale */, 3 /* ColorMode.RGB */, 2 /* ColorMode.Indexed */, 4 /* ColorMode.CMYK */];
62
69
  const colorModes = ['bitmap', 'grayscale', 'indexed', 'RGB', 'CMYK', '', '', 'multichannel', 'duotone', 'lab'];
63
70
  function setupGrayscale(data) {
64
71
  const size = data.width * data.height * 4;
@@ -68,6 +75,37 @@ function setupGrayscale(data) {
68
75
  data.data[i + 2] = c;
69
76
  }
70
77
  }
78
+ function createTextDecoder(label, options) {
79
+ try {
80
+ return typeof TextDecoder !== 'undefined' ? new TextDecoder(label, options) : null;
81
+ }
82
+ catch (_a) {
83
+ return null; // encoding not supported
84
+ }
85
+ }
86
+ // non-fatal UTF-8 decoder, malformed byte sequences are replaced with U+FFFD
87
+ function getUtf8Decoder(reader) {
88
+ if (reader.utf8Decoder === undefined)
89
+ reader.utf8Decoder = createTextDecoder('utf-8', { ignoreBOM: true });
90
+ return reader.utf8Decoder;
91
+ }
92
+ // UTF-8 decoder that throws on malformed byte sequences
93
+ function getUtf8StrictDecoder(reader) {
94
+ if (reader.utf8StrictDecoder === undefined)
95
+ reader.utf8StrictDecoder = createTextDecoder('utf-8', { ignoreBOM: true, fatal: true });
96
+ return reader.utf8StrictDecoder;
97
+ }
98
+ // GBK decoder that throws on malformed byte sequences
99
+ function getGbkStrictDecoder(reader) {
100
+ if (reader.gbkDecoder === undefined)
101
+ reader.gbkDecoder = createTextDecoder('gbk', { fatal: true });
102
+ return reader.gbkDecoder;
103
+ }
104
+ function decodeUtf8(reader, buffer) {
105
+ // native decoder is faster only for longer strings
106
+ const decoder = buffer.byteLength > 1000 ? getUtf8Decoder(reader) : null;
107
+ return decoder ? decoder.decode(buffer) : (0, utf8_1.decodeString)(buffer);
108
+ }
71
109
  function createReader(buffer, offset, length) {
72
110
  const view = new DataView(buffer, offset, length);
73
111
  return { view, offset: 0, strict: false, debug: false, large: false, globalAlpha: false, log: console.log };
@@ -125,22 +163,24 @@ function readFixedPoint32(reader) {
125
163
  function readFixedPointPath32(reader) {
126
164
  return readInt32(reader) / (1 << 24);
127
165
  }
166
+ // NOTE: returned array may alias the reader's underlying buffer, never write into it
128
167
  function readBytes(reader, length) {
129
- const start = reader.view.byteOffset + reader.offset;
168
+ const start = reader.offset;
169
+ const viewLength = reader.view.byteLength;
130
170
  reader.offset += length;
131
- if ((start + length) > reader.view.buffer.byteLength) {
171
+ if ((start + length) > viewLength) {
132
172
  // fix for broken PSD files that are missing part of file at the end
133
173
  warnOrThrow(reader, 'Reading bytes exceeding buffer length');
134
174
  if (length > (100 * 1024 * 1024))
135
175
  throw new Error('Reading past end of file'); // limit to 100MB
136
176
  const result = new Uint8Array(length);
137
- const len = Math.min(length, reader.view.byteLength - start);
177
+ const len = Math.min(length, viewLength - start);
138
178
  if (len > 0)
139
- result.set(new Uint8Array(reader.view.buffer, start, len));
179
+ result.set(new Uint8Array(reader.view.buffer, reader.view.byteOffset + start, len));
140
180
  return result;
141
181
  }
142
182
  else {
143
- return new Uint8Array(reader.view.buffer, start, length);
183
+ return new Uint8Array(reader.view.buffer, reader.view.byteOffset + start, length);
144
184
  }
145
185
  }
146
186
  function readSignature(reader) {
@@ -165,31 +205,52 @@ function readUnicodeString(reader) {
165
205
  const length = readUint32(reader);
166
206
  return readUnicodeStringWithLength(reader, length);
167
207
  }
168
- function readUnicodeStringWithLength(reader, length) {
208
+ const STRING_CHUNK_SIZE = 0x2000;
209
+ function checkReadBounds(reader, size) {
210
+ if (reader.offset < 0 || (reader.offset + size) > reader.view.byteLength) {
211
+ throw new RangeError('Offset is outside the bounds of the DataView');
212
+ }
213
+ }
214
+ function readUtf16String(reader, length, littleEndian) {
215
+ if (!(length > 0))
216
+ return '';
217
+ checkReadBounds(reader, length * 2);
218
+ const view = reader.view;
219
+ const codes = new Uint16Array(Math.min(length, STRING_CHUNK_SIZE));
220
+ let offset = reader.offset;
169
221
  let text = '';
170
- while (length--) {
171
- const value = readUint16(reader);
172
- if (value || length > 0) { // remove trailing \0
173
- text += String.fromCharCode(value);
222
+ for (let i = 0; i < length; i += STRING_CHUNK_SIZE) {
223
+ const count = Math.min(STRING_CHUNK_SIZE, length - i);
224
+ for (let j = 0; j < count; j++, offset += 2) {
225
+ codes[j] = view.getUint16(offset, littleEndian);
174
226
  }
227
+ text += String.fromCharCode.apply(null, (count === codes.length ? codes : codes.subarray(0, count)));
175
228
  }
229
+ reader.offset = offset;
230
+ // remove trailing \0
231
+ if (text.charCodeAt(text.length - 1) === 0)
232
+ text = text.substring(0, text.length - 1);
176
233
  return text;
177
234
  }
178
- function readUnicodeStringWithLengthLE(reader, length) {
235
+ function bytesToString(bytes) {
179
236
  let text = '';
180
- while (length--) {
181
- const value = readUint16LE(reader);
182
- if (value || length > 0) { // remove trailing \0
183
- text += String.fromCharCode(value);
184
- }
237
+ for (let i = 0; i < bytes.length; i += STRING_CHUNK_SIZE) {
238
+ text += String.fromCharCode.apply(null, bytes.subarray(i, i + STRING_CHUNK_SIZE));
185
239
  }
186
240
  return text;
187
241
  }
242
+ function readUnicodeStringWithLength(reader, length) {
243
+ return readUtf16String(reader, length, false);
244
+ }
245
+ function readUnicodeStringWithLengthLE(reader, length) {
246
+ return readUtf16String(reader, length, true);
247
+ }
188
248
  function readAsciiString(reader, length) {
189
- let text = '';
190
- while (length--) {
191
- text += String.fromCharCode(readUint8(reader));
192
- }
249
+ if (!(length > 0))
250
+ return '';
251
+ checkReadBounds(reader, length);
252
+ const text = bytesToString(new Uint8Array(reader.view.buffer, reader.view.byteOffset + reader.offset, length));
253
+ reader.offset += length;
193
254
  return text;
194
255
  }
195
256
  function skipBytes(reader, count) {
@@ -203,12 +264,7 @@ function checkSignature(reader, a, b) {
203
264
  }
204
265
  }
205
266
  function readShortString(reader, length) {
206
- const buffer = readBytes(reader, length);
207
- let result = '';
208
- for (let i = 0; i < buffer.length; i++) {
209
- result += String.fromCharCode(buffer[i]);
210
- }
211
- return result;
267
+ return bytesToString(readBytes(reader, length));
212
268
  }
213
269
  function isValidSignature(sig) {
214
270
  return sig === '8BIM' || sig === 'MeSa' || sig === 'AgHg' || sig === 'PHUT' || sig === 'DCSR';
@@ -229,7 +285,8 @@ function readPsd(reader, readOptions = {}) {
229
285
  const maxSize = version === 1 ? 30000 : 300000;
230
286
  if (width > maxSize || height > maxSize)
231
287
  throw new Error(`Invalid size: ${width}x${height}`);
232
- if (channels > 16)
288
+ // up to 56 channels plus transparency of composite image (Photoshop CS3 writes 57 channels for 53 alpha channels)
289
+ if (channels < 1 || channels > 57)
233
290
  throw new Error(`Invalid channel count: ${channels}`);
234
291
  if (![1, 8, 16, 32].includes(bitsPerChannel))
235
292
  throw new Error(`Invalid bitsPerChannel: ${bitsPerChannel}`);
@@ -239,6 +296,7 @@ function readPsd(reader, readOptions = {}) {
239
296
  Object.assign(reader, readOptions);
240
297
  reader.large = version === 2;
241
298
  reader.globalAlpha = false;
299
+ reader.layersInGlobalInfo = false;
242
300
  if (!('totalMemoryLimit' in reader)) { // setting totalMemoryLimit to undefined explicitly disables memory limit
243
301
  reader.totalMemoryLimit = 2 * 1024 * 1024 * 1024; // default 2GB memory limit
244
302
  }
@@ -266,17 +324,21 @@ function readPsd(reader, readOptions = {}) {
266
324
  });
267
325
  // image resources
268
326
  const imageResources = {};
327
+ let unreadResourceBytes = 0;
269
328
  readSection(reader, 1, left => {
270
- while (left() > 0) {
271
- realignWithSignature(reader, isValidSignature);
329
+ // stop if there's not enough data left for resource header (some files have few trailing bytes in this section)
330
+ while (left() >= 12) {
331
+ const signature = realignWithSignature(reader, isValidSignature);
272
332
  const id = readUint16(reader);
273
- readPascalString(reader, 2); // name
333
+ const nameLength = readUint8(reader);
334
+ const name = readBytes(reader, nameLength);
335
+ skipBytes(reader, (nameLength + 1) % 2); // pad to even size
274
336
  readSection(reader, 2, left => {
275
337
  const handler = imageResources_1.resourceHandlersMap[id];
276
338
  const skip = id === 1036 && !!reader.skipThumbnail;
277
339
  if (handler && !skip) {
278
340
  try {
279
- handler.read(reader, imageResources, left);
341
+ handler.read(reader, imageResources, left, { signature, id, name, width, height });
280
342
  }
281
343
  catch (e) {
282
344
  if (reader.throwForMissingFeatures)
@@ -285,13 +347,20 @@ function readPsd(reader, readOptions = {}) {
285
347
  }
286
348
  }
287
349
  else {
288
- // options.logMissingFeatures && console.log(`Unhandled image resource: ${id} (${left()})`);
350
+ reader.logMissingFeatures && reader.log(`Unhandled image resource: ${id} (${left()})`);
289
351
  skipBytes(reader, left());
290
352
  }
291
353
  });
292
354
  }
355
+ unreadResourceBytes = left();
356
+ skipBytes(reader, left());
293
357
  });
294
- const { layersGroup, layerGroupsEnabledId } = imageResources, rest = __rest(imageResources, ["layersGroup", "layerGroupsEnabledId"]);
358
+ // some files have image resources section length few bytes larger than the data, layer and mask info section
359
+ // starts right after the last resource in that case
360
+ if (unreadResourceBytes && !sectionFits(reader, reader.offset) && sectionFits(reader, reader.offset - unreadResourceBytes)) {
361
+ reader.offset -= unreadResourceBytes;
362
+ }
363
+ const { layersGroup, layerGroupsEnabledId, savedPathsOrigination } = imageResources, rest = __rest(imageResources, ["layersGroup", "layerGroupsEnabledId", "savedPathsOrigination"]);
295
364
  if (Object.keys(rest).length) {
296
365
  psd.imageResources = rest;
297
366
  }
@@ -331,7 +400,12 @@ function readPsd(reader, readOptions = {}) {
331
400
  const skipComposite = reader.skipCompositeImageData && (reader.skipLayerImageData || hasChildren);
332
401
  if (!skipComposite) {
333
402
  if (reader.useRawData) {
334
- psd.rawCompositeData = new Uint8Array(reader.view.buffer, reader.view.byteOffset + reader.offset);
403
+ const rawOffset = Math.min(reader.offset, reader.view.byteLength);
404
+ const rawCompositeData = new Uint8Array(reader.view.buffer, reader.view.byteOffset + rawOffset, reader.view.byteLength - rawOffset);
405
+ psd.rawCompositeData = rawCompositeData;
406
+ psd.rawCompositeDataInfo = { large: reader.large, globalAlpha: reader.globalAlpha };
407
+ if (reader.layersInGlobalInfo)
408
+ psd.rawCompositeDataInfo.layersInGlobalInfo = true;
335
409
  }
336
410
  else {
337
411
  const imageData = readImageData(reader, psd);
@@ -343,9 +417,6 @@ function readPsd(reader, readOptions = {}) {
343
417
  }
344
418
  }
345
419
  }
346
- // TODO: show converted color mode instead of original PSD file color mode
347
- // but add option to preserve file color mode (need to return image data instead of canvas in that case)
348
- // psd.colorMode = ColorMode.RGB; // we convert all color modes to RGB
349
420
  return psd;
350
421
  }
351
422
  function readLayerInfo(reader, psd, imageResources) {
@@ -386,7 +457,9 @@ function readLayerInfo(reader, psd, imageResources) {
386
457
  stack.push(l);
387
458
  }
388
459
  else if (type === 3 /* SectionDividerType.BoundingSectionDivider */) {
389
- stack.pop();
460
+ // ignore unmatched section dividers, never pop the root
461
+ if (stack.length > 1)
462
+ stack.pop();
390
463
  // this was workaround because I didn't know what `lsdk` section was, now it's probably not needed anymore
391
464
  // } else if (l.name === '</Layer group>' && !l.sectionDivider && !l.top && !l.left && !l.bottom && !l.right) {
392
465
  // // sometimes layer group terminator doesn't have sectionDivider, so we just guess here (PS bug ?)
@@ -419,9 +492,12 @@ function readLayerRecord(reader, psd, imageResources) {
419
492
  }
420
493
  checkSignature(reader, '8BIM');
421
494
  const blendMode = readSignature(reader);
422
- if (!helpers_1.toBlendMode[blendMode])
423
- throw new Error(`Invalid blend mode: '${blendMode}'`);
424
- layer.blendMode = helpers_1.toBlendMode[blendMode];
495
+ if (!helpers_1.toBlendMode[blendMode]) {
496
+ if (reader.throwForMissingFeatures)
497
+ throw new Error(`Invalid blend mode: '${blendMode}'`);
498
+ reader.logMissingFeatures && reader.log(`Unknown blend mode: '${blendMode}', using normal blend mode`);
499
+ }
500
+ layer.blendMode = helpers_1.toBlendMode[blendMode] || 'normal';
425
501
  layer.opacity = readUint8(reader) / 0xff;
426
502
  layer.clipping = readUint8(reader) === 1;
427
503
  const flags = readUint8(reader);
@@ -449,11 +525,20 @@ function readLayerRecord(reader, psd, imageResources) {
449
525
  });
450
526
  return { layer, channels };
451
527
  }
528
+ // negative sizes are treated as empty (Photoshop writes them for some empty layers, like 0,0,-1,0 bounds of fill layer)
529
+ // checks if length of section (layer and mask info) at given offset fits in the file
530
+ function sectionFits(reader, offset) {
531
+ const lengthSize = reader.large ? 8 : 4;
532
+ if (offset < 0 || offset + lengthSize > reader.view.byteLength)
533
+ return false;
534
+ const length = reader.large ? reader.view.getUint32(offset) * 0x100000000 + reader.view.getUint32(offset + 4) : reader.view.getUint32(offset);
535
+ return offset + lengthSize + length <= reader.view.byteLength;
536
+ }
452
537
  function isValidBoxSize(box, reader) {
453
538
  const width = (box.right || 0) - (box.left || 0);
454
539
  const height = (box.bottom || 0) - (box.top || 0);
455
540
  const maxSize = reader.large ? 300000 : 30000;
456
- return width >= 0 && height >= 0 && width <= maxSize && height <= maxSize;
541
+ return width <= maxSize && height <= maxSize;
457
542
  }
458
543
  function readLayerMaskData(reader, layer) {
459
544
  return readSection(reader, 1, left => {
@@ -472,7 +557,7 @@ function readLayerMaskData(reader, layer) {
472
557
  mask.positionRelativeToLayer = (flags & 1 /* LayerMaskFlags.PositionRelativeToLayer */) !== 0;
473
558
  mask.disabled = (flags & 2 /* LayerMaskFlags.LayerMaskDisabled */) !== 0;
474
559
  mask.fromVectorData = (flags & 8 /* LayerMaskFlags.LayerMaskFromRenderingOtherData */) !== 0;
475
- if (left() >= 18) {
560
+ if (hasRealMaskData(reader, flags, left())) {
476
561
  const realMask = {};
477
562
  layer.realMask = realMask;
478
563
  const realFlags = readUint8(reader);
@@ -501,6 +586,43 @@ function readLayerMaskData(reader, layer) {
501
586
  skipBytes(reader, left());
502
587
  });
503
588
  }
589
+ function maskParamsSize(params) {
590
+ return 1 +
591
+ ((params & 1 /* MaskParams.UserMaskDensity */) ? 1 : 0) +
592
+ ((params & 2 /* MaskParams.UserMaskFeather */) ? 8 : 0) +
593
+ ((params & 4 /* MaskParams.VectorMaskDensity */) ? 1 : 0) +
594
+ ((params & 8 /* MaskParams.VectorMaskFeather */) ? 8 : 0);
595
+ }
596
+ // checks if data at offset is mask parameters followed by up to 3 bytes of zero padding, ending exactly at offset + left
597
+ function isMaskParamsLayout(view, offset, left) {
598
+ if (left < 1)
599
+ return false;
600
+ const params = view.getUint8(offset);
601
+ if (params === 0 || (params & ~0x0f))
602
+ return false; // only 4 parameter flags exist
603
+ const size = maskParamsSize(params);
604
+ if (left < size || left - size > 3)
605
+ return false;
606
+ for (let i = size; i < left; i++) {
607
+ if (view.getUint8(offset + i) !== 0)
608
+ return false;
609
+ }
610
+ return true;
611
+ }
612
+ // real mask data (18 bytes) is followed by mask parameters (if flag is set) and padding
613
+ function hasRealMaskData(reader, flags, left) {
614
+ if (left < 18)
615
+ return false;
616
+ if (!(flags & 16 /* LayerMaskFlags.MaskHasParametersAppliedToIt */))
617
+ return true;
618
+ const { view, offset } = reader;
619
+ if (isMaskParamsLayout(view, offset, left))
620
+ return false;
621
+ if (isMaskParamsLayout(view, offset + 18, left - 18))
622
+ return true;
623
+ // unknown layout, assume real mask if there's enough data for it (and mask parameters)
624
+ return left === 18 || left - 18 >= maskParamsSize(view.getUint8(offset + 18));
625
+ }
504
626
  function readBlendingRange(reader) {
505
627
  return [readUint8(reader), readUint8(reader), readUint8(reader), readUint8(reader)];
506
628
  }
@@ -535,9 +657,9 @@ function readLayerChannelImageData(reader, psd, layer, channels) {
535
657
  reader.offset -= 1;
536
658
  compression = readUint16(reader);
537
659
  }
538
- // try to fix broken files where there's 1 byte shift of channel
660
+ // try to fix broken files where there's 1 byte shift of channel (in the other direction)
539
661
  if (compression > 3) {
540
- reader.offset -= 3;
662
+ reader.offset -= 4;
541
663
  compression = readUint16(reader);
542
664
  }
543
665
  if (compression > 3)
@@ -567,8 +689,13 @@ function getCompositeImageData(psd) {
567
689
  if (!data)
568
690
  return undefined;
569
691
  const reader = createReader(data.buffer, data.byteOffset, data.byteLength);
570
- const imageData = readImageData(reader, psd);
571
- return imageData;
692
+ const info = psd.rawCompositeDataInfo;
693
+ if (info) {
694
+ reader.large = info.large;
695
+ reader.globalAlpha = info.globalAlpha;
696
+ reader.layersInGlobalInfo = !!info.layersInGlobalInfo;
697
+ }
698
+ return readImageData(reader, psd);
572
699
  }
573
700
  function getCompositeCanvas(psd) {
574
701
  return imageDataToCanvasSafe(getCompositeImageData(psd));
@@ -610,18 +737,18 @@ function decodeLayerPixels(layer, useImageData) {
610
737
  function decodeLayerImageData(layer, options) {
611
738
  var _a, _b, _c;
612
739
  let { throwForMissingFeatures, useImageData } = options;
613
- const imageData = getDataFromLayer(layer, LayerDataType.Layer, throwForMissingFeatures, options.totalMemoryLimit);
740
+ const imageData = getDataFromLayer(layer, LayerDataType.Layer, throwForMissingFeatures, options.totalMemoryLimit, options);
614
741
  setImageDataOrCanvas(layer, imageData, useImageData);
615
742
  if (options.totalMemoryLimit !== undefined)
616
743
  options.totalMemoryLimit -= (_a = imageData === null || imageData === void 0 ? void 0 : imageData.data.byteLength) !== null && _a !== void 0 ? _a : 0;
617
744
  if (layer.mask) {
618
- const maskData = getDataFromLayer(layer, LayerDataType.Mask, throwForMissingFeatures, options.totalMemoryLimit);
745
+ const maskData = getDataFromLayer(layer, LayerDataType.Mask, throwForMissingFeatures, options.totalMemoryLimit, options);
619
746
  setImageDataOrCanvas(layer.mask, maskData, useImageData);
620
747
  if (options.totalMemoryLimit !== undefined)
621
748
  options.totalMemoryLimit -= (_b = maskData === null || maskData === void 0 ? void 0 : maskData.data.byteLength) !== null && _b !== void 0 ? _b : 0;
622
749
  }
623
750
  if (layer.realMask) {
624
- const maskData = getDataFromLayer(layer, LayerDataType.RealMask, throwForMissingFeatures, options.totalMemoryLimit);
751
+ const maskData = getDataFromLayer(layer, LayerDataType.RealMask, throwForMissingFeatures, options.totalMemoryLimit, options);
625
752
  setImageDataOrCanvas(layer.realMask, maskData, useImageData);
626
753
  if (options.totalMemoryLimit !== undefined)
627
754
  options.totalMemoryLimit -= (_c = maskData === null || maskData === void 0 ? void 0 : maskData.data.byteLength) !== null && _c !== void 0 ? _c : 0;
@@ -634,7 +761,8 @@ var LayerDataType;
634
761
  LayerDataType[LayerDataType["Mask"] = 1] = "Mask";
635
762
  LayerDataType[LayerDataType["RealMask"] = 2] = "RealMask";
636
763
  })(LayerDataType || (LayerDataType = {}));
637
- function getDataFromLayer(layer, read, throwForMissingFeatures, memoryLimit) {
764
+ function getDataFromLayer(layer, read, throwForMissingFeatures, memoryLimit, logOptions) {
765
+ var _a;
638
766
  if (!layer.rawData)
639
767
  return undefined;
640
768
  const { colorMode, bitsPerChannel, channels, large } = layer.rawData;
@@ -646,22 +774,23 @@ function getDataFromLayer(layer, read, throwForMissingFeatures, memoryLimit) {
646
774
  let initializedAlpha = false;
647
775
  if (layerWidth && layerHeight && read === LayerDataType.Layer) {
648
776
  if (cmyk) {
649
- if (bitsPerChannel !== 8)
650
- throw new Error('bitsPerChannel Not supproted');
651
- imageData = { width: layerWidth, height: layerHeight, data: new Uint8ClampedArray(layerWidth * layerHeight * 5) };
777
+ if (bitsPerChannel !== 8 && bitsPerChannel !== 16)
778
+ throw new Error('bitsPerChannel not supported');
779
+ imageData = createImageDataBitDepth(layerWidth, layerHeight, bitsPerChannel, 5, memoryLimit);
652
780
  }
653
781
  else {
654
782
  imageData = createImageDataBitDepth(layerWidth, layerHeight, bitsPerChannel, 4, memoryLimit);
655
783
  }
656
784
  }
657
- if (helpers_1.RAW_IMAGE_DATA) { // TODO: use layer.rawData instead
658
- layer.imageDataRaw = [];
659
- layer.imageDataRawCompression = [];
660
- }
661
785
  for (const { id, compression, data } of channels) {
662
786
  if (!data)
663
787
  continue;
664
788
  const dataReader = createReader(data.buffer, data.byteOffset, data.byteLength);
789
+ dataReader.debug = !!(logOptions === null || logOptions === void 0 ? void 0 : logOptions.debug);
790
+ if (logOptions === null || logOptions === void 0 ? void 0 : logOptions.log)
791
+ dataReader.log = logOptions.log;
792
+ if (memoryLimit !== undefined)
793
+ dataReader.totalMemoryLimit = Math.max(0, memoryLimit - ((_a = imageData === null || imageData === void 0 ? void 0 : imageData.data.byteLength) !== null && _a !== void 0 ? _a : 0));
665
794
  if (id === -2 /* ChannelID.UserMask */ || id === -3 /* ChannelID.RealUserMask */) {
666
795
  if (id === -2 /* ChannelID.UserMask */ && read !== LayerDataType.Mask)
667
796
  continue;
@@ -674,17 +803,9 @@ function getDataFromLayer(layer, read, throwForMissingFeatures, memoryLimit) {
674
803
  const maskHeight = Math.max(0, (mask.bottom || 0) - (mask.top || 0));
675
804
  if (maskWidth && maskHeight) {
676
805
  maskData = createImageDataBitDepth(maskWidth, maskHeight, bitsPerChannel, 4, memoryLimit);
806
+ if (memoryLimit !== undefined)
807
+ dataReader.totalMemoryLimit = Math.max(0, dataReader.totalMemoryLimit - maskData.data.byteLength);
677
808
  readData(dataReader, data.byteLength, maskData, compression, maskWidth, maskHeight, bitsPerChannel, 0, large, 4);
678
- if (helpers_1.RAW_IMAGE_DATA) { // TODO: use layer.rawData instead
679
- if (id === -2 /* ChannelID.UserMask */) {
680
- layer.maskDataRawCompression = compression;
681
- layer.maskDataRaw = data;
682
- }
683
- else {
684
- layer.realMaskDataRawCompression = compression;
685
- layer.realMaskDataRaw = data;
686
- }
687
- }
688
809
  setupGrayscale(maskData);
689
810
  resetAlpha(maskData, false);
690
811
  }
@@ -701,24 +822,20 @@ function getDataFromLayer(layer, read, throwForMissingFeatures, memoryLimit) {
701
822
  }
702
823
  }
703
824
  readData(dataReader, data.byteLength, targetData, compression, layerWidth, layerHeight, bitsPerChannel, offset, large, cmyk ? 5 : 4);
704
- if (helpers_1.RAW_IMAGE_DATA) { // TODO: use layer.rawData instead
705
- layer.imageDataRawCompression[id] = compression;
706
- layer.imageDataRaw[id] = data;
707
- }
708
- if (targetData && colorMode === 1 /* ColorMode.Grayscale */) {
709
- setupGrayscale(targetData);
710
- }
711
825
  }
712
826
  if (id === -1 /* ChannelID.Transparency */) {
713
827
  initializedAlpha = true;
714
828
  }
715
829
  }
716
830
  if (imageData) {
831
+ if (colorMode === 1 /* ColorMode.Grayscale */)
832
+ setupGrayscale(imageData);
717
833
  if (!initializedAlpha)
718
834
  resetAlpha(imageData, cmyk);
719
835
  if (cmyk) {
720
836
  const cmykData = imageData;
721
- imageData = (0, helpers_1.createImageData)(cmykData.width, cmykData.height);
837
+ const rgbMemoryLimit = memoryLimit === undefined ? undefined : Math.max(0, memoryLimit - cmykData.data.byteLength);
838
+ imageData = createImageDataBitDepth(cmykData.width, cmykData.height, bitsPerChannel, 4, rgbMemoryLimit);
722
839
  cmykToRgb(cmykData, imageData, false);
723
840
  }
724
841
  }
@@ -729,7 +846,7 @@ function readData(reader, length, pixels, compression, width, height, bitDepth,
729
846
  return;
730
847
  if (compression === 0 /* Compression.RawData */) {
731
848
  if (length !== (width * height * Math.floor(bitDepth / 8))) {
732
- reader.log(`Invalid length (${length}, ${width * height * Math.floor(bitDepth / 8)})`);
849
+ warnOrThrow(reader, `Invalid length (${length}, ${width * height * Math.floor(bitDepth / 8)})`);
733
850
  }
734
851
  const data = readBytes(reader, length);
735
852
  readDataRaw(data, pixels, bitDepth, step, offset);
@@ -740,11 +857,11 @@ function readData(reader, length, pixels, compression, width, height, bitDepth,
740
857
  }
741
858
  else if (compression === 2 /* Compression.ZipWithoutPrediction */) {
742
859
  const data = readBytes(reader, length);
743
- readDataZip(data, pixels, width, height, bitDepth, step, offset, false);
860
+ readDataZip(data, pixels, width, height, bitDepth, step, offset, false, reader);
744
861
  }
745
862
  else if (compression === 3 /* Compression.ZipWithPrediction */) {
746
863
  const data = readBytes(reader, length);
747
- readDataZip(data, pixels, width, height, bitDepth, step, offset, true);
864
+ readDataZip(data, pixels, width, height, bitDepth, step, offset, true, reader);
748
865
  }
749
866
  else {
750
867
  throw new Error(`Invalid Compression type: ${compression}`);
@@ -759,7 +876,7 @@ function readGlobalLayerMaskInfo(reader) {
759
876
  const colorSpace2 = readUint16(reader);
760
877
  const colorSpace3 = readUint16(reader);
761
878
  const colorSpace4 = readUint16(reader);
762
- const opacity = readUint16(reader) / 0xff;
879
+ const opacity = readUint16(reader) / 100; // 0 - 100
763
880
  const kind = readUint8(reader);
764
881
  skipBytes(reader, left()); // 3 bytes of padding ?
765
882
  return { overlayColorSpace, colorSpace1, colorSpace2, colorSpace3, colorSpace4, opacity, kind };
@@ -791,7 +908,15 @@ function readAdditionalLayerInfo(reader, target, psd, imageResources) {
791
908
  const sig = realignWithSignature(reader, isValidAdditionalInfoSignature);
792
909
  const key = readSignature(reader);
793
910
  // `largeAdditionalInfoKeys` fallback, because some keys don't have 8B64 signature even when they are 64bit
794
- const u64 = sig === '8B64' || (reader.large && helpers_1.largeAdditionalInfoKeys.indexOf(key) !== -1);
911
+ let u64 = sig === '8B64' || (reader.large && helpers_1.largeAdditionalInfoKeys.indexOf(key) !== -1);
912
+ // these keys were written by older versions of this library with 32bit length and 8BIM signature,
913
+ // 64bit length always has high 32bits set to 0 (lengths over 4GB are not supported)
914
+ if (reader.large && sig === '8BIM' && helpers_1.largeAdditionalInfoKeysObserved.indexOf(key) !== -1) {
915
+ // 32bit length of 0 is followed by signature of the next block, 64bit length would continue with its low 32bits
916
+ const next = reader.offset + 8 <= reader.view.byteLength ? reader.view.getUint32(reader.offset + 4) : 0;
917
+ const nextIsSignature = next === 0x3842494d /* 8BIM */ || next === 0x38423634 /* 8B64 */;
918
+ u64 = reader.view.getUint32(reader.offset) === 0 && !nextIsSignature;
919
+ }
795
920
  readSection(reader, 2, left => {
796
921
  const handler = additionalInfo_1.infoHandlersMap[key];
797
922
  if (handler) {
@@ -804,7 +929,7 @@ function readAdditionalLayerInfo(reader, target, psd, imageResources) {
804
929
  }
805
930
  }
806
931
  else {
807
- reader.logMissingFeatures && reader.log(`Unhandled additional info: ${key}`);
932
+ reader.logMissingFeatures && key !== 'GenI' && key !== 'OCIO' && key !== 'CAI ' && reader.log(`Unhandled additional info: ${key}`);
808
933
  skipBytes(reader, left());
809
934
  }
810
935
  if (left()) {
@@ -836,14 +961,15 @@ function createImageDataBitDepth(width, height, bitDepth, channels, memoryLimit)
836
961
  }
837
962
  }
838
963
  function readImageData(reader, psd) {
839
- var _a;
964
+ var _a, _b, _c;
840
965
  const compression = readUint16(reader);
841
966
  const bitsPerChannel = (_a = psd.bitsPerChannel) !== null && _a !== void 0 ? _a : 8;
967
+ const { width, height } = psd;
842
968
  if (exports.supportedColorModes.indexOf(psd.colorMode) === -1)
843
969
  throw new Error(`Color mode not supported: ${psd.colorMode}`);
844
970
  if (compression !== 0 /* Compression.RawData */ && compression !== 1 /* Compression.RleCompressed */)
845
971
  throw new Error(`Compression type not supported: ${compression}`);
846
- const imageData = createImageDataBitDepth(psd.width, psd.height, bitsPerChannel, 4, reader.totalMemoryLimit);
972
+ const imageData = createImageDataBitDepth(width, height, bitsPerChannel, 4, reader.totalMemoryLimit);
847
973
  if (reader.totalMemoryLimit !== undefined)
848
974
  reader.totalMemoryLimit -= imageData.data.byteLength;
849
975
  (0, helpers_1.resetImageData)(imageData);
@@ -851,47 +977,23 @@ function readImageData(reader, psd) {
851
977
  case 0 /* ColorMode.Bitmap */: {
852
978
  if (bitsPerChannel !== 1)
853
979
  throw new Error('Invalid bitsPerChannel for bitmap color mode');
980
+ const rowBytes = Math.ceil(width / 8);
854
981
  let bytes;
855
982
  if (compression === 0 /* Compression.RawData */) {
856
- bytes = readBytes(reader, Math.ceil(psd.width / 8) * psd.height);
857
- }
858
- else if (compression === 1 /* Compression.RleCompressed */) {
859
- bytes = new Uint8Array(psd.width * psd.height);
860
- readDataRLE(reader, { data: bytes, width: psd.width, height: psd.height }, psd.width, psd.height, 8, 1, [0], reader.large);
983
+ bytes = readBytes(reader, rowBytes * height);
861
984
  }
862
985
  else {
863
- throw new Error(`Compression not supported: ${compression}`);
986
+ bytes = new Uint8Array(rowBytes * height);
987
+ readDataRLE(reader, { data: bytes, width: rowBytes, height }, rowBytes, height, 8, 1, [0], reader.large);
864
988
  }
865
- (0, helpers_1.decodeBitmap)(bytes, imageData.data, psd.width, psd.height);
989
+ (0, helpers_1.decodeBitmap)(bytes, imageData.data, width, height);
866
990
  break;
867
991
  }
868
992
  case 3 /* ColorMode.RGB */:
869
993
  case 1 /* ColorMode.Grayscale */: {
870
- const channels = psd.colorMode === 1 /* ColorMode.Grayscale */ ? [0] : [0, 1, 2];
871
- if (psd.channels && psd.channels > 3) {
872
- for (let i = 3; i < psd.channels; i++) {
873
- // TODO: store these channels in additional image data
874
- channels.push(i);
875
- }
876
- }
877
- else if (reader.globalAlpha) {
878
- channels.push(3);
879
- }
880
- if (compression === 0 /* Compression.RawData */) {
881
- for (let i = 0; i < channels.length; i++) {
882
- const data = readBytes(reader, psd.width * psd.height * Math.floor(bitsPerChannel / 8));
883
- readDataRaw(data, imageData, bitsPerChannel, 4, channels[i]);
884
- }
885
- }
886
- else if (compression === 1 /* Compression.RleCompressed */) {
887
- const start = reader.offset;
888
- readDataRLE(reader, imageData, psd.width, psd.height, bitsPerChannel, 4, channels, reader.large);
889
- if (helpers_1.RAW_IMAGE_DATA)
890
- psd.imageDataRaw = new Uint8Array(reader.view.buffer, reader.view.byteOffset + start, reader.offset - start);
891
- }
892
- else {
893
- throw new Error(`Compression not supported: ${compression}`);
894
- }
994
+ const colorChannels = psd.colorMode === 1 /* ColorMode.Grayscale */ ? 1 : 3;
995
+ const offsets = compositeChannelOffsets(reader, psd, colorChannels, 3);
996
+ readCompositeChannels(reader, compression, imageData, bitsPerChannel, 4, offsets);
895
997
  if (psd.colorMode === 1 /* ColorMode.Grayscale */) {
896
998
  setupGrayscale(imageData);
897
999
  }
@@ -899,288 +1001,450 @@ function readImageData(reader, psd) {
899
1001
  }
900
1002
  case 2 /* ColorMode.Indexed */: {
901
1003
  if (bitsPerChannel !== 8)
902
- throw new Error('bitsPerChannel Not supproted');
903
- if (psd.channels !== 1)
904
- throw new Error('Invalid channel count');
1004
+ throw new Error('bitsPerChannel not supported');
905
1005
  if (!psd.palette)
906
1006
  throw new Error('Missing color palette');
907
- if (compression === 0 /* Compression.RawData */) {
908
- throw new Error(`Compression not supported: ${compression}`);
909
- }
910
- else if (compression === 1 /* Compression.RleCompressed */) {
911
- const indexedImageData = {
912
- width: imageData.width,
913
- height: imageData.height,
914
- data: new Uint8Array(imageData.width * imageData.height),
915
- };
916
- readDataRLE(reader, indexedImageData, psd.width, psd.height, bitsPerChannel, 1, [0], reader.large);
917
- indexedToRgb(indexedImageData, imageData, psd.palette);
918
- }
919
- else {
920
- throw new Error(`Compression not supported: ${compression}`);
921
- }
1007
+ const offsets = compositeChannelOffsets(reader, psd, 1, -1);
1008
+ const indexedImageData = { width, height, data: new Uint8Array(width * height) };
1009
+ readCompositeChannels(reader, compression, indexedImageData, 8, 1, offsets);
1010
+ indexedToRgb(indexedImageData, imageData, psd.palette, (_c = (_b = psd.imageResources) === null || _b === void 0 ? void 0 : _b.transparencyIndex) !== null && _c !== void 0 ? _c : -1);
922
1011
  break;
923
1012
  }
924
1013
  case 4 /* ColorMode.CMYK */: {
925
- if (bitsPerChannel !== 8)
926
- throw new Error('bitsPerChannel Not supproted');
927
- if (psd.channels !== 4)
928
- throw new Error(`Invalid channel count`);
929
- const channels = [0, 1, 2, 3];
930
- if (reader.globalAlpha)
931
- channels.push(4);
932
- if (compression === 0 /* Compression.RawData */) {
933
- throw new Error(`Compression not supported: ${compression}`);
934
- // TODO: ...
935
- // for (let i = 0; i < channels.length; i++) {
936
- // readDataRaw(reader, imageData, channels[i], psd.width, psd.height);
937
- // }
938
- }
939
- else if (compression === 1 /* Compression.RleCompressed */) {
940
- const cmykImageData = {
941
- width: imageData.width,
942
- height: imageData.height,
943
- data: new Uint8Array(imageData.width * imageData.height * 5),
944
- };
945
- const start = reader.offset;
946
- readDataRLE(reader, cmykImageData, psd.width, psd.height, bitsPerChannel, 5, channels, reader.large);
947
- cmykToRgb(cmykImageData, imageData, true);
948
- if (helpers_1.RAW_IMAGE_DATA)
949
- psd.imageDataRaw = new Uint8Array(reader.view.buffer, reader.view.byteOffset + start, reader.offset - start);
1014
+ if (bitsPerChannel !== 8 && bitsPerChannel !== 16)
1015
+ throw new Error('bitsPerChannel not supported');
1016
+ const offsets = compositeChannelOffsets(reader, psd, 4, 4);
1017
+ const cmykImageData = createImageDataBitDepth(width, height, bitsPerChannel, 5, reader.totalMemoryLimit);
1018
+ resetAlpha(cmykImageData, true);
1019
+ readCompositeChannels(reader, compression, cmykImageData, bitsPerChannel, 5, offsets);
1020
+ cmykToRgb(cmykImageData, imageData, !!reader.globalAlpha);
1021
+ break;
1022
+ }
1023
+ default: throw new Error(`Color mode not supported: ${psd.colorMode}`);
1024
+ }
1025
+ // remove weird white matte (already removed when converting from CMYK)
1026
+ if (reader.globalAlpha && psd.colorMode !== 4 /* ColorMode.CMYK */) {
1027
+ removeWhiteMatte(imageData);
1028
+ }
1029
+ return imageData;
1030
+ }
1031
+ // returns target offset for each of the composite image channels stored in the file, -1 for channels that are skipped
1032
+ function compositeChannelOffsets(reader, psd, colorChannels, alphaOffset) {
1033
+ var _a, _b, _c;
1034
+ const channelCount = (_a = psd.channels) !== null && _a !== void 0 ? _a : (colorChannels + (reader.globalAlpha ? 1 : 0));
1035
+ if (channelCount < colorChannels)
1036
+ throw new Error(`Invalid channel count: ${channelCount}`);
1037
+ // First extra channel is transparency if document has global alpha (negative layer count). Some non-Photoshop
1038
+ // applications write transparency channel without setting global alpha, in that case extra channel is not
1039
+ // listed in alpha channel names (Photoshop names all user alpha channels / saved selections).
1040
+ // Photoshop also lists merged transparency as first alpha channel named "Transparency" (in documents marked with
1041
+ // Mtrn/Mt16/Mt32 additional info, or in some 16bit documents with layers stored in Lr16 without any other mark)
1042
+ const alphaNames = (_c = (_b = psd.imageResources) === null || _b === void 0 ? void 0 : _b.alphaChannelNames) !== null && _c !== void 0 ? _c : [];
1043
+ const namedChannels = alphaNames.length;
1044
+ const namedTransparency = !!reader.layersInGlobalInfo && alphaNames[0] === 'Transparency' && namedChannels === channelCount - colorChannels;
1045
+ const hasTransparency = reader.globalAlpha || namedTransparency || (channelCount - colorChannels) > namedChannels;
1046
+ const offsets = [];
1047
+ for (let i = 0; i < channelCount; i++) {
1048
+ if (i < colorChannels) {
1049
+ offsets.push(i);
1050
+ }
1051
+ else if (i === colorChannels && hasTransparency && alphaOffset >= 0) {
1052
+ offsets.push(alphaOffset);
1053
+ }
1054
+ else {
1055
+ // TODO: store these channels (alpha channels / saved selections, spot colors) in additional image data
1056
+ offsets.push(-1);
1057
+ }
1058
+ }
1059
+ return offsets;
1060
+ }
1061
+ function readCompositeChannels(reader, compression, pixelData, bitsPerChannel, step, offsets) {
1062
+ if (compression === 0 /* Compression.RawData */) {
1063
+ const channelSize = pixelData.width * pixelData.height * bytesPerValue(bitsPerChannel);
1064
+ for (const offset of offsets) {
1065
+ if (offset < 0) {
1066
+ skipBytes(reader, channelSize);
950
1067
  }
951
1068
  else {
952
- throw new Error(`Compression not supported: ${compression}`);
1069
+ readDataRaw(readBytes(reader, channelSize), pixelData, bitsPerChannel, step, offset);
1070
+ }
1071
+ }
1072
+ }
1073
+ else if (compression === 1 /* Compression.RleCompressed */) {
1074
+ readDataRLE(reader, pixelData, pixelData.width, pixelData.height, bitsPerChannel, step, offsets, reader.large);
1075
+ }
1076
+ else {
1077
+ throw new Error(`Compression not supported: ${compression}`);
1078
+ }
1079
+ }
1080
+ // un-premultiplies colors from white matte, used for composite image of documents with global alpha
1081
+ function removeWhiteMatte({ data, width, height }) {
1082
+ const size = width * height * 4;
1083
+ if (data instanceof Float32Array) {
1084
+ for (let i = 0; i < size; i += 4) {
1085
+ const a = data[i + 3];
1086
+ if (a > 0 && a < 1) {
1087
+ const ra = 1 / a;
1088
+ const invA = 1 - ra;
1089
+ for (let j = 0; j < 3; j++) {
1090
+ const v = data[i + j] * ra + invA;
1091
+ data[i + j] = v < 0 ? 0 : v;
1092
+ }
1093
+ }
1094
+ }
1095
+ }
1096
+ else if (data instanceof Uint16Array) {
1097
+ for (let i = 0; i < size; i += 4) {
1098
+ const pa = data[i + 3];
1099
+ if (pa !== 0 && pa !== 0xffff) {
1100
+ const ra = 0xffff / pa;
1101
+ const invA = 0xffff * (1 - ra);
1102
+ for (let j = 0; j < 3; j++) {
1103
+ const v = Math.round(data[i + j] * ra + invA);
1104
+ data[i + j] = v < 0 ? 0 : (v > 0xffff ? 0xffff : v);
1105
+ }
953
1106
  }
954
- break;
955
1107
  }
956
- default: throw new Error(`Color mode not supported: ${psd.colorMode}`);
957
1108
  }
958
- // remove weird white matte
959
- if (reader.globalAlpha) {
960
- if (psd.bitsPerChannel !== 8)
961
- throw new Error('bitsPerChannel Not supproted');
962
- const p = imageData.data;
963
- const size = imageData.width * imageData.height * 4;
1109
+ else {
1110
+ // Uint8ClampedArray clamps and rounds values on assignment
964
1111
  for (let i = 0; i < size; i += 4) {
965
- const pa = p[i + 3];
1112
+ const pa = data[i + 3];
966
1113
  if (pa != 0 && pa != 255) {
967
1114
  const a = pa / 255;
968
1115
  const ra = 1 / a;
969
1116
  const invA = 255 * (1 - ra);
970
- p[i + 0] = p[i + 0] * ra + invA;
971
- p[i + 1] = p[i + 1] * ra + invA;
972
- p[i + 2] = p[i + 2] * ra + invA;
1117
+ data[i + 0] = data[i + 0] * ra + invA;
1118
+ data[i + 1] = data[i + 1] * ra + invA;
1119
+ data[i + 2] = data[i + 2] * ra + invA;
973
1120
  }
974
1121
  }
975
1122
  }
976
- return imageData;
977
1123
  }
978
- function cmykToRgb(cmyk, rgb, reverseAlpha) {
1124
+ // converts value in 0-255 range to target range, rounding and clamping it
1125
+ function toRange(value, max) {
1126
+ value = Math.round(value * (max / 255));
1127
+ return value <= 0 ? 0 : (value >= max ? max : value);
1128
+ }
1129
+ // Polynomial approximation of U.S. Web Coated (SWOP) v2 CMYK to RGB conversion (default CMYK profile of Photoshop).
1130
+ // Coefficients are taken from pdf.js (DeviceCmykCS in src/core/colorspace.js, https://github.com/mozilla/pdf.js),
1131
+ // Copyright 2012 Mozilla Foundation, licensed under the Apache License, Version 2.0
1132
+ // (http://www.apache.org/licenses/LICENSE-2.0)
1133
+ // cmyk - 8 or 16 bit data with 5 channels (cmyka), rgb - 8 or 16 bit data with 4 channels (rgba)
1134
+ // removeMatte - colors are blended with white (no ink), used for composite image of documents with global alpha
1135
+ function cmykToRgb(cmyk, rgb, removeMatte) {
979
1136
  const size = rgb.width * rgb.height * 4;
980
1137
  const srcData = cmyk.data;
981
1138
  const dstData = rgb.data;
1139
+ const srcMax = srcData instanceof Uint16Array ? 0xffff : 0xff;
1140
+ const dstMax = dstData instanceof Uint16Array ? 0xffff : 0xff;
982
1141
  for (let src = 0, dst = 0; dst < size; src += 5, dst += 4) {
983
- const c = srcData[src];
984
- const m = srcData[src + 1];
985
- const y = srcData[src + 2];
986
- const k = srcData[src + 3];
987
- dstData[dst] = ((((c * k) | 0) / 255) | 0);
988
- dstData[dst + 1] = ((((m * k) | 0) / 255) | 0);
989
- dstData[dst + 2] = ((((y * k) | 0) / 255) | 0);
990
- dstData[dst + 3] = reverseAlpha ? 255 - srcData[src + 4] : srcData[src + 4];
991
- }
992
- // for (let src = 0, dst = 0; dst < size; src += 5, dst += 4) {
993
- // const c = 1 - (srcData[src + 0] / 255);
994
- // const m = 1 - (srcData[src + 1] / 255);
995
- // const y = 1 - (srcData[src + 2] / 255);
996
- // // const k = srcData[src + 3] / 255;
997
- // dstData[dst + 0] = ((1 - c * 0.8) * 255) | 0;
998
- // dstData[dst + 1] = ((1 - m * 0.8) * 255) | 0;
999
- // dstData[dst + 2] = ((1 - y * 0.8) * 255) | 0;
1000
- // dstData[dst + 3] = reverseAlpha ? 255 - srcData[src + 4] : srcData[src + 4];
1001
- // }
1002
- }
1003
- function indexedToRgb(indexed, rgb, palette) {
1142
+ const a = srcData[src + 4];
1143
+ // image data stores inverted values (0 = 100% ink)
1144
+ let c = 1 - srcData[src] / srcMax;
1145
+ let m = 1 - srcData[src + 1] / srcMax;
1146
+ let y = 1 - srcData[src + 2] / srcMax;
1147
+ let k = 1 - srcData[src + 3] / srcMax;
1148
+ if (removeMatte && a > 0 && a < srcMax) {
1149
+ const ra = srcMax / a;
1150
+ c = Math.min(1, c * ra);
1151
+ m = Math.min(1, m * ra);
1152
+ y = Math.min(1, y * ra);
1153
+ k = Math.min(1, k * ra);
1154
+ }
1155
+ dstData[dst] = toRange(255 +
1156
+ c * (-4.387332384609988 * c + 54.48615194189176 * m + 18.82290502165302 * y + 212.25662451639585 * k - 285.2331026137004) +
1157
+ m * (1.7149763477362134 * m - 5.6096736904047315 * y - 17.873870861415444 * k - 5.497006427196366) +
1158
+ y * (-2.5217340131683033 * y - 21.248923337353073 * k + 17.5119270841813) +
1159
+ k * (-21.86122147463605 * k - 189.48180835922747), dstMax);
1160
+ dstData[dst + 1] = toRange(255 +
1161
+ c * (8.841041422036149 * c + 60.118027045597366 * m + 6.871425592049007 * y + 31.159100130055922 * k - 79.2970844816548) +
1162
+ m * (-15.310361306967817 * m + 17.575251261109482 * y + 131.35250912493976 * k - 190.9453302588951) +
1163
+ y * (4.444339102852739 * y + 9.8632861493405 * k - 24.86741582555878) +
1164
+ k * (-20.737325471181034 * k - 187.80453709719578), dstMax);
1165
+ dstData[dst + 2] = toRange(255 +
1166
+ c * (0.8842522430003296 * c + 8.078677503112928 * m + 30.89978309703729 * y - 0.23883238689178934 * k - 14.183576799673286) +
1167
+ m * (10.49593273432072 * m + 63.02378494754052 * y + 50.606957656360734 * k - 112.23884253719248) +
1168
+ y * (0.03296041114873217 * y + 115.60384449646641 * k - 193.58209356861505) +
1169
+ k * (-22.33816807309886 * k - 180.12613974708367), dstMax);
1170
+ dstData[dst + 3] = srcMax === dstMax ? a : Math.round(a * dstMax / srcMax);
1171
+ }
1172
+ }
1173
+ // transparencyIndex - palette index of transparent color (-1 if there's none)
1174
+ function indexedToRgb(indexed, rgb, palette, transparencyIndex) {
1004
1175
  const size = indexed.width * indexed.height;
1005
1176
  const srcData = indexed.data;
1006
1177
  const dstData = rgb.data;
1007
1178
  for (let src = 0, dst = 0; src < size; src++, dst += 4) {
1008
- const c = palette[srcData[src]];
1179
+ const index = srcData[src];
1180
+ const c = palette[index];
1009
1181
  dstData[dst + 0] = c.r;
1010
1182
  dstData[dst + 1] = c.g;
1011
1183
  dstData[dst + 2] = c.b;
1012
- dstData[dst + 3] = 255;
1184
+ dstData[dst + 3] = index === transparencyIndex ? 0 : 255;
1013
1185
  }
1014
1186
  }
1015
- function verifyCompatible(a, b) {
1016
- if ((a.byteLength / a.length) !== (b.byteLength / b.length)) {
1017
- throw new Error('Invalid array types');
1187
+ // number of bytes used to store single channel value, 1-bit data is handled as bytes
1188
+ function bytesPerValue(bitDepth) {
1189
+ switch (bitDepth) {
1190
+ case 1:
1191
+ case 8: return 1;
1192
+ case 16: return 2;
1193
+ case 32: return 4;
1194
+ default: throw new Error(`Invalid bitDepth (${bitDepth})`);
1018
1195
  }
1019
1196
  }
1020
- function bytesToArray(bytes, bitDepth) {
1021
- if (bitDepth === 8) {
1022
- return bytes;
1197
+ function verifyArrayType(data, bytesPerValue) {
1198
+ if (data.BYTES_PER_ELEMENT !== bytesPerValue) {
1199
+ throw new Error('Invalid array types');
1023
1200
  }
1024
- else if (bitDepth === 16) {
1025
- // PSD files store 16-bit channel data in big-endian byte order.
1026
- // Swap each pair of bytes so that Uint16Array (native-endian) reads the correct values.
1027
- for (let i = 0; i < bytes.byteLength; i += 2) {
1028
- const tmp = bytes[i];
1029
- bytes[i] = bytes[i + 1];
1030
- bytes[i + 1] = tmp;
1031
- }
1032
- if (bytes.byteOffset % 2) {
1033
- const result = new Uint16Array(bytes.byteLength / 2);
1034
- new Uint8Array(result.buffer, result.byteOffset, result.byteLength).set(bytes);
1035
- return result;
1036
- }
1037
- else {
1038
- return new Uint16Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 2);
1201
+ }
1202
+ // NOTE: `buffer` may alias input file data, it must not be modified here
1203
+ function readDataRaw(buffer, pixelData, bitDepth, step, offset) {
1204
+ const bpv = bytesPerValue(bitDepth);
1205
+ if (!pixelData || offset < 0 || offset >= step)
1206
+ return;
1207
+ const data = pixelData.data;
1208
+ verifyArrayType(data, bpv);
1209
+ const size = Math.min(pixelData.width * pixelData.height, Math.floor(buffer.byteLength / bpv)) | 0;
1210
+ if (bpv === 1) {
1211
+ for (let i = 0, p = offset | 0; i < size; i++, p = (p + step) | 0) {
1212
+ data[p] = buffer[i];
1039
1213
  }
1040
1214
  }
1041
- else if (bitDepth === 32) {
1042
- if (bytes.byteOffset % 4) {
1043
- const result = new Float32Array(bytes.byteLength / 4);
1044
- new Uint8Array(result.buffer, result.byteOffset, result.byteLength).set(bytes);
1045
- return result;
1046
- }
1047
- else {
1048
- return new Float32Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 4);
1215
+ else if (bpv === 2) {
1216
+ for (let i = 0, j = 0, p = offset | 0; i < size; i++, j += 2, p = (p + step) | 0) {
1217
+ data[p] = (buffer[j] << 8) | buffer[j + 1];
1049
1218
  }
1050
1219
  }
1051
1220
  else {
1052
- throw new Error(`Invalid bitDepth (${bitDepth})`);
1053
- }
1054
- }
1055
- function copyChannelToPixelData(pixelData, channel, offset, step) {
1056
- verifyCompatible(pixelData.data, channel);
1057
- const size = pixelData.width * pixelData.height;
1058
- const data = pixelData.data;
1059
- for (let i = 0, p = offset | 0; i < size; i++, p = (p + step) | 0) {
1060
- data[p] = channel[i];
1061
- }
1062
- }
1063
- function readDataRaw(buffer, pixelData, bitDepth, step, offset) {
1064
- if (bitDepth == 32) {
1065
- for (let i = 0; i < buffer.byteLength; i += 4) {
1066
- const a = buffer[i + 0];
1067
- const b = buffer[i + 1];
1068
- const c = buffer[i + 2];
1069
- const d = buffer[i + 3];
1070
- buffer[i + 0] = d;
1071
- buffer[i + 1] = c;
1072
- buffer[i + 2] = b;
1073
- buffer[i + 3] = a;
1221
+ const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
1222
+ for (let i = 0, j = 0, p = offset | 0; i < size; i++, j += 4, p = (p + step) | 0) {
1223
+ data[p] = view.getFloat32(j, false);
1074
1224
  }
1075
1225
  }
1076
- const array = bytesToArray(buffer, bitDepth);
1077
- if (pixelData && offset < step) {
1078
- copyChannelToPixelData(pixelData, array, offset, step);
1079
- }
1080
1226
  }
1081
- function decodePredicted(data, width, height, mod) {
1227
+ function decodePredicted(data, width, height) {
1082
1228
  for (let y = 0; y < height; y++) {
1083
1229
  const offset = y * width;
1084
1230
  for (let x = 1, o = offset + 1; x < width; x++, o++) {
1085
- data[o] = (data[o - 1] + data[o]) % mod;
1086
- }
1087
- }
1088
- }
1089
- function readDataZip(compressed, pixelData, width, height, bitDepth, step, offset, prediction) {
1090
- const decompressed = (0, pako_1.inflate)(compressed);
1091
- if (pixelData && offset < step) {
1092
- const array = bytesToArray(decompressed, bitDepth);
1093
- if (bitDepth === 8) {
1094
- if (prediction)
1095
- decodePredicted(decompressed, width, height, 0x100);
1096
- copyChannelToPixelData(pixelData, decompressed, offset, step);
1097
- }
1098
- else if (bitDepth === 16) {
1099
- if (prediction)
1100
- decodePredicted(array, width, height, 0x10000);
1101
- copyChannelToPixelData(pixelData, array, offset, step);
1102
- }
1103
- else if (bitDepth === 32) {
1104
- if (prediction)
1105
- decodePredicted(decompressed, width * 4, height, 0x100);
1106
- let di = offset;
1107
- const dst = new Uint32Array(pixelData.data.buffer, pixelData.data.byteOffset, pixelData.data.length);
1108
- for (let y = 0; y < height; y++) {
1109
- let a = width * 4 * y;
1110
- for (let x = 0; x < width; x++, a++, di += step) {
1111
- const b = a + width;
1112
- const c = b + width;
1113
- const d = c + width;
1114
- dst[di] = ((decompressed[a] << 24) | (decompressed[b] << 16) | (decompressed[c] << 8) | decompressed[d]) >>> 0;
1231
+ data[o] = data[o - 1] + data[o]; // Uint8Array wraps values on assignment
1232
+ }
1233
+ }
1234
+ }
1235
+ const zipOverflow = new Error('Decompressed data exceeds expected size');
1236
+ // inflates data, stopping when output exceeds expected size (protects against zip bombs)
1237
+ function inflateLimited(compressed, expectedSize, reader) {
1238
+ const output = new Uint8Array(expectedSize);
1239
+ let position = 0;
1240
+ let overflow = false;
1241
+ const inflator = new pako_1.Inflate({ chunkSize: Math.max(1024, Math.min(expectedSize, 0x10000)) });
1242
+ inflator.onData = (chunk) => {
1243
+ const data = chunk;
1244
+ const count = Math.min(data.length, expectedSize - position);
1245
+ if (count > 0)
1246
+ output.set(count === data.length ? data : data.subarray(0, count), position);
1247
+ position += count;
1248
+ if (count < data.length) {
1249
+ overflow = true;
1250
+ throw zipOverflow;
1251
+ }
1252
+ };
1253
+ inflator.onEnd = (status) => {
1254
+ var _a;
1255
+ // default handler collects all the chunks into result, we only need the error status
1256
+ inflator.err = status;
1257
+ inflator.msg = ((_a = inflator.strm) === null || _a === void 0 ? void 0 : _a.msg) || '';
1258
+ };
1259
+ try {
1260
+ inflator.push(compressed, true);
1261
+ }
1262
+ catch (e) {
1263
+ if (e !== zipOverflow)
1264
+ throw e;
1265
+ }
1266
+ if (overflow) {
1267
+ if (reader)
1268
+ warnOrThrow(reader, zipOverflow.message);
1269
+ }
1270
+ else if (inflator.err) {
1271
+ throw new Error(`Invalid zip data: ${inflator.msg}`);
1272
+ }
1273
+ return output;
1274
+ }
1275
+ function readDataZip(compressed, pixelData, width, height, bitDepth, step, offset, prediction, reader) {
1276
+ const bpv = bytesPerValue(bitDepth);
1277
+ if (!pixelData || offset < 0 || offset >= step)
1278
+ return;
1279
+ const data = pixelData.data;
1280
+ verifyArrayType(data, bpv);
1281
+ const expectedSize = width * height * bpv;
1282
+ if (reader)
1283
+ consumeMemory(reader, expectedSize);
1284
+ try {
1285
+ const decompressed = inflateLimited(compressed, expectedSize, reader);
1286
+ if (bpv === 1) {
1287
+ for (let y = 0, i = 0; y < height; y++) {
1288
+ let prev = 0;
1289
+ for (let x = 0, p = offset + y * width * step; x < width; x++, i++, p += step) {
1290
+ let v = decompressed[i];
1291
+ if (prediction)
1292
+ v = prev = (prev + v) & 0xff;
1293
+ data[p] = v;
1294
+ }
1295
+ }
1296
+ }
1297
+ else if (bpv === 2) {
1298
+ for (let y = 0, i = 0; y < height; y++) {
1299
+ let prev = 0;
1300
+ for (let x = 0, p = offset + y * width * step; x < width; x++, i += 2, p += step) {
1301
+ let v = (decompressed[i] << 8) | decompressed[i + 1];
1302
+ if (prediction)
1303
+ v = prev = (prev + v) & 0xffff;
1304
+ data[p] = v;
1115
1305
  }
1116
1306
  }
1117
1307
  }
1118
1308
  else {
1119
- throw new Error('Invalid bitDepth');
1309
+ const dst = new Uint32Array(data.buffer, data.byteOffset, data.length);
1310
+ if (prediction) {
1311
+ // bytes are delta encoded per row and stored in planar order (all 1st bytes, all 2nd bytes, ...)
1312
+ decodePredicted(decompressed, width * 4, height);
1313
+ for (let y = 0, di = offset; y < height; y++) {
1314
+ let a = width * 4 * y;
1315
+ for (let x = 0; x < width; x++, a++, di += step) {
1316
+ const b = a + width;
1317
+ const c = b + width;
1318
+ const d = c + width;
1319
+ dst[di] = ((decompressed[a] << 24) | (decompressed[b] << 16) | (decompressed[c] << 8) | decompressed[d]) >>> 0;
1320
+ }
1321
+ }
1322
+ }
1323
+ else {
1324
+ const view = new DataView(decompressed.buffer, decompressed.byteOffset, decompressed.byteLength);
1325
+ for (let i = 0, j = 0, di = offset, size = width * height; i < size; i++, j += 4, di += step) {
1326
+ dst[di] = view.getUint32(j, false);
1327
+ }
1328
+ }
1120
1329
  }
1121
1330
  }
1122
- }
1123
- function readDataRLE(reader, pixelData, width, height, _bitDepth, step, offsets, large) {
1124
- const data = pixelData && pixelData.data;
1125
- let lengths;
1126
- if (large) {
1127
- consumeMemory(reader, offsets.length * height * 4);
1128
- lengths = new Uint32Array(offsets.length * height);
1129
- for (let o = 0, li = 0; o < offsets.length; o++) {
1130
- for (let y = 0; y < height; y++, li++) {
1131
- lengths[li] = readUint32(reader);
1331
+ finally {
1332
+ if (reader)
1333
+ recoverMemory(reader, expectedSize);
1334
+ }
1335
+ }
1336
+ // decodes single PackBits row, returns number of decoded values
1337
+ function decodeRLERow(src, i, end, dst, p, step, count) {
1338
+ let x = 0;
1339
+ while (i < end && x < count) {
1340
+ const header = src[i++];
1341
+ if (header > 128) {
1342
+ if (i >= end)
1343
+ break;
1344
+ const value = src[i++];
1345
+ let n = 257 - header;
1346
+ if (n > count - x)
1347
+ n = count - x;
1348
+ x += n;
1349
+ for (; n > 0; n--, p += step) {
1350
+ dst[p] = value;
1351
+ }
1352
+ }
1353
+ else if (header < 128) {
1354
+ let n = header + 1;
1355
+ if (n > count - x)
1356
+ n = count - x;
1357
+ if (n > end - i)
1358
+ n = end - i;
1359
+ x += n;
1360
+ for (; n > 0; n--, p += step) {
1361
+ dst[p] = src[i++];
1132
1362
  }
1133
1363
  }
1364
+ else {
1365
+ // ignore 128
1366
+ }
1367
+ }
1368
+ return x;
1369
+ }
1370
+ // converts big-endian 16/32-bit values from bytes and writes them to `data` starting at `p` with `step`
1371
+ function scatterValues(bytes, count, bpv, data, p, step) {
1372
+ if (bpv === 2) {
1373
+ for (let x = 0, i = 0; x < count; x++, i += 2, p += step) {
1374
+ data[p] = (bytes[i] << 8) | bytes[i + 1];
1375
+ }
1134
1376
  }
1135
1377
  else {
1136
- consumeMemory(reader, offsets.length * height * 2);
1137
- lengths = new Uint16Array(offsets.length * height);
1138
- for (let o = 0, li = 0; o < offsets.length; o++) {
1139
- for (let y = 0; y < height; y++, li++) {
1140
- lengths[li] = readUint16(reader);
1141
- }
1378
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
1379
+ for (let x = 0, i = 0; x < count; x++, i += 4, p += step) {
1380
+ data[p] = view.getFloat32(i, false);
1142
1381
  }
1143
1382
  }
1144
- // if (bitDepth !== 1 && bitDepth !== 8) throw new Error(`Invalid bit depth (${bitDepth})`);
1145
- const extraLimit = (step - 1) | 0; // 3 for rgb, 4 for cmyk
1146
- for (let c = 0, li = 0; c < offsets.length; c++) {
1147
- const offset = offsets[c] | 0;
1148
- const extra = c > extraLimit || offset > extraLimit;
1149
- if (!data || extra) {
1150
- for (let y = 0; y < height; y++, li++) {
1151
- skipBytes(reader, lengths[li]);
1152
- }
1383
+ }
1384
+ // decodes RLE rows of single channel, each row starts at the beginning of the image row
1385
+ function decodeRLEChannel(src, lengths, li, pos, width, height, data, offset, step, bpv, row) {
1386
+ const srcEnd = src.length;
1387
+ const rowSize = width * bpv;
1388
+ for (let y = 0; y < height; y++, li++) {
1389
+ const rowStart = pos < srcEnd ? pos : srcEnd;
1390
+ pos += lengths[li];
1391
+ const rowEnd = pos < srcEnd ? pos : srcEnd;
1392
+ if (rowStart >= rowEnd)
1393
+ continue;
1394
+ if (bpv === 1) {
1395
+ decodeRLERow(src, rowStart, rowEnd, data, offset + y * width * step, step, width);
1153
1396
  }
1154
1397
  else {
1155
- for (let y = 0, p = offset | 0; y < height; y++, li++) {
1156
- const length = lengths[li];
1157
- const buffer = readBytes(reader, length);
1158
- for (let i = 0, x = 0; i < length; i++) {
1159
- let header = buffer[i];
1160
- if (header > 128) {
1161
- const value = buffer[++i];
1162
- header = (256 - header) | 0;
1163
- for (let j = 0; j <= header && x < width; j = (j + 1) | 0, x = (x + 1) | 0) {
1164
- data[p] = value;
1165
- p = (p + step) | 0;
1166
- }
1167
- }
1168
- else if (header < 128) {
1169
- for (let j = 0; j <= header && x < width; j = (j + 1) | 0, x = (x + 1) | 0) {
1170
- data[p] = buffer[++i];
1171
- p = (p + step) | 0;
1172
- }
1173
- }
1174
- else {
1175
- // ignore 128
1176
- }
1177
- // This showed up on some images from non-photoshop programs, ignoring it seems to work just fine.
1178
- // if (i >= length) throw new Error(`Invalid RLE data: exceeded buffer size ${i}/${length}`);
1179
- }
1398
+ const decoded = decodeRLERow(src, rowStart, rowEnd, row, 0, 1, rowSize);
1399
+ scatterValues(row, Math.floor(decoded / bpv), bpv, data, offset + y * width * step, step);
1400
+ }
1401
+ }
1402
+ }
1403
+ function readDataRLE(reader, pixelData, width, height, bitDepth, step, offsets, large) {
1404
+ const data = pixelData && pixelData.data;
1405
+ const rows = offsets.length * height;
1406
+ const lengthSize = large ? 4 : 2;
1407
+ consumeMemory(reader, rows * lengthSize);
1408
+ try {
1409
+ if ((reader.offset + rows * lengthSize) > reader.view.byteLength) {
1410
+ throw new Error('RLE row lengths exceed buffer length');
1411
+ }
1412
+ const lengths = large ? new Uint32Array(rows) : new Uint16Array(rows);
1413
+ let total = 0;
1414
+ if (large) {
1415
+ for (let i = 0; i < rows; i++)
1416
+ total += (lengths[i] = readUint32(reader));
1417
+ }
1418
+ else {
1419
+ for (let i = 0; i < rows; i++)
1420
+ total += (lengths[i] = readUint16(reader));
1421
+ }
1422
+ const start = reader.offset;
1423
+ const srcEnd = reader.view.byteLength;
1424
+ if (total > (srcEnd - start)) {
1425
+ // broken or truncated file, decode only data that is available
1426
+ warnOrThrow(reader, 'RLE data exceeds buffer length');
1427
+ }
1428
+ const bpv = bytesPerValue(bitDepth);
1429
+ if (data)
1430
+ verifyArrayType(data, bpv);
1431
+ const src = new Uint8Array(reader.view.buffer, reader.view.byteOffset, srcEnd);
1432
+ const row = (data && bpv > 1) ? new Uint8Array(width * bpv) : undefined;
1433
+ let pos = start;
1434
+ for (let c = 0, li = 0; c < offsets.length; c++, li += height) {
1435
+ const offset = offsets[c] | 0;
1436
+ const channelStart = pos;
1437
+ for (let y = 0; y < height; y++)
1438
+ pos += lengths[li + y];
1439
+ if (data && offset >= 0 && offset < step) {
1440
+ decodeRLEChannel(src, lengths, li, channelStart, width, height, data, offset, step, bpv, row);
1180
1441
  }
1181
1442
  }
1443
+ reader.offset = pos;
1444
+ }
1445
+ finally {
1446
+ recoverMemory(reader, rows * lengthSize);
1182
1447
  }
1183
- recoverMemory(reader, lengths.byteLength);
1184
1448
  }
1185
1449
  function readSection(reader, round, func, skipEmpty = true, eightBytes = false) {
1186
1450
  let length = readUint32(reader);
@@ -1228,19 +1492,19 @@ function readColor(reader) {
1228
1492
  skipBytes(reader, 2);
1229
1493
  return { h, s, b };
1230
1494
  }
1231
- case 2 /* ColorSpace.CMYK */: {
1232
- const c = readUint16(reader) / 257;
1233
- const m = readUint16(reader) / 257;
1234
- const y = readUint16(reader) / 257;
1235
- const k = readUint16(reader) / 257;
1495
+ case 2 /* ColorSpace.CMYK */: { // 0 = 100% ink
1496
+ const c = (0xffff - readUint16(reader)) / 257;
1497
+ const m = (0xffff - readUint16(reader)) / 257;
1498
+ const y = (0xffff - readUint16(reader)) / 257;
1499
+ const k = (0xffff - readUint16(reader)) / 257;
1236
1500
  return { c, m, y, k };
1237
1501
  }
1238
1502
  case 7 /* ColorSpace.Lab */: {
1239
1503
  const l = readInt16(reader) / 10000;
1240
1504
  const ta = readInt16(reader);
1241
1505
  const tb = readInt16(reader);
1242
- const a = ta < 0 ? (ta / 12800) : (ta / 12700);
1243
- const b = tb < 0 ? (tb / 12800) : (tb / 12700);
1506
+ const a = (0, helpers_1.normalizeLabAB)(ta, 100);
1507
+ const b = (0, helpers_1.normalizeLabAB)(tb, 100);
1244
1508
  skipBytes(reader, 2);
1245
1509
  return { l, a, b };
1246
1510
  }
@@ -1253,6 +1517,24 @@ function readColor(reader) {
1253
1517
  throw new Error('Invalid color space');
1254
1518
  }
1255
1519
  }
1520
+ const MAX_PATTERN_SIZE = 30000;
1521
+ // reads pattern, unsupported or invalid patterns are skipped (returns undefined) unless throwForMissingFeatures is set
1522
+ function readPatternSafe(reader) {
1523
+ let length = reader.view.getUint32(reader.offset);
1524
+ while (length % 4)
1525
+ length++;
1526
+ const end = reader.offset + 4 + length;
1527
+ try {
1528
+ return readPattern(reader);
1529
+ }
1530
+ catch (e) {
1531
+ if (reader.throwForMissingFeatures)
1532
+ throw e;
1533
+ reader.logMissingFeatures && reader.log(`Skipped pattern: ${e.message}`);
1534
+ reader.offset = end;
1535
+ return undefined;
1536
+ }
1537
+ }
1256
1538
  function readPattern(reader) {
1257
1539
  let length = readUint32(reader);
1258
1540
  while (length % 4)
@@ -1264,8 +1546,8 @@ function readPattern(reader) {
1264
1546
  const colorMode = readUint32(reader);
1265
1547
  const x = readInt16(reader);
1266
1548
  const y = readInt16(reader);
1267
- // we only support RGB and grayscale for now
1268
- if (colorMode !== 3 /* ColorMode.RGB */ && colorMode !== 1 /* ColorMode.Grayscale */ && colorMode !== 2 /* ColorMode.Indexed */) {
1549
+ // we only support RGB, grayscale, indexed and CMYK for now
1550
+ if (colorMode !== 3 /* ColorMode.RGB */ && colorMode !== 1 /* ColorMode.Grayscale */ && colorMode !== 2 /* ColorMode.Indexed */ && colorMode !== 4 /* ColorMode.CMYK */) {
1269
1551
  throw new Error(`Unsupported pattern color mode: ${colorMode}`);
1270
1552
  }
1271
1553
  let name = readUnicodeString(reader);
@@ -1279,7 +1561,9 @@ function readPattern(reader) {
1279
1561
  b: readUint8(reader),
1280
1562
  });
1281
1563
  }
1282
- skipBytes(reader, 4); // no idea what this is
1564
+ // some writers follow the palette with 4 extra bytes (unknown)
1565
+ if (reader.view.getUint32(reader.offset) !== 3)
1566
+ skipBytes(reader, 4);
1283
1567
  }
1284
1568
  // virtual memory array list
1285
1569
  const version2 = readUint32(reader);
@@ -1293,17 +1577,37 @@ function readPattern(reader) {
1293
1577
  const channelsCount = readUint32(reader);
1294
1578
  const width = right - left;
1295
1579
  const height = bottom - top;
1580
+ if (width < 0 || height < 0)
1581
+ throw new Error('Invalid pattern bounds');
1582
+ if (width > MAX_PATTERN_SIZE || height > MAX_PATTERN_SIZE)
1583
+ throw new Error(`Pattern too large (${width}x${height})`);
1584
+ if (channelsCount > 64)
1585
+ throw new Error(`Invalid pattern channel count: ${channelsCount}`);
1586
+ // channels cover whole pattern, each 2 bytes of RLE data can produce at most 128 bytes
1587
+ if (width * height > (Math.min(end, reader.view.byteLength) - reader.offset) * 64)
1588
+ throw new Error('Pattern data out of bounds');
1296
1589
  const size = width * height * 4;
1297
1590
  consumeMemory(reader, size);
1298
1591
  const data = new Uint8Array(size);
1299
1592
  for (let i = 3; i < data.byteLength; i += 4) {
1300
1593
  data[i] = 255;
1301
1594
  }
1302
- for (let i = 0, ch = 0; i < (channelsCount + 2); i++) {
1595
+ // first written arrays are color channels, next one is transparency (Photoshop stores it in the last, sheet mask, array)
1596
+ const colorChannels = colorMode === 3 /* ColorMode.RGB */ ? 3 : (colorMode === 4 /* ColorMode.CMYK */ ? 4 : 1);
1597
+ // CMYK channels are decoded into separate buffer (5 values per pixel, inverted, 0 = 100% ink) and converted to RGB at the end
1598
+ const cmyk = colorMode === 4 /* ColorMode.CMYK */ ? new Uint8Array(width * height * 5).fill(255) : undefined;
1599
+ if (cmyk)
1600
+ consumeMemory(reader, cmyk.byteLength);
1601
+ const alphaTarget = cmyk ? 4 : 3;
1602
+ for (let i = 0, ch = 0; i < (channelsCount + 2) && reader.offset + 4 <= end; i++) {
1303
1603
  const has = readUint32(reader);
1304
1604
  if (!has)
1305
1605
  continue;
1306
1606
  const length = readUint32(reader);
1607
+ if (!length)
1608
+ continue;
1609
+ if (length < 23)
1610
+ throw new Error('Invalid pattern channel length');
1307
1611
  const pixelDepth = readUint32(reader);
1308
1612
  const ctop = readUint32(reader);
1309
1613
  const cleft = readUint32(reader);
@@ -1311,97 +1615,92 @@ function readPattern(reader) {
1311
1615
  const cright = readUint32(reader);
1312
1616
  const pixelDepth2 = readUint16(reader);
1313
1617
  const compressionMode = readUint8(reader); // 0 - raw, 1 - rle
1314
- const dataLength = length - (4 + 16 + 2 + 1);
1315
- const cdata = readBytes(reader, dataLength);
1316
- if (pixelDepth !== 8 || pixelDepth2 !== 8) {
1317
- throw new Error('16bit pixel depth not supported for patterns');
1318
- }
1618
+ const cdata = readBytes(reader, length - 23);
1619
+ const depth = (pixelDepth2 === 8 || pixelDepth2 === 16 || pixelDepth2 === 32) ? pixelDepth2 : pixelDepth;
1620
+ if (depth !== 8 && depth !== 16 && depth !== 32)
1621
+ throw new Error(`Unsupported pattern pixel depth: ${depth}`);
1622
+ if (ctop < top || cleft < left || cbottom > bottom || cright > right || cbottom < ctop || cright < cleft) {
1623
+ throw new Error('Pattern channel bounds outside of pattern bounds');
1624
+ }
1625
+ const target = ch < colorChannels ? ch : (ch === colorChannels ? alphaTarget : -1);
1626
+ ch++;
1627
+ if (target === -1)
1628
+ continue;
1319
1629
  const w = cright - cleft;
1320
1630
  const h = cbottom - ctop;
1321
- const ox = cleft - left;
1322
- const oy = ctop - top;
1631
+ const rowBytes = w * (depth / 8);
1632
+ const channelSize = rowBytes * h;
1633
+ let bytes;
1323
1634
  if (compressionMode === 0) {
1324
- if (colorMode === 3 /* ColorMode.RGB */ && ch < 3) {
1325
- for (let y = 0; y < h; y++) {
1326
- for (let x = 0; x < w; x++) {
1327
- const src = x + y * w;
1328
- const dst = (ox + x + (y + oy) * width) * 4;
1329
- data[dst + ch] = cdata[src];
1330
- }
1331
- }
1332
- }
1333
- else if (colorMode === 1 /* ColorMode.Grayscale */ && ch < 1) {
1334
- for (let y = 0; y < h; y++) {
1335
- for (let x = 0; x < w; x++) {
1336
- const src = x + y * w;
1337
- const dst = (ox + x + (y + oy) * width) * 4;
1338
- const value = cdata[src];
1339
- data[dst + 0] = value;
1340
- data[dst + 1] = value;
1341
- data[dst + 2] = value;
1342
- }
1343
- }
1344
- }
1345
- else if (colorMode === 2 /* ColorMode.Indexed */) {
1346
- for (let y = 0; y < h; y++) {
1347
- for (let x = 0; x < w; x++) {
1348
- const src = x + y * w;
1349
- const dst = (ox + x + (y + oy) * width) * 4;
1350
- const index = cdata[src];
1351
- const color = palette[index];
1352
- data[dst + 0] = color.r;
1353
- data[dst + 1] = color.g;
1354
- data[dst + 2] = color.b;
1355
- }
1356
- }
1357
- }
1358
- else {
1359
- if (reader.throwForMissingFeatures)
1360
- throw new Error('Invalid color pattern');
1361
- }
1635
+ if (cdata.byteLength < channelSize)
1636
+ throw new Error('Pattern channel data out of bounds');
1637
+ bytes = cdata;
1362
1638
  }
1363
1639
  else if (compressionMode === 1) {
1364
- consumeMemory(reader, w * h);
1365
- const pixelData = { data, width, height };
1366
- const tempData = { data: new Uint8Array(w * h), width: w, height: h };
1640
+ // each 2 bytes of RLE data can produce at most 128 bytes
1641
+ if (channelSize > cdata.byteLength * 64)
1642
+ throw new Error('Pattern channel data out of bounds');
1643
+ consumeMemory(reader, channelSize);
1644
+ bytes = new Uint8Array(channelSize);
1367
1645
  const cdataReader = createReader(cdata.buffer, cdata.byteOffset, cdata.byteLength);
1368
- if (colorMode === 3 /* ColorMode.RGB */ && ch < 3) {
1369
- readDataRLE(cdataReader, tempData, w, h, 8, 1, [0], false);
1370
- copyChannelToRGBA(tempData, pixelData, ox, oy, ch);
1371
- }
1372
- if (colorMode === 1 /* ColorMode.Grayscale */ && ch < 1) {
1373
- readDataRLE(cdataReader, tempData, w, h, 8, 1, [0], false);
1374
- copyChannelToRGBA(tempData, pixelData, ox, oy, 0);
1375
- setupGrayscale(pixelData);
1376
- }
1377
- if (colorMode === 2 /* ColorMode.Indexed */) {
1378
- // TODO:
1379
- throw new Error('Indexed pattern color mode not implemented');
1380
- }
1381
- recoverMemory(reader, w * h);
1646
+ readDataRLE(cdataReader, { data: bytes, width: rowBytes, height: h }, rowBytes, h, 8, 1, [0], false);
1382
1647
  }
1383
1648
  else {
1384
1649
  throw new Error('Invalid pattern compression mode');
1385
1650
  }
1386
- ch++;
1651
+ const ox = cleft - left;
1652
+ const oy = ctop - top;
1653
+ for (let y = 0; y < h; y++) {
1654
+ for (let x = 0; x < w; x++) {
1655
+ const src = x + y * w;
1656
+ const dst = (ox + x + (y + oy) * width) * 4;
1657
+ const value = depth === 8 ? bytes[src] : patternValueTo8bit(bytes, src, depth);
1658
+ if (cmyk) {
1659
+ cmyk[(ox + x + (y + oy) * width) * 5 + target] = value;
1660
+ }
1661
+ else if (target === 3) {
1662
+ data[dst + 3] = value;
1663
+ }
1664
+ else if (colorMode === 2 /* ColorMode.Indexed */) {
1665
+ const color = palette[value];
1666
+ data[dst + 0] = color.r;
1667
+ data[dst + 1] = color.g;
1668
+ data[dst + 2] = color.b;
1669
+ }
1670
+ else if (colorMode === 1 /* ColorMode.Grayscale */) {
1671
+ data[dst + 0] = value;
1672
+ data[dst + 1] = value;
1673
+ data[dst + 2] = value;
1674
+ }
1675
+ else {
1676
+ data[dst + target] = value;
1677
+ }
1678
+ }
1679
+ }
1680
+ if (compressionMode === 1)
1681
+ recoverMemory(reader, channelSize);
1682
+ }
1683
+ if (cmyk) {
1684
+ cmykToRgb({ width, height, data: cmyk }, { width, height, data }, false);
1685
+ recoverMemory(reader, cmyk.byteLength);
1387
1686
  }
1388
1687
  reader.offset = end;
1389
1688
  return { id, name, x, y, bounds: { x: left, y: top, w: width, h: height }, data };
1390
1689
  }
1391
- function copyChannelToRGBA(srcData, dstData, ox, oy, offset) {
1392
- const w = srcData.width;
1393
- const h = srcData.height;
1394
- const width = dstData.width;
1395
- for (let y = 0; y < h; y++) {
1396
- for (let x = 0; x < w; x++) {
1397
- const src = x + y * w;
1398
- const dst = (ox + x + (y + oy) * width) * 4;
1399
- const value = srcData.data[src];
1400
- dstData.data[dst + offset] = value;
1401
- }
1690
+ // converts big-endian 16bit or 32bit (float) value at given index to 8bit value
1691
+ function patternValueTo8bit(bytes, index, depth) {
1692
+ if (depth === 16) {
1693
+ return Math.round(((bytes[index * 2] << 8) | bytes[index * 2 + 1]) / 257);
1694
+ }
1695
+ else {
1696
+ const view = new DataView(bytes.buffer, bytes.byteOffset + index * 4, 4);
1697
+ const value = view.getFloat32(0, false);
1698
+ return value > 0 ? (value < 1 ? Math.round(value * 255) : 255) : 0;
1402
1699
  }
1403
1700
  }
1404
1701
  function consumeMemory(reader, size) {
1702
+ if (!(size >= 0))
1703
+ throw new Error('Invalid size');
1405
1704
  if (reader.totalMemoryLimit !== undefined) {
1406
1705
  if (reader.totalMemoryLimit < size)
1407
1706
  throw new Error('Exceeded memory limit');