| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259 |
- // Copyright 2017 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package sfnt
- import (
- "golang.org/x/text/encoding/charmap"
- )
- // Platform IDs and Platform Specific IDs as per
- // https://www.microsoft.com/typography/otspec/name.htm
- const (
- pidUnicode = 0
- pidMacintosh = 1
- pidWindows = 3
- psidUnicode2BMPOnly = 3
- psidUnicode2FullRepertoire = 4
- // Note that FontForge may generate a bogus Platform Specific ID (value 10)
- // for the Unicode Platform ID (value 0). See
- // https://github.com/fontforge/fontforge/issues/2728
- psidMacintoshRoman = 0
- psidWindowsSymbol = 0
- psidWindowsUCS2 = 1
- psidWindowsUCS4 = 10
- )
- // platformEncodingWidth returns the number of bytes per character assumed by
- // the given Platform ID and Platform Specific ID.
- //
- // Very old fonts, from before Unicode was widely adopted, assume only 1 byte
- // per character: a character map.
- //
- // Old fonts, from when Unicode meant the Basic Multilingual Plane (BMP),
- // assume that 2 bytes per character is sufficient.
- //
- // Recent fonts naturally support the full range of Unicode code points, which
- // can take up to 4 bytes per character. Such fonts might still choose one of
- // the legacy encodings if e.g. their repertoire is limited to the BMP, for
- // greater compatibility with older software, or because the resultant file
- // size can be smaller.
- func platformEncodingWidth(pid, psid uint16) int {
- switch pid {
- case pidUnicode:
- switch psid {
- case psidUnicode2BMPOnly:
- return 2
- case psidUnicode2FullRepertoire:
- return 4
- }
- case pidMacintosh:
- switch psid {
- case psidMacintoshRoman:
- return 1
- }
- case pidWindows:
- switch psid {
- case psidWindowsSymbol:
- return 2
- case psidWindowsUCS2:
- return 2
- case psidWindowsUCS4:
- return 4
- }
- }
- return 0
- }
- // The various cmap formats are described at
- // https://www.microsoft.com/typography/otspec/cmap.htm
- var supportedCmapFormat = func(format, pid, psid uint16) bool {
- switch format {
- case 0:
- return pid == pidMacintosh && psid == psidMacintoshRoman
- case 4:
- return true
- case 12:
- return true
- }
- return false
- }
- func (f *Font) makeCachedGlyphIndex(buf []byte, offset, length uint32, format uint16) ([]byte, glyphIndexFunc, error) {
- switch format {
- case 0:
- return f.makeCachedGlyphIndexFormat0(buf, offset, length)
- case 4:
- return f.makeCachedGlyphIndexFormat4(buf, offset, length)
- case 12:
- return f.makeCachedGlyphIndexFormat12(buf, offset, length)
- }
- panic("unreachable")
- }
- func (f *Font) makeCachedGlyphIndexFormat0(buf []byte, offset, length uint32) ([]byte, glyphIndexFunc, error) {
- if length != 6+256 || offset+length > f.cmap.length {
- return nil, nil, errInvalidCmapTable
- }
- var err error
- buf, err = f.src.view(buf, int(f.cmap.offset+offset), int(length))
- if err != nil {
- return nil, nil, err
- }
- var table [256]byte
- copy(table[:], buf[6:])
- return buf, func(f *Font, b *Buffer, r rune) (GlyphIndex, error) {
- x, ok := charmap.Macintosh.EncodeRune(r)
- if !ok {
- // The source rune r is not representable in the Macintosh-Roman encoding.
- return 0, nil
- }
- return GlyphIndex(table[x]), nil
- }, nil
- }
- func (f *Font) makeCachedGlyphIndexFormat4(buf []byte, offset, length uint32) ([]byte, glyphIndexFunc, error) {
- const headerSize = 14
- if offset+headerSize > f.cmap.length {
- return nil, nil, errInvalidCmapTable
- }
- var err error
- buf, err = f.src.view(buf, int(f.cmap.offset+offset), headerSize)
- if err != nil {
- return nil, nil, err
- }
- offset += headerSize
- segCount := u16(buf[6:])
- if segCount&1 != 0 {
- return nil, nil, errInvalidCmapTable
- }
- segCount /= 2
- if segCount > maxCmapSegments {
- return nil, nil, errUnsupportedNumberOfCmapSegments
- }
- eLength := 8*uint32(segCount) + 2
- if offset+eLength > f.cmap.length {
- return nil, nil, errInvalidCmapTable
- }
- buf, err = f.src.view(buf, int(f.cmap.offset+offset), int(eLength))
- if err != nil {
- return nil, nil, err
- }
- offset += eLength
- entries := make([]cmapEntry16, segCount)
- for i := range entries {
- entries[i] = cmapEntry16{
- end: u16(buf[0*len(entries)+0+2*i:]),
- start: u16(buf[2*len(entries)+2+2*i:]),
- delta: u16(buf[4*len(entries)+2+2*i:]),
- offset: u16(buf[6*len(entries)+2+2*i:]),
- }
- }
- indexesBase := f.cmap.offset + offset
- indexesLength := f.cmap.length - offset
- return buf, func(f *Font, b *Buffer, r rune) (GlyphIndex, error) {
- if uint32(r) > 0xffff {
- return 0, nil
- }
- c := uint16(r)
- for i, j := 0, len(entries); i < j; {
- h := i + (j-i)/2
- entry := &entries[h]
- if c < entry.start {
- j = h
- } else if entry.end < c {
- i = h + 1
- } else if entry.offset == 0 {
- return GlyphIndex(c + entry.delta), nil
- } else {
- offset := uint32(entry.offset) + 2*uint32(h-len(entries)+int(c-entry.start))
- if offset > indexesLength || offset+2 > indexesLength {
- return 0, errInvalidCmapTable
- }
- x, err := b.view(&f.src, int(indexesBase+offset), 2)
- if err != nil {
- return 0, err
- }
- return GlyphIndex(u16(x)), nil
- }
- }
- return 0, nil
- }, nil
- }
- func (f *Font) makeCachedGlyphIndexFormat12(buf []byte, offset, _ uint32) ([]byte, glyphIndexFunc, error) {
- const headerSize = 16
- if offset+headerSize > f.cmap.length {
- return nil, nil, errInvalidCmapTable
- }
- var err error
- buf, err = f.src.view(buf, int(f.cmap.offset+offset), headerSize)
- if err != nil {
- return nil, nil, err
- }
- length := u32(buf[4:])
- if f.cmap.length < offset || length > f.cmap.length-offset {
- return nil, nil, errInvalidCmapTable
- }
- offset += headerSize
- numGroups := u32(buf[12:])
- if numGroups > maxCmapSegments {
- return nil, nil, errUnsupportedNumberOfCmapSegments
- }
- eLength := 12 * numGroups
- if headerSize+eLength != length {
- return nil, nil, errInvalidCmapTable
- }
- buf, err = f.src.view(buf, int(f.cmap.offset+offset), int(eLength))
- if err != nil {
- return nil, nil, err
- }
- offset += eLength
- entries := make([]cmapEntry32, numGroups)
- for i := range entries {
- entries[i] = cmapEntry32{
- start: u32(buf[0+12*i:]),
- end: u32(buf[4+12*i:]),
- delta: u32(buf[8+12*i:]),
- }
- }
- return buf, func(f *Font, b *Buffer, r rune) (GlyphIndex, error) {
- c := uint32(r)
- for i, j := 0, len(entries); i < j; {
- h := i + (j-i)/2
- entry := &entries[h]
- if c < entry.start {
- j = h
- } else if entry.end < c {
- i = h + 1
- } else {
- return GlyphIndex(c - entry.start + entry.delta), nil
- }
- }
- return 0, nil
- }, nil
- }
- type cmapEntry16 struct {
- end, start, delta, offset uint16
- }
- type cmapEntry32 struct {
- start, end, delta uint32
- }
|