github.com/pdfcpu/pdfcpu@v0.11.1/pkg/font/install.go (about) 1 /* 2 Copyright 2019 The pdfcpu Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Package font provides support for TrueType fonts. 18 package font 19 20 import ( 21 "bytes" 22 "encoding/binary" 23 "encoding/gob" 24 "fmt" 25 "io" 26 "os" 27 "path/filepath" 28 "reflect" 29 "sort" 30 "strings" 31 "unicode/utf16" 32 33 "github.com/pdfcpu/pdfcpu/pkg/log" 34 "github.com/pkg/errors" 35 ) 36 37 const ( 38 sfntVersionTrueType = "\x00\x01\x00\x00" 39 sfntVersionTrueTypeApple = "true" 40 sfntVersionCFF = "OTTO" 41 ttfHeadMagicNumber = 0x5F0F3CF5 42 ttcTag = "ttcf" 43 ) 44 45 type ttf struct { 46 PostscriptName string // name: NameID 6 47 Protected bool // OS/2: fsType 48 UnitsPerEm int // head: unitsPerEm 49 Ascent int // OS/2: sTypoAscender 50 Descent int // OS/2: sTypoDescender 51 CapHeight int // OS/2: sCapHeight 52 FirstChar uint16 // OS/2: fsFirstCharIndex 53 LastChar uint16 // OS/2: fsLastCharIndex 54 UnicodeRange [4]uint32 // OS/2: Unicode Character Range 55 LLx, LLy, URx, URy float64 // head: xMin, yMin, xMax, yMax (fontbox) 56 ItalicAngle float64 // post: italicAngle 57 FixedPitch bool // post: isFixedPitch 58 Bold bool // OS/2: usWeightClass == 7 59 HorMetricsCount int // hhea: numOfLongHorMetrics 60 GlyphCount int // maxp: numGlyphs 61 GlyphWidths []int // hmtx: fd.HorMetricsCount.advanceWidth 62 Chars map[uint32]uint16 // cmap: Unicode character to glyph index 63 ToUnicode map[uint16]uint32 // map glyph index to unicode character 64 Planes map[int]bool // used Unicode planes 65 FontFile []byte 66 } 67 68 func (fd ttf) String() string { 69 return fmt.Sprintf(` 70 PostscriptName = %s 71 Protected = %t 72 UnitsPerEm = %d 73 Ascent = %d 74 Descent = %d 75 CapHeight = %d 76 FirstChar = %d 77 LastChar = %d 78 FontBoundingBox = (%.2f, %.2f, %.2f, %.2f) 79 ItalicAngle = %.2f 80 FixedPitch = %t 81 Bold = %t 82 HorMetricsCount = %d 83 GlyphCount = %d`, 84 fd.PostscriptName, 85 fd.Protected, 86 fd.UnitsPerEm, 87 fd.Ascent, 88 fd.Descent, 89 fd.CapHeight, 90 fd.FirstChar, 91 fd.LastChar, 92 fd.LLx, fd.LLy, fd.URx, fd.URy, 93 fd.ItalicAngle, 94 fd.FixedPitch, 95 fd.Bold, 96 fd.HorMetricsCount, 97 fd.GlyphCount, 98 ) 99 } 100 101 func (fd ttf) toPDFGlyphSpace(i int) int { 102 return i * 1000 / fd.UnitsPerEm 103 } 104 105 type myUint32 []uint32 106 107 func (f myUint32) Len() int { 108 return len(f) 109 } 110 111 func (f myUint32) Less(i, j int) bool { 112 return f[i] < f[j] 113 } 114 115 func (f myUint32) Swap(i, j int) { 116 f[i], f[j] = f[j], f[i] 117 } 118 119 func (fd ttf) PrintChars() string { 120 var min = uint16(0xFFFF) 121 var max uint16 122 var sb strings.Builder 123 sb.WriteByte(0x0a) 124 125 keys := make(myUint32, 0, len(fd.Chars)) 126 for k := range fd.Chars { 127 keys = append(keys, k) 128 } 129 sort.Sort(keys) 130 131 for _, c := range keys { 132 g := fd.Chars[c] 133 if g > max { 134 max = g 135 } 136 if g < min { 137 min = g 138 } 139 sb.WriteString(fmt.Sprintf("#%x -> #%x(%d)\n", c, g, g)) 140 } 141 fmt.Printf("using glyphs[%08x,%08x] [%d,%d]\n", min, max, min, max) 142 fmt.Printf("using glyphs #%x - #%x (%d-%d)\n", min, max, min, max) 143 return sb.String() 144 } 145 146 type table struct { 147 chksum uint32 148 off uint32 149 size uint32 150 padded uint32 151 data []byte 152 } 153 154 func (t table) uint16(off int) uint16 { 155 return binary.BigEndian.Uint16(t.data[off:]) 156 } 157 158 func (t table) int16(off int) int16 { 159 return int16(t.uint16(off)) 160 } 161 162 func (t table) uint32(off int) uint32 { 163 return binary.BigEndian.Uint32(t.data[off:]) 164 } 165 166 func (t table) fixed32(off int) float64 { 167 return float64(t.uint32(off)) / 65536.0 168 } 169 170 func (t table) parseFontHeaderTable(fd *ttf) error { 171 // table "head" 172 magic := t.uint32(12) 173 if magic != ttfHeadMagicNumber { 174 return fmt.Errorf("parseHead: wrong magic number") 175 } 176 177 unitsPerEm := t.uint16(18) 178 //fmt.Printf("unitsPerEm: %d\n", unitsPerEm) 179 fd.UnitsPerEm = int(unitsPerEm) 180 181 llx := t.int16(36) 182 //fmt.Printf("llx: %d\n", llx) 183 fd.LLx = float64(fd.toPDFGlyphSpace(int(llx))) 184 185 lly := t.int16(38) 186 //fmt.Printf("lly: %d\n", lly) 187 fd.LLy = float64(fd.toPDFGlyphSpace(int(lly))) 188 189 urx := t.int16(40) 190 //fmt.Printf("urx: %d\n", urx) 191 fd.URx = float64(fd.toPDFGlyphSpace(int(urx))) 192 193 ury := t.int16(42) 194 //fmt.Printf("ury: %d\n", ury) 195 fd.URy = float64(fd.toPDFGlyphSpace(int(ury))) 196 197 return nil 198 } 199 200 func uint16ToBigEndianBytes(i uint16) []byte { 201 b := make([]byte, 2) 202 binary.BigEndian.PutUint16(b, i) 203 return b 204 } 205 206 func uint32ToBigEndianBytes(i uint32) []byte { 207 b := make([]byte, 4) 208 binary.BigEndian.PutUint32(b, i) 209 return b 210 } 211 212 func utf16BEToString(bb []byte) string { 213 buf := make([]uint16, len(bb)/2) 214 for i := 0; i < len(buf); i++ { 215 buf[i] = binary.BigEndian.Uint16(bb[2*i:]) 216 } 217 return string(utf16.Decode(buf)) 218 } 219 220 func (t table) parsePostScriptTable(fd *ttf) error { 221 // table "post" 222 italicAngle := t.fixed32(4) 223 //fmt.Printf("italicAngle: %2.2f\n", italicAngle) 224 fd.ItalicAngle = italicAngle 225 226 isFixedPitch := t.uint16(16) 227 //fmt.Printf("isFixedPitch: %t\n", isFixedPitch != 0) 228 fd.FixedPitch = isFixedPitch != 0 229 230 return nil 231 } 232 233 // func printUnicodeRange(off int, r uint32) { 234 // for i := 0; i < 32; i++ { 235 // if r&1 > 0 { 236 // fmt.Printf("bit %d: on\n", off+i) 237 // } 238 // r >>= 1 239 // } 240 // } 241 242 func (t table) parseWindowsMetricsTable(fd *ttf) error { 243 // table "OS/2" 244 version := t.uint16(0) 245 fsType := t.uint16(8) 246 fd.Protected = fsType&2 > 0 247 //fmt.Printf("protected: %t\n", fd.Protected) 248 249 uniCodeRange1 := t.uint32(42) 250 //fmt.Printf("uniCodeRange1: %032b\n", uniCodeRange1) 251 fd.UnicodeRange[0] = uniCodeRange1 252 253 uniCodeRange2 := t.uint32(46) 254 //fmt.Printf("uniCodeRange2: %032b\n", uniCodeRange2) 255 fd.UnicodeRange[1] = uniCodeRange2 256 257 uniCodeRange3 := t.uint32(50) 258 //fmt.Printf("uniCodeRange3: %032b\n", uniCodeRange3) 259 fd.UnicodeRange[2] = uniCodeRange3 260 261 uniCodeRange4 := t.uint32(54) 262 //fmt.Printf("uniCodeRange4: %032b\n", uniCodeRange4) 263 fd.UnicodeRange[3] = uniCodeRange4 264 265 // printUnicodeRange(0, uniCodeRange1) 266 // printUnicodeRange(32, uniCodeRange2) 267 // printUnicodeRange(64, uniCodeRange3) 268 // printUnicodeRange(96, uniCodeRange4) 269 270 sTypoAscender := t.int16(68) 271 fd.Ascent = fd.toPDFGlyphSpace(int(sTypoAscender)) 272 273 sTypoDescender := t.int16(70) 274 fd.Descent = fd.toPDFGlyphSpace(int(sTypoDescender)) 275 276 // sCapHeight: This field was defined in version 2 of the OS/2 table. 277 // sCapHeight = int16(0) 278 if version >= 2 { 279 sCapHeight := t.int16(88) 280 fd.CapHeight = fd.toPDFGlyphSpace(int(sCapHeight)) 281 } else { 282 // TODO the value may be set equal to the top of the unscaled and unhinted glyph bounding box 283 // of the glyph encoded at U+0048 (LATIN CAPITAL LETTER H). 284 fd.CapHeight = fd.Ascent 285 } 286 287 fsSelection := t.uint16(62) 288 fd.Bold = fsSelection&0x40 > 0 289 290 fsFirstCharIndex := t.uint16(64) 291 fd.FirstChar = fsFirstCharIndex 292 293 fsLastCharIndex := t.uint16(66) 294 fd.LastChar = fsLastCharIndex 295 296 return nil 297 } 298 299 func (t table) parseNamingTable(fd *ttf) error { 300 // table "name" 301 count := int(t.uint16(2)) 302 stringOffset := t.uint16(4) 303 var nameID uint16 304 baseOff := 6 305 for i := 0; i < count; i++ { 306 recOff := baseOff + i*12 307 pf := t.uint16(recOff) 308 enc := t.uint16(recOff + 2) 309 lang := t.uint16(recOff + 4) 310 nameID = t.uint16(recOff + 6) 311 l := t.uint16(recOff + 8) 312 o := t.uint16(recOff + 10) 313 soff := stringOffset + o 314 s := t.data[soff : soff+l] 315 if nameID == 6 { 316 if pf == 3 && enc == 1 && lang == 0x0409 { 317 fd.PostscriptName = utf16BEToString(s) 318 return nil 319 } 320 if pf == 1 && enc == 0 && lang == 0 { 321 fd.PostscriptName = string(s) 322 return nil 323 } 324 } 325 } 326 327 return errors.New("pdfcpu: unable to identify postscript name") 328 } 329 330 func (t table) parseHorizontalHeaderTable(fd *ttf) error { 331 // table "hhea" 332 ascent := t.int16(4) 333 //fmt.Printf("ascent: %d\n", ascent) 334 if fd.Ascent == 0 { 335 fd.Ascent = fd.toPDFGlyphSpace(int(ascent)) 336 } 337 338 descent := t.int16(6) 339 //fmt.Printf("descent: %d\n", descent) 340 if fd.Descent == 0 { 341 fd.Descent = fd.toPDFGlyphSpace(int(descent)) 342 } 343 344 //lineGap := t.int16(8) 345 //fmt.Printf("lineGap: %d\n", lineGap) 346 347 //advanceWidthMax := t.uint16(10) 348 //fmt.Printf("advanceWidthMax: %d\n", advanceWidthMax) 349 350 //minLeftSideBearing := t.int16(12) 351 //fmt.Printf("minLeftSideBearing: %d\n", minLeftSideBearing) 352 353 //minRightSideBearing := t.int16(14) 354 //fmt.Printf("minRightSideBearing: %d\n", minRightSideBearing) 355 356 //xMaxExtent := t.int16(16) 357 //fmt.Printf("xMaxExtent: %d\n", xMaxExtent) 358 359 numOfLongHorMetrics := t.uint16(34) 360 //fmt.Printf("numOfLongHorMetrics: %d\n", numOfLongHorMetrics) 361 fd.HorMetricsCount = int(numOfLongHorMetrics) 362 363 return nil 364 } 365 366 func (t table) parseMaximumProfile(fd *ttf) error { 367 // table "maxp" 368 numGlyphs := t.uint16(4) 369 fd.GlyphCount = int(numGlyphs) 370 return nil 371 } 372 373 func (t table) parseHorizontalMetricsTable(fd *ttf) error { 374 // table "hmtx" 375 fd.GlyphWidths = make([]int, fd.GlyphCount) 376 377 for i := 0; i < int(fd.HorMetricsCount); i++ { 378 fd.GlyphWidths[i] = fd.toPDFGlyphSpace(int(t.uint16(i * 4))) 379 } 380 381 for i := fd.HorMetricsCount; i < fd.GlyphCount; i++ { 382 fd.GlyphWidths[i] = fd.GlyphWidths[fd.HorMetricsCount-1] 383 } 384 385 return nil 386 } 387 388 func (t table) parseCMapFormat4(fd *ttf) error { 389 fd.Planes[0] = true 390 segCount := int(t.uint16(6) / 2) 391 endOff := 14 392 startOff := endOff + 2*segCount + 2 393 deltaOff := startOff + 2*segCount 394 rangeOff := deltaOff + 2*segCount 395 396 count := 0 397 for i := 0; i < segCount; i++ { 398 sc := t.uint16(startOff + i*2) 399 startCode := uint32(sc) 400 if fd.FirstChar == 0 { 401 fd.FirstChar = sc 402 } 403 ec := t.uint16(endOff + i*2) 404 endCode := uint32(ec) 405 if fd.LastChar == 0 { 406 fd.LastChar = ec 407 } 408 idDelta := uint32(t.uint16(deltaOff + i*2)) 409 idRangeOff := int(t.uint16(rangeOff + i*2)) 410 var v uint16 411 for c, j := startCode, 0; c <= endCode && c != 0xFFFF; c++ { 412 if idRangeOff > 0 { 413 v = t.uint16(rangeOff + i*2 + idRangeOff + j*2) 414 } else { 415 v = uint16(c + idDelta) 416 } 417 if gi := v; gi > 0 { 418 fd.Chars[c] = gi 419 fd.ToUnicode[gi] = c 420 count++ 421 } 422 j++ 423 } 424 } 425 return nil 426 } 427 428 func (t table) parseCMapFormat12(fd *ttf) error { 429 numGroups := int(t.uint32(12)) 430 off := 16 431 count := 0 432 var ( 433 lowestStartCode uint32 434 prevCode uint32 435 ) 436 for i := 0; i < numGroups; i++ { 437 base := off + i*12 438 startCode := t.uint32(base) 439 if lowestStartCode == 0 { 440 lowestStartCode = startCode 441 fd.Planes[int(lowestStartCode/0x10000)] = true 442 } 443 if startCode/0x10000 != prevCode/0x10000 { 444 fd.Planes[int(startCode/0x10000)] = true 445 } 446 endCode := t.uint32(base + 4) 447 if startCode != endCode { 448 if startCode/0x10000 != endCode/0x10000 { 449 fd.Planes[int(endCode/0x10000)] = true 450 } 451 } 452 prevCode = endCode 453 startGlyphID := uint16(t.uint32(base + 8)) 454 for c, gi := startCode, startGlyphID; c <= endCode; c++ { 455 fd.Chars[c] = gi 456 fd.ToUnicode[gi] = c 457 gi++ 458 count++ 459 } 460 } 461 return nil 462 } 463 464 func (t table) parseCharToGlyphMappingTable(fd *ttf) error { 465 // table "cmap" 466 467 fd.Chars = map[uint32]uint16{} 468 fd.ToUnicode = map[uint16]uint32{} 469 fd.Planes = map[int]bool{} 470 tableCount := t.uint16(2) 471 baseOff := 4 472 var pf, enc, f uint16 473 m := map[string]table{} 474 475 for i := 0; i < int(tableCount); i++ { 476 off := baseOff + i*8 477 pf = t.uint16(off) 478 enc = t.uint16(off + 2) 479 o := t.uint32(off + 4) 480 f = t.uint16(int(o)) 481 if f == 14 { 482 continue 483 } 484 l := uint32(t.uint16(int(o) + 2)) 485 if f >= 8 { 486 l = t.uint32(int(o) + 4) 487 } 488 b := t.data[o : o+l] 489 t1 := table{off: o, size: uint32(l), data: b} 490 k := fmt.Sprintf("p%02d.e%02d.f%02d", pf, enc, f) 491 m[k] = t1 492 } 493 494 if t, ok := m["p00.e10.f12"]; ok { 495 return t.parseCMapFormat12(fd) 496 } 497 if t, ok := m["p00.e04.f12"]; ok { 498 return t.parseCMapFormat12(fd) 499 } 500 if t, ok := m["p03.e10.f12"]; ok { 501 return t.parseCMapFormat12(fd) 502 } 503 if t, ok := m["p00.e03.f04"]; ok { 504 return t.parseCMapFormat4(fd) 505 } 506 if t, ok := m["p03.e01.f04"]; ok { 507 return t.parseCMapFormat4(fd) 508 } 509 510 return fmt.Errorf("pdfcpu: unsupported cmap table") 511 } 512 513 func calcTableChecksum(tag string, b []byte) uint32 { 514 sum := uint32(0) 515 c := (len(b) + 3) / 4 516 for i := 0; i < c; i++ { 517 if tag == "head" && i == 2 { 518 continue 519 } 520 sum += binary.BigEndian.Uint32(b[i*4:]) 521 } 522 return sum 523 } 524 525 func getNext32BitAlignedLength(i uint32) uint32 { 526 if i%4 > 0 { 527 return i + (4 - i%4) 528 } 529 return i 530 } 531 532 func headerAndTables(fn string, r io.ReaderAt, baseOff int64) ([]byte, map[string]*table, error) { 533 header := make([]byte, 12) 534 n, err := r.ReadAt(header, baseOff) 535 if err != nil { 536 return nil, nil, err 537 } 538 if n != 12 { 539 return nil, nil, fmt.Errorf("pdfcpu: corrupt ttf file: %s", fn) 540 } 541 542 st := string(header[:4]) 543 544 if st == sfntVersionCFF { 545 return nil, nil, fmt.Errorf("pdfcpu: %s is based on OpenType CFF and unsupported at the moment :(", fn) 546 } 547 548 if st != sfntVersionTrueType && st != sfntVersionTrueTypeApple { 549 return nil, nil, fmt.Errorf("pdfcpu: unrecognized font format: %s", fn) 550 } 551 552 c := int(binary.BigEndian.Uint16(header[4:])) 553 554 b := make([]byte, c*16) 555 n, err = r.ReadAt(b, baseOff+12) 556 if err != nil { 557 return nil, nil, err 558 } 559 if n != c*16 { 560 return nil, nil, fmt.Errorf("pdfcpu: corrupt ttf file: %s", fn) 561 } 562 563 byteCount := uint32(12) 564 tables := map[string]*table{} 565 566 for j := 0; j < c; j++ { 567 off := j * 16 568 b1 := b[off : off+16] 569 tag := string(b1[:4]) 570 chk := binary.BigEndian.Uint32(b1[4:]) 571 o := binary.BigEndian.Uint32(b1[8:]) 572 l := binary.BigEndian.Uint32(b1[12:]) 573 ll := getNext32BitAlignedLength(l) 574 byteCount += ll 575 t := make([]byte, ll) 576 n, err = r.ReadAt(t, int64(o)) 577 if err != nil { 578 return nil, nil, err 579 } 580 if n != int(ll) { 581 return nil, nil, fmt.Errorf("pdfcpu: corrupt table: %s", tag) 582 } 583 sum := calcTableChecksum(tag, t) 584 if sum != chk { 585 fmt.Printf("pdfcpu: fixing table<%s> checksum error; want:%d got:%d\n", tag, chk, sum) 586 chk = sum 587 } 588 tables[tag] = &table{chksum: chk, off: o, size: l, padded: ll, data: t} 589 } 590 591 return header, tables, nil 592 } 593 594 func parse(tags map[string]*table, tag string, fd *ttf) error { 595 t, found := tags[tag] 596 if !found { 597 // OS/2 is optional for True Type fonts. 598 if tag == "OS/2" { 599 return nil 600 } 601 return fmt.Errorf("pdfcpu: tag: %s unavailable", tag) 602 } 603 if t.data == nil { 604 return fmt.Errorf("pdfcpu: tag: %s no data", tag) 605 } 606 607 var err error 608 609 switch tag { 610 case "head": 611 err = t.parseFontHeaderTable(fd) 612 case "OS/2": 613 err = t.parseWindowsMetricsTable(fd) 614 case "post": 615 err = t.parsePostScriptTable(fd) 616 case "name": 617 err = t.parseNamingTable(fd) 618 case "hhea": 619 err = t.parseHorizontalHeaderTable(fd) 620 case "maxp": 621 err = t.parseMaximumProfile(fd) 622 case "hmtx": 623 err = t.parseHorizontalMetricsTable(fd) 624 case "cmap": 625 err = t.parseCharToGlyphMappingTable(fd) 626 } 627 628 return err 629 } 630 631 func writeGob(fileName string, fd ttf) error { 632 f, err := os.Create(fileName) 633 if err != nil { 634 return err 635 } 636 defer f.Close() 637 enc := gob.NewEncoder(f) 638 return enc.Encode(fd) 639 } 640 641 func readGob(fileName string, fd *ttf) error { 642 f, err := os.Open(fileName) 643 if err != nil { 644 return err 645 } 646 defer f.Close() 647 dec := gob.NewDecoder(f) 648 return dec.Decode(fd) 649 } 650 651 func installTrueTypeRep(fontDir, fontName string, header []byte, tables map[string]*table) error { 652 fd := ttf{} 653 //fmt.Println(fontName) 654 for _, v := range []string{"head", "OS/2", "post", "name", "hhea", "maxp", "hmtx", "cmap"} { 655 if err := parse(tables, v, &fd); err != nil { 656 return err 657 } 658 } 659 660 bb, err := createTTF(header, tables) 661 if err != nil { 662 return err 663 } 664 fd.FontFile = bb 665 666 if log.CLIEnabled() { 667 log.CLI.Println(fd.PostscriptName) 668 } 669 670 gobName := filepath.Join(fontDir, fd.PostscriptName+".gob") 671 672 // Write the populated ttf struct as gob. 673 if err := writeGob(gobName, fd); err != nil { 674 return err 675 } 676 677 // Read gob and double check integrity. 678 fdNew := ttf{} 679 if err := readGob(gobName, &fdNew); err != nil { 680 return err 681 } 682 683 if !reflect.DeepEqual(fd, fdNew) { 684 return errors.Errorf("pdfcpu: %s can't be installed", fontName) 685 } 686 687 return nil 688 } 689 690 // InstallTrueTypeCollection saves an internal representation of all fonts 691 // contained in a TrueType collection to the pdfcpu config dir. 692 func InstallTrueTypeCollection(fontDir, fn string) error { 693 f, err := os.Open(fn) 694 if err != nil { 695 return err 696 } 697 defer f.Close() 698 699 b := make([]byte, 12) 700 n, err := f.Read(b) 701 if err != nil { 702 return err 703 } 704 if n != 12 { 705 return fmt.Errorf("pdfcpu: corrupt ttc file: %s", fn) 706 } 707 708 if string(b[:4]) != ttcTag { 709 return fmt.Errorf("pdfcpu: corrupt ttc file: %s", fn) 710 } 711 712 c := int(binary.BigEndian.Uint32(b[8:])) 713 714 b = make([]byte, c*4) 715 n, err = f.ReadAt(b, 12) 716 if err != nil { 717 return err 718 } 719 if n != c*4 { 720 return fmt.Errorf("pdfcpu: corrupt ttc file: %s", fn) 721 } 722 723 // Process contained fonts. 724 for i := 0; i < c; i++ { 725 off := int64(binary.BigEndian.Uint32(b[i*4:])) 726 header, tables, err := headerAndTables(fn, f, off) 727 if err != nil { 728 return err 729 } 730 if err := installTrueTypeRep(fontDir, fn, header, tables); err != nil { 731 return err 732 } 733 } 734 735 return nil 736 } 737 738 // InstallTrueTypeFont saves an internal representation of TrueType font fontName to the pdfcpu config dir. 739 func InstallTrueTypeFont(fontDir, fontName string) error { 740 f, err := os.Open(fontName) 741 if err != nil { 742 return err 743 } 744 defer f.Close() 745 746 header, tables, err := headerAndTables(fontName, f, 0) 747 if err != nil { 748 return err 749 } 750 return installTrueTypeRep(fontDir, fontName, header, tables) 751 } 752 753 // InstallFontFromBytes saves an internal representation of TrueType font fontName to the pdfcpu config dir. 754 func InstallFontFromBytes(fontDir, fontName string, bb []byte) error { 755 rd := bytes.NewReader(bb) 756 header, tables, err := headerAndTables(fontName, rd, 0) 757 if err != nil { 758 return err 759 } 760 return installTrueTypeRep(fontDir, fontName, header, tables) 761 } 762 763 func ttfTables(tableCount int, bb []byte) (map[string]*table, error) { 764 tables := map[string]*table{} 765 b := bb[12:] 766 for j := 0; j < tableCount; j++ { 767 off := j * 16 768 b1 := b[off : off+16] 769 tag := string(b1[:4]) 770 chksum := binary.BigEndian.Uint32(b1[4:]) 771 o := binary.BigEndian.Uint32(b1[8:]) 772 l := binary.BigEndian.Uint32(b1[12:]) 773 ll := getNext32BitAlignedLength(l) 774 t := append([]byte(nil), bb[o:o+ll]...) 775 tables[tag] = &table{chksum: chksum, off: o, size: l, padded: ll, data: t} 776 } 777 return tables, nil 778 } 779 780 func glyfOffset(loca *table, gid, indexToLocFormat int) int { 781 if indexToLocFormat == 0 { 782 // short offsets 783 return 2 * int(loca.uint16(2*gid)) 784 } 785 // 1 .. long offsets 786 return int(loca.uint32(4 * gid)) 787 } 788 789 func writeGlyfOffset(buf *bytes.Buffer, off, indexToLocFormat int) { 790 var bb []byte 791 if indexToLocFormat == 0 { 792 // 0 .. short offsets 793 bb = uint16ToBigEndianBytes(uint16(off / 2)) 794 } else { 795 // 1 .. long offsets 796 bb = uint32ToBigEndianBytes(uint32(off)) 797 } 798 buf.Write(bb) 799 } 800 801 func pad(bb []byte) []byte { 802 i := len(bb) % 4 803 if i == 0 { 804 return bb 805 } 806 for j := 0; j < 4-i; j++ { 807 bb = append(bb, 0x00) 808 } 809 return bb 810 } 811 812 func glyphOffsets(gid int, locaFull, glyfsFull *table, numGlyphs, indexToLocFormat int) (int, int) { 813 offFrom := glyfOffset(locaFull, gid, indexToLocFormat) 814 var offThru int 815 if gid == numGlyphs { 816 offThru = int(glyfsFull.padded) 817 } else { 818 offThru = glyfOffset(locaFull, gid+1, indexToLocFormat) 819 } 820 return offFrom, offThru 821 } 822 823 func resolveCompoundGlyph(fontName string, bb []byte, usedGIDs map[uint16]bool, 824 locaFull, glyfsFull *table, numGlyphs, indexToLocFormat int) error { 825 last := false 826 for off := 10; !last; { 827 flags := binary.BigEndian.Uint16(bb[off:]) 828 last = flags&0x20 == 0 829 wordArgs := flags&0x01 > 0 830 831 gid := binary.BigEndian.Uint16(bb[off+2:]) 832 833 // Position behind arguments. 834 off += 6 835 if wordArgs { 836 off += 2 837 } 838 839 // Position behind transform. 840 if flags&0x08 > 0 { 841 off += 2 842 } else if flags&0x40 > 0 { 843 off += 4 844 } else if flags&0x80 > 0 { 845 off += 8 846 } 847 848 if _, ok := usedGIDs[gid]; ok { 849 // duplicate 850 continue 851 } 852 853 offFrom, offThru := glyphOffsets(int(gid), locaFull, glyfsFull, numGlyphs, indexToLocFormat) 854 if offThru < offFrom { 855 return errors.Errorf("pdfcpu: illegal glyfOffset for font: %s", fontName) 856 } 857 if offFrom == offThru { 858 // not available 859 continue 860 } 861 862 usedGIDs[gid] = true 863 864 cbb := glyfsFull.data[offFrom:offThru] 865 if cbb[0]&0x80 == 0 { 866 // simple 867 continue 868 } 869 870 if err := resolveCompoundGlyph(fontName, cbb, usedGIDs, locaFull, glyfsFull, numGlyphs, indexToLocFormat); err != nil { 871 return err 872 } 873 } 874 return nil 875 } 876 877 func resolveCompoundGlyphs(fontName string, usedGIDs map[uint16]bool, locaFull, glyfsFull *table, numGlyphs, indexToLocFormat int) error { 878 gids := make([]uint16, len(usedGIDs)) 879 for k := range usedGIDs { 880 gids = append(gids, k) 881 } 882 for _, gid := range gids { 883 offFrom, offThru := glyphOffsets(int(gid), locaFull, glyfsFull, numGlyphs, indexToLocFormat) 884 if offThru < offFrom { 885 return errors.Errorf("pdfcpu: illegal glyfOffset for font: %s", fontName) 886 } 887 if offFrom == offThru { 888 continue 889 } 890 bb := glyfsFull.data[offFrom:offThru] 891 if bb[0]&0x80 == 0 { 892 // simple 893 continue 894 } 895 if err := resolveCompoundGlyph(fontName, bb, usedGIDs, locaFull, glyfsFull, numGlyphs, indexToLocFormat); err != nil { 896 return err 897 } 898 } 899 return nil 900 } 901 902 func glyfAndLoca(fontName string, tables map[string]*table, usedGIDs map[uint16]bool) error { 903 head, ok := tables["head"] 904 if !ok { 905 return errors.Errorf("pdfcpu: missing \"head\" table for font: %s", fontName) 906 } 907 908 maxp, ok := tables["maxp"] 909 if !ok { 910 return errors.Errorf("pdfcpu: missing \"maxp\" table for font: %s", fontName) 911 } 912 913 glyfsFull, ok := tables["glyf"] 914 if !ok { 915 return errors.Errorf("pdfcpu: missing \"glyf\" table for font: %s", fontName) 916 } 917 918 locaFull, ok := tables["loca"] 919 if !ok { 920 return errors.Errorf("pdfcpu: missing \"loca\" table for font: %s", fontName) 921 } 922 923 indexToLocFormat := int(head.uint16(50)) 924 // 0 .. short offsets 925 // 1 .. long offsets 926 numGlyphs := int(maxp.uint16(4)) 927 928 if err := resolveCompoundGlyphs(fontName, usedGIDs, locaFull, glyfsFull, numGlyphs, indexToLocFormat); err != nil { 929 return err 930 } 931 932 gids := make([]int, 0, len(usedGIDs)+1) 933 gids = append(gids, 0) 934 for gid := range usedGIDs { 935 gids = append(gids, int(gid)) 936 } 937 sort.Ints(gids) 938 939 glyfBytes := []byte{} 940 var buf bytes.Buffer 941 off := 0 942 firstPendingGID := 0 943 944 for _, gid := range gids { 945 offFrom, offThru := glyphOffsets(gid, locaFull, glyfsFull, numGlyphs, indexToLocFormat) 946 if offThru < offFrom { 947 return errors.Errorf("pdfcpu: illegal glyfOffset for font: %s", fontName) 948 } 949 if offThru != offFrom { 950 // We have a glyph outline. 951 for i := 0; i < gid-firstPendingGID; i++ { 952 writeGlyfOffset(&buf, off, indexToLocFormat) 953 } 954 glyfBytes = append(glyfBytes, glyfsFull.data[offFrom:offThru]...) 955 writeGlyfOffset(&buf, off, indexToLocFormat) 956 off += offThru - offFrom 957 firstPendingGID = gid + 1 958 } 959 } 960 for i := 0; i <= numGlyphs-firstPendingGID; i++ { 961 writeGlyfOffset(&buf, off, indexToLocFormat) 962 } 963 964 bb := buf.Bytes() 965 locaFull.size = uint32(len(bb)) 966 locaFull.data = pad(bb) 967 locaFull.padded = uint32(len(locaFull.data)) 968 969 glyfsFull.size = uint32(len(glyfBytes)) 970 glyfsFull.data = pad(glyfBytes) 971 glyfsFull.padded = uint32(len(glyfsFull.data)) 972 973 return nil 974 } 975 976 func createTTF(header []byte, tables map[string]*table) ([]byte, error) { 977 tags := []string{} 978 for t := range tables { 979 tags = append(tags, t) 980 } 981 sort.Strings(tags) 982 983 buf := bytes.NewBuffer(header) 984 off := uint32(len(header) + len(tables)*16) 985 o := off 986 for _, tag := range tags { 987 t := tables[tag] 988 if _, err := buf.WriteString(tag); err != nil { 989 return nil, err 990 } 991 if tag == "loca" || tag == "glyf" { 992 t.chksum = calcTableChecksum(tag, t.data) 993 } 994 if _, err := buf.Write(uint32ToBigEndianBytes(t.chksum)); err != nil { 995 return nil, err 996 } 997 t.off = o 998 if _, err := buf.Write(uint32ToBigEndianBytes(t.off)); err != nil { 999 return nil, err 1000 } 1001 if _, err := buf.Write(uint32ToBigEndianBytes(t.size)); err != nil { 1002 return nil, err 1003 } 1004 o += t.padded 1005 } 1006 1007 for _, tag := range tags { 1008 t := tables[tag] 1009 n, err := buf.Write(t.data) 1010 if err != nil { 1011 return nil, err 1012 } 1013 if n != len(t.data) || n != int(t.padded) { 1014 return nil, errors.Errorf("pdfcpu: unable to write %s data\n", tag) 1015 } 1016 } 1017 1018 return buf.Bytes(), nil 1019 } 1020 1021 // Subset creates a new font file based on usedGIDs. 1022 func Subset(fontName string, usedGIDs map[uint16]bool) ([]byte, error) { 1023 bb, err := Read(fontName) 1024 if err != nil { 1025 return nil, err 1026 } 1027 1028 header := bb[:12] 1029 tableCount := int(binary.BigEndian.Uint16(header[4:])) 1030 tables, err := ttfTables(tableCount, bb) 1031 if err != nil { 1032 return nil, err 1033 } 1034 1035 if err := glyfAndLoca(fontName, tables, usedGIDs); err != nil { 1036 return nil, err 1037 } 1038 1039 return createTTF(header, tables) 1040 }