ag-psd 16.0.0 → 17.0.1

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.
package/src/descriptor.ts CHANGED
@@ -7,15 +7,16 @@ import {
7
7
  LayerEffectSatin, LayerEffectShadow, LayerEffectsInfo, LayerEffectSolidFill,
8
8
  LayerEffectsOuterGlow, LayerEffectStroke, LineAlignment, LineCapType, LineJoinType,
9
9
  Orientation, TextGridding, TimelineKey, TimelineKeyInterpolation, TimelineTrack, TimelineTrackType,
10
- Units, UnitsValue, VectorContent, WarpStyle
10
+ Units, UnitsBounds, UnitsValue, VectorContent, WarpStyle
11
11
  } from './psd';
12
12
  import {
13
13
  PsdReader, readSignature, readUnicodeString, readUint32, readUint8, readFloat64,
14
- readBytes, readAsciiString, readInt32, readFloat32, readInt32LE, readUnicodeStringWithLength
14
+ readBytes, readAsciiString, readInt32, readFloat32, readInt32LE, readUnicodeStringWithLengthLE
15
15
  } from './psdReader';
16
16
  import {
17
17
  PsdWriter, writeSignature, writeBytes, writeUint32, writeFloat64, writeUint8,
18
- writeUnicodeStringWithPadding, writeInt32, writeFloat32, writeUnicodeString
18
+ writeUnicodeStringWithPadding, writeInt32, writeFloat32, writeUnicodeString, writeInt32LE,
19
+ writeUnicodeStringWithoutLengthLE
19
20
  } from './psdWriter';
20
21
 
21
22
  interface Dict { [key: string]: string; }
