ag-psd 19.0.1 → 20.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/CHANGELOG.md +5 -0
  2. package/README_PSD.md +6 -0
  3. package/TODO +5 -0
  4. package/dist/abr.js +4 -4
  5. package/dist/abr.js.map +1 -1
  6. package/dist/additionalInfo.d.ts +3 -3
  7. package/dist/additionalInfo.js +99 -10
  8. package/dist/additionalInfo.js.map +1 -1
  9. package/dist/bundle.js +405 -142
  10. package/dist/descriptor.js +21 -16
  11. package/dist/descriptor.js.map +1 -1
  12. package/dist/helpers.d.ts +2 -7
  13. package/dist/helpers.js +33 -10
  14. package/dist/helpers.js.map +1 -1
  15. package/dist/imageResources.js +37 -0
  16. package/dist/imageResources.js.map +1 -1
  17. package/dist/psd.d.ts +46 -3
  18. package/dist/psd.js +8 -1
  19. package/dist/psd.js.map +1 -1
  20. package/dist/psdReader.d.ts +11 -5
  21. package/dist/psdReader.js +179 -97
  22. package/dist/psdReader.js.map +1 -1
  23. package/dist/psdWriter.js +24 -4
  24. package/dist/psdWriter.js.map +1 -1
  25. package/dist-es/abr.js +4 -4
  26. package/dist-es/abr.js.map +1 -1
  27. package/dist-es/additionalInfo.d.ts +3 -3
  28. package/dist-es/additionalInfo.js +102 -13
  29. package/dist-es/additionalInfo.js.map +1 -1
  30. package/dist-es/descriptor.js +21 -16
  31. package/dist-es/descriptor.js.map +1 -1
  32. package/dist-es/helpers.d.ts +2 -7
  33. package/dist-es/helpers.js +31 -9
  34. package/dist-es/helpers.js.map +1 -1
  35. package/dist-es/imageResources.js +37 -0
  36. package/dist-es/imageResources.js.map +1 -1
  37. package/dist-es/psd.d.ts +46 -3
  38. package/dist-es/psd.js +7 -0
  39. package/dist-es/psd.js.map +1 -1
  40. package/dist-es/psdReader.d.ts +11 -5
  41. package/dist-es/psdReader.js +178 -99
  42. package/dist-es/psdReader.js.map +1 -1
  43. package/dist-es/psdWriter.js +25 -5
  44. package/dist-es/psdWriter.js.map +1 -1
  45. package/package.json +1 -1
  46. package/src/abr.ts +4 -4
  47. package/src/additionalInfo.ts +142 -49
  48. package/src/descriptor.ts +14 -9
  49. package/src/helpers.ts +35 -18
  50. package/src/imageResources.ts +53 -0
  51. package/src/psd.ts +41 -5
  52. package/src/psdReader.ts +170 -126
  53. package/src/psdWriter.ts +33 -14
@@ -1,37 +1,10 @@
1
1
  import { fromByteArray, toByteArray } from 'base64-js';
2
2
  import { readEffects, writeEffects } from './effectsHelpers';
3
3
  import { clamp, createEnum, layerColors, MOCK_HANDLERS } from './helpers';
