1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127 | 1×
1×
1×
1×
3×
3×
54×
54×
1×
372×
3×
9×
369×
72×
297×
191×
106×
1×
633×
8×
24×
625×
101×
524×
320×
204×
204×
204×
57×
57×
57×
57×
57×
1094×
1094×
363×
113×
363×
363×
363×
363×
731×
57×
802×
57×
69×
69×
69×
1050×
1050×
549×
549×
1233×
549×
549×
69×
40×
69×
8×
8×
8×
128×
128×
60×
60×
117×
60×
60×
81×
8×
8×
3×
3×
8×
1×
| isEqual = require 'deep-equal'
r = require 'restructure'
CFFOperand = require './CFFOperand'
class CFFDict
constructor: (@ops = []) ->
@fields = {}
for field in @ops
key = if Array.isArray(field[0]) then field[0][0] << 8 | field[0][1] else field[0]
@fields[key] = field
decodeOperands = (type, stream, ret, operands) ->
if Array.isArray(type)
for op, i in operands
decodeOperands type[i], stream, ret, [op]
else if type.decode?
type.decode(stream, ret, operands)
else
switch type
when 'number', 'offset', 'sid'
operands[0]
when 'boolean'
!!operands[0]
else
operands
encodeOperands = (type, stream, ctx, operands) ->
if Array.isArray(type)
for op, i in operands
encodeOperands(type[i], stream, ctx, op)[0]
else if type.encode?
type.encode(stream, operands, ctx)
else if typeof operands is 'number'
[operands]
else Iif typeof operands is 'boolean'
[+operands]
else Eif Array.isArray(operands)
operands
else
[operands]
decode: (stream, parent) ->
end = stream.pos + parent.length
ret = {}
operands = []
# define hidden properties
Object.defineProperties ret,
parent: { value: parent }
_startOffset: { value: stream.pos }
while stream.pos < end
b = stream.readUInt8()
if b <= 21
if b is 12
b = (b << 8) | stream.readUInt8()
field = @fields[b]
Ithrow new Error "Unknown operator " + b unless field
ret[field[1]] = decodeOperands field[2], stream, ret, operands
operands = []
else
operands.push CFFOperand.decode(stream, b)
# fill in defaults
for key, field of @fields
ret[field[1]] ?= field[3]
return ret
size: (dict, parent, includePointers = true) ->
ctx =
parent: parent
val: dict
pointerSize: 0
startOffset: parent.startOffset or 0
len = 0
for k, field of @fields
val = dict[field[1]]
continue if not val? or isEqual val, field[3]
operands = encodeOperands field[2], null, ctx, val
for op in operands
len += CFFOperand.size op
key = if Array.isArray(field[0]) then field[0] else [field[0]]
len += key.length
if includePointers
len += ctx.pointerSize
return len
encode: (stream, dict, parent) ->
ctx =
pointers: []
startOffset: stream.pos
parent: parent
val: dict
pointerSize: 0
ctx.pointerOffset = stream.pos + @size(dict, ctx, false)
for field in @ops
val = dict[field[1]]
continue if not val? or isEqual val, field[3]
operands = encodeOperands field[2], stream, ctx, val
for op in operands
CFFOperand.encode stream, op
key = if Array.isArray(field[0]) then field[0] else [field[0]]
for op in key
stream.writeUInt8 op
i = 0
while i < ctx.pointers.length
ptr = ctx.pointers[i++]
ptr.type.encode(stream, ptr.val, ptr.parent)
return
module.exports = CFFDict
|