1
2
3
4
5 package sfnt
6
7 import (
8 "golang.org/x/text/encoding/charmap"
9 )
10
11
12
13 const (
14 pidUnicode = 0
15 pidMacintosh = 1
16 pidWindows = 3
17
18 psidUnicode2BMPOnly = 3
19 psidUnicode2FullRepertoire = 4
20
21
22
23
24 psidMacintoshRoman = 0
25
26 psidWindowsSymbol = 0
27 psidWindowsUCS2 = 1
28 psidWindowsUCS4 = 10
29 )
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 func platformEncodingWidth(pid, psid uint16) int {
46 switch pid {
47 case pidUnicode:
48 switch psid {
49 case psidUnicode2BMPOnly:
50 return 2
51 case psidUnicode2FullRepertoire:
52 return 4
53 }
54
55 case pidMacintosh:
56 switch psid {
57 case psidMacintoshRoman:
58 return 1
59 }
60
61 case pidWindows:
62 switch psid {
63 case psidWindowsSymbol:
64 return 2
65 case psidWindowsUCS2:
66 return 2
67 case psidWindowsUCS4:
68 return 4
69 }
70 }
71 return 0
72 }
73
74
75
76
77 var supportedCmapFormat = func(format, pid, psid uint16) bool {
78 switch format {
79 case 0:
80 return pid == pidMacintosh && psid == psidMacintoshRoman
81 case 4:
82 return true
83 case 6:
84 return true
85 case 12:
86 return true
87 }
88 return false
89 }
90
91 func (f *Font) makeCachedGlyphIndex(buf []byte, offset, length uint32, format uint16) ([]byte, glyphIndexFunc, error) {
92 switch format {
93 case 0:
94 return f.makeCachedGlyphIndexFormat0(buf, offset, length)
95 case 4:
96 return f.makeCachedGlyphIndexFormat4(buf, offset, length)
97 case 6:
98 return f.makeCachedGlyphIndexFormat6(buf, offset, length)
99 case 12:
100 return f.makeCachedGlyphIndexFormat12(buf, offset, length)
101 }
102 panic("unreachable")
103 }
104
105 func (f *Font) makeCachedGlyphIndexFormat0(buf []byte, offset, length uint32) ([]byte, glyphIndexFunc, error) {
106 if length != 6+256 || offset+length > f.cmap.length {
107 return nil, nil, errInvalidCmapTable
108 }
109 var err error
110 buf, err = f.src.view(buf, int(f.cmap.offset+offset), int(length))
111 if err != nil {
112 return nil, nil, err
113 }
114 var table [256]byte
115 copy(table[:], buf[6:])
116 return buf, func(f *Font, b *Buffer, r rune) (GlyphIndex, error) {
117 x, ok := charmap.Macintosh.EncodeRune(r)
118 if !ok {
119
120 return 0, nil
121 }
122 return GlyphIndex(table[x]), nil
123 }, nil
124 }
125
126 func (f *Font) makeCachedGlyphIndexFormat4(buf []byte, offset, length uint32) ([]byte, glyphIndexFunc, error) {
127 const headerSize = 14
128 if offset+headerSize > f.cmap.length {
129 return nil, nil, errInvalidCmapTable
130 }
131 var err error
132 buf, err = f.src.view(buf, int(f.cmap.offset+offset), headerSize)
133 if err != nil {
134 return nil, nil, err
135 }
136 offset += headerSize
137
138 segCount := u16(buf[6:])
139 if segCount&1 != 0 {
140 return nil, nil, errInvalidCmapTable
141 }
142 segCount /= 2
143 if segCount > maxCmapSegments {
144 return nil, nil, errUnsupportedNumberOfCmapSegments
145 }
146
147 eLength := 8*uint32(segCount) + 2
148 if offset+eLength > f.cmap.length {
149 return nil, nil, errInvalidCmapTable
150 }
151 buf, err = f.src.view(buf, int(f.cmap.offset+offset), int(eLength))
152 if err != nil {
153 return nil, nil, err
154 }
155 offset += eLength
156
157 entries := make([]cmapEntry16, segCount)
158 for i := range entries {
159 entries[i] = cmapEntry16{
160 end: u16(buf[0*len(entries)+0+2*i:]),
161 start: u16(buf[2*len(entries)+2+2*i:]),
162 delta: u16(buf[4*len(entries)+2+2*i:]),
163 offset: u16(buf[6*len(entries)+2+2*i:]),
164 }
165 }
166 indexesBase := f.cmap.offset + offset
167 indexesLength := f.cmap.length - offset
168
169 return buf, func(f *Font, b *Buffer, r rune) (GlyphIndex, error) {
170 if uint32(r) > 0xffff {
171 return 0, nil
172 }
173
174 c := uint16(r)
175 for i, j := 0, len(entries); i < j; {
176 h := i + (j-i)/2
177 entry := &entries[h]
178 if c < entry.start {
179 j = h
180 } else if entry.end < c {
181 i = h + 1
182 } else if entry.offset == 0 {
183 return GlyphIndex(c + entry.delta), nil
184 } else {
185 offset := uint32(entry.offset) + 2*uint32(h-len(entries)+int(c-entry.start))
186 if offset > indexesLength || offset+2 > indexesLength {
187 return 0, errInvalidCmapTable
188 }
189 if b == nil {
190 b = &Buffer{}
191 }
192 x, err := b.view(&f.src, int(indexesBase+offset), 2)
193 if err != nil {
194 return 0, err
195 }
196 return GlyphIndex(u16(x)), nil
197 }
198 }
199 return 0, nil
200 }, nil
201 }
202
203 func (f *Font) makeCachedGlyphIndexFormat6(buf []byte, offset, length uint32) ([]byte, glyphIndexFunc, error) {
204 const headerSize = 10
205 if offset+headerSize > f.cmap.length {
206 return nil, nil, errInvalidCmapTable
207 }
208 var err error
209 buf, err = f.src.view(buf, int(f.cmap.offset+offset), headerSize)
210 if err != nil {
211 return nil, nil, err
212 }
213 offset += headerSize
214
215 firstCode := u16(buf[6:])
216 entryCount := u16(buf[8:])
217
218 eLength := 2 * uint32(entryCount)
219 if offset+eLength > f.cmap.length {
220 return nil, nil, errInvalidCmapTable
221 }
222
223 if entryCount != 0 {
224 buf, err = f.src.view(buf, int(f.cmap.offset+offset), int(eLength))
225 if err != nil {
226 return nil, nil, err
227 }
228 offset += eLength
229 }
230
231 entries := make([]uint16, entryCount)
232 for i := range entries {
233 entries[i] = u16(buf[2*i:])
234 }
235
236 return buf, func(f *Font, b *Buffer, r rune) (GlyphIndex, error) {
237 if uint16(r) < firstCode {
238 return 0, nil
239 }
240
241 c := int(uint16(r) - firstCode)
242 if c >= len(entries) {
243 return 0, nil
244 }
245 return GlyphIndex(entries[c]), nil
246 }, nil
247 }
248
249 func (f *Font) makeCachedGlyphIndexFormat12(buf []byte, offset, _ uint32) ([]byte, glyphIndexFunc, error) {
250 const headerSize = 16
251 if offset+headerSize > f.cmap.length {
252 return nil, nil, errInvalidCmapTable
253 }
254 var err error
255 buf, err = f.src.view(buf, int(f.cmap.offset+offset), headerSize)
256 if err != nil {
257 return nil, nil, err
258 }
259 length := u32(buf[4:])
260 if f.cmap.length < offset || length > f.cmap.length-offset {
261 return nil, nil, errInvalidCmapTable
262 }
263 offset += headerSize
264
265 numGroups := u32(buf[12:])
266 if numGroups > maxCmapSegments {
267 return nil, nil, errUnsupportedNumberOfCmapSegments
268 }
269
270 eLength := 12 * numGroups
271 if headerSize+eLength != length {
272 return nil, nil, errInvalidCmapTable
273 }
274 buf, err = f.src.view(buf, int(f.cmap.offset+offset), int(eLength))
275 if err != nil {
276 return nil, nil, err
277 }
278 offset += eLength
279
280 entries := make([]cmapEntry32, numGroups)
281 for i := range entries {
282 entries[i] = cmapEntry32{
283 start: u32(buf[0+12*i:]),
284 end: u32(buf[4+12*i:]),
285 delta: u32(buf[8+12*i:]),
286 }
287 }
288
289 return buf, func(f *Font, b *Buffer, r rune) (GlyphIndex, error) {
290 c := uint32(r)
291 for i, j := 0, len(entries); i < j; {
292 h := i + (j-i)/2
293 entry := &entries[h]
294 if c < entry.start {
295 j = h
296 } else if entry.end < c {
297 i = h + 1
298 } else {
299 return GlyphIndex(c - entry.start + entry.delta), nil
300 }
301 }
302 return 0, nil
303 }, nil
304 }
305
306 type cmapEntry16 struct {
307 end, start, delta, offset uint16
308 }
309
310 type cmapEntry32 struct {
311 start, end, delta uint32
312 }
313
View as plain text