-
Notifications
You must be signed in to change notification settings - Fork 7
/
aat_table_trak.go
146 lines (125 loc) · 3.71 KB
/
aat_table_trak.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
package truetype
import (
"encoding/binary"
"errors"
)
type TableTrak struct {
Horizontal, Vertical TrakData // may be empty
}
// IsEmpty return `true` it the table has no entries.
func (t TableTrak) IsEmpty() bool {
return len(t.Horizontal.Entries)+len(t.Vertical.Entries) == 0
}
func parseTrakTable(data []byte) (out TableTrak, err error) {
if len(data) < 12 {
return out, errors.New("invalid trak table (EOF)")
}
// ignoring version and format
horizOffset := binary.BigEndian.Uint16(data[6:])
vertOffset := binary.BigEndian.Uint16(data[6:])
if horizOffset != 0 {
out.Horizontal, err = parseTrakData(data, int(horizOffset))
if err != nil {
return out, err
}
}
if vertOffset != 0 {
out.Vertical, err = parseTrakData(data, int(vertOffset))
if err != nil {
return out, err
}
}
return out, nil
}
type TrackEntry struct {
PerSizeTracking []int16 // in font units, with length len(Sizes)
Track float32
NameIndex NameID
}
type TrakData struct {
Entries []TrackEntry
Sizes []float32
}
// idx is assumed to verify idx <= len(Sizes) - 2
func (td TrakData) interpolateAt(idx int, targetSize float32, trackSizes []int16) float32 {
s0 := td.Sizes[idx]
s1 := td.Sizes[idx+1]
var t float32
if s0 != s1 {
t = (targetSize - s0) / (s1 - s0)
}
return t*float32(trackSizes[idx+1]) + (1.-t)*float32(trackSizes[idx])
}
// GetTracking select the tracking for the given `trackValue` and apply it
// for `ptem`. It returns 0 if not found.
func (td TrakData) GetTracking(ptem float32, trackValue float32) float32 {
// Choose track.
var trackTableEntry *TrackEntry
for i := range td.Entries {
/* Note: Seems like the track entries are sorted by values. But the
* spec doesn't explicitly say that. It just mentions it in the example. */
if td.Entries[i].Track == trackValue {
trackTableEntry = &td.Entries[i]
break
}
}
if trackTableEntry == nil {
return 0.
}
// Choose size.
if len(td.Sizes) == 0 {
return 0.
}
if len(td.Sizes) == 1 {
return float32(trackTableEntry.PerSizeTracking[0])
}
var sizeIndex int
for sizeIndex = range td.Sizes {
if td.Sizes[sizeIndex] >= ptem {
break
}
}
if sizeIndex != 0 {
sizeIndex = sizeIndex - 1
}
return td.interpolateAt(sizeIndex, ptem, trackTableEntry.PerSizeTracking)
}
func parseTrakData(data []byte, offset int) (out TrakData, err error) {
if len(data) < int(offset)+8 {
return out, errors.New("invalid trak data table (EOF)")
}
nTracks := binary.BigEndian.Uint16(data[offset:])
nSizes := binary.BigEndian.Uint16(data[offset+2:])
sizeTableOffset := int(binary.BigEndian.Uint32(data[offset+4:]))
if len(data) < offset+8+8*int(nTracks) {
return out, errors.New("invalid trak data table (EOF)")
}
out.Entries = make([]TrackEntry, nTracks)
for i := range out.Entries {
out.Entries[i].Track = fixed1616ToFloat(binary.BigEndian.Uint32(data[offset+8+i*8:]))
out.Entries[i].NameIndex = NameID(binary.BigEndian.Uint16(data[offset+8+i*8+4:]))
sizeOffset := binary.BigEndian.Uint16(data[offset+8+i*8+6:])
out.Entries[i].PerSizeTracking, err = parseTrackSizes(data, int(sizeOffset), nSizes)
if err != nil {
return out, err
}
}
if len(data) < sizeTableOffset+4*int(nSizes) {
return out, errors.New("invalid trak data table (EOF)")
}
out.Sizes = make([]float32, nSizes)
for i := range out.Sizes {
out.Sizes[i] = fixed1616ToFloat(binary.BigEndian.Uint32(data[sizeTableOffset+4*i:]))
}
return out, nil
}
func parseTrackSizes(data []byte, offset int, count uint16) ([]int16, error) {
if len(data) < offset+int(count)*2 {
return nil, errors.New("invalid trak table per-sizes values (EOF)")
}
out := make([]int16, count)
for i := range out {
out[i] = int16(binary.BigEndian.Uint16(data[offset+2*i:]))
}
return out, nil
}