...

Source file src/golang.org/x/image/font/sfnt/cmap.go

Documentation: golang.org/x/image/font/sfnt

     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  }
   313  

View as plain text