| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414 |
- // Copyright 2016 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
- // Compact Font Format (CFF) fonts are written in PostScript, a stack-based
- // programming language.
- //
- // A fundamental concept is a DICT, or a key-value map, expressed in reverse
- // Polish notation. For example, this sequence of operations:
- // - push the number 379
- // - version operator
- // - push the number 392
- // - Notice operator
- // - etc
- // - push the number 100
- // - push the number 0
- // - push the number 500
- // - push the number 800
- // - FontBBox operator
- // - etc
- // defines a DICT that maps "version" to the String ID (SID) 379, "Notice" to
- // the SID 392, "FontBBox" to the four numbers [100, 0, 500, 800], etc.
- //
- // The first 391 String IDs (starting at 0) are predefined as per the CFF spec
- // Appendix A, in 5176.CFF.pdf referenced below. For example, 379 means
- // "001.000". String ID 392 is not predefined, and is mapped by a separate
- // structure, the "String INDEX", inside the CFF data. (String ID 391 is also
- // not predefined. Specifically for ../testdata/CFFTest.otf, 391 means
- // "uni4E2D", as this font contains a glyph for U+4E2D).
- //
- // The actual glyph vectors are similarly encoded (in PostScript), in a format
- // called Type 2 Charstrings. The wire encoding is similar to but not exactly
- // the same as CFF's. For example, the byte 0x05 means FontBBox for CFF DICTs,
- // but means rlineto (relative line-to) for Type 2 Charstrings. See
- // 5176.CFF.pdf Appendix H and 5177.Type2.pdf Appendix A in the PDF files
- // referenced below.
- //
- // CFF is a stand-alone format, but CFF as used in SFNT fonts have further
- // restrictions. For example, a stand-alone CFF can contain multiple fonts, but
- // https://www.microsoft.com/typography/OTSPEC/cff.htm says that "The Name
- // INDEX in the CFF must contain only one entry; that is, there must be only
- // one font in the CFF FontSet".
- //
- // The relevant specifications are:
- // - http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5176.CFF.pdf
- // - http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5177.Type2.pdf
- import (
- "fmt"
- "math"
- "strconv"
- "golang.org/x/image/math/fixed"
- )
- const (
- // psArgStackSize is the argument stack size for a PostScript interpreter.
- // 5176.CFF.pdf section 4 "DICT Data" says that "An operator may be
- // preceded by up to a maximum of 48 operands". 5177.Type2.pdf Appendix B
- // "Type 2 Charstring Implementation Limits" says that "Argument stack 48".
- psArgStackSize = 48
- // Similarly, Appendix B says "Subr nesting, stack limit 10".
- psCallStackSize = 10
- )
- func bigEndian(b []byte) uint32 {
- switch len(b) {
- case 1:
- return uint32(b[0])
- case 2:
- return uint32(b[0])<<8 | uint32(b[1])
- case 3:
- return uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2])
- case 4:
- return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3])
- }
- 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.
- type cffParser struct {
- src *source
- base int
- offset int
- end int
- err error
- buf []byte
- locBuf [2]uint32
- psi psInterpreter
- }
- func (p *cffParser) parse(numGlyphs int32) (ret glyphData, err error) {
- // Parse the header.
- {
- if !p.read(4) {
- return glyphData{}, p.err
- }
- if p.buf[0] != 1 || p.buf[1] != 0 || p.buf[2] != 4 {
- return glyphData{}, errUnsupportedCFFVersion
- }
- }
- // Parse the Name INDEX.
- {
- count, offSize, ok := p.parseIndexHeader()
- if !ok {
- return glyphData{}, p.err
- }
- // https://www.microsoft.com/typography/OTSPEC/cff.htm says that "The
- // Name INDEX in the CFF must contain only one entry".
- if count != 1 {
- return glyphData{}, errInvalidCFFTable
- }
- if !p.parseIndexLocations(p.locBuf[:2], count, offSize) {
- return glyphData{}, p.err
- }
- p.offset = int(p.locBuf[1])
- }
- // Parse the Top DICT INDEX.
- p.psi.topDict.initialize()
- {
- count, offSize, ok := p.parseIndexHeader()
- if !ok {
- return glyphData{}, p.err
- }
- // 5176.CFF.pdf section 8 "Top DICT INDEX" says that the count here
- // should match the count of the Name INDEX, which is 1.
- if count != 1 {
- return glyphData{}, errInvalidCFFTable
- }
- if !p.parseIndexLocations(p.locBuf[:2], count, offSize) {
- return glyphData{}, p.err
- }
- if !p.read(int(p.locBuf[1] - p.locBuf[0])) {
- return glyphData{}, p.err
- }
- if p.err = p.psi.run(psContextTopDict, p.buf, 0, 0); p.err != nil {
- return glyphData{}, p.err
- }
- }
- // Skip the String INDEX.
- {
- count, offSize, ok := p.parseIndexHeader()
- if !ok {
- return glyphData{}, p.err
- }
- if count != 0 {
- // Read the last location. Locations are off by 1 byte. See the
- // comment in parseIndexLocations.
- if !p.skip(int(count * offSize)) {
- return glyphData{}, p.err
- }
- if !p.read(int(offSize)) {
- return glyphData{}, p.err
- }
- loc := bigEndian(p.buf) - 1
- // Check that locations are in bounds.
- if uint32(p.end-p.offset) < loc {
- return glyphData{}, errInvalidCFFTable
- }
- // Skip the index data.
- if !p.skip(int(loc)) {
- return glyphData{}, p.err
- }
- }
- }
- // Parse the Global Subrs [Subroutines] INDEX.
- {
- count, offSize, ok := p.parseIndexHeader()
- if !ok {
- return glyphData{}, p.err
- }
- if count != 0 {
- if count > maxNumSubroutines {
- return glyphData{}, errUnsupportedNumberOfSubroutines
- }
- ret.gsubrs = make([]uint32, count+1)
- if !p.parseIndexLocations(ret.gsubrs, count, offSize) {
- return glyphData{}, p.err
- }
- }
- }
- // Parse the CharStrings INDEX, whose location was found in the Top DICT.
- {
- if !p.seekFromBase(p.psi.topDict.charStringsOffset) {
- return glyphData{}, errInvalidCFFTable
- }
- count, offSize, ok := p.parseIndexHeader()
- if !ok {
- return glyphData{}, p.err
- }
- if count == 0 || int32(count) != numGlyphs {
- return glyphData{}, errInvalidCFFTable
- }
- ret.locations = make([]uint32, count+1)
- if !p.parseIndexLocations(ret.locations, count, offSize) {
- return glyphData{}, p.err
- }
- }
- if !p.psi.topDict.isCIDFont {
- // 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 int32) (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 < int(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()
- if length != 0 {
- fullLength := int32(p.end - p.base)
- if offset <= 0 || fullLength < offset || fullLength-offset < length || length < 0 {
- return nil, errInvalidCFFTable
- }
- p.offset = p.base + int(offset)
- if !p.read(int(length)) {
- return nil, p.err
- }
- if p.err = p.psi.run(psContextPrivateDict, p.buf, 0, 0); p.err != nil {
- return nil, p.err
- }
- }
- // Parse the Local Subrs [Subroutines] INDEX, whose location was found in
- // the Private DICT.
- if p.psi.privateDict.subrsOffset != 0 {
- if !p.seekFromBase(offset + p.psi.privateDict.subrsOffset) {
- return nil, errInvalidCFFTable
- }
- count, offSize, ok := p.parseIndexHeader()
- if !ok {
- return nil, p.err
- }
- if count != 0 {
- if count > maxNumSubroutines {
- return nil, errUnsupportedNumberOfSubroutines
- }
- subrs = make([]uint32, count+1)
- if !p.parseIndexLocations(subrs, count, offSize) {
- return nil, p.err
- }
- }
- }
- return subrs, err
- }
- // read sets p.buf to view the n bytes from p.offset to p.offset+n. It also
- // advances p.offset by n.
- //
- // As per the source.view method, the caller should not modify the contents of
- // p.buf after read returns, other than by calling read again.
- //
- // The caller should also avoid modifying the pointer / length / capacity of
- // the p.buf slice, not just avoid modifying the slice's contents, in order to
- // maximize the opportunity to re-use p.buf's allocated memory when viewing the
- // underlying source data for subsequent read calls.
- func (p *cffParser) read(n int) (ok bool) {
- if n < 0 || p.end-p.offset < n {
- p.err = errInvalidCFFTable
- return false
- }
- 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
- return p.err == nil
- }
- func (p *cffParser) skip(n int) (ok bool) {
- if p.end-p.offset < n {
- p.err = errInvalidCFFTable
- return false
- }
- p.offset += n
- 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) {
- if !p.read(2) {
- return 0, 0, false
- }
- count = int32(u16(p.buf[:2]))
- // 5176.CFF.pdf section 5 "INDEX Data" says that "An empty INDEX is
- // represented by a count field with a 0 value and no additional fields.
- // Thus, the total size of an empty INDEX is 2 bytes".
- if count == 0 {
- return count, 0, true
- }
- if !p.read(1) {
- return 0, 0, false
- }
- offSize = int32(p.buf[0])
- if offSize < 1 || 4 < offSize {
- p.err = errInvalidCFFTable
- return 0, 0, false
- }
- return count, offSize, true
- }
- func (p *cffParser) parseIndexLocations(dst []uint32, count, offSize int32) (ok bool) {
- if count == 0 {
- return true
- }
- if len(dst) != int(count+1) {
- panic("unreachable")
- }
- if !p.read(len(dst) * int(offSize)) {
- return false
- }
- buf, prev := p.buf, uint32(0)
- for i := range dst {
- loc := bigEndian(buf[:offSize])
- buf = buf[offSize:]
- // Locations are off by 1 byte. 5176.CFF.pdf section 5 "INDEX Data"
- // says that "Offsets in the offset array are relative to the byte that
- // precedes the object data... This ensures that every object has a
- // corresponding offset which is always nonzero".
- if loc == 0 {
- p.err = errInvalidCFFTable
- return false
- }
- loc--
- // In the same paragraph, "Therefore the first element of the offset
- // array is always 1" before correcting for the off-by-1.
- if i == 0 {
- if loc != 0 {
- p.err = errInvalidCFFTable
- break
- }
- } else if loc <= prev { // Check that locations are increasing.
- p.err = errInvalidCFFTable
- break
- }
- // Check that locations are in bounds.
- if uint32(p.end-p.offset) < loc {
- p.err = errInvalidCFFTable
- break
- }
- dst[i] = uint32(p.offset) + loc
- prev = loc
- }
- return p.err == nil
- }
- type psCallStackEntry struct {
- offset, length uint32
- }
- type psContext uint32
- const (
- psContextTopDict psContext = iota
- psContextPrivateDict
- psContextType2Charstring
- )
- // psTopDictData contains fields specific to the Top DICT context.
- type psTopDictData struct {
- charStringsOffset int32
- fdArray int32
- fdSelect int32
- isCIDFont bool
- privateDictOffset int32
- privateDictLength int32
- }
- func (d *psTopDictData) initialize() {
- *d = psTopDictData{}
- }
- // psPrivateDictData contains fields specific to the Private DICT context.
- type psPrivateDictData struct {
- subrsOffset int32
- }
- func (d *psPrivateDictData) initialize() {
- *d = psPrivateDictData{}
- }
- // psType2CharstringsData contains fields specific to the Type 2 Charstrings
- // context.
- type psType2CharstringsData struct {
- f *Font
- b *Buffer
- x int32
- y int32
- firstX int32
- firstY int32
- hintBits int32
- seenWidth 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, glyphIndex GlyphIndex) {
- *d = psType2CharstringsData{
- f: f,
- b: b,
- glyphIndex: glyphIndex,
- }
- }
- func (d *psType2CharstringsData) closePath() {
- if d.x != d.firstX || d.y != d.firstY {
- d.b.segments = append(d.b.segments, Segment{
- Op: SegmentOpLineTo,
- Args: [3]fixed.Point26_6{{
- X: fixed.Int26_6(d.firstX),
- Y: fixed.Int26_6(d.firstY),
- }},
- })
- }
- }
- func (d *psType2CharstringsData) moveTo(dx, dy int32) {
- d.closePath()
- d.x += dx
- d.y += dy
- d.b.segments = append(d.b.segments, Segment{
- Op: SegmentOpMoveTo,
- Args: [3]fixed.Point26_6{{
- X: fixed.Int26_6(d.x),
- Y: fixed.Int26_6(d.y),
- }},
- })
- d.firstX = d.x
- d.firstY = d.y
- }
- func (d *psType2CharstringsData) lineTo(dx, dy int32) {
- d.x += dx
- d.y += dy
- d.b.segments = append(d.b.segments, Segment{
- Op: SegmentOpLineTo,
- Args: [3]fixed.Point26_6{{
- X: fixed.Int26_6(d.x),
- Y: fixed.Int26_6(d.y),
- }},
- })
- }
- func (d *psType2CharstringsData) cubeTo(dxa, dya, dxb, dyb, dxc, dyc int32) {
- d.x += dxa
- d.y += dya
- xa := fixed.Int26_6(d.x)
- ya := fixed.Int26_6(d.y)
- d.x += dxb
- d.y += dyb
- xb := fixed.Int26_6(d.x)
- yb := fixed.Int26_6(d.y)
- d.x += dxc
- d.y += dyc
- xc := fixed.Int26_6(d.x)
- yc := fixed.Int26_6(d.y)
- d.b.segments = append(d.b.segments, Segment{
- Op: SegmentOpCubeTo,
- Args: [3]fixed.Point26_6{{X: xa, Y: ya}, {X: xb, Y: yb}, {X: xc, Y: yc}},
- })
- }
- // psInterpreter is a PostScript interpreter.
- type psInterpreter struct {
- ctx psContext
- instructions []byte
- instrOffset uint32
- instrLength uint32
- argStack struct {
- a [psArgStackSize]int32
- top int32
- }
- callStack struct {
- a [psCallStackSize]psCallStackEntry
- top int32
- }
- parseNumberBuf [maxRealNumberStrLen]byte
- topDict psTopDictData
- privateDict psPrivateDictData
- type2Charstrings psType2CharstringsData
- }
- func (p *psInterpreter) hasMoreInstructions() bool {
- if len(p.instructions) != 0 {
- return true
- }
- for i := int32(0); i < p.callStack.top; i++ {
- if p.callStack.a[i].length != 0 {
- return true
- }
- }
- return false
- }
- // run runs the instructions in the given PostScript context. For the
- // psContextType2Charstring context, offset and length give the location of the
- // instructions in p.type2Charstrings.f.src.
- func (p *psInterpreter) run(ctx psContext, instructions []byte, offset, length uint32) error {
- p.ctx = ctx
- p.instructions = instructions
- p.instrOffset = offset
- p.instrLength = length
- p.argStack.top = 0
- p.callStack.top = 0
- loop:
- for len(p.instructions) > 0 {
- // Push a numeric operand on the stack, if applicable.
- if hasResult, err := p.parseNumber(); hasResult {
- if err != nil {
- return err
- }
- continue
- }
- // Otherwise, execute an operator.
- b := p.instructions[0]
- p.instructions = p.instructions[1:]
- for escaped, ops := false, psOperators[ctx][0]; ; {
- if b == escapeByte && !escaped {
- if len(p.instructions) <= 0 {
- return errInvalidCFFTable
- }
- b = p.instructions[0]
- p.instructions = p.instructions[1:]
- escaped = true
- ops = psOperators[ctx][1]
- continue
- }
- if int(b) < len(ops) {
- if op := ops[b]; op.name != "" {
- if p.argStack.top < op.numPop {
- return errInvalidCFFTable
- }
- if op.run != nil {
- if err := op.run(p); err != nil {
- return err
- }
- }
- if op.numPop < 0 {
- p.argStack.top = 0
- } else {
- p.argStack.top -= op.numPop
- }
- continue loop
- }
- }
- if escaped {
- return fmt.Errorf("sfnt: unrecognized CFF 2-byte operator (12 %d)", b)
- } else {
- return fmt.Errorf("sfnt: unrecognized CFF 1-byte operator (%d)", b)
- }
- }
- }
- return nil
- }
- // See 5176.CFF.pdf section 4 "DICT Data".
- func (p *psInterpreter) parseNumber() (hasResult bool, err error) {
- number := int32(0)
- switch b := p.instructions[0]; {
- case b == 28:
- if len(p.instructions) < 3 {
- return true, errInvalidCFFTable
- }
- number, hasResult = int32(int16(u16(p.instructions[1:]))), true
- p.instructions = p.instructions[3:]
- case b == 29 && p.ctx != psContextType2Charstring:
- if len(p.instructions) < 5 {
- return true, errInvalidCFFTable
- }
- number, hasResult = int32(u32(p.instructions[1:])), true
- p.instructions = p.instructions[5:]
- case b == 30 && p.ctx != psContextType2Charstring:
- // Parse a real number. This isn't listed in 5176.CFF.pdf Table 3
- // "Operand Encoding" but that table lists integer encodings. Further
- // down the page it says "A real number operand is provided in addition
- // to integer operands. This operand begins with a byte value of 30
- // followed by a variable-length sequence of bytes."
- s := p.parseNumberBuf[:0]
- p.instructions = p.instructions[1:]
- loop:
- for {
- if len(p.instructions) == 0 {
- return true, errInvalidCFFTable
- }
- b := p.instructions[0]
- p.instructions = p.instructions[1:]
- // Process b's two nibbles, high then low.
- for i := 0; i < 2; i++ {
- nib := b >> 4
- b = b << 4
- if nib == 0x0f {
- f, err := strconv.ParseFloat(string(s), 32)
- if err != nil {
- return true, errInvalidCFFTable
- }
- number, hasResult = int32(math.Float32bits(float32(f))), true
- break loop
- }
- if nib == 0x0d {
- return true, errInvalidCFFTable
- }
- if len(s)+maxNibbleDefsLength > len(p.parseNumberBuf) {
- return true, errUnsupportedRealNumberEncoding
- }
- s = append(s, nibbleDefs[nib]...)
- }
- }
- case b < 32:
- // No-op.
- case b < 247:
- p.instructions = p.instructions[1:]
- number, hasResult = int32(b)-139, true
- case b < 251:
- if len(p.instructions) < 2 {
- return true, errInvalidCFFTable
- }
- b1 := p.instructions[1]
- p.instructions = p.instructions[2:]
- number, hasResult = +int32(b-247)*256+int32(b1)+108, true
- case b < 255:
- if len(p.instructions) < 2 {
- return true, errInvalidCFFTable
- }
- b1 := p.instructions[1]
- p.instructions = p.instructions[2:]
- number, hasResult = -int32(b-251)*256-int32(b1)-108, true
- case b == 255 && p.ctx == psContextType2Charstring:
- if len(p.instructions) < 5 {
- return true, errInvalidCFFTable
- }
- number, hasResult = int32(u32(p.instructions[1:])), true
- p.instructions = p.instructions[5:]
- }
- if hasResult {
- if p.argStack.top == psArgStackSize {
- return true, errInvalidCFFTable
- }
- p.argStack.a[p.argStack.top] = number
- p.argStack.top++
- }
- return hasResult, nil
- }
- const maxNibbleDefsLength = len("E-")
- // nibbleDefs encodes 5176.CFF.pdf Table 5 "Nibble Definitions".
- var nibbleDefs = [16]string{
- 0x00: "0",
- 0x01: "1",
- 0x02: "2",
- 0x03: "3",
- 0x04: "4",
- 0x05: "5",
- 0x06: "6",
- 0x07: "7",
- 0x08: "8",
- 0x09: "9",
- 0x0a: ".",
- 0x0b: "E",
- 0x0c: "E-",
- 0x0d: "",
- 0x0e: "-",
- 0x0f: "",
- }
- type psOperator struct {
- // numPop is the number of stack values to pop. -1 means "array" and -2
- // means "delta" as per 5176.CFF.pdf Table 6 "Operand Types".
- numPop int32
- // name is the operator name. An empty name (i.e. the zero value for the
- // struct overall) means an unrecognized 1-byte operator.
- name string
- // run is the function that implements the operator. Nil means that we
- // ignore the operator, other than popping its arguments off the stack.
- run func(*psInterpreter) error
- }
- // psOperators holds the 1-byte and 2-byte operators for PostScript interpreter
- // contexts.
- var psOperators = [...][2][]psOperator{
- // The Top DICT operators are defined by 5176.CFF.pdf Table 9 "Top DICT
- // Operator Entries" and Table 10 "CIDFont Operator Extensions".
- psContextTopDict: {{
- // 1-byte operators.
- 0: {+1, "version", nil},
- 1: {+1, "Notice", nil},
- 2: {+1, "FullName", nil},
- 3: {+1, "FamilyName", nil},
- 4: {+1, "Weight", nil},
- 5: {-1, "FontBBox", nil},
- 13: {+1, "UniqueID", nil},
- 14: {-1, "XUID", nil},
- 15: {+1, "charset", nil},
- 16: {+1, "Encoding", nil},
- 17: {+1, "CharStrings", func(p *psInterpreter) error {
- p.topDict.charStringsOffset = p.argStack.a[p.argStack.top-1]
- return nil
- }},
- 18: {+2, "Private", func(p *psInterpreter) error {
- p.topDict.privateDictLength = p.argStack.a[p.argStack.top-2]
- p.topDict.privateDictOffset = p.argStack.a[p.argStack.top-1]
- return nil
- }},
- }, {
- // 2-byte operators. The first byte is the escape byte.
- 0: {+1, "Copyright", nil},
- 1: {+1, "isFixedPitch", nil},
- 2: {+1, "ItalicAngle", nil},
- 3: {+1, "UnderlinePosition", nil},
- 4: {+1, "UnderlineThickness", nil},
- 5: {+1, "PaintType", nil},
- 6: {+1, "CharstringType", nil},
- 7: {-1, "FontMatrix", nil},
- 8: {+1, "StrokeWidth", nil},
- 20: {+1, "SyntheticBase", nil},
- 21: {+1, "PostScript", nil},
- 22: {+1, "BaseFontName", nil},
- 23: {-2, "BaseFontBlend", nil},
- 30: {+3, "ROS", func(p *psInterpreter) error {
- p.topDict.isCIDFont = true
- return nil
- }},
- 31: {+1, "CIDFontVersion", nil},
- 32: {+1, "CIDFontRevision", nil},
- 33: {+1, "CIDFontType", nil},
- 34: {+1, "CIDCount", nil},
- 35: {+1, "UIDBase", nil},
- 36: {+1, "FDArray", func(p *psInterpreter) error {
- 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},
- }},
- // The Private DICT operators are defined by 5176.CFF.pdf Table 23 "Private
- // DICT Operators".
- psContextPrivateDict: {{
- // 1-byte operators.
- 6: {-2, "BlueValues", nil},
- 7: {-2, "OtherBlues", nil},
- 8: {-2, "FamilyBlues", nil},
- 9: {-2, "FamilyOtherBlues", nil},
- 10: {+1, "StdHW", nil},
- 11: {+1, "StdVW", nil},
- 19: {+1, "Subrs", func(p *psInterpreter) error {
- p.privateDict.subrsOffset = p.argStack.a[p.argStack.top-1]
- return nil
- }},
- 20: {+1, "defaultWidthX", nil},
- 21: {+1, "nominalWidthX", nil},
- }, {
- // 2-byte operators. The first byte is the escape byte.
- 9: {+1, "BlueScale", nil},
- 10: {+1, "BlueShift", nil},
- 11: {+1, "BlueFuzz", nil},
- 12: {-2, "StemSnapH", nil},
- 13: {-2, "StemSnapV", nil},
- 14: {+1, "ForceBold", nil},
- 17: {+1, "LanguageGroup", nil},
- 18: {+1, "ExpansionFactor", nil},
- 19: {+1, "initialRandomSeed", nil},
- }},
- // The Type 2 Charstring operators are defined by 5177.Type2.pdf Appendix A
- // "Type 2 Charstring Command Codes".
- psContextType2Charstring: {{
- // 1-byte operators.
- 0: {}, // Reserved.
- 1: {-1, "hstem", t2CStem},
- 2: {}, // Reserved.
- 3: {-1, "vstem", t2CStem},
- 4: {-1, "vmoveto", t2CVmoveto},
- 5: {-1, "rlineto", t2CRlineto},
- 6: {-1, "hlineto", t2CHlineto},
- 7: {-1, "vlineto", t2CVlineto},
- 8: {-1, "rrcurveto", t2CRrcurveto},
- 9: {}, // Reserved.
- 10: {+1, "callsubr", t2CCallsubr},
- 11: {+0, "return", t2CReturn},
- 12: {}, // escape.
- 13: {}, // Reserved.
- 14: {-1, "endchar", t2CEndchar},
- 15: {}, // Reserved.
- 16: {}, // Reserved.
- 17: {}, // Reserved.
- 18: {-1, "hstemhm", t2CStem},
- 19: {-1, "hintmask", t2CMask},
- 20: {-1, "cntrmask", t2CMask},
- 21: {-1, "rmoveto", t2CRmoveto},
- 22: {-1, "hmoveto", t2CHmoveto},
- 23: {-1, "vstemhm", t2CStem},
- 24: {-1, "rcurveline", t2CRcurveline},
- 25: {-1, "rlinecurve", t2CRlinecurve},
- 26: {-1, "vvcurveto", t2CVvcurveto},
- 27: {-1, "hhcurveto", t2CHhcurveto},
- 28: {}, // shortint.
- 29: {+1, "callgsubr", t2CCallgsubr},
- 30: {-1, "vhcurveto", t2CVhcurveto},
- 31: {-1, "hvcurveto", t2CHvcurveto},
- }, {
- // 2-byte operators. The first byte is the escape byte.
- 34: {+7, "hflex", t2CHflex},
- 36: {+9, "hflex1", t2CHflex1},
- // TODO: more operators.
- }},
- }
- // 5176.CFF.pdf section 4 "DICT Data" says that "Two-byte operators have an
- // initial escape byte of 12".
- const escapeByte = 12
- // t2CReadWidth reads the optional width adjustment. If present, it is on the
- // bottom of the arg stack. nArgs is the expected number of arguments on the
- // stack. A negative nArgs means a multiple of 2.
- //
- // 5177.Type2.pdf page 16 Note 4 says: "The first stack-clearing operator,
- // which must be one of hstem, hstemhm, vstem, vstemhm, cntrmask, hintmask,
- // hmoveto, vmoveto, rmoveto, or endchar, takes an additional argument — the
- // width... which may be expressed as zero or one numeric argument."
- func t2CReadWidth(p *psInterpreter, nArgs int32) {
- if p.type2Charstrings.seenWidth {
- return
- }
- p.type2Charstrings.seenWidth = true
- if nArgs >= 0 {
- if p.argStack.top != nArgs+1 {
- return
- }
- } else if p.argStack.top&1 == 0 {
- return
- }
- // When parsing a standalone CFF, we'd save the value of p.argStack.a[0]
- // here as it defines the glyph's width (horizontal advance). Specifically,
- // if present, it is a delta to the font-global nominalWidthX value found
- // in the Private DICT. If absent, the glyph's width is the defaultWidthX
- // value in that dict. See 5176.CFF.pdf section 15 "Private DICT Data".
- //
- // For a CFF embedded in an SFNT font (i.e. an OpenType font), glyph widths
- // are already stored in the hmtx table, separate to the CFF table, and it
- // is simpler to parse that table for all OpenType fonts (PostScript and
- // TrueType). We therefore ignore the width value here, and just remove it
- // from the bottom of the argStack.
- copy(p.argStack.a[:p.argStack.top-1], p.argStack.a[1:p.argStack.top])
- p.argStack.top--
- }
- func t2CStem(p *psInterpreter) error {
- t2CReadWidth(p, -1)
- if p.argStack.top%2 != 0 {
- return errInvalidCFFTable
- }
- // We update the number of hintBits need to parse hintmask and cntrmask
- // instructions, but this Type 2 Charstring implementation otherwise
- // ignores the stem hints.
- p.type2Charstrings.hintBits += p.argStack.top / 2
- if p.type2Charstrings.hintBits > maxHintBits {
- return errUnsupportedNumberOfHints
- }
- return nil
- }
- func t2CMask(p *psInterpreter) error {
- // 5176.CFF.pdf section 4.3 "Hint Operators" says that "If hstem and vstem
- // hints are both declared at the beginning of a charstring, and this
- // sequence is followed directly by the hintmask or cntrmask operators, the
- // vstem hint operator need not be included."
- //
- // What we implement here is more permissive (but the same as what the
- // FreeType implementation does, and simpler than tracking the previous
- // operator and other hinting state): if a hintmask is given any arguments
- // (i.e. the argStack is non-empty), we run an implicit vstem operator.
- //
- // Note that the vstem operator consumes from p.argStack, but the hintmask
- // or cntrmask operators consume from p.instructions.
- if p.argStack.top != 0 {
- if err := t2CStem(p); err != nil {
- return err
- }
- } else if !p.type2Charstrings.seenWidth {
- p.type2Charstrings.seenWidth = true
- }
- hintBytes := (p.type2Charstrings.hintBits + 7) / 8
- if len(p.instructions) < int(hintBytes) {
- return errInvalidCFFTable
- }
- p.instructions = p.instructions[hintBytes:]
- return nil
- }
- func t2CHmoveto(p *psInterpreter) error {
- t2CReadWidth(p, 1)
- if p.argStack.top != 1 {
- return errInvalidCFFTable
- }
- p.type2Charstrings.moveTo(p.argStack.a[0], 0)
- return nil
- }
- func t2CVmoveto(p *psInterpreter) error {
- t2CReadWidth(p, 1)
- if p.argStack.top != 1 {
- return errInvalidCFFTable
- }
- p.type2Charstrings.moveTo(0, p.argStack.a[0])
- return nil
- }
- func t2CRmoveto(p *psInterpreter) error {
- t2CReadWidth(p, 2)
- if p.argStack.top != 2 {
- return errInvalidCFFTable
- }
- p.type2Charstrings.moveTo(p.argStack.a[0], p.argStack.a[1])
- return nil
- }
- func t2CHlineto(p *psInterpreter) error { return t2CLineto(p, false) }
- func t2CVlineto(p *psInterpreter) error { return t2CLineto(p, true) }
- func t2CLineto(p *psInterpreter, vertical bool) error {
- if !p.type2Charstrings.seenWidth || p.argStack.top < 1 {
- return errInvalidCFFTable
- }
- for i := int32(0); i < p.argStack.top; i, vertical = i+1, !vertical {
- dx, dy := p.argStack.a[i], int32(0)
- if vertical {
- dx, dy = dy, dx
- }
- p.type2Charstrings.lineTo(dx, dy)
- }
- return nil
- }
- func t2CRlineto(p *psInterpreter) error {
- if !p.type2Charstrings.seenWidth || p.argStack.top < 2 || p.argStack.top%2 != 0 {
- return errInvalidCFFTable
- }
- for i := int32(0); i < p.argStack.top; i += 2 {
- p.type2Charstrings.lineTo(p.argStack.a[i], p.argStack.a[i+1])
- }
- return nil
- }
- // As per 5177.Type2.pdf section 4.1 "Path Construction Operators",
- //
- // rcurveline is:
- // - {dxa dya dxb dyb dxc dyc}+ dxd dyd
- //
- // rlinecurve is:
- // - {dxa dya}+ dxb dyb dxc dyc dxd dyd
- func t2CRcurveline(p *psInterpreter) error {
- if !p.type2Charstrings.seenWidth || p.argStack.top < 8 || p.argStack.top%6 != 2 {
- return errInvalidCFFTable
- }
- i := int32(0)
- for iMax := p.argStack.top - 2; i < iMax; i += 6 {
- p.type2Charstrings.cubeTo(
- p.argStack.a[i+0],
- p.argStack.a[i+1],
- p.argStack.a[i+2],
- p.argStack.a[i+3],
- p.argStack.a[i+4],
- p.argStack.a[i+5],
- )
- }
- p.type2Charstrings.lineTo(p.argStack.a[i], p.argStack.a[i+1])
- return nil
- }
- func t2CRlinecurve(p *psInterpreter) error {
- if !p.type2Charstrings.seenWidth || p.argStack.top < 8 || p.argStack.top%2 != 0 {
- return errInvalidCFFTable
- }
- i := int32(0)
- for iMax := p.argStack.top - 6; i < iMax; i += 2 {
- p.type2Charstrings.lineTo(p.argStack.a[i], p.argStack.a[i+1])
- }
- p.type2Charstrings.cubeTo(
- p.argStack.a[i+0],
- p.argStack.a[i+1],
- p.argStack.a[i+2],
- p.argStack.a[i+3],
- p.argStack.a[i+4],
- p.argStack.a[i+5],
- )
- return nil
- }
- // As per 5177.Type2.pdf section 4.1 "Path Construction Operators",
- //
- // hhcurveto is:
- // - dy1 {dxa dxb dyb dxc}+
- //
- // vvcurveto is:
- // - dx1 {dya dxb dyb dyc}+
- //
- // hvcurveto is one of:
- // - dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf?
- // - {dxa dxb dyb dyc dyd dxe dye dxf}+ dyf?
- //
- // vhcurveto is one of:
- // - dy1 dx2 dy2 dx3 {dxa dxb dyb dyc dyd dxe dye dxf}* dyf?
- // - {dya dxb dyb dxc dxd dxe dye dyf}+ dxf?
- func t2CHhcurveto(p *psInterpreter) error { return t2CCurveto(p, false, false) }
- func t2CVvcurveto(p *psInterpreter) error { return t2CCurveto(p, false, true) }
- func t2CHvcurveto(p *psInterpreter) error { return t2CCurveto(p, true, false) }
- func t2CVhcurveto(p *psInterpreter) error { return t2CCurveto(p, true, true) }
- // t2CCurveto implements the hh / vv / hv / vh xxcurveto operators. N relative
- // cubic curve requires 6*N control points, but only 4*N+0 or 4*N+1 are used
- // here: all (or all but one) of the piecewise cubic curve's tangents are
- // implicitly horizontal or vertical.
- //
- // swap is whether that implicit horizontal / vertical constraint swaps as you
- // move along the piecewise cubic curve. If swap is false, the constraints are
- // either all horizontal or all vertical. If swap is true, it alternates.
- //
- // vertical is whether the first implicit constraint is vertical.
- func t2CCurveto(p *psInterpreter, swap, vertical bool) error {
- if !p.type2Charstrings.seenWidth || p.argStack.top < 4 {
- return errInvalidCFFTable
- }
- i := int32(0)
- switch p.argStack.top & 3 {
- case 0:
- // No-op.
- case 1:
- if swap {
- break
- }
- i = 1
- if vertical {
- p.type2Charstrings.x += p.argStack.a[0]
- } else {
- p.type2Charstrings.y += p.argStack.a[0]
- }
- default:
- return errInvalidCFFTable
- }
- for i != p.argStack.top {
- i = t2CCurveto4(p, swap, vertical, i)
- if i < 0 {
- return errInvalidCFFTable
- }
- if swap {
- vertical = !vertical
- }
- }
- return nil
- }
- func t2CCurveto4(p *psInterpreter, swap bool, vertical bool, i int32) (j int32) {
- if i+4 > p.argStack.top {
- return -1
- }
- dxa := p.argStack.a[i+0]
- dya := int32(0)
- dxb := p.argStack.a[i+1]
- dyb := p.argStack.a[i+2]
- dxc := p.argStack.a[i+3]
- dyc := int32(0)
- i += 4
- if vertical {
- dxa, dya = dya, dxa
- }
- if swap {
- if i+1 == p.argStack.top {
- dyc = p.argStack.a[i]
- i++
- }
- }
- if swap != vertical {
- dxc, dyc = dyc, dxc
- }
- p.type2Charstrings.cubeTo(dxa, dya, dxb, dyb, dxc, dyc)
- return i
- }
- func t2CRrcurveto(p *psInterpreter) error {
- if !p.type2Charstrings.seenWidth || p.argStack.top < 6 || p.argStack.top%6 != 0 {
- return errInvalidCFFTable
- }
- for i := int32(0); i != p.argStack.top; i += 6 {
- p.type2Charstrings.cubeTo(
- p.argStack.a[i+0],
- p.argStack.a[i+1],
- p.argStack.a[i+2],
- p.argStack.a[i+3],
- p.argStack.a[i+4],
- p.argStack.a[i+5],
- )
- }
- return nil
- }
- // For the flex operators, we ignore the flex depth and always produce cubic
- // segments, not linear segments. It's not obvious why the Type 2 Charstring
- // format cares about switching behavior based on a metric in pixels, not in
- // ideal font units. The Go vector rasterizer has no problems with almost
- // linear cubic segments.
- func t2CHflex(p *psInterpreter) error {
- p.type2Charstrings.cubeTo(
- p.argStack.a[0], 0,
- p.argStack.a[1], +p.argStack.a[2],
- p.argStack.a[3], 0,
- )
- p.type2Charstrings.cubeTo(
- p.argStack.a[4], 0,
- p.argStack.a[5], -p.argStack.a[2],
- p.argStack.a[6], 0,
- )
- return nil
- }
- func t2CHflex1(p *psInterpreter) error {
- dy1 := p.argStack.a[1]
- dy2 := p.argStack.a[3]
- dy5 := p.argStack.a[7]
- dy6 := -dy1 - dy2 - dy5
- p.type2Charstrings.cubeTo(
- p.argStack.a[0], dy1,
- p.argStack.a[2], dy2,
- p.argStack.a[4], 0,
- )
- p.type2Charstrings.cubeTo(
- p.argStack.a[5], 0,
- p.argStack.a[6], dy5,
- p.argStack.a[8], dy6,
- )
- return nil
- }
- // subrBias returns the subroutine index bias as per 5177.Type2.pdf section 4.7
- // "Subroutine Operators".
- func subrBias(numSubroutines int) int32 {
- if numSubroutines < 1240 {
- return 107
- }
- if numSubroutines < 33900 {
- return 1131
- }
- return 32768
- }
- func t2CCallgsubr(p *psInterpreter) error {
- 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 {
- if p.callStack.top == psCallStackSize || len(subrs) == 0 {
- return errInvalidCFFTable
- }
- length := uint32(len(p.instructions))
- p.callStack.a[p.callStack.top] = psCallStackEntry{
- offset: p.instrOffset + p.instrLength - length,
- length: length,
- }
- p.callStack.top++
- subrIndex := p.argStack.a[p.argStack.top-1] + subrBias(len(subrs)-1)
- if subrIndex < 0 || int32(len(subrs)-1) <= subrIndex {
- return errInvalidCFFTable
- }
- i := subrs[subrIndex+0]
- j := subrs[subrIndex+1]
- if j < i {
- return errInvalidCFFTable
- }
- if j-i > maxGlyphDataLength {
- return errUnsupportedGlyphDataLength
- }
- buf, err := p.type2Charstrings.b.view(&p.type2Charstrings.f.src, int(i), int(j-i))
- if err != nil {
- return err
- }
- p.instructions = buf
- p.instrOffset = i
- p.instrLength = j - i
- return nil
- }
- func t2CReturn(p *psInterpreter) error {
- if p.callStack.top <= 0 {
- return errInvalidCFFTable
- }
- p.callStack.top--
- o := p.callStack.a[p.callStack.top].offset
- n := p.callStack.a[p.callStack.top].length
- buf, err := p.type2Charstrings.b.view(&p.type2Charstrings.f.src, int(o), int(n))
- if err != nil {
- return err
- }
- p.instructions = buf
- p.instrOffset = o
- p.instrLength = n
- return nil
- }
- func t2CEndchar(p *psInterpreter) error {
- t2CReadWidth(p, 0)
- if p.argStack.top != 0 || p.hasMoreInstructions() {
- if p.argStack.top == 4 {
- // TODO: process the implicit "seac" command as per 5177.Type2.pdf
- // Appendix C "Compatibility and Deprecated Operators".
- return errUnsupportedType2Charstring
- }
- return errInvalidCFFTable
- }
- p.type2Charstrings.closePath()
- p.type2Charstrings.ended = true
- return nil
- }
|