4
- import {
5
- LayerAdditionalInfo, BezierPath, Psd, ReadOptions, BrightnessAdjustment, ExposureAdjustment, VibranceAdjustment,
6
- ColorBalanceAdjustment, BlackAndWhiteAdjustment, PhotoFilterAdjustment, ChannelMixerChannel,
7
- ChannelMixerAdjustment, PosterizeAdjustment, ThresholdAdjustment, GradientMapAdjustment, CMYK,
8
- SelectiveColorAdjustment, ColorLookupAdjustment, LevelsAdjustmentChannel, LevelsAdjustment,
9
- CurvesAdjustment, CurvesAdjustmentChannel, HueSaturationAdjustment, HueSaturationAdjustmentChannel,
10
- PresetInfo, Color, ColorBalanceValues, WriteOptions, LinkedFile, PlacedLayerType, Warp, KeyDescriptorItem,
11
- BooleanOperation, LayerEffectsInfo, Annotation, LayerVectorMask, AnimationFrame, Timeline, PlacedLayerFilter,
12
- UnitsValue, Filter, PlacedLayer,
13
- } from './psd';
14
- import {
15
- PsdReader, readSignature, readUnicodeString, skipBytes, readUint32, readUint8, readFloat64, readUint16,
16
- readBytes, readInt16, checkSignature, readFloat32, readFixedPointPath32, readSection, readColor, readInt32,
17
- readPascalString, readUnicodeStringWithLength, readAsciiString, readPattern,
18
- } from './psdReader';
19
- import {
20
- PsdWriter, writeZeros, writeSignature, writeBytes, writeUint32, writeUint16, writeFloat64, writeUint8,
21
- writeInt16, writeFloat32, writeFixedPointPath32, writeUnicodeString, writeSection, writeUnicodeStringWithPadding,
22
- writeColor, writePascalString, writeInt32,
23
- } from './psdWriter';
24
- import {
25
- Annt, BlnM, DescriptorColor, DescriptorUnitsValue, parsePercent, parseUnits, parseUnitsOrNumber, QuiltWarpDescriptor,
26
- strokeStyleLineAlignment, strokeStyleLineCapType, strokeStyleLineJoinType, TextDescriptor, textGridding,
27
- unitsPercent, unitsValue, WarpDescriptor, warpStyle, writeVersionAndDescriptor,
28
- readVersionAndDescriptor, StrokeDescriptor, Ornt, horzVrtcToXY, LmfxDescriptor, Lfx2Descriptor,
29
- FrameListDescriptor, TimelineDescriptor, FrameDescriptor, xyToHorzVrtc, serializeEffects,
30
- parseEffects, parseColor, serializeColor, serializeVectorContent, parseVectorContent, parseTrackList,
31
- serializeTrackList, FractionDescriptor, BlrM, BlrQ, SmBQ, SmBM, DspM, UndA, Cnvr, RplS, SphM, Wvtp, ZZTy,
32
- Dstr, Chnl, MztT, Lns, blurType, DfsM, ExtT, ExtR, FlCl, CntE, WndM, Drct, IntE, IntC, FlMd,
33
- unitsPercentF, frac, ClrS, descBoundsToBounds, boundsToDescBounds,
34
- } from './descriptor';
4
+ import { LayerAdditionalInfo, BezierPath, Psd, BrightnessAdjustment, ExposureAdjustment, VibranceAdjustment, ColorBalanceAdjustment, BlackAndWhiteAdjustment, PhotoFilterAdjustment, ChannelMixerChannel, ChannelMixerAdjustment, PosterizeAdjustment, ThresholdAdjustment, GradientMapAdjustment, CMYK, SelectiveColorAdjustment, ColorLookupAdjustment, LevelsAdjustmentChannel, LevelsAdjustment, CurvesAdjustment, CurvesAdjustmentChannel, HueSaturationAdjustment, HueSaturationAdjustmentChannel, PresetInfo, Color, ColorBalanceValues, WriteOptions, LinkedFile, PlacedLayerType, Warp, KeyDescriptorItem, BooleanOperation, LayerEffectsInfo, Annotation, LayerVectorMask, AnimationFrame, Timeline, PlacedLayerFilter, UnitsValue, Filter, PlacedLayer } from './psd';
5
+ import { PsdReader, readSignature, readUnicodeString, skipBytes, readUint32, readUint8, readFloat64, readUint16, readBytes, readInt16, checkSignature, readFloat32, readFixedPointPath32, readSection, readColor, readInt32, readPascalString, readUnicodeStringWithLength, readAsciiString, readPattern, readLayerInfo, ReadOptionsExt } from './psdReader';
6
+ import { PsdWriter, writeZeros, writeSignature, writeBytes, writeUint32, writeUint16, writeFloat64, writeUint8, writeInt16, writeFloat32, writeFixedPointPath32, writeUnicodeString, writeSection, writeUnicodeStringWithPadding, writeColor, writePascalString, writeInt32 } from './psdWriter';
7
+ import { Annt, BlnM, DescriptorColor, DescriptorUnitsValue, parsePercent, parseUnits, parseUnitsOrNumber, QuiltWarpDescriptor, strokeStyleLineAlignment, strokeStyleLineCapType, strokeStyleLineJoinType, TextDescriptor, textGridding, unitsPercent, unitsValue, WarpDescriptor, warpStyle, writeVersionAndDescriptor, readVersionAndDescriptor, StrokeDescriptor, Ornt, horzVrtcToXY, LmfxDescriptor, Lfx2Descriptor, FrameListDescriptor, TimelineDescriptor, FrameDescriptor, xyToHorzVrtc, serializeEffects, parseEffects, parseColor, serializeColor, serializeVectorContent, parseVectorContent, parseTrackList, serializeTrackList, FractionDescriptor, BlrM, BlrQ, SmBQ, SmBM, DspM, UndA, Cnvr, RplS, SphM, Wvtp, ZZTy, Dstr, Chnl, MztT, Lns, blurType, DfsM, ExtT, ExtR, FlCl, CntE, WndM, Drct, IntE, IntC, FlMd, unitsPercentF, frac, ClrS, descBoundsToBounds, boundsToDescBounds } from './descriptor';
35
8
  import { serializeEngineData, parseEngineData } from './engineData';
36
9
  import { encodeEngineData, decodeEngineData } from './text';
37
10
 
@@ -41,7 +14,7 @@ export interface ExtendedWriteOptions extends WriteOptions {
41
14
  }
42
15
 
