/
glyphs_glyf_src.go
348 lines (305 loc) · 9.64 KB
/
glyphs_glyf_src.go
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
package tables
import (
"encoding/binary"
"errors"
"fmt"
)
// shared with gvar, sbix, eblc
// return an error only if data is not long enough
func ParseLoca(src []byte, numGlyphs int, isLong bool) (out []uint32, err error) {
var size int
if isLong {
size = (numGlyphs + 1) * 4
} else {
size = (numGlyphs + 1) * 2
}
if L := len(src); L < size {
return nil, fmt.Errorf("reading Loca: EOF: expected length: %d, got %d", size, L)
}
out = make([]uint32, numGlyphs+1)
if isLong {
for i := range out {
out[i] = binary.BigEndian.Uint32(src[4*i:])
}
} else {
for i := range out {
out[i] = 2 * uint32(binary.BigEndian.Uint16(src[2*i:])) // The actual local offset divided by 2 is stored.
}
}
return out, nil
}
// Glyph Data
type Glyf []Glyph
// ParseGlyf parses the 'glyf' table.
// locaOffsets has length numGlyphs + 1, and is returned by ParseLoca
func ParseGlyf(src []byte, locaOffsets []uint32) (Glyf, error) {
out := make(Glyf, len(locaOffsets)-1)
var err error
for i := range out {
start, end := locaOffsets[i], locaOffsets[i+1]
// If a glyph has no outline, then loca[n] = loca [n+1].
if start == end {
continue
}
out[i], _, err = ParseGlyph(src[start:end])
if err != nil {
return nil, err
}
}
return out, nil
}
type Glyph struct {
numberOfContours int16 // If the number of contours is greater than or equal to zero, this is a simple glyph. If negative, this is a composite glyph — the value -1 should be used for composite glyphs.
XMin int16 // Minimum x for coordinate data.
YMin int16 // Minimum y for coordinate data.
XMax int16 // Maximum x for coordinate data.
YMax int16 // Maximum y for coordinate data.
Data GlyphData `isOpaque:"" subsliceStart:"AtCurrent"`
}
func (gl *Glyph) parseData(src []byte) (err error) {
if gl.numberOfContours >= 0 { // simple glyph
gl.Data, _, err = ParseSimpleGlyph(src, int(gl.numberOfContours))
} else { // composite glyph
gl.Data, _, err = ParseCompositeGlyph(src)
}
return err
}
type GlyphData interface {
isGlyphData()
}
func (SimpleGlyph) isGlyphData() {}
func (CompositeGlyph) isGlyphData() {}
type SimpleGlyph struct {
EndPtsOfContours []uint16 // [numberOfContours] Array of point indices for the last point of each contour, in increasing numeric order.
Instructions []byte `arrayCount:"FirstUint16"` // [instructionLength] Array of instruction byte code for the glyph.
Points []GlyphContourPoint `isOpaque:"" subsliceStart:"AtCurrent"`
}
type GlyphContourPoint struct {
Flag uint8
X, Y int16
}
const (
xShortVector = 0x02
xIsSameOrPositiveXShortVector = 0x10
yShortVector = 0x04
yIsSameOrPositiveYShortVector = 0x20
)
func (sg *SimpleGlyph) parsePoints(src []byte, _ int) error {
if len(sg.EndPtsOfContours) == 0 {
return nil
}
numPoints := int(sg.EndPtsOfContours[len(sg.EndPtsOfContours)-1]) + 1
const repeatFlag = 0x08
sg.Points = make([]GlyphContourPoint, numPoints)
// read flags
// to avoid costly length check, we also precompute the expected data size for coordinates
var (
coordinatesLengthX, coordinatesLengthY int
cursor int
L = len(src)
)
for i := 0; i < numPoints; i++ {
if L <= cursor {
return errors.New("invalid simple glyph data flags (EOF)")
}
flag := src[cursor]
sg.Points[i].Flag = flag
cursor++
localLengthX, localLengthY := 0, 0
if flag&xShortVector != 0 {
localLengthX = 1
} else if flag&xIsSameOrPositiveXShortVector == 0 {
localLengthX = 2
}
if flag&yShortVector != 0 {
localLengthY = 1
} else if flag&yIsSameOrPositiveYShortVector == 0 {
localLengthY = 2
}
if flag&repeatFlag != 0 {
if L <= cursor {
return errors.New("invalid simple glyph data flags (EOF)")
}
repeatCount := int(src[cursor])
cursor++
if i+repeatCount+1 > numPoints { // gracefully handle out of bounds
repeatCount = numPoints - i - 1
}
subSlice := sg.Points[i+1 : i+repeatCount+1]
for j := range subSlice {
subSlice[j].Flag = flag
}
i += repeatCount
localLengthX += repeatCount * localLengthX
localLengthY += repeatCount * localLengthY
}
coordinatesLengthX += localLengthX
coordinatesLengthY += localLengthY
}
src = src[cursor:]
if L, E := len(src), coordinatesLengthX+coordinatesLengthY; L < E {
return fmt.Errorf("EOF: expected length: %d, got %d", E, L)
}
dataX, dataY := src[:coordinatesLengthX], src[coordinatesLengthX:coordinatesLengthX+coordinatesLengthY]
// read x and y coordinates
parseGlyphContourPoints(dataX, dataY, sg.Points)
return nil
}
// returns the position after the read and the relative coordinate
// the input slice has already been checked for length
func readContourPoint(flag byte, data []byte, pos int, shortFlag, sameFlag uint8) (int, int16) {
var v int16
if flag&shortFlag != 0 {
val := data[pos]
pos++
if flag&sameFlag != 0 {
v += int16(val)
} else {
v -= int16(val)
}
} else if flag&sameFlag == 0 {
val := binary.BigEndian.Uint16(data[pos:])
pos += 2
v += int16(val)
}
return pos, v
}
// update the points in place
func parseGlyphContourPoints(dataX, dataY []byte, points []GlyphContourPoint) {
var (
posX, posY int // position into data
vX, offsetX, vY, offsetY int16 // coordinates are relative to the previous
)
for i, p := range points {
posX, offsetX = readContourPoint(p.Flag, dataX, posX, xShortVector, xIsSameOrPositiveXShortVector)
vX += offsetX
points[i].X = vX
posY, offsetY = readContourPoint(p.Flag, dataY, posY, yShortVector, yIsSameOrPositiveYShortVector)
vY += offsetY
points[i].Y = vY
}
}
type CompositeGlyph struct {
Glyphs []CompositeGlyphPart `isOpaque:""`
Instructions []byte `isOpaque:""`
}
const arg1And2AreWords = 1
func (cg *CompositeGlyph) parseGlyphs(src []byte) error {
const (
_ = 1 << iota
_
_
weHaveAScale
_
moreComponents
weHaveAnXAndYScale
weHaveATwoByTwo
weHaveInstructions
)
var flags uint16
for do := true; do; do = flags&moreComponents != 0 {
var part CompositeGlyphPart
if L := len(src); L < 4 {
return fmt.Errorf("EOF: expected length: %d, got %d", 4, L)
}
flags = binary.BigEndian.Uint16(src)
part.Flags = flags
part.GlyphIndex = GlyphID(binary.BigEndian.Uint16(src[2:]))
if flags&arg1And2AreWords != 0 { // 16 bits
if L, E := len(src), 4+4; L < E {
return fmt.Errorf("EOF: expected length: %d, got %d", E, L)
}
part.arg1 = binary.BigEndian.Uint16(src[4:])
part.arg2 = binary.BigEndian.Uint16(src[6:])
src = src[8:]
} else {
if L, E := len(src), 4+2; L < E {
return fmt.Errorf("EOF: expected length: %d, got %d", E, L)
}
part.arg1 = uint16(src[4])
part.arg2 = uint16(src[5])
src = src[6:]
}
part.Scale[0], part.Scale[3] = 1, 1
if flags&weHaveAScale != 0 {
if L := len(src); L < 2 {
return fmt.Errorf("EOF: expected length: %d, got %d", 2, L)
}
part.Scale[0] = Float214FromUint(binary.BigEndian.Uint16(src))
part.Scale[3] = part.Scale[0]
src = src[2:]
} else if flags&weHaveAnXAndYScale != 0 {
if L := len(src); L < 4 {
return fmt.Errorf("EOF: expected length: %d, got %d", 4, L)
}
part.Scale[0] = Float214FromUint(binary.BigEndian.Uint16(src))
part.Scale[3] = Float214FromUint(binary.BigEndian.Uint16(src[2:]))
src = src[4:]
} else if flags&weHaveATwoByTwo != 0 {
if L := len(src); L < 8 {
return fmt.Errorf("EOF: expected length: %d, got %d", 8, L)
}
part.Scale[0] = Float214FromUint(binary.BigEndian.Uint16(src))
part.Scale[1] = Float214FromUint(binary.BigEndian.Uint16(src[2:]))
part.Scale[2] = Float214FromUint(binary.BigEndian.Uint16(src[4:]))
part.Scale[3] = Float214FromUint(binary.BigEndian.Uint16(src[6:]))
src = src[8:]
}
cg.Glyphs = append(cg.Glyphs, part)
}
if flags&weHaveInstructions != 0 {
if L := len(src); L < 2 {
return fmt.Errorf("EOF: expected length: 2, got %d", L)
}
E := int(binary.BigEndian.Uint16(src))
if L := len(src); L < E {
return fmt.Errorf("EOF: expected length: %d, got %d", E, len(src))
}
cg.Instructions = src[0:E]
}
return nil
}
// already handled in parseGlyphs
func (cg *CompositeGlyph) parseInstructions(src []byte) error { return nil }
type CompositeGlyphPart struct {
Flags uint16
GlyphIndex GlyphID
// raw value before interpretation:
// arg1 and arg2 may be either :
// - unsigned, when used as indices into the contour point list
// (see ArgsAsIndices)
// - signed, when used as translation in the transformation matrix
// (see ArgsAsTranslation)
arg1, arg2 uint16
// Scale is a matrix x, 01, 10, y ; default to identity
Scale [4]float32
}
func (c *CompositeGlyphPart) HasUseMyMetrics() bool {
const useMyMetrics = 0x0200
return c.Flags&useMyMetrics != 0
}
// return true if arg1 and arg2 indicated an anchor point,
// not offsets
func (c *CompositeGlyphPart) IsAnchored() bool {
const argsAreXyValues = 0x0002
return c.Flags&argsAreXyValues == 0
}
func (c *CompositeGlyphPart) IsScaledOffsets() bool {
const (
scaledComponentOffset = 0x0800
unscaledComponentOffset = 0x1000
)
return c.Flags&(scaledComponentOffset|unscaledComponentOffset) == scaledComponentOffset
}
func (c *CompositeGlyphPart) ArgsAsTranslation() (int16, int16) {
// arg1 and arg2 are interpreted as signed integers here
// the conversion depends on the original size (8 or 16 bits)
if c.Flags&arg1And2AreWords != 0 {
return int16(c.arg1), int16(c.arg2)
}
return int16(int8(uint8(c.arg1))), int16(int8(uint8(c.arg2)))
}
func (c *CompositeGlyphPart) ArgsAsIndices() (int, int) {
// arg1 and arg2 are interpreted as unsigned integers here
return int(c.arg1), int(c.arg2)
}