ag-psd 24.0.0 → 26.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 (53) hide show
  1. package/.v8-cache/v22.13.1-x64-00250a7c/4511bacf +0 -0
  2. package/.v8-cache/v22.13.1-x64-00250a7c/57d3380b +0 -0
  3. package/.v8-cache/v22.13.1-x64-00250a7c/6cbfc0ec +0 -0
  4. package/.v8-cache/v22.13.1-x64-00250a7c/7100ee08 +0 -0
  5. package/.v8-cache/v22.13.1-x64-00250a7c/75e41e43 +0 -0
  6. package/.v8-cache/v22.13.1-x64-00250a7c/8d0bf0b5 +0 -0
  7. package/.v8-cache/v22.13.1-x64-00250a7c/acc36e66 +0 -0
  8. package/.v8-cache/v22.13.1-x64-00250a7c/b3c2fab7 +0 -0
  9. package/.v8-cache/v22.13.1-x64-00250a7c/c314aece +0 -0
  10. package/.v8-cache/v22.13.1-x64-00250a7c/cfc49f4f +0 -0
  11. package/.v8-cache/v22.13.1-x64-00250a7c/e03e2acd +0 -0
  12. package/CHANGELOG.md +7 -0
  13. package/{TODO → TODO.md} +46 -0
  14. package/accept.js +18 -1
  15. package/dist/additionalInfo.js +62 -63
  16. package/dist/additionalInfo.js.map +1 -1
  17. package/dist/bundle.js +246 -186
  18. package/dist/descriptor.d.ts +4 -3
  19. package/dist/descriptor.js +9 -5
  20. package/dist/descriptor.js.map +1 -1
  21. package/dist/helpers.d.ts +1 -0
  22. package/dist/helpers.js.map +1 -1
  23. package/dist/imageResources.js +39 -5
  24. package/dist/imageResources.js.map +1 -1
  25. package/dist/psd.d.ts +12 -1
  26. package/dist/psd.js.map +1 -1
  27. package/dist/psdReader.js +39 -36
  28. package/dist/psdReader.js.map +1 -1
  29. package/dist/psdWriter.js +96 -76
  30. package/dist/psdWriter.js.map +1 -1
  31. package/dist-es/additionalInfo.js +62 -63
  32. package/dist-es/additionalInfo.js.map +1 -1
  33. package/dist-es/descriptor.d.ts +4 -3
  34. package/dist-es/descriptor.js +9 -5
  35. package/dist-es/descriptor.js.map +1 -1
  36. package/dist-es/helpers.d.ts +1 -0
  37. package/dist-es/helpers.js.map +1 -1
  38. package/dist-es/imageResources.js +41 -7
  39. package/dist-es/imageResources.js.map +1 -1
  40. package/dist-es/psd.d.ts +12 -1
  41. package/dist-es/psd.js.map +1 -1
  42. package/dist-es/psdReader.js +39 -36
  43. package/dist-es/psdReader.js.map +1 -1
  44. package/dist-es/psdWriter.js +96 -76
  45. package/dist-es/psdWriter.js.map +1 -1
  46. package/package.json +1 -1
  47. package/src/additionalInfo.ts +66 -66
  48. package/src/descriptor.ts +12 -8
  49. package/src/helpers.ts +1 -0
  50. package/src/imageResources.ts +46 -20
  51. package/src/psd.ts +9 -1
  52. package/src/psdReader.ts +43 -40
  53. package/src/psdWriter.ts +92 -78
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ag-psd",
3
- "version": "24.0.0",
3
+ "version": "26.0.0",
4
4
  "description": "Library for reading and writing PSD files",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist-es/index.js",