43
16
  type HasMethod = (target: LayerAdditionalInfo) => boolean;
44
- type ReadMethod = (reader: PsdReader, target: LayerAdditionalInfo, left: () => number, psd: Psd, options: ReadOptions) => void;
17
+ type ReadMethod = (reader: PsdReader, target: LayerAdditionalInfo, left: () => number, psd: Psd, options: ReadOptionsExt) => void;
45
18
  type WriteMethod = (writer: PsdWriter, target: LayerAdditionalInfo, psd: Psd, options: ExtendedWriteOptions) => void;
46
19
 
47
20
  export interface InfoHandler {
@@ -90,9 +63,11 @@ addHandler(
90
63
 
91
64
  if (readInt16(reader) !== 50) throw new Error(`Invalid TySh text version`);
92
65
  const text: TextDescriptor = readVersionAndDescriptor(reader);
66
+ // console.log(require('util').inspect(text, false, 99, false), 'utf8');
93
67
 
94
68
  if (readInt16(reader) !== 1) throw new Error(`Invalid TySh warp version`);
95
69
  const warp: WarpDescriptor = readVersionAndDescriptor(reader);
70
+ // console.log(require('util').inspect(warp, false, 99, false), 'utf8');
96
71
 
97
72
  target.text = {
98
73
  transform,
@@ -120,6 +95,7 @@ addHandler(
120
95
  if (text.EngineData) {
121
96
  const engineData = parseEngineData(text.EngineData);
122
97
  const textData = decodeEngineData(engineData);
98
+ // console.log(require('util').inspect(engineData, false, 99, false), 'utf8');
123
99
 
124
100
  // require('fs').writeFileSync(`layer-${target.name}.txt`, require('util').inspect(engineData, false, 99, false), 'utf8');
125
101
  // const before = parseEngineData(text.EngineData);
@@ -732,10 +708,21 @@ interface CustomDescriptor {
732
708
  layerTime?: number;
733
709
  }
734
710
 
711
+ interface CmlsDescriptor {
712
+ origFXRefPoint?: { Hrzn: number; Vrtc: number; };
713
+ LyrI: number;
714
+ layerSettings: {
715
+ enab?: boolean;
716
+ Ofst?: { Hrzn: number; Vrtc: number; };
717
+ FXRefPoint?: { Hrzn: number; Vrtc: number; };
718
+ compList: number[];
719
+ }[];
720
+ }
721
+
735
722
  addHandler(
736
723
  'shmd',
737
724
  target => target.timestamp !== undefined || target.animationFrames !== undefined ||
738
- target.animationFrameFlags !== undefined || target.timeline !== undefined,
725
+ target.animationFrameFlags !== undefined || target.timeline !== undefined || target.comps !== undefined,
739
726
  (reader, target, left, _, options) => {
740
727
  const count = readUint32(reader);
741
728
 
@@ -798,8 +785,25 @@ addHandler(
798
785
 
799
786
  target.timeline = timeline;
800
787
  // console.log('tmln:result', target.name, target.id, require('util').inspect(timeline, false, 99, true));
788
+ } else if (key === 'cmls') {
789
+ const desc = readVersionAndDescriptor(reader) as CmlsDescriptor;
790
+ // console.log('cmls', require('util').inspect(desc, false, 99, true));
791
+
792
+ target.comps = {
793
+ settings: [],
794
+ };
795
+
796
+ if (desc.origFXRefPoint) target.comps.originalEffectsReferencePoint = { x: desc.origFXRefPoint.Hrzn, y: desc.origFXRefPoint.Vrtc };
797
+
798
+ for (const item of desc.layerSettings) {
799
+ target.comps.settings.push({ compList: item.compList });
800
+ const t = target.comps.settings[target.comps.settings.length - 1];
801
+ if ('enab' in item) t.enabled = item.enab;
802
+ if (item.Ofst) t.offset = { x: item.Ofst.Hrzn, y: item.Ofst.Vrtc };
803
+ if (item.FXRefPoint) t.effectsReferencePoint = { x: item.FXRefPoint.Hrzn, y: item.FXRefPoint.Vrtc };
804
+ }
801
805
  } else {
802
- options.logDevFeatures && console.log('Unhandled "shmd" section key', key);
806
+ options.logMissingFeatures && console.log('Unhandled "shmd" section key', key);
803
807
  }
804
808
 
805
809
  skipBytes(reader, left());
@@ -809,13 +813,14 @@ addHandler(
809
813
  skipBytes(reader, left());
810
814
  },
811
815
  (writer, target, _, options) => {
812
- const { animationFrames, animationFrameFlags, timestamp, timeline } = target;
816
+ const { animationFrames, animationFrameFlags, timestamp, timeline, comps } = target;
813
817
 
814
818
  let count = 0;
815
819
  if (animationFrames) count++;
816
820
  if (animationFrameFlags) count++;
817
821
  if (timeline) count++;
818
822
  if (timestamp !== undefined) count++;
823
+ if (comps) count++;
819
824
  writeUint32(writer, count);
820
825
 
821
826
  if (animationFrames) {
@@ -883,7 +888,7 @@ addHandler(
883
888
  desc.trackList = serializeTrackList(timeline.tracks);
884
889
  }
885
890
 
886
- const id = options.layerToId.get(target) || target.id || 0;
891
+ const id = options.layerToId.get(target) || target.id;
887
892
  if (!id) throw new Error('You need to provide layer.id value whan writing document with animations');
888
893
  desc.LyrI = id;
889
894
 
@@ -904,6 +909,38 @@ addHandler(
904
909
  writeVersionAndDescriptor(writer, '', 'metadata', desc);
905
910
  }, true);
906
911
  }
912
+
913
+ if (comps) {
914
+ writeSignature(writer, '8BIM');
915
+ writeSignature(writer, 'cmls');
916
+ writeUint8(writer, 0); // copy (always false)
917
+ writeZeros(writer, 3);
918
+ writeSection(writer, 2, () => {
919
+ const id = options.layerToId.get(target) || target.id;
920
+ if (!id) throw new Error('You need to provide layer.id value whan writing document with layer comps');
921
+
922
+ const desc: CmlsDescriptor = {} as any;
923
+
924
+ if (comps.originalEffectsReferencePoint) {
925
+ desc.origFXRefPoint = { Hrzn: comps.originalEffectsReferencePoint.x, Vrtc: comps.originalEffectsReferencePoint.y };
926
+ }
927
+
928
+ desc.LyrI = id;
929
+ desc.layerSettings = [];
930
+
931
+ for (const item of comps.settings) {
932
+ const t: CmlsDescriptor['layerSettings'][0] = {} as any;
933
+ if (item.enabled !== undefined) t.enab = item.enabled;
934
+ if (item.offset) t.Ofst = { Hrzn: item.offset.x, Vrtc: item.offset.y };
935
+ if (item.effectsReferencePoint) t.FXRefPoint = { Hrzn: item.effectsReferencePoint.x, Vrtc: item.effectsReferencePoint.y };
936
+ t.compList = item.compList;
937
+ desc.layerSettings.push(t);
938
+ }
939
+
940
+ // console.log('cmls', require('util').inspect(desc, false, 99, true));
941
+ writeVersionAndDescriptor(writer, '', 'null', desc);
942
+ }, true);
943
+ }
907
944
  },
908
945
  );
909
946
 
@@ -3304,6 +3341,52 @@ addHandler(
3304
3341
  },
3305
3342
  );
3306
3343
 
3344
+ addHandler(
3345
+ 'Lr16',
3346
+ () => false,
3347
+ (reader, _target, _left, psd, options) => {
3348
+ readLayerInfo(reader, psd, options);
3349
+ },
3350
+ (_writer, _target) => {
3351
+ },
3352
+ );
3353
+
3354
+ addHandler(
3355
+ 'LMsk',
3356
+ hasKey('userMask'),
3357
+ (reader, target) => {
3358
+ target.userMask = {
3359
+ colorSpace: readColor(reader),
3360
+ opacity: readUint16(reader) / 0xff,
3361
+ };
3362
+ const flag = readUint8(reader);
3363
+ if (flag !== 128) throw new Error('Invalid flag value');
3364
+ skipBytes(reader, 1);
3365
+ },
3366
+ (writer, target) => {
3367
+ const userMask = target.userMask!;
3368
+ writeColor(writer, userMask.colorSpace);
3369
+ writeUint16(writer, clamp(userMask.opacity, 0, 1) * 0xff);
3370
+ writeUint8(writer, 128);
3371
+ writeZeros(writer, 1);
3372
+ },
3373
+ );
3374
+
3375
+ if (MOCK_HANDLERS) {
3376
+ addHandler(
3377
+ 'vowv', // appears with Lr16 section ?
3378
+ _ => false,
3379
+ (reader, target, left) => {
3380
+ const value = readUint32(reader); // 2 ????
3381
+ reader; target;
3382
+ console.log('vowv', { value }, left());
3383
+ },
3384
+ (_writer, _target) => {
3385
+ // TODO: write
3386
+ },
3387
+ );
3388
+ }
3389
+
3307
3390
  if (MOCK_HANDLERS) {
3308
3391
  addHandler(
3309
3392
  'Patt',
@@ -3614,30 +3697,33 @@ addHandlerAlias('lnkD', 'lnk2');
3614
3697
  addHandlerAlias('lnk3', 'lnk2');
3615
3698
  addHandlerAlias('lnkE', 'lnk2');
3616
3699
 
3617
- interface ExtensionDesc {
3618
- generatorSettings: {
3619
- generator_45_assets: { json: string; };
3620
- layerTime: number;
3621
- };
3700
+ interface PthsDescriptor {
3701
+ pathList: {
3702
+ _classID: 'pathInfoClass';
3703
+ pathUnicodeName: string;
3704
+ pathSymmetryClass: {
3705
+ _classID: 'pathSymmetryClass';
3706
+ pathSymmetryMode: string; // 'pathSymmetryModeEnum.pathSymmetryModeBasicPath'
3707
+ };
3708
+ }[];
3622
3709
  }
3623
3710
 
3624
3711
  addHandler(
3625
3712
  'pths',
3626
3713
  hasKey('pathList'),
3627
3714
  (reader, target) => {
3628
- const descriptor = readVersionAndDescriptor(reader);
3629
-
3630
- target.pathList = []; // TODO: read paths (find example with non-empty list)
3631
-
3632
- descriptor;
3633
- // console.log('pths', descriptor); // TODO: remove this
3715
+ const desc = readVersionAndDescriptor(reader, true) as PthsDescriptor;
3716
+ // console.log(require('util').inspect(desc, false, 99, true));
3717
+ // if (options.throwForMissingFeatures && desc?.pathList?.length) throw new Error('non-empty pathList in `pths`');
3718
+ desc;
3719
+ target.pathList = []; // TODO: read paths
3634
3720
  },
3635
3721
  (writer, _target) => {
3636
- const descriptor = {
3722
+ const desc: PthsDescriptor = {
3637
3723
  pathList: [], // TODO: write paths
3638
3724
  };
3639
3725
 
3640
- writeVersionAndDescriptor(writer, '', 'pathsDataClass', descriptor);
3726
+ writeVersionAndDescriptor(writer, '', 'pathsDataClass', desc);
3641
3727
  },
3642
3728
  );
3643
3729
 
@@ -4804,6 +4890,13 @@ addHandler(
4804
4890
  },
4805
4891
  );
4806
4892
 
4893
+ interface ExtensionDesc {
4894
+ generatorSettings: {
4895
+ generator_45_assets: { json: string; };
4896
+ layerTime: number;
4897
+ };
4898
+ }
4899
+
4807
4900
  // extension settings ?, ignore it
4808
4901
  addHandler(
4809
4902
  'extn',
package/src/descriptor.ts CHANGED
@@ -136,6 +136,8 @@ const fieldToExtType: ExtTypeDict = {
136
136
  sdwM: makeType('Parameters', 'adaptCorrectTones'),
137
137
  hglM: makeType('Parameters', 'adaptCorrectTones'),
138
138
  customShape: makeType('', 'customShape'),
139
+ origFXRefPoint: nullType,
140
+ FXRefPoint: nullType,
139
141
  };
140
142
 
141
143
  const fieldToArrayExtType: ExtTypeDict = {
@@ -167,6 +169,8 @@ const fieldToArrayExtType: ExtTypeDict = {
167
169
  puppetShapeList: makeType('', 'puppetShape'),
168
170
  channelDenoise: makeType('', 'channelDenoiseParams'),
169
171
  ShrP: makeType('', 'Pnt '),
172
+ layerSettings: nullType,
173
+ list: nullType,
170
174
  };
171
175
 
172
176
  const typeToField: { [key: string]: string[]; } = {
@@ -174,7 +178,7 @@ const typeToField: { [key: string]: string[]; } = {
174
178
  'Txt ', 'printerName', 'Nm ', 'Idnt', 'blackAndWhitePresetFileName', 'LUT3DFileName',
175
179
  'presetFileName', 'curvesPresetFileName', 'mixerPresetFileName', 'placed', 'description', 'reason',
176
180
  'artboardPresetName', 'json', 'clipID', 'relPath', 'fullPath', 'mediaDescriptor', 'Msge',
177
- 'altTag', 'url', 'cellText', 'preset', 'KnNm', 'FPth',
181
+ 'altTag', 'url', 'cellText', 'preset', 'KnNm', 'FPth', 'comment',
178
182
  ],
179
183
  'tdta': [
180
184
  'EngineData', 'LUT3DFileData', 'indexArray', 'originalVertexArray', 'deformedVertexArray',
@@ -192,7 +196,7 @@ const typeToField: { [key: string]: string[]; } = {
192
196
  'topOutset', 'leftOutset', 'bottomOutset', 'rightOutset', 'filterID', 'meshQuality',
193
197
  'meshExpansion', 'meshRigidity', 'VrsM', 'VrsN', 'NmbG', 'WLMn', 'WLMx', 'AmMn', 'AmMx', 'SclH', 'SclV',
194
198
  'Lvl ', 'TlNm', 'TlOf', 'FlRs', 'Thsh', 'ShrS', 'ShrE', 'FlRs', 'Vrnc', 'Strg', 'ExtS', 'ExtD',
195
- 'HrzS', 'VrtS', 'NmbR', 'EdgF', 'Ang1', 'Ang2', 'Ang3', 'Ang4',
199
+ 'HrzS', 'VrtS', 'NmbR', 'EdgF', 'Ang1', 'Ang2', 'Ang3', 'Ang4', 'lastAppliedComp', 'capturedInfo',
196
200
  ],
197
201
  'enum': [
198
202
  'textGridding', 'Ornt', 'warpStyle', 'warpRotate', 'Inte', 'Bltn', 'ClrS', 'BlrQ',
@@ -237,7 +241,7 @@ const typeToField: { [key: string]: string[]; } = {
237
241
  'sheetTimelineOptions', 'audioClipList', 'trackList', 'globalTrackList', 'keyList', 'audioClipList',
238
242
  'warpValues', 'selectedPin', 'Pts ', 'SbpL', 'pathComponents', 'pinOffsets', 'posFinalPins',
239
243
  'pinVertexIndices', 'PinP', 'PnRt', 'PnOv', 'PnDp', 'filterFXList', 'puppetShapeList', 'ShrP',
240
- 'channelDenoise', 'Mtrx',
244
+ 'channelDenoise', 'Mtrx', 'layerSettings', 'list', 'compList',
241
245
  ],
242
246
  'ObAr': ['meshPoints', 'quiltSliceX', 'quiltSliceY'],
243
247
  'obj ': ['null', 'Chnl'],
@@ -285,6 +289,7 @@ const fieldToArrayType: Dict = {
285
289
  ShrP: 'Objc',
286
290
  channelDenoise: 'Objc',
287
291
  Mtrx: 'long',
292
+ compList: 'long',
288
293
  };
289
294
 
290
295
  const fieldToType: Dict = {};
@@ -336,7 +341,7 @@ export function readAsciiStringOrClassId(reader: PsdReader) {
336
341
  }
337
342
 
338
343
  function writeAsciiStringOrClassId(writer: PsdWriter, value: string) {
339
- if (value.length === 4 && value !== 'warp' && value !== 'time' && value !== 'hold') {
344
+ if (value.length === 4 && value !== 'warp' && value !== 'time' && value !== 'hold' && value !== 'list') {
340
345
  // write classId
341
346
  writeInt32(writer, 0);
342
347
  writeSignature(writer, value);
@@ -473,9 +478,9 @@ function readOSType(reader: PsdReader, type: string, includeClass: boolean) {
473
478
  const items: any[] = [];
474
479
 
475
480
  for (let i = 0; i < length; i++) {
476
- const type = readSignature(reader);
477
- // console.log(' >', type);
478
- items.push(readOSType(reader, type, includeClass));
481
+ const itemType = readSignature(reader);
482
+ // console.log(' >', itemType);
483
+ items.push(readOSType(reader, itemType, includeClass));
479
484
  }
480
485
 
481
486
  return items;
@@ -497,9 +502,9 @@ function readOSType(reader: PsdReader, type: string, includeClass: boolean) {
497
502
  case 'TEXT': // String
498
503
  return readUnicodeString(reader);
499
504
  case 'enum': { // Enumerated
500
- const type = readAsciiStringOrClassId(reader);
505
+ const enumType = readAsciiStringOrClassId(reader);
501
506
  const value = readAsciiStringOrClassId(reader);
502
- return `${type}.${value}`;
507
+ return `${enumType}.${value}`;
503
508
  }
504
509
  case 'long': // Integer
505
510
  return readInt32(reader);
package/src/helpers.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { fromByteArray } from 'base64-js';
2
2
  import { deflate } from 'pako';
3
- import { Layer, BlendMode, LayerColor } from './psd';
3
+ import { Layer, BlendMode, LayerColor, PixelData, PixelArray } from './psd';
4
4
 
5
5
  export const MOCK_HANDLERS = false;
6
6
  export const RAW_IMAGE_DATA = false;
@@ -138,14 +138,6 @@ export interface LayerChannelData {
138
138
  mask?: Bounds;
139
139
  }
140
140
 
141
- export type PixelArray = Uint8ClampedArray | Uint8Array;
142
-
143
- export interface PixelData {
144
- data: PixelArray;
145
- width: number;
146
- height: number;
147
- }
148
-
149
141
  export function offsetForChannel(channelId: ChannelID, cmyk: boolean) {
150
142
  switch (channelId) {
151
143
  case ChannelID.Color0: return 0;
@@ -174,26 +166,51 @@ export function hasAlpha(data: PixelData) {
174
166
  }
175
167
 
176
168
  export function resetImageData({ data }: PixelData) {
177
- const buffer = new Uint32Array(data.buffer);
178
- const size = buffer.length | 0;
169
+ const alpha = (data instanceof Uint32Array) ? (0xffffffff >>> 0) : ((data instanceof Uint16Array) ? 0xffff : 0xff);
170
+
171
+ for (let p = 0, size = data.length | 0; p < size; p = (p + 4) | 0) {
172
+ data[p + 0] = 0;
173
+ data[p + 1] = 0;
174
+ data[p + 2] = 0;
175
+ data[p + 3] = alpha;
176
+ }
177
+ }
179
178
 
180
- for (let p = 0; p < size; p = (p + 1) | 0) {
181
- buffer[p] = 0xff000000;
179
+ export function imageDataToCanvas(pixelData: PixelData) {
180
+ const canvas = createCanvas(pixelData.width, pixelData.height);
181
+ let imageData: ImageData;
182
+
183
+ if (pixelData.data instanceof Uint8ClampedArray) {
184
+ imageData = pixelData as ImageData;
185
+ } else {
186
+ imageData = createImageData(pixelData.width, pixelData.height);
187
+ const src = pixelData.data;
188
+ const dst = imageData.data;
189
+ const shift = (src instanceof Uint32Array) ? 24 : ((src instanceof Uint16Array) ? 8 : 0);
190
+
191
+ for (let i = 0, size = src.length; i < size; i++) {
192
+ dst[i] = src[i] >>> shift;
193
+ }
182
194
  }
195
+
196
+ canvas.getContext('2d')!.putImageData(imageData, 0, 0);
197
+ return canvas;
183
198
  }
184
199
 
185
200
  export function decodeBitmap(input: PixelArray, output: PixelArray, width: number, height: number) {
201
+ if (input instanceof Uint32Array || input instanceof Uint16Array) throw new Error('Invalid bit depth');
202
+
186
203
  for (let y = 0, p = 0, o = 0; y < height; y++) {
187
204
  for (let x = 0; x < width;) {
188
205
  let b = input[o++];
189
206
 
190
- for (let i = 0; i < 8 && x < width; i++, x++) {
207
+ for (let i = 0; i < 8 && x < width; i++, x++, p += 4) {
191
208
  const v = b & 0x80 ? 0 : 255;
192
209
  b = b << 1;
193
- output[p++] = v;
194
- output[p++] = v;
195
- output[p++] = v;
196
- output[p++] = 255;
210
+ output[p + 0] = v;
211
+ output[p + 1] = v;
212
+ output[p + 2] = v;
213
+ output[p + 3] = 255;
197
214
  }
198
215
  }
199
216
  }
@@ -566,6 +566,59 @@ addHandler(
566
566
  },
567
567
  );
568
568
 
569
+ interface LayerCompsDescriptor {
570
+ list: {
571
+ _classID: 'Comp';
572
+ 'Nm ': string;
573
+ compID: number;
574
+ capturedInfo: number;
575
+ comment?: string;
576
+ }[];
577
+ lastAppliedComp?: number;
578
+ }
579
+
580
+ addHandler(
581
+ 1065, // Layer Comps
582
+ target => target.layerComps !== undefined,
583
+ (reader, target) => {
584
+ const desc = readVersionAndDescriptor(reader, true) as LayerCompsDescriptor;
585
+ // console.log('CompList', require('util').inspect(desc, false, 99, true));
586
+
587
+ target.layerComps = { list: [] };
588
+
589
+ for (const item of desc.list) {
590
+ target.layerComps.list.push({
591
+ id: item.compID,
592
+ name: item['Nm '],
593
+ capturedInfo: item.capturedInfo,
594
+ });
595
+
596
+ if ('comment' in item) target.layerComps.list[target.layerComps.list.length - 1].comment = item.comment;
597
+ }
598
+
599
+ if ('lastAppliedComp' in desc) target.layerComps.lastApplied = desc.lastAppliedComp;
600
+ },
601
+ (writer, target) => {
602
+ const layerComps = target.layerComps!;
603
+ const desc: LayerCompsDescriptor = { list: [] };
604
+
605
+ for (const item of layerComps.list) {
606
+ const t: LayerCompsDescriptor['list'][0] = {} as any;
607
+ t._classID = 'Comp';
608
+ t['Nm '] = item.name;
609
+ if ('comment' in item) t.comment = item.comment;
610
+ t.compID = item.id;
611
+ t.capturedInfo = item.capturedInfo;
612
+ desc.list.push(t);
613
+ }
614
+
615
+ if ('lastApplied' in layerComps) desc.lastAppliedComp = layerComps.lastApplied;
616
+
617
+ // console.log('CompList', require('util').inspect(desc, false, 99, true));
618
+ writeVersionAndDescriptor(writer, '', 'CompList', desc);
619
+ },
620
+ );
621
+
569
622
  MOCK_HANDLERS && addHandler(
570
623
  1092, // ???
571
624
  target => (target as any)._ir1092 !== undefined,
package/src/psd.ts CHANGED
@@ -24,7 +24,7 @@ export const enum SectionDividerType {
24
24
 
25
25
  export type RGBA = { r: number; g: number; b: number; a: number; }; // values from 0 to 255
26
26
  export type RGB = { r: number; g: number; b: number; }; // values from 0 to 255
27
- export type FRGB = { fr: number; fg: number; fb: number; }; // values from 0 to 1 (can be above 1)
27
+ export type FRGB = { fr: number; fg: number; fb: number; }; // values from 0 to 1 (can be above 1, can be negative)
28
28
  export type HSB = { h: number; s: number; b: number; }; // values from 0 to 1
29
29
  export type CMYK = { c: number; m: number; y: number; k: number; }; // values from 0 to 255
30
30
  export type LAB = { l: number; a: number; b: number; }; // values `l` from 0 to 1; `a` and `b` from -1 to 1
@@ -222,6 +222,14 @@ export interface LayerEffectsInfo {
222
222
  patternOverlay?: LayerEffectPatternOverlay; // not supported yet because of `Patt` section not implemented
223
223
  }
224
224
 
225
+ export type PixelArray = Uint8ClampedArray | Uint8Array | Uint16Array | Uint32Array;
226
+
227
+ export interface PixelData {
228
+ data: PixelArray; // type depends on document bit depth
229
+ width: number;
230
+ height: number;
231
+ }
232
+
225
233
  export interface LayerMaskData {
226
234
  top?: number;
227
235
  left?: number;
@@ -236,7 +244,7 @@ export interface LayerMaskData {
236
244
  vectorMaskDensity?: number;
237
245
  vectorMaskFeather?: number;
238
246
  canvas?: HTMLCanvasElement;
239
- imageData?: ImageData;
247
+ imageData?: PixelData;
240
248
  }
241
249
 
242
250
  export type TextGridding = 'none' | 'round'; // TODO: other values (no idea where to set it up in Photoshop)
@@ -416,7 +424,6 @@ export interface LayerTextData {
416
424
  shapeType?: 'point' | 'box';
417
425
  pointBase?: number[];
418
426
  boxBounds?: number[];
419
-
420
427
  bounds?: UnitsBounds;
421
428
  boundingBox?: UnitsBounds;
422
429
  }
@@ -1385,12 +1392,32 @@ export interface LayerAdditionalInfo {
1385
1392
  data: Uint8Array;
1386
1393
  };
1387
1394
  }[];
1395
+ comps?: {
1396
+ originalEffectsReferencePoint?: { x: number; y: number; };
1397
+ settings: {
1398
+ enabled?: boolean;
1399
+ compList: number[];
1400
+ offset?: { x: number; y: number; };
1401
+ effectsReferencePoint?: { x: number; y: number; };
1402
+ }[];
1403
+ };
1404
+ userMask?: {
1405
+ colorSpace: Color;
1406
+ opacity: number;
1407
+ };
1388
1408
 
1389
1409
  // Base64 encoded raw EngineData, currently just kept in original state to support
1390
1410
  // loading and modifying PSD file without breaking text layers.
1391
1411
  engineData?: string;
1392
1412
  }
1393
1413
 
1414
+ export enum LayerCompCapturedInfo {
1415
+ None = 0,
1416
+ Visibility = 1,
1417
+ Position = 2,
1418
+ Appearance = 4,
1419
+ }
1420
+
1394
1421
  export interface ImageResources {
1395
1422
  layerState?: number;
1396
1423
  layersGroup?: number[];
@@ -1564,6 +1591,15 @@ export interface ImageResources {
1564
1591
  rightOutset?: number;
1565
1592
  }[];
1566
1593
  }[];
1594
+ layerComps?: {
1595
+ list: {
1596
+ id: number;
1597
+ name: string;
1598
+ comment?: string;
1599
+ capturedInfo: LayerCompCapturedInfo;
1600
+ }[];
1601
+ lastApplied?: number;
1602
+ };
1567
1603
  }
1568
1604
 
1569
1605
  export interface GlobalLayerMaskInfo {
@@ -1600,7 +1636,7 @@ export interface Layer extends LayerAdditionalInfo {
1600
1636
  hidden?: boolean;
1601
1637
  clipping?: boolean;
1602
1638
  canvas?: HTMLCanvasElement;
1603
- imageData?: ImageData;
1639
+ imageData?: PixelData;
1604
1640
  children?: Layer[];
1605
1641
  /** Applies only for layer groups. */
1606
1642
  opened?: boolean;
@@ -1614,7 +1650,7 @@ export interface Psd extends LayerAdditionalInfo {
1614
1650
  colorMode?: ColorMode;
1615
1651
  children?: Layer[];
1616
1652
  canvas?: HTMLCanvasElement;
1617
- imageData?: ImageData;
1653
+ imageData?: PixelData;
1618
1654
  imageResources?: ImageResources;
1619
1655
  linkedFiles?: LinkedFile[]; // used in smart objects
1620
1656
  artboards?: {