github.com/andybalholm/giopdf@v0.0.0-20220317170119-aad9a095ad48/cff/parser.go (about) 1 package cff 2 3 // code is adapted from golang.org/x/image/font/sfnt 4 5 import ( 6 "encoding/binary" 7 "errors" 8 "fmt" 9 "math" 10 11 "github.com/benoitkugler/textlayout/fonts" 12 ps "github.com/benoitkugler/textlayout/fonts/psinterpreter" 13 "github.com/benoitkugler/textlayout/fonts/simpleencodings" 14 ) 15 16 var ( 17 errInvalidCFFTable = errors.New("invalid CFF font file") 18 errUnsupportedCFFVersion = errors.New("unsupported CFF version") 19 errUnsupportedRealNumberEncoding = errors.New("unsupported real number encoding") 20 errUnsupportedCFFFDSelectTable = errors.New("unsupported FD Select version") 21 errUnsupportedNumberOfSubroutines = errors.New("unsupported number of subroutines") 22 23 be = binary.BigEndian 24 ) 25 26 const ( 27 // since SID = 0 means .notdef, we use a reserved value 28 // to mean unset 29 unsetSID = uint16(0xFFFF) 30 ) 31 32 type userStrings [][]byte 33 34 // return either the predefined string or the user defined one 35 func (u userStrings) getString(sid uint16) (string, error) { 36 if sid == unsetSID { 37 return "", nil 38 } 39 if sid < 391 { 40 return stdStrings[sid], nil 41 } 42 sid -= 391 43 if int(sid) >= len(u) { 44 return "", fmt.Errorf("invalid glyph index %d", sid) 45 } 46 return string(u[sid]), nil 47 } 48 49 // Compact Font Format (CFF) fonts are written in PostScript, a stack-based 50 // programming language. 51 // 52 // A fundamental concept is a DICT, or a key-value map, expressed in reverse 53 // Polish notation. For example, this sequence of operations: 54 // - push the number 379 55 // - version operator 56 // - push the number 392 57 // - Notice operator 58 // - etc 59 // - push the number 100 60 // - push the number 0 61 // - push the number 500 62 // - push the number 800 63 // - FontBBox operator 64 // - etc 65 // defines a DICT that maps "version" to the String ID (SID) 379, "Notice" to 66 // the SID 392, "FontBBox" to the four numbers [100, 0, 500, 800], etc. 67 // 68 // The first 391 String IDs (starting at 0) are predefined as per the CFF spec 69 // Appendix A, in 5176.CFF.pdf referenced below. For example, 379 means 70 // "001.000". String ID 392 is not predefined, and is mapped by a separate 71 // structure, the "String INDEX", inside the CFF data. (String ID 391 is also 72 // not predefined. Specifically for ../testdata/CFFTest.otf, 391 means 73 // "uni4E2D", as this font contains a glyph for U+4E2D). 74 // 75 // The actual glyph vectors are similarly encoded (in PostScript), in a format 76 // called Type 2 Charstrings. The wire encoding is similar to but not exactly 77 // the same as CFF's. For example, the byte 0x05 means FontBBox for CFF DICTs, 78 // but means rlineto (relative line-to) for Type 2 Charstrings. See 79 // 5176.CFF.pdf Appendix H and 5177.Type2.pdf Appendix A in the PDF files 80 // referenced below. 81 // 82 // The relevant specifications are: 83 // - http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5176.CFF.pdf 84 // - http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5177.Type2.pdf 85 type cffParser struct { 86 src []byte // whole input 87 offset int // current position 88 } 89 90 func (p *cffParser) parse() ([]Font, error) { 91 // header was checked prior to this call 92 93 // Parse the Name INDEX. 94 fontNames, err := p.parseNames() 95 if err != nil { 96 return nil, err 97 } 98 99 topDicts, err := p.parseTopDicts() 100 if err != nil { 101 return nil, err 102 } 103 // 5176.CFF.pdf section 8 "Top DICT INDEX" says that the count here 104 // should match the count of the Name INDEX 105 if len(topDicts) != len(fontNames) { 106 return nil, fmt.Errorf("top DICT length doest not match Names (%d, %d)", len(topDicts), 107 len(fontNames)) 108 } 109 110 // parse the String INDEX. 111 strs, err := p.parseUserStrings() 112 if err != nil { 113 return nil, err 114 } 115 116 out := make([]Font, len(topDicts)) 117 118 // use the strings to fetch the PSInfo 119 for i, topDict := range topDicts { 120 out[i].fontName = fontNames[i] 121 out[i].userStrings = strs 122 out[i].PSInfo, err = topDict.toInfo(strs) 123 if err != nil { 124 return nil, err 125 } 126 out[i].cidFontName, err = strs.getString(topDict.cidFontName) 127 if err != nil { 128 return nil, err 129 } 130 out[i].FontMatrix = topDict.fontMatrix 131 } 132 133 // Parse the Global Subrs [Subroutines] INDEX, 134 // shared among all fonts. 135 globalSubrs, err := p.parseIndex() 136 if err != nil { 137 return nil, err 138 } 139 140 for i, topDict := range topDicts { 141 out[i].globalSubrs = globalSubrs 142 143 // Parse the CharStrings INDEX, whose location was found in the Top DICT. 144 if err = p.seek(topDict.charStringsOffset); err != nil { 145 return nil, err 146 } 147 out[i].charstrings, err = p.parseIndex() 148 if err != nil { 149 return nil, err 150 } 151 numGlyphs := uint16(len(out[i].charstrings)) 152 153 out[i].charset, err = p.parseCharset(topDict.charsetOffset, numGlyphs) 154 if err != nil { 155 return nil, err 156 } 157 158 out[i].Encoding, err = p.parseEncoding(topDict.encodingOffset, numGlyphs, out[i].charset, strs) 159 if err != nil { 160 return nil, err 161 } 162 163 if !topDict.isCIDFont { 164 // Parse the Private DICT, whose location was found in the Top DICT. 165 var localSubrs [][]byte 166 var priv privateDict 167 priv, localSubrs, err = p.parsePrivateDICT(topDict.privateDictOffset, topDict.privateDictLength) 168 if err != nil { 169 return nil, err 170 } 171 out[i].localSubrs = [][][]byte{localSubrs} 172 out[i].priv = []privateDict{priv} 173 } else { 174 // Parse the Font Dict Select data, whose location was found in the Top 175 // DICT. 176 out[i].fdSelect, err = p.parseFDSelect(topDict.fdSelect, numGlyphs) 177 if err != nil { 178 return nil, err 179 } 180 indexExtent := out[i].fdSelect.extent() 181 182 // Parse the Font Dicts. Each one contains its own Private DICT. 183 if err = p.seek(topDict.fdArray); err != nil { 184 return nil, err 185 } 186 topDicts, err := p.parseTopDicts() 187 if err != nil { 188 return nil, err 189 } 190 if len(topDicts) < indexExtent { 191 return nil, fmt.Errorf("invalid number of font dicts: %d (for %d)", 192 len(topDicts), indexExtent) 193 } 194 out[i].localSubrs = make([][][]byte, len(topDicts)) 195 out[i].priv = make([]privateDict, len(topDicts)) 196 for j, topDict := range topDicts { 197 out[i].priv[j], out[i].localSubrs[j], err = p.parsePrivateDICT(topDict.privateDictOffset, topDict.privateDictLength) 198 if err != nil { 199 return nil, err 200 } 201 } 202 } 203 } 204 205 return out, nil 206 } 207 208 func (p *cffParser) parseTopDicts() ([]topDictData, error) { 209 // Parse the Top DICT INDEX. 210 instructions, err := p.parseIndex() 211 if err != nil { 212 return nil, err 213 } 214 215 out := make([]topDictData, len(instructions)) // guarded by uint16 max size 216 var psi ps.Machine 217 for i, buf := range instructions { 218 topDict := &out[i] 219 220 // set default value before parsing 221 topDict.underlinePosition = -100 222 topDict.underlineThickness = 50 223 topDict.version = unsetSID 224 topDict.notice = unsetSID 225 topDict.fullName = unsetSID 226 topDict.familyName = unsetSID 227 topDict.weight = unsetSID 228 topDict.cidFontName = unsetSID 229 topDict.fontMatrix = []float32{0.001, 0, 0, 0.001, 0, 0} 230 231 if err = psi.Run(buf, nil, nil, topDict); err != nil { 232 return nil, err 233 } 234 } 235 return out, nil 236 } 237 238 // parse the general form of an index 239 func (p *cffParser) parseIndex() ([][]byte, error) { 240 count, offSize, err := p.parseIndexHeader() 241 if err != nil { 242 return nil, err 243 } 244 if count == 0 { 245 return nil, nil 246 } 247 248 out := make([][]byte, count) 249 250 stringsLocations := make([]uint32, int(count)+1) 251 if err := p.parseIndexLocations(stringsLocations, offSize); err != nil { 252 return nil, err 253 } 254 255 for i := range out { 256 length := stringsLocations[i+1] - stringsLocations[i] 257 buf, err := p.read(int(length)) 258 if err != nil { 259 return nil, err 260 } 261 out[i] = buf 262 } 263 return out, nil 264 } 265 266 // parse the Name INDEX 267 func (p *cffParser) parseNames() ([][]byte, error) { 268 return p.parseIndex() 269 } 270 271 // parse the String INDEX 272 func (p *cffParser) parseUserStrings() (userStrings, error) { 273 index, err := p.parseIndex() 274 return userStrings(index), err 275 } 276 277 // Parse the charset data, whose location was found in the Top DICT. 278 func (p *cffParser) parseCharset(charsetOffset int32, numGlyphs uint16) ([]uint16, error) { 279 // Predefined charset may have offset of 0 to 2 // Table 22 280 var charset []uint16 281 switch charsetOffset { 282 case 0: // ISOAdobe 283 charset = charsetISOAdobe[:] 284 case 1: // Expert 285 charset = charsetExpert[:] 286 case 2: // ExpertSubset 287 charset = charsetExpertSubset[:] 288 default: // custom 289 if err := p.seek(charsetOffset); err != nil { 290 return nil, err 291 } 292 buf, err := p.read(1) 293 if err != nil { 294 return nil, err 295 } 296 charset = make([]uint16, numGlyphs) 297 switch buf[0] { // format 298 case 0: 299 buf, err = p.read(2 * (int(numGlyphs) - 1)) // ".notdef" is omited, and has an implicit SID of 0 300 if err != nil { 301 return nil, err 302 } 303 for i := uint16(1); i < numGlyphs; i++ { 304 charset[i] = be.Uint16(buf[2*i-2:]) 305 } 306 case 1: 307 for i := uint16(1); i < numGlyphs; { 308 buf, err = p.read(3) 309 if err != nil { 310 return nil, err 311 } 312 first, nLeft := be.Uint16(buf), uint16(buf[2]) 313 for j := uint16(0); j <= nLeft && i < numGlyphs; j++ { 314 charset[i] = first + j 315 i++ 316 } 317 } 318 case 2: 319 for i := uint16(1); i < numGlyphs; { 320 buf, err = p.read(4) 321 if err != nil { 322 return nil, err 323 } 324 first, nLeft := be.Uint16(buf), be.Uint16(buf[2:]) 325 for j := uint16(0); j <= nLeft && i < numGlyphs; j++ { 326 charset[i] = first + j 327 i++ 328 } 329 } 330 default: 331 return nil, fmt.Errorf("invalid custom charset format %d", buf[0]) 332 } 333 } 334 return charset, nil 335 } 336 337 // Parse the encoding data, whose location was found in the Top DICT. 338 func (p *cffParser) parseEncoding(encodingOffset int32, numGlyphs uint16, charset []uint16, strs userStrings) (*simpleencodings.Encoding, error) { 339 // Predefined encoding may have offset of 0 to 1 // Table 16 340 switch encodingOffset { 341 case 0: // Standard 342 return &simpleencodings.AdobeStandard, nil 343 case 1: // Expert 344 return &expertEncoding, nil 345 default: // custom 346 var encoding simpleencodings.Encoding 347 if err := p.seek(encodingOffset); err != nil { 348 return nil, err 349 } 350 buf, err := p.read(2) 351 if err != nil { 352 return nil, err 353 } 354 format, size, charL := buf[0], int32(buf[1]), int32(len(charset)) 355 // high order bit may be set for supplemental encoding data 356 switch f := format & 0xf; f { // 0111_1111 357 case 0: 358 if size > int32(numGlyphs) { // truncate 359 size = int32(numGlyphs) 360 } 361 buf, err = p.read(int(size)) 362 if err != nil { 363 return nil, err 364 } 365 for i := int32(1); i < size && i < charL; i++ { 366 c := buf[i-1] 367 encoding[c], err = strs.getString(charset[i]) 368 if err != nil { 369 return nil, err 370 } 371 } 372 case 1: 373 buf, err = p.read(2 * int(size)) 374 if err != nil { 375 return nil, err 376 } 377 nCodes := int32(1) 378 for i := int32(0); i < size; i++ { 379 c, nLeft := buf[2*i], buf[2*i+1] 380 for j := byte(0); j < nLeft && nCodes < int32(numGlyphs) && nCodes < charL; j++ { 381 encoding[c], err = strs.getString(charset[nCodes]) 382 if err != nil { 383 return nil, err 384 } 385 nCodes++ 386 c++ 387 } 388 } 389 default: 390 return nil, fmt.Errorf("invalid custom encoding format %d", f) 391 } 392 393 if format&0x80 != 0 { // 1000_000 394 nSupsBuf, err := p.read(1) 395 if err != nil { 396 return nil, err 397 } 398 buf, err := p.read(3 * int(nSupsBuf[0])) 399 if err != nil { 400 return nil, err 401 } 402 for i := byte(0); i < nSupsBuf[0]; i++ { 403 code, sid := buf[3*i], be.Uint16(buf[3*i+1:]) 404 encoding[code], err = strs.getString(sid) 405 if err != nil { 406 return nil, err 407 } 408 } 409 } 410 return &encoding, nil 411 } 412 } 413 414 // fdSelect holds a CFF font's Font Dict Select data. 415 type fdSelect interface { 416 fontDictIndex(glyph fonts.GID) (byte, error) 417 // return the maximum index + 1 (it's the length of an array 418 // which can be safely indexed by the indexes) 419 extent() int 420 } 421 422 type fdSelect0 []byte 423 424 func (fds fdSelect0) fontDictIndex(glyph fonts.GID) (byte, error) { 425 if int(glyph) >= len(fds) { 426 return 0, errors.New("invalid glyph index") 427 } 428 return fds[glyph], nil 429 } 430 431 func (fds fdSelect0) extent() int { 432 max := -1 433 for _, b := range fds { 434 if int(b) > max { 435 max = int(b) 436 } 437 } 438 return max + 1 439 } 440 441 type range3 struct { 442 first fonts.GID 443 fd byte 444 } 445 446 type fdSelect3 struct { 447 ranges []range3 448 sentinel fonts.GID // = numGlyphs 449 } 450 451 func (fds fdSelect3) fontDictIndex(x fonts.GID) (byte, error) { 452 lo, hi := 0, len(fds.ranges) 453 for lo < hi { 454 i := (lo + hi) / 2 455 r := fds.ranges[i] 456 xlo := r.first 457 if x < xlo { 458 hi = i 459 continue 460 } 461 xhi := fds.sentinel 462 if i < len(fds.ranges)-1 { 463 xhi = fds.ranges[i+1].first 464 } 465 if xhi <= x { 466 lo = i + 1 467 continue 468 } 469 return r.fd, nil 470 } 471 return 0, errors.New("invalid glyph index") 472 } 473 474 func (fds fdSelect3) extent() int { 475 max := -1 476 for _, b := range fds.ranges { 477 if int(b.fd) > max { 478 max = int(b.fd) 479 } 480 } 481 return max + 1 482 } 483 484 // parseFDSelect parses the Font Dict Select data as per 5176.CFF.pdf section 485 // 19 "FDSelect". 486 func (p *cffParser) parseFDSelect(offset int32, numGlyphs uint16) (fdSelect, error) { 487 if err := p.seek(offset); err != nil { 488 return nil, err 489 } 490 buf, err := p.read(1) 491 if err != nil { 492 return nil, err 493 } 494 switch buf[0] { // format 495 case 0: 496 if len(p.src) < p.offset+int(numGlyphs) { 497 return nil, errors.New("invalid FDSelect data") 498 } 499 return fdSelect0(p.src[p.offset : p.offset+int(numGlyphs)]), nil 500 case 3: 501 buf, err = p.read(2) 502 if err != nil { 503 return nil, err 504 } 505 numRanges := be.Uint16(buf) 506 if len(p.src) < p.offset+3*int(numRanges)+2 { 507 return nil, errors.New("invalid FDSelect data") 508 } 509 out := fdSelect3{ 510 sentinel: fonts.GID(numGlyphs), 511 ranges: make([]range3, numRanges), 512 } 513 for i := range out.ranges { 514 // buf holds the range [xlo, xhi). 515 out.ranges[i].first = fonts.GID(be.Uint16(p.src[p.offset+3*i:])) 516 out.ranges[i].fd = p.src[p.offset+3*i+2] 517 } 518 return out, nil 519 } 520 return nil, errUnsupportedCFFFDSelectTable 521 } 522 523 // Parse Private DICT and the Local Subrs [Subroutines] INDEX 524 func (p *cffParser) parsePrivateDICT(offset, length int32) (priv privateDict, subrs [][]byte, err error) { 525 if length == 0 { 526 return privateDict{}, nil, nil 527 } 528 if err := p.seek(offset); err != nil { 529 return privateDict{}, nil, err 530 } 531 buf, err := p.read(int(length)) 532 if err != nil { 533 return privateDict{}, nil, err 534 } 535 var psi ps.Machine 536 if err = psi.Run(buf, nil, nil, &priv); err != nil { 537 return privateDict{}, nil, err 538 } 539 540 if priv.subrsOffset == 0 { 541 return priv, nil, nil 542 } 543 544 // "The local subrs offset is relative to the beginning of the Private DICT data" 545 if err = p.seek(offset + priv.subrsOffset); err != nil { 546 return privateDict{}, nil, errors.New("invalid local subroutines offset") 547 } 548 subrs, err = p.parseIndex() 549 if err != nil { 550 return privateDict{}, nil, err 551 } 552 return priv, subrs, nil 553 } 554 555 // read returns the n bytes from p.offset and advances p.offset by n. 556 func (p *cffParser) read(n int) ([]byte, error) { 557 if n < 0 || len(p.src) < p.offset+n { 558 return nil, errors.New("invalid CFF font file (EOF)") 559 } 560 out := p.src[p.offset : p.offset+n] 561 p.offset += n 562 return out, nil 563 } 564 565 // skip advances p.offset by n. 566 func (p *cffParser) skip(n int) error { 567 if len(p.src) < p.offset+n { 568 return errors.New("invalid CFF font file (EOF)") 569 } 570 p.offset += n 571 return nil 572 } 573 574 func (p *cffParser) seek(offset int32) error { 575 if offset < 0 || len(p.src) < int(offset) { 576 return errors.New("invalid CFF font file (EOF)") 577 } 578 p.offset = int(offset) 579 return nil 580 } 581 582 func bigEndian(b []byte) uint32 { 583 switch len(b) { 584 case 1: 585 return uint32(b[0]) 586 case 2: 587 return uint32(b[0])<<8 | uint32(b[1]) 588 case 3: 589 return uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2]) 590 case 4: 591 return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]) 592 } 593 panic("unreachable") 594 } 595 596 func (p *cffParser) parseIndexHeader() (count uint16, offSize int32, err error) { 597 buf, err := p.read(2) 598 if err != nil { 599 return 0, 0, err 600 } 601 count = be.Uint16(buf) 602 // 5176.CFF.pdf section 5 "INDEX Data" says that "An empty INDEX is 603 // represented by a count field with a 0 value and no additional fields. 604 // Thus, the total size of an empty INDEX is 2 bytes". 605 if count == 0 { 606 return count, 0, nil 607 } 608 buf, err = p.read(1) 609 if err != nil { 610 return 0, 0, err 611 } 612 offSize = int32(buf[0]) 613 if offSize < 1 || 4 < offSize { 614 return 0, 0, fmt.Errorf("invalid offset size %d", offSize) 615 } 616 return count, offSize, nil 617 } 618 619 func (p *cffParser) parseIndexLocations(dst []uint32, offSize int32) error { 620 if len(dst) == 0 { 621 return nil 622 } 623 buf, err := p.read(len(dst) * int(offSize)) 624 if err != nil { 625 return err 626 } 627 628 prev := uint32(0) 629 for i := range dst { 630 loc := bigEndian(buf[:offSize]) 631 buf = buf[offSize:] 632 633 // Locations are off by 1 byte. 5176.CFF.pdf section 5 "INDEX Data" 634 // says that "Offsets in the offset array are relative to the byte that 635 // precedes the object data... This ensures that every object has a 636 // corresponding offset which is always nonzero". 637 if loc == 0 { 638 return errors.New("invalid CFF index locations (0)") 639 } 640 loc-- 641 642 // In the same paragraph, "Therefore the first element of the offset 643 // array is always 1" before correcting for the off-by-1. 644 if i == 0 { 645 if loc != 0 { 646 return errors.New("invalid CFF index locations (not 0)") 647 } 648 } else if loc < prev { // Check that locations are increasing 649 return errors.New("invalid CFF index locations (not increasing)") 650 } 651 652 // Check that locations are in bounds. 653 if uint32(len(p.src)-p.offset) < loc { 654 return errors.New("invalid CFF index locations (out of bounds)") 655 } 656 657 dst[i] = uint32(p.offset) + loc 658 prev = loc 659 } 660 return nil 661 } 662 663 // topDictData contains fields specific to the Top DICT context. 664 type topDictData struct { 665 // SIDs, to be decoded using the string index 666 version, notice, fullName, familyName, weight uint16 667 isFixedPitch bool 668 italicAngle, underlinePosition, underlineThickness float32 669 charsetOffset int32 670 encodingOffset int32 671 charStringsOffset int32 672 fdArray int32 673 fdSelect int32 674 isCIDFont bool 675 cidFontName uint16 676 privateDictOffset int32 677 privateDictLength int32 678 fontMatrix []float32 679 } 680 681 // resolve the strings 682 func (topDict topDictData) toInfo(strs userStrings) (out fonts.PSInfo, err error) { 683 out.Version, err = strs.getString(topDict.version) 684 if err != nil { 685 return out, err 686 } 687 out.Notice, err = strs.getString(topDict.notice) 688 if err != nil { 689 return out, err 690 } 691 out.FullName, err = strs.getString(topDict.fullName) 692 if err != nil { 693 return out, err 694 } 695 out.FamilyName, err = strs.getString(topDict.familyName) 696 if err != nil { 697 return out, err 698 } 699 out.Weight, err = strs.getString(topDict.weight) 700 if err != nil { 701 return out, err 702 } 703 out.IsFixedPitch = topDict.isFixedPitch 704 out.ItalicAngle = int(topDict.italicAngle) 705 out.UnderlinePosition = int(topDict.underlinePosition) 706 out.UnderlineThickness = int(topDict.underlineThickness) 707 return out, nil 708 } 709 710 func (topDict *topDictData) Context() ps.PsContext { return ps.TopDict } 711 712 func (topDict *topDictData) Apply(op ps.PsOperator, state *ps.Machine) error { 713 ops := topDictOperators[0] 714 if op.IsEscaped { 715 ops = topDictOperators[1] 716 } 717 if int(op.Operator) >= len(ops) { 718 return fmt.Errorf("invalid operator %s in Top Dict", op) 719 } 720 opFunc := ops[op.Operator] 721 if opFunc.run == nil { 722 return fmt.Errorf("invalid operator %s in Top Dict", op) 723 } 724 if state.ArgStack.Top < opFunc.numPop { 725 return fmt.Errorf("invalid number of arguments for operator %s in Top Dict", op) 726 } 727 err := opFunc.run(topDict, state) 728 if err != nil { 729 return err 730 } 731 err = state.ArgStack.PopN(opFunc.numPop) 732 return err 733 } 734 735 // The Top DICT operators are defined by 5176.CFF.pdf Table 9 "Top DICT 736 // Operator Entries" and Table 10 "CIDFont Operator Extensions". 737 type topDictOperator struct { 738 // run is the function that implements the operator. Nil means that we 739 // ignore the operator, other than popping its arguments off the stack. 740 run func(*topDictData, *ps.Machine) error 741 742 // numPop is the number of stack values to pop. -1 means "array" and -2 743 // means "delta" as per 5176.CFF.pdf Table 6 "Operand Types". 744 numPop int32 745 } 746 747 func topDictNoOp(*topDictData, *ps.Machine) error { return nil } 748 749 var topDictOperators = [2][]topDictOperator{ 750 // 1-byte operators. 751 { 752 0: {func(t *topDictData, s *ps.Machine) error { 753 t.version = s.ArgStack.Uint16() 754 return nil 755 }, +1 /*version*/}, 756 1: {func(t *topDictData, s *ps.Machine) error { 757 t.notice = s.ArgStack.Uint16() 758 return nil 759 }, +1 /*Notice*/}, 760 2: {func(t *topDictData, s *ps.Machine) error { 761 t.fullName = s.ArgStack.Uint16() 762 return nil 763 }, +1 /*FullName*/}, 764 3: {func(t *topDictData, s *ps.Machine) error { 765 t.familyName = s.ArgStack.Uint16() 766 return nil 767 }, +1 /*FamilyName*/}, 768 4: {func(t *topDictData, s *ps.Machine) error { 769 t.weight = s.ArgStack.Uint16() 770 return nil 771 }, +1 /*Weight*/}, 772 5: {topDictNoOp, -1 /*FontBBox*/}, 773 13: {topDictNoOp, +1 /*UniqueID*/}, 774 14: {topDictNoOp, -1 /*XUID*/}, 775 15: {func(t *topDictData, s *ps.Machine) error { 776 t.charsetOffset = s.ArgStack.Vals[s.ArgStack.Top-1] 777 return nil 778 }, +1 /*charset*/}, 779 16: {func(t *topDictData, s *ps.Machine) error { 780 t.encodingOffset = s.ArgStack.Vals[s.ArgStack.Top-1] 781 return nil 782 }, +1 /*Encoding*/}, 783 17: {func(t *topDictData, s *ps.Machine) error { 784 t.charStringsOffset = s.ArgStack.Vals[s.ArgStack.Top-1] 785 return nil 786 }, +1 /*CharStrings*/}, 787 18: {func(t *topDictData, s *ps.Machine) error { 788 t.privateDictLength = s.ArgStack.Vals[s.ArgStack.Top-2] 789 t.privateDictOffset = s.ArgStack.Vals[s.ArgStack.Top-1] 790 return nil 791 }, +2 /*Private*/}, 792 }, 793 // 2-byte operators. The first byte is the escape byte. 794 { 795 0: {topDictNoOp, +1 /*Copyright*/}, 796 1: {func(t *topDictData, s *ps.Machine) error { 797 t.isFixedPitch = s.ArgStack.Vals[s.ArgStack.Top-1] == 1 798 return nil 799 }, +1 /*isFixedPitch*/}, 800 2: {func(t *topDictData, s *ps.Machine) error { 801 t.italicAngle = s.ArgStack.Float() 802 return nil 803 }, +1 /*ItalicAngle*/}, 804 3: {func(t *topDictData, s *ps.Machine) error { 805 t.underlinePosition = s.ArgStack.Float() 806 return nil 807 }, +1 /*UnderlinePosition*/}, 808 4: {func(t *topDictData, s *ps.Machine) error { 809 t.underlineThickness = s.ArgStack.Float() 810 return nil 811 }, +1 /*UnderlineThickness*/}, 812 5: {topDictNoOp, +1 /*PaintType*/}, 813 6: {func(_ *topDictData, i *ps.Machine) error { 814 if version := i.ArgStack.Vals[i.ArgStack.Top-1]; version != 2 { 815 return fmt.Errorf("charstring type %d not supported", version) 816 } 817 return nil 818 }, +1 /*CharstringType*/}, 819 7: {func(t *topDictData, s *ps.Machine) error { 820 if s.ArgStack.Top != 6 { 821 return fmt.Errorf("wrong number of arguments for FontMatrix: got %d, want 6", s.ArgStack.Top) 822 } 823 t.fontMatrix = make([]float32, 6) 824 for i := range t.fontMatrix { 825 t.fontMatrix[i] = math.Float32frombits(uint32(s.ArgStack.Vals[i])) 826 } 827 return nil 828 }, -1 /*FontMatrix*/}, 829 8: {topDictNoOp, +1 /*StrokeWidth*/}, 830 20: {topDictNoOp, +1 /*SyntheticBase*/}, 831 21: {topDictNoOp, +1 /*PostScript*/}, 832 22: {topDictNoOp, +1 /*BaseFontName*/}, 833 23: {topDictNoOp, -2 /*BaseFontBlend*/}, 834 30: {func(t *topDictData, _ *ps.Machine) error { 835 t.isCIDFont = true 836 return nil 837 }, +3 /*ROS*/}, 838 31: {topDictNoOp, +1 /*CIDFontVersion*/}, 839 32: {topDictNoOp, +1 /*CIDFontRevision*/}, 840 33: {topDictNoOp, +1 /*CIDFontType*/}, 841 34: {topDictNoOp, +1 /*CIDCount*/}, 842 35: {topDictNoOp, +1 /*UIDBase*/}, 843 36: {func(t *topDictData, s *ps.Machine) error { 844 t.fdArray = s.ArgStack.Vals[s.ArgStack.Top-1] 845 return nil 846 }, +1 /*FDArray*/}, 847 37: {func(t *topDictData, s *ps.Machine) error { 848 t.fdSelect = s.ArgStack.Vals[s.ArgStack.Top-1] 849 return nil 850 }, +1 /*FDSelect*/}, 851 38: {func(t *topDictData, s *ps.Machine) error { 852 t.cidFontName = s.ArgStack.Uint16() 853 return nil 854 }, +1 /*FontName*/}, 855 }, 856 } 857 858 // privateDict contains fields specific to the Private DICT context. 859 type privateDict struct { 860 subrsOffset int32 861 defaultWidthX, nominalWidthX int32 862 } 863 864 func (privateDict) Context() ps.PsContext { return ps.PrivateDict } 865 866 // The Private DICT operators are defined by 5176.CFF.pdf Table 23 "Private 867 // DICT Operators". 868 func (priv *privateDict) Apply(op ps.PsOperator, state *ps.Machine) error { 869 if !op.IsEscaped { // 1-byte operators. 870 switch op.Operator { 871 case 6, 7, 8, 9: // "BlueValues" "OtherBlues" "FamilyBlues" "FamilyOtherBlues" 872 return state.ArgStack.PopN(-2) 873 case 10, 11: // "StdHW" "StdVW" 874 return state.ArgStack.PopN(1) 875 case 20: // "defaultWidthX" 876 if state.ArgStack.Top < 1 { 877 return errors.New("invalid stack size for 'defaultWidthX' in private Dict charstring") 878 } 879 priv.defaultWidthX = state.ArgStack.Vals[state.ArgStack.Top-1] 880 return state.ArgStack.PopN(1) 881 case 21: // "nominalWidthX" 882 if state.ArgStack.Top < 1 { 883 return errors.New("invalid stack size for 'nominalWidthX' in private Dict charstring") 884 } 885 priv.nominalWidthX = state.ArgStack.Vals[state.ArgStack.Top-1] 886 return state.ArgStack.PopN(1) 887 case 19: // "Subrs" pop 1 888 if state.ArgStack.Top < 1 { 889 return errors.New("invalid stack size for 'subrs' in private Dict charstring") 890 } 891 priv.subrsOffset = state.ArgStack.Vals[state.ArgStack.Top-1] 892 return state.ArgStack.PopN(1) 893 } 894 } else { // 2-byte operators. The first byte is the escape byte. 895 switch op.Operator { 896 case 9, 10, 11, 14, 17, 18, 19: // "BlueScale" "BlueShift" "BlueFuzz" "ForceBold" "LanguageGroup" "ExpansionFactor" "initialRandomSeed" 897 return state.ArgStack.PopN(1) 898 case 12, 13: // "StemSnapH" "StemSnapV" 899 return state.ArgStack.PopN(-2) 900 } 901 } 902 return errors.New("invalid operand in private Dict charstring") 903 }