ag-psd 15.0.4 → 15.0.5
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/package.json +1 -1
- package/src/abr.ts +540 -0
- package/src/additionalInfo.ts +2688 -0
- package/src/csh.ts +44 -0
- package/src/descriptor.ts +1804 -0
- package/src/effectsHelpers.ts +305 -0
- package/src/engineData.ts +359 -0
- package/src/helpers.ts +387 -0
- package/src/imageResources.ts +1439 -0
- package/src/index.ts +44 -0
- package/src/initializeCanvas.ts +25 -0
- package/src/jpeg.ts +1166 -0
- package/src/psd.ts +1184 -0
- package/src/psdReader.ts +1091 -0
- package/src/psdWriter.ts +754 -0
- package/src/text.ts +751 -0
- package/src/utf8.ts +160 -0
|
@@ -0,0 +1,1804 @@
|
|
|
1
|
+
import { createEnum } from './helpers';
|
|
2
|
+
import {
|
|
3
|
+
AntiAlias, BevelDirection, BevelStyle, BevelTechnique, BlendMode, Color, EffectContour,
|
|
4
|
+
EffectNoiseGradient, EffectPattern, EffectSolidGradient, ExtraGradientInfo, ExtraPatternInfo,
|
|
5
|
+
GlowSource, GlowTechnique, GradientStyle, InterpolationMethod, LayerEffectBevel,
|
|
6
|
+
LayerEffectGradientOverlay, LayerEffectInnerGlow, LayerEffectPatternOverlay,
|
|
7
|
+
LayerEffectSatin, LayerEffectShadow, LayerEffectsInfo, LayerEffectSolidFill,
|
|
8
|
+
LayerEffectsOuterGlow, LayerEffectStroke, LineAlignment, LineCapType, LineJoinType,
|
|
9
|
+
Orientation, TextGridding, TimelineKey, TimelineKeyInterpolation, TimelineTrack, TimelineTrackType,
|
|
10
|
+
Units, UnitsValue, VectorContent, WarpStyle
|
|
11
|
+
} from './psd';
|
|
12
|
+
import {
|
|
13
|
+
PsdReader, readSignature, readUnicodeString, readUint32, readUint8, readFloat64,
|
|
14
|
+
readBytes, readAsciiString, readInt32, readFloat32, readInt32LE, readUnicodeStringWithLength
|
|
15
|
+
} from './psdReader';
|
|
16
|
+
import {
|
|
17
|
+
PsdWriter, writeSignature, writeBytes, writeUint32, writeFloat64, writeUint8,
|
|
18
|
+
writeUnicodeStringWithPadding, writeInt32, writeFloat32, writeUnicodeString
|
|
19
|
+
} from './psdWriter';
|
|
20
|
+
|
|
21
|
+
interface Dict { [key: string]: string; }
|
|
22
|
+
interface NameClassID { name: string; classID: string; }
|
|
23
|
+
interface ExtTypeDict { [key: string]: NameClassID; }
|
|
24
|
+
|
|
25
|
+
function revMap(map: Dict) {
|
|
26
|
+
const result: Dict = {};
|
|
27
|
+
Object.keys(map).forEach(key => result[map[key]] = key);
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const unitsMap: Dict = {
|
|
32
|
+
'#Ang': 'Angle',
|
|
33
|
+
'#Rsl': 'Density',
|
|
34
|
+
'#Rlt': 'Distance',
|
|
35
|
+
'#Nne': 'None',
|
|
36
|
+
'#Prc': 'Percent',
|
|
37
|
+
'#Pxl': 'Pixels',
|
|
38
|
+
'#Mlm': 'Millimeters',
|
|
39
|
+
'#Pnt': 'Points',
|
|
40
|
+
'RrPi': 'Picas',
|
|
41
|
+
'RrIn': 'Inches',
|
|
42
|
+
'RrCm': 'Centimeters',
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const unitsMapRev = revMap(unitsMap);
|
|
46
|
+
let logErrors = false;
|
|
47
|
+
|
|
48
|
+
export function setLogErrors(value: boolean) {
|
|
49
|
+
logErrors = value;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function makeType(name: string, classID: string) {
|
|
53
|
+
return { name, classID };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const nullType = makeType('', 'null');
|
|
57
|
+
|
|
58
|
+
const fieldToExtType: ExtTypeDict = {
|
|
59
|
+
strokeStyleContent: makeType('', 'solidColorLayer'),
|
|
60
|
+
// printProofSetup: makeType('校样设置', 'proofSetup'), // TESTING
|
|
61
|
+
printProofSetup: makeType('Proof Setup', 'proofSetup'),
|
|
62
|
+
patternFill: makeType('', 'patternFill'),
|
|
63
|
+
Grad: makeType('Gradient', 'Grdn'),
|
|
64
|
+
ebbl: makeType('', 'ebbl'),
|
|
65
|
+
SoFi: makeType('', 'SoFi'),
|
|
66
|
+
GrFl: makeType('', 'GrFl'),
|
|
67
|
+
sdwC: makeType('', 'RGBC'),
|
|
68
|
+
hglC: makeType('', 'RGBC'),
|
|
69
|
+
'Clr ': makeType('', 'RGBC'),
|
|
70
|
+
'tintColor': makeType('', 'RGBC'),
|
|
71
|
+
Ofst: makeType('', 'Pnt '),
|
|
72
|
+
ChFX: makeType('', 'ChFX'),
|
|
73
|
+
MpgS: makeType('', 'ShpC'),
|
|
74
|
+
DrSh: makeType('', 'DrSh'),
|
|
75
|
+
IrSh: makeType('', 'IrSh'),
|
|
76
|
+
OrGl: makeType('', 'OrGl'),
|
|
77
|
+
IrGl: makeType('', 'IrGl'),
|
|
78
|
+
TrnS: makeType('', 'ShpC'),
|
|
79
|
+
Ptrn: makeType('', 'Ptrn'),
|
|
80
|
+
FrFX: makeType('', 'FrFX'),
|
|
81
|
+
phase: makeType('', 'Pnt '),
|
|
82
|
+
frameStep: nullType,
|
|
83
|
+
duration: nullType,
|
|
84
|
+
workInTime: nullType,
|
|
85
|
+
workOutTime: nullType,
|
|
86
|
+
audioClipGroupList: nullType,
|
|
87
|
+
bounds: makeType('', 'Rctn'),
|
|
88
|
+
customEnvelopeWarp: makeType('', 'customEnvelopeWarp'),
|
|
89
|
+
warp: makeType('', 'warp'),
|
|
90
|
+
'Sz ': makeType('', 'Pnt '),
|
|
91
|
+
origin: makeType('', 'Pnt '),
|
|
92
|
+
autoExpandOffset: makeType('', 'Pnt '),
|
|
93
|
+
keyOriginShapeBBox: makeType('', 'unitRect'),
|
|
94
|
+
Vrsn: nullType,
|
|
95
|
+
psVersion: nullType,
|
|
96
|
+
docDefaultNewArtboardBackgroundColor: makeType('', 'RGBC'),
|
|
97
|
+
artboardRect: makeType('', 'classFloatRect'),
|
|
98
|
+
keyOriginRRectRadii: makeType('', 'radii'),
|
|
99
|
+
keyOriginBoxCorners: nullType,
|
|
100
|
+
rectangleCornerA: makeType('', 'Pnt '),
|
|
101
|
+
rectangleCornerB: makeType('', 'Pnt '),
|
|
102
|
+
rectangleCornerC: makeType('', 'Pnt '),
|
|
103
|
+
rectangleCornerD: makeType('', 'Pnt '),
|
|
104
|
+
compInfo: nullType,
|
|
105
|
+
Trnf: makeType('Transform', 'Trnf'),
|
|
106
|
+
quiltWarp: makeType('', 'quiltWarp'),
|
|
107
|
+
generatorSettings: nullType,
|
|
108
|
+
crema: nullType,
|
|
109
|
+
FrIn: nullType,
|
|
110
|
+
blendOptions: nullType,
|
|
111
|
+
FXRf: nullType,
|
|
112
|
+
Lefx: nullType,
|
|
113
|
+
time: nullType,
|
|
114
|
+
animKey: nullType,
|
|
115
|
+
timeScope: nullType,
|
|
116
|
+
inTime: nullType,
|
|
117
|
+
outTime: nullType,
|
|
118
|
+
sheetStyle: nullType,
|
|
119
|
+
translation: nullType,
|
|
120
|
+
Skew: nullType,
|
|
121
|
+
'Lnk ': makeType('', 'ExternalFileLink'),
|
|
122
|
+
frameReader: makeType('', 'FrameReader'),
|
|
123
|
+
effectParams: makeType('', 'motionTrackEffectParams'),
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const fieldToArrayExtType: ExtTypeDict = {
|
|
127
|
+
'Crv ': makeType('', 'CrPt'),
|
|
128
|
+
Clrs: makeType('', 'Clrt'),
|
|
129
|
+
Trns: makeType('', 'TrnS'),
|
|
130
|
+
keyDescriptorList: nullType,
|
|
131
|
+
solidFillMulti: makeType('', 'SoFi'),
|
|
132
|
+
gradientFillMulti: makeType('', 'GrFl'),
|
|
133
|
+
dropShadowMulti: makeType('', 'DrSh'),
|
|
134
|
+
innerShadowMulti: makeType('', 'IrSh'),
|
|
135
|
+
frameFXMulti: makeType('', 'FrFX'),
|
|
136
|
+
FrIn: nullType,
|
|
137
|
+
FSts: nullType,
|
|
138
|
+
LaSt: nullType,
|
|
139
|
+
sheetTimelineOptions: nullType,
|
|
140
|
+
trackList: makeType('', 'animationTrack'),
|
|
141
|
+
globalTrackList: makeType('', 'animationTrack'),
|
|
142
|
+
keyList: nullType,
|
|
143
|
+
audioClipGroupList: nullType,
|
|
144
|
+
audioClipList: nullType,
|
|
145
|
+
countObjectList: makeType('', 'countObject'),
|
|
146
|
+
countGroupList: makeType('', 'countGroup'),
|
|
147
|
+
slices: makeType('', 'slice'),
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const typeToField: { [key: string]: string[]; } = {
|
|
151
|
+
'TEXT': [
|
|
152
|
+
'Txt ', 'printerName', 'Nm ', 'Idnt', 'blackAndWhitePresetFileName', 'LUT3DFileName',
|
|
153
|
+
'presetFileName', 'curvesPresetFileName', 'mixerPresetFileName', 'placed', 'description', 'reason',
|
|
154
|
+
'artboardPresetName', 'json', 'clipID', 'relPath', 'fullPath', 'mediaDescriptor', 'Msge',
|
|
155
|
+
'altTag', 'url', 'cellText',
|
|
156
|
+
],
|
|
157
|
+
'tdta': ['EngineData', 'LUT3DFileData'],
|
|
158
|
+
'long': [
|
|
159
|
+
'TextIndex', 'RndS', 'Mdpn', 'Smth', 'Lctn', 'strokeStyleVersion', 'LaID', 'Vrsn', 'Cnt ',
|
|
160
|
+
'Brgh', 'Cntr', 'means', 'vibrance', 'Strt', 'bwPresetKind', 'presetKind', 'comp', 'compID', 'originalCompID',
|
|
161
|
+
'curvesPresetKind', 'mixerPresetKind', 'uOrder', 'vOrder', 'PgNm', 'totalPages', 'Crop',
|
|
162
|
+
'numerator', 'denominator', 'frameCount', 'Annt', 'keyOriginType', 'unitValueQuadVersion',
|
|
163
|
+
'keyOriginIndex', 'major', 'minor', 'fix', 'docDefaultNewArtboardBackgroundType', 'artboardBackgroundType',
|
|
164
|
+
'numModifyingFX', 'deformNumRows', 'deformNumCols', 'FrID', 'FrDl', 'FsID', 'LCnt', 'AFrm', 'AFSt',
|
|
165
|
+
'numBefore', 'numAfter', 'Spcn', 'minOpacity', 'maxOpacity', 'BlnM', 'sheetID', 'gblA', 'globalAltitude',
|
|
166
|
+
'descVersion', 'frameReaderType', 'LyrI', 'zoomOrigin', 'fontSize', 'Rds ', 'sliceID',
|
|
167
|
+
'topOutset', 'leftOutset', 'bottomOutset', 'rightOutset',
|
|
168
|
+
],
|
|
169
|
+
'enum': [
|
|
170
|
+
'textGridding', 'Ornt', 'warpStyle', 'warpRotate', 'Inte', 'Bltn', 'ClrS',
|
|
171
|
+
'sdwM', 'hglM', 'bvlT', 'bvlS', 'bvlD', 'Md ', 'glwS', 'GrdF', 'GlwT',
|
|
172
|
+
'strokeStyleLineCapType', 'strokeStyleLineJoinType', 'strokeStyleLineAlignment',
|
|
173
|
+
'strokeStyleBlendMode', 'PntT', 'Styl', 'lookupType', 'LUTFormat', 'dataOrder',
|
|
174
|
+
'tableOrder', 'enableCompCore', 'enableCompCoreGPU', 'compCoreSupport', 'compCoreGPUSupport', 'Engn',
|
|
175
|
+
'enableCompCoreThreads', 'gs99', 'FrDs', 'trackID', 'animInterpStyle', 'horzAlign',
|
|
176
|
+
'vertAlign', 'bgColorType',
|
|
177
|
+
],
|
|
178
|
+
'bool': [
|
|
179
|
+
'PstS', 'printSixteenBit', 'masterFXSwitch', 'enab', 'uglg', 'antialiasGloss',
|
|
180
|
+
'useShape', 'useTexture', 'uglg', 'antialiasGloss', 'useShape', 'Vsbl',
|
|
181
|
+
'useTexture', 'Algn', 'Rvrs', 'Dthr', 'Invr', 'VctC', 'ShTr', 'layerConceals',
|
|
182
|
+
'strokeEnabled', 'fillEnabled', 'strokeStyleScaleLock', 'strokeStyleStrokeAdjust',
|
|
183
|
+
'hardProof', 'MpBl', 'paperWhite', 'useLegacy', 'Auto', 'Lab ', 'useTint', 'keyShapeInvalidated',
|
|
184
|
+
'autoExpandEnabled', 'autoNestEnabled', 'autoPositionEnabled', 'shrinkwrapOnSaveEnabled',
|
|
185
|
+
'present', 'showInDialog', 'overprint', 'sheetDisclosed', 'lightsDisclosed', 'meshesDisclosed',
|
|
186
|
+
'materialsDisclosed', 'hasMotion', 'muted', 'Effc', 'selected', 'autoScope', 'fillCanvas',
|
|
187
|
+
'cellTextIsHTML',
|
|
188
|
+
],
|
|
189
|
+
'doub': [
|
|
190
|
+
'warpValue', 'warpPerspective', 'warpPerspectiveOther', 'Intr', 'Wdth', 'Hght',
|
|
191
|
+
'strokeStyleMiterLimit', 'strokeStyleResolution', 'layerTime', 'keyOriginResolution',
|
|
192
|
+
'xx', 'xy', 'yx', 'yy', 'tx', 'ty', 'FrGA', 'frameRate', 'audioLevel', 'rotation',
|
|
193
|
+
'X ', 'Y ',
|
|
194
|
+
],
|
|
195
|
+
'UntF': [
|
|
196
|
+
'Scl ', 'sdwO', 'hglO', 'lagl', 'Lald', 'srgR', 'blur', 'Sftn', 'Opct', 'Dstn', 'Angl',
|
|
197
|
+
'Ckmt', 'Nose', 'Inpr', 'ShdN', 'strokeStyleLineWidth', 'strokeStyleLineDashOffset',
|
|
198
|
+
'strokeStyleOpacity', 'H ', 'Top ', 'Left', 'Btom', 'Rght', 'Rslt',
|
|
199
|
+
'topRight', 'topLeft', 'bottomLeft', 'bottomRight',
|
|
200
|
+
],
|
|
201
|
+
'VlLs': [
|
|
202
|
+
'Crv ', 'Clrs', 'Mnm ', 'Mxm ', 'Trns', 'pathList', 'strokeStyleLineDashSet', 'FrLs', 'slices',
|
|
203
|
+
'LaSt', 'Trnf', 'nonAffineTransform', 'keyDescriptorList', 'guideIndeces', 'gradientFillMulti',
|
|
204
|
+
'solidFillMulti', 'frameFXMulti', 'innerShadowMulti', 'dropShadowMulti', 'FrIn', 'FSts', 'FsFr',
|
|
205
|
+
'sheetTimelineOptions', 'audioClipList', 'trackList', 'globalTrackList', 'keyList', 'audioClipList',
|
|
206
|
+
'warpValues',
|
|
207
|
+
],
|
|
208
|
+
'ObAr': ['meshPoints', 'quiltSliceX', 'quiltSliceY'],
|
|
209
|
+
'obj ': ['null'],
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const channels = [
|
|
213
|
+
'Rd ', 'Grn ', 'Bl ', 'Yllw', 'Ylw ', 'Cyn ', 'Mgnt', 'Blck', 'Gry ', 'Lmnc', 'A ', 'B ',
|
|
214
|
+
];
|
|
215
|
+
|
|
216
|
+
const fieldToArrayType: Dict = {
|
|
217
|
+
'Mnm ': 'long',
|
|
218
|
+
'Mxm ': 'long',
|
|
219
|
+
'FrLs': 'long',
|
|
220
|
+
'strokeStyleLineDashSet': 'UntF',
|
|
221
|
+
'Trnf': 'doub',
|
|
222
|
+
'nonAffineTransform': 'doub',
|
|
223
|
+
'keyDescriptorList': 'Objc',
|
|
224
|
+
'gradientFillMulti': 'Objc',
|
|
225
|
+
'solidFillMulti': 'Objc',
|
|
226
|
+
'frameFXMulti': 'Objc',
|
|
227
|
+
'innerShadowMulti': 'Objc',
|
|
228
|
+
'dropShadowMulti': 'Objc',
|
|
229
|
+
'LaSt': 'Objc',
|
|
230
|
+
'FrIn': 'Objc',
|
|
231
|
+
'FSts': 'Objc',
|
|
232
|
+
'FsFr': 'long',
|
|
233
|
+
'blendOptions': 'Objc',
|
|
234
|
+
'sheetTimelineOptions': 'Objc',
|
|
235
|
+
'keyList': 'Objc',
|
|
236
|
+
'warpValues': 'doub',
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const fieldToType: Dict = {};
|
|
240
|
+
|
|
241
|
+
for (const type of Object.keys(typeToField)) {
|
|
242
|
+
for (const field of typeToField[type]) {
|
|
243
|
+
fieldToType[field] = type;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
for (const field of Object.keys(fieldToExtType)) {
|
|
248
|
+
if (!fieldToType[field]) fieldToType[field] = 'Objc';
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
for (const field of Object.keys(fieldToArrayExtType)) {
|
|
252
|
+
fieldToArrayType[field] = 'Objc';
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function getTypeByKey(key: string, value: any, root: string, parent: any) {
|
|
256
|
+
if (key === 'null' && root === 'slices') {
|
|
257
|
+
return 'TEXT';
|
|
258
|
+
} else if (key === 'groupID') {
|
|
259
|
+
return root === 'slices' ? 'long' : 'TEXT';
|
|
260
|
+
} else if (key === 'Sz ') {
|
|
261
|
+
return ('Wdth' in value) ? 'Objc' : (('units' in value) ? 'UntF' : 'doub');
|
|
262
|
+
} else if (key === 'Type') {
|
|
263
|
+
return typeof value === 'string' ? 'enum' : 'long';
|
|
264
|
+
} else if (key === 'AntA') {
|
|
265
|
+
return typeof value === 'string' ? 'enum' : 'bool';
|
|
266
|
+
} else if ((key === 'Hrzn' || key === 'Vrtc') && parent.Type === 'keyType.Pstn') {
|
|
267
|
+
return 'long';
|
|
268
|
+
} else if (key === 'Hrzn' || key === 'Vrtc' || key === 'Top ' || key === 'Left' || key === 'Btom' || key === 'Rght') {
|
|
269
|
+
if (root === 'slices') return 'long';
|
|
270
|
+
return typeof value === 'number' ? 'doub' : 'UntF';
|
|
271
|
+
} else if (key === 'Vrsn') {
|
|
272
|
+
return typeof value === 'number' ? 'long' : 'Objc';
|
|
273
|
+
} else if (key === 'Rd ' || key === 'Grn ' || key === 'Bl ') {
|
|
274
|
+
return root === 'artd' ? 'long' : 'doub';
|
|
275
|
+
} else if (key === 'Trnf') {
|
|
276
|
+
return Array.isArray(value) ? 'VlLs' : 'Objc';
|
|
277
|
+
} else {
|
|
278
|
+
return fieldToType[key];
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export function readAsciiStringOrClassId(reader: PsdReader) {
|
|
283
|
+
const length = readInt32(reader);
|
|
284
|
+
return readAsciiString(reader, length || 4);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function writeAsciiStringOrClassId(writer: PsdWriter, value: string) {
|
|
288
|
+
if (value.length === 4 && value !== 'warp' && value !== 'time' && value !== 'hold') {
|
|
289
|
+
// write classId
|
|
290
|
+
writeInt32(writer, 0);
|
|
291
|
+
writeSignature(writer, value);
|
|
292
|
+
} else {
|
|
293
|
+
// write ascii string
|
|
294
|
+
writeInt32(writer, value.length);
|
|
295
|
+
|
|
296
|
+
for (let i = 0; i < value.length; i++) {
|
|
297
|
+
writeUint8(writer, value.charCodeAt(i));
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function readDescriptorStructure(reader: PsdReader) {
|
|
303
|
+
const object: any = {};
|
|
304
|
+
// object.__struct =
|
|
305
|
+
readClassStructure(reader);
|
|
306
|
+
const itemsCount = readUint32(reader);
|
|
307
|
+
// console.log('//', object.__struct);
|
|
308
|
+
for (let i = 0; i < itemsCount; i++) {
|
|
309
|
+
const key = readAsciiStringOrClassId(reader);
|
|
310
|
+
const type = readSignature(reader);
|
|
311
|
+
// console.log(`> '${key}' '${type}'`);
|
|
312
|
+
const data = readOSType(reader, type);
|
|
313
|
+
// if (!getTypeByKey(key, data)) console.log(`> '${key}' '${type}'`, data);
|
|
314
|
+
object[key] = data;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return object;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export function writeDescriptorStructure(writer: PsdWriter, name: string, classId: string, value: any, root: string) {
|
|
321
|
+
if (logErrors && !classId) console.log('Missing classId for: ', name, classId, value);
|
|
322
|
+
|
|
323
|
+
// write class structure
|
|
324
|
+
writeUnicodeStringWithPadding(writer, name);
|
|
325
|
+
writeAsciiStringOrClassId(writer, classId);
|
|
326
|
+
|
|
327
|
+
const keys = Object.keys(value);
|
|
328
|
+
writeUint32(writer, keys.length);
|
|
329
|
+
|
|
330
|
+
for (const key of keys) {
|
|
331
|
+
let type = getTypeByKey(key, value[key], root, value);
|
|
332
|
+
let extType = fieldToExtType[key];
|
|
333
|
+
|
|
334
|
+
if (key === 'origin') {
|
|
335
|
+
type = root === 'slices' ? 'enum' : 'Objc';
|
|
336
|
+
} else if (key === 'bounds' && root === 'slices') {
|
|
337
|
+
type = 'Objc';
|
|
338
|
+
extType = makeType('', 'Rct1');
|
|
339
|
+
} else if (key === 'Scl ' && 'Hrzn' in value[key]) {
|
|
340
|
+
type = 'Objc';
|
|
341
|
+
extType = nullType;
|
|
342
|
+
} else if (key === 'audioClipGroupList' && keys.length === 1) {
|
|
343
|
+
type = 'VlLs';
|
|
344
|
+
} else if ((key === 'Strt' || key === 'Brgh') && 'H ' in value) {
|
|
345
|
+
type = 'doub';
|
|
346
|
+
} else if (key === 'Strt') {
|
|
347
|
+
type = 'Objc';
|
|
348
|
+
extType = nullType;
|
|
349
|
+
} else if (channels.indexOf(key) !== -1) {
|
|
350
|
+
type = (classId === 'RGBC' && root !== 'artd') ? 'doub' : 'long';
|
|
351
|
+
} else if (key === 'profile') {
|
|
352
|
+
type = classId === 'printOutput' ? 'TEXT' : 'tdta';
|
|
353
|
+
} else if (key === 'strokeStyleContent') {
|
|
354
|
+
if (value[key]['Clr ']) {
|
|
355
|
+
extType = makeType('', 'solidColorLayer');
|
|
356
|
+
} else if (value[key].Grad) {
|
|
357
|
+
extType = makeType('', 'gradientLayer');
|
|
358
|
+
} else if (value[key].Ptrn) {
|
|
359
|
+
extType = makeType('', 'patternLayer');
|
|
360
|
+
} else {
|
|
361
|
+
logErrors && console.log('Invalid strokeStyleContent value', value[key]);
|
|
362
|
+
}
|
|
363
|
+
} else if (key === 'bounds' && root === 'quiltWarp') {
|
|
364
|
+
extType = makeType('', 'classFloatRect');
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (extType && extType.classID === 'RGBC') {
|
|
368
|
+
if ('H ' in value[key]) extType = { classID: 'HSBC', name: '' };
|
|
369
|
+
// TODO: other color spaces
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
writeAsciiStringOrClassId(writer, key);
|
|
373
|
+
writeSignature(writer, type || 'long');
|
|
374
|
+
writeOSType(writer, type || 'long', value[key], key, extType, root);
|
|
375
|
+
if (logErrors && !type) console.log(`Missing descriptor field type for: '${key}' in`, value);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function readOSType(reader: PsdReader, type: string) {
|
|
380
|
+
switch (type) {
|
|
381
|
+
case 'obj ': // Reference
|
|
382
|
+
return readReferenceStructure(reader);
|
|
383
|
+
case 'Objc': // Descriptor
|
|
384
|
+
case 'GlbO': // GlobalObject same as Descriptor
|
|
385
|
+
return readDescriptorStructure(reader);
|
|
386
|
+
case 'VlLs': { // List
|
|
387
|
+
const length = readInt32(reader);
|
|
388
|
+
const items: any[] = [];
|
|
389
|
+
|
|
390
|
+
for (let i = 0; i < length; i++) {
|
|
391
|
+
const type = readSignature(reader);
|
|
392
|
+
// console.log(' >', type);
|
|
393
|
+
items.push(readOSType(reader, type));
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
return items;
|
|
397
|
+
}
|
|
398
|
+
case 'doub': // Double
|
|
399
|
+
return readFloat64(reader);
|
|
400
|
+
case 'UntF': { // Unit double
|
|
401
|
+
const units = readSignature(reader);
|
|
402
|
+
const value = readFloat64(reader);
|
|
403
|
+
if (!unitsMap[units]) throw new Error(`Invalid units: ${units}`);
|
|
404
|
+
return { units: unitsMap[units], value };
|
|
405
|
+
}
|
|
406
|
+
case 'UnFl': { // Unit float
|
|
407
|
+
const units = readSignature(reader);
|
|
408
|
+
const value = readFloat32(reader);
|
|
409
|
+
if (!unitsMap[units]) throw new Error(`Invalid units: ${units}`);
|
|
410
|
+
return { units: unitsMap[units], value };
|
|
411
|
+
}
|
|
412
|
+
case 'TEXT': // String
|
|
413
|
+
return readUnicodeString(reader);
|
|
414
|
+
case 'enum': { // Enumerated
|
|
415
|
+
const type = readAsciiStringOrClassId(reader);
|
|
416
|
+
const value = readAsciiStringOrClassId(reader);
|
|
417
|
+
return `${type}.${value}`;
|
|
418
|
+
}
|
|
419
|
+
case 'long': // Integer
|
|
420
|
+
return readInt32(reader);
|
|
421
|
+
case 'comp': { // Large Integer
|
|
422
|
+
const low = readUint32(reader);
|
|
423
|
+
const high = readUint32(reader);
|
|
424
|
+
return { low, high };
|
|
425
|
+
}
|
|
426
|
+
case 'bool': // Boolean
|
|
427
|
+
return !!readUint8(reader);
|
|
428
|
+
case 'type': // Class
|
|
429
|
+
case 'GlbC': // Class
|
|
430
|
+
return readClassStructure(reader);
|
|
431
|
+
case 'alis': { // Alias
|
|
432
|
+
const length = readInt32(reader);
|
|
433
|
+
return readAsciiString(reader, length);
|
|
434
|
+
}
|
|
435
|
+
case 'tdta': { // Raw Data
|
|
436
|
+
const length = readInt32(reader);
|
|
437
|
+
return readBytes(reader, length);
|
|
438
|
+
}
|
|
439
|
+
case 'ObAr': { // Object array
|
|
440
|
+
readInt32(reader); // version: 16
|
|
441
|
+
readUnicodeString(reader); // name: ''
|
|
442
|
+
readAsciiStringOrClassId(reader); // 'rationalPoint'
|
|
443
|
+
const length = readInt32(reader);
|
|
444
|
+
const items: any[] = [];
|
|
445
|
+
|
|
446
|
+
for (let i = 0; i < length; i++) {
|
|
447
|
+
const type1 = readAsciiStringOrClassId(reader); // type Hrzn | Vrtc
|
|
448
|
+
readSignature(reader); // UnFl
|
|
449
|
+
|
|
450
|
+
readSignature(reader); // units ? '#Pxl'
|
|
451
|
+
const valuesCount = readInt32(reader);
|
|
452
|
+
const values: number[] = [];
|
|
453
|
+
for (let j = 0; j < valuesCount; j++) {
|
|
454
|
+
values.push(readFloat64(reader));
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
items.push({ type: type1, values });
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
return items;
|
|
461
|
+
}
|
|
462
|
+
case 'Pth ': { // File path
|
|
463
|
+
/*const length =*/ readInt32(reader);
|
|
464
|
+
const sig = readSignature(reader);
|
|
465
|
+
/*const pathSize =*/ readInt32LE(reader);
|
|
466
|
+
const charsCount = readInt32LE(reader);
|
|
467
|
+
const path = readUnicodeStringWithLength(reader, charsCount);
|
|
468
|
+
return { sig, path };
|
|
469
|
+
}
|
|
470
|
+
default:
|
|
471
|
+
throw new Error(`Invalid TySh descriptor OSType: ${type} at ${reader.offset.toString(16)}`);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
const ObArTypes: { [key: string]: string | undefined; } = {
|
|
476
|
+
meshPoints: 'rationalPoint',
|
|
477
|
+
quiltSliceX: 'UntF',
|
|
478
|
+
quiltSliceY: 'UntF',
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
function writeOSType(writer: PsdWriter, type: string, value: any, key: string, extType: NameClassID | undefined, root: string) {
|
|
482
|
+
switch (type) {
|
|
483
|
+
case 'obj ': // Reference
|
|
484
|
+
writeReferenceStructure(writer, key, value);
|
|
485
|
+
break;
|
|
486
|
+
case 'Objc': // Descriptor
|
|
487
|
+
case 'GlbO': // GlobalObject same as Descriptor
|
|
488
|
+
if (!extType) throw new Error(`Missing ext type for: '${key}' (${JSON.stringify(value)})`);
|
|
489
|
+
writeDescriptorStructure(writer, extType.name, extType.classID, value, root);
|
|
490
|
+
break;
|
|
491
|
+
case 'VlLs': // List
|
|
492
|
+
writeInt32(writer, value.length);
|
|
493
|
+
|
|
494
|
+
for (let i = 0; i < value.length; i++) {
|
|
495
|
+
const type = fieldToArrayType[key];
|
|
496
|
+
writeSignature(writer, type || 'long');
|
|
497
|
+
writeOSType(writer, type || 'long', value[i], '', fieldToArrayExtType[key], root);
|
|
498
|
+
if (logErrors && !type) console.log(`Missing descriptor array type for: '${key}' in`, value);
|
|
499
|
+
}
|
|
500
|
+
break;
|
|
501
|
+
case 'doub': // Double
|
|
502
|
+
writeFloat64(writer, value);
|
|
503
|
+
break;
|
|
504
|
+
case 'UntF': // Unit double
|
|
505
|
+
if (!unitsMapRev[value.units]) throw new Error(`Invalid units: ${value.units} in ${key}`);
|
|
506
|
+
writeSignature(writer, unitsMapRev[value.units]);
|
|
507
|
+
writeFloat64(writer, value.value);
|
|
508
|
+
break;
|
|
509
|
+
case 'UnFl': // Unit float
|
|
510
|
+
if (!unitsMapRev[value.units]) throw new Error(`Invalid units: ${value.units} in ${key}`);
|
|
511
|
+
writeSignature(writer, unitsMapRev[value.units]);
|
|
512
|
+
writeFloat32(writer, value.value);
|
|
513
|
+
break;
|
|
514
|
+
case 'TEXT': // String
|
|
515
|
+
writeUnicodeStringWithPadding(writer, value);
|
|
516
|
+
break;
|
|
517
|
+
case 'enum': { // Enumerated
|
|
518
|
+
const [_type, val] = value.split('.');
|
|
519
|
+
writeAsciiStringOrClassId(writer, _type);
|
|
520
|
+
writeAsciiStringOrClassId(writer, val);
|
|
521
|
+
break;
|
|
522
|
+
}
|
|
523
|
+
case 'long': // Integer
|
|
524
|
+
writeInt32(writer, value);
|
|
525
|
+
break;
|
|
526
|
+
// case 'comp': // Large Integer
|
|
527
|
+
// writeLargeInteger(reader);
|
|
528
|
+
case 'bool': // Boolean
|
|
529
|
+
writeUint8(writer, value ? 1 : 0);
|
|
530
|
+
break;
|
|
531
|
+
// case 'type': // Class
|
|
532
|
+
// case 'GlbC': // Class
|
|
533
|
+
// writeClassStructure(reader);
|
|
534
|
+
// case 'alis': // Alias
|
|
535
|
+
// writeAliasStructure(reader);
|
|
536
|
+
case 'tdta': // Raw Data
|
|
537
|
+
writeInt32(writer, value.byteLength);
|
|
538
|
+
writeBytes(writer, value);
|
|
539
|
+
break;
|
|
540
|
+
case 'ObAr': { // Object array
|
|
541
|
+
writeInt32(writer, 16); // version
|
|
542
|
+
writeUnicodeStringWithPadding(writer, ''); // name
|
|
543
|
+
const type = ObArTypes[key];
|
|
544
|
+
if (!type) throw new Error(`Not implemented ObArType for: ${key}`);
|
|
545
|
+
writeAsciiStringOrClassId(writer, type);
|
|
546
|
+
writeInt32(writer, value.length);
|
|
547
|
+
|
|
548
|
+
for (let i = 0; i < value.length; i++) {
|
|
549
|
+
writeAsciiStringOrClassId(writer, value[i].type); // Hrzn | Vrtc
|
|
550
|
+
writeSignature(writer, 'UnFl');
|
|
551
|
+
writeSignature(writer, '#Pxl');
|
|
552
|
+
writeInt32(writer, value[i].values.length);
|
|
553
|
+
|
|
554
|
+
for (let j = 0; j < value[i].values.length; j++) {
|
|
555
|
+
writeFloat64(writer, value[i].values[j]);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
break;
|
|
559
|
+
}
|
|
560
|
+
// case 'Pth ': // File path
|
|
561
|
+
// writeFilePath(reader);
|
|
562
|
+
default:
|
|
563
|
+
throw new Error(`Not implemented descriptor OSType: ${type}`);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
function readReferenceStructure(reader: PsdReader) {
|
|
568
|
+
const itemsCount = readInt32(reader);
|
|
569
|
+
const items: any[] = [];
|
|
570
|
+
|
|
571
|
+
for (let i = 0; i < itemsCount; i++) {
|
|
572
|
+
const type = readSignature(reader);
|
|
573
|
+
|
|
574
|
+
switch (type) {
|
|
575
|
+
case 'prop': { // Property
|
|
576
|
+
readClassStructure(reader);
|
|
577
|
+
const keyID = readAsciiStringOrClassId(reader);
|
|
578
|
+
items.push(keyID);
|
|
579
|
+
break;
|
|
580
|
+
}
|
|
581
|
+
case 'Clss': // Class
|
|
582
|
+
items.push(readClassStructure(reader));
|
|
583
|
+
break;
|
|
584
|
+
case 'Enmr': { // Enumerated Reference
|
|
585
|
+
readClassStructure(reader);
|
|
586
|
+
const typeID = readAsciiStringOrClassId(reader);
|
|
587
|
+
const value = readAsciiStringOrClassId(reader);
|
|
588
|
+
items.push(`${typeID}.${value}`);
|
|
589
|
+
break;
|
|
590
|
+
}
|
|
591
|
+
case 'rele': { // Offset
|
|
592
|
+
// const { name, classID } =
|
|
593
|
+
readClassStructure(reader);
|
|
594
|
+
items.push(readUint32(reader));
|
|
595
|
+
break;
|
|
596
|
+
}
|
|
597
|
+
case 'Idnt': // Identifier
|
|
598
|
+
items.push(readInt32(reader));
|
|
599
|
+
break;
|
|
600
|
+
case 'indx': // Index
|
|
601
|
+
items.push(readInt32(reader));
|
|
602
|
+
break;
|
|
603
|
+
case 'name': { // Name
|
|
604
|
+
readClassStructure(reader);
|
|
605
|
+
items.push(readUnicodeString(reader));
|
|
606
|
+
break;
|
|
607
|
+
}
|
|
608
|
+
default:
|
|
609
|
+
throw new Error(`Invalid descriptor reference type: ${type}`);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
return items;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
function writeReferenceStructure(writer: PsdWriter, _key: string, items: any[]) {
|
|
617
|
+
writeInt32(writer, items.length);
|
|
618
|
+
|
|
619
|
+
for (let i = 0; i < items.length; i++) {
|
|
620
|
+
const value = items[i];
|
|
621
|
+
let type = 'unknown';
|
|
622
|
+
|
|
623
|
+
if (typeof value === 'string') {
|
|
624
|
+
if (/^[a-z]+\.[a-z]+$/i.test(value)) {
|
|
625
|
+
type = 'Enmr';
|
|
626
|
+
} else {
|
|
627
|
+
type = 'name';
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
writeSignature(writer, type);
|
|
632
|
+
|
|
633
|
+
switch (type) {
|
|
634
|
+
// case 'prop': // Property
|
|
635
|
+
// case 'Clss': // Class
|
|
636
|
+
case 'Enmr': { // Enumerated Reference
|
|
637
|
+
const [typeID, enumValue] = value.split('.');
|
|
638
|
+
writeClassStructure(writer, '\0', typeID);
|
|
639
|
+
writeAsciiStringOrClassId(writer, typeID);
|
|
640
|
+
writeAsciiStringOrClassId(writer, enumValue);
|
|
641
|
+
break;
|
|
642
|
+
}
|
|
643
|
+
// case 'rele': // Offset
|
|
644
|
+
// case 'Idnt': // Identifier
|
|
645
|
+
// case 'indx': // Index
|
|
646
|
+
case 'name': { // Name
|
|
647
|
+
writeClassStructure(writer, '\0', 'Lyr ');
|
|
648
|
+
writeUnicodeString(writer, value + '\0');
|
|
649
|
+
break;
|
|
650
|
+
}
|
|
651
|
+
default:
|
|
652
|
+
throw new Error(`Invalid descriptor reference type: ${type}`);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
return items;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
function readClassStructure(reader: PsdReader) {
|
|
660
|
+
const name = readUnicodeString(reader);
|
|
661
|
+
const classID = readAsciiStringOrClassId(reader);
|
|
662
|
+
// console.log({ name, classID });
|
|
663
|
+
return { name, classID };
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
function writeClassStructure(writer: PsdWriter, name: string, classID: string) {
|
|
667
|
+
writeUnicodeString(writer, name);
|
|
668
|
+
writeAsciiStringOrClassId(writer, classID);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
export function readVersionAndDescriptor(reader: PsdReader) {
|
|
672
|
+
const version = readUint32(reader);
|
|
673
|
+
if (version !== 16) throw new Error(`Invalid descriptor version: ${version}`);
|
|
674
|
+
const desc = readDescriptorStructure(reader);
|
|
675
|
+
// console.log(require('util').inspect(desc, false, 99, true));
|
|
676
|
+
return desc;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
export function writeVersionAndDescriptor(writer: PsdWriter, name: string, classID: string, descriptor: any, root = '') {
|
|
680
|
+
writeUint32(writer, 16); // version
|
|
681
|
+
writeDescriptorStructure(writer, name, classID, descriptor, root);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
export type DescriptorUnits = 'Angle' | 'Density' | 'Distance' | 'None' | 'Percent' | 'Pixels' |
|
|
685
|
+
'Millimeters' | 'Points' | 'Picas' | 'Inches' | 'Centimeters';
|
|
686
|
+
|
|
687
|
+
export interface DescriptorUnitsValue {
|
|
688
|
+
units: DescriptorUnits;
|
|
689
|
+
value: number;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
export type DescriptorColor = {
|
|
693
|
+
'Rd ': number;
|
|
694
|
+
'Grn ': number;
|
|
695
|
+
'Bl ': number;
|
|
696
|
+
} | {
|
|
697
|
+
'H ': DescriptorUnitsValue;
|
|
698
|
+
Strt: number;
|
|
699
|
+
Brgh: number;
|
|
700
|
+
} | {
|
|
701
|
+
'Cyn ': number;
|
|
702
|
+
Mgnt: number;
|
|
703
|
+
'Ylw ': number;
|
|
704
|
+
Blck: number;
|
|
705
|
+
} | {
|
|
706
|
+
'Gry ': number;
|
|
707
|
+
} | {
|
|
708
|
+
Lmnc: number;
|
|
709
|
+
'A ': number;
|
|
710
|
+
'B ': number;
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
export interface DesciptorPattern {
|
|
714
|
+
'Nm ': string;
|
|
715
|
+
Idnt: string;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
export type DesciptorGradient = {
|
|
719
|
+
'Nm ': string;
|
|
720
|
+
GrdF: 'GrdF.CstS';
|
|
721
|
+
Intr: number;
|
|
722
|
+
Clrs: {
|
|
723
|
+
'Clr ': DescriptorColor;
|
|
724
|
+
Type: 'Clry.UsrS';
|
|
725
|
+
Lctn: number;
|
|
726
|
+
Mdpn: number;
|
|
727
|
+
}[];
|
|
728
|
+
Trns: {
|
|
729
|
+
Opct: DescriptorUnitsValue;
|
|
730
|
+
Lctn: number;
|
|
731
|
+
Mdpn: number;
|
|
732
|
+
}[];
|
|
733
|
+
} | {
|
|
734
|
+
GrdF: 'GrdF.ClNs';
|
|
735
|
+
Smth: number;
|
|
736
|
+
'Nm ': string;
|
|
737
|
+
ClrS: string;
|
|
738
|
+
RndS: number;
|
|
739
|
+
VctC?: boolean;
|
|
740
|
+
ShTr?: boolean;
|
|
741
|
+
'Mnm ': number[];
|
|
742
|
+
'Mxm ': number[];
|
|
743
|
+
};
|
|
744
|
+
|
|
745
|
+
export interface DescriptorColorContent {
|
|
746
|
+
'Clr ': DescriptorColor;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
export interface DescriptorGradientContent {
|
|
750
|
+
Grad: DesciptorGradient;
|
|
751
|
+
Type: string;
|
|
752
|
+
Dthr?: boolean;
|
|
753
|
+
Rvrs?: boolean;
|
|
754
|
+
Angl?: DescriptorUnitsValue;
|
|
755
|
+
'Scl '?: DescriptorUnitsValue;
|
|
756
|
+
Algn?: boolean;
|
|
757
|
+
Ofst?: { Hrzn: DescriptorUnitsValue; Vrtc: DescriptorUnitsValue; };
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
export interface DescriptorPatternContent {
|
|
761
|
+
Ptrn: DesciptorPattern;
|
|
762
|
+
Lnkd?: boolean;
|
|
763
|
+
phase?: { Hrzn: number; Vrtc: number; };
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
export type DescriptorVectorContent = DescriptorColorContent | DescriptorGradientContent | DescriptorPatternContent;
|
|
767
|
+
|
|
768
|
+
export interface StrokeDescriptor {
|
|
769
|
+
strokeStyleVersion: number;
|
|
770
|
+
strokeEnabled: boolean;
|
|
771
|
+
fillEnabled: boolean;
|
|
772
|
+
strokeStyleLineWidth: DescriptorUnitsValue;
|
|
773
|
+
strokeStyleLineDashOffset: DescriptorUnitsValue;
|
|
774
|
+
strokeStyleMiterLimit: number;
|
|
775
|
+
strokeStyleLineCapType: string;
|
|
776
|
+
strokeStyleLineJoinType: string;
|
|
777
|
+
strokeStyleLineAlignment: string;
|
|
778
|
+
strokeStyleScaleLock: boolean;
|
|
779
|
+
strokeStyleStrokeAdjust: boolean;
|
|
780
|
+
strokeStyleLineDashSet: DescriptorUnitsValue[];
|
|
781
|
+
strokeStyleBlendMode: string;
|
|
782
|
+
strokeStyleOpacity: DescriptorUnitsValue;
|
|
783
|
+
strokeStyleContent: DescriptorVectorContent;
|
|
784
|
+
strokeStyleResolution: number;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
export interface TextDescriptor {
|
|
788
|
+
'Txt ': string;
|
|
789
|
+
textGridding: string;
|
|
790
|
+
Ornt: string;
|
|
791
|
+
AntA: string;
|
|
792
|
+
TextIndex: number;
|
|
793
|
+
EngineData?: Uint8Array;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
export interface WarpDescriptor {
|
|
797
|
+
warpStyle: string;
|
|
798
|
+
warpValue?: number;
|
|
799
|
+
warpValues?: number[]
|
|
800
|
+
warpPerspective: number;
|
|
801
|
+
warpPerspectiveOther: number;
|
|
802
|
+
warpRotate: string;
|
|
803
|
+
bounds?: {
|
|
804
|
+
'Top ': DescriptorUnitsValue;
|
|
805
|
+
Left: DescriptorUnitsValue;
|
|
806
|
+
Btom: DescriptorUnitsValue;
|
|
807
|
+
Rght: DescriptorUnitsValue;
|
|
808
|
+
};
|
|
809
|
+
uOrder: number;
|
|
810
|
+
vOrder: number;
|
|
811
|
+
customEnvelopeWarp?: {
|
|
812
|
+
meshPoints: {
|
|
813
|
+
type: 'Hrzn' | 'Vrtc';
|
|
814
|
+
values: number[];
|
|
815
|
+
}[];
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
export interface QuiltWarpDescriptor extends WarpDescriptor {
|
|
820
|
+
deformNumRows: number;
|
|
821
|
+
deformNumCols: number;
|
|
822
|
+
customEnvelopeWarp: {
|
|
823
|
+
quiltSliceX: {
|
|
824
|
+
type: 'quiltSliceX';
|
|
825
|
+
values: number[];
|
|
826
|
+
}[];
|
|
827
|
+
quiltSliceY: {
|
|
828
|
+
type: 'quiltSliceY';
|
|
829
|
+
values: number[];
|
|
830
|
+
}[];
|
|
831
|
+
meshPoints: {
|
|
832
|
+
type: 'Hrzn' | 'Vrtc';
|
|
833
|
+
values: number[];
|
|
834
|
+
}[];
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
export interface FractionDescriptor {
|
|
839
|
+
numerator: number;
|
|
840
|
+
denominator: number;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
export interface HrznVrtcDescriptor {
|
|
844
|
+
Hrzn: number;
|
|
845
|
+
Vrtc: number;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
export interface FrameDescriptor {
|
|
849
|
+
FrLs: number[];
|
|
850
|
+
enab?: boolean;
|
|
851
|
+
IMsk?: { Ofst: HrznVrtcDescriptor };
|
|
852
|
+
VMsk?: { Ofst: HrznVrtcDescriptor };
|
|
853
|
+
Ofst?: HrznVrtcDescriptor;
|
|
854
|
+
FXRf?: HrznVrtcDescriptor;
|
|
855
|
+
Lefx?: Lfx2Descriptor;
|
|
856
|
+
blendOptions?: { Opct: DescriptorUnitsValue; };
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
export interface FrameListDescriptor {
|
|
860
|
+
LaID: number; // layer ID
|
|
861
|
+
LaSt: FrameDescriptor[];
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
export function horzVrtcToXY(hv: HrznVrtcDescriptor): { x: number; y: number; } {
|
|
865
|
+
return { x: hv.Hrzn, y: hv.Vrtc };
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
export function xyToHorzVrtc(xy: { x: number; y: number; }): HrznVrtcDescriptor {
|
|
869
|
+
return { Hrzn: xy.x, Vrtc: xy.y };
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
export type TimelineAnimKeyDescriptor = {
|
|
873
|
+
Type: 'keyType.Opct';
|
|
874
|
+
Opct: DescriptorUnitsValue;
|
|
875
|
+
} | {
|
|
876
|
+
Type: 'keyType.Trnf';
|
|
877
|
+
'Scl ': HrznVrtcDescriptor;
|
|
878
|
+
Skew: HrznVrtcDescriptor;
|
|
879
|
+
rotation: number;
|
|
880
|
+
translation: HrznVrtcDescriptor;
|
|
881
|
+
} | {
|
|
882
|
+
Type: 'keyType.Pstn';
|
|
883
|
+
Hrzn: number;
|
|
884
|
+
Vrtc: number;
|
|
885
|
+
} | {
|
|
886
|
+
Type: 'keyType.sheetStyle';
|
|
887
|
+
sheetStyle: {
|
|
888
|
+
Vrsn: number;
|
|
889
|
+
Lefx?: Lfx2Descriptor;
|
|
890
|
+
blendOptions: {};
|
|
891
|
+
};
|
|
892
|
+
} | {
|
|
893
|
+
Type: 'keyType.globalLighting';
|
|
894
|
+
gblA: number;
|
|
895
|
+
globalAltitude: number;
|
|
896
|
+
};
|
|
897
|
+
|
|
898
|
+
export interface TimelineKeyDescriptor {
|
|
899
|
+
Vrsn: 1;
|
|
900
|
+
animInterpStyle: 'animInterpStyle.Lnr ' | 'animInterpStyle.hold';
|
|
901
|
+
time: FractionDescriptor;
|
|
902
|
+
animKey: TimelineAnimKeyDescriptor;
|
|
903
|
+
selected: boolean;
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
export interface TimelineTrackDescriptor {
|
|
907
|
+
trackID: 'stdTrackID.globalLightingTrack' | 'stdTrackID.opacityTrack' | 'stdTrackID.styleTrack' | 'stdTrackID.sheetTransformTrack' | 'stdTrackID.sheetPositionTrack';
|
|
908
|
+
Vrsn: 1;
|
|
909
|
+
enab: boolean;
|
|
910
|
+
Effc: boolean;
|
|
911
|
+
effectParams?: {
|
|
912
|
+
keyList: TimelineKeyDescriptor[];
|
|
913
|
+
fillCanvas: boolean;
|
|
914
|
+
zoomOrigin: number;
|
|
915
|
+
};
|
|
916
|
+
keyList: TimelineKeyDescriptor[];
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
export interface TimeScopeDescriptor {
|
|
920
|
+
Vrsn: 1;
|
|
921
|
+
Strt: FractionDescriptor;
|
|
922
|
+
duration: FractionDescriptor;
|
|
923
|
+
inTime: FractionDescriptor;
|
|
924
|
+
outTime: FractionDescriptor;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
export interface TimelineDescriptor {
|
|
928
|
+
Vrsn: 1;
|
|
929
|
+
timeScope: TimeScopeDescriptor;
|
|
930
|
+
autoScope: boolean;
|
|
931
|
+
audioLevel: number;
|
|
932
|
+
LyrI: number;
|
|
933
|
+
trackList?: TimelineTrackDescriptor[];
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
export interface EffectDescriptor extends Partial<DescriptorGradientContent>, Partial<DescriptorPatternContent> {
|
|
937
|
+
enab?: boolean;
|
|
938
|
+
Styl: string;
|
|
939
|
+
PntT?: string;
|
|
940
|
+
'Md '?: string;
|
|
941
|
+
Opct?: DescriptorUnitsValue;
|
|
942
|
+
'Sz '?: DescriptorUnitsValue;
|
|
943
|
+
'Clr '?: DescriptorColor;
|
|
944
|
+
present?: boolean;
|
|
945
|
+
showInDialog?: boolean;
|
|
946
|
+
overprint?: boolean;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
export interface Lfx2Descriptor {
|
|
950
|
+
'Scl '?: DescriptorUnitsValue;
|
|
951
|
+
masterFXSwitch?: boolean;
|
|
952
|
+
DrSh?: EffectDescriptor;
|
|
953
|
+
IrSh?: EffectDescriptor;
|
|
954
|
+
OrGl?: EffectDescriptor;
|
|
955
|
+
IrGl?: EffectDescriptor;
|
|
956
|
+
ebbl?: EffectDescriptor;
|
|
957
|
+
SoFi?: EffectDescriptor;
|
|
958
|
+
patternFill?: EffectDescriptor;
|
|
959
|
+
GrFl?: EffectDescriptor;
|
|
960
|
+
ChFX?: EffectDescriptor;
|
|
961
|
+
FrFX?: EffectDescriptor;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
export interface LmfxDescriptor {
|
|
965
|
+
'Scl '?: DescriptorUnitsValue;
|
|
966
|
+
masterFXSwitch?: boolean;
|
|
967
|
+
numModifyingFX?: number;
|
|
968
|
+
OrGl?: EffectDescriptor;
|
|
969
|
+
IrGl?: EffectDescriptor;
|
|
970
|
+
ebbl?: EffectDescriptor;
|
|
971
|
+
ChFX?: EffectDescriptor;
|
|
972
|
+
dropShadowMulti?: EffectDescriptor[];
|
|
973
|
+
innerShadowMulti?: EffectDescriptor[];
|
|
974
|
+
solidFillMulti?: EffectDescriptor[];
|
|
975
|
+
gradientFillMulti?: EffectDescriptor[];
|
|
976
|
+
frameFXMulti?: EffectDescriptor[];
|
|
977
|
+
patternFill?: EffectDescriptor; // ???
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
function parseFxObject(fx: EffectDescriptor) {
|
|
981
|
+
const stroke: LayerEffectStroke = {
|
|
982
|
+
enabled: !!fx.enab,
|
|
983
|
+
position: FStl.decode(fx.Styl),
|
|
984
|
+
fillType: FrFl.decode(fx.PntT!),
|
|
985
|
+
blendMode: BlnM.decode(fx['Md ']!),
|
|
986
|
+
opacity: parsePercent(fx.Opct),
|
|
987
|
+
size: parseUnits(fx['Sz ']!),
|
|
988
|
+
};
|
|
989
|
+
|
|
990
|
+
if (fx.present !== undefined) stroke.present = fx.present;
|
|
991
|
+
if (fx.showInDialog !== undefined) stroke.showInDialog = fx.showInDialog;
|
|
992
|
+
if (fx.overprint !== undefined) stroke.overprint = fx.overprint;
|
|
993
|
+
if (fx['Clr ']) stroke.color = parseColor(fx['Clr ']);
|
|
994
|
+
if (fx.Grad) stroke.gradient = parseGradientContent(fx as any);
|
|
995
|
+
if (fx.Ptrn) stroke.pattern = parsePatternContent(fx as any);
|
|
996
|
+
|
|
997
|
+
return stroke;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
function serializeFxObject(stroke: LayerEffectStroke) {
|
|
1001
|
+
let FrFX: EffectDescriptor = {} as any;
|
|
1002
|
+
FrFX.enab = !!stroke.enabled;
|
|
1003
|
+
if (stroke.present !== undefined) FrFX.present = !!stroke.present;
|
|
1004
|
+
if (stroke.showInDialog !== undefined) FrFX.showInDialog = !!stroke.showInDialog;
|
|
1005
|
+
FrFX.Styl = FStl.encode(stroke.position);
|
|
1006
|
+
FrFX.PntT = FrFl.encode(stroke.fillType);
|
|
1007
|
+
FrFX['Md '] = BlnM.encode(stroke.blendMode);
|
|
1008
|
+
FrFX.Opct = unitsPercent(stroke.opacity);
|
|
1009
|
+
FrFX['Sz '] = unitsValue(stroke.size, 'size');
|
|
1010
|
+
if (stroke.color) FrFX['Clr '] = serializeColor(stroke.color);
|
|
1011
|
+
if (stroke.gradient) FrFX = { ...FrFX, ...serializeGradientContent(stroke.gradient) };
|
|
1012
|
+
if (stroke.pattern) FrFX = { ...FrFX, ...serializePatternContent(stroke.pattern) };
|
|
1013
|
+
if (stroke.overprint !== undefined) FrFX.overprint = !!stroke.overprint;
|
|
1014
|
+
return FrFX;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
export function serializeEffects(e: LayerEffectsInfo, log: boolean, multi: boolean) {
|
|
1018
|
+
const info: Lfx2Descriptor & LmfxDescriptor = multi ? {
|
|
1019
|
+
'Scl ': unitsPercent(e.scale ?? 1),
|
|
1020
|
+
masterFXSwitch: !e.disabled,
|
|
1021
|
+
} : {
|
|
1022
|
+
masterFXSwitch: !e.disabled,
|
|
1023
|
+
'Scl ': unitsPercent(e.scale ?? 1),
|
|
1024
|
+
};
|
|
1025
|
+
|
|
1026
|
+
const arrayKeys: (keyof LayerEffectsInfo)[] = ['dropShadow', 'innerShadow', 'solidFill', 'gradientOverlay', 'stroke'];
|
|
1027
|
+
for (const key of arrayKeys) {
|
|
1028
|
+
if (e[key] && !Array.isArray(e[key])) throw new Error(`${key} should be an array`);
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
if (e.dropShadow?.[0] && !multi) info.DrSh = serializeEffectObject(e.dropShadow[0], 'dropShadow', log);
|
|
1032
|
+
if (e.dropShadow?.[0] && multi) info.dropShadowMulti = e.dropShadow.map(i => serializeEffectObject(i, 'dropShadow', log));
|
|
1033
|
+
if (e.innerShadow?.[0] && !multi) info.IrSh = serializeEffectObject(e.innerShadow[0], 'innerShadow', log);
|
|
1034
|
+
if (e.innerShadow?.[0] && multi) info.innerShadowMulti = e.innerShadow.map(i => serializeEffectObject(i, 'innerShadow', log));
|
|
1035
|
+
if (e.outerGlow) info.OrGl = serializeEffectObject(e.outerGlow, 'outerGlow', log);
|
|
1036
|
+
if (e.solidFill?.[0] && multi) info.solidFillMulti = e.solidFill.map(i => serializeEffectObject(i, 'solidFill', log));
|
|
1037
|
+
if (e.gradientOverlay?.[0] && multi) info.gradientFillMulti = e.gradientOverlay.map(i => serializeEffectObject(i, 'gradientOverlay', log));
|
|
1038
|
+
if (e.stroke?.[0] && multi) info.frameFXMulti = e.stroke.map(i => serializeFxObject(i));
|
|
1039
|
+
if (e.innerGlow) info.IrGl = serializeEffectObject(e.innerGlow, 'innerGlow', log);
|
|
1040
|
+
if (e.bevel) info.ebbl = serializeEffectObject(e.bevel, 'bevel', log);
|
|
1041
|
+
if (e.solidFill?.[0] && !multi) info.SoFi = serializeEffectObject(e.solidFill[0], 'solidFill', log);
|
|
1042
|
+
if (e.patternOverlay) info.patternFill = serializeEffectObject(e.patternOverlay, 'patternOverlay', log);
|
|
1043
|
+
if (e.gradientOverlay?.[0] && !multi) info.GrFl = serializeEffectObject(e.gradientOverlay[0], 'gradientOverlay', log);
|
|
1044
|
+
if (e.satin) info.ChFX = serializeEffectObject(e.satin, 'satin', log);
|
|
1045
|
+
if (e.stroke?.[0] && !multi) info.FrFX = serializeFxObject(e.stroke?.[0]);
|
|
1046
|
+
|
|
1047
|
+
if (multi) {
|
|
1048
|
+
info.numModifyingFX = 0;
|
|
1049
|
+
|
|
1050
|
+
for (const key of Object.keys(e)) {
|
|
1051
|
+
const value = (e as any)[key];
|
|
1052
|
+
if (Array.isArray(value)) {
|
|
1053
|
+
for (const effect of value) {
|
|
1054
|
+
if (effect.enabled) info.numModifyingFX++;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
return info;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
export function parseEffects(info: Lfx2Descriptor & LmfxDescriptor, log: boolean) {
|
|
1064
|
+
const effects: LayerEffectsInfo = {};
|
|
1065
|
+
if (!info.masterFXSwitch) effects.disabled = true;
|
|
1066
|
+
if (info['Scl ']) effects.scale = parsePercent(info['Scl ']);
|
|
1067
|
+
if (info.DrSh) effects.dropShadow = [parseEffectObject(info.DrSh, log)];
|
|
1068
|
+
if (info.dropShadowMulti) effects.dropShadow = info.dropShadowMulti.map(i => parseEffectObject(i, log));
|
|
1069
|
+
if (info.IrSh) effects.innerShadow = [parseEffectObject(info.IrSh, log)];
|
|
1070
|
+
if (info.innerShadowMulti) effects.innerShadow = info.innerShadowMulti.map(i => parseEffectObject(i, log));
|
|
1071
|
+
if (info.OrGl) effects.outerGlow = parseEffectObject(info.OrGl, log);
|
|
1072
|
+
if (info.IrGl) effects.innerGlow = parseEffectObject(info.IrGl, log);
|
|
1073
|
+
if (info.ebbl) effects.bevel = parseEffectObject(info.ebbl, log);
|
|
1074
|
+
if (info.SoFi) effects.solidFill = [parseEffectObject(info.SoFi, log)];
|
|
1075
|
+
if (info.solidFillMulti) effects.solidFill = info.solidFillMulti.map(i => parseEffectObject(i, log));
|
|
1076
|
+
if (info.patternFill) effects.patternOverlay = parseEffectObject(info.patternFill, log);
|
|
1077
|
+
if (info.GrFl) effects.gradientOverlay = [parseEffectObject(info.GrFl, log)];
|
|
1078
|
+
if (info.gradientFillMulti) effects.gradientOverlay = info.gradientFillMulti.map(i => parseEffectObject(i, log));
|
|
1079
|
+
if (info.ChFX) effects.satin = parseEffectObject(info.ChFX, log);
|
|
1080
|
+
if (info.FrFX) effects.stroke = [parseFxObject(info.FrFX)];
|
|
1081
|
+
if (info.frameFXMulti) effects.stroke = info.frameFXMulti.map(i => parseFxObject(i));
|
|
1082
|
+
return effects;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
function parseKeyList(keyList: TimelineKeyDescriptor[], logMissingFeatures: boolean) {
|
|
1086
|
+
const keys: TimelineKey[] = [];
|
|
1087
|
+
|
|
1088
|
+
for (let j = 0; j < keyList.length; j++) {
|
|
1089
|
+
const key = keyList[j];
|
|
1090
|
+
const { time, selected, animKey } = key;
|
|
1091
|
+
const interpolation = animInterpStyleEnum.decode(key.animInterpStyle);
|
|
1092
|
+
|
|
1093
|
+
switch (animKey.Type) {
|
|
1094
|
+
case 'keyType.Opct':
|
|
1095
|
+
keys.push({ interpolation, time, selected, type: 'opacity', value: parsePercent(animKey.Opct) });
|
|
1096
|
+
break;
|
|
1097
|
+
case 'keyType.Pstn':
|
|
1098
|
+
keys.push({ interpolation, time, selected, type: 'position', x: animKey.Hrzn, y: animKey.Vrtc });
|
|
1099
|
+
break;
|
|
1100
|
+
case 'keyType.Trnf':
|
|
1101
|
+
keys.push({
|
|
1102
|
+
interpolation, time, selected, type: 'transform',
|
|
1103
|
+
scale: horzVrtcToXY(animKey['Scl ']), skew: horzVrtcToXY(animKey.Skew), rotation: animKey.rotation, translation: horzVrtcToXY(animKey.translation)
|
|
1104
|
+
});
|
|
1105
|
+
break;
|
|
1106
|
+
case 'keyType.sheetStyle': {
|
|
1107
|
+
const key: TimelineKey = { interpolation, time, selected, type: 'style' };
|
|
1108
|
+
if (animKey.sheetStyle.Lefx) key.style = parseEffects(animKey.sheetStyle.Lefx, logMissingFeatures);
|
|
1109
|
+
keys.push(key);
|
|
1110
|
+
break;
|
|
1111
|
+
}
|
|
1112
|
+
case 'keyType.globalLighting': {
|
|
1113
|
+
keys.push({
|
|
1114
|
+
interpolation, time, selected, type: 'globalLighting',
|
|
1115
|
+
globalAngle: animKey.gblA, globalAltitude: animKey.globalAltitude
|
|
1116
|
+
});
|
|
1117
|
+
break;
|
|
1118
|
+
}
|
|
1119
|
+
default: throw new Error(`Unsupported keyType value`);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
return keys;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
function serializeKeyList(keys: TimelineKey[]): TimelineKeyDescriptor[] {
|
|
1127
|
+
const keyList: TimelineKeyDescriptor[] = [];
|
|
1128
|
+
|
|
1129
|
+
for (let j = 0; j < keys.length; j++) {
|
|
1130
|
+
const key = keys[j];
|
|
1131
|
+
const { time, selected = false, interpolation } = key;
|
|
1132
|
+
const animInterpStyle = animInterpStyleEnum.encode(interpolation) as 'animInterpStyle.Lnr ' | 'animInterpStyle.hold';
|
|
1133
|
+
let animKey: TimelineAnimKeyDescriptor;
|
|
1134
|
+
|
|
1135
|
+
switch (key.type) {
|
|
1136
|
+
case 'opacity':
|
|
1137
|
+
animKey = { Type: 'keyType.Opct', Opct: unitsPercent(key.value) };
|
|
1138
|
+
break;
|
|
1139
|
+
case 'position':
|
|
1140
|
+
animKey = { Type: 'keyType.Pstn', Hrzn: key.x, Vrtc: key.y };
|
|
1141
|
+
break;
|
|
1142
|
+
case 'transform':
|
|
1143
|
+
animKey = { Type: 'keyType.Trnf', 'Scl ': xyToHorzVrtc(key.scale), Skew: xyToHorzVrtc(key.skew), rotation: key.rotation, translation: xyToHorzVrtc(key.translation) };
|
|
1144
|
+
break;
|
|
1145
|
+
case 'style':
|
|
1146
|
+
animKey = { Type: 'keyType.sheetStyle', sheetStyle: { Vrsn: 1, blendOptions: {} } };
|
|
1147
|
+
if (key.style) animKey.sheetStyle = { Vrsn: 1, Lefx: serializeEffects(key.style, false, false), blendOptions: {} };
|
|
1148
|
+
break;
|
|
1149
|
+
case 'globalLighting': {
|
|
1150
|
+
animKey = { Type: 'keyType.globalLighting', gblA: key.globalAngle, globalAltitude: key.globalAltitude };
|
|
1151
|
+
break;
|
|
1152
|
+
}
|
|
1153
|
+
default: throw new Error(`Unsupported keyType value`);
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
keyList.push({ Vrsn: 1, animInterpStyle, time, animKey, selected });
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
return keyList;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
export function parseTrackList(trackList: TimelineTrackDescriptor[], logMissingFeatures: boolean) {
|
|
1163
|
+
const tracks: TimelineTrack[] = [];
|
|
1164
|
+
|
|
1165
|
+
for (let i = 0; i < trackList.length; i++) {
|
|
1166
|
+
const tr = trackList[i];
|
|
1167
|
+
const track: TimelineTrack = {
|
|
1168
|
+
type: stdTrackID.decode(tr.trackID),
|
|
1169
|
+
enabled: tr.enab,
|
|
1170
|
+
keys: parseKeyList(tr.keyList, logMissingFeatures),
|
|
1171
|
+
};
|
|
1172
|
+
|
|
1173
|
+
if (tr.effectParams) {
|
|
1174
|
+
track.effectParams = {
|
|
1175
|
+
fillCanvas: tr.effectParams.fillCanvas,
|
|
1176
|
+
zoomOrigin: tr.effectParams.zoomOrigin,
|
|
1177
|
+
keys: parseKeyList(tr.effectParams.keyList, logMissingFeatures),
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
tracks.push(track);
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
return tracks;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
export function serializeTrackList(tracks: TimelineTrack[]): TimelineTrackDescriptor[] {
|
|
1188
|
+
const trackList: TimelineTrackDescriptor[] = [];
|
|
1189
|
+
|
|
1190
|
+
for (let i = 0; i < tracks.length; i++) {
|
|
1191
|
+
const t = tracks[i];
|
|
1192
|
+
trackList.push({
|
|
1193
|
+
trackID: stdTrackID.encode(t.type) as any,
|
|
1194
|
+
Vrsn: 1,
|
|
1195
|
+
enab: !!t.enabled,
|
|
1196
|
+
Effc: !!t.effectParams,
|
|
1197
|
+
...(t.effectParams ? {
|
|
1198
|
+
effectParams: {
|
|
1199
|
+
keyList: serializeKeyList(t.keys),
|
|
1200
|
+
fillCanvas: t.effectParams.fillCanvas,
|
|
1201
|
+
zoomOrigin: t.effectParams.zoomOrigin,
|
|
1202
|
+
}
|
|
1203
|
+
} : {}),
|
|
1204
|
+
keyList: serializeKeyList(t.keys),
|
|
1205
|
+
});
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
return trackList;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
type AllEffects = LayerEffectShadow & LayerEffectsOuterGlow & LayerEffectStroke &
|
|
1212
|
+
LayerEffectInnerGlow & LayerEffectBevel & LayerEffectSolidFill &
|
|
1213
|
+
LayerEffectPatternOverlay & LayerEffectSatin & LayerEffectGradientOverlay;
|
|
1214
|
+
|
|
1215
|
+
function parseEffectObject(obj: any, reportErrors: boolean) {
|
|
1216
|
+
const result: AllEffects = {} as any;
|
|
1217
|
+
|
|
1218
|
+
for (const key of Object.keys(obj)) {
|
|
1219
|
+
const val = obj[key];
|
|
1220
|
+
|
|
1221
|
+
switch (key) {
|
|
1222
|
+
case 'enab': result.enabled = !!val; break;
|
|
1223
|
+
case 'uglg': result.useGlobalLight = !!val; break;
|
|
1224
|
+
case 'AntA': result.antialiased = !!val; break;
|
|
1225
|
+
case 'Algn': result.align = !!val; break;
|
|
1226
|
+
case 'Dthr': result.dither = !!val; break;
|
|
1227
|
+
case 'Invr': result.invert = !!val; break;
|
|
1228
|
+
case 'Rvrs': result.reverse = !!val; break;
|
|
1229
|
+
case 'Clr ': result.color = parseColor(val); break;
|
|
1230
|
+
case 'hglC': result.highlightColor = parseColor(val); break;
|
|
1231
|
+
case 'sdwC': result.shadowColor = parseColor(val); break;
|
|
1232
|
+
case 'Styl': result.position = FStl.decode(val); break;
|
|
1233
|
+
case 'Md ': result.blendMode = BlnM.decode(val); break;
|
|
1234
|
+
case 'hglM': result.highlightBlendMode = BlnM.decode(val); break;
|
|
1235
|
+
case 'sdwM': result.shadowBlendMode = BlnM.decode(val); break;
|
|
1236
|
+
case 'bvlS': result.style = BESl.decode(val); break;
|
|
1237
|
+
case 'bvlD': result.direction = BESs.decode(val); break;
|
|
1238
|
+
case 'bvlT': result.technique = bvlT.decode(val) as any; break;
|
|
1239
|
+
case 'GlwT': result.technique = BETE.decode(val) as any; break;
|
|
1240
|
+
case 'glwS': result.source = IGSr.decode(val); break;
|
|
1241
|
+
case 'Type': result.type = GrdT.decode(val); break;
|
|
1242
|
+
case 'gs99': result.interpolationMethod = gradientInterpolationMethodType.decode(val); break;
|
|
1243
|
+
case 'Opct': result.opacity = parsePercent(val); break;
|
|
1244
|
+
case 'hglO': result.highlightOpacity = parsePercent(val); break;
|
|
1245
|
+
case 'sdwO': result.shadowOpacity = parsePercent(val); break;
|
|
1246
|
+
case 'lagl': result.angle = parseAngle(val); break;
|
|
1247
|
+
case 'Angl': result.angle = parseAngle(val); break;
|
|
1248
|
+
case 'Lald': result.altitude = parseAngle(val); break;
|
|
1249
|
+
case 'Sftn': result.soften = parseUnits(val); break;
|
|
1250
|
+
case 'srgR': result.strength = parsePercent(val); break;
|
|
1251
|
+
case 'blur': result.size = parseUnits(val); break;
|
|
1252
|
+
case 'Nose': result.noise = parsePercent(val); break;
|
|
1253
|
+
case 'Inpr': result.range = parsePercent(val); break;
|
|
1254
|
+
case 'Ckmt': result.choke = parseUnits(val); break;
|
|
1255
|
+
case 'ShdN': result.jitter = parsePercent(val); break;
|
|
1256
|
+
case 'Dstn': result.distance = parseUnits(val); break;
|
|
1257
|
+
case 'Scl ': result.scale = parsePercent(val); break;
|
|
1258
|
+
case 'Ptrn': result.pattern = { name: val['Nm '], id: val.Idnt }; break;
|
|
1259
|
+
case 'phase': result.phase = { x: val.Hrzn, y: val.Vrtc }; break;
|
|
1260
|
+
case 'Ofst': result.offset = { x: parsePercent(val.Hrzn), y: parsePercent(val.Vrtc) }; break;
|
|
1261
|
+
case 'MpgS':
|
|
1262
|
+
case 'TrnS':
|
|
1263
|
+
result.contour = {
|
|
1264
|
+
name: val['Nm '],
|
|
1265
|
+
curve: (val['Crv '] as any[]).map(p => ({ x: p.Hrzn, y: p.Vrtc })),
|
|
1266
|
+
};
|
|
1267
|
+
break;
|
|
1268
|
+
case 'Grad': result.gradient = parseGradient(val); break;
|
|
1269
|
+
case 'useTexture':
|
|
1270
|
+
case 'useShape':
|
|
1271
|
+
case 'layerConceals':
|
|
1272
|
+
case 'present':
|
|
1273
|
+
case 'showInDialog':
|
|
1274
|
+
case 'antialiasGloss': result[key] = val; break;
|
|
1275
|
+
default:
|
|
1276
|
+
reportErrors && console.log(`Invalid effect key: '${key}', value:`, val);
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
return result;
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
function serializeEffectObject(obj: any, objName: string, reportErrors: boolean) {
|
|
1284
|
+
const result: any = {};
|
|
1285
|
+
|
|
1286
|
+
for (const objKey of Object.keys(obj)) {
|
|
1287
|
+
const key: keyof AllEffects = objKey as any;
|
|
1288
|
+
const val = obj[key];
|
|
1289
|
+
|
|
1290
|
+
switch (key) {
|
|
1291
|
+
case 'enabled': result.enab = !!val; break;
|
|
1292
|
+
case 'useGlobalLight': result.uglg = !!val; break;
|
|
1293
|
+
case 'antialiased': result.AntA = !!val; break;
|
|
1294
|
+
case 'align': result.Algn = !!val; break;
|
|
1295
|
+
case 'dither': result.Dthr = !!val; break;
|
|
1296
|
+
case 'invert': result.Invr = !!val; break;
|
|
1297
|
+
case 'reverse': result.Rvrs = !!val; break;
|
|
1298
|
+
case 'color': result['Clr '] = serializeColor(val); break;
|
|
1299
|
+
case 'highlightColor': result.hglC = serializeColor(val); break;
|
|
1300
|
+
case 'shadowColor': result.sdwC = serializeColor(val); break;
|
|
1301
|
+
case 'position': result.Styl = FStl.encode(val); break;
|
|
1302
|
+
case 'blendMode': result['Md '] = BlnM.encode(val); break;
|
|
1303
|
+
case 'highlightBlendMode': result.hglM = BlnM.encode(val); break;
|
|
1304
|
+
case 'shadowBlendMode': result.sdwM = BlnM.encode(val); break;
|
|
1305
|
+
case 'style': result.bvlS = BESl.encode(val); break;
|
|
1306
|
+
case 'direction': result.bvlD = BESs.encode(val); break;
|
|
1307
|
+
case 'technique':
|
|
1308
|
+
if (objName === 'bevel') {
|
|
1309
|
+
result.bvlT = bvlT.encode(val);
|
|
1310
|
+
} else {
|
|
1311
|
+
result.GlwT = BETE.encode(val);
|
|
1312
|
+
}
|
|
1313
|
+
break;
|
|
1314
|
+
case 'source': result.glwS = IGSr.encode(val); break;
|
|
1315
|
+
case 'type': result.Type = GrdT.encode(val); break;
|
|
1316
|
+
case 'interpolationMethod': result.gs99 = gradientInterpolationMethodType.encode(val); break;
|
|
1317
|
+
case 'opacity': result.Opct = unitsPercent(val); break;
|
|
1318
|
+
case 'highlightOpacity': result.hglO = unitsPercent(val); break;
|
|
1319
|
+
case 'shadowOpacity': result.sdwO = unitsPercent(val); break;
|
|
1320
|
+
case 'angle':
|
|
1321
|
+
if (objName === 'gradientOverlay') {
|
|
1322
|
+
result.Angl = unitsAngle(val);
|
|
1323
|
+
} else {
|
|
1324
|
+
result.lagl = unitsAngle(val);
|
|
1325
|
+
}
|
|
1326
|
+
break;
|
|
1327
|
+
case 'altitude': result.Lald = unitsAngle(val); break;
|
|
1328
|
+
case 'soften': result.Sftn = unitsValue(val, key); break;
|
|
1329
|
+
case 'strength': result.srgR = unitsPercent(val); break;
|
|
1330
|
+
case 'size': result.blur = unitsValue(val, key); break;
|
|
1331
|
+
case 'noise': result.Nose = unitsPercent(val); break;
|
|
1332
|
+
case 'range': result.Inpr = unitsPercent(val); break;
|
|
1333
|
+
case 'choke': result.Ckmt = unitsValue(val, key); break;
|
|
1334
|
+
case 'jitter': result.ShdN = unitsPercent(val); break;
|
|
1335
|
+
case 'distance': result.Dstn = unitsValue(val, key); break;
|
|
1336
|
+
case 'scale': result['Scl '] = unitsPercent(val); break;
|
|
1337
|
+
case 'pattern': result.Ptrn = { 'Nm ': val.name, Idnt: val.id }; break;
|
|
1338
|
+
case 'phase': result.phase = { Hrzn: val.x, Vrtc: val.y }; break;
|
|
1339
|
+
case 'offset': result.Ofst = { Hrzn: unitsPercent(val.x), Vrtc: unitsPercent(val.y) }; break;
|
|
1340
|
+
case 'contour': {
|
|
1341
|
+
result[objName === 'satin' ? 'MpgS' : 'TrnS'] = {
|
|
1342
|
+
'Nm ': (val as EffectContour).name,
|
|
1343
|
+
'Crv ': (val as EffectContour).curve.map(p => ({ Hrzn: p.x, Vrtc: p.y })),
|
|
1344
|
+
};
|
|
1345
|
+
break;
|
|
1346
|
+
}
|
|
1347
|
+
case 'gradient': result.Grad = serializeGradient(val); break;
|
|
1348
|
+
case 'useTexture':
|
|
1349
|
+
case 'useShape':
|
|
1350
|
+
case 'layerConceals':
|
|
1351
|
+
case 'present':
|
|
1352
|
+
case 'showInDialog':
|
|
1353
|
+
case 'antialiasGloss':
|
|
1354
|
+
result[key] = val;
|
|
1355
|
+
break;
|
|
1356
|
+
default:
|
|
1357
|
+
reportErrors && console.log(`Invalid effect key: '${key}', value:`, val);
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
return result;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
function parseGradient(grad: DesciptorGradient): EffectSolidGradient | EffectNoiseGradient {
|
|
1365
|
+
if (grad.GrdF === 'GrdF.CstS') {
|
|
1366
|
+
const samples: number = grad.Intr || 4096;
|
|
1367
|
+
|
|
1368
|
+
return {
|
|
1369
|
+
type: 'solid',
|
|
1370
|
+
name: grad['Nm '],
|
|
1371
|
+
smoothness: grad.Intr / 4096,
|
|
1372
|
+
colorStops: grad.Clrs.map(s => ({
|
|
1373
|
+
color: parseColor(s['Clr ']),
|
|
1374
|
+
location: s.Lctn / samples,
|
|
1375
|
+
midpoint: s.Mdpn / 100,
|
|
1376
|
+
})),
|
|
1377
|
+
opacityStops: grad.Trns.map(s => ({
|
|
1378
|
+
opacity: parsePercent(s.Opct),
|
|
1379
|
+
location: s.Lctn / samples,
|
|
1380
|
+
midpoint: s.Mdpn / 100,
|
|
1381
|
+
})),
|
|
1382
|
+
};
|
|
1383
|
+
} else {
|
|
1384
|
+
return {
|
|
1385
|
+
type: 'noise',
|
|
1386
|
+
name: grad['Nm '],
|
|
1387
|
+
roughness: grad.Smth / 4096,
|
|
1388
|
+
colorModel: ClrS.decode(grad.ClrS),
|
|
1389
|
+
randomSeed: grad.RndS,
|
|
1390
|
+
restrictColors: !!grad.VctC,
|
|
1391
|
+
addTransparency: !!grad.ShTr,
|
|
1392
|
+
min: grad['Mnm '].map(x => x / 100),
|
|
1393
|
+
max: grad['Mxm '].map(x => x / 100),
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
function serializeGradient(grad: EffectSolidGradient | EffectNoiseGradient): DesciptorGradient {
|
|
1399
|
+
if (grad.type === 'solid') {
|
|
1400
|
+
const samples = Math.round((grad.smoothness ?? 1) * 4096);
|
|
1401
|
+
return {
|
|
1402
|
+
'Nm ': grad.name || '',
|
|
1403
|
+
GrdF: 'GrdF.CstS',
|
|
1404
|
+
Intr: samples,
|
|
1405
|
+
Clrs: grad.colorStops.map(s => ({
|
|
1406
|
+
'Clr ': serializeColor(s.color),
|
|
1407
|
+
Type: 'Clry.UsrS',
|
|
1408
|
+
Lctn: Math.round(s.location * samples),
|
|
1409
|
+
Mdpn: Math.round((s.midpoint ?? 0.5) * 100),
|
|
1410
|
+
})),
|
|
1411
|
+
Trns: grad.opacityStops.map(s => ({
|
|
1412
|
+
Opct: unitsPercent(s.opacity),
|
|
1413
|
+
Lctn: Math.round(s.location * samples),
|
|
1414
|
+
Mdpn: Math.round((s.midpoint ?? 0.5) * 100),
|
|
1415
|
+
})),
|
|
1416
|
+
};
|
|
1417
|
+
} else {
|
|
1418
|
+
return {
|
|
1419
|
+
GrdF: 'GrdF.ClNs',
|
|
1420
|
+
'Nm ': grad.name || '',
|
|
1421
|
+
ShTr: !!grad.addTransparency,
|
|
1422
|
+
VctC: !!grad.restrictColors,
|
|
1423
|
+
ClrS: ClrS.encode(grad.colorModel),
|
|
1424
|
+
RndS: grad.randomSeed || 0,
|
|
1425
|
+
Smth: Math.round((grad.roughness ?? 1) * 4096),
|
|
1426
|
+
'Mnm ': (grad.min || [0, 0, 0, 0]).map(x => x * 100),
|
|
1427
|
+
'Mxm ': (grad.max || [1, 1, 1, 1]).map(x => x * 100),
|
|
1428
|
+
};
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
function parseGradientContent(descriptor: DescriptorGradientContent) {
|
|
1433
|
+
const result = parseGradient(descriptor.Grad) as (EffectSolidGradient | EffectNoiseGradient) & ExtraGradientInfo;
|
|
1434
|
+
result.style = GrdT.decode(descriptor.Type);
|
|
1435
|
+
if (descriptor.Dthr !== undefined) result.dither = descriptor.Dthr;
|
|
1436
|
+
if (descriptor.Rvrs !== undefined) result.reverse = descriptor.Rvrs;
|
|
1437
|
+
if (descriptor.Angl !== undefined) result.angle = parseAngle(descriptor.Angl);
|
|
1438
|
+
if (descriptor['Scl '] !== undefined) result.scale = parsePercent(descriptor['Scl ']);
|
|
1439
|
+
if (descriptor.Algn !== undefined) result.align = descriptor.Algn;
|
|
1440
|
+
if (descriptor.Ofst !== undefined) {
|
|
1441
|
+
result.offset = {
|
|
1442
|
+
x: parsePercent(descriptor.Ofst.Hrzn),
|
|
1443
|
+
y: parsePercent(descriptor.Ofst.Vrtc)
|
|
1444
|
+
};
|
|
1445
|
+
}
|
|
1446
|
+
return result;
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
function parsePatternContent(descriptor: DescriptorPatternContent) {
|
|
1450
|
+
const result: EffectPattern & ExtraPatternInfo = {
|
|
1451
|
+
name: descriptor.Ptrn['Nm '],
|
|
1452
|
+
id: descriptor.Ptrn.Idnt,
|
|
1453
|
+
};
|
|
1454
|
+
if (descriptor.Lnkd !== undefined) result.linked = descriptor.Lnkd;
|
|
1455
|
+
if (descriptor.phase !== undefined) result.phase = { x: descriptor.phase.Hrzn, y: descriptor.phase.Vrtc };
|
|
1456
|
+
return result;
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
|
|
1460
|
+
export function parseVectorContent(descriptor: DescriptorVectorContent): VectorContent {
|
|
1461
|
+
if ('Grad' in descriptor) {
|
|
1462
|
+
return parseGradientContent(descriptor);
|
|
1463
|
+
} else if ('Ptrn' in descriptor) {
|
|
1464
|
+
return { type: 'pattern', ...parsePatternContent(descriptor) };
|
|
1465
|
+
} else if ('Clr ' in descriptor) {
|
|
1466
|
+
return { type: 'color', color: parseColor(descriptor['Clr ']) };
|
|
1467
|
+
} else {
|
|
1468
|
+
throw new Error('Invalid vector content');
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
function serializeGradientContent(content: (EffectSolidGradient | EffectNoiseGradient) & ExtraGradientInfo) {
|
|
1473
|
+
const result: DescriptorGradientContent = {} as any;
|
|
1474
|
+
if (content.dither !== undefined) result.Dthr = content.dither;
|
|
1475
|
+
if (content.reverse !== undefined) result.Rvrs = content.reverse;
|
|
1476
|
+
if (content.angle !== undefined) result.Angl = unitsAngle(content.angle);
|
|
1477
|
+
result.Type = GrdT.encode(content.style);
|
|
1478
|
+
if (content.align !== undefined) result.Algn = content.align;
|
|
1479
|
+
if (content.scale !== undefined) result['Scl '] = unitsPercent(content.scale);
|
|
1480
|
+
if (content.offset) {
|
|
1481
|
+
result.Ofst = {
|
|
1482
|
+
Hrzn: unitsPercent(content.offset.x),
|
|
1483
|
+
Vrtc: unitsPercent(content.offset.y),
|
|
1484
|
+
};
|
|
1485
|
+
}
|
|
1486
|
+
result.Grad = serializeGradient(content);
|
|
1487
|
+
return result;
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
function serializePatternContent(content: EffectPattern & ExtraPatternInfo) {
|
|
1491
|
+
const result: DescriptorPatternContent = {
|
|
1492
|
+
Ptrn: {
|
|
1493
|
+
'Nm ': content.name || '',
|
|
1494
|
+
Idnt: content.id || '',
|
|
1495
|
+
}
|
|
1496
|
+
};
|
|
1497
|
+
if (content.linked !== undefined) result.Lnkd = !!content.linked;
|
|
1498
|
+
if (content.phase !== undefined) result.phase = { Hrzn: content.phase.x, Vrtc: content.phase.y };
|
|
1499
|
+
return result;
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
export function serializeVectorContent(content: VectorContent): { descriptor: DescriptorVectorContent; key: string; } {
|
|
1503
|
+
if (content.type === 'color') {
|
|
1504
|
+
return { key: 'SoCo', descriptor: { 'Clr ': serializeColor(content.color) } };
|
|
1505
|
+
} else if (content.type === 'pattern') {
|
|
1506
|
+
return { key: 'PtFl', descriptor: serializePatternContent(content) };
|
|
1507
|
+
} else {
|
|
1508
|
+
return { key: 'GdFl', descriptor: serializeGradientContent(content) };
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
export function parseColor(color: DescriptorColor): Color {
|
|
1513
|
+
if ('H ' in color) {
|
|
1514
|
+
return { h: parsePercentOrAngle(color['H ']), s: color.Strt, b: color.Brgh };
|
|
1515
|
+
} else if ('Rd ' in color) {
|
|
1516
|
+
return { r: color['Rd '], g: color['Grn '], b: color['Bl '] };
|
|
1517
|
+
} else if ('Cyn ' in color) {
|
|
1518
|
+
return { c: color['Cyn '], m: color.Mgnt, y: color['Ylw '], k: color.Blck };
|
|
1519
|
+
} else if ('Gry ' in color) {
|
|
1520
|
+
return { k: color['Gry '] };
|
|
1521
|
+
} else if ('Lmnc' in color) {
|
|
1522
|
+
return { l: color.Lmnc, a: color['A '], b: color['B '] };
|
|
1523
|
+
} else {
|
|
1524
|
+
throw new Error('Unsupported color descriptor');
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
export function serializeColor(color: Color | undefined): DescriptorColor {
|
|
1529
|
+
if (!color) {
|
|
1530
|
+
return { 'Rd ': 0, 'Grn ': 0, 'Bl ': 0 };
|
|
1531
|
+
} else if ('r' in color) {
|
|
1532
|
+
return { 'Rd ': color.r || 0, 'Grn ': color.g || 0, 'Bl ': color.b || 0 };
|
|
1533
|
+
} else if ('h' in color) {
|
|
1534
|
+
return { 'H ': unitsAngle(color.h * 360), Strt: color.s || 0, Brgh: color.b || 0 };
|
|
1535
|
+
} else if ('c' in color) {
|
|
1536
|
+
return { 'Cyn ': color.c || 0, Mgnt: color.m || 0, 'Ylw ': color.y || 0, Blck: color.k || 0 };
|
|
1537
|
+
} else if ('l' in color) {
|
|
1538
|
+
return { Lmnc: color.l || 0, 'A ': color.a || 0, 'B ': color.b || 0 };
|
|
1539
|
+
} else if ('k' in color) {
|
|
1540
|
+
return { 'Gry ': color.k };
|
|
1541
|
+
} else {
|
|
1542
|
+
throw new Error('Invalid color value');
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
export function parseAngle(x: DescriptorUnitsValue) {
|
|
1547
|
+
if (x === undefined) return 0;
|
|
1548
|
+
if (x.units !== 'Angle') throw new Error(`Invalid units: ${x.units}`);
|
|
1549
|
+
return x.value;
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
export function parsePercent(x: DescriptorUnitsValue | undefined) {
|
|
1553
|
+
if (x === undefined) return 1;
|
|
1554
|
+
if (x.units !== 'Percent') throw new Error(`Invalid units: ${x.units}`);
|
|
1555
|
+
return x.value / 100;
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
export function parsePercentOrAngle(x: DescriptorUnitsValue | undefined) {
|
|
1559
|
+
if (x === undefined) return 1;
|
|
1560
|
+
if (x.units === 'Percent') return x.value / 100;
|
|
1561
|
+
if (x.units === 'Angle') return x.value / 360;
|
|
1562
|
+
throw new Error(`Invalid units: ${x.units}`);
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
export function parseUnits({ units, value }: DescriptorUnitsValue): UnitsValue {
|
|
1566
|
+
if (
|
|
1567
|
+
units !== 'Pixels' && units !== 'Millimeters' && units !== 'Points' && units !== 'None' &&
|
|
1568
|
+
units !== 'Picas' && units !== 'Inches' && units !== 'Centimeters' && units !== 'Density'
|
|
1569
|
+
) {
|
|
1570
|
+
throw new Error(`Invalid units: ${JSON.stringify({ units, value })}`);
|
|
1571
|
+
}
|
|
1572
|
+
return { value, units };
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
export function parseUnitsOrNumber(value: DescriptorUnitsValue | number, units: Units = 'Pixels'): UnitsValue {
|
|
1576
|
+
if (typeof value === 'number') return { value, units };
|
|
1577
|
+
return parseUnits(value);
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
export function parseUnitsToNumber({ units, value }: DescriptorUnitsValue, expectedUnits: string): number {
|
|
1581
|
+
if (units !== expectedUnits) throw new Error(`Invalid units: ${JSON.stringify({ units, value })}`);
|
|
1582
|
+
return value;
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
export function unitsAngle(value: number | undefined): DescriptorUnitsValue {
|
|
1586
|
+
return { units: 'Angle', value: value || 0 };
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
export function unitsPercent(value: number | undefined): DescriptorUnitsValue {
|
|
1590
|
+
return { units: 'Percent', value: Math.round((value || 0) * 100) };
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
export function unitsValue(x: UnitsValue | undefined, key: string): DescriptorUnitsValue {
|
|
1594
|
+
if (x == null) return { units: 'Pixels', value: 0 };
|
|
1595
|
+
|
|
1596
|
+
if (typeof x !== 'object')
|
|
1597
|
+
throw new Error(`Invalid value: ${JSON.stringify(x)} (key: ${key}) (should have value and units)`);
|
|
1598
|
+
|
|
1599
|
+
const { units, value } = x;
|
|
1600
|
+
|
|
1601
|
+
if (typeof value !== 'number')
|
|
1602
|
+
throw new Error(`Invalid value in ${JSON.stringify(x)} (key: ${key})`);
|
|
1603
|
+
|
|
1604
|
+
if (
|
|
1605
|
+
units !== 'Pixels' && units !== 'Millimeters' && units !== 'Points' && units !== 'None' &&
|
|
1606
|
+
units !== 'Picas' && units !== 'Inches' && units !== 'Centimeters' && units !== 'Density'
|
|
1607
|
+
) {
|
|
1608
|
+
throw new Error(`Invalid units in ${JSON.stringify(x)} (key: ${key})`);
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
return { units, value };
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
export const textGridding = createEnum<TextGridding>('textGridding', 'none', {
|
|
1615
|
+
none: 'None',
|
|
1616
|
+
round: 'Rnd ',
|
|
1617
|
+
});
|
|
1618
|
+
|
|
1619
|
+
export const Ornt = createEnum<Orientation>('Ornt', 'horizontal', {
|
|
1620
|
+
horizontal: 'Hrzn',
|
|
1621
|
+
vertical: 'Vrtc',
|
|
1622
|
+
});
|
|
1623
|
+
|
|
1624
|
+
export const Annt = createEnum<AntiAlias>('Annt', 'sharp', {
|
|
1625
|
+
none: 'Anno',
|
|
1626
|
+
sharp: 'antiAliasSharp',
|
|
1627
|
+
crisp: 'AnCr',
|
|
1628
|
+
strong: 'AnSt',
|
|
1629
|
+
smooth: 'AnSm',
|
|
1630
|
+
platform: 'antiAliasPlatformGray',
|
|
1631
|
+
platformLCD: 'antiAliasPlatformLCD',
|
|
1632
|
+
});
|
|
1633
|
+
|
|
1634
|
+
export const warpStyle = createEnum<WarpStyle>('warpStyle', 'none', {
|
|
1635
|
+
none: 'warpNone',
|
|
1636
|
+
arc: 'warpArc',
|
|
1637
|
+
arcLower: 'warpArcLower',
|
|
1638
|
+
arcUpper: 'warpArcUpper',
|
|
1639
|
+
arch: 'warpArch',
|
|
1640
|
+
bulge: 'warpBulge',
|
|
1641
|
+
shellLower: 'warpShellLower',
|
|
1642
|
+
shellUpper: 'warpShellUpper',
|
|
1643
|
+
flag: 'warpFlag',
|
|
1644
|
+
wave: 'warpWave',
|
|
1645
|
+
fish: 'warpFish',
|
|
1646
|
+
rise: 'warpRise',
|
|
1647
|
+
fisheye: 'warpFisheye',
|
|
1648
|
+
inflate: 'warpInflate',
|
|
1649
|
+
squeeze: 'warpSqueeze',
|
|
1650
|
+
twist: 'warpTwist',
|
|
1651
|
+
cylinder: 'warpCylinder',
|
|
1652
|
+
custom: 'warpCustom',
|
|
1653
|
+
});
|
|
1654
|
+
|
|
1655
|
+
export const BlnM = createEnum<BlendMode>('BlnM', 'normal', {
|
|
1656
|
+
'normal': 'Nrml',
|
|
1657
|
+
'dissolve': 'Dslv',
|
|
1658
|
+
'darken': 'Drkn',
|
|
1659
|
+
'multiply': 'Mltp',
|
|
1660
|
+
'color burn': 'CBrn',
|
|
1661
|
+
'linear burn': 'linearBurn',
|
|
1662
|
+
'darker color': 'darkerColor',
|
|
1663
|
+
'lighten': 'Lghn',
|
|
1664
|
+
'screen': 'Scrn',
|
|
1665
|
+
'color dodge': 'CDdg',
|
|
1666
|
+
'linear dodge': 'linearDodge',
|
|
1667
|
+
'lighter color': 'lighterColor',
|
|
1668
|
+
'overlay': 'Ovrl',
|
|
1669
|
+
'soft light': 'SftL',
|
|
1670
|
+
'hard light': 'HrdL',
|
|
1671
|
+
'vivid light': 'vividLight',
|
|
1672
|
+
'linear light': 'linearLight',
|
|
1673
|
+
'pin light': 'pinLight',
|
|
1674
|
+
'hard mix': 'hardMix',
|
|
1675
|
+
'difference': 'Dfrn',
|
|
1676
|
+
'exclusion': 'Xclu',
|
|
1677
|
+
'subtract': 'blendSubtraction',
|
|
1678
|
+
'divide': 'blendDivide',
|
|
1679
|
+
'hue': 'H ',
|
|
1680
|
+
'saturation': 'Strt',
|
|
1681
|
+
'color': 'Clr ',
|
|
1682
|
+
'luminosity': 'Lmns',
|
|
1683
|
+
// used in ABR
|
|
1684
|
+
'linear height': 'linearHeight',
|
|
1685
|
+
'height': 'Hght',
|
|
1686
|
+
'subtraction': 'Sbtr', // 2nd version of subtract ?
|
|
1687
|
+
});
|
|
1688
|
+
|
|
1689
|
+
export const BESl = createEnum<BevelStyle>('BESl', 'inner bevel', {
|
|
1690
|
+
'inner bevel': 'InrB',
|
|
1691
|
+
'outer bevel': 'OtrB',
|
|
1692
|
+
'emboss': 'Embs',
|
|
1693
|
+
'pillow emboss': 'PlEb',
|
|
1694
|
+
'stroke emboss': 'strokeEmboss',
|
|
1695
|
+
});
|
|
1696
|
+
|
|
1697
|
+
export const bvlT = createEnum<BevelTechnique>('bvlT', 'smooth', {
|
|
1698
|
+
'smooth': 'SfBL',
|
|
1699
|
+
'chisel hard': 'PrBL',
|
|
1700
|
+
'chisel soft': 'Slmt',
|
|
1701
|
+
});
|
|
1702
|
+
|
|
1703
|
+
export const BESs = createEnum<BevelDirection>('BESs', 'up', {
|
|
1704
|
+
up: 'In ',
|
|
1705
|
+
down: 'Out ',
|
|
1706
|
+
});
|
|
1707
|
+
|
|
1708
|
+
export const BETE = createEnum<GlowTechnique>('BETE', 'softer', {
|
|
1709
|
+
softer: 'SfBL',
|
|
1710
|
+
precise: 'PrBL',
|
|
1711
|
+
});
|
|
1712
|
+
|
|
1713
|
+
export const IGSr = createEnum<GlowSource>('IGSr', 'edge', {
|
|
1714
|
+
edge: 'SrcE',
|
|
1715
|
+
center: 'SrcC',
|
|
1716
|
+
});
|
|
1717
|
+
|
|
1718
|
+
export const GrdT = createEnum<GradientStyle>('GrdT', 'linear', {
|
|
1719
|
+
linear: 'Lnr ',
|
|
1720
|
+
radial: 'Rdl ',
|
|
1721
|
+
angle: 'Angl',
|
|
1722
|
+
reflected: 'Rflc',
|
|
1723
|
+
diamond: 'Dmnd',
|
|
1724
|
+
});
|
|
1725
|
+
|
|
1726
|
+
export const animInterpStyleEnum = createEnum<TimelineKeyInterpolation>('animInterpStyle', 'linear', {
|
|
1727
|
+
linear: 'Lnr ',
|
|
1728
|
+
hold: 'hold',
|
|
1729
|
+
});
|
|
1730
|
+
|
|
1731
|
+
export const stdTrackID = createEnum<TimelineTrackType>('stdTrackID', 'opacity', {
|
|
1732
|
+
opacity: 'opacityTrack',
|
|
1733
|
+
style: 'styleTrack',
|
|
1734
|
+
sheetTransform: 'sheetTransformTrack',
|
|
1735
|
+
sheetPosition: 'sheetPositionTrack',
|
|
1736
|
+
globalLighting: 'globalLightingTrack',
|
|
1737
|
+
});
|
|
1738
|
+
|
|
1739
|
+
export const gradientInterpolationMethodType = createEnum<InterpolationMethod>('gradientInterpolationMethodType', 'perceptual', {
|
|
1740
|
+
perceptual: 'Perc',
|
|
1741
|
+
linear: 'Lnr',
|
|
1742
|
+
classic: 'Gcls',
|
|
1743
|
+
});
|
|
1744
|
+
|
|
1745
|
+
export const ClrS = createEnum<'rgb' | 'hsb' | 'lab'>('ClrS', 'rgb', {
|
|
1746
|
+
rgb: 'RGBC',
|
|
1747
|
+
hsb: 'HSBl',
|
|
1748
|
+
lab: 'LbCl',
|
|
1749
|
+
});
|
|
1750
|
+
|
|
1751
|
+
export const FStl = createEnum<'inside' | 'center' | 'outside'>('FStl', 'outside', {
|
|
1752
|
+
outside: 'OutF',
|
|
1753
|
+
center: 'CtrF',
|
|
1754
|
+
inside: 'InsF'
|
|
1755
|
+
});
|
|
1756
|
+
|
|
1757
|
+
export const FrFl = createEnum<'color' | 'gradient' | 'pattern'>('FrFl', 'color', {
|
|
1758
|
+
color: 'SClr',
|
|
1759
|
+
gradient: 'GrFl',
|
|
1760
|
+
pattern: 'Ptrn',
|
|
1761
|
+
});
|
|
1762
|
+
|
|
1763
|
+
export const ESliceType = createEnum<'image' | 'noImage'>('ESliceType', 'image', {
|
|
1764
|
+
image: 'Img ',
|
|
1765
|
+
noImage: 'noImage',
|
|
1766
|
+
});
|
|
1767
|
+
|
|
1768
|
+
export const ESliceHorzAlign = createEnum<'default'>('ESliceHorzAlign', 'default', {
|
|
1769
|
+
default: 'default',
|
|
1770
|
+
});
|
|
1771
|
+
|
|
1772
|
+
export const ESliceVertAlign = createEnum<'default'>('ESliceVertAlign', 'default', {
|
|
1773
|
+
default: 'default',
|
|
1774
|
+
});
|
|
1775
|
+
|
|
1776
|
+
export const ESliceOrigin = createEnum<'userGenerated' | 'autoGenerated' | 'layer'>('ESliceOrigin', 'userGenerated', {
|
|
1777
|
+
userGenerated: 'userGenerated',
|
|
1778
|
+
autoGenerated: 'autoGenerated',
|
|
1779
|
+
layer: 'layer',
|
|
1780
|
+
});
|
|
1781
|
+
|
|
1782
|
+
export const ESliceBGColorType = createEnum<'none' | 'matte' | 'color'>('ESliceBGColorType', 'none', {
|
|
1783
|
+
none: 'None',
|
|
1784
|
+
matte: 'matte',
|
|
1785
|
+
color: 'Clr ',
|
|
1786
|
+
});
|
|
1787
|
+
|
|
1788
|
+
export const strokeStyleLineCapType = createEnum<LineCapType>('strokeStyleLineCapType', 'butt', {
|
|
1789
|
+
butt: 'strokeStyleButtCap',
|
|
1790
|
+
round: 'strokeStyleRoundCap',
|
|
1791
|
+
square: 'strokeStyleSquareCap',
|
|
1792
|
+
});
|
|
1793
|
+
|
|
1794
|
+
export const strokeStyleLineJoinType = createEnum<LineJoinType>('strokeStyleLineJoinType', 'miter', {
|
|
1795
|
+
miter: 'strokeStyleMiterJoin',
|
|
1796
|
+
round: 'strokeStyleRoundJoin',
|
|
1797
|
+
bevel: 'strokeStyleBevelJoin',
|
|
1798
|
+
});
|
|
1799
|
+
|
|
1800
|
+
export const strokeStyleLineAlignment = createEnum<LineAlignment>('strokeStyleLineAlignment', 'inside', {
|
|
1801
|
+
inside: 'strokeStyleAlignInside',
|
|
1802
|
+
center: 'strokeStyleAlignCenter',
|
|
1803
|
+
outside: 'strokeStyleAlignOutside',
|
|
1804
|
+
});
|