-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathparse_collection.go
151 lines (135 loc) · 4.34 KB
/
parse_collection.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
package truetype
import (
"encoding/binary"
"errors"
"fmt"
"io"
"github.com/benoitkugler/textlayout/fonts"
)
const maxNumFonts = 1024 // security implementation limit
// returns the offsets of each font
func parseTTCHeader(r io.Reader) ([]uint32, error) {
// The https://www.microsoft.com/typography/otspec/otff.htm "Font
// Collections" section describes the TTC header.
var buf [12]byte
if _, err := r.Read(buf[:]); err != nil {
return nil, err
}
// skip versions
numFonts := binary.BigEndian.Uint32(buf[8:])
if numFonts == 0 {
return nil, errors.New("empty font collection")
}
if numFonts > maxNumFonts {
return nil, fmt.Errorf("number of fonts (%d) in collection exceed implementation limit (%d)",
numFonts, maxNumFonts)
}
offsetsBytes := make([]byte, numFonts*4)
_, err := io.ReadFull(r, offsetsBytes)
if err != nil {
return nil, err
}
return parseUint32s(offsetsBytes, int(numFonts)), nil
}
// parseDfont parses a dfont resource map, as per
// https://github.com/kreativekorp/ksfl/wiki/Macintosh-Resource-File-Format
//
// That unofficial wiki page lists all of its fields as *signed* integers,
// which looks unusual. The actual file format might use *unsigned* integers in
// various places, but until we have either an official specification or an
// actual dfont file where this matters, we'll use signed integers and treat
// negative values as invalid.
func parseDfont(r fonts.Resource) ([]uint32, error) {
var buf [16]byte
if _, err := r.Read(buf[:]); err != nil {
return nil, err
}
resourceMapOffset := binary.BigEndian.Uint32(buf[4:])
resourceMapLength := binary.BigEndian.Uint32(buf[12:])
const (
// (maxTableOffset + maxTableLength) will not overflow an int32.
maxTableLength = 1 << 29
maxTableOffset = 1 << 29
)
if resourceMapOffset > maxTableOffset || resourceMapLength > maxTableLength {
return nil, errUnsupportedTableOffsetLength
}
const headerSize = 28
if resourceMapLength < headerSize {
return nil, errInvalidDfont
}
_, err := r.ReadAt(buf[:2], int64(resourceMapOffset+24))
if err != nil {
return nil, err
}
typeListOffset := int64(int16(binary.BigEndian.Uint16(buf[:])))
if typeListOffset < headerSize || resourceMapLength < uint32(typeListOffset)+2 {
return nil, errInvalidDfont
}
_, err = r.ReadAt(buf[:2], int64(resourceMapOffset)+typeListOffset)
if err != nil {
return nil, err
}
typeCount := int(binary.BigEndian.Uint16(buf[:])) // The number of types, minus one.
if typeCount == 0xFFFF {
return nil, errInvalidDfont
}
typeCount += 1
const tSize = 8
if tSize*uint32(typeCount) > resourceMapLength-uint32(typeListOffset)-2 {
return nil, errInvalidDfont
}
typeList := make([]byte, tSize*typeCount)
_, err = r.ReadAt(typeList, int64(resourceMapOffset)+typeListOffset+2)
if err != nil {
return nil, err
}
numFonts, resourceListOffset := 0, 0
for i := 0; i < typeCount; i++ {
if binary.BigEndian.Uint32(typeList[tSize*i:]) != 0x73666e74 { // "sfnt".
continue
}
numFonts = int(int16(binary.BigEndian.Uint16(typeList[tSize*i+4:])))
if numFonts < 0 {
return nil, errInvalidDfont
}
// https://github.com/kreativekorp/ksfl/wiki/Macintosh-Resource-File-Format
// says that the value in the wire format is "the number of
// resources of this type, minus one."
numFonts++
resourceListOffset = int(int16(binary.BigEndian.Uint16((typeList[tSize*i+6:]))))
if resourceListOffset < 0 {
return nil, errInvalidDfont
}
}
if numFonts == 0 {
return nil, errInvalidDfont
}
if numFonts > maxNumFonts {
return nil, fmt.Errorf("number of fonts (%d) in collection exceed implementation limit (%d)",
numFonts, maxNumFonts)
}
const rSize = 12
o, n := uint32(int(typeListOffset)+resourceListOffset), rSize*uint32(numFonts)
if o > resourceMapLength || n > resourceMapLength-o {
return nil, errInvalidDfont
}
offsetsBytes := make([]byte, n)
_, err = r.ReadAt(offsetsBytes, int64(resourceMapOffset+o))
if err != nil {
return nil, err
}
offsets := make([]uint32, numFonts)
for i := range offsets {
o := 0xffffff & binary.BigEndian.Uint32(offsetsBytes[rSize*i+4:])
// Offsets are relative to the resource data start, not the file start.
// A particular resource's data also starts with a 4-byte length, which
// we skip.
o += dfontResourceDataOffset + 4
if o > maxTableOffset {
return nil, errUnsupportedTableOffsetLength
}
offsets[i] = o
}
return offsets, nil
}