@@ -357,6 +357,17 @@ addHandler(
357
357
  addHandlerAlias('vsms', 'vmsk');
358
358
  // addHandlerAlias('vmsk', 'vsms');
359
359
 
360
+ addHandler(
361
+ 'vowv', // something with vectors?
362
+ hasKey('vowv'),
363
+ (reader, target) => {
364
+ target.vowv = readUint32(reader); // always 2 ????
365
+ },
366
+ (writer, target) => {
367
+ writeUint32(writer, target.vowv!);
368
+ },
369
+ );
370
+
360
371
  interface VogkDescriptor {
361
372
  keyDescriptorList: {
362
373
  keyShapeInvalidated?: boolean;
@@ -447,62 +458,59 @@ addHandler(
447
458
  for (let i = 0; i < orig.keyDescriptorList.length; i++) {
448
459
  const item = orig.keyDescriptorList[i];
449
460
 
450
- if (item.keyShapeInvalidated) {
451
- desc.keyDescriptorList.push({ keyShapeInvalidated: true, keyOriginIndex: i });
452
- } else {
453
- desc.keyDescriptorList.push({} as any); // we're adding keyOriginIndex at the end
454
-
455
- const out = desc.keyDescriptorList[desc.keyDescriptorList.length - 1];
461
+ desc.keyDescriptorList.push({} as any); // we're adding keyOriginIndex at the end
456
462
 
457
- if (item.keyOriginType != null) out.keyOriginType = item.keyOriginType;
458
- if (item.keyOriginResolution != null) out.keyOriginResolution = item.keyOriginResolution;
463
+ const out = desc.keyDescriptorList[desc.keyDescriptorList.length - 1];
459
464
 
460
- const radii = item.keyOriginRRectRadii;
461
- if (radii) {
462
- out.keyOriginRRectRadii = {
463
- unitValueQuadVersion: 1,
464
- topRight: unitsValue(radii.topRight, 'topRight'),
465
- topLeft: unitsValue(radii.topLeft, 'topLeft'),
466
- bottomLeft: unitsValue(radii.bottomLeft, 'bottomLeft'),
467
- bottomRight: unitsValue(radii.bottomRight, 'bottomRight'),
468
- };
469
- }
465
+ if (item.keyOriginType != null) out.keyOriginType = item.keyOriginType;
466
+ if (item.keyOriginResolution != null) out.keyOriginResolution = item.keyOriginResolution;
470
467
 
471
- const box = item.keyOriginShapeBoundingBox;
472
- if (box) {
473
- out.keyOriginShapeBBox = {
474
- unitValueQuadVersion: 1,
475
- 'Top ': unitsValue(box.top, 'top'),
476
- Left: unitsValue(box.left, 'left'),
477
- Btom: unitsValue(box.bottom, 'bottom'),
478
- Rght: unitsValue(box.right, 'right'),
479
- };
480
- }
468
+ const radii = item.keyOriginRRectRadii;
469
+ if (radii) {
470
+ out.keyOriginRRectRadii = {
471
+ unitValueQuadVersion: 1,
472
+ topRight: unitsValue(radii.topRight, 'topRight'),
473
+ topLeft: unitsValue(radii.topLeft, 'topLeft'),
474
+ bottomLeft: unitsValue(radii.bottomLeft, 'bottomLeft'),
475
+ bottomRight: unitsValue(radii.bottomRight, 'bottomRight'),
476
+ };
477
+ }
481
478
 
482
- const corners = item.keyOriginBoxCorners;
483
- if (corners && corners.length === 4) {
484
- out.keyOriginBoxCorners = {
485
- rectangleCornerA: { Hrzn: corners[0].x, Vrtc: corners[0].y },
486
- rectangleCornerB: { Hrzn: corners[1].x, Vrtc: corners[1].y },
487
- rectangleCornerC: { Hrzn: corners[2].x, Vrtc: corners[2].y },
488
- rectangleCornerD: { Hrzn: corners[3].x, Vrtc: corners[3].y },
489
- };
490
- }
479
+ const box = item.keyOriginShapeBoundingBox;
480
+ if (box) {
481
+ out.keyOriginShapeBBox = {
482
+ unitValueQuadVersion: 1,
483
+ 'Top ': unitsValue(box.top, 'top'),
484
+ Left: unitsValue(box.left, 'left'),
485
+ Btom: unitsValue(box.bottom, 'bottom'),
486
+ Rght: unitsValue(box.right, 'right'),
487
+ };
488
+ }
491
489
 
492
- const transform = item.transform;
493
- if (transform && transform.length === 6) {
494
- out.Trnf = {
495
- xx: transform[0],
496
- xy: transform[1],
497
- yx: transform[2],
498
- yy: transform[3],
499
- tx: transform[4],
500
- ty: transform[5],
501
- };
502
- }
490
+ const corners = item.keyOriginBoxCorners;
491
+ if (corners && corners.length === 4) {
492
+ out.keyOriginBoxCorners = {
493
+ rectangleCornerA: { Hrzn: corners[0].x, Vrtc: corners[0].y },
494
+ rectangleCornerB: { Hrzn: corners[1].x, Vrtc: corners[1].y },
495
+ rectangleCornerC: { Hrzn: corners[2].x, Vrtc: corners[2].y },
496
+ rectangleCornerD: { Hrzn: corners[3].x, Vrtc: corners[3].y },
497
+ };
498
+ }
503
499
 
504
- out.keyOriginIndex = i;
500
+ const transform = item.transform;
501
+ if (transform && transform.length === 6) {
502
+ out.Trnf = {
503
+ xx: transform[0],
504
+ xy: transform[1],
505
+ yx: transform[2],
506
+ yy: transform[3],
507
+ tx: transform[4],
508
+ ty: transform[5],
509
+ };
505
510
  }
511
+
512
+ if (item.keyShapeInvalidated != null) out.keyShapeInvalidated = item.keyShapeInvalidated;
513
+ out.keyOriginIndex = i;
506
514
  }
507
515
 
508
516
  writeInt32(writer, 1); // version
@@ -1128,7 +1136,13 @@ function encodeWarp(warp: Warp): WarpDescriptor {
1128
1136
  warpPerspective: warp.perspective || 0,
1129
1137
  warpPerspectiveOther: warp.perspectiveOther || 0,
1130
1138
  warpRotate: Ornt.encode(warp.rotate),
1131
- bounds: {
1139
+ bounds: /*1 ? { // testing
1140
+ _classID: 'classFloatRect',
1141
+ 'Top ': bounds && bounds.top && bounds.top.value || 0,
1142
+ Left: bounds && bounds.left && bounds.left.value || 0,
1143
+ Btom: bounds && bounds.bottom && bounds.bottom.value || 0,
1144
+ Rght: bounds && bounds.right && bounds.right.value || 0,
1145
+ } :*/ {
1132
1146
  'Top ': unitsValue(bounds && bounds.top || { units: 'Pixels', value: 0 }, 'bounds.top'),
1133
1147
  Left: unitsValue(bounds && bounds.left || { units: 'Pixels', value: 0 }, 'bounds.left'),
1134
1148
  Btom: unitsValue(bounds && bounds.bottom || { units: 'Pixels', value: 0 }, 'bounds.bottom'),
@@ -1315,7 +1329,7 @@ type SoLdDescriptorFilterItem = {
1315
1329
  } | {
1316
1330
  filterID: 1198747202;
1317
1331
  Fltr: {
1318
- _name: 'Gaussian Blur';
1332
+ _name: 'Gaussian Blur' | '高斯模糊';
1319
1333
  _classID: 'GsnB';
1320
1334
  'Rds ': DescriptorUnitsValue;
1321
1335
  };
@@ -2636,6 +2650,7 @@ function serializeFilterFXItem(f: Filter): SoLdDescriptorFilterItem {
2636
2650
  case 'gaussian blur': return {
2637
2651
  ...base,
2638
2652
  Fltr: {
2653
+ // _name: '高斯模糊', // Testing
2639
2654
  _name: 'Gaussian Blur',
2640
2655
  _classID: 'GsnB',
2641
2656
  'Rds ': uvRadius(f.filter),
@@ -3542,21 +3557,6 @@ addHandler(
3542
3557
  },
3543
3558
  );
3544
3559
 
3545
- if (MOCK_HANDLERS) {
3546
- addHandler(
3547
- 'vowv', // appears with Lr16 section ?
3548
- _ => false,
3549
- (reader, target, left) => {
3550
- const value = readUint32(reader); // always 2 ????
3551
- reader; target;
3552
- console.log('vowv', { value }, left());
3553
- },
3554
- (_writer, _target) => {
3555
- // TODO: write
3556
- },
3557
- );
3558
- }
3559
-
3560
3560
  if (MOCK_HANDLERS) {
3561
3561
  addHandler(
3562
3562
  'Patt',
package/src/descriptor.ts CHANGED
@@ -56,12 +56,14 @@ function makeType(name: string, classID: string) {
56
56
 
57
57
  const nullType = makeType('', 'null');
58
58
 
59
+ const USE_CHINESE = false; // Testing
60
+
59
61
  const fieldToExtType: ExtTypeDict = {
60
62
  strokeStyleContent: makeType('', 'solidColorLayer'),
61
- // printProofSetup: makeType('校样设置', 'proofSetup'), // TESTING
62
- printProofSetup: makeType('Proof Setup', 'proofSetup'),
63
+ printProofSetup: makeType(USE_CHINESE ? '校样设置' : 'Proof Setup', 'proofSetup'),
64
+ Grad: makeType(USE_CHINESE ? '渐变' : 'Gradient', 'Grdn'),
65
+ Trnf: makeType(USE_CHINESE ? '变换' : 'Transform', 'Trnf'),
63
66
  patternFill: makeType('', 'patternFill'),
64
- Grad: makeType('Gradient', 'Grdn'),
65
67
  ebbl: makeType('', 'ebbl'),
66
68
  SoFi: makeType('', 'SoFi'),
67
69
  GrFl: makeType('', 'GrFl'),
@@ -103,7 +105,6 @@ const fieldToExtType: ExtTypeDict = {
103
105
  rectangleCornerC: makeType('', 'Pnt '),
104
106
  rectangleCornerD: makeType('', 'Pnt '),
105
107
  compInfo: nullType,
106
- Trnf: makeType('Transform', 'Trnf'),
107
108
  quiltWarp: makeType('', 'quiltWarp'),
108
109
  generatorSettings: nullType,
109
110
  crema: nullType,
@@ -208,7 +209,7 @@ const typeToField: { [key: string]: string[]; } = {
208
209
  'tableOrder', 'enableCompCore', 'enableCompCoreGPU', 'compCoreSupport', 'compCoreGPUSupport', 'Engn',
209
210
  'enableCompCoreThreads', 'gs99', 'FrDs', 'trackID', 'animInterpStyle', 'horzAlign',
210
211
  'vertAlign', 'bgColorType', 'shapeOperation', 'UndA', 'Wvtp', 'Drct', 'WndM', 'Edg ', 'FlCl', 'IntE',
211
- 'IntC', 'Cnvr', 'Fl ', 'Dstr', 'MztT', 'Lns ', 'ExtT', 'DspM', 'ExtR', 'ZZTy', 'SphM', 'SmBQ', 'placedLayerOCIOConversion',
212
+ 'IntC', 'Cnvr', 'Fl ', 'Dstr', 'MztT', 'Lns ', 'ExtT', 'DspM', 'ExtR', 'ZZTy', 'SphM', 'SmBQ', 'placedLayerOCIOConversion', 'gradientsInterpolationMethod',
212
213
  ],
213
214
  'bool': [
214
215
  'PstS', 'printSixteenBit', 'masterFXSwitch', 'enab', 'uglg', 'antialiasGloss',
@@ -879,11 +880,12 @@ export interface DescriptorColorContent {
879
880
  }
880
881
 
881
882
  export interface DescriptorGradientContent {
882
- Grad: DesciptorGradient;
883
- Type: string;
884
883
  Dthr?: boolean;
885
- Rvrs?: boolean;
884
+ gradientsInterpolationMethod?: string; // 'gradientInterpolationMethodType.Smoo'
886
885
  Angl?: DescriptorUnitsValue;
886
+ Type: string;
887
+ Grad: DesciptorGradient;
888
+ Rvrs?: boolean;
887
889
  'Scl '?: DescriptorUnitsValue;
888
890
  Algn?: boolean;
889
891
  Ofst?: { Hrzn: DescriptorUnitsValue; Vrtc: DescriptorUnitsValue; };
@@ -1611,6 +1613,7 @@ function parseGradientContent(descriptor: DescriptorGradientContent) {
1611
1613
  const result = parseGradient(descriptor.Grad) as (EffectSolidGradient | EffectNoiseGradient) & ExtraGradientInfo;
1612
1614
  result.style = GrdT.decode(descriptor.Type);
1613
1615
  if (descriptor.Dthr !== undefined) result.dither = descriptor.Dthr;
1616
+ if (descriptor.gradientsInterpolationMethod !== undefined) result.interpolationMethod = gradientInterpolationMethodType.decode(descriptor.gradientsInterpolationMethod);
1614
1617
  if (descriptor.Rvrs !== undefined) result.reverse = descriptor.Rvrs;
1615
1618
  if (descriptor.Angl !== undefined) result.angle = parseAngle(descriptor.Angl);
1616
1619
  if (descriptor['Scl '] !== undefined) result.scale = parsePercent(descriptor['Scl ']);
@@ -1650,6 +1653,7 @@ export function parseVectorContent(descriptor: DescriptorVectorContent): VectorC
1650
1653
  function serializeGradientContent(content: (EffectSolidGradient | EffectNoiseGradient) & ExtraGradientInfo) {
1651
1654
  const result: DescriptorGradientContent = {} as any;
1652
1655
  if (content.dither !== undefined) result.Dthr = content.dither;
1656
+ if (content.interpolationMethod !== undefined) result.gradientsInterpolationMethod = gradientInterpolationMethodType.encode(content.interpolationMethod);
1653
1657
  if (content.reverse !== undefined) result.Rvrs = content.reverse;
1654
1658
  if (content.angle !== undefined) result.Angl = unitsAngle(content.angle);
1655
1659
  result.Type = GrdT.encode(content.style);
package/src/helpers.ts CHANGED
@@ -136,6 +136,7 @@ export interface LayerChannelData {
136
136
  right: number;
137
137
  bottom: number;
138
138
  mask?: Bounds;
139
+ realMask?: Bounds;
139
140
  }
140
141
 
141
142
  export function offsetForChannel(channelId: ChannelID, cmyk: boolean) {
@@ -1,22 +1,10 @@
1
1
  import { toByteArray } from 'base64-js';
2
2
  import { BlendMode, ImageResources, RenderingIntent } from './psd';
3
- import {
4
- PsdReader, readPascalString, readUnicodeString, readUint32, readUint16, readUint8, readFloat64,
5
- readBytes, skipBytes, readFloat32, readInt16, readFixedPoint32, readSignature, checkSignature,
6
- readSection, readColor, readInt32
7
- } from './psdReader';
8
- import {
9
- PsdWriter, writePascalString, writeUnicodeString, writeUint32, writeUint8, writeFloat64, writeUint16,
10
- writeBytes, writeInt16, writeFloat32, writeFixedPoint32, writeUnicodeStringWithPadding, writeColor, writeSignature,
11
- writeSection, writeInt32,
12
- } from './psdWriter';
3
+ import { PsdReader, readUnicodeString, readUint32, readUint16, readUint8, readFloat64, readBytes, skipBytes, readFloat32, readInt16, readFixedPoint32, readSignature, checkSignature, readSection, readColor, readInt32 } from './psdReader';
4
+ import { PsdWriter, writeUnicodeString, writeUint32, writeUint8, writeFloat64, writeUint16, writeBytes, writeInt16, writeFloat32, writeFixedPoint32, writeUnicodeStringWithPadding, writeColor, writeSignature, writeSection, writeInt32, } from './psdWriter';
13
5
  import { createCanvasFromData, createEnum, MOCK_HANDLERS } from './helpers';
14
6
  import { decodeString, encodeString } from './utf8';
15
- import {
16
- ESliceBGColorType, ESliceHorzAlign, ESliceOrigin, ESliceType, ESliceVertAlign, frac,
17
- FractionDescriptor, parseTrackList, readVersionAndDescriptor, serializeTrackList, TimelineTrackDescriptor,
18
- TimeScopeDescriptor, writeVersionAndDescriptor
19
- } from './descriptor';
7
+ import { ESliceBGColorType, ESliceHorzAlign, ESliceOrigin, ESliceType, ESliceVertAlign, frac, FractionDescriptor, parseTrackList, readVersionAndDescriptor, serializeTrackList, TimelineTrackDescriptor, TimeScopeDescriptor, writeVersionAndDescriptor } from './descriptor';
20
8
 
21
9
  export interface ResourceHandler {
22
10
  key: number;
@@ -62,6 +50,38 @@ function writeUtf8String(writer: PsdWriter, value: string) {
62
50
  writeBytes(writer, buffer);
63
51
  }
64
52
 
53
+ function readEncodedString(reader: PsdReader) {
54
+ const length = readUint8(reader);
55
+ const buffer = readBytes(reader, length);
56
+
57
+ let notAscii = false;
58
+ for (let i = 0; i < buffer.byteLength; i++) {
59
+ if (buffer[i] & 0x80) {
60
+ notAscii = true;
61
+ break;
62
+ }
63
+ }
64
+
65
+ if (notAscii) {
66
+ const decoder = new TextDecoder('gbk');
67
+ return decoder.decode(buffer)
68
+ } else {
69
+ return decodeString(buffer);
70
+ }
71
+ }
72
+
73
+ function writeEncodedString(writer: PsdWriter, value: string) {
74
+ let ascii = '';
75
+
76
+ for (let i = 0, code = value.codePointAt(i++); code !== undefined; code = value.codePointAt(i++)) {
77
+ ascii += code > 0x7f ? '?' : String.fromCodePoint(code);
78
+ }
79
+
80
+ const buffer = encodeString(ascii);
81
+ writeUint8(writer, buffer.byteLength);
82
+ writeBytes(writer, buffer);
83
+ }
84
+
65
85
  MOCK_HANDLERS && addHandler(
66
86
  1028, // IPTC-NAA record
67
87
  target => (target as any)._ir1028 !== undefined,
@@ -273,16 +293,22 @@ addHandler(
273
293
  1006,
274
294
  target => target.alphaChannelNames !== undefined,
275
295
  (reader, target, left) => {
276
- target.alphaChannelNames = [];
296
+ if (!target.alphaChannelNames) { // skip if the unicode versions are already read
297
+ target.alphaChannelNames = [];
277
298
 
278
- while (left() > 0) {
279
- const value = readPascalString(reader, 1);
280
- target.alphaChannelNames.push(value);
299
+ while (left() > 0) {
300
+ const value = readEncodedString(reader);
301
+ // const value = readPascalString(reader, 1);
302
+ target.alphaChannelNames.push(value);
303
+ }
304
+ } else {
305
+ skipBytes(reader, left());
281
306
  }
282
307
  },
283
308
  (writer, target) => {
284
309
  for (const name of target.alphaChannelNames!) {
285
- writePascalString(writer, name, 1);
310
+ writeEncodedString(writer, name);
311
+ // writePascalString(writer, name, 1);
286
312
  }
287
313
  },
288
314
  );
package/src/psd.ts CHANGED
@@ -264,7 +264,7 @@ export type Justification = 'left' | 'right' | 'center' | 'justify-left' | 'just
264
264
  export type LineCapType = 'butt' | 'round' | 'square';
265
265
  export type LineJoinType = 'miter' | 'round' | 'bevel';
266
266
  export type LineAlignment = 'inside' | 'center' | 'outside';
267
- export type InterpolationMethod = 'classic' | 'perceptual' | 'linear';
267
+ export type InterpolationMethod = 'classic' | 'perceptual' | 'linear' | 'smooth';
268
268
 
269
269
  export interface Warp {
270
270
  style?: WarpStyle;
@@ -457,6 +457,7 @@ export interface ExtraGradientInfo {
457
457
  scale?: number;
458
458
  angle?: number;
459
459
  dither?: boolean;
460
+ interpolationMethod?: InterpolationMethod;
460
461
  reverse?: boolean;
461
462
  align?: boolean;
462
463
  offset?: { x: number; y: number; };
@@ -1322,6 +1323,7 @@ export interface LayerAdditionalInfo {
1322
1323
  id?: number; // layer id
1323
1324
  version?: number; // layer version
1324
1325
  mask?: LayerMaskData;
1326
+ realMask?: LayerMaskData;
1325
1327
  blendClippendElements?: boolean; // has to be set to `true` when using `color burn` blend mode (otherwise `transparencyShapesLayer` is set incorrectly)
1326
1328
  blendInteriorElements?: boolean;
1327
1329
  knockout?: boolean;
@@ -1441,6 +1443,12 @@ export interface LayerAdditionalInfo {
1441
1443
  colorSpace: Color;
1442
1444
  opacity: number;
1443
1445
  };
1446
+ blendingRanges?: {
1447
+ compositeGrayBlendSource: number[];
1448
+ compositeGraphBlendDestinationRange: number[];
1449
+ ranges: { sourceRange: number[]; destRange: number[]; }[];
1450
+ };
1451
+ vowv?: number; // ???
1444
1452
 
1445
1453
  // Base64 encoded raw EngineData, currently just kept in original state to support
1446
1454
  // loading and modifying PSD file without breaking text layers.
package/src/psdReader.ts CHANGED
@@ -326,7 +326,7 @@ export function readPsd(reader: PsdReader, readOptions: ReadOptions = {}) {
326
326
  }
327
327
 
328
328
  if (left() >= 12) {
329
- readAdditionalLayerInfo(reader, psd, psd,);
329
+ readAdditionalLayerInfo(reader, psd, psd);
330
330
  } else {
331
331
  // opt.logMissingFeatures && console.log('skipping leftover bytes', left());
332
332
  skipBytes(reader, left());
@@ -443,10 +443,10 @@ function readLayerRecord(reader: PsdReader, psd: Psd) {
443
443
  skipBytes(reader, 1);
444
444
 
445
445
  readSection(reader, 1, left => {
446
- const mask = readLayerMaskData(reader);
447
- if (mask) layer.mask = mask;
446
+ readLayerMaskData(reader, layer);
448
447
 
449
- /*const blendingRanges =*/ readLayerBlendingRanges(reader);
448
+ const blendingRanges = readLayerBlendingRanges(reader);
449
+ if (blendingRanges) layer.blendingRanges = blendingRanges;
450
450
  layer.name = readPascalString(reader, 1); // should be padded to 4, but is not sometimes
451
451
 
452
452
  // HACK: fix for sometimes layer.name string not being padded correctly, just skip until we get valid signature
@@ -460,11 +460,13 @@ function readLayerRecord(reader: PsdReader, psd: Psd) {
460
460
  return { layer, channels };
461
461
  }
462
462
 
463
- function readLayerMaskData(reader: PsdReader) {
463
+ function readLayerMaskData(reader: PsdReader, layer: Layer) {
464
464
  return readSection<LayerMaskData | undefined>(reader, 1, left => {
465
465
  if (!left()) return undefined;
466
466
 
467
467
  const mask: LayerMaskData = {};
468
+ layer.mask = mask;
469
+
468
470
  mask.top = readInt32(reader);
469
471
  mask.left = readInt32(reader);
470
472
  mask.bottom = readInt32(reader);
@@ -476,6 +478,22 @@ function readLayerMaskData(reader: PsdReader) {
476
478
  mask.disabled = (flags & LayerMaskFlags.LayerMaskDisabled) !== 0;
477
479
  mask.fromVectorData = (flags & LayerMaskFlags.LayerMaskFromRenderingOtherData) !== 0;
478
480
 
481
+ if (left() >= 18) {
482
+ const realMask: LayerMaskData = {};
483
+ layer.realMask = realMask;
484
+
485
+ const realFlags = readUint8(reader);
486
+ realMask.positionRelativeToLayer = (realFlags & LayerMaskFlags.PositionRelativeToLayer) !== 0;
487
+ realMask.disabled = (realFlags & LayerMaskFlags.LayerMaskDisabled) !== 0;
488
+ realMask.fromVectorData = (realFlags & LayerMaskFlags.LayerMaskFromRenderingOtherData) !== 0;
489
+
490
+ realMask.defaultColor = readUint8(reader); // Real user mask background. 0 or 255.
491
+ realMask.top = readInt32(reader);
492
+ realMask.left = readInt32(reader);
493
+ realMask.bottom = readInt32(reader);
494
+ realMask.right = readInt32(reader);
495
+ }
496
+
479
497
  if (flags & LayerMaskFlags.MaskHasParametersAppliedToIt) {
480
498
  const params = readUint8(reader);
481
499
  if (params & MaskParams.UserMaskDensity) mask.userMaskDensity = readUint8(reader) / 0xff;
@@ -484,37 +502,23 @@ function readLayerMaskData(reader: PsdReader) {
484
502
  if (params & MaskParams.VectorMaskFeather) mask.vectorMaskFeather = readFloat64(reader);
485
503
  }
486
504
 
487
- if (left() > 2) {
488
- // TODO: handle these values, this is RealUserMask
489
- /*const realFlags = readUint8(reader);
490
- const realUserMaskBackground = readUint8(reader);
491
- const top2 = readInt32(reader);
492
- const left2 = readInt32(reader);
493
- const bottom2 = readInt32(reader);
494
- const right2 = readInt32(reader);
495
-
496
- // TEMP
497
- (mask as any)._real = { realFlags, realUserMaskBackground, top2, left2, bottom2, right2 };*/
498
-
499
- if (reader.logMissingFeatures) {
500
- reader.log('Unhandled extra real user mask params');
501
- }
502
- }
503
-
504
505
  skipBytes(reader, left());
505
- return mask;
506
506
  });
507
507
  }
508
508
 
509
+ function readBlendingRange(reader: PsdReader) {
510
+ return [readUint8(reader), readUint8(reader), readUint8(reader), readUint8(reader)];
511
+ }
512
+
509
513
  function readLayerBlendingRanges(reader: PsdReader) {
510
514
  return readSection(reader, 1, left => {
511
- const compositeGrayBlendSource = readUint32(reader);
512
- const compositeGraphBlendDestinationRange = readUint32(reader);
513
- const ranges = [];
515
+ const compositeGrayBlendSource = readBlendingRange(reader);
516
+ const compositeGraphBlendDestinationRange = readBlendingRange(reader);
517
+ const ranges: { sourceRange: number[]; destRange: number[]; }[] = [];
514
518
 
515
519
  while (left() > 0) {
516
- const sourceRange = readUint32(reader);
517
- const destRange = readUint32(reader);
520
+ const sourceRange = readBlendingRange(reader);
521
+ const destRange = readBlendingRange(reader);
518
522
  ranges.push({ sourceRange, destRange });
519
523
  }
520
524
 
@@ -567,13 +571,13 @@ function readLayerChannelImageData(reader: PsdReader, psd: Psd, layer: Layer, ch
567
571
 
568
572
  if (compression > 3) throw new Error(`Invalid compression: ${compression}`);
569
573
 
570
- if (channel.id === ChannelID.UserMask) {
571
- const mask = layer.mask;
572
-
573
- if (!mask) throw new Error(`Missing layer mask data`);
574
+ if (channel.id === ChannelID.UserMask || channel.id === ChannelID.RealUserMask) {
575
+ const mask = channel.id === ChannelID.UserMask ? layer.mask : layer.realMask;
576
+ if (!mask) throw new Error(`Missing layer ${channel.id === ChannelID.UserMask ? 'mask' : 'real mask'} data`);
574
577
 
575
578
  const maskWidth = (mask.right || 0) - (mask.left || 0);
576
579
  const maskHeight = (mask.bottom || 0) - (mask.top || 0);
580
+ if (maskWidth < 0 || maskHeight < 0 || maskWidth > 30000 || maskHeight > 30000) throw new Error('Invalid mask size');
577
581
 
578
582
  if (maskWidth && maskHeight) {
579
583
  const maskData = createImageDataBitDepth(maskWidth, maskHeight, psd.bitsPerChannel ?? 8);
@@ -583,8 +587,13 @@ function readLayerChannelImageData(reader: PsdReader, psd: Psd, layer: Layer, ch
583
587
  readData(reader, channel.length, maskData, compression, maskWidth, maskHeight, psd.bitsPerChannel ?? 8, 0, reader.large, 4);
584
588
 
585
589
  if (RAW_IMAGE_DATA) {
586
- (layer as any).maskDataRawCompression = compression;
587
- (layer as any).maskDataRaw = new Uint8Array(reader.view.buffer, reader.view.byteOffset + start, reader.offset - start);
590
+ if (channel.id === ChannelID.UserMask) {
591
+ (layer as any).maskDataRawCompression = compression;
592
+ (layer as any).maskDataRaw = new Uint8Array(reader.view.buffer, reader.view.byteOffset + start, reader.offset - start);
593
+ } else {
594
+ (layer as any).realMaskDataRawCompression = compression;
595
+ (layer as any).realMaskDataRaw = new Uint8Array(reader.view.buffer, reader.view.byteOffset + start, reader.offset - start);
596
+ }
588
597
  }
589
598
 
590
599
  setupGrayscale(maskData);
@@ -595,12 +604,6 @@ function readLayerChannelImageData(reader: PsdReader, psd: Psd, layer: Layer, ch
595
604
  mask.canvas = imageDataToCanvas(maskData);
596
605
  }
597
606
  }
598
- } else if (channel.id === ChannelID.RealUserMask) {
599
- if (reader.logMissingFeatures) {
600
- reader.log(`RealUserMask not supported`);
601
- }
602
-
603
- reader.offset = start + channel.length;
604
607
  } else {
605
608
  const offset = offsetForChannel(channel.id, cmyk);
606
609
  let targetData = imageData;