git.lucas.co / go_mono
git clone https://git.lucas.co/go_mono.git

font/sfnt/cmap.go (7.6K)

  1 // Copyright 2017 The Go Authors. All rights reserved.
  2 // Use of this source code is governed by a BSD-style
  3 // license that can be found in the LICENSE file.
  4 
  5 package sfnt
  6 
  7 import (
  8 	"golang.org/x/text/encoding/charmap"
  9 )
 10 
 11 // Platform IDs and Platform Specific IDs as per
 12 // https://www.microsoft.com/typography/otspec/name.htm
 13 const (
 14 	pidUnicode   = 0
 15 	pidMacintosh = 1
 16 	pidWindows   = 3
 17 
 18 	psidUnicode2BMPOnly        = 3
 19 	psidUnicode2FullRepertoire = 4
 20 	// Note that FontForge may generate a bogus Platform Specific ID (value 10)
 21 	// for the Unicode Platform ID (value 0). See
 22 	// https://github.com/fontforge/fontforge/issues/2728
 23 
 24 	psidMacintoshRoman = 0
 25 
 26 	psidWindowsSymbol = 0
 27 	psidWindowsUCS2   = 1
 28 	psidWindowsUCS4   = 10
 29 )
 30 
 31 // platformEncodingWidth returns the number of bytes per character assumed by
 32 // the given Platform ID and Platform Specific ID.
 33 //
 34 // Very old fonts, from before Unicode was widely adopted, assume only 1 byte
 35 // per character: a character map.
 36 //
 37 // Old fonts, from when Unicode meant the Basic Multilingual Plane (BMP),
 38 // assume that 2 bytes per character is sufficient.
 39 //
 40 // Recent fonts naturally support the full range of Unicode code points, which
 41 // can take up to 4 bytes per character. Such fonts might still choose one of
 42 // the legacy encodings if e.g. their repertoire is limited to the BMP, for
 43 // greater compatibility with older software, or because the resultant file
 44 // size can be smaller.
 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 // The various cmap formats are described at
 75 // https://www.microsoft.com/typography/otspec/cmap.htm
 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 			// The source rune r is not representable in the Macintosh-Roman encoding.
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 }