ag-psd 32.0.0 → 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.
- package/.v8-cache/v24.7.0-x64-ef5a0af0/03e19709 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/05135617 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/1130885f +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/226e2801 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/2a19133e +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/4511bacf +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/68416cb2 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/71df9cb3 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/7ea06cea +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/932f0770 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/acc36e66 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/b3c2fab7 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/c314aece +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/cfc49f4f +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/cff9434a +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/dc86f000 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/e03e2acd +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/e6745c2b +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/e6b99945 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/eeeebdfe +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/f6fd8125 +0 -0
- package/CHANGELOG.md +78 -0
- package/LICENSE +5 -0
- package/README.md +3 -6
- package/README_PSD.md +242 -81
- package/dist/abr.d.ts +11 -4
- package/dist/abr.js +357 -230
- package/dist/abr.js.map +1 -1
- package/dist/additionalInfo.d.ts +54 -2
- package/dist/additionalInfo.js +487 -306
- package/dist/additionalInfo.js.map +1 -1
- package/dist/ase.d.ts +6 -1
- package/dist/ase.js +61 -61
- package/dist/ase.js.map +1 -1
- package/dist/bundle.js +1937 -1115
- package/dist/descriptor.d.ts +14 -8
- package/dist/descriptor.js +202 -186
- package/dist/descriptor.js.map +1 -1
- package/dist/helpers.d.ts +1 -1
- package/dist/helpers.js +4 -2
- package/dist/helpers.js.map +1 -1
- package/dist/imageResources.d.ts +15 -3
- package/dist/imageResources.js +503 -142
- package/dist/imageResources.js.map +1 -1
- package/dist/jpeg.js +8 -8
- package/dist/jpeg.js.map +1 -1
- package/dist/psd.d.ts +148 -17
- package/dist/psd.js.map +1 -1
- package/dist/psdReader.d.ts +10 -0
- package/dist/psdReader.js +278 -166
- package/dist/psdReader.js.map +1 -1
- package/dist/psdWriter.d.ts +1 -0
- package/dist/psdWriter.js +77 -62
- package/dist/psdWriter.js.map +1 -1
- package/dist/text.js +17 -6
- package/dist/text.js.map +1 -1
- package/dist/utf8.js +0 -3
- package/dist/utf8.js.map +1 -1
- package/dist-es/abr.d.ts +11 -4
- package/dist-es/abr.js +357 -230
- package/dist-es/abr.js.map +1 -1
- package/dist-es/additionalInfo.d.ts +54 -2
- package/dist-es/additionalInfo.js +487 -306
- package/dist-es/additionalInfo.js.map +1 -1
- package/dist-es/ase.d.ts +6 -1
- package/dist-es/ase.js +61 -61
- package/dist-es/ase.js.map +1 -1
- package/dist-es/descriptor.d.ts +14 -8
- package/dist-es/descriptor.js +202 -186
- package/dist-es/descriptor.js.map +1 -1
- package/dist-es/helpers.d.ts +1 -1
- package/dist-es/helpers.js +4 -2
- package/dist-es/helpers.js.map +1 -1
- package/dist-es/imageResources.d.ts +15 -3
- package/dist-es/imageResources.js +503 -142
- package/dist-es/imageResources.js.map +1 -1
- package/dist-es/jpeg.js +8 -8
- package/dist-es/jpeg.js.map +1 -1
- package/dist-es/psd.d.ts +148 -17
- package/dist-es/psd.js.map +1 -1
- package/dist-es/psdReader.d.ts +10 -0
- package/dist-es/psdReader.js +278 -166
- package/dist-es/psdReader.js.map +1 -1
- package/dist-es/psdWriter.d.ts +1 -0
- package/dist-es/psdWriter.js +77 -62
- package/dist-es/psdWriter.js.map +1 -1
- package/dist-es/text.js +17 -6
- package/dist-es/text.js.map +1 -1
- package/dist-es/utf8.js +0 -3
- package/dist-es/utf8.js.map +1 -1
- package/package.json +2 -2
- package/src/abr.ts +375 -235
- package/src/additionalInfo.ts +663 -420
- package/src/ase.ts +66 -61
- package/src/descriptor.ts +199 -195
- package/src/helpers.ts +4 -1
- package/src/imageResources.ts +632 -168
- package/src/jpeg.ts +8 -8
- package/src/psd.ts +156 -30
- package/src/psdReader.ts +273 -172
- package/src/psdWriter.ts +95 -58
- package/src/text.ts +17 -6
- package/src/utf8.ts +0 -4
package/src/descriptor.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createEnum, normalizeLabAB, denormalizeLabAB } from './helpers';
|
|
2
2
|
import {
|
|
3
|
-
AntiAlias, BevelDirection, BevelStyle, BevelTechnique, BlendMode, Color, EffectContour,
|
|
3
|
+
AntiAlias, BevelDirection, BevelStyle, BevelTechnique, BlendMode, Color, ColorStop, EffectContour,
|
|
4
4
|
EffectNoiseGradient, EffectPattern, EffectSolidGradient, ExtraGradientInfo, ExtraPatternInfo,
|
|
5
5
|
GlowSource, GlowTechnique, GradientStyle, InterpolationMethod, LayerEffectBevel,
|
|
6
6
|
LayerEffectGradientOverlay, LayerEffectInnerGlow, LayerEffectPatternOverlay,
|
|
@@ -149,6 +149,7 @@ const fieldToArrayExtType: ExtTypeDict = {
|
|
|
149
149
|
Clrs: makeType('', 'Clrt'),
|
|
150
150
|
Trns: makeType('', 'TrnS'),
|
|
151
151
|
keyDescriptorList: nullType,
|
|
152
|
+
keyRootDescriptorList: nullType,
|
|
152
153
|
solidFillMulti: makeType('', 'SoFi'),
|
|
153
154
|
gradientFillMulti: makeType('', 'GrFl'),
|
|
154
155
|
dropShadowMulti: makeType('', 'DrSh'),
|
|
@@ -183,17 +184,23 @@ const fieldToArrayExtType: ExtTypeDict = {
|
|
|
183
184
|
|
|
184
185
|
const typeToField: { [key: string]: string[]; } = {
|
|
185
186
|
'TEXT': [
|
|
186
|
-
'Txt ', 'printerName', 'Nm ', 'Idnt', 'blackAndWhitePresetFileName', 'LUT3DFileName',
|
|
187
|
+
'Txt ', 'printerName', 'Nm ', 'Idnt', 'blackAndWhitePresetFileName', 'LUT3DFileName', 'exportOption', 'scaleSettings',
|
|
188
|
+
'BackgroundImagePath', 'ImageSubfolderName',
|
|
187
189
|
'presetFileName', 'curvesPresetFileName', 'mixerPresetFileName', 'placed', 'description', 'reason',
|
|
188
190
|
'artboardPresetName', 'json', 'clipID', 'relPath', 'fullPath', 'mediaDescriptor', 'Msge',
|
|
189
|
-
'altTag', 'url', 'cellText', 'preset', 'KnNm', 'FPth', 'comment', 'originalPath',
|
|
191
|
+
'altTag', 'url', 'cellText', 'preset', 'KnNm', 'FPth', 'comment', 'originalPath', 'Bk ', 'pathUnicodeName',
|
|
190
192
|
],
|
|
191
193
|
'tdta': [
|
|
192
194
|
'EngineData', 'LUT3DFileData', 'indexArray', 'originalVertexArray', 'deformedVertexArray',
|
|
193
|
-
'LqMe',
|
|
195
|
+
'LqMe', 'bookKey',
|
|
194
196
|
],
|
|
195
197
|
'long': [
|
|
196
|
-
'TextIndex', 'RndS', 'Mdpn', 'Smth', 'Lctn', 'strokeStyleVersion', 'LaID', 'Vrsn', 'Cnt ',
|
|
198
|
+
'TextIndex', 'RndS', 'Mdpn', 'Smth', 'Lctn', 'strokeStyleVersion', 'LaID', 'Vrsn', 'Cnt ', 'bookID',
|
|
199
|
+
'cropRectBottom', 'cropRectLeft', 'cropRectRight', 'cropRectTop',
|
|
200
|
+
// save for web output settings
|
|
201
|
+
'Version', 'BackgroundColorBlue', 'BackgroundColorGreen', 'BackgroundColorRed', 'BackgroundColorState', 'AttributeCase', 'Encoding',
|
|
202
|
+
'DuplicateFileNameBehavior', 'ImageMapLocation', 'ImageMapType', 'Indent', 'LineEndings', 'SpacersEmptyCells',
|
|
203
|
+
'SpacersHorizontal', 'SpacersVertical', 'StylesFormat', 'TDWidthHeight', 'TagCase', 'ColorPolicy', 'MetadataPolicy',
|
|
197
204
|
'Brgh', 'Cntr', 'means', 'vibrance', 'Strt', 'bwPresetKind', 'comp', 'compID', 'originalCompID',
|
|
198
205
|
'curvesPresetKind', 'mixerPresetKind', 'uOrder', 'vOrder', 'PgNm', 'totalPages', 'Crop',
|
|
199
206
|
'numerator', 'denominator', 'frameCount', 'Annt', 'keyOriginType', 'unitValueQuadVersion',
|
|
@@ -215,7 +222,7 @@ const typeToField: { [key: string]: string[]; } = {
|
|
|
215
222
|
'tableOrder', 'enableCompCore', 'enableCompCoreGPU', 'compCoreSupport', 'compCoreGPUSupport', 'Engn',
|
|
216
223
|
'enableCompCoreThreads', 'gs99', 'FrDs', 'trackID', 'animInterpStyle', 'horzAlign',
|
|
217
224
|
'vertAlign', 'bgColorType', 'shapeOperation', 'UndA', 'Wvtp', 'Drct', 'WndM', 'Edg ', 'FlCl', 'IntE',
|
|
218
|
-
'IntC', 'Cnvr', 'Fl ', 'Dstr', 'MztT', 'Lns ', 'ExtT', 'DspM', 'ExtR', 'ZZTy', 'SphM', 'SmBQ', 'placedLayerOCIOConversion', 'gradientsInterpolationMethod',
|
|
225
|
+
'IntC', 'Cnvr', 'Fl ', 'Dstr', 'MztT', 'Lns ', 'ExtT', 'DspM', 'ExtR', 'ZZTy', 'SphM', 'SmBQ', 'placedLayerOCIOConversion', 'gradientsInterpolationMethod', 'pathSymmetryMode',
|
|
219
226
|
'interpretAlpha',
|
|
220
227
|
],
|
|
221
228
|
'bool': [
|
|
@@ -232,7 +239,7 @@ const typeToField: { [key: string]: string[]; } = {
|
|
|
232
239
|
'moreAccurate', 'GpuY', 'LIWy', 'Cnty', 'Lnkd', 'showAlteredVideo', 'lightingOn',
|
|
233
240
|
],
|
|
234
241
|
'doub': [
|
|
235
|
-
'warpValue', 'warpPerspective', 'warpPerspectiveOther', 'Intr', 'Wdth', 'Hght',
|
|
242
|
+
'warpValue', 'warpPerspective', 'warpPerspectiveOther', 'Intr', 'Wdth', 'Hght', 'Gmm ',
|
|
236
243
|
'strokeStyleMiterLimit', 'strokeStyleResolution', 'layerTime', 'keyOriginResolution',
|
|
237
244
|
'xx', 'xy', 'yx', 'yy', 'tx', 'ty', 'FrGA', 'frameRate', 'audioLevel', 'rotation',
|
|
238
245
|
'X ', 'Y ', 'redFloat', 'greenFloat', 'blueFloat', 'imageResolution',
|
|
@@ -246,7 +253,7 @@ const typeToField: { [key: string]: string[]; } = {
|
|
|
246
253
|
'topRight', 'topLeft', 'bottomLeft', 'bottomRight', 'ClNs', 'Shrp',
|
|
247
254
|
],
|
|
248
255
|
'VlLs': [
|
|
249
|
-
'Crv ', 'Clrs', 'Mnm ', 'Mxm ', 'Trns', 'pathList', 'strokeStyleLineDashSet', 'FrLs', 'slices',
|
|
256
|
+
'Crv ', 'Clrs', 'Mnm ', 'Mxm ', 'Trns', 'pathList', 'keyRootDescriptorList', 'strokeStyleLineDashSet', 'FrLs', 'slices',
|
|
250
257
|
'LaSt', 'Trnf', 'nonAffineTransform', 'keyDescriptorList', 'guideIndeces', 'gradientFillMulti',
|
|
251
258
|
'solidFillMulti', 'frameFXMulti', 'innerShadowMulti', 'dropShadowMulti', 'FrIn', 'FSts', 'FsFr',
|
|
252
259
|
'sheetTimelineOptions', 'audioClipList', 'trackList', 'globalTrackList', 'keyList', 'audioClipList',
|
|
@@ -268,53 +275,37 @@ const channels = [
|
|
|
268
275
|
// color classes whose channel values are written as doubles ('GRYC' and 'LABC' are kept for backwards compatibility)
|
|
269
276
|
const doubleColorClasses = ['Grsc', 'LbCl', 'GRYC', 'LABC'];
|
|
270
277
|
|
|
271
|
-
// Hidden (non-enumerable) markers used to remember how values were stored in the file, so they can be
|
|
272
|
-
// written back the same way. They don't change the shape of the objects returned by the reader.
|
|
273
|
-
const osTypeKey = typeof Symbol !== 'undefined' ? Symbol('agPsdOSType') : '__agPsdOSType';
|
|
274
|
-
const obArInfoKey = typeof Symbol !== 'undefined' ? Symbol('agPsdObAr') : '__agPsdObAr';
|
|
275
|
-
const referenceInfoKey = typeof Symbol !== 'undefined' ? Symbol('agPsdReference') : '__agPsdReference';
|
|
276
|
-
|
|
277
|
-
function setHidden<T extends object>(target: T, key: symbol | string, value: any): T {
|
|
278
|
-
Object.defineProperty(target, key, { value, enumerable: false, configurable: true, writable: true });
|
|
279
|
-
return target;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
function getHidden(target: any, key: symbol | string): any {
|
|
283
|
-
return (target && typeof target === 'object') ? target[key] : undefined;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
const osTypeFamilies: { [key: string]: string; } = {
|
|
287
|
-
UntF: 'UntF', UnFl: 'UntF',
|
|
288
|
-
Objc: 'Objc', GlbO: 'Objc',
|
|
289
|
-
type: 'type', GlbC: 'type',
|
|
290
|
-
comp: 'comp',
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
// returns the OS type the value was originally read with, if it's compatible with the type we'd write it as
|
|
294
278
|
// fallback for unknown keys, based on the type of the value
|
|
295
279
|
function guessOSType(value: any): string | undefined {
|
|
296
280
|
if (typeof value === 'boolean') return 'bool';
|
|
297
281
|
if (typeof value === 'number' && !Number.isInteger(value)) return 'doub';
|
|
298
282
|
if (Array.isArray(value)) return 'VlLs';
|
|
299
|
-
if (value && typeof value === 'object' && typeof value._classID === 'string') return 'Objc';
|
|
300
|
-
return undefined;
|
|
301
|
-
}
|
|
302
283
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
284
|
+
if (value && typeof value === 'object') {
|
|
285
|
+
if (typeof value._classID === 'string') return 'Objc';
|
|
286
|
+
if (typeof value.classID === 'string') return 'type'; // class read from 'type' or 'GlbC'
|
|
287
|
+
if (typeof value.low === 'number' && typeof value.high === 'number') return 'comp';
|
|
288
|
+
if (typeof value.units === 'string' && typeof value.value === 'number') return 'UntF';
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return undefined;
|
|
308
292
|
}
|
|
309
293
|
|
|
310
294
|
const fieldToArrayType: Dict = {
|
|
311
295
|
'Mnm ': 'long',
|
|
312
296
|
'Mxm ': 'long',
|
|
297
|
+
HtmlFileNameComponents: 'long', // save for web output settings
|
|
298
|
+
SavingFileNameComponents: 'long',
|
|
299
|
+
SliceFileNameComponents: 'long',
|
|
300
|
+
Inpt: 'long', // levels filter
|
|
301
|
+
Otpt: 'long', // levels filter
|
|
313
302
|
FrLs: 'long',
|
|
314
303
|
strokeStyleLineDashSet: 'UntF',
|
|
315
304
|
Trnf: 'doub',
|
|
316
305
|
nonAffineTransform: 'doub',
|
|
317
306
|
keyDescriptorList: 'Objc',
|
|
307
|
+
keyRootDescriptorList: 'Objc',
|
|
308
|
+
pathList: 'Objc',
|
|
318
309
|
gradientFillMulti: 'Objc',
|
|
319
310
|
solidFillMulti: 'Objc',
|
|
320
311
|
frameFXMulti: 'Objc',
|
|
@@ -396,6 +387,12 @@ function getTypeByKey(key: string, value: any, root: string, parent: any) {
|
|
|
396
387
|
return 'long';
|
|
397
388
|
} else if ((key === 'Inpt' || key === 'Otpt') && parent._classID === 'HsbP') {
|
|
398
389
|
return 'enum';
|
|
390
|
+
} else if ((key === 'Inpt' || key === 'Otpt') && parent._classID === 'LvlA') {
|
|
391
|
+
return 'VlLs';
|
|
392
|
+
} else if (key === 'Intr' && typeof value === 'boolean') {
|
|
393
|
+
return 'bool'; // interpolation in print style
|
|
394
|
+
} else if (key === 'PgPs') {
|
|
395
|
+
return 'enum';
|
|
399
396
|
} else if (key === 'LghD' && parent._classID === 'oilPaint') {
|
|
400
397
|
return 'long';
|
|
401
398
|
} else {
|
|
@@ -409,7 +406,7 @@ export function readAsciiStringOrClassId(reader: PsdReader) {
|
|
|
409
406
|
}
|
|
410
407
|
|
|
411
408
|
export function writeAsciiStringOrClassId(writer: PsdWriter, value: string) {
|
|
412
|
-
if (value.length === 4 && value !== 'warp' && value !== 'time' && value !== 'hold' && value !== 'list') {
|
|
409
|
+
if (value.length === 4 && value !== 'warp' && value !== 'time' && value !== 'hold' && value !== 'list' && value !== 'json') {
|
|
413
410
|
// write classId
|
|
414
411
|
writeInt32(writer, 0);
|
|
415
412
|
writeSignature(writer, value);
|
|
@@ -441,12 +438,21 @@ export function readDescriptorStructure(reader: PsdReader, includeClass: boolean
|
|
|
441
438
|
return object;
|
|
442
439
|
}
|
|
443
440
|
|
|
441
|
+
export const outputSettingsRoot = 'outputSettings';
|
|
442
|
+
|
|
444
443
|
export function writeDescriptorStructure(writer: PsdWriter, name: string, classId: string, value: any, root: string) {
|
|
445
444
|
if (logErrors && !classId) console.log('Missing classId for: ', name, classId, value);
|
|
446
445
|
|
|
447
446
|
// write class structure
|
|
448
447
|
writeUnicodeStringWithPadding(writer, name);
|
|
449
|
-
|
|
448
|
+
|
|
449
|
+
if (root === outputSettingsRoot && classId === 'null') {
|
|
450
|
+
// Photoshop writes 'null' class id as 4 character string in save for web output settings
|
|
451
|
+
writeInt32(writer, 4);
|
|
452
|
+
writeSignature(writer, classId);
|
|
453
|
+
} else {
|
|
454
|
+
writeAsciiStringOrClassId(writer, classId);
|
|
455
|
+
}
|
|
450
456
|
|
|
451
457
|
const allKeys = Object.keys(value);
|
|
452
458
|
const keys = allKeys.filter(key => key !== '_name' && key !== '_classID' && value[key] !== undefined);
|
|
@@ -533,12 +539,14 @@ export function writeDescriptorStructure(writer: PsdWriter, name: string, classI
|
|
|
533
539
|
extType = makeType('', 'classFloatRect');
|
|
534
540
|
}
|
|
535
541
|
|
|
542
|
+
// guess color class from its fields (colors created by serializeColor always have _classID set)
|
|
536
543
|
if (extType && extType.classID === 'RGBC') {
|
|
537
544
|
if ('H ' in value[key]) extType = { classID: 'HSBC', name: '' };
|
|
538
|
-
|
|
545
|
+
else if ('Cyn ' in value[key]) extType = { classID: 'CMYC', name: '' };
|
|
546
|
+
else if ('Lmnc' in value[key]) extType = { classID: 'LbCl', name: '' };
|
|
547
|
+
else if ('Gry ' in value[key]) extType = { classID: 'Grsc', name: '' };
|
|
539
548
|
}
|
|
540
549
|
|
|
541
|
-
type = getRecordedOSType(value[key], type);
|
|
542
550
|
if (!type) type = guessOSType(value[key]);
|
|
543
551
|
|
|
544
552
|
writeAsciiStringOrClassId(writer, key);
|
|
@@ -553,9 +561,8 @@ function readOSType(reader: PsdReader, type: string, includeClass: boolean) {
|
|
|
553
561
|
case 'obj ': // Reference
|
|
554
562
|
return readReferenceStructure(reader);
|
|
555
563
|
case 'Objc': // Descriptor
|
|
556
|
-
return readDescriptorStructure(reader, includeClass);
|
|
557
564
|
case 'GlbO': // GlobalObject same as Descriptor
|
|
558
|
-
return
|
|
565
|
+
return readDescriptorStructure(reader, includeClass);
|
|
559
566
|
case 'VlLs': { // List
|
|
560
567
|
const length = readInt32(reader);
|
|
561
568
|
const items: any[] = [];
|
|
@@ -580,7 +587,7 @@ function readOSType(reader: PsdReader, type: string, includeClass: boolean) {
|
|
|
580
587
|
const units = readSignature(reader);
|
|
581
588
|
const value = readFloat32(reader);
|
|
582
589
|
if (!unitsMap[units]) throw new Error(`Invalid units: ${units}`);
|
|
583
|
-
return
|
|
590
|
+
return { units: unitsMap[units], value };
|
|
584
591
|
}
|
|
585
592
|
case 'TEXT': // String
|
|
586
593
|
return readUnicodeString(reader);
|
|
@@ -594,13 +601,13 @@ function readOSType(reader: PsdReader, type: string, includeClass: boolean) {
|
|
|
594
601
|
case 'comp': { // Large Integer
|
|
595
602
|
const low = readUint32(reader);
|
|
596
603
|
const high = readUint32(reader);
|
|
597
|
-
return
|
|
604
|
+
return { low, high };
|
|
598
605
|
}
|
|
599
606
|
case 'bool': // Boolean
|
|
600
607
|
return !!readUint8(reader);
|
|
601
608
|
case 'type': // Class
|
|
602
609
|
case 'GlbC': // Class
|
|
603
|
-
return
|
|
610
|
+
return readClassStructure(reader);
|
|
604
611
|
case 'alis': { // Alias
|
|
605
612
|
const length = readInt32(reader);
|
|
606
613
|
return readAsciiString(reader, length);
|
|
@@ -612,24 +619,24 @@ function readOSType(reader: PsdReader, type: string, includeClass: boolean) {
|
|
|
612
619
|
case 'ObAr': { // Object array
|
|
613
620
|
readInt32(reader); // version: 16
|
|
614
621
|
readUnicodeString(reader); // name: ''
|
|
615
|
-
|
|
622
|
+
readAsciiStringOrClassId(reader); // classID: 'rationalPoint'
|
|
616
623
|
const length = readInt32(reader);
|
|
617
624
|
const items: any[] = [];
|
|
618
625
|
|
|
619
626
|
for (let i = 0; i < length; i++) {
|
|
620
627
|
const type1 = readAsciiStringOrClassId(reader); // type Hrzn | Vrtc
|
|
621
|
-
|
|
622
|
-
|
|
628
|
+
readSignature(reader); // UnFl
|
|
629
|
+
readSignature(reader); // units ? '#Pxl'
|
|
623
630
|
const valuesCount = readInt32(reader);
|
|
624
631
|
const values: number[] = [];
|
|
625
632
|
for (let j = 0; j < valuesCount; j++) {
|
|
626
633
|
values.push(readFloat64(reader));
|
|
627
634
|
}
|
|
628
635
|
|
|
629
|
-
items.push(
|
|
636
|
+
items.push({ type: type1, values });
|
|
630
637
|
}
|
|
631
638
|
|
|
632
|
-
return
|
|
639
|
+
return items;
|
|
633
640
|
}
|
|
634
641
|
case 'Pth ': { // File path
|
|
635
642
|
/*const length =*/ readInt32(reader); // total size of all fields below
|
|
@@ -669,7 +676,7 @@ function writeOSType(writer: PsdWriter, type: string, value: any, key: string, e
|
|
|
669
676
|
writeInt32(writer, value.length);
|
|
670
677
|
|
|
671
678
|
for (let i = 0; i < value.length; i++) {
|
|
672
|
-
const type =
|
|
679
|
+
const type = fieldToArrayType[key] ?? guessOSType(value[i]);
|
|
673
680
|
writeSignature(writer, type || 'long');
|
|
674
681
|
writeOSType(writer, type || 'long', value[i], `${key}[]`, fieldToArrayExtType[key], root);
|
|
675
682
|
if (logErrors && !type) console.log(`Missing descriptor array type for: '${key}' in`, value);
|
|
@@ -733,16 +740,15 @@ function writeOSType(writer: PsdWriter, type: string, value: any, key: string, e
|
|
|
733
740
|
case 'ObAr': { // Object array
|
|
734
741
|
writeInt32(writer, 16); // version
|
|
735
742
|
writeUnicodeStringWithPadding(writer, ''); // name
|
|
736
|
-
const type =
|
|
743
|
+
const type = ObArTypes[key];
|
|
737
744
|
if (!type) throw new Error(`Not implemented ObArType for: ${key}`);
|
|
738
745
|
writeAsciiStringOrClassId(writer, type);
|
|
739
746
|
writeInt32(writer, value.length);
|
|
740
747
|
|
|
741
748
|
for (let i = 0; i < value.length; i++) {
|
|
742
|
-
const info = getHidden(value[i], obArInfoKey);
|
|
743
749
|
writeAsciiStringOrClassId(writer, value[i].type); // Hrzn | Vrtc
|
|
744
|
-
writeSignature(writer,
|
|
745
|
-
writeSignature(writer,
|
|
750
|
+
writeSignature(writer, 'UnFl');
|
|
751
|
+
writeSignature(writer, '#Pxl');
|
|
746
752
|
writeInt32(writer, value[i].values.length);
|
|
747
753
|
|
|
748
754
|
for (let j = 0; j < value[i].values.length; j++) {
|
|
@@ -752,12 +758,13 @@ function writeOSType(writer: PsdWriter, type: string, value: any, key: string, e
|
|
|
752
758
|
break;
|
|
753
759
|
}
|
|
754
760
|
case 'Pth ': { // File path
|
|
755
|
-
const
|
|
761
|
+
const path = value.path + '\0'; // Photoshop stores trailing null character (stripped when reading)
|
|
762
|
+
const length = 4 + 4 + 4 + path.length * 2;
|
|
756
763
|
writeInt32(writer, length);
|
|
757
764
|
writeSignature(writer, value.sig);
|
|
758
765
|
writeInt32LE(writer, length);
|
|
759
|
-
writeInt32LE(writer,
|
|
760
|
-
writeUnicodeStringWithoutLengthLE(writer,
|
|
766
|
+
writeInt32LE(writer, path.length);
|
|
767
|
+
writeUnicodeStringWithoutLengthLE(writer, path);
|
|
761
768
|
break;
|
|
762
769
|
}
|
|
763
770
|
default:
|
|
@@ -765,86 +772,64 @@ function writeOSType(writer: PsdWriter, type: string, value: any, key: string, e
|
|
|
765
772
|
}
|
|
766
773
|
}
|
|
767
774
|
|
|
775
|
+
// Reference item with explicit reference type and class, can be used in place of a value in reference arrays
|
|
776
|
+
// passed to writeVersionAndDescriptor (references are read as values only: strings, numbers and class objects)
|
|
768
777
|
export interface DescriptorReferenceItem {
|
|
769
778
|
type: 'prop' | 'Clss' | 'Enmr' | 'rele' | 'Idnt' | 'indx' | 'name';
|
|
770
|
-
|
|
771
|
-
|
|
779
|
+
_name?: string; // class name
|
|
780
|
+
_classID?: string;
|
|
772
781
|
keyID?: string; // prop
|
|
773
782
|
typeID?: string; // Enmr
|
|
774
783
|
enumValue?: string; // Enmr
|
|
775
784
|
value?: string | number; // rele, Idnt, indx, name
|
|
776
785
|
}
|
|
777
786
|
|
|
778
|
-
|
|
779
|
-
switch (item.type) {
|
|
780
|
-
case 'prop': return item.keyID;
|
|
781
|
-
case 'Clss': return { name: item.name || '', classID: item.classID };
|
|
782
|
-
case 'Enmr': return `${item.typeID}.${item.enumValue}`;
|
|
783
|
-
default: return item.value;
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
function referenceItemMatches(item: DescriptorReferenceItem | undefined, value: any) {
|
|
788
|
-
if (!item) return false;
|
|
789
|
-
const expected = referenceItemToValue(item);
|
|
790
|
-
if (expected && typeof expected === 'object') {
|
|
791
|
-
return !!value && typeof value === 'object' && value.name === expected.name && value.classID === expected.classID;
|
|
792
|
-
}
|
|
793
|
-
return expected === value;
|
|
794
|
-
}
|
|
787
|
+
const referenceTypes = ['prop', 'Clss', 'Enmr', 'rele', 'Idnt', 'indx', 'name'];
|
|
795
788
|
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
export function createDescriptorReference(items: DescriptorReferenceItem[]): any[] {
|
|
799
|
-
return setHidden(items.map(referenceItemToValue), referenceInfoKey, items);
|
|
789
|
+
function isReferenceItem(value: any): value is DescriptorReferenceItem {
|
|
790
|
+
return !!value && typeof value === 'object' && referenceTypes.indexOf(value.type) !== -1;
|
|
800
791
|
}
|
|
801
792
|
|
|
802
793
|
function readReferenceStructure(reader: PsdReader) {
|
|
803
794
|
const itemsCount = readInt32(reader);
|
|
804
|
-
const
|
|
795
|
+
const items: any[] = [];
|
|
805
796
|
|
|
806
797
|
for (let i = 0; i < itemsCount; i++) {
|
|
807
798
|
const type = readSignature(reader);
|
|
808
799
|
|
|
809
800
|
switch (type) {
|
|
810
|
-
case 'prop':
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
infos.push({ type, name, classID, keyID });
|
|
801
|
+
case 'prop': // Property
|
|
802
|
+
readClassStructure(reader);
|
|
803
|
+
items.push(readAsciiStringOrClassId(reader));
|
|
814
804
|
break;
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
const { name, classID } = readClassStructure(reader);
|
|
818
|
-
infos.push({ type, name, classID });
|
|
805
|
+
case 'Clss': // Class
|
|
806
|
+
items.push(readClassStructure(reader));
|
|
819
807
|
break;
|
|
820
|
-
}
|
|
821
808
|
case 'Enmr': { // Enumerated Reference
|
|
822
|
-
|
|
809
|
+
readClassStructure(reader);
|
|
823
810
|
const typeID = readAsciiStringOrClassId(reader);
|
|
824
811
|
const enumValue = readAsciiStringOrClassId(reader);
|
|
825
|
-
|
|
812
|
+
items.push(`${typeID}.${enumValue}`);
|
|
826
813
|
break;
|
|
827
814
|
}
|
|
828
|
-
case 'rele':
|
|
829
|
-
|
|
830
|
-
|
|
815
|
+
case 'rele': // Offset
|
|
816
|
+
readClassStructure(reader);
|
|
817
|
+
items.push(readUint32(reader));
|
|
831
818
|
break;
|
|
832
|
-
}
|
|
833
819
|
case 'Idnt': // Identifier
|
|
834
820
|
case 'indx': // Index
|
|
835
|
-
|
|
821
|
+
items.push(readInt32(reader));
|
|
836
822
|
break;
|
|
837
|
-
case 'name':
|
|
838
|
-
|
|
839
|
-
|
|
823
|
+
case 'name': // Name
|
|
824
|
+
readClassStructure(reader);
|
|
825
|
+
items.push(readUnicodeString(reader));
|
|
840
826
|
break;
|
|
841
|
-
}
|
|
842
827
|
default:
|
|
843
828
|
throw new Error(`Invalid descriptor reference type: ${type}`);
|
|
844
829
|
}
|
|
845
830
|
}
|
|
846
831
|
|
|
847
|
-
return
|
|
832
|
+
return items;
|
|
848
833
|
}
|
|
849
834
|
|
|
850
835
|
function guessReferenceItem(value: any): DescriptorReferenceItem {
|
|
@@ -854,14 +839,14 @@ function guessReferenceItem(value: any): DescriptorReferenceItem {
|
|
|
854
839
|
const match = /^([a-z ]{4}|[a-z]{5,})\.([a-z ]+)$/i.exec(value);
|
|
855
840
|
|
|
856
841
|
if (match) {
|
|
857
|
-
return { type: 'Enmr',
|
|
842
|
+
return { type: 'Enmr', _name: '', _classID: match[1], typeID: match[1], enumValue: match[2] };
|
|
858
843
|
} else {
|
|
859
|
-
return { type: 'name',
|
|
844
|
+
return { type: 'name', _name: '', _classID: 'Lyr ', value };
|
|
860
845
|
}
|
|
861
846
|
} else if (typeof value === 'number') {
|
|
862
847
|
return { type: 'Idnt', value };
|
|
863
848
|
} else if (value && typeof value === 'object' && typeof value.classID === 'string') {
|
|
864
|
-
return { type: 'Clss',
|
|
849
|
+
return { type: 'Clss', _name: value.name || '', _classID: value.classID };
|
|
865
850
|
} else {
|
|
866
851
|
throw new Error(`Invalid descriptor reference value: ${JSON.stringify(value)}`);
|
|
867
852
|
}
|
|
@@ -869,46 +854,39 @@ function guessReferenceItem(value: any): DescriptorReferenceItem {
|
|
|
869
854
|
|
|
870
855
|
// path selection state (image resource 1088) is a reference to paths, enumerated references there use 'Path' class
|
|
871
856
|
// (name references use 'Lyr ' class, they reference layer of the vector mask)
|
|
872
|
-
export function pathSelectionStateToReference(items: string[]):
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
return createDescriptorReference(items.map((value, i) => {
|
|
876
|
-
if (referenceItemMatches(infos?.[i], value)) return infos![i]; // keep information read from the file
|
|
877
|
-
|
|
857
|
+
export function pathSelectionStateToReference(items: string[]): DescriptorReferenceItem[] {
|
|
858
|
+
return items.map(value => {
|
|
878
859
|
const item = guessReferenceItem(value);
|
|
879
|
-
if (item.type === 'Enmr') item.
|
|
860
|
+
if (item.type === 'Enmr') item._classID = 'Path';
|
|
880
861
|
return item;
|
|
881
|
-
})
|
|
862
|
+
});
|
|
882
863
|
}
|
|
883
864
|
|
|
884
865
|
function writeReferenceStructure(writer: PsdWriter, _key: string, items: any[]) {
|
|
885
866
|
if (!Array.isArray(items)) throw new Error(`Invalid reference value: ${JSON.stringify(items)}, key: ${_key}`);
|
|
886
867
|
|
|
887
|
-
const infos: DescriptorReferenceItem[] | undefined = getHidden(items, referenceInfoKey);
|
|
888
868
|
writeInt32(writer, items.length);
|
|
889
869
|
|
|
890
|
-
for (
|
|
891
|
-
const
|
|
892
|
-
const recorded = infos?.[i];
|
|
893
|
-
const item = referenceItemMatches(recorded, value) ? recorded! : guessReferenceItem(value);
|
|
870
|
+
for (const value of items) {
|
|
871
|
+
const item = isReferenceItem(value) ? value : guessReferenceItem(value);
|
|
894
872
|
|
|
895
873
|
writeSignature(writer, item.type);
|
|
896
874
|
|
|
897
875
|
switch (item.type) {
|
|
898
876
|
case 'prop': // Property
|
|
899
|
-
writeClassStructure(writer, item.
|
|
877
|
+
writeClassStructure(writer, item._name || '', item._classID!);
|
|
900
878
|
writeAsciiStringOrClassId(writer, item.keyID!);
|
|
901
879
|
break;
|
|
902
880
|
case 'Clss': // Class
|
|
903
|
-
writeClassStructure(writer, item.
|
|
881
|
+
writeClassStructure(writer, item._name || '', item._classID!);
|
|
904
882
|
break;
|
|
905
883
|
case 'Enmr': // Enumerated Reference
|
|
906
|
-
writeClassStructure(writer, item.
|
|
884
|
+
writeClassStructure(writer, item._name || '', item._classID!);
|
|
907
885
|
writeAsciiStringOrClassId(writer, item.typeID!);
|
|
908
886
|
writeAsciiStringOrClassId(writer, item.enumValue!);
|
|
909
887
|
break;
|
|
910
888
|
case 'rele': // Offset
|
|
911
|
-
writeClassStructure(writer, item.
|
|
889
|
+
writeClassStructure(writer, item._name || '', item._classID!);
|
|
912
890
|
writeUint32(writer, item.value as number);
|
|
913
891
|
break;
|
|
914
892
|
case 'Idnt': // Identifier
|
|
@@ -916,7 +894,7 @@ function writeReferenceStructure(writer: PsdWriter, _key: string, items: any[])
|
|
|
916
894
|
writeInt32(writer, item.value as number);
|
|
917
895
|
break;
|
|
918
896
|
case 'name': // Name
|
|
919
|
-
writeClassStructure(writer, item.
|
|
897
|
+
writeClassStructure(writer, item._name || '', item._classID!);
|
|
920
898
|
writeUnicodeStringWithPadding(writer, `${item.value}`);
|
|
921
899
|
break;
|
|
922
900
|
default:
|
|
@@ -1006,8 +984,8 @@ export type DesciptorGradient = {
|
|
|
1006
984
|
GrdF: 'GrdF.CstS';
|
|
1007
985
|
Intr: number;
|
|
1008
986
|
Clrs: {
|
|
1009
|
-
'Clr '
|
|
1010
|
-
Type
|
|
987
|
+
'Clr '?: DescriptorColor;
|
|
988
|
+
Type?: string; // 'Clry.UsrS' | 'Clry.FrgC' | 'Clry.BckC'
|
|
1011
989
|
Lctn: number;
|
|
1012
990
|
Mdpn: number;
|
|
1013
991
|
}[];
|
|
@@ -1047,7 +1025,9 @@ export interface DescriptorGradientContent {
|
|
|
1047
1025
|
export interface DescriptorPatternContent {
|
|
1048
1026
|
Ptrn: DesciptorPattern;
|
|
1049
1027
|
Lnkd?: boolean;
|
|
1050
|
-
phase?: { Hrzn: number; Vrtc: number; };
|
|
1028
|
+
phase?: { Hrzn: number | DescriptorUnitsValue; Vrtc: number | DescriptorUnitsValue; };
|
|
1029
|
+
'Scl '?: DescriptorUnitsValue;
|
|
1030
|
+
Angl?: DescriptorUnitsValue;
|
|
1051
1031
|
}
|
|
1052
1032
|
|
|
1053
1033
|
export type DescriptorVectorContent = DescriptorColorContent | DescriptorGradientContent | DescriptorPatternContent;
|
|
@@ -1343,7 +1323,7 @@ function parseFxObject(fx: EffectDescriptor) {
|
|
|
1343
1323
|
fillType: FrFl.decode(fx.PntT!),
|
|
1344
1324
|
blendMode: BlnM.decode(fx['Md ']!),
|
|
1345
1325
|
opacity: parsePercent(fx.Opct),
|
|
1346
|
-
size:
|
|
1326
|
+
size: parseUnitsOrNumber(fx['Sz '] as DescriptorUnitsValue | number), // stored as number (pixels) in old versions
|
|
1347
1327
|
};
|
|
1348
1328
|
|
|
1349
1329
|
if (fx.present !== undefined) stroke.present = fx.present;
|
|
@@ -1428,16 +1408,16 @@ export function serializeEffects(e: LayerEffectsInfo, log: boolean, multi: boole
|
|
|
1428
1408
|
|
|
1429
1409
|
export function parseEffects(info: Lfx2Descriptor & LmfxDescriptor, log: boolean) {
|
|
1430
1410
|
const effects: LayerEffectsInfo = {};
|
|
1431
|
-
const { masterFXSwitch, DrSh, dropShadowMulti, IrSh, innerShadowMulti, OrGl, IrGl, ebbl, SoFi, solidFillMulti, patternFill, GrFl, gradientFillMulti, ChFX, FrFX, frameFXMulti, numModifyingFX, ...rest } = info;
|
|
1411
|
+
const { _name, _classID, 'Scl ': scale, masterFXSwitch, DrSh, dropShadowMulti, IrSh, innerShadowMulti, OrGl, IrGl, ebbl, SoFi, solidFillMulti, patternFill, GrFl, gradientFillMulti, ChFX, FrFX, frameFXMulti, numModifyingFX, ...rest } = info as typeof info & { _name?: string; _classID?: string; };
|
|
1432
1412
|
if (!masterFXSwitch) effects.disabled = true;
|
|
1433
|
-
if (
|
|
1413
|
+
if (scale) effects.scale = parsePercent(scale);
|
|
1434
1414
|
if (DrSh) effects.dropShadow = [parseEffectObject(DrSh, log)];
|
|
1435
1415
|
if (dropShadowMulti) effects.dropShadow = dropShadowMulti.map(i => parseEffectObject(i, log));
|
|
1436
1416
|
if (IrSh) effects.innerShadow = [parseEffectObject(IrSh, log)];
|
|
1437
1417
|
if (innerShadowMulti) effects.innerShadow = innerShadowMulti.map(i => parseEffectObject(i, log));
|
|
1438
1418
|
if (OrGl) effects.outerGlow = parseEffectObject(OrGl, log);
|
|
1439
1419
|
if (IrGl) effects.innerGlow = parseEffectObject(IrGl, log);
|
|
1440
|
-
if (ebbl) effects.bevel = parseEffectObject(ebbl, log);
|
|
1420
|
+
if (ebbl) effects.bevel = parseEffectObject(ebbl, log, 'bevel');
|
|
1441
1421
|
if (SoFi) effects.solidFill = [parseEffectObject(SoFi, log)];
|
|
1442
1422
|
if (solidFillMulti) effects.solidFill = solidFillMulti.map(i => parseEffectObject(i, log));
|
|
1443
1423
|
if (patternFill) effects.patternOverlay = parseEffectObject(patternFill, log);
|
|
@@ -1446,7 +1426,7 @@ export function parseEffects(info: Lfx2Descriptor & LmfxDescriptor, log: boolean
|
|
|
1446
1426
|
if (ChFX) effects.satin = parseEffectObject(ChFX, log);
|
|
1447
1427
|
if (FrFX) effects.stroke = [parseFxObject(FrFX)];
|
|
1448
1428
|
if (frameFXMulti) effects.stroke = frameFXMulti.map(i => parseFxObject(i));
|
|
1449
|
-
if (log && Object.keys(rest).length
|
|
1429
|
+
if (log && Object.keys(rest).length) console.log('Unhandled effect keys:', rest);
|
|
1450
1430
|
return effects;
|
|
1451
1431
|
}
|
|
1452
1432
|
|
|
@@ -1581,7 +1561,7 @@ type AllEffects = LayerEffectShadow & LayerEffectsOuterGlow & LayerEffectStroke
|
|
|
1581
1561
|
LayerEffectInnerGlow & LayerEffectBevel & LayerEffectSolidFill &
|
|
1582
1562
|
LayerEffectPatternOverlay & LayerEffectSatin & LayerEffectGradientOverlay;
|
|
1583
1563
|
|
|
1584
|
-
function parseEffectObject(obj: any, reportErrors: boolean) {
|
|
1564
|
+
function parseEffectObject(obj: any, reportErrors: boolean, objName?: string) {
|
|
1585
1565
|
const result: AllEffects = {} as any;
|
|
1586
1566
|
|
|
1587
1567
|
for (const key of Object.keys(obj)) {
|
|
@@ -1608,7 +1588,10 @@ function parseEffectObject(obj: any, reportErrors: boolean) {
|
|
|
1608
1588
|
case 'GlwT': result.technique = BETE.decode(val) as any; break;
|
|
1609
1589
|
case 'glwS': result.source = IGSr.decode(val); break;
|
|
1610
1590
|
case 'Type': result.type = GrdT.decode(val); break;
|
|
1611
|
-
case 'gs99':
|
|
1591
|
+
case 'gs99': // gradient overlay
|
|
1592
|
+
case 'gradientsInterpolationMethod': // glows
|
|
1593
|
+
result.interpolationMethod = gradientInterpolationMethodType.decode(val);
|
|
1594
|
+
break;
|
|
1612
1595
|
case 'Opct': result.opacity = parsePercent(val); break;
|
|
1613
1596
|
case 'hglO': result.highlightOpacity = parsePercent(val); break;
|
|
1614
1597
|
case 'sdwO': result.shadowOpacity = parsePercent(val); break;
|
|
@@ -1625,15 +1608,23 @@ function parseEffectObject(obj: any, reportErrors: boolean) {
|
|
|
1625
1608
|
case 'Dstn': result.distance = parseUnits(val); break;
|
|
1626
1609
|
case 'Scl ': result.scale = parsePercent(val); break;
|
|
1627
1610
|
case 'Ptrn': result.pattern = { name: val['Nm '], id: val.Idnt }; break;
|
|
1628
|
-
case 'phase': result.phase =
|
|
1611
|
+
case 'phase': result.phase = parsePhase(val); break;
|
|
1629
1612
|
case 'Ofst': result.offset = { x: parsePercent(val.Hrzn), y: parsePercent(val.Vrtc) }; break;
|
|
1630
1613
|
case 'MpgS':
|
|
1631
|
-
case 'TrnS':
|
|
1632
|
-
|
|
1614
|
+
case 'TrnS': {
|
|
1615
|
+
const contour: EffectContour = {
|
|
1633
1616
|
name: val['Nm '],
|
|
1634
1617
|
curve: (val['Crv '] as any[]).map(p => ({ x: p.Hrzn, y: p.Vrtc })),
|
|
1635
1618
|
};
|
|
1619
|
+
|
|
1620
|
+
// bevel can have both: gloss contour (TrnS) and contour of the shape (MpgS)
|
|
1621
|
+
if (objName === 'bevel' && key === 'MpgS') {
|
|
1622
|
+
result.shapeContour = contour;
|
|
1623
|
+
} else {
|
|
1624
|
+
result.contour = contour;
|
|
1625
|
+
}
|
|
1636
1626
|
break;
|
|
1627
|
+
}
|
|
1637
1628
|
case 'Grad': result.gradient = parseGradient(val); break;
|
|
1638
1629
|
case 'useTexture':
|
|
1639
1630
|
case 'useShape':
|
|
@@ -1691,7 +1682,9 @@ function serializeEffectObject(obj: any, objName: string, reportErrors: boolean)
|
|
|
1691
1682
|
break;
|
|
1692
1683
|
case 'source': result.glwS = IGSr.encode(val); break;
|
|
1693
1684
|
case 'type': result.Type = GrdT.encode(val); break;
|
|
1694
|
-
case 'interpolationMethod':
|
|
1685
|
+
case 'interpolationMethod':
|
|
1686
|
+
result[(objName === 'outerGlow' || objName === 'innerGlow') ? 'gradientsInterpolationMethod' : 'gs99'] = gradientInterpolationMethodType.encode(val);
|
|
1687
|
+
break;
|
|
1695
1688
|
case 'opacity': result.Opct = unitsPercent(val); break;
|
|
1696
1689
|
case 'highlightOpacity': result.hglO = unitsPercent(val); break;
|
|
1697
1690
|
case 'shadowOpacity': result.sdwO = unitsPercent(val); break;
|
|
@@ -1714,14 +1707,9 @@ function serializeEffectObject(obj: any, objName: string, reportErrors: boolean)
|
|
|
1714
1707
|
case 'scale': result['Scl '] = unitsPercent(val); break;
|
|
1715
1708
|
case 'pattern': result.Ptrn = { 'Nm ': val.name, Idnt: val.id }; break;
|
|
1716
1709
|
case 'phase': result.phase = { Hrzn: val.x, Vrtc: val.y }; break;
|
|
1717
|
-
case 'offset': result.Ofst = { Hrzn:
|
|
1718
|
-
case 'contour':
|
|
1719
|
-
|
|
1720
|
-
'Nm ': (val as EffectContour).name,
|
|
1721
|
-
'Crv ': (val as EffectContour).curve.map(p => ({ Hrzn: p.x, Vrtc: p.y })),
|
|
1722
|
-
};
|
|
1723
|
-
break;
|
|
1724
|
-
}
|
|
1710
|
+
case 'offset': result.Ofst = { Hrzn: unitsPercentF(val.x), Vrtc: unitsPercentF(val.y) }; break;
|
|
1711
|
+
case 'contour': result[objName === 'satin' ? 'MpgS' : 'TrnS'] = serializeContour(val); break;
|
|
1712
|
+
case 'shapeContour': result.MpgS = serializeContour(val); break;
|
|
1725
1713
|
case 'gradient': result.Grad = serializeGradient(val); break;
|
|
1726
1714
|
case 'useTexture':
|
|
1727
1715
|
case 'useShape':
|
|
@@ -1739,6 +1727,13 @@ function serializeEffectObject(obj: any, objName: string, reportErrors: boolean)
|
|
|
1739
1727
|
return result;
|
|
1740
1728
|
}
|
|
1741
1729
|
|
|
1730
|
+
function serializeContour(contour: EffectContour) {
|
|
1731
|
+
return {
|
|
1732
|
+
'Nm ': contour.name,
|
|
1733
|
+
'Crv ': contour.curve.map(p => ({ Hrzn: p.x, Vrtc: p.y })),
|
|
1734
|
+
};
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1742
1737
|
function parseGradient(grad: DesciptorGradient): EffectSolidGradient | EffectNoiseGradient {
|
|
1743
1738
|
if (grad.GrdF === 'GrdF.CstS') {
|
|
1744
1739
|
// stop locations are always in 0..4096 range, independent of smoothness (Intr)
|
|
@@ -1748,11 +1743,22 @@ function parseGradient(grad: DesciptorGradient): EffectSolidGradient | EffectNoi
|
|
|
1748
1743
|
type: 'solid',
|
|
1749
1744
|
name: grad['Nm '],
|
|
1750
1745
|
smoothness: grad.Intr / 4096,
|
|
1751
|
-
colorStops: grad.Clrs.map(s =>
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1746
|
+
colorStops: grad.Clrs.map(s => {
|
|
1747
|
+
const stop: ColorStop = {
|
|
1748
|
+
color: s['Clr '] ? parseColor(s['Clr ']) : { r: 0, g: 0, b: 0 },
|
|
1749
|
+
location: s.Lctn / samples,
|
|
1750
|
+
midpoint: s.Mdpn / 100,
|
|
1751
|
+
};
|
|
1752
|
+
|
|
1753
|
+
if (s.Type) {
|
|
1754
|
+
try {
|
|
1755
|
+
const type = Clry.decode(s.Type);
|
|
1756
|
+
if (type !== 'user') stop.type = type;
|
|
1757
|
+
} catch { } // unknown stop type, read as user color
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
return stop;
|
|
1761
|
+
}),
|
|
1756
1762
|
opacityStops: grad.Trns.map(s => ({
|
|
1757
1763
|
opacity: parsePercent(s.Opct),
|
|
1758
1764
|
location: s.Lctn / samples,
|
|
@@ -1784,12 +1790,12 @@ function serializeGradient(grad: EffectSolidGradient | EffectNoiseGradient): Des
|
|
|
1784
1790
|
Intr: Math.round((grad.smoothness ?? 1) * 4096),
|
|
1785
1791
|
Clrs: grad.colorStops.map(s => ({
|
|
1786
1792
|
'Clr ': serializeColor(s.color),
|
|
1787
|
-
Type:
|
|
1793
|
+
Type: Clry.encode(s.type ?? 'user'),
|
|
1788
1794
|
Lctn: Math.round(s.location * samples),
|
|
1789
1795
|
Mdpn: Math.round((s.midpoint ?? 0.5) * 100),
|
|
1790
1796
|
})),
|
|
1791
1797
|
Trns: grad.opacityStops.map(s => ({
|
|
1792
|
-
Opct:
|
|
1798
|
+
Opct: unitsPercentF(s.opacity),
|
|
1793
1799
|
Lctn: Math.round(s.location * samples),
|
|
1794
1800
|
Mdpn: Math.round((s.midpoint ?? 0.5) * 100),
|
|
1795
1801
|
})),
|
|
@@ -1827,13 +1833,23 @@ function parseGradientContent(descriptor: DescriptorGradientContent) {
|
|
|
1827
1833
|
return result;
|
|
1828
1834
|
}
|
|
1829
1835
|
|
|
1836
|
+
// phase is usually stored as plain numbers, but some files store it as unit values
|
|
1837
|
+
function parsePhase({ Hrzn, Vrtc }: { Hrzn: number | DescriptorUnitsValue; Vrtc: number | DescriptorUnitsValue; }) {
|
|
1838
|
+
return {
|
|
1839
|
+
x: typeof Hrzn === 'number' ? Hrzn : Hrzn.value,
|
|
1840
|
+
y: typeof Vrtc === 'number' ? Vrtc : Vrtc.value,
|
|
1841
|
+
};
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1830
1844
|
function parsePatternContent(descriptor: DescriptorPatternContent) {
|
|
1831
1845
|
const result: EffectPattern & ExtraPatternInfo = {
|
|
1832
1846
|
name: descriptor.Ptrn['Nm '],
|
|
1833
1847
|
id: descriptor.Ptrn.Idnt,
|
|
1834
1848
|
};
|
|
1835
1849
|
if (descriptor.Lnkd !== undefined) result.linked = descriptor.Lnkd;
|
|
1836
|
-
if (descriptor.phase !== undefined) result.phase =
|
|
1850
|
+
if (descriptor.phase !== undefined) result.phase = parsePhase(descriptor.phase);
|
|
1851
|
+
if (descriptor['Scl '] !== undefined) result.scale = parsePercent(descriptor['Scl ']);
|
|
1852
|
+
if (descriptor.Angl !== undefined) result.angle = parseAngle(descriptor.Angl);
|
|
1837
1853
|
return result;
|
|
1838
1854
|
}
|
|
1839
1855
|
|
|
@@ -1858,11 +1874,11 @@ function serializeGradientContent(content: (EffectSolidGradient | EffectNoiseGra
|
|
|
1858
1874
|
if (content.angle !== undefined) result.Angl = unitsAngle(content.angle);
|
|
1859
1875
|
result.Type = GrdT.encode(content.style);
|
|
1860
1876
|
if (content.align !== undefined) result.Algn = content.align;
|
|
1861
|
-
if (content.scale !== undefined) result['Scl '] =
|
|
1877
|
+
if (content.scale !== undefined) result['Scl '] = unitsPercentF(content.scale);
|
|
1862
1878
|
if (content.offset) {
|
|
1863
1879
|
result.Ofst = {
|
|
1864
|
-
Hrzn:
|
|
1865
|
-
Vrtc:
|
|
1880
|
+
Hrzn: unitsPercentF(content.offset.x),
|
|
1881
|
+
Vrtc: unitsPercentF(content.offset.y),
|
|
1866
1882
|
};
|
|
1867
1883
|
}
|
|
1868
1884
|
result.Grad = serializeGradient(content);
|
|
@@ -1878,6 +1894,8 @@ function serializePatternContent(content: EffectPattern & ExtraPatternInfo) {
|
|
|
1878
1894
|
};
|
|
1879
1895
|
if (content.linked !== undefined) result.Lnkd = !!content.linked;
|
|
1880
1896
|
if (content.phase !== undefined) result.phase = { Hrzn: content.phase.x, Vrtc: content.phase.y };
|
|
1897
|
+
if (content.scale !== undefined) result['Scl '] = unitsPercentF(content.scale);
|
|
1898
|
+
if (content.angle !== undefined) result.Angl = unitsAngle(content.angle);
|
|
1881
1899
|
return result;
|
|
1882
1900
|
}
|
|
1883
1901
|
|
|
@@ -1894,10 +1912,9 @@ export function serializeVectorContent(content: VectorContent): { descriptor: De
|
|
|
1894
1912
|
// Descriptor color values: HSB saturation/brightness 0..100, CMYK 0..100, Gray 0..100, Lab L 0..100, a/b -128..127
|
|
1895
1913
|
// Color values (see psd.ts): HSB 0..1, CMYK 0..255, Gray 0..255, Lab L 0..1, a/b -1..1 (same as readColor/writeColor)
|
|
1896
1914
|
|
|
1897
|
-
const colorSourceKey = typeof Symbol !== 'undefined' ? Symbol('agPsdColorSource') : '__agPsdColorSource';
|
|
1898
|
-
|
|
1899
1915
|
const colorConversions: { [key: string]: ((x: number) => number)[] | undefined; } = {
|
|
1900
1916
|
// key: [descriptor -> color, color -> descriptor]
|
|
1917
|
+
'H ': [x => x / 360, x => x * 360], // angle
|
|
1901
1918
|
Strt: [x => x / 100, x => x * 100],
|
|
1902
1919
|
Brgh: [x => x / 100, x => x * 100],
|
|
1903
1920
|
'Cyn ': [x => x * 255 / 100, x => x * 100 / 255],
|
|
@@ -1926,41 +1943,36 @@ function colorToDescriptorValue(key: string, value: number) {
|
|
|
1926
1943
|
return result;
|
|
1927
1944
|
}
|
|
1928
1945
|
|
|
1929
|
-
function
|
|
1930
|
-
|
|
1931
|
-
if (keys.length !== Object.keys(b).length) return false;
|
|
1932
|
-
return keys.every(key => a[key] === b[key]);
|
|
1946
|
+
function isBookColor(color: any) {
|
|
1947
|
+
return color._classID === 'BkCl' || 'Bk ' in color || 'bookID' in color;
|
|
1933
1948
|
}
|
|
1934
1949
|
|
|
1935
1950
|
export function parseColor(color: DescriptorColor): Color {
|
|
1936
|
-
let result: Color;
|
|
1937
|
-
|
|
1938
1951
|
if ('H ' in color) {
|
|
1939
|
-
|
|
1952
|
+
return { h: parsePercentOrAngle(color['H ']), s: color.Strt / 100, b: color.Brgh / 100 };
|
|
1940
1953
|
} else if ('Rd ' in color) {
|
|
1941
1954
|
return { r: color['Rd '], g: color['Grn '], b: color['Bl '] };
|
|
1942
1955
|
} else if ('Cyn ' in color) {
|
|
1943
|
-
|
|
1956
|
+
return {
|
|
1944
1957
|
c: color['Cyn '] * 255 / 100,
|
|
1945
1958
|
m: color.Mgnt * 255 / 100,
|
|
1946
1959
|
y: color['Ylw '] * 255 / 100,
|
|
1947
1960
|
k: color.Blck * 255 / 100,
|
|
1948
1961
|
};
|
|
1949
1962
|
} else if ('Gry ' in color) {
|
|
1950
|
-
|
|
1963
|
+
return { k: color['Gry '] * 255 / 100 };
|
|
1951
1964
|
} else if ('Lmnc' in color) {
|
|
1952
|
-
|
|
1965
|
+
return { l: color.Lmnc / 100, a: normalizeLabAB(color['A ']), b: normalizeLabAB(color['B ']) };
|
|
1953
1966
|
} else if ('redFloat' in color) {
|
|
1954
1967
|
return { fr: color.redFloat, fg: color.greenFloat, fb: color.blueFloat };
|
|
1968
|
+
} else if (isBookColor(color)) {
|
|
1969
|
+
return { r: 0, g: 0, b: 0 }; // book (Pantone) colors are not supported
|
|
1955
1970
|
} else {
|
|
1956
1971
|
throw new Error('Unsupported color descriptor');
|
|
1957
1972
|
}
|
|
1958
|
-
|
|
1959
|
-
// remember original descriptor values, so unmodified colors can be written back without conversion errors
|
|
1960
|
-
return setHidden(result, colorSourceKey, { color: { ...result }, descriptor: color });
|
|
1961
1973
|
}
|
|
1962
1974
|
|
|
1963
|
-
function
|
|
1975
|
+
export function serializeColor(color: Color | undefined): DescriptorColor {
|
|
1964
1976
|
if (!color) {
|
|
1965
1977
|
return { _name: '', _classID: 'RGBC', 'Rd ': 0, 'Grn ': 0, 'Bl ': 0 };
|
|
1966
1978
|
} else if ('r' in color) {
|
|
@@ -1970,7 +1982,7 @@ function serializeColorValues(color: Color | undefined): DescriptorColor {
|
|
|
1970
1982
|
} else if ('h' in color) {
|
|
1971
1983
|
return {
|
|
1972
1984
|
_name: '', _classID: 'HSBC',
|
|
1973
|
-
'H ': unitsAngle((color.h || 0)
|
|
1985
|
+
'H ': unitsAngle(colorToDescriptorValue('H ', color.h || 0)),
|
|
1974
1986
|
Strt: colorToDescriptorValue('Strt', color.s || 0),
|
|
1975
1987
|
Brgh: colorToDescriptorValue('Brgh', color.b || 0),
|
|
1976
1988
|
};
|
|
@@ -1996,22 +2008,6 @@ function serializeColorValues(color: Color | undefined): DescriptorColor {
|
|
|
1996
2008
|
}
|
|
1997
2009
|
}
|
|
1998
2010
|
|
|
1999
|
-
export function serializeColor(color: Color | undefined): DescriptorColor {
|
|
2000
|
-
const result: any = serializeColorValues(color);
|
|
2001
|
-
const source = getHidden(color, colorSourceKey);
|
|
2002
|
-
|
|
2003
|
-
if (source && shallowEqual(source.color, color)) {
|
|
2004
|
-
// color was not modified since it was read, use exact original values
|
|
2005
|
-
for (const key of Object.keys(result)) {
|
|
2006
|
-
if (colorConversions[key] && typeof source.descriptor[key] === 'number') {
|
|
2007
|
-
result[key] = source.descriptor[key];
|
|
2008
|
-
}
|
|
2009
|
-
}
|
|
2010
|
-
}
|
|
2011
|
-
|
|
2012
|
-
return result;
|
|
2013
|
-
}
|
|
2014
|
-
|
|
2015
2011
|
export function parseAngle(x: DescriptorUnitsValue) {
|
|
2016
2012
|
if (x === undefined) return 0;
|
|
2017
2013
|
if (x.units !== 'Angle') throw new Error(`Invalid units: ${x.units}`);
|
|
@@ -2034,7 +2030,8 @@ export function parsePercentOrAngle(x: DescriptorUnitsValue | undefined) {
|
|
|
2034
2030
|
export function parseUnits({ units, value }: DescriptorUnitsValue): UnitsValue {
|
|
2035
2031
|
if (
|
|
2036
2032
|
units !== 'Pixels' && units !== 'Millimeters' && units !== 'Points' && units !== 'None' &&
|
|
2037
|
-
units !== 'Picas' && units !== 'Inches' && units !== 'Centimeters' && units !== 'Density'
|
|
2033
|
+
units !== 'Picas' && units !== 'Inches' && units !== 'Centimeters' && units !== 'Density' &&
|
|
2034
|
+
units !== 'Distance'
|
|
2038
2035
|
) {
|
|
2039
2036
|
throw new Error(`Invalid units: ${JSON.stringify({ units, value })}`);
|
|
2040
2037
|
}
|
|
@@ -2076,7 +2073,8 @@ export function unitsValue(x: UnitsValue | undefined, key: string): DescriptorUn
|
|
|
2076
2073
|
|
|
2077
2074
|
if (
|
|
2078
2075
|
units !== 'Pixels' && units !== 'Millimeters' && units !== 'Points' && units !== 'None' &&
|
|
2079
|
-
units !== 'Picas' && units !== 'Inches' && units !== 'Centimeters' && units !== 'Density'
|
|
2076
|
+
units !== 'Picas' && units !== 'Inches' && units !== 'Centimeters' && units !== 'Density' &&
|
|
2077
|
+
units !== 'Distance'
|
|
2080
2078
|
) {
|
|
2081
2079
|
throw new Error(`Invalid units in ${JSON.stringify(x)} (key: ${key})`);
|
|
2082
2080
|
}
|
|
@@ -2222,6 +2220,12 @@ export const gradientInterpolationMethodType = createEnum<InterpolationMethod>('
|
|
|
2222
2220
|
// TODO: stripes
|
|
2223
2221
|
});
|
|
2224
2222
|
|
|
2223
|
+
export const Clry = createEnum<'user' | 'foreground' | 'background'>('Clry', 'user', {
|
|
2224
|
+
user: 'UsrS',
|
|
2225
|
+
foreground: 'FrgC',
|
|
2226
|
+
background: 'BckC',
|
|
2227
|
+
});
|
|
2228
|
+
|
|
2225
2229
|
export const ClrS = createEnum<'rgb' | 'hsb' | 'lab' | 'hsl'>('ClrS', 'rgb', {
|
|
2226
2230
|
rgb: 'RGBC',
|
|
2227
2231
|
hsb: 'HSBl',
|