@@ -118,6 +119,7 @@ const fieldToExtType: ExtTypeDict = {
118
119
  sheetStyle: nullType,
119
120
  translation: nullType,
120
121
  Skew: nullType,
122
+ boundingBox: makeType('', 'boundingBox'),
121
123
  'Lnk ': makeType('', 'ExternalFileLink'),
122
124
  frameReader: makeType('', 'FrameReader'),
123
125
  effectParams: makeType('', 'motionTrackEffectParams'),
@@ -125,11 +127,15 @@ const fieldToExtType: ExtTypeDict = {
125
127
  Anch: makeType('', 'Pnt '),
126
128
  'Fwd ': makeType('', 'Pnt '),
127
129
  'Bwd ': makeType('', 'Pnt '),
130
+ FlrC: makeType('', 'Pnt '),
128
131
  meshBoundaryPath: makeType('', 'pathClass'),
129
132
  filterFX: makeType('', 'filterFXStyle'),
130
133
  Fltr: makeType('', 'rigidTransform'),
131
134
  FrgC: makeType('', 'RGBC'),
132
135
  BckC: makeType('', 'RGBC'),
136
+ sdwM: makeType('Parameters', 'adaptCorrectTones'),
137
+ hglM: makeType('Parameters', 'adaptCorrectTones'),
138
+ customShape: makeType('', 'customShape'),
133
139
  };
134
140
 
135
141
  const fieldToArrayExtType: ExtTypeDict = {
@@ -159,6 +165,8 @@ const fieldToArrayExtType: ExtTypeDict = {
159
165
  pathComponents: makeType('', 'PaCm'),
160
166
  filterFXList: makeType('', 'filterFX'),
161
167
  puppetShapeList: makeType('', 'puppetShape'),
168
+ channelDenoise: makeType('', 'channelDenoiseParams'),
169
+ ShrP: makeType('', 'Pnt '),
162
170
  };
163
171
 
164
172
  const typeToField: { [key: string]: string[]; } = {
@@ -166,9 +174,12 @@ const typeToField: { [key: string]: string[]; } = {
166
174
  'Txt ', 'printerName', 'Nm ', 'Idnt', 'blackAndWhitePresetFileName', 'LUT3DFileName',
167
175
  'presetFileName', 'curvesPresetFileName', 'mixerPresetFileName', 'placed', 'description', 'reason',
168
176
  'artboardPresetName', 'json', 'clipID', 'relPath', 'fullPath', 'mediaDescriptor', 'Msge',
169
- 'altTag', 'url', 'cellText',
177
+ 'altTag', 'url', 'cellText', 'preset', 'KnNm', 'FPth',
178
+ ],
179
+ 'tdta': [
180
+ 'EngineData', 'LUT3DFileData', 'indexArray', 'originalVertexArray', 'deformedVertexArray',
181
+ 'LqMe',
170
182
  ],
171
- 'tdta': ['EngineData', 'LUT3DFileData', 'indexArray', 'originalVertexArray', 'deformedVertexArray'],
172
183
  'long': [
173
184
  'TextIndex', 'RndS', 'Mdpn', 'Smth', 'Lctn', 'strokeStyleVersion', 'LaID', 'Vrsn', 'Cnt ',
174
185
  'Brgh', 'Cntr', 'means', 'vibrance', 'Strt', 'bwPresetKind', 'presetKind', 'comp', 'compID', 'originalCompID',
@@ -179,16 +190,19 @@ const typeToField: { [key: string]: string[]; } = {
179
190
  'numBefore', 'numAfter', 'Spcn', 'minOpacity', 'maxOpacity', 'BlnM', 'sheetID', 'gblA', 'globalAltitude',
180
191
  'descVersion', 'frameReaderType', 'LyrI', 'zoomOrigin', 'fontSize', 'Rds ', 'sliceID',
181
192
  'topOutset', 'leftOutset', 'bottomOutset', 'rightOutset', 'filterID', 'meshQuality',
182
- 'meshExpansion', 'meshRigidity', 'VrsM', 'VrsN',
193
+ 'meshExpansion', 'meshRigidity', 'VrsM', 'VrsN', 'NmbG', 'WLMn', 'WLMx', 'AmMn', 'AmMx', 'SclH', 'SclV',
194
+ 'Lvl ', 'TlNm', 'TlOf', 'FlRs', 'Thsh', 'ShrS', 'ShrE', 'FlRs', 'Vrnc', 'Strg', 'ExtS', 'ExtD',
195
+ 'HrzS', 'VrtS', 'NmbR', 'EdgF', 'Ang1', 'Ang2', 'Ang3', 'Ang4',
183
196
  ],
184
197
  'enum': [
185
- 'textGridding', 'Ornt', 'warpStyle', 'warpRotate', 'Inte', 'Bltn', 'ClrS',
186
- 'sdwM', 'hglM', 'bvlT', 'bvlS', 'bvlD', 'Md ', 'glwS', 'GrdF', 'GlwT',
198
+ 'textGridding', 'Ornt', 'warpStyle', 'warpRotate', 'Inte', 'Bltn', 'ClrS', 'BlrQ',
199
+ 'bvlT', 'bvlS', 'bvlD', 'Md ', 'glwS', 'GrdF', 'GlwT', 'RplS', 'BlrM', 'SmBM',
187
200
  'strokeStyleLineCapType', 'strokeStyleLineJoinType', 'strokeStyleLineAlignment',
188
201
  'strokeStyleBlendMode', 'PntT', 'Styl', 'lookupType', 'LUTFormat', 'dataOrder',
189
202
  'tableOrder', 'enableCompCore', 'enableCompCoreGPU', 'compCoreSupport', 'compCoreGPUSupport', 'Engn',
190
203
  'enableCompCoreThreads', 'gs99', 'FrDs', 'trackID', 'animInterpStyle', 'horzAlign',
191
- 'vertAlign', 'bgColorType', 'shapeOperation',
204
+ 'vertAlign', 'bgColorType', 'shapeOperation', 'UndA', 'Wvtp', 'Drct', 'WndM', 'Edg ', 'FlCl', 'IntE',
205
+ 'IntC', 'Cnvr', 'Fl ', 'Dstr', 'MztT', 'Lns ', 'ExtT', 'DspM', 'ExtR', 'ZZTy', 'SphM', 'SmBQ',
192
206
  ],
193
207
  'bool': [
194
208
  'PstS', 'printSixteenBit', 'masterFXSwitch', 'enab', 'uglg', 'antialiasGloss',
@@ -200,7 +214,8 @@ const typeToField: { [key: string]: string[]; } = {
200
214
  'present', 'showInDialog', 'overprint', 'sheetDisclosed', 'lightsDisclosed', 'meshesDisclosed',
201
215
  'materialsDisclosed', 'hasMotion', 'muted', 'Effc', 'selected', 'autoScope', 'fillCanvas',
202
216
  'cellTextIsHTML', 'Smoo', 'Clsp', 'validAtPosition', 'rigidType', 'hasoptions', 'filterMaskEnable',
203
- 'filterMaskLinked', 'filterMaskExtendWithWhite',
217
+ 'filterMaskLinked', 'filterMaskExtendWithWhite', 'removeJPEGArtifact', 'Mnch', 'ExtF', 'ExtM',
218
+ 'moreAccurate', 'GpuY', 'LIWy',
204
219
  ],
205
220
  'doub': [
206
221
  'warpValue', 'warpPerspective', 'warpPerspectiveOther', 'Intr', 'Wdth', 'Hght',
@@ -210,10 +225,10 @@ const typeToField: { [key: string]: string[]; } = {
210
225
  'PuX0', 'PuX1', 'PuX2', 'PuX3', 'PuY0', 'PuY1', 'PuY2', 'PuY3'
211
226
  ],
212
227
  'UntF': [
213
- 'Scl ', 'sdwO', 'hglO', 'lagl', 'Lald', 'srgR', 'blur', 'Sftn', 'Opct', 'Dstn', 'Angl',
228
+ 'sdwO', 'hglO', 'lagl', 'Lald', 'srgR', 'blur', 'Sftn', 'Opct', 'Dstn', 'Angl',
214
229
  'Ckmt', 'Nose', 'Inpr', 'ShdN', 'strokeStyleLineWidth', 'strokeStyleLineDashOffset',
215
230
  'strokeStyleOpacity', 'H ', 'Top ', 'Left', 'Btom', 'Rght', 'Rslt',
216
- 'topRight', 'topLeft', 'bottomLeft', 'bottomRight',
231
+ 'topRight', 'topLeft', 'bottomLeft', 'bottomRight', 'ClNs', 'Shrp',
217
232
  ],
218
233
  'VlLs': [
219
234
  'Crv ', 'Clrs', 'Mnm ', 'Mxm ', 'Trns', 'pathList', 'strokeStyleLineDashSet', 'FrLs', 'slices',
@@ -221,10 +236,12 @@ const typeToField: { [key: string]: string[]; } = {
221
236
  'solidFillMulti', 'frameFXMulti', 'innerShadowMulti', 'dropShadowMulti', 'FrIn', 'FSts', 'FsFr',
222
237
  'sheetTimelineOptions', 'audioClipList', 'trackList', 'globalTrackList', 'keyList', 'audioClipList',
223
238
  'warpValues', 'selectedPin', 'Pts ', 'SbpL', 'pathComponents', 'pinOffsets', 'posFinalPins',
224
- 'pinVertexIndices', 'PinP', 'PnRt', 'PnOv', 'PnDp', 'filterFXList', 'puppetShapeList',
239
+ 'pinVertexIndices', 'PinP', 'PnRt', 'PnOv', 'PnDp', 'filterFXList', 'puppetShapeList', 'ShrP',
240
+ 'channelDenoise', 'Mtrx',
225
241
  ],
226
242
  'ObAr': ['meshPoints', 'quiltSliceX', 'quiltSliceY'],
227
- 'obj ': ['null'],
243
+ 'obj ': ['null', 'Chnl'],
244
+ 'Pth ': ['DspF'],
228
245
  };
229
246
 
230
247
  const channels = [
@@ -265,6 +282,9 @@ const fieldToArrayType: Dict = {
265
282
  PnDp: 'doub',
266
283
  filterFXList: 'Objc',
267
284
  puppetShapeList: 'Objc',
285
+ ShrP: 'Objc',
286
+ channelDenoise: 'Objc',
287
+ Mtrx: 'long',
268
288
  };
269
289
 
270
290
  const fieldToType: Dict = {};
@@ -331,11 +351,13 @@ function writeAsciiStringOrClassId(writer: PsdWriter, value: string) {
331
351
  }
332
352
 
333
353
  export function readDescriptorStructure(reader: PsdReader) {
334
- const object: any = {};
335
- // object.__struct =
354
+ // const struct =
336
355
  readClassStructure(reader);
356
+ // const object: any = { _name: struct.name, _classID: struct.classID };
357
+ const object: any = {};
358
+ // console.log('>> ', struct);
337
359
  const itemsCount = readUint32(reader);
338
- // console.log('//', object.__struct);
360
+
339
361
  for (let i = 0; i < itemsCount; i++) {
340
362
  const key = readAsciiStringOrClassId(reader);
341
363
  const type = readSignature(reader);
@@ -356,25 +378,59 @@ export function writeDescriptorStructure(writer: PsdWriter, name: string, classI
356
378
  writeAsciiStringOrClassId(writer, classId);
357
379
 
358
380
  const keys = Object.keys(value);
359
- writeUint32(writer, keys.length);
381
+ let keyCount = keys.length;
382
+ if ('_name' in value) keyCount--;
383
+ if ('_classID' in value) keyCount--;
384
+
385
+ writeUint32(writer, keyCount);
360
386
 
361
387
  for (const key of keys) {
388
+ if (key === '_name' || key === '_classID') continue;
389
+
362
390
  let type = getTypeByKey(key, value[key], root, value);
363
391
  let extType = fieldToExtType[key];
364
392
 
365
- if (key === 'origin') {
393
+ if (key === 'bounds' && root === 'text') {
394
+ extType = makeType('', 'bounds');
395
+ } else if (key === 'origin') {
366
396
  type = root === 'slices' ? 'enum' : 'Objc';
397
+ } else if ((key === 'Cyn ' || key === 'Mgnt' || key === 'Ylw ' || key === 'Blck') && value._classID === 'CMYC') {
398
+ type = 'doub';
399
+ } else if (/^PN[a-z][a-z]$/.test(key)) {
400
+ type = 'TEXT';
401
+ } else if (/^PT[a-z][a-z]$/.test(key)) {
402
+ type = 'long';
403
+ } else if (/^PF[a-z][a-z]$/.test(key)) {
404
+ type = 'doub';
405
+ } else if (key === 'ClSz' || key === 'Rds ' || key === 'Amnt') {
406
+ type = typeof value[key] === 'number' ? 'long' : 'UntF';
407
+ } else if ((key === 'sdwM' || key === 'hglM') && typeof value[key] === 'string') {
408
+ type = 'enum';
409
+ } else if (key === 'blur' && typeof value[key] === 'string') {
410
+ type = 'enum';
411
+ } else if (key === 'Angl' && typeof value[key] === 'number') {
412
+ type = 'doub'; // ???
367
413
  } else if (key === 'bounds' && root === 'slices') {
368
414
  type = 'Objc';
369
415
  extType = makeType('', 'Rct1');
370
- } else if (key === 'Scl ' && 'Hrzn' in value[key]) {
371
- type = 'Objc';
372
- extType = nullType;
416
+ } else if (key === 'Scl ') {
417
+ if (typeof value[key] === 'object' && 'Hrzn' in value[key]) {
418
+ type = 'Objc';
419
+ extType = nullType;
420
+ } else if (typeof value[key] === 'number') {
421
+ type = 'long';
422
+ } else {
423
+ type = 'UntF';
424
+ }
373
425
  } else if (key === 'audioClipGroupList' && keys.length === 1) {
374
426
  type = 'VlLs';
375
427
  } else if ((key === 'Strt' || key === 'Brgh') && 'H ' in value) {
376
428
  type = 'doub';
377
- } else if (key === 'Strt') {
429
+ } else if (key === 'Wdth' && typeof value[key] === 'object') {
430
+ type = 'UntF';
431
+ } else if (key === 'Ofst' && typeof value[key] === 'number') {
432
+ type = 'long';
433
+ } else if (key === 'Strt' && typeof value[key] === 'object') {
378
434
  type = 'Objc';
379
435
  extType = nullType;
380
436
  } else if (channels.indexOf(key) !== -1) {
@@ -491,11 +547,11 @@ function readOSType(reader: PsdReader, type: string) {
491
547
  return items;
492
548
  }
493
549
  case 'Pth ': { // File path
494
- /*const length =*/ readInt32(reader);
550
+ /*const length =*/ readInt32(reader); // total size of all fields below
495
551
  const sig = readSignature(reader);
496
- /*const pathSize =*/ readInt32LE(reader);
552
+ /*const pathSize =*/ readInt32LE(reader); // the same as length
497
553
  const charsCount = readInt32LE(reader);
498
- const path = readUnicodeStringWithLength(reader, charsCount);
554
+ const path = readUnicodeStringWithLengthLE(reader, charsCount);
499
555
  return { sig, path };
500
556
  }
501
557
  default:
@@ -515,11 +571,16 @@ function writeOSType(writer: PsdWriter, type: string, value: any, key: string, e
515
571
  writeReferenceStructure(writer, key, value);
516
572
  break;
517
573
  case 'Objc': // Descriptor
518
- case 'GlbO': // GlobalObject same as Descriptor
574
+ case 'GlbO': { // GlobalObject same as Descriptor
575
+ if (typeof value !== 'object') throw new Error(`Invalid struct value: ${JSON.stringify(value)}, key: ${key}`);
519
576
  if (!extType) throw new Error(`Missing ext type for: '${key}' (${JSON.stringify(value)})`);
520
- writeDescriptorStructure(writer, extType.name, extType.classID, value, root);
577
+ const name = value._name || extType.name;
578
+ const classID = value._classID || extType.classID;
579
+ writeDescriptorStructure(writer, name, classID, value, root);
521
580
  break;
581
+ }
522
582
  case 'VlLs': // List
583
+ if (!Array.isArray(value)) throw new Error(`Invalid list value: ${JSON.stringify(value)}, key: ${key}`);
523
584
  writeInt32(writer, value.length);
524
585
 
525
586
  for (let i = 0; i < value.length; i++) {
@@ -530,6 +591,7 @@ function writeOSType(writer: PsdWriter, type: string, value: any, key: string, e
530
591
  }
531
592
  break;
532
593
  case 'doub': // Double
594
+ if (typeof value !== 'number') throw new Error(`Invalid number value: ${JSON.stringify(value)}, key: ${key}`);
533
595
  writeFloat64(writer, value);
534
596
  break;
535
597
  case 'UntF': // Unit double
@@ -546,17 +608,20 @@ function writeOSType(writer: PsdWriter, type: string, value: any, key: string, e
546
608
  writeUnicodeStringWithPadding(writer, value);
547
609
  break;
548
610
  case 'enum': { // Enumerated
611
+ if (typeof value !== 'string') throw new Error(`Invalid enum value: ${JSON.stringify(value)}, key: ${key}`);
549
612
  const [_type, val] = value.split('.');
550
613
  writeAsciiStringOrClassId(writer, _type);
551
614
  writeAsciiStringOrClassId(writer, val);
552
615
  break;
553
616
  }
554
617
  case 'long': // Integer
618
+ if (typeof value !== 'number') throw new Error(`Invalid integer value: ${JSON.stringify(value)}, key: ${key}`);
555
619
  writeInt32(writer, value);
556
620
  break;
557
621
  // case 'comp': // Large Integer
558
622
  // writeLargeInteger(reader);
559
623
  case 'bool': // Boolean
624
+ if (typeof value !== 'boolean') throw new Error(`Invalid boolean value: ${JSON.stringify(value)}, key: ${key}`);
560
625
  writeUint8(writer, value ? 1 : 0);
561
626
  break;
562
627
  // case 'type': // Class
@@ -588,8 +653,15 @@ function writeOSType(writer: PsdWriter, type: string, value: any, key: string, e
588
653
  }
589
654
  break;
590
655
  }
591
- // case 'Pth ': // File path
592
- // writeFilePath(reader);
656
+ case 'Pth ': { // File path
657
+ const length = 4 + 4 + 4 + value.path.length * 2;
658
+ writeInt32(writer, length);
659
+ writeSignature(writer, value.sig);
660
+ writeInt32LE(writer, length);
661
+ writeInt32LE(writer, value.path.length);
662
+ writeUnicodeStringWithoutLengthLE(writer, value.path);
663
+ break;
664
+ }
593
665
  default:
594
666
  throw new Error(`Not implemented descriptor OSType: ${type}`);
595
667
  }
@@ -690,7 +762,6 @@ function writeReferenceStructure(writer: PsdWriter, _key: string, items: any[])
690
762
  function readClassStructure(reader: PsdReader) {
691
763
  const name = readUnicodeString(reader);
692
764
  const classID = readAsciiStringOrClassId(reader);
693
- // console.log({ name, classID });
694
765
  return { name, classID };
695
766
  }
696
767
 
@@ -721,25 +792,37 @@ export interface DescriptorUnitsValue {
721
792
  }
722
793
 
723
794
  export type DescriptorColor = {
795
+ _name: '';
796
+ _classID: 'RGBC';
724
797
  'Rd ': number;
725
798
  'Grn ': number;
726
799
  'Bl ': number;
727
800
  } | {
801
+ _name: '';
802
+ _classID: 'HSBC'; // ???
728
803
  'H ': DescriptorUnitsValue;
729
804
  Strt: number;
730
805
  Brgh: number;
731
806
  } | {
807
+ _name: '';
808
+ _classID: 'CMYC';
732
809
  'Cyn ': number;
733
810
  Mgnt: number;
734
811
  'Ylw ': number;
735
812
  Blck: number;
736
813
  } | {
814
+ _name: '';
815
+ _classID: 'GRYC'; // ???
737
816
  'Gry ': number;
738
817
  } | {
818
+ _name: '';
819
+ _classID: 'LABC'; // ???
739
820
  Lmnc: number;
740
821
  'A ': number;
741
822
  'B ': number;
742
823
  } | {
824
+ _name: '';
825
+ _classID: 'XXXX'; // ???
743
826
  redFloat: number;
744
827
  greenFloat: number;
745
828
  blueFloat: number;
@@ -819,11 +902,20 @@ export interface StrokeDescriptor {
819
902
  strokeStyleResolution: number;
820
903
  }
821
904
 
905
+ export interface BoundsDescriptor {
906
+ Left: DescriptorUnitsValue;
907
+ 'Top ': DescriptorUnitsValue;
908
+ Rght: DescriptorUnitsValue;
909
+ Btom: DescriptorUnitsValue;
910
+ }
911
+
822
912
  export interface TextDescriptor {
823
913
  'Txt ': string;
824
914
  textGridding: string;
825
915
  Ornt: string;
826
916
  AntA: string;
917
+ bounds?: BoundsDescriptor;
918
+ boundingBox?: BoundsDescriptor;
827
919
  TextIndex: number;
828
920
  EngineData?: Uint8Array;
829
921
  }
@@ -844,6 +936,8 @@ export interface WarpDescriptor {
844
936
  uOrder: number;
845
937
  vOrder: number;
846
938
  customEnvelopeWarp?: {
939
+ _name: '';
940
+ _classID: 'customEnvelopeWarp';
847
941
  meshPoints: {
848
942
  type: 'Hrzn' | 'Vrtc';
849
943
  values: number[];
@@ -855,6 +949,8 @@ export interface QuiltWarpDescriptor extends WarpDescriptor {
855
949
  deformNumRows: number;
856
950
  deformNumCols: number;
857
951
  customEnvelopeWarp: {
952
+ _name: '';
953
+ _classID: 'customEnvelopeWarp';
858
954
  quiltSliceX: {
859
955
  type: 'quiltSliceX';
860
956
  values: number[];
@@ -904,6 +1000,24 @@ export function xyToHorzVrtc(xy: { x: number; y: number; }): HrznVrtcDescriptor
904
1000
  return { Hrzn: xy.x, Vrtc: xy.y };
905
1001
  }
906
1002
 
1003
+ export function descBoundsToBounds(desc: BoundsDescriptor): UnitsBounds {
1004
+ return {
1005
+ top: parseUnits(desc['Top ']),
1006
+ left: parseUnits(desc.Left),
1007
+ right: parseUnits(desc.Rght),
1008
+ bottom: parseUnits(desc.Btom),
1009
+ };
1010
+ }
1011
+
1012
+ export function boundsToDescBounds(bounds: UnitsBounds): BoundsDescriptor {
1013
+ return {
1014
+ Left: unitsValue(bounds.left, 'bounds.left'),
1015
+ ['Top ']: unitsValue(bounds.top, 'bounds.top'),
1016
+ Rght: unitsValue(bounds.right, 'bounds.right'),
1017
+ Btom: unitsValue(bounds.bottom, 'bounds.bottom'),
1018
+ };
1019
+ }
1020
+
907
1021
  export type TimelineAnimKeyDescriptor = {
908
1022
  Type: 'keyType.Opct';
909
1023
  Opct: DescriptorUnitsValue;
@@ -1122,7 +1236,8 @@ function parseKeyList(keyList: TimelineKeyDescriptor[], logMissingFeatures: bool
1122
1236
 
1123
1237
  for (let j = 0; j < keyList.length; j++) {
1124
1238
  const key = keyList[j];
1125
- const { time, selected, animKey } = key;
1239
+ const { time: { denominator, numerator }, selected, animKey } = key;
1240
+ const time = { numerator, denominator };
1126
1241
  const interpolation = animInterpStyleEnum.decode(key.animInterpStyle);
1127
1242
 
1128
1243
  switch (animKey.Type) {
@@ -1307,6 +1422,9 @@ function parseEffectObject(obj: any, reportErrors: boolean) {
1307
1422
  case 'present':
1308
1423
  case 'showInDialog':
1309
1424
  case 'antialiasGloss': result[key] = val; break;
1425
+ case '_name':
1426
+ case '_classID':
1427
+ break;
1310
1428
  default:
1311
1429
  reportErrors && console.log(`Invalid effect key: '${key}', value:`, val);
1312
1430
  }
@@ -1564,19 +1682,19 @@ export function parseColor(color: DescriptorColor): Color {
1564
1682
 
1565
1683
  export function serializeColor(color: Color | undefined): DescriptorColor {
1566
1684
  if (!color) {
1567
- return { 'Rd ': 0, 'Grn ': 0, 'Bl ': 0 };
1685
+ return { _name: '', _classID: 'RGBC', 'Rd ': 0, 'Grn ': 0, 'Bl ': 0 };
1568
1686
  } else if ('r' in color) {
1569
- return { 'Rd ': color.r || 0, 'Grn ': color.g || 0, 'Bl ': color.b || 0 };
1687
+ return { _name: '', _classID: 'RGBC', 'Rd ': color.r || 0, 'Grn ': color.g || 0, 'Bl ': color.b || 0 };
1570
1688
  } else if ('fr' in color) {
1571
- return { redFloat: color.fr, greenFloat: color.fg, blueFloat: color.fb };
1689
+ return { _name: '', _classID: 'XXXX', redFloat: color.fr, greenFloat: color.fg, blueFloat: color.fb };
1572
1690
  } else if ('h' in color) {
1573
- return { 'H ': unitsAngle(color.h * 360), Strt: color.s || 0, Brgh: color.b || 0 };
1691
+ return { _name: '', _classID: 'HSBC', 'H ': unitsAngle(color.h * 360), Strt: color.s || 0, Brgh: color.b || 0 };
1574
1692
  } else if ('c' in color) {
1575
- return { 'Cyn ': color.c || 0, Mgnt: color.m || 0, 'Ylw ': color.y || 0, Blck: color.k || 0 };
1693
+ return { _name: '', _classID: 'CMYC', 'Cyn ': color.c || 0, Mgnt: color.m || 0, 'Ylw ': color.y || 0, Blck: color.k || 0 };
1576
1694
  } else if ('l' in color) {
1577
- return { Lmnc: color.l || 0, 'A ': color.a || 0, 'B ': color.b || 0 };
1695
+ return { _name: '', _classID: 'LABC', Lmnc: color.l || 0, 'A ': color.a || 0, 'B ': color.b || 0 };
1578
1696
  } else if ('k' in color) {
1579
- return { 'Gry ': color.k };
1697
+ return { _name: '', _classID: 'GRYC', 'Gry ': color.k };
1580
1698
  } else {
1581
1699
  throw new Error('Invalid color value');
1582
1700
  }
@@ -1629,6 +1747,10 @@ export function unitsPercent(value: number | undefined): DescriptorUnitsValue {
1629
1747
  return { units: 'Percent', value: Math.round((value || 0) * 100) };
1630
1748
  }
1631
1749
 
1750
+ export function unitsPercentF(value: number | undefined): DescriptorUnitsValue {
1751
+ return { units: 'Percent', value: (value || 0) * 100 };
1752
+ }
1753
+
1632
1754
  export function unitsValue(x: UnitsValue | undefined, key: string): DescriptorUnitsValue {
1633
1755
  if (x == null) return { units: 'Pixels', value: 0 };
1634
1756
 
@@ -1650,6 +1772,10 @@ export function unitsValue(x: UnitsValue | undefined, key: string): DescriptorUn
1650
1772
  return { units, value };
1651
1773
  }
1652
1774
 
1775
+ export function frac({ numerator, denominator }: FractionDescriptor) {
1776
+ return { numerator, denominator };
1777
+ }
1778
+
1653
1779
  export const textGridding = createEnum<TextGridding>('textGridding', 'none', {
1654
1780
  none: 'None',
1655
1781
  round: 'Rnd ',
@@ -1785,6 +1911,7 @@ export const ClrS = createEnum<'rgb' | 'hsb' | 'lab'>('ClrS', 'rgb', {
1785
1911
  rgb: 'RGBC',
1786
1912
  hsb: 'HSBl',
1787
1913
  lab: 'LbCl',
1914
+ hsl: 'HSLC',
1788
1915
  });
1789
1916
 
1790
1917
  export const FStl = createEnum<'inside' | 'center' | 'outside'>('FStl', 'outside', {
@@ -1841,3 +1968,166 @@ export const strokeStyleLineAlignment = createEnum<LineAlignment>('strokeStyleLi
1841
1968
  center: 'strokeStyleAlignCenter',
1842
1969
  outside: 'strokeStyleAlignOutside',
1843
1970
  });
1971
+
1972
+ export const BlrM = createEnum<'spin' | 'zoom'>('BlrM', 'ispinmage', {
1973
+ spin: 'Spn ',
1974
+ zoom: 'Zm ',
1975
+ });
1976
+
1977
+ export const BlrQ = createEnum<'draft' | 'good' | 'best'>('BlrQ', 'good', {
1978
+ draft: 'Drft',
1979
+ good: 'Gd ',
1980
+ best: 'Bst ',
1981
+ });
1982
+
1983
+ export const SmBM = createEnum<'normal' | 'edge only' | 'overlay edge'>('SmBM', 'normal', {
1984
+ normal: 'SBMN',
1985
+ 'edge only': 'SBME',
1986
+ 'overlay edge': 'SBMO',
1987
+ });
1988
+
1989
+ export const SmBQ = createEnum<'low' | 'medium' | 'high'>('SmBQ', 'medium', {
1990
+ low: 'SBQL',
1991
+ medium: 'SBQM',
1992
+ high: 'SBQH',
1993
+ });
1994
+
1995
+ export const DspM = createEnum<'stretch to fit' | 'tile'>('DspM', 'stretch to fit', {
1996
+ 'stretch to fit': 'StrF',
1997
+ 'tile': 'Tile',
1998
+ });
1999
+
2000
+ export const UndA = createEnum<'wrap around' | 'repeat edge pixels'>('UndA', 'repeat edge pixels', {
2001
+ 'wrap around': 'WrpA',
2002
+ 'repeat edge pixels': 'RptE',
2003
+ });
2004
+
2005
+ export const Cnvr = createEnum<'rectangular to polar' | 'polar to rectangular'>('Cnvr', 'rectangular to polar', {
2006
+ 'rectangular to polar': 'RctP',
2007
+ 'polar to rectangular': 'PlrR',
2008
+ });
2009
+
2010
+ export const RplS = createEnum<'small' | 'medium' | 'large'>('RplS', 'medium', {
2011
+ small: 'Sml ',
2012
+ medium: 'Mdm ',
2013
+ large: 'Lrg ',
2014
+ });
2015
+
2016
+ export const SphM = createEnum<'normal' | 'horizontal only' | 'vertical only'>('SphM', 'normal', {
2017
+ 'normal': 'Nrml',
2018
+ 'horizontal only': 'HrzO',
2019
+ 'vertical only': 'VrtO',
2020
+ });
2021
+
2022
+ export const Wvtp = createEnum<'sine' | 'triangle' | 'square'>('Wvtp', 'sine', {
2023
+ sine: 'WvSn',
2024
+ triangle: 'WvTr',
2025
+ square: 'WvSq',
2026
+ });
2027
+
2028
+ export const ZZTy = createEnum<'around center' | 'out from center' | 'pond ripples'>('ZZTy', 'pond ripples', {
2029
+ 'around center': 'ArnC',
2030
+ 'out from center': 'OtFr',
2031
+ 'pond ripples': 'PndR',
2032
+ });
2033
+
2034
+ export const Dstr = createEnum<'uniform' | 'gaussian'>('Dstr', 'uniform', {
2035
+ uniform: 'Unfr',
2036
+ gaussian: 'Gsn ',
2037
+ });
2038
+
2039
+ export const Chnl = createEnum<'red' | 'green' | 'blue' | 'composite'>('Chnl', 'composite', {
2040
+ red: 'Rd ',
2041
+ green: 'Grn ',
2042
+ blue: 'Bl ',
2043
+ composite: 'Cmps',
2044
+ });
2045
+
2046
+ export const MztT = createEnum<'fine dots' | 'medium dots' | 'grainy dots' | 'coarse dots' | 'short lines' | 'medium lines' | 'long lines' | 'short strokes' | 'medium strokes' | 'long strokes'>('MztT', 'fine dots', {
2047
+ 'fine dots': 'FnDt',
2048
+ 'medium dots': 'MdmD',
2049
+ 'grainy dots': 'GrnD',
2050
+ 'coarse dots': 'CrsD',
2051
+ 'short lines': 'ShrL',
2052
+ 'medium lines': 'MdmL',
2053
+ 'long lines': 'LngL',
2054
+ 'short strokes': 'ShSt',
2055
+ 'medium strokes': 'MdmS',
2056
+ 'long strokes': 'LngS',
2057
+ });
2058
+
2059
+ export const Lns = createEnum<'50-300mm zoom' | '32mm prime' | '105mm prime' | 'movie prime'>('Lns ', '50-300mm zoom', {
2060
+ '50-300mm zoom': 'Zm ',
2061
+ '32mm prime': 'Nkn ',
2062
+ '105mm prime': 'Nkn1',
2063
+ 'movie prime': 'PnVs',
2064
+ });
2065
+
2066
+ export const blurType = createEnum<'gaussian blur' | 'lens blur' | 'motion blur'>('blurType', 'gaussian blur', {
2067
+ 'gaussian blur': 'GsnB',
2068
+ 'lens blur': 'lensBlur',
2069
+ 'motion blur': 'MtnB',
2070
+ });
2071
+
2072
+ export const DfsM = createEnum<'normal' | 'darken only' | 'lighten only' | 'anisotropic'>('DfsM', 'normal', {
2073
+ 'normal': 'Nrml',
2074
+ 'darken only': 'DrkO',
2075
+ 'lighten only': 'LghO',
2076
+ 'anisotropic': 'anisotropic',
2077
+ });
2078
+
2079
+ export const ExtT = createEnum<'blocks' | 'pyramids'>('ExtT', 'blocks', {
2080
+ blocks: 'Blks',
2081
+ pyramids: 'Pyrm',
2082
+ });
2083
+
2084
+ export const ExtR = createEnum<'random' | 'level-based'>('ExtR', 'random', {
2085
+ random: 'Rndm',
2086
+ 'level-based': 'LvlB',
2087
+ });
2088
+
2089
+ export const FlCl = createEnum<'background color' | 'foreground color' | 'inverse image' | 'unaltered image'>('FlCl', 'background color', {
2090
+ 'background color': 'FlBc',
2091
+ 'foreground color': 'FlFr',
2092
+ 'inverse image': 'FlIn',
2093
+ 'unaltered image': 'FlSm',
2094
+ });
2095
+
2096
+ export const CntE = createEnum<'lower' | 'upper'>('CntE', 'upper', {
2097
+ lower: 'Lwr ',
2098
+ upper: 'Upr ',
2099
+ });
2100
+
2101
+ export const WndM = createEnum<'wind' | 'blast' | 'stagger'>('WndM', 'wind', {
2102
+ wind: 'Wnd ',
2103
+ blast: 'Blst',
2104
+ stagger: 'Stgr',
2105
+ });
2106
+
2107
+ export const Drct = createEnum<'left' | 'right'>('Drct', 'from the right', {
2108
+ left: 'Left',
2109
+ right: 'Rght',
2110
+ });
2111
+
2112
+ export const IntE = createEnum<'odd lines' | 'even lines'>('IntE', 'odd lines', {
2113
+ 'odd lines': 'ElmO',
2114
+ 'even lines': 'ElmE',
2115
+ });
2116
+
2117
+ export const IntC = createEnum<'duplication' | 'interpolation'>('IntC', 'interpolation', {
2118
+ duplication: 'CrtD',
2119
+ interpolation: 'CrtI',
2120
+ });
2121
+
2122
+ export const FlMd = createEnum<'set to transparent' | 'repeat edge pixels' | 'wrap around'>('FlMd', 'wrap around', {
2123
+ 'set to transparent': 'Bckg',
2124
+ 'repeat edge pixels': 'Rpt ',
2125
+ 'wrap around': 'Wrp ',
2126
+ });
2127
+
2128
+ export const prjM = createEnum<'fisheye' | 'perspective' | 'auto' | 'full spherical'>('prjM', 'fisheye', {
2129
+ 'fisheye': 'fisP',
2130
+ 'perspective': 'perP',
2131
+ 'auto': 'auto',
2132
+ 'full spherical': 'fusP',
2133
+ });