font/sfnt: implement Font Dict Select.
Change-Id: I8c463b41421a35455701847520367add4727cbe3 Reviewed-on: https://go-review.googlesource.com/38871 Reviewed-by: David Crawshaw <crawshaw@golang.org>
This commit is contained in:
parent
59d151bf14
commit
f36ba34967
|
@ -80,6 +80,44 @@ func bigEndian(b []byte) uint32 {
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fdSelect holds a CFF font's Font Dict Select data.
|
||||||
|
type fdSelect struct {
|
||||||
|
format uint8
|
||||||
|
numRanges uint16
|
||||||
|
offset int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *fdSelect) lookup(f *Font, b *Buffer, x GlyphIndex) (int, error) {
|
||||||
|
switch t.format {
|
||||||
|
case 0:
|
||||||
|
buf, err := b.view(&f.src, int(t.offset)+int(x), 1)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return int(buf[0]), nil
|
||||||
|
case 3:
|
||||||
|
lo, hi := 0, int(t.numRanges)
|
||||||
|
for lo < hi {
|
||||||
|
i := (lo + hi) / 2
|
||||||
|
buf, err := b.view(&f.src, int(t.offset)+3*i, 3+2)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
// buf holds the range [xlo, xhi).
|
||||||
|
if xlo := GlyphIndex(u16(buf[0:])); x < xlo {
|
||||||
|
hi = i
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if xhi := GlyphIndex(u16(buf[3:])); xhi <= x {
|
||||||
|
lo = i + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return int(buf[2]), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
// cffParser parses the CFF table from an SFNT font.
|
// cffParser parses the CFF table from an SFNT font.
|
||||||
type cffParser struct {
|
type cffParser struct {
|
||||||
src *source
|
src *source
|
||||||
|
@ -94,14 +132,14 @@ type cffParser struct {
|
||||||
psi psInterpreter
|
psi psInterpreter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *cffParser) parse() (locations, gsubrs, subrs []uint32, err error) {
|
func (p *cffParser) parse(numGlyphs int) (ret glyphData, err error) {
|
||||||
// Parse the header.
|
// Parse the header.
|
||||||
{
|
{
|
||||||
if !p.read(4) {
|
if !p.read(4) {
|
||||||
return nil, nil, nil, p.err
|
return glyphData{}, p.err
|
||||||
}
|
}
|
||||||
if p.buf[0] != 1 || p.buf[1] != 0 || p.buf[2] != 4 {
|
if p.buf[0] != 1 || p.buf[1] != 0 || p.buf[2] != 4 {
|
||||||
return nil, nil, nil, errUnsupportedCFFVersion
|
return glyphData{}, errUnsupportedCFFVersion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,15 +147,15 @@ func (p *cffParser) parse() (locations, gsubrs, subrs []uint32, err error) {
|
||||||
{
|
{
|
||||||
count, offSize, ok := p.parseIndexHeader()
|
count, offSize, ok := p.parseIndexHeader()
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, nil, nil, p.err
|
return glyphData{}, p.err
|
||||||
}
|
}
|
||||||
// https://www.microsoft.com/typography/OTSPEC/cff.htm says that "The
|
// https://www.microsoft.com/typography/OTSPEC/cff.htm says that "The
|
||||||
// Name INDEX in the CFF must contain only one entry".
|
// Name INDEX in the CFF must contain only one entry".
|
||||||
if count != 1 {
|
if count != 1 {
|
||||||
return nil, nil, nil, errInvalidCFFTable
|
return glyphData{}, errInvalidCFFTable
|
||||||
}
|
}
|
||||||
if !p.parseIndexLocations(p.locBuf[:2], count, offSize) {
|
if !p.parseIndexLocations(p.locBuf[:2], count, offSize) {
|
||||||
return nil, nil, nil, p.err
|
return glyphData{}, p.err
|
||||||
}
|
}
|
||||||
p.offset = int(p.locBuf[1])
|
p.offset = int(p.locBuf[1])
|
||||||
}
|
}
|
||||||
|
@ -127,21 +165,21 @@ func (p *cffParser) parse() (locations, gsubrs, subrs []uint32, err error) {
|
||||||
{
|
{
|
||||||
count, offSize, ok := p.parseIndexHeader()
|
count, offSize, ok := p.parseIndexHeader()
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, nil, nil, p.err
|
return glyphData{}, p.err
|
||||||
}
|
}
|
||||||
// 5176.CFF.pdf section 8 "Top DICT INDEX" says that the count here
|
// 5176.CFF.pdf section 8 "Top DICT INDEX" says that the count here
|
||||||
// should match the count of the Name INDEX, which is 1.
|
// should match the count of the Name INDEX, which is 1.
|
||||||
if count != 1 {
|
if count != 1 {
|
||||||
return nil, nil, nil, errInvalidCFFTable
|
return glyphData{}, errInvalidCFFTable
|
||||||
}
|
}
|
||||||
if !p.parseIndexLocations(p.locBuf[:2], count, offSize) {
|
if !p.parseIndexLocations(p.locBuf[:2], count, offSize) {
|
||||||
return nil, nil, nil, p.err
|
return glyphData{}, p.err
|
||||||
}
|
}
|
||||||
if !p.read(int(p.locBuf[1] - p.locBuf[0])) {
|
if !p.read(int(p.locBuf[1] - p.locBuf[0])) {
|
||||||
return nil, nil, nil, p.err
|
return glyphData{}, p.err
|
||||||
}
|
}
|
||||||
if p.err = p.psi.run(psContextTopDict, p.buf, 0, 0); p.err != nil {
|
if p.err = p.psi.run(psContextTopDict, p.buf, 0, 0); p.err != nil {
|
||||||
return nil, nil, nil, p.err
|
return glyphData{}, p.err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,25 +187,25 @@ func (p *cffParser) parse() (locations, gsubrs, subrs []uint32, err error) {
|
||||||
{
|
{
|
||||||
count, offSize, ok := p.parseIndexHeader()
|
count, offSize, ok := p.parseIndexHeader()
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, nil, nil, p.err
|
return glyphData{}, p.err
|
||||||
}
|
}
|
||||||
if count != 0 {
|
if count != 0 {
|
||||||
// Read the last location. Locations are off by 1 byte. See the
|
// Read the last location. Locations are off by 1 byte. See the
|
||||||
// comment in parseIndexLocations.
|
// comment in parseIndexLocations.
|
||||||
if !p.skip(int(count * offSize)) {
|
if !p.skip(int(count * offSize)) {
|
||||||
return nil, nil, nil, p.err
|
return glyphData{}, p.err
|
||||||
}
|
}
|
||||||
if !p.read(int(offSize)) {
|
if !p.read(int(offSize)) {
|
||||||
return nil, nil, nil, p.err
|
return glyphData{}, p.err
|
||||||
}
|
}
|
||||||
loc := bigEndian(p.buf) - 1
|
loc := bigEndian(p.buf) - 1
|
||||||
// Check that locations are in bounds.
|
// Check that locations are in bounds.
|
||||||
if uint32(p.end-p.offset) < loc {
|
if uint32(p.end-p.offset) < loc {
|
||||||
return nil, nil, nil, errInvalidCFFTable
|
return glyphData{}, errInvalidCFFTable
|
||||||
}
|
}
|
||||||
// Skip the index data.
|
// Skip the index data.
|
||||||
if !p.skip(int(loc)) {
|
if !p.skip(int(loc)) {
|
||||||
return nil, nil, nil, p.err
|
return glyphData{}, p.err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,80 +214,171 @@ func (p *cffParser) parse() (locations, gsubrs, subrs []uint32, err error) {
|
||||||
{
|
{
|
||||||
count, offSize, ok := p.parseIndexHeader()
|
count, offSize, ok := p.parseIndexHeader()
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, nil, nil, p.err
|
return glyphData{}, p.err
|
||||||
}
|
}
|
||||||
if count != 0 {
|
if count != 0 {
|
||||||
if count > maxNumSubroutines {
|
if count > maxNumSubroutines {
|
||||||
return nil, nil, nil, errUnsupportedNumberOfSubroutines
|
return glyphData{}, errUnsupportedNumberOfSubroutines
|
||||||
}
|
}
|
||||||
gsubrs = make([]uint32, count+1)
|
ret.gsubrs = make([]uint32, count+1)
|
||||||
if !p.parseIndexLocations(gsubrs, count, offSize) {
|
if !p.parseIndexLocations(ret.gsubrs, count, offSize) {
|
||||||
return nil, nil, nil, p.err
|
return glyphData{}, p.err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the CharStrings INDEX, whose location was found in the Top DICT.
|
// Parse the CharStrings INDEX, whose location was found in the Top DICT.
|
||||||
{
|
{
|
||||||
if p.psi.topDict.charStrings <= 0 || int32(p.end-p.base) < p.psi.topDict.charStrings {
|
if !p.seekFromBase(p.psi.topDict.charStringsOffset) {
|
||||||
return nil, nil, nil, errInvalidCFFTable
|
return glyphData{}, errInvalidCFFTable
|
||||||
}
|
}
|
||||||
p.offset = p.base + int(p.psi.topDict.charStrings)
|
|
||||||
count, offSize, ok := p.parseIndexHeader()
|
count, offSize, ok := p.parseIndexHeader()
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, nil, nil, p.err
|
return glyphData{}, p.err
|
||||||
}
|
}
|
||||||
if count == 0 {
|
if count == 0 || int(count) != numGlyphs {
|
||||||
return nil, nil, nil, errInvalidCFFTable
|
return glyphData{}, errInvalidCFFTable
|
||||||
}
|
}
|
||||||
locations = make([]uint32, count+1)
|
ret.locations = make([]uint32, count+1)
|
||||||
if !p.parseIndexLocations(locations, count, offSize) {
|
if !p.parseIndexLocations(ret.locations, count, offSize) {
|
||||||
return nil, nil, nil, p.err
|
return glyphData{}, p.err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !p.psi.topDict.isCIDFont {
|
||||||
// Parse the Private DICT, whose location was found in the Top DICT.
|
// Parse the Private DICT, whose location was found in the Top DICT.
|
||||||
|
ret.singleSubrs, err = p.parsePrivateDICT(
|
||||||
|
p.psi.topDict.privateDictOffset,
|
||||||
|
p.psi.topDict.privateDictLength,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return glyphData{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Parse the Font Dict Select data, whose location was found in the Top
|
||||||
|
// DICT.
|
||||||
|
ret.fdSelect, err = p.parseFDSelect(p.psi.topDict.fdSelect, numGlyphs)
|
||||||
|
if err != nil {
|
||||||
|
return glyphData{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the Font Dicts. Each one contains its own Private DICT.
|
||||||
|
if !p.seekFromBase(p.psi.topDict.fdArray) {
|
||||||
|
return glyphData{}, errInvalidCFFTable
|
||||||
|
}
|
||||||
|
|
||||||
|
count, offSize, ok := p.parseIndexHeader()
|
||||||
|
if !ok {
|
||||||
|
return glyphData{}, p.err
|
||||||
|
}
|
||||||
|
if count > maxNumFontDicts {
|
||||||
|
return glyphData{}, errUnsupportedNumberOfFontDicts
|
||||||
|
}
|
||||||
|
|
||||||
|
fdLocations := make([]uint32, count+1)
|
||||||
|
if !p.parseIndexLocations(fdLocations, count, offSize) {
|
||||||
|
return glyphData{}, p.err
|
||||||
|
}
|
||||||
|
|
||||||
|
privateDicts := make([]struct {
|
||||||
|
offset, length int32
|
||||||
|
}, count)
|
||||||
|
|
||||||
|
for i := range privateDicts {
|
||||||
|
length := fdLocations[i+1] - fdLocations[i]
|
||||||
|
if !p.read(int(length)) {
|
||||||
|
return glyphData{}, errInvalidCFFTable
|
||||||
|
}
|
||||||
|
p.psi.topDict.initialize()
|
||||||
|
if p.err = p.psi.run(psContextTopDict, p.buf, 0, 0); p.err != nil {
|
||||||
|
return glyphData{}, p.err
|
||||||
|
}
|
||||||
|
privateDicts[i].offset = p.psi.topDict.privateDictOffset
|
||||||
|
privateDicts[i].length = p.psi.topDict.privateDictLength
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.multiSubrs = make([][]uint32, count)
|
||||||
|
for i, pd := range privateDicts {
|
||||||
|
ret.multiSubrs[i], err = p.parsePrivateDICT(pd.offset, pd.length)
|
||||||
|
if err != nil {
|
||||||
|
return glyphData{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseFDSelect parses the Font Dict Select data as per 5176.CFF.pdf section
|
||||||
|
// 19 "FDSelect".
|
||||||
|
func (p *cffParser) parseFDSelect(offset int32, numGlyphs int) (ret fdSelect, err error) {
|
||||||
|
if !p.seekFromBase(p.psi.topDict.fdSelect) {
|
||||||
|
return fdSelect{}, errInvalidCFFTable
|
||||||
|
}
|
||||||
|
if !p.read(1) {
|
||||||
|
return fdSelect{}, p.err
|
||||||
|
}
|
||||||
|
ret.format = p.buf[0]
|
||||||
|
switch ret.format {
|
||||||
|
case 0:
|
||||||
|
if p.end-p.offset < numGlyphs {
|
||||||
|
return fdSelect{}, errInvalidCFFTable
|
||||||
|
}
|
||||||
|
ret.offset = int32(p.offset)
|
||||||
|
return ret, nil
|
||||||
|
case 3:
|
||||||
|
if !p.read(2) {
|
||||||
|
return fdSelect{}, p.err
|
||||||
|
}
|
||||||
|
ret.numRanges = u16(p.buf)
|
||||||
|
if p.end-p.offset < 3*int(ret.numRanges)+2 {
|
||||||
|
return fdSelect{}, errInvalidCFFTable
|
||||||
|
}
|
||||||
|
ret.offset = int32(p.offset)
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
return fdSelect{}, errUnsupportedCFFFDSelectTable
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *cffParser) parsePrivateDICT(offset, length int32) (subrs []uint32, err error) {
|
||||||
p.psi.privateDict.initialize()
|
p.psi.privateDict.initialize()
|
||||||
if p.psi.topDict.privateDictLength != 0 {
|
if length != 0 {
|
||||||
offset := p.psi.topDict.privateDictOffset
|
|
||||||
length := p.psi.topDict.privateDictLength
|
|
||||||
fullLength := int32(p.end - p.base)
|
fullLength := int32(p.end - p.base)
|
||||||
if offset <= 0 || fullLength < offset || fullLength-offset < length || length < 0 {
|
if offset <= 0 || fullLength < offset || fullLength-offset < length || length < 0 {
|
||||||
return nil, nil, nil, errInvalidCFFTable
|
return nil, errInvalidCFFTable
|
||||||
}
|
}
|
||||||
p.offset = p.base + int(offset)
|
p.offset = p.base + int(offset)
|
||||||
if !p.read(int(length)) {
|
if !p.read(int(length)) {
|
||||||
return nil, nil, nil, p.err
|
return nil, p.err
|
||||||
}
|
}
|
||||||
if p.err = p.psi.run(psContextPrivateDict, p.buf, 0, 0); p.err != nil {
|
if p.err = p.psi.run(psContextPrivateDict, p.buf, 0, 0); p.err != nil {
|
||||||
return nil, nil, nil, p.err
|
return nil, p.err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the Local Subrs [Subroutines] INDEX, whose location was found in
|
// Parse the Local Subrs [Subroutines] INDEX, whose location was found in
|
||||||
// the Private DICT.
|
// the Private DICT.
|
||||||
if p.psi.privateDict.subrs != 0 {
|
if p.psi.privateDict.subrsOffset != 0 {
|
||||||
offset := p.psi.topDict.privateDictOffset + p.psi.privateDict.subrs
|
if !p.seekFromBase(offset + p.psi.privateDict.subrsOffset) {
|
||||||
if offset <= 0 || int32(p.end-p.base) < offset {
|
return nil, errInvalidCFFTable
|
||||||
return nil, nil, nil, errInvalidCFFTable
|
|
||||||
}
|
}
|
||||||
p.offset = p.base + int(offset)
|
|
||||||
count, offSize, ok := p.parseIndexHeader()
|
count, offSize, ok := p.parseIndexHeader()
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, nil, nil, p.err
|
return nil, p.err
|
||||||
}
|
}
|
||||||
if count != 0 {
|
if count != 0 {
|
||||||
if count > maxNumSubroutines {
|
if count > maxNumSubroutines {
|
||||||
return nil, nil, nil, errUnsupportedNumberOfSubroutines
|
return nil, errUnsupportedNumberOfSubroutines
|
||||||
}
|
}
|
||||||
subrs = make([]uint32, count+1)
|
subrs = make([]uint32, count+1)
|
||||||
if !p.parseIndexLocations(subrs, count, offSize) {
|
if !p.parseIndexLocations(subrs, count, offSize) {
|
||||||
return nil, nil, nil, p.err
|
return nil, p.err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return locations, gsubrs, subrs, nil
|
return subrs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// read sets p.buf to view the n bytes from p.offset to p.offset+n. It also
|
// read sets p.buf to view the n bytes from p.offset to p.offset+n. It also
|
||||||
|
@ -263,11 +392,12 @@ func (p *cffParser) parse() (locations, gsubrs, subrs []uint32, err error) {
|
||||||
// maximize the opportunity to re-use p.buf's allocated memory when viewing the
|
// maximize the opportunity to re-use p.buf's allocated memory when viewing the
|
||||||
// underlying source data for subsequent read calls.
|
// underlying source data for subsequent read calls.
|
||||||
func (p *cffParser) read(n int) (ok bool) {
|
func (p *cffParser) read(n int) (ok bool) {
|
||||||
if p.end-p.offset < n {
|
if n < 0 || p.end-p.offset < n {
|
||||||
p.err = errInvalidCFFTable
|
p.err = errInvalidCFFTable
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
p.buf, p.err = p.src.view(p.buf, p.offset, n)
|
p.buf, p.err = p.src.view(p.buf, p.offset, n)
|
||||||
|
// TODO: if p.err == io.EOF, change that to a different error??
|
||||||
p.offset += n
|
p.offset += n
|
||||||
return p.err == nil
|
return p.err == nil
|
||||||
}
|
}
|
||||||
|
@ -281,6 +411,14 @@ func (p *cffParser) skip(n int) (ok bool) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *cffParser) seekFromBase(offset int32) (ok bool) {
|
||||||
|
if offset < 0 || int32(p.end-p.base) < offset {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
p.offset = p.base + int(offset)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (p *cffParser) parseIndexHeader() (count, offSize int32, ok bool) {
|
func (p *cffParser) parseIndexHeader() (count, offSize int32, ok bool) {
|
||||||
if !p.read(2) {
|
if !p.read(2) {
|
||||||
return 0, 0, false
|
return 0, 0, false
|
||||||
|
@ -367,7 +505,10 @@ const (
|
||||||
|
|
||||||
// psTopDictData contains fields specific to the Top DICT context.
|
// psTopDictData contains fields specific to the Top DICT context.
|
||||||
type psTopDictData struct {
|
type psTopDictData struct {
|
||||||
charStrings int32
|
charStringsOffset int32
|
||||||
|
fdArray int32
|
||||||
|
fdSelect int32
|
||||||
|
isCIDFont bool
|
||||||
privateDictOffset int32
|
privateDictOffset int32
|
||||||
privateDictLength int32
|
privateDictLength int32
|
||||||
}
|
}
|
||||||
|
@ -378,7 +519,7 @@ func (d *psTopDictData) initialize() {
|
||||||
|
|
||||||
// psPrivateDictData contains fields specific to the Private DICT context.
|
// psPrivateDictData contains fields specific to the Private DICT context.
|
||||||
type psPrivateDictData struct {
|
type psPrivateDictData struct {
|
||||||
subrs int32
|
subrsOffset int32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *psPrivateDictData) initialize() {
|
func (d *psPrivateDictData) initialize() {
|
||||||
|
@ -394,12 +535,18 @@ type psType2CharstringsData struct {
|
||||||
hintBits int32
|
hintBits int32
|
||||||
seenWidth bool
|
seenWidth bool
|
||||||
ended bool
|
ended bool
|
||||||
|
glyphIndex GlyphIndex
|
||||||
|
// fdSelectIndexPlusOne is the result of the Font Dict Select lookup, plus
|
||||||
|
// one. That plus one lets us use the zero value to denote either unused
|
||||||
|
// (for CFF fonts with a single Font Dict) or lazily evaluated.
|
||||||
|
fdSelectIndexPlusOne int32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *psType2CharstringsData) initialize(f *Font, b *Buffer) {
|
func (d *psType2CharstringsData) initialize(f *Font, b *Buffer, glyphIndex GlyphIndex) {
|
||||||
*d = psType2CharstringsData{
|
*d = psType2CharstringsData{
|
||||||
f: f,
|
f: f,
|
||||||
b: b,
|
b: b,
|
||||||
|
glyphIndex: glyphIndex,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -513,7 +660,7 @@ func (p *psInterpreter) parseNumber() (hasResult bool, err error) {
|
||||||
number, hasResult = int32(int16(u16(p.instructions[1:]))), true
|
number, hasResult = int32(int16(u16(p.instructions[1:]))), true
|
||||||
p.instructions = p.instructions[3:]
|
p.instructions = p.instructions[3:]
|
||||||
|
|
||||||
case b == 29 && p.ctx == psContextTopDict:
|
case b == 29 && p.ctx != psContextType2Charstring:
|
||||||
if len(p.instructions) < 5 {
|
if len(p.instructions) < 5 {
|
||||||
return true, errInvalidCFFTable
|
return true, errInvalidCFFTable
|
||||||
}
|
}
|
||||||
|
@ -651,7 +798,7 @@ var psOperators = [...][2][]psOperator{
|
||||||
15: {+1, "charset", nil},
|
15: {+1, "charset", nil},
|
||||||
16: {+1, "Encoding", nil},
|
16: {+1, "Encoding", nil},
|
||||||
17: {+1, "CharStrings", func(p *psInterpreter) error {
|
17: {+1, "CharStrings", func(p *psInterpreter) error {
|
||||||
p.topDict.charStrings = p.argStack.a[p.argStack.top-1]
|
p.topDict.charStringsOffset = p.argStack.a[p.argStack.top-1]
|
||||||
return nil
|
return nil
|
||||||
}},
|
}},
|
||||||
18: {+2, "Private", func(p *psInterpreter) error {
|
18: {+2, "Private", func(p *psInterpreter) error {
|
||||||
|
@ -674,14 +821,23 @@ var psOperators = [...][2][]psOperator{
|
||||||
21: {+1, "PostScript", nil},
|
21: {+1, "PostScript", nil},
|
||||||
22: {+1, "BaseFontName", nil},
|
22: {+1, "BaseFontName", nil},
|
||||||
23: {-2, "BaseFontBlend", nil},
|
23: {-2, "BaseFontBlend", nil},
|
||||||
30: {+3, "ROS", nil},
|
30: {+3, "ROS", func(p *psInterpreter) error {
|
||||||
|
p.topDict.isCIDFont = true
|
||||||
|
return nil
|
||||||
|
}},
|
||||||
31: {+1, "CIDFontVersion", nil},
|
31: {+1, "CIDFontVersion", nil},
|
||||||
32: {+1, "CIDFontRevision", nil},
|
32: {+1, "CIDFontRevision", nil},
|
||||||
33: {+1, "CIDFontType", nil},
|
33: {+1, "CIDFontType", nil},
|
||||||
34: {+1, "CIDCount", nil},
|
34: {+1, "CIDCount", nil},
|
||||||
35: {+1, "UIDBase", nil},
|
35: {+1, "UIDBase", nil},
|
||||||
36: {+1, "FDArray", nil},
|
36: {+1, "FDArray", func(p *psInterpreter) error {
|
||||||
37: {+1, "FDSelect", nil},
|
p.topDict.fdArray = p.argStack.a[p.argStack.top-1]
|
||||||
|
return nil
|
||||||
|
}},
|
||||||
|
37: {+1, "FDSelect", func(p *psInterpreter) error {
|
||||||
|
p.topDict.fdSelect = p.argStack.a[p.argStack.top-1]
|
||||||
|
return nil
|
||||||
|
}},
|
||||||
38: {+1, "FontName", nil},
|
38: {+1, "FontName", nil},
|
||||||
}},
|
}},
|
||||||
|
|
||||||
|
@ -696,7 +852,7 @@ var psOperators = [...][2][]psOperator{
|
||||||
10: {+1, "StdHW", nil},
|
10: {+1, "StdHW", nil},
|
||||||
11: {+1, "StdVW", nil},
|
11: {+1, "StdVW", nil},
|
||||||
19: {+1, "Subrs", func(p *psInterpreter) error {
|
19: {+1, "Subrs", func(p *psInterpreter) error {
|
||||||
p.privateDict.subrs = p.argStack.a[p.argStack.top-1]
|
p.privateDict.subrsOffset = p.argStack.a[p.argStack.top-1]
|
||||||
return nil
|
return nil
|
||||||
}},
|
}},
|
||||||
20: {+1, "defaultWidthX", nil},
|
20: {+1, "defaultWidthX", nil},
|
||||||
|
@ -1123,8 +1279,29 @@ func subrBias(numSubroutines int) int32 {
|
||||||
return 32768
|
return 32768
|
||||||
}
|
}
|
||||||
|
|
||||||
func t2CCallgsubr(p *psInterpreter) error { return t2CCall(p, p.type2Charstrings.f.cached.gsubrs) }
|
func t2CCallgsubr(p *psInterpreter) error {
|
||||||
func t2CCallsubr(p *psInterpreter) error { return t2CCall(p, p.type2Charstrings.f.cached.subrs) }
|
return t2CCall(p, p.type2Charstrings.f.cached.glyphData.gsubrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func t2CCallsubr(p *psInterpreter) error {
|
||||||
|
t := &p.type2Charstrings
|
||||||
|
d := &t.f.cached.glyphData
|
||||||
|
subrs := d.singleSubrs
|
||||||
|
if d.multiSubrs != nil {
|
||||||
|
if t.fdSelectIndexPlusOne == 0 {
|
||||||
|
index, err := d.fdSelect.lookup(t.f, t.b, t.glyphIndex)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if index < 0 || len(d.multiSubrs) <= index {
|
||||||
|
return errInvalidCFFTable
|
||||||
|
}
|
||||||
|
t.fdSelectIndexPlusOne = int32(index + 1)
|
||||||
|
}
|
||||||
|
subrs = d.multiSubrs[t.fdSelectIndexPlusOne-1]
|
||||||
|
}
|
||||||
|
return t2CCall(p, subrs)
|
||||||
|
}
|
||||||
|
|
||||||
func t2CCall(p *psInterpreter, subrs []uint32) error {
|
func t2CCall(p *psInterpreter, subrs []uint32) error {
|
||||||
if p.callStack.top == psCallStackSize || len(subrs) == 0 {
|
if p.callStack.top == psCallStackSize || len(subrs) == 0 {
|
||||||
|
|
|
@ -88,7 +88,7 @@ func TestProprietaryAdobeSourceCodeProTTF(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProprietaryAdobeSourceHanSansSC(t *testing.T) {
|
func TestProprietaryAdobeSourceHanSansSC(t *testing.T) {
|
||||||
testProprietary(t, "adobe", "SourceHanSansSC-Regular.otf", 65535, 2)
|
testProprietary(t, "adobe", "SourceHanSansSC-Regular.otf", 65535, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProprietaryAdobeSourceSansProOTF(t *testing.T) {
|
func TestProprietaryAdobeSourceSansProOTF(t *testing.T) {
|
||||||
|
@ -302,6 +302,18 @@ kernLoop:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for x, want := range proprietaryFDSelectTestCases[qualifiedFilename] {
|
||||||
|
got, err := f.cached.glyphData.fdSelect.lookup(f, &buf, x)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("fdSelect.lookup(%d): %v", x, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("fdSelect.lookup(%d): got %d, want %d", x, got, want)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// proprietaryNumFonts holds the expected number of fonts in each collection,
|
// proprietaryNumFonts holds the expected number of fonts in each collection,
|
||||||
|
@ -446,6 +458,77 @@ var proprietaryGlyphIndexTestCases = map[string]map[rune]GlyphIndex{
|
||||||
// - for TrueType glyphs, ttx coordinates are absolute, and consecutive
|
// - for TrueType glyphs, ttx coordinates are absolute, and consecutive
|
||||||
// off-curve points implies an on-curve point at the midpoint.
|
// off-curve points implies an on-curve point at the midpoint.
|
||||||
var proprietaryGlyphTestCases = map[string]map[rune][]Segment{
|
var proprietaryGlyphTestCases = map[string]map[rune][]Segment{
|
||||||
|
"adobe/SourceHanSansSC-Regular.otf": {
|
||||||
|
'!': {
|
||||||
|
// -312 123 callsubr # 123 + bias = 230
|
||||||
|
// : # Arg stack is [-312].
|
||||||
|
// : -13 140 -119 -21 return
|
||||||
|
// : # Arg stack is [-312 -13 140 -119 -21].
|
||||||
|
// 120 callsubr # 120 + bias = 227
|
||||||
|
// : # Arg stack is [-312 -13 140 -119 -21].
|
||||||
|
// : hstemhm
|
||||||
|
// : 95 132 -103 75 return
|
||||||
|
// : # Arg stack is [95 132 -103 75].
|
||||||
|
// hintmask 01010000
|
||||||
|
// 8 callsubr # 8 + bias = 115
|
||||||
|
// : # Arg stack is [].
|
||||||
|
// : 130 221 rmoveto
|
||||||
|
moveTo(130, 221),
|
||||||
|
// : 63 hlineto
|
||||||
|
lineTo(193, 221),
|
||||||
|
// : 12 424 3 -735 callgsubr # -735 + bias = 396
|
||||||
|
// : : # Arg stack is [12 424 3].
|
||||||
|
// : : 104 rlineto
|
||||||
|
lineTo(205, 645),
|
||||||
|
lineTo(208, 749),
|
||||||
|
// : : -93 hlineto
|
||||||
|
lineTo(115, 749),
|
||||||
|
// : : 3 -104 rlineto
|
||||||
|
lineTo(118, 645),
|
||||||
|
// : : return
|
||||||
|
// : : # Arg stack is [].
|
||||||
|
// : return
|
||||||
|
// : # Arg stack is [].
|
||||||
|
// hintmask 01100000
|
||||||
|
// 106 callsubr # 106 + bias = 213
|
||||||
|
// : # Arg stack is [].
|
||||||
|
// : 43 -658 rmoveto
|
||||||
|
moveTo(161, -13),
|
||||||
|
// : 37 29 28 41 return
|
||||||
|
// : # Arg stack is [37 29 28 41].
|
||||||
|
// hvcurveto
|
||||||
|
cubeTo(198, -13, 227, 15, 227, 56),
|
||||||
|
// hintmask 10100000
|
||||||
|
// 41 -29 30 -37 -36 -30 -30 -41 vhcurveto
|
||||||
|
cubeTo(227, 97, 198, 127, 161, 127),
|
||||||
|
cubeTo(125, 127, 95, 97, 95, 56),
|
||||||
|
// hintmask 01100000
|
||||||
|
// 111 callsubr # 111 + bias = 218
|
||||||
|
// : # Arg stack is [].
|
||||||
|
// : -41 30 -28 36 vhcurveto
|
||||||
|
cubeTo(95, 15, 125, -13, 161, -13),
|
||||||
|
// : endchar
|
||||||
|
},
|
||||||
|
|
||||||
|
'二': { // U+4E8C <CJK Ideograph> "two; twice"
|
||||||
|
// 23 81 510 79 hstem
|
||||||
|
// 60 881 cntrmask 11000000
|
||||||
|
// 144 693 rmoveto
|
||||||
|
moveTo(144, 693),
|
||||||
|
// -79 713 79 vlineto
|
||||||
|
lineTo(144, 614),
|
||||||
|
lineTo(857, 614),
|
||||||
|
lineTo(857, 693),
|
||||||
|
// -797 -589 rmoveto
|
||||||
|
moveTo(60, 104),
|
||||||
|
// -81 881 81 vlineto
|
||||||
|
lineTo(60, 23),
|
||||||
|
lineTo(941, 23),
|
||||||
|
lineTo(941, 104),
|
||||||
|
// endchar
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
"adobe/SourceSansPro-Regular.otf": {
|
"adobe/SourceSansPro-Regular.otf": {
|
||||||
',': {
|
',': {
|
||||||
// -309 -1 115 hstem
|
// -309 -1 115 hstem
|
||||||
|
@ -966,3 +1049,81 @@ var proprietaryKernTestCases = map[string][]kernTestCase{
|
||||||
{2048, font.HintingNone, [2]rune{'\uf041', '\uf042'}, 0},
|
{2048, font.HintingNone, [2]rune{'\uf041', '\uf042'}, 0},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// proprietaryFDSelectTestCases hold a sample of each font's Font Dict Select
|
||||||
|
// (FDSelect) map. The numerical values can be verified by grepping the output
|
||||||
|
// of the ttx tool:
|
||||||
|
//
|
||||||
|
// grep CharString.*fdSelectIndex SourceHanSansSC-Regular.ttx
|
||||||
|
//
|
||||||
|
// will print lines like this:
|
||||||
|
//
|
||||||
|
// <CharString name="cid00100" fdSelectIndex="15">
|
||||||
|
// <CharString name="cid00101" fdSelectIndex="15">
|
||||||
|
// <CharString name="cid00102" fdSelectIndex="3">
|
||||||
|
// <CharString name="cid00103" fdSelectIndex="15">
|
||||||
|
//
|
||||||
|
// As for what the values like 3 or 15 actually mean, grepping that ttx file
|
||||||
|
// for "FontName" gives this list:
|
||||||
|
//
|
||||||
|
// 0: <FontName value="SourceHanSansSC-Regular-Alphabetic"/>
|
||||||
|
// 1: <FontName value="SourceHanSansSC-Regular-AlphabeticDigits"/>
|
||||||
|
// 2: <FontName value="SourceHanSansSC-Regular-Bopomofo"/>
|
||||||
|
// 3: <FontName value="SourceHanSansSC-Regular-Dingbats"/>
|
||||||
|
// 4: <FontName value="SourceHanSansSC-Regular-DingbatsDigits"/>
|
||||||
|
// 5: <FontName value="SourceHanSansSC-Regular-Generic"/>
|
||||||
|
// 6: <FontName value="SourceHanSansSC-Regular-HDingbats"/>
|
||||||
|
// 7: <FontName value="SourceHanSansSC-Regular-HHangul"/>
|
||||||
|
// 8: <FontName value="SourceHanSansSC-Regular-HKana"/>
|
||||||
|
// 9: <FontName value="SourceHanSansSC-Regular-HWidth"/>
|
||||||
|
// 10: <FontName value="SourceHanSansSC-Regular-HWidthCJK"/>
|
||||||
|
// 11: <FontName value="SourceHanSansSC-Regular-HWidthDigits"/>
|
||||||
|
// 12: <FontName value="SourceHanSansSC-Regular-Hangul"/>
|
||||||
|
// 13: <FontName value="SourceHanSansSC-Regular-Ideographs"/>
|
||||||
|
// 14: <FontName value="SourceHanSansSC-Regular-Kana"/>
|
||||||
|
// 15: <FontName value="SourceHanSansSC-Regular-Proportional"/>
|
||||||
|
// 16: <FontName value="SourceHanSansSC-Regular-ProportionalCJK"/>
|
||||||
|
// 17: <FontName value="SourceHanSansSC-Regular-ProportionalDigits"/>
|
||||||
|
// 18: <FontName value="SourceHanSansSC-Regular-VKana"/>
|
||||||
|
//
|
||||||
|
// As a sanity check, the cmap table maps U+3127 BOPOMOFO LETTER I to the glyph
|
||||||
|
// named "cid65353", proprietaryFDSelectTestCases here maps 65353 to Font Dict
|
||||||
|
// 2, and the list immediately above maps 2 to "Bopomofo".
|
||||||
|
var proprietaryFDSelectTestCases = map[string]map[GlyphIndex]int{
|
||||||
|
"adobe/SourceHanSansSC-Regular.otf": {
|
||||||
|
0: 5,
|
||||||
|
1: 15,
|
||||||
|
2: 15,
|
||||||
|
16: 15,
|
||||||
|
17: 17,
|
||||||
|
26: 17,
|
||||||
|
27: 15,
|
||||||
|
100: 15,
|
||||||
|
101: 15,
|
||||||
|
102: 3,
|
||||||
|
103: 15,
|
||||||
|
777: 4,
|
||||||
|
1000: 3,
|
||||||
|
2000: 3,
|
||||||
|
3000: 13,
|
||||||
|
4000: 13,
|
||||||
|
20000: 13,
|
||||||
|
48000: 12,
|
||||||
|
59007: 1,
|
||||||
|
59024: 0,
|
||||||
|
59087: 8,
|
||||||
|
59200: 7,
|
||||||
|
59211: 6,
|
||||||
|
60000: 13,
|
||||||
|
63000: 16,
|
||||||
|
63039: 9,
|
||||||
|
63060: 11,
|
||||||
|
63137: 10,
|
||||||
|
65353: 2,
|
||||||
|
65486: 14,
|
||||||
|
65505: 18,
|
||||||
|
65506: 5,
|
||||||
|
65533: 5,
|
||||||
|
65534: 5,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
|
@ -53,6 +53,7 @@ const (
|
||||||
maxCompoundStackSize = 64
|
maxCompoundStackSize = 64
|
||||||
maxGlyphDataLength = 64 * 1024
|
maxGlyphDataLength = 64 * 1024
|
||||||
maxHintBits = 256
|
maxHintBits = 256
|
||||||
|
maxNumFontDicts = 256
|
||||||
maxNumFonts = 256
|
maxNumFonts = 256
|
||||||
maxNumTables = 256
|
maxNumTables = 256
|
||||||
maxRealNumberStrLen = 64 // Maximum length in bytes of the "-123.456E-7" representation.
|
maxRealNumberStrLen = 64 // Maximum length in bytes of the "-123.456E-7" representation.
|
||||||
|
@ -86,6 +87,7 @@ var (
|
||||||
errInvalidTableTagOrder = errors.New("sfnt: invalid table tag order")
|
errInvalidTableTagOrder = errors.New("sfnt: invalid table tag order")
|
||||||
errInvalidUCS2String = errors.New("sfnt: invalid UCS-2 string")
|
errInvalidUCS2String = errors.New("sfnt: invalid UCS-2 string")
|
||||||
|
|
||||||
|
errUnsupportedCFFFDSelectTable = errors.New("sfnt: unsupported CFF FDSelect table")
|
||||||
errUnsupportedCFFVersion = errors.New("sfnt: unsupported CFF version")
|
errUnsupportedCFFVersion = errors.New("sfnt: unsupported CFF version")
|
||||||
errUnsupportedCmapEncodings = errors.New("sfnt: unsupported cmap encodings")
|
errUnsupportedCmapEncodings = errors.New("sfnt: unsupported cmap encodings")
|
||||||
errUnsupportedCompoundGlyph = errors.New("sfnt: unsupported compound glyph")
|
errUnsupportedCompoundGlyph = errors.New("sfnt: unsupported compound glyph")
|
||||||
|
@ -93,6 +95,7 @@ var (
|
||||||
errUnsupportedKernTable = errors.New("sfnt: unsupported kern table")
|
errUnsupportedKernTable = errors.New("sfnt: unsupported kern table")
|
||||||
errUnsupportedRealNumberEncoding = errors.New("sfnt: unsupported real number encoding")
|
errUnsupportedRealNumberEncoding = errors.New("sfnt: unsupported real number encoding")
|
||||||
errUnsupportedNumberOfCmapSegments = errors.New("sfnt: unsupported number of cmap segments")
|
errUnsupportedNumberOfCmapSegments = errors.New("sfnt: unsupported number of cmap segments")
|
||||||
|
errUnsupportedNumberOfFontDicts = errors.New("sfnt: unsupported number of font dicts")
|
||||||
errUnsupportedNumberOfFonts = errors.New("sfnt: unsupported number of fonts")
|
errUnsupportedNumberOfFonts = errors.New("sfnt: unsupported number of fonts")
|
||||||
errUnsupportedNumberOfHints = errors.New("sfnt: unsupported number of hints")
|
errUnsupportedNumberOfHints = errors.New("sfnt: unsupported number of hints")
|
||||||
errUnsupportedNumberOfSubroutines = errors.New("sfnt: unsupported number of subroutines")
|
errUnsupportedNumberOfSubroutines = errors.New("sfnt: unsupported number of subroutines")
|
||||||
|
@ -438,6 +441,7 @@ type Font struct {
|
||||||
kern table
|
kern table
|
||||||
|
|
||||||
cached struct {
|
cached struct {
|
||||||
|
glyphData glyphData
|
||||||
glyphIndex glyphIndexFunc
|
glyphIndex glyphIndexFunc
|
||||||
indexToLocFormat bool // false means short, true means long.
|
indexToLocFormat bool // false means short, true means long.
|
||||||
isPostScript bool
|
isPostScript bool
|
||||||
|
@ -445,23 +449,11 @@ type Font struct {
|
||||||
kernOffset int32
|
kernOffset int32
|
||||||
postTableVersion uint32
|
postTableVersion uint32
|
||||||
unitsPerEm Units
|
unitsPerEm Units
|
||||||
|
|
||||||
// The glyph data for the i'th glyph index is in
|
|
||||||
// src[locations[i+0]:locations[i+1]].
|
|
||||||
//
|
|
||||||
// The slice length equals 1 plus the number of glyphs.
|
|
||||||
locations []uint32
|
|
||||||
|
|
||||||
// For PostScript fonts, the bytecode for the i'th global or local
|
|
||||||
// subroutine is in src[x[i+0]:x[i+1]].
|
|
||||||
//
|
|
||||||
// The slice length equals 1 plus the number of subroutines
|
|
||||||
gsubrs, subrs []uint32
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NumGlyphs returns the number of glyphs in f.
|
// NumGlyphs returns the number of glyphs in f.
|
||||||
func (f *Font) NumGlyphs() int { return len(f.cached.locations) - 1 }
|
func (f *Font) NumGlyphs() int { return len(f.cached.glyphData.locations) - 1 }
|
||||||
|
|
||||||
// UnitsPerEm returns the number of units per em for f.
|
// UnitsPerEm returns the number of units per em for f.
|
||||||
func (f *Font) UnitsPerEm() Units { return f.cached.unitsPerEm }
|
func (f *Font) UnitsPerEm() Units { return f.cached.unitsPerEm }
|
||||||
|
@ -487,7 +479,11 @@ func (f *Font) initialize(offset int) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
buf, numGlyphs, locations, gsubrs, subrs, err := f.parseMaxp(buf, indexToLocFormat, isPostScript)
|
buf, numGlyphs, err := f.parseMaxp(buf, isPostScript)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
buf, glyphData, err := f.parseGlyphData(buf, numGlyphs, indexToLocFormat, isPostScript)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -504,6 +500,7 @@ func (f *Font) initialize(offset int) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f.cached.glyphData = glyphData
|
||||||
f.cached.glyphIndex = glyphIndex
|
f.cached.glyphIndex = glyphIndex
|
||||||
f.cached.indexToLocFormat = indexToLocFormat
|
f.cached.indexToLocFormat = indexToLocFormat
|
||||||
f.cached.isPostScript = isPostScript
|
f.cached.isPostScript = isPostScript
|
||||||
|
@ -511,9 +508,6 @@ func (f *Font) initialize(offset int) error {
|
||||||
f.cached.kernOffset = kernOffset
|
f.cached.kernOffset = kernOffset
|
||||||
f.cached.postTableVersion = postTableVersion
|
f.cached.postTableVersion = postTableVersion
|
||||||
f.cached.unitsPerEm = unitsPerEm
|
f.cached.unitsPerEm = unitsPerEm
|
||||||
f.cached.locations = locations
|
|
||||||
f.cached.gsubrs = gsubrs
|
|
||||||
f.cached.subrs = subrs
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -778,24 +772,44 @@ func (f *Font) parseKernFormat0(buf []byte, offset, length int) (buf1 []byte, ke
|
||||||
return buf, kernNumPairs, int32(offset) + headerSize, nil
|
return buf, kernNumPairs, int32(offset) + headerSize, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Font) parseMaxp(buf []byte, indexToLocFormat, isPostScript bool) (buf1 []byte, numGlyphs int, locations, gsubrs, subrs []uint32, err error) {
|
func (f *Font) parseMaxp(buf []byte, isPostScript bool) (buf1 []byte, numGlyphs int, err error) {
|
||||||
// https://www.microsoft.com/typography/otspec/maxp.htm
|
// https://www.microsoft.com/typography/otspec/maxp.htm
|
||||||
|
|
||||||
if isPostScript {
|
if isPostScript {
|
||||||
if f.maxp.length != 6 {
|
if f.maxp.length != 6 {
|
||||||
return nil, 0, nil, nil, nil, errInvalidMaxpTable
|
return nil, 0, errInvalidMaxpTable
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if f.maxp.length != 32 {
|
if f.maxp.length != 32 {
|
||||||
return nil, 0, nil, nil, nil, errInvalidMaxpTable
|
return nil, 0, errInvalidMaxpTable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
u, err := f.src.u16(buf, f.maxp, 4)
|
u, err := f.src.u16(buf, f.maxp, 4)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, nil, nil, nil, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
numGlyphs = int(u)
|
return buf, int(u), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type glyphData struct {
|
||||||
|
// The glyph data for the i'th glyph index is in
|
||||||
|
// src[locations[i+0]:locations[i+1]].
|
||||||
|
//
|
||||||
|
// The slice length equals 1 plus the number of glyphs.
|
||||||
|
locations []uint32
|
||||||
|
|
||||||
|
// For PostScript fonts, the bytecode for the i'th global or local
|
||||||
|
// subroutine is in src[x[i+0]:x[i+1]].
|
||||||
|
//
|
||||||
|
// The []uint32 slice length equals 1 plus the number of subroutines
|
||||||
|
gsubrs []uint32
|
||||||
|
singleSubrs []uint32
|
||||||
|
multiSubrs [][]uint32
|
||||||
|
|
||||||
|
fdSelect fdSelect
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Font) parseGlyphData(buf []byte, numGlyphs int, indexToLocFormat, isPostScript bool) (buf1 []byte, ret glyphData, err error) {
|
||||||
if isPostScript {
|
if isPostScript {
|
||||||
p := cffParser{
|
p := cffParser{
|
||||||
src: &f.src,
|
src: &f.src,
|
||||||
|
@ -803,21 +817,21 @@ func (f *Font) parseMaxp(buf []byte, indexToLocFormat, isPostScript bool) (buf1
|
||||||
offset: int(f.cff.offset),
|
offset: int(f.cff.offset),
|
||||||
end: int(f.cff.offset + f.cff.length),
|
end: int(f.cff.offset + f.cff.length),
|
||||||
}
|
}
|
||||||
locations, gsubrs, subrs, err = p.parse()
|
ret, err = p.parse(numGlyphs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, nil, nil, nil, err
|
return nil, glyphData{}, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
locations, err = parseLoca(&f.src, f.loca, f.glyf.offset, indexToLocFormat, numGlyphs)
|
ret.locations, err = parseLoca(&f.src, f.loca, f.glyf.offset, indexToLocFormat, numGlyphs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, nil, nil, nil, err
|
return nil, glyphData{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(locations) != numGlyphs+1 {
|
if len(ret.locations) != numGlyphs+1 {
|
||||||
return nil, 0, nil, nil, nil, errInvalidLocationData
|
return nil, glyphData{}, errInvalidLocationData
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf, numGlyphs, locations, gsubrs, subrs, nil
|
return buf, ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Font) parsePost(buf []byte, numGlyphs int) (buf1 []byte, postTableVersion uint32, err error) {
|
func (f *Font) parsePost(buf []byte, numGlyphs int) (buf1 []byte, postTableVersion uint32, err error) {
|
||||||
|
@ -866,8 +880,8 @@ func (f *Font) viewGlyphData(b *Buffer, x GlyphIndex) (buf []byte, offset, lengt
|
||||||
if f.NumGlyphs() <= xx {
|
if f.NumGlyphs() <= xx {
|
||||||
return nil, 0, 0, ErrNotFound
|
return nil, 0, 0, ErrNotFound
|
||||||
}
|
}
|
||||||
i := f.cached.locations[xx+0]
|
i := f.cached.glyphData.locations[xx+0]
|
||||||
j := f.cached.locations[xx+1]
|
j := f.cached.glyphData.locations[xx+1]
|
||||||
if j < i {
|
if j < i {
|
||||||
return nil, 0, 0, errInvalidGlyphDataLength
|
return nil, 0, 0, errInvalidGlyphDataLength
|
||||||
}
|
}
|
||||||
|
@ -900,7 +914,7 @@ func (f *Font) LoadGlyph(b *Buffer, x GlyphIndex, ppem fixed.Int26_6, opts *Load
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
b.psi.type2Charstrings.initialize(f, b)
|
b.psi.type2Charstrings.initialize(f, b, x)
|
||||||
if err := b.psi.run(psContextType2Charstring, buf, offset, length); err != nil {
|
if err := b.psi.run(psContextType2Charstring, buf, offset, length); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user