codeberg.org/go-pdf/fpdf@v0.11.1/utf8fontfile.go (about) 1 // Copyright ©2023 The go-pdf Authors. All rights reserved. 2 // Use of this source code is governed by a MIT-style 3 // license that can be found in the LICENSE file. 4 5 /* 6 * Copyright (c) 2019 Arteom Korotkiy (Gmail: arteomkorotkiy) 7 * 8 * Permission to use, copy, modify, and distribute this software for any 9 * purpose with or without fee is hereby granted, provided that the above 10 * copyright notice and this permission notice appear in all copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 */ 20 21 package fpdf 22 23 import ( 24 "bytes" 25 "encoding/binary" 26 "fmt" 27 "math" 28 "sort" 29 ) 30 31 // flags 32 const symbolWords = 1 << 0 33 const symbolScale = 1 << 3 34 const symbolContinue = 1 << 5 35 const symbolAllScale = 1 << 6 36 const symbol2x2 = 1 << 7 37 38 // CID map Init 39 const toUnicode = "/CIDInit /ProcSet findresource begin\n12 dict begin\nbegincmap\n/CIDSystemInfo\n<</Registry (Adobe)\n/Ordering (UCS)\n/Supplement 0\n>> def\n/CMapName /Adobe-Identity-UCS def\n/CMapType 2 def\n1 begincodespacerange\n<0000> <FFFF>\nendcodespacerange\n1 beginbfrange\n<0000> <FFFF> <0000>\nendbfrange\nendcmap\nCMapName currentdict /CMap defineresource pop\nend\nend" 40 41 type utf8FontFile struct { 42 fileReader *fileReader 43 LastRune int 44 tableDescriptions map[string]*tableDescription 45 outTablesData map[string][]byte 46 symbolPosition []int 47 charSymbolDictionary map[int]int 48 Ascent int 49 Descent int 50 fontElementSize int 51 Bbox fontBoxType 52 CapHeight int 53 StemV int 54 ItalicAngle int 55 Flags int 56 UnderlinePosition float64 57 UnderlineThickness float64 58 CharWidths []int 59 DefaultWidth float64 60 symbolData map[int]map[string][]int 61 CodeSymbolDictionary map[int]int 62 } 63 64 type tableDescription struct { 65 name string 66 checksum []int 67 position int 68 size int 69 } 70 71 type fileReader struct { 72 readerPosition int64 73 array []byte 74 } 75 76 func (fr *fileReader) Read(s int) []byte { 77 b := fr.array[fr.readerPosition : fr.readerPosition+int64(s)] 78 fr.readerPosition += int64(s) 79 return b 80 } 81 82 func (fr *fileReader) seek(shift int64, flag int) (int64, error) { 83 if flag == 0 { 84 fr.readerPosition = shift 85 } else if flag == 1 { 86 fr.readerPosition += shift 87 } else if flag == 2 { 88 fr.readerPosition = int64(len(fr.array)) - shift 89 } 90 return int64(fr.readerPosition), nil 91 } 92 93 func newUTF8Font(reader *fileReader) *utf8FontFile { 94 utf := utf8FontFile{ 95 fileReader: reader, 96 } 97 return &utf 98 } 99 100 func (utf *utf8FontFile) parseFile() error { 101 utf.fileReader.readerPosition = 0 102 utf.symbolPosition = make([]int, 0) 103 utf.charSymbolDictionary = make(map[int]int) 104 utf.tableDescriptions = make(map[string]*tableDescription) 105 utf.outTablesData = make(map[string][]byte) 106 utf.Ascent = 0 107 utf.Descent = 0 108 codeType := uint32(utf.readUint32()) 109 if codeType == 0x4F54544F { 110 return fmt.Errorf("not supported\n ") 111 } 112 if codeType == 0x74746366 { 113 return fmt.Errorf("not supported\n ") 114 } 115 if codeType != 0x00010000 && codeType != 0x74727565 { 116 return fmt.Errorf("not a TrueType font: codeType=%v\n ", codeType) 117 } 118 utf.generateTableDescriptions() 119 utf.parseTables() 120 return nil 121 } 122 123 func (utf *utf8FontFile) generateTableDescriptions() { 124 125 tablesCount := utf.readUint16() 126 _ = utf.readUint16() 127 _ = utf.readUint16() 128 _ = utf.readUint16() 129 utf.tableDescriptions = make(map[string]*tableDescription) 130 131 for i := 0; i < tablesCount; i++ { 132 record := tableDescription{ 133 name: utf.readTableName(), 134 checksum: []int{utf.readUint16(), utf.readUint16()}, 135 position: utf.readUint32(), 136 size: utf.readUint32(), 137 } 138 utf.tableDescriptions[record.name] = &record 139 } 140 } 141 142 func (utf *utf8FontFile) readTableName() string { 143 return string(utf.fileReader.Read(4)) 144 } 145 146 func (utf *utf8FontFile) readUint16() int { 147 s := utf.fileReader.Read(2) 148 return (int(s[0]) << 8) + int(s[1]) 149 } 150 151 func (utf *utf8FontFile) readUint32() int { 152 s := utf.fileReader.Read(4) 153 return (int(s[0]) * 16777216) + (int(s[1]) << 16) + (int(s[2]) << 8) + int(s[3]) // 16777216 = 1<<24 154 } 155 156 func (utf *utf8FontFile) calcInt32(x, y []int) []int { 157 answer := make([]int, 2) 158 if y[1] > x[1] { 159 x[1] += 1 << 16 160 x[0]++ 161 } 162 answer[1] = x[1] - y[1] 163 if y[0] > x[0] { 164 x[0] += 1 << 16 165 } 166 answer[0] = x[0] - y[0] 167 answer[0] = answer[0] & 0xFFFF 168 return answer 169 } 170 171 func (utf *utf8FontFile) generateChecksum(data []byte) []int { 172 if (len(data) % 4) != 0 { 173 for i := 0; (len(data) % 4) != 0; i++ { 174 data = append(data, 0) 175 } 176 } 177 answer := []int{0x0000, 0x0000} 178 for i := 0; i < len(data); i += 4 { 179 answer[0] += (int(data[i]) << 8) + int(data[i+1]) 180 answer[1] += (int(data[i+2]) << 8) + int(data[i+3]) 181 answer[0] += answer[1] >> 16 182 answer[1] = answer[1] & 0xFFFF 183 answer[0] = answer[0] & 0xFFFF 184 } 185 return answer 186 } 187 188 func (utf *utf8FontFile) seek(shift int) { 189 _, _ = utf.fileReader.seek(int64(shift), 0) 190 } 191 192 func (utf *utf8FontFile) skip(delta int) { 193 _, _ = utf.fileReader.seek(int64(delta), 1) 194 } 195 196 // SeekTable position 197 func (utf *utf8FontFile) SeekTable(name string) int { 198 return utf.seekTable(name, 0) 199 } 200 201 func (utf *utf8FontFile) seekTable(name string, offsetInTable int) int { 202 _, _ = utf.fileReader.seek(int64(utf.tableDescriptions[name].position+offsetInTable), 0) 203 return int(utf.fileReader.readerPosition) 204 } 205 206 func (utf *utf8FontFile) readInt16() int16 { 207 s := utf.fileReader.Read(2) 208 a := (int16(s[0]) << 8) + int16(s[1]) 209 if (int(a) & (1 << 15)) == 0 { 210 a = int16(int(a) - (1 << 16)) 211 } 212 return a 213 } 214 215 func (utf *utf8FontFile) getUint16(pos int) int { 216 _, _ = utf.fileReader.seek(int64(pos), 0) 217 s := utf.fileReader.Read(2) 218 return (int(s[0]) << 8) + int(s[1]) 219 } 220 221 func (utf *utf8FontFile) splice(stream []byte, offset int, value []byte) []byte { 222 stream = append([]byte{}, stream...) 223 return append(append(stream[:offset], value...), stream[offset+len(value):]...) 224 } 225 226 func (utf *utf8FontFile) insertUint16(stream []byte, offset int, value int) []byte { 227 up := make([]byte, 2) 228 binary.BigEndian.PutUint16(up, uint16(value)) 229 return utf.splice(stream, offset, up) 230 } 231 232 func (utf *utf8FontFile) getRange(pos, length int) []byte { 233 _, _ = utf.fileReader.seek(int64(pos), 0) 234 if length < 1 { 235 return make([]byte, 0) 236 } 237 s := utf.fileReader.Read(length) 238 return s 239 } 240 241 func (utf *utf8FontFile) getTableData(name string) []byte { 242 desckrip := utf.tableDescriptions[name] 243 if desckrip == nil { 244 return nil 245 } 246 if desckrip.size == 0 { 247 return nil 248 } 249 _, _ = utf.fileReader.seek(int64(desckrip.position), 0) 250 s := utf.fileReader.Read(desckrip.size) 251 return s 252 } 253 254 func (utf *utf8FontFile) setOutTable(name string, data []byte) { 255 if data == nil { 256 return 257 } 258 if name == "head" { 259 data = utf.splice(data, 8, []byte{0, 0, 0, 0}) 260 } 261 utf.outTablesData[name] = data 262 } 263 264 func arrayKeys(arr map[int]string) []int { 265 answer := make([]int, len(arr)) 266 i := 0 267 for key := range arr { 268 answer[i] = key 269 i++ 270 } 271 return answer 272 } 273 274 func inArray(s int, arr []int) bool { 275 for _, i := range arr { 276 if s == i { 277 return true 278 } 279 } 280 return false 281 } 282 283 func (utf *utf8FontFile) parseNAMETable() int { 284 namePosition := utf.SeekTable("name") 285 format := utf.readUint16() 286 if format != 0 { 287 fmt.Printf("Illegal format %d\n", format) 288 return format 289 } 290 nameCount := utf.readUint16() 291 stringDataPosition := namePosition + utf.readUint16() 292 names := map[int]string{1: "", 2: "", 3: "", 4: "", 6: ""} 293 keys := arrayKeys(names) 294 counter := len(names) 295 for i := 0; i < nameCount; i++ { 296 system := utf.readUint16() 297 code := utf.readUint16() 298 local := utf.readUint16() 299 nameID := utf.readUint16() 300 size := utf.readUint16() 301 position := utf.readUint16() 302 if !inArray(nameID, keys) { 303 continue 304 } 305 currentName := "" 306 if system == 3 && code == 1 && local == 0x409 { 307 oldPos := utf.fileReader.readerPosition 308 utf.seek(stringDataPosition + position) 309 if size%2 != 0 { 310 fmt.Printf("name is not binar byte format\n") 311 return format 312 } 313 size /= 2 314 currentName = "" 315 for size > 0 { 316 char := utf.readUint16() 317 currentName += string(rune(char)) 318 size-- 319 } 320 utf.fileReader.readerPosition = oldPos 321 utf.seek(int(oldPos)) 322 } else if system == 1 && code == 0 && local == 0 { 323 oldPos := utf.fileReader.readerPosition 324 currentName = string(utf.getRange(stringDataPosition+position, size)) 325 utf.fileReader.readerPosition = oldPos 326 utf.seek(int(oldPos)) 327 } 328 if currentName != "" && names[nameID] == "" { 329 names[nameID] = currentName 330 counter-- 331 if counter == 0 { 332 break 333 } 334 } 335 } 336 return format 337 } 338 339 func (utf *utf8FontFile) parseHEADTable() { 340 utf.SeekTable("head") 341 utf.skip(18) 342 utf.fontElementSize = utf.readUint16() 343 scale := 1000.0 / float64(utf.fontElementSize) 344 utf.skip(16) 345 xMin := utf.readInt16() 346 yMin := utf.readInt16() 347 xMax := utf.readInt16() 348 yMax := utf.readInt16() 349 utf.Bbox = fontBoxType{int(float64(xMin) * scale), int(float64(yMin) * scale), int(float64(xMax) * scale), int(float64(yMax) * scale)} 350 utf.skip(3 * 2) 351 _ = utf.readUint16() 352 symbolDataFormat := utf.readUint16() 353 if symbolDataFormat != 0 { 354 fmt.Printf("Unknown symbol data format %d\n", symbolDataFormat) 355 return 356 } 357 } 358 359 func (utf *utf8FontFile) parseHHEATable() int { 360 metricsCount := 0 361 if _, OK := utf.tableDescriptions["hhea"]; OK { 362 scale := 1000.0 / float64(utf.fontElementSize) 363 utf.SeekTable("hhea") 364 utf.skip(4) 365 hheaAscender := utf.readInt16() 366 hheaDescender := utf.readInt16() 367 utf.Ascent = int(float64(hheaAscender) * scale) 368 utf.Descent = int(float64(hheaDescender) * scale) 369 utf.skip(24) 370 metricDataFormat := utf.readUint16() 371 if metricDataFormat != 0 { 372 fmt.Printf("Unknown horizontal metric data format %d\n", metricDataFormat) 373 return 0 374 } 375 metricsCount = utf.readUint16() 376 if metricsCount == 0 { 377 fmt.Printf("Number of horizontal metrics is 0\n") 378 return 0 379 } 380 } 381 return metricsCount 382 } 383 384 func (utf *utf8FontFile) parseOS2Table() int { 385 var weightType int 386 scale := 1000.0 / float64(utf.fontElementSize) 387 if _, OK := utf.tableDescriptions["OS/2"]; OK { 388 utf.SeekTable("OS/2") 389 version := utf.readUint16() 390 utf.skip(2) 391 weightType = utf.readUint16() 392 utf.skip(2) 393 fsType := utf.readUint16() 394 if fsType == 0x0002 || (fsType&0x0300) != 0 { 395 fmt.Printf("ERROR - copyright restrictions.\n") 396 return 0 397 } 398 utf.skip(20) 399 _ = utf.readInt16() 400 401 utf.skip(36) 402 sTypoAscender := utf.readInt16() 403 sTypoDescender := utf.readInt16() 404 if utf.Ascent == 0 { 405 utf.Ascent = int(float64(sTypoAscender) * scale) 406 } 407 if utf.Descent == 0 { 408 utf.Descent = int(float64(sTypoDescender) * scale) 409 } 410 if version > 1 { 411 utf.skip(16) 412 sCapHeight := utf.readInt16() 413 utf.CapHeight = int(float64(sCapHeight) * scale) 414 } else { 415 utf.CapHeight = utf.Ascent 416 } 417 } else { 418 weightType = 500 419 if utf.Ascent == 0 { 420 utf.Ascent = int(float64(utf.Bbox.Ymax) * scale) 421 } 422 if utf.Descent == 0 { 423 utf.Descent = int(float64(utf.Bbox.Ymin) * scale) 424 } 425 utf.CapHeight = utf.Ascent 426 } 427 utf.StemV = 50 + int(math.Pow(float64(weightType)/65.0, 2)) 428 return weightType 429 } 430 431 func (utf *utf8FontFile) parsePOSTTable(weight int) { 432 utf.SeekTable("post") 433 utf.skip(4) 434 utf.ItalicAngle = int(utf.readInt16()) + utf.readUint16()/65536.0 435 scale := 1000.0 / float64(utf.fontElementSize) 436 utf.UnderlinePosition = float64(utf.readInt16()) * scale 437 utf.UnderlineThickness = float64(utf.readInt16()) * scale 438 fixed := utf.readUint32() 439 440 utf.Flags = 4 441 442 if utf.ItalicAngle != 0 { 443 utf.Flags = utf.Flags | 64 444 } 445 if weight >= 600 { 446 utf.Flags = utf.Flags | 262144 447 } 448 if fixed != 0 { 449 utf.Flags = utf.Flags | 1 450 } 451 } 452 453 func (utf *utf8FontFile) parseCMAPTable(format int) int { 454 cmapPosition := utf.SeekTable("cmap") 455 utf.skip(2) 456 cmapTableCount := utf.readUint16() 457 cidCMAPPosition := 0 458 for i := 0; i < cmapTableCount; i++ { 459 system := utf.readUint16() 460 coded := utf.readUint16() 461 position := utf.readUint32() 462 oldReaderPosition := utf.fileReader.readerPosition 463 if (system == 3 && coded == 1) || system == 0 { // Microsoft, Unicode 464 v := utf.getUint16(cmapPosition + position) 465 if v == 4 { 466 if cidCMAPPosition == 0 { 467 cidCMAPPosition = cmapPosition + position 468 } 469 break 470 } 471 } 472 utf.seek(int(oldReaderPosition)) 473 } 474 if cidCMAPPosition == 0 { 475 fmt.Printf("Font does not have cmap for Unicode\n") 476 return cidCMAPPosition 477 } 478 return cidCMAPPosition 479 } 480 481 func (utf *utf8FontFile) parseTables() { 482 f := utf.parseNAMETable() 483 utf.parseHEADTable() 484 n := utf.parseHHEATable() 485 w := utf.parseOS2Table() 486 utf.parsePOSTTable(w) 487 runeCMAPPosition := utf.parseCMAPTable(f) 488 489 utf.SeekTable("maxp") 490 utf.skip(4) 491 numSymbols := utf.readUint16() 492 493 symbolCharDictionary := make(map[int][]int) 494 charSymbolDictionary := make(map[int]int) 495 utf.generateSCCSDictionaries(runeCMAPPosition, symbolCharDictionary, charSymbolDictionary) 496 497 scale := 1000.0 / float64(utf.fontElementSize) 498 utf.parseHMTXTable(n, numSymbols, symbolCharDictionary, scale) 499 } 500 501 func (utf *utf8FontFile) generateCMAP() map[int][]int { 502 cmapPosition := utf.SeekTable("cmap") 503 utf.skip(2) 504 cmapTableCount := utf.readUint16() 505 runeCmapPosition := 0 506 for i := 0; i < cmapTableCount; i++ { 507 system := utf.readUint16() 508 coder := utf.readUint16() 509 position := utf.readUint32() 510 oldPosition := utf.fileReader.readerPosition 511 if (system == 3 && coder == 1) || system == 0 { 512 format := utf.getUint16(cmapPosition + position) 513 if format == 4 { 514 runeCmapPosition = cmapPosition + position 515 break 516 } 517 } 518 utf.seek(int(oldPosition)) 519 } 520 521 if runeCmapPosition == 0 { 522 fmt.Printf("Font does not have cmap for Unicode\n") 523 return nil 524 } 525 526 symbolCharDictionary := make(map[int][]int) 527 charSymbolDictionary := make(map[int]int) 528 utf.generateSCCSDictionaries(runeCmapPosition, symbolCharDictionary, charSymbolDictionary) 529 530 utf.charSymbolDictionary = charSymbolDictionary 531 532 return symbolCharDictionary 533 } 534 535 func (utf *utf8FontFile) parseSymbols(usedRunes map[int]int) (map[int]int, map[int]int, map[int]int, []int) { 536 symbolCollection := map[int]int{0: 0} 537 charSymbolPairCollection := make(map[int]int) 538 for _, char := range usedRunes { 539 if _, OK := utf.charSymbolDictionary[char]; OK { 540 symbolCollection[utf.charSymbolDictionary[char]] = char 541 charSymbolPairCollection[char] = utf.charSymbolDictionary[char] 542 543 } 544 utf.LastRune = max(utf.LastRune, char) 545 } 546 547 begin := utf.tableDescriptions["glyf"].position 548 549 symbolArray := make(map[int]int) 550 symbolCollectionKeys := keySortInt(symbolCollection) 551 552 symbolCounter := 0 553 maxRune := 0 554 for _, oldSymbolIndex := range symbolCollectionKeys { 555 maxRune = max(maxRune, symbolCollection[oldSymbolIndex]) 556 symbolArray[oldSymbolIndex] = symbolCounter 557 symbolCounter++ 558 } 559 charSymbolPairCollectionKeys := keySortInt(charSymbolPairCollection) 560 runeSymbolPairCollection := make(map[int]int) 561 for _, runa := range charSymbolPairCollectionKeys { 562 runeSymbolPairCollection[runa] = symbolArray[charSymbolPairCollection[runa]] 563 } 564 utf.CodeSymbolDictionary = runeSymbolPairCollection 565 566 symbolCollectionKeys = keySortInt(symbolCollection) 567 for _, oldSymbolIndex := range symbolCollectionKeys { 568 _, symbolArray, symbolCollection, symbolCollectionKeys = utf.getSymbols(oldSymbolIndex, &begin, symbolArray, symbolCollection, symbolCollectionKeys) 569 } 570 571 return runeSymbolPairCollection, symbolArray, symbolCollection, symbolCollectionKeys 572 } 573 574 func (utf *utf8FontFile) generateCMAPTable(cidSymbolPairCollection map[int]int, numSymbols int) []byte { 575 cidSymbolPairCollectionKeys := keySortInt(cidSymbolPairCollection) 576 cidID := 0 577 cidArray := make(map[int][]int) 578 prevCid := -2 579 prevSymbol := -1 580 for _, cid := range cidSymbolPairCollectionKeys { 581 if cid == (prevCid+1) && cidSymbolPairCollection[cid] == (prevSymbol+1) { 582 if n, OK := cidArray[cidID]; !OK || n == nil { 583 cidArray[cidID] = make([]int, 0) 584 } 585 cidArray[cidID] = append(cidArray[cidID], cidSymbolPairCollection[cid]) 586 } else { 587 cidID = cid 588 cidArray[cidID] = make([]int, 0) 589 cidArray[cidID] = append(cidArray[cidID], cidSymbolPairCollection[cid]) 590 } 591 prevCid = cid 592 prevSymbol = cidSymbolPairCollection[cid] 593 } 594 cidArrayKeys := keySortArrayRangeMap(cidArray) 595 segCount := len(cidArray) + 1 596 597 searchRange := 1 598 entrySelector := 0 599 for searchRange*2 <= segCount { 600 searchRange = searchRange * 2 601 entrySelector = entrySelector + 1 602 } 603 searchRange = searchRange * 2 604 rangeShift := segCount*2 - searchRange 605 length := 16 + (8 * segCount) + (numSymbols + 1) 606 cmap := []int{0, 1, 3, 1, 0, 12, 4, length, 0, segCount * 2, searchRange, entrySelector, rangeShift} 607 608 for _, start := range cidArrayKeys { 609 endCode := start + (len(cidArray[start]) - 1) 610 cmap = append(cmap, endCode) 611 } 612 cmap = append(cmap, 0xFFFF) 613 cmap = append(cmap, 0) 614 615 cmap = append(cmap, cidArrayKeys...) 616 cmap = append(cmap, 0xFFFF) 617 for _, cidKey := range cidArrayKeys { 618 idDelta := -(cidKey - cidArray[cidKey][0]) 619 cmap = append(cmap, idDelta) 620 } 621 cmap = append(cmap, 1) 622 for range cidArray { 623 cmap = append(cmap, 0) 624 625 } 626 cmap = append(cmap, 0) 627 for _, start := range cidArrayKeys { 628 cmap = append(cmap, cidArray[start]...) 629 } 630 cmap = append(cmap, 0) 631 cmapstr := make([]byte, 0) 632 for _, cm := range cmap { 633 cmapstr = append(cmapstr, packUint16(cm)...) 634 } 635 return cmapstr 636 } 637 638 // GenerateCutFont fill utf8FontFile from .utf file, only with runes from usedRunes 639 func (utf *utf8FontFile) GenerateCutFont(usedRunes map[int]int) []byte { 640 utf.fileReader.readerPosition = 0 641 utf.symbolPosition = make([]int, 0) 642 utf.charSymbolDictionary = make(map[int]int) 643 utf.tableDescriptions = make(map[string]*tableDescription) 644 utf.outTablesData = make(map[string][]byte) 645 utf.Ascent = 0 646 utf.Descent = 0 647 utf.skip(4) 648 utf.LastRune = 0 649 utf.generateTableDescriptions() 650 651 utf.SeekTable("head") 652 utf.skip(50) 653 LocaFormat := utf.readUint16() 654 655 utf.SeekTable("hhea") 656 utf.skip(34) 657 metricsCount := utf.readUint16() 658 oldMetrics := metricsCount 659 660 utf.SeekTable("maxp") 661 utf.skip(4) 662 numSymbols := utf.readUint16() 663 664 symbolCharDictionary := utf.generateCMAP() 665 if symbolCharDictionary == nil { 666 return nil 667 } 668 669 utf.parseHMTXTable(metricsCount, numSymbols, symbolCharDictionary, 1.0) 670 671 utf.parseLOCATable(LocaFormat, numSymbols) 672 673 cidSymbolPairCollection, symbolArray, symbolCollection, symbolCollectionKeys := utf.parseSymbols(usedRunes) 674 675 metricsCount = len(symbolCollection) 676 numSymbols = metricsCount 677 678 utf.setOutTable("name", utf.getTableData("name")) 679 utf.setOutTable("cvt ", utf.getTableData("cvt ")) 680 utf.setOutTable("fpgm", utf.getTableData("fpgm")) 681 utf.setOutTable("prep", utf.getTableData("prep")) 682 utf.setOutTable("gasp", utf.getTableData("gasp")) 683 684 postTable := utf.getTableData("post") 685 postTable = append(append([]byte{0x00, 0x03, 0x00, 0x00}, postTable[4:16]...), []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}...) 686 utf.setOutTable("post", postTable) 687 688 delete(cidSymbolPairCollection, 0) 689 690 utf.setOutTable("cmap", utf.generateCMAPTable(cidSymbolPairCollection, numSymbols)) 691 692 symbolData := utf.getTableData("glyf") 693 694 offsets := make([]int, 0) 695 glyfData := make([]byte, 0) 696 pos := 0 697 hmtxData := make([]byte, 0) 698 utf.symbolData = make(map[int]map[string][]int) 699 700 for _, originalSymbolIdx := range symbolCollectionKeys { 701 hm := utf.getMetrics(oldMetrics, originalSymbolIdx) 702 hmtxData = append(hmtxData, hm...) 703 704 offsets = append(offsets, pos) 705 symbolPos := utf.symbolPosition[originalSymbolIdx] 706 symbolLen := utf.symbolPosition[originalSymbolIdx+1] - symbolPos 707 data := symbolData[symbolPos : symbolPos+symbolLen] 708 var up int 709 if symbolLen > 0 { 710 up = unpackUint16(data[0:2]) 711 } 712 713 if symbolLen > 2 && (up&(1<<15)) != 0 { 714 posInSymbol := 10 715 flags := symbolContinue 716 nComponentElements := 0 717 for (flags & symbolContinue) != 0 { 718 nComponentElements++ 719 up = unpackUint16(data[posInSymbol : posInSymbol+2]) 720 flags = up 721 up = unpackUint16(data[posInSymbol+2 : posInSymbol+4]) 722 symbolIdx := up 723 if _, OK := utf.symbolData[originalSymbolIdx]; !OK { 724 utf.symbolData[originalSymbolIdx] = make(map[string][]int) 725 } 726 if _, OK := utf.symbolData[originalSymbolIdx]["compSymbols"]; !OK { 727 utf.symbolData[originalSymbolIdx]["compSymbols"] = make([]int, 0) 728 } 729 utf.symbolData[originalSymbolIdx]["compSymbols"] = append(utf.symbolData[originalSymbolIdx]["compSymbols"], symbolIdx) 730 data = utf.insertUint16(data, posInSymbol+2, symbolArray[symbolIdx]) 731 posInSymbol += 4 732 if (flags & symbolWords) != 0 { 733 posInSymbol += 4 734 } else { 735 posInSymbol += 2 736 } 737 if (flags & symbolScale) != 0 { 738 posInSymbol += 2 739 } else if (flags & symbolAllScale) != 0 { 740 posInSymbol += 4 741 } else if (flags & symbol2x2) != 0 { 742 posInSymbol += 8 743 } 744 } 745 } 746 747 glyfData = append(glyfData, data...) 748 pos += symbolLen 749 if pos%4 != 0 { 750 padding := 4 - (pos % 4) 751 glyfData = append(glyfData, make([]byte, padding)...) 752 pos += padding 753 } 754 } 755 756 offsets = append(offsets, pos) 757 utf.setOutTable("glyf", glyfData) 758 759 utf.setOutTable("hmtx", hmtxData) 760 761 locaData := make([]byte, 0) 762 if ((pos + 1) >> 1) > 0xFFFF { 763 LocaFormat = 1 764 for _, offset := range offsets { 765 locaData = append(locaData, packUint32(offset)...) 766 } 767 } else { 768 LocaFormat = 0 769 for _, offset := range offsets { 770 locaData = append(locaData, packUint16(offset/2)...) 771 } 772 } 773 utf.setOutTable("loca", locaData) 774 775 headData := utf.getTableData("head") 776 headData = utf.insertUint16(headData, 50, LocaFormat) 777 utf.setOutTable("head", headData) 778 779 hheaData := utf.getTableData("hhea") 780 hheaData = utf.insertUint16(hheaData, 34, metricsCount) 781 utf.setOutTable("hhea", hheaData) 782 783 maxp := utf.getTableData("maxp") 784 maxp = utf.insertUint16(maxp, 4, numSymbols) 785 utf.setOutTable("maxp", maxp) 786 787 os2Data := utf.getTableData("OS/2") 788 utf.setOutTable("OS/2", os2Data) 789 790 return utf.assembleTables() 791 } 792 793 func (utf *utf8FontFile) getSymbols(originalSymbolIdx int, start *int, symbolSet map[int]int, SymbolsCollection map[int]int, SymbolsCollectionKeys []int) (*int, map[int]int, map[int]int, []int) { 794 symbolPos := utf.symbolPosition[originalSymbolIdx] 795 symbolSize := utf.symbolPosition[originalSymbolIdx+1] - symbolPos 796 if symbolSize == 0 { 797 return start, symbolSet, SymbolsCollection, SymbolsCollectionKeys 798 } 799 utf.seek(*start + symbolPos) 800 801 lineCount := utf.readInt16() 802 803 if lineCount < 0 { 804 utf.skip(8) 805 flags := symbolContinue 806 for flags&symbolContinue != 0 { 807 flags = utf.readUint16() 808 symbolIndex := utf.readUint16() 809 if _, OK := symbolSet[symbolIndex]; !OK { 810 symbolSet[symbolIndex] = len(SymbolsCollection) 811 SymbolsCollection[symbolIndex] = 1 812 SymbolsCollectionKeys = append(SymbolsCollectionKeys, symbolIndex) 813 } 814 oldPosition, _ := utf.fileReader.seek(0, 1) 815 _, _, _, SymbolsCollectionKeys = utf.getSymbols(symbolIndex, start, symbolSet, SymbolsCollection, SymbolsCollectionKeys) 816 utf.seek(int(oldPosition)) 817 if flags&symbolWords != 0 { 818 utf.skip(4) 819 } else { 820 utf.skip(2) 821 } 822 if flags&symbolScale != 0 { 823 utf.skip(2) 824 } else if flags&symbolAllScale != 0 { 825 utf.skip(4) 826 } else if flags&symbol2x2 != 0 { 827 utf.skip(8) 828 } 829 } 830 } 831 return start, symbolSet, SymbolsCollection, SymbolsCollectionKeys 832 } 833 834 func (utf *utf8FontFile) parseHMTXTable(numberOfHMetrics, numSymbols int, symbolToChar map[int][]int, scale float64) { 835 var widths int 836 start := utf.SeekTable("hmtx") 837 arrayWidths := 0 838 var arr []int 839 utf.CharWidths = make([]int, 256*256) 840 charCount := 0 841 arr = unpackUint16Array(utf.getRange(start, numberOfHMetrics*4)) 842 for symbol := 0; symbol < numberOfHMetrics; symbol++ { 843 arrayWidths = arr[(symbol*2)+1] 844 if _, OK := symbolToChar[symbol]; OK || symbol == 0 { 845 846 if arrayWidths >= (1 << 15) { 847 arrayWidths = 0 848 } 849 if symbol == 0 { 850 utf.DefaultWidth = scale * float64(arrayWidths) 851 continue 852 } 853 for _, char := range symbolToChar[symbol] { 854 if char != 0 && char != 65535 { 855 widths = int(math.Round(scale * float64(arrayWidths))) 856 if widths == 0 { 857 widths = 65535 858 } 859 if char < 196608 { 860 utf.CharWidths[char] = widths 861 charCount++ 862 } 863 } 864 } 865 } 866 } 867 diff := numSymbols - numberOfHMetrics 868 for pos := 0; pos < diff; pos++ { 869 symbol := pos + numberOfHMetrics 870 if _, OK := symbolToChar[symbol]; OK { 871 for _, char := range symbolToChar[symbol] { 872 if char != 0 && char != 65535 { 873 widths = int(math.Round(scale * float64(arrayWidths))) 874 if widths == 0 { 875 widths = 65535 876 } 877 if char < 196608 { 878 utf.CharWidths[char] = widths 879 charCount++ 880 } 881 } 882 } 883 } 884 } 885 utf.CharWidths[0] = charCount 886 } 887 888 func (utf *utf8FontFile) getMetrics(metricCount, gid int) []byte { 889 start := utf.SeekTable("hmtx") 890 var metrics []byte 891 if gid < metricCount { 892 utf.seek(start + (gid * 4)) 893 metrics = utf.fileReader.Read(4) 894 } else { 895 utf.seek(start + ((metricCount - 1) * 4)) 896 metrics = utf.fileReader.Read(2) 897 utf.seek(start + (metricCount * 2) + (gid * 2)) 898 metrics = append(metrics, utf.fileReader.Read(2)...) 899 } 900 return metrics 901 } 902 903 func (utf *utf8FontFile) parseLOCATable(format, numSymbols int) { 904 start := utf.SeekTable("loca") 905 utf.symbolPosition = make([]int, 0) 906 if format == 0 { 907 data := utf.getRange(start, (numSymbols*2)+2) 908 arr := unpackUint16Array(data) 909 for n := 0; n <= numSymbols; n++ { 910 utf.symbolPosition = append(utf.symbolPosition, arr[n+1]*2) 911 } 912 } else if format == 1 { 913 data := utf.getRange(start, (numSymbols*4)+4) 914 arr := unpackUint32Array(data) 915 for n := 0; n <= numSymbols; n++ { 916 utf.symbolPosition = append(utf.symbolPosition, arr[n+1]) 917 } 918 } else { 919 fmt.Printf("Unknown loca table format %d\n", format) 920 return 921 } 922 } 923 924 func (utf *utf8FontFile) generateSCCSDictionaries(runeCmapPosition int, symbolCharDictionary map[int][]int, charSymbolDictionary map[int]int) { 925 maxRune := 0 926 utf.seek(runeCmapPosition + 2) 927 size := utf.readUint16() 928 rim := runeCmapPosition + size 929 utf.skip(2) 930 931 segmentSize := utf.readUint16() / 2 932 utf.skip(6) 933 completers := make([]int, 0) 934 for i := 0; i < segmentSize; i++ { 935 completers = append(completers, utf.readUint16()) 936 } 937 utf.skip(2) 938 beginners := make([]int, 0) 939 for i := 0; i < segmentSize; i++ { 940 beginners = append(beginners, utf.readUint16()) 941 } 942 sizes := make([]int, 0) 943 for i := 0; i < segmentSize; i++ { 944 sizes = append(sizes, int(utf.readInt16())) 945 } 946 readerPositionStart := utf.fileReader.readerPosition 947 positions := make([]int, 0) 948 for i := 0; i < segmentSize; i++ { 949 positions = append(positions, utf.readUint16()) 950 } 951 var symbol int 952 for n := 0; n < segmentSize; n++ { 953 completePosition := completers[n] + 1 954 for char := beginners[n]; char < completePosition; char++ { 955 if positions[n] == 0 { 956 symbol = (char + sizes[n]) & 0xFFFF 957 } else { 958 position := (char-beginners[n])*2 + positions[n] 959 position = int(readerPositionStart) + 2*n + position 960 if position >= rim { 961 symbol = 0 962 } else { 963 symbol = utf.getUint16(position) 964 if symbol != 0 { 965 symbol = (symbol + sizes[n]) & 0xFFFF 966 } 967 } 968 } 969 charSymbolDictionary[char] = symbol 970 if char < 196608 { 971 maxRune = max(char, maxRune) 972 } 973 symbolCharDictionary[symbol] = append(symbolCharDictionary[symbol], char) 974 } 975 } 976 } 977 978 func max(i, n int) int { 979 if n > i { 980 return n 981 } 982 return i 983 } 984 985 func (utf *utf8FontFile) assembleTables() []byte { 986 answer := make([]byte, 0) 987 tablesCount := len(utf.outTablesData) 988 findSize := 1 989 writer := 0 990 for findSize*2 <= tablesCount { 991 findSize = findSize * 2 992 writer = writer + 1 993 } 994 findSize = findSize * 16 995 rOffset := tablesCount*16 - findSize 996 997 answer = append(answer, packHeader(0x00010000, tablesCount, findSize, writer, rOffset)...) 998 999 tables := make(map[string][]byte, len(utf.outTablesData)) 1000 for k, v := range utf.outTablesData { 1001 tables[k] = make([]byte, len(v)) 1002 copy(tables[k], v) 1003 } 1004 tablesNames := keySortStrings(tables) 1005 1006 offset := 12 + tablesCount*16 1007 begin := 0 1008 1009 for _, name := range tablesNames { 1010 if name == "head" { 1011 begin = offset 1012 } 1013 answer = append(answer, []byte(name)...) 1014 checksum := utf.generateChecksum(tables[name]) 1015 answer = append(answer, pack2Uint16(checksum[0], checksum[1])...) 1016 answer = append(answer, pack2Uint32(offset, len(tables[name]))...) 1017 paddedLength := (len(tables[name]) + 3) &^ 3 1018 offset = offset + paddedLength 1019 } 1020 1021 for _, key := range tablesNames { 1022 data := append([]byte{}, tables[key]...) 1023 data = append(data, []byte{0, 0, 0}...) 1024 answer = append(answer, data[:(len(data)&^3)]...) 1025 } 1026 1027 checksum := utf.generateChecksum([]byte(answer)) 1028 checksum = utf.calcInt32([]int{0xB1B0, 0xAFBA}, checksum) 1029 answer = utf.splice(answer, (begin + 8), pack2Uint16(checksum[0], checksum[1])) 1030 return answer 1031 } 1032 1033 func unpackUint16Array(data []byte) []int { 1034 answer := make([]int, 1) 1035 r := bytes.NewReader(data) 1036 bs := make([]byte, 2) 1037 var e error 1038 var c int 1039 c, e = r.Read(bs) 1040 for e == nil && c > 0 { 1041 answer = append(answer, int(binary.BigEndian.Uint16(bs))) 1042 c, e = r.Read(bs) 1043 } 1044 return answer 1045 } 1046 1047 func unpackUint32Array(data []byte) []int { 1048 answer := make([]int, 1) 1049 r := bytes.NewReader(data) 1050 bs := make([]byte, 4) 1051 var e error 1052 var c int 1053 c, e = r.Read(bs) 1054 for e == nil && c > 0 { 1055 answer = append(answer, int(binary.BigEndian.Uint32(bs))) 1056 c, e = r.Read(bs) 1057 } 1058 return answer 1059 } 1060 1061 func unpackUint16(data []byte) int { 1062 return int(binary.BigEndian.Uint16(data)) 1063 } 1064 1065 func packHeader(N uint32, n1, n2, n3, n4 int) []byte { 1066 answer := make([]byte, 0) 1067 bs4 := make([]byte, 4) 1068 binary.BigEndian.PutUint32(bs4, N) 1069 answer = append(answer, bs4...) 1070 bs := make([]byte, 2) 1071 binary.BigEndian.PutUint16(bs, uint16(n1)) 1072 answer = append(answer, bs...) 1073 binary.BigEndian.PutUint16(bs, uint16(n2)) 1074 answer = append(answer, bs...) 1075 binary.BigEndian.PutUint16(bs, uint16(n3)) 1076 answer = append(answer, bs...) 1077 binary.BigEndian.PutUint16(bs, uint16(n4)) 1078 answer = append(answer, bs...) 1079 return answer 1080 } 1081 1082 func pack2Uint16(n1, n2 int) []byte { 1083 answer := make([]byte, 0) 1084 bs := make([]byte, 2) 1085 binary.BigEndian.PutUint16(bs, uint16(n1)) 1086 answer = append(answer, bs...) 1087 binary.BigEndian.PutUint16(bs, uint16(n2)) 1088 answer = append(answer, bs...) 1089 return answer 1090 } 1091 1092 func pack2Uint32(n1, n2 int) []byte { 1093 answer := make([]byte, 0) 1094 bs := make([]byte, 4) 1095 binary.BigEndian.PutUint32(bs, uint32(n1)) 1096 answer = append(answer, bs...) 1097 binary.BigEndian.PutUint32(bs, uint32(n2)) 1098 answer = append(answer, bs...) 1099 return answer 1100 } 1101 1102 func packUint32(n1 int) []byte { 1103 bs := make([]byte, 4) 1104 binary.BigEndian.PutUint32(bs, uint32(n1)) 1105 return bs 1106 } 1107 1108 func packUint16(n1 int) []byte { 1109 bs := make([]byte, 2) 1110 binary.BigEndian.PutUint16(bs, uint16(n1)) 1111 return bs 1112 } 1113 1114 func keySortStrings(s map[string][]byte) []string { 1115 keys := make([]string, len(s)) 1116 i := 0 1117 for key := range s { 1118 keys[i] = key 1119 i++ 1120 } 1121 sort.Strings(keys) 1122 return keys 1123 } 1124 1125 func keySortInt(s map[int]int) []int { 1126 keys := make([]int, len(s)) 1127 i := 0 1128 for key := range s { 1129 keys[i] = key 1130 i++ 1131 } 1132 sort.Ints(keys) 1133 return keys 1134 } 1135 1136 func keySortArrayRangeMap(s map[int][]int) []int { 1137 keys := make([]int, len(s)) 1138 i := 0 1139 for key := range s { 1140 keys[i] = key 1141 i++ 1142 } 1143 sort.Ints(keys) 1144 return keys 1145 } 1146 1147 // UTF8CutFont is a utility function that generates a TrueType font composed 1148 // only of the runes included in cutset. The rune glyphs are copied from This 1149 // function is demonstrated in ExampleUTF8CutFont(). 1150 func UTF8CutFont(inBuf []byte, cutset string) (outBuf []byte) { 1151 f := newUTF8Font(&fileReader{readerPosition: 0, array: inBuf}) 1152 runes := map[int]int{} 1153 for i, r := range cutset { 1154 runes[i] = int(r) 1155 } 1156 outBuf = f.GenerateCutFont(runes) 1157 return 1158 }