-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathtable_glyf.go
452 lines (396 loc) · 12.4 KB
/
table_glyf.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
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
package truetype
import (
"encoding/binary"
"errors"
"fmt"
"sort"
"github.com/benoitkugler/textlayout/fonts"
)
const maxCompositeNesting = 20 // protect against malicious fonts
type TableGlyf []GlyphData // length numGlyphs
// shared with gvar, sbix, eblc
// return an error only if data is not long enough
func parseTableLoca(data []byte, numGlyphs int, isLong bool) (out []uint32, err error) {
var size int
if isLong {
size = (numGlyphs + 1) * 4
} else {
size = (numGlyphs + 1) * 2
}
if len(data) < size {
return nil, errors.New("invalid location table (EOF)")
}
if isLong {
out = parseUint32s(data, numGlyphs+1)
} else {
out = make([]uint32, numGlyphs+1)
for i := range out {
out[i] = 2 * uint32(binary.BigEndian.Uint16(data[2*i:])) // The actual local offset divided by 2 is stored.
}
}
return out, nil
}
// locaOffsets has length numGlyphs + 1
func parseTableGlyf(data []byte, locaOffsets []uint32) (TableGlyf, error) {
out := make(TableGlyf, len(locaOffsets)-1)
var err error
for i := range out {
// If a glyph has no outline, then loca[n] = loca [n+1].
if locaOffsets[i] == locaOffsets[i+1] {
continue
}
out[i], err = parseGlyphData(data, locaOffsets[i])
if err != nil {
return nil, err
}
}
return out, nil
}
type contourPoint struct {
fonts.SegmentPoint
isOnCurve bool
isEndPoint bool // this point is the last of the current contour
isExplicit bool // this point is referenced, i.e., explicit deltas specified */
}
func (c *contourPoint) translate(x, y float32) {
c.X += x
c.Y += y
}
func (c *contourPoint) transform(matrix [4]float32) {
px := c.X*matrix[0] + c.Y*matrix[2]
c.Y = c.X*matrix[1] + c.Y*matrix[3]
c.X = px
}
type GlyphData struct {
data interface{ isGlyphData() } // nil for absent glyphs
Xmin, Ymin, Xmax, Ymax int16
}
func (simpleGlyphData) isGlyphData() {}
func (compositeGlyphData) isGlyphData() {}
// does not includes phantom points
func (g GlyphData) pointNumbersCount() int {
switch g := g.data.(type) {
case simpleGlyphData:
return len(g.points)
case compositeGlyphData:
/* pseudo component points for each component in composite glyph */
return len(g.glyphs)
}
return 0
}
func (g GlyphData) getExtents(metrics TableHVmtx, gid GID) fonts.GlyphExtents {
var extents fonts.GlyphExtents
/* Undocumented rasterizer behavior: shift glyph to the left by (lsb - xMin), i.e., xMin = lsb */
/* extents.XBearing = hb_min (glyph_header.xMin, glyph_header.xMax); */
if int(gid) < len(metrics) {
extents.XBearing = float32(metrics[gid].SideBearing)
}
extents.YBearing = float32(max16(g.Ymin, g.Ymax))
extents.Width = float32(max16(g.Xmin, g.Xmax) - min16(g.Xmin, g.Xmax))
extents.Height = float32(min16(g.Ymin, g.Ymax) - max16(g.Ymin, g.Ymax))
return extents
}
func parseGlyphData(data []byte, offset uint32) (out GlyphData, err error) {
if len(data) < int(offset)+10 {
return out, errors.New("invalid 'glyf' table (EOF)")
}
data = data[offset:]
numberOfContours := int(int16(binary.BigEndian.Uint16(data))) // careful with the conversion to signed integer
out.Xmin = int16(binary.BigEndian.Uint16(data[2:]))
out.Ymin = int16(binary.BigEndian.Uint16(data[4:]))
out.Xmax = int16(binary.BigEndian.Uint16(data[6:]))
out.Ymax = int16(binary.BigEndian.Uint16(data[8:]))
if numberOfContours >= 0 { // simple glyph
out.data, err = parseSimpleGlyphData(data[10:], numberOfContours)
} else { // composite glyph
out.data, err = parseCompositeGlyphData(data[10:])
}
return out, err
}
type glyphContourPoint struct {
flag uint8
x, y int16
}
const overlapSimple = 0x40
type simpleGlyphData struct {
// valid indexes in `points` after parsing, one
// for each contour
endPtsOfContours []uint16
instructions []byte
// all the points
points []glyphContourPoint
}
// return all the contour points, without phantoms
func (sg simpleGlyphData) getContourPoints() []contourPoint {
points := make([]contourPoint, len(sg.points))
for _, end := range sg.endPtsOfContours {
points[end].isEndPoint = true
}
for i, p := range sg.points {
points[i].X, points[i].Y = float32(p.x), float32(p.y)
points[i].isOnCurve = p.flag&flagOnCurve != 0
}
return points
}
// 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
}
const (
flagOnCurve = 1 << 0 // 0x0001
xShortVector = 0x02
xIsSameOrPositiveXShortVector = 0x10
yShortVector = 0x04
yIsSameOrPositiveYShortVector = 0x20
)
// 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
}
}
// data starts after the glyph header
func parseSimpleGlyphData(data []byte, numberOfContours int) (out simpleGlyphData, err error) {
out.endPtsOfContours, err = parseUint16s(data, numberOfContours)
if err != nil {
return out, fmt.Errorf("invalid simple glyph data: %s", err)
}
if !sort.SliceIsSorted(out.endPtsOfContours, func(i, j int) bool {
return out.endPtsOfContours[i] < out.endPtsOfContours[j]
}) {
return out, errors.New("invalid simple glyph data end points")
}
out.instructions, data, err = parseGlyphInstruction(data[2*numberOfContours:])
if err != nil {
return out, fmt.Errorf("invalid simple glyph data: %s", err)
}
if len(out.endPtsOfContours) == 0 {
return out, nil
}
numPoints := int(out.endPtsOfContours[len(out.endPtsOfContours)-1]) + 1
const repeatFlag = 0x08
out.points = make([]glyphContourPoint, numPoints)
// read flags
// to avoid costly length check, we also precompute the expected data size for coordinates
var coordinatesLengthX, coordinatesLengthY int
for i := 0; i < numPoints; i++ {
if len(data) == 0 {
return out, errors.New("invalid simple glyph data flags (EOF)")
}
flag := data[0]
out.points[i].flag = flag
data = data[1:]
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 len(data) == 0 {
return out, errors.New("invalid simple glyph data flags (EOF)")
}
repeatCount := int(data[0])
data = data[1:]
if i+repeatCount+1 > numPoints { // gracefully handle out of bounds
repeatCount = numPoints - i - 1
}
subSlice := out.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
}
if coordinatesLengthX+coordinatesLengthY > len(data) {
return out, errors.New("invalid simple glyph data points (EOF)")
}
dataX, dataY := data[:coordinatesLengthX], data[coordinatesLengthX:coordinatesLengthX+coordinatesLengthY]
// read x and y coordinates
parseGlyphContourPoints(dataX, dataY, out.points)
return out, nil
}
type compositeGlyphData struct {
glyphs []compositeGlyphPart
instructions []byte
}
type compositeGlyphPart struct {
flags uint16
glyphIndex GID
// 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 [4]float32 // x, 01, 10, y ; default to identity
}
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
}
const arg1And2AreWords = 1
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)
}
func (c *compositeGlyphPart) transformPoints(points []contourPoint) {
var transX, transY float32
if !c.isAnchored() {
arg1, arg2 := c.argsAsTranslation()
transX, transY = float32(arg1), float32(arg2)
}
scale := c.scale
// shortcut identity transform
if transX == 0 && transY == 0 && scale == [4]float32{1, 0, 0, 1} {
return
}
if c.isScaledOffsets() {
for i := range points {
points[i].translate(transX, transY)
points[i].transform(scale)
}
} else {
for i := range points {
points[i].transform(scale)
points[i].translate(transX, transY)
}
}
}
// data starts after the glyph header
func parseCompositeGlyphData(data []byte) (out compositeGlyphData, err error) {
const (
_ = 1 << iota
_
_
weHaveAScale
_
moreComponents
weHaveAnXAndYScale
weHaveATwoByTwo
weHaveInstructions
)
var flags uint16
for do := true; do; do = flags&moreComponents != 0 {
var part compositeGlyphPart
if len(data) < 4 {
return out, errors.New("invalid composite glyph data (EOF)")
}
flags = binary.BigEndian.Uint16(data)
part.flags = flags
part.glyphIndex = GID(binary.BigEndian.Uint16(data[2:]))
if flags&arg1And2AreWords != 0 { // 16 bits
if len(data) < 4+4 {
return out, errors.New("invalid composite glyph data (EOF)")
}
part.arg1 = binary.BigEndian.Uint16(data[4:])
part.arg2 = binary.BigEndian.Uint16(data[6:])
data = data[8:]
} else {
if len(data) < 4+2 {
return out, errors.New("invalid composite glyph data (EOF)")
}
part.arg1 = uint16(data[4])
part.arg2 = uint16(data[5])
data = data[6:]
}
part.scale[0], part.scale[3] = 1, 1
if flags&weHaveAScale != 0 {
if len(data) < 2 {
return out, errors.New("invalid composite glyph data (EOF)")
}
part.scale[0] = fixed214ToFloat(binary.BigEndian.Uint16(data))
part.scale[3] = part.scale[0]
data = data[2:]
} else if flags&weHaveAnXAndYScale != 0 {
if len(data) < 4 {
return out, errors.New("invalid composite glyph data (EOF)")
}
part.scale[0] = fixed214ToFloat(binary.BigEndian.Uint16(data))
part.scale[3] = fixed214ToFloat(binary.BigEndian.Uint16(data[2:]))
data = data[4:]
} else if flags&weHaveATwoByTwo != 0 {
if len(data) < 8 {
return out, errors.New("invalid composite glyph data (EOF)")
}
part.scale[0] = fixed214ToFloat(binary.BigEndian.Uint16(data))
part.scale[1] = fixed214ToFloat(binary.BigEndian.Uint16(data[2:]))
part.scale[2] = fixed214ToFloat(binary.BigEndian.Uint16(data[4:]))
part.scale[3] = fixed214ToFloat(binary.BigEndian.Uint16(data[6:]))
data = data[8:]
}
out.glyphs = append(out.glyphs, part)
}
if flags&weHaveInstructions != 0 {
out.instructions, _, err = parseGlyphInstruction(data)
if err != nil {
return out, fmt.Errorf("invalid composite glyph data: %s", err)
}
}
return out, nil
}
func parseGlyphInstruction(data []byte) ([]byte, []byte, error) {
if len(data) < 2 {
return nil, nil, errors.New("invalid glyph instructions (EOF)")
}
instructionLength := int(binary.BigEndian.Uint16(data))
if len(data) < 2+instructionLength {
return nil, nil, errors.New("invalid glyph instructions (EOF)")
}
return data[2 : 2+instructionLength], data[2+instructionLength:], nil
}