github.com/phpdave11/gofpdf@v1.4.2/utf8fontfile.go (about) 1 /* 2 * Copyright (c) 2019 Arteom Korotkiy (Gmail: arteomkorotkiy) 3 * 4 * Permission to use, copy, modify, and distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 package gofpdf 18 19 import ( 20 "bytes" 21 "encoding/binary" 22 "fmt" 23 "math" 24 "sort" 25 ) 26 27 // flags 28 const symbolWords = 1 << 0 29 const symbolScale = 1 << 3 30 const symbolContinue = 1 << 5 31 const symbolAllScale = 1 << 6 32 const symbol2x2 = 1 << 7 33 34 // CID map Init 35 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" 36 37 type utf8FontFile struct { 38 fileReader *fileReader 39 LastRune int 40 tableDescriptions map[string]*tableDescription 41 outTablesData map[string][]byte 42 symbolPosition []int 43 charSymbolDictionary map[int]int 44 Ascent int 45 Descent int 46 fontElementSize int 47 Bbox fontBoxType 48 CapHeight int 49 StemV int 50 ItalicAngle int 51 Flags int 52 UnderlinePosition float64 53 UnderlineThickness float64 54 CharWidths []int 55 DefaultWidth float64 56 symbolData map[int]map[string][]int 57 CodeSymbolDictionary map[int]int 58 } 59 60 type tableDescription struct { 61 name string 62 checksum []int 63 position int 64 size int 65 } 66 67 type fileReader struct { 68 readerPosition int64 69 array []byte 70 } 71 72 func (fr *fileReader) Read(s int) []byte { 73 b := fr.array[fr.readerPosition : fr.readerPosition+int64(s)] 74 fr.readerPosition += int64(s) 75 return b 76 } 77 78 func (fr *fileReader) seek(shift int64, flag int) (int64, error) { 79 if flag == 0 { 80 fr.readerPosition = shift 81 } else if flag == 1 { 82 fr.readerPosition += shift 83 } else if flag == 2 { 84 fr.readerPosition = int64(len(fr.array)) - shift 85 } 86 return int64(fr.readerPosition), nil 87 } 88 89 func newUTF8Font(reader *fileReader) *utf8FontFile { 90 utf := utf8FontFile{ 91 fileReader: reader, 92 } 93 return &utf 94 } 95 96 func (utf *utf8FontFile) parseFile() error { 97 utf.fileReader.readerPosition = 0 98 utf.symbolPosition = make([]int, 0) 99 utf.charSymbolDictionary = make(map[int]int) 100 utf.tableDescriptions = make(map[string]*tableDescription) 101 utf.outTablesData = make(map[string][]byte) 102 utf.Ascent = 0 103 utf.Descent = 0 104 codeType := uint32(utf.readUint32()) 105 if codeType == 0x4F54544F { 106 return fmt.Errorf("not supported\n ") 107 } 108 if codeType == 0x74746366 { 109 return fmt.Errorf("not supported\n ") 110 } 111 if codeType != 0x00010000 && codeType != 0x74727565 { 112 return fmt.Errorf("Not a TrueType font: codeType=%v\n ", codeType) 113 } 114 utf.generateTableDescriptions() 115 utf.parseTables() 116 return nil 117 } 118 119 func (utf *utf8FontFile) generateTableDescriptions() { 120 121 tablesCount := utf.readUint16() 122 _ = utf.readUint16() 123 _ = utf.readUint16() 124 _ = utf.readUint16() 125 utf.tableDescriptions = make(map[string]*tableDescription) 126 127 for i := 0; i < tablesCount; i++ { 128 record := tableDescription{ 129 name: utf.readTableName(), 130 checksum: []int{utf.readUint16(), utf.readUint16()}, 131 position: utf.readUint32(), 132 size: utf.readUint32(), 133 } 134 utf.tableDescriptions[record.name] = &record 135 } 136 } 137 138 func (utf *utf8FontFile) readTableName() string { 139 return string(utf.fileReader.Read(4)) 140 } 141 142 func (utf *utf8FontFile) readUint16() int { 143 s := utf.fileReader.Read(2) 144 return (int(s[0]) << 8) + int(s[1]) 145 } 146 147 func (utf *utf8FontFile) readUint32() int { 148 s := utf.fileReader.Read(4) 149 return (int(s[0]) * 16777216) + (int(s[1]) << 16) + (int(s[2]) << 8) + int(s[3]) // 16777216 = 1<<24 150 } 151 152 func (utf *utf8FontFile) calcInt32(x, y []int) []int { 153 answer := make([]int, 2) 154 if y[1] > x[1] { 155 x[1] += 1 << 16 156 x[0]++ 157 } 158 answer[1] = x[1] - y[1] 159 if y[0] > x[0] { 160 x[0] += 1 << 16 161 } 162 answer[0] = x[0] - y[0] 163 answer[0] = answer[0] & 0xFFFF 164 return answer 165 } 166 167 func (utf *utf8FontFile) generateChecksum(data []byte) []int { 168 if (len(data) % 4) != 0 { 169 for i := 0; (len(data) % 4) != 0; i++ { 170 data = append(data, 0) 171 } 172 } 173 answer := []int{0x0000, 0x0000} 174 for i := 0; i < len(data); i += 4 { 175 answer[0] += (int(data[i]) << 8) + int(data[i+1]) 176 answer[1] += (int(data[i+2]) << 8) + int(data[i+3]) 177 answer[0] += answer[1] >> 16 178 answer[1] = answer[1] & 0xFFFF 179 answer[0] = answer[0] & 0xFFFF 180 } 181 return answer 182 } 183 184 func (utf *utf8FontFile) seek(shift int) { 185 _, _ = utf.fileReader.seek(int64(shift), 0) 186 } 187 188 func (utf *utf8FontFile) skip(delta int) { 189 _, _ = utf.fileReader.seek(int64(delta), 1) 190 } 191 192 //SeekTable position 193 func (utf *utf8FontFile) SeekTable(name string) int { 194 return utf.seekTable(name, 0) 195 } 196 197 func (utf *utf8FontFile) seekTable(name string, offsetInTable int) int { 198 _, _ = utf.fileReader.seek(int64(utf.tableDescriptions[name].position+offsetInTable), 0) 199 return int(utf.fileReader.readerPosition) 200 } 201 202 func (utf *utf8FontFile) readInt16() int16 { 203 s := utf.fileReader.Read(2) 204 a := (int16(s[0]) << 8) + int16(s[1]) 205 if (int(a) & (1 << 15)) == 0 { 206 a = int16(int(a) - (1 << 16)) 207 } 208 return a 209 } 210 211 func (utf *utf8FontFile) getUint16(pos int) int { 212 _, _ = utf.fileReader.seek(int64(pos), 0) 213 s := utf.fileReader.Read(2) 214 return (int(s[0]) << 8) + int(s[1]) 215 } 216 217 func (utf *utf8FontFile) splice(stream []byte, offset int, value []byte) []byte { 218 stream = append([]byte{}, stream...) 219 return append(append(stream[:offset], value...), stream[offset+len(value):]...) 220 } 221 222 func (utf *utf8FontFile) insertUint16(stream []byte, offset int, value int) []byte { 223 up := make([]byte, 2) 224 binary.BigEndian.PutUint16(up, uint16(value)) 225 return utf.splice(stream, offset, up) 226 } 227 228 func (utf *utf8FontFile) getRange(pos, length int) []byte { 229 _, _ = utf.fileReader.seek(int64(pos), 0) 230 if length < 1 { 231 return make([]byte, 0) 232 } 233 s := utf.fileReader.Read(length) 234 return s 235 } 236 237 func (utf *utf8FontFile) getTableData(name string) []byte { 238 desckrip := utf.tableDescriptions[name] 239 if desckrip == nil { 240 return nil 241 } 242 if desckrip.size == 0 { 243 return nil 244 } 245 _, _ = utf.fileReader.seek(int64(desckrip.position), 0) 246 s := utf.fileReader.Read(desckrip.size) 247 return s 248 } 249 250 func (utf *utf8FontFile) setOutTable(name string, data []byte) { 251 if data == nil { 252 return 253 } 254 if name == "head" { 255 data = utf.splice(data, 8, []byte{0, 0, 0, 0}) 256 } 257 utf.outTablesData[name] = data 258 } 259 260 func arrayKeys(arr map[int]string) []int { 261 answer := make([]int, len(arr)) 262 i := 0 263 for key := range arr { 264 answer[i] = key 265 i++ 266 } 267 return answer 268 } 269 270 func inArray(s int, arr []int) bool { 271 for _, i := range arr { 272 if s == i { 273 return true 274 } 275 } 276 return false 277 } 278 279 func (utf *utf8FontFile) parseNAMETable() int { 280 namePosition := utf.SeekTable("name") 281 format := utf.readUint16() 282 if format != 0 { 283 fmt.Printf("Illegal format %d\n", format) 284 return format 285 } 286 nameCount := utf.readUint16() 287 stringDataPosition := namePosition + utf.readUint16() 288 names := map[int]string{1: "", 2: "", 3: "", 4: "", 6: ""} 289 keys := arrayKeys(names) 290 counter := len(names) 291 for i := 0; i < nameCount; i++ { 292 system := utf.readUint16() 293 code := utf.readUint16() 294 local := utf.readUint16() 295 nameID := utf.readUint16() 296 size := utf.readUint16() 297 position := utf.readUint16() 298 if !inArray(nameID, keys) { 299 continue 300 } 301 currentName := "" 302 if system == 3 && code == 1 && local == 0x409 { 303 oldPos := utf.fileReader.readerPosition 304 utf.seek(stringDataPosition + position) 305 if size%2 != 0 { 306 fmt.Printf("name is not binar byte format\n") 307 return format 308 } 309 size /= 2 310 currentName = "" 311 for size > 0 { 312 char := utf.readUint16() 313 currentName += string(rune(char)) 314 size-- 315 } 316 utf.fileReader.readerPosition = oldPos 317 utf.seek(int(oldPos)) 318 } else if system == 1 && code == 0 && local == 0 { 319 oldPos := utf.fileReader.readerPosition 320 currentName = string(utf.getRange(stringDataPosition+position, size)) 321 utf.fileReader.readerPosition = oldPos 322 utf.seek(int(oldPos)) 323 } 324 if currentName != "" && names[nameID] == "" { 325 names[nameID] = currentName 326 counter-- 327 if counter == 0 { 328 break 329 } 330 } 331 } 332 return format 333 } 334 335 func (utf *utf8FontFile) parseHEADTable() { 336 utf.SeekTable("head") 337 utf.skip(18) 338 utf.fontElementSize = utf.readUint16() 339 scale := 1000.0 / float64(utf.fontElementSize) 340 utf.skip(16) 341 xMin := utf.readInt16() 342 yMin := utf.readInt16() 343 xMax := utf.readInt16() 344 yMax := utf.readInt16() 345 utf.Bbox = fontBoxType{int(float64(xMin) * scale), int(float64(yMin) * scale), int(float64(xMax) * scale), int(float64(yMax) * scale)} 346 utf.skip(3 * 2) 347 _ = utf.readUint16() 348 symbolDataFormat := utf.readUint16() 349 if symbolDataFormat != 0 { 350 fmt.Printf("Unknown symbol data format %d\n", symbolDataFormat) 351 return 352 } 353 } 354 355 func (utf *utf8FontFile) parseHHEATable() int { 356 metricsCount := 0 357 if _, OK := utf.tableDescriptions["hhea"]; OK { 358 scale := 1000.0 / float64(utf.fontElementSize) 359 utf.SeekTable("hhea") 360 utf.skip(4) 361 hheaAscender := utf.readInt16() 362 hheaDescender := utf.readInt16() 363 utf.Ascent = int(float64(hheaAscender) * scale) 364 utf.Descent = int(float64(hheaDescender) * scale) 365 utf.skip(24) 366 metricDataFormat := utf.readUint16() 367 if metricDataFormat != 0 { 368 fmt.Printf("Unknown horizontal metric data format %d\n", metricDataFormat) 369 return 0 370 } 371 metricsCount = utf.readUint16() 372 if metricsCount == 0 { 373 fmt.Printf("Number of horizontal metrics is 0\n") 374 return 0 375 } 376 } 377 return metricsCount 378 } 379 380 func (utf *utf8FontFile) parseOS2Table() int { 381 var weightType int 382 scale := 1000.0 / float64(utf.fontElementSize) 383 if _, OK := utf.tableDescriptions["OS/2"]; OK { 384 utf.SeekTable("OS/2") 385 version := utf.readUint16() 386 utf.skip(2) 387 weightType = utf.readUint16() 388 utf.skip(2) 389 fsType := utf.readUint16() 390 if fsType == 0x0002 || (fsType&0x0300) != 0 { 391 fmt.Printf("ERROR - copyright restrictions.\n") 392 return 0 393 } 394 utf.skip(20) 395 _ = utf.readInt16() 396 397 utf.skip(36) 398 sTypoAscender := utf.readInt16() 399 sTypoDescender := utf.readInt16() 400 if utf.Ascent == 0 { 401 utf.Ascent = int(float64(sTypoAscender) * scale) 402 } 403 if utf.Descent == 0 { 404 utf.Descent = int(float64(sTypoDescender) * scale) 405 } 406 if version > 1 { 407 utf.skip(16) 408 sCapHeight := utf.readInt16() 409 utf.CapHeight = int(float64(sCapHeight) * scale) 410 } else { 411 utf.CapHeight = utf.Ascent 412 } 413 } else { 414 weightType = 500 415 if utf.Ascent == 0 { 416 utf.Ascent = int(float64(utf.Bbox.Ymax) * scale) 417 } 418 if utf.Descent == 0 { 419 utf.Descent = int(float64(utf.Bbox.Ymin) * scale) 420 } 421 utf.CapHeight = utf.Ascent 422 } 423 utf.StemV = 50 + int(math.Pow(float64(weightType)/65.0, 2)) 424 return weightType 425 } 426 427 func (utf *utf8FontFile) parsePOSTTable(weight int) { 428 utf.SeekTable("post") 429 utf.skip(4) 430 utf.ItalicAngle = int(utf.readInt16()) + utf.readUint16()/65536.0 431 scale := 1000.0 / float64(utf.fontElementSize) 432 utf.UnderlinePosition = float64(utf.readInt16()) * scale 433 utf.UnderlineThickness = float64(utf.readInt16()) * scale 434 fixed := utf.readUint32() 435 436 utf.Flags = 4 437 438 if utf.ItalicAngle != 0 { 439 utf.Flags = utf.Flags | 64 440 } 441 if weight >= 600 { 442 utf.Flags = utf.Flags | 262144 443 } 444 if fixed != 0 { 445 utf.Flags = utf.Flags | 1 446 } 447 } 448 449 func (utf *utf8FontFile) parseCMAPTable(format int) int { 450 cmapPosition := utf.SeekTable("cmap") 451 utf.skip(2) 452 cmapTableCount := utf.readUint16() 453 cidCMAPPosition := 0 454 for i := 0; i < cmapTableCount; i++ { 455 system := utf.readUint16() 456 coded := utf.readUint16() 457 position := utf.readUint32() 458 oldReaderPosition := utf.fileReader.readerPosition 459 if (system == 3 && coded == 1) || system == 0 { // Microsoft, Unicode 460 format = utf.getUint16(cmapPosition + position) 461 if format == 4 { 462 if cidCMAPPosition == 0 { 463 cidCMAPPosition = cmapPosition + position 464 } 465 break 466 } 467 } 468 utf.seek(int(oldReaderPosition)) 469 } 470 if cidCMAPPosition == 0 { 471 fmt.Printf("Font does not have cmap for Unicode\n") 472 return cidCMAPPosition 473 } 474 return cidCMAPPosition 475 } 476 477 func (utf *utf8FontFile) parseTables() { 478 f := utf.parseNAMETable() 479 utf.parseHEADTable() 480 n := utf.parseHHEATable() 481 w := utf.parseOS2Table() 482 utf.parsePOSTTable(w) 483 runeCMAPPosition := utf.parseCMAPTable(f) 484 485 utf.SeekTable("maxp") 486 utf.skip(4) 487 numSymbols := utf.readUint16() 488 489 symbolCharDictionary := make(map[int][]int) 490 charSymbolDictionary := make(map[int]int) 491 utf.generateSCCSDictionaries(runeCMAPPosition, symbolCharDictionary, charSymbolDictionary) 492 493 scale := 1000.0 / float64(utf.fontElementSize) 494 utf.parseHMTXTable(n, numSymbols, symbolCharDictionary, scale) 495 } 496 497 func (utf *utf8FontFile) generateCMAP() map[int][]int { 498 cmapPosition := utf.SeekTable("cmap") 499 utf.skip(2) 500 cmapTableCount := utf.readUint16() 501 runeCmapPosition := 0 502 for i := 0; i < cmapTableCount; i++ { 503 system := utf.readUint16() 504 coder := utf.readUint16() 505 position := utf.readUint32() 506 oldPosition := utf.fileReader.readerPosition 507 if (system == 3 && coder == 1) || system == 0 { 508 format := utf.getUint16(cmapPosition + position) 509 if format == 4 { 510 runeCmapPosition = cmapPosition + position 511 break 512 } 513 } 514 utf.seek(int(oldPosition)) 515 } 516 517 if runeCmapPosition == 0 { 518 fmt.Printf("Font does not have cmap for Unicode\n") 519 return nil 520 } 521 522 symbolCharDictionary := make(map[int][]int) 523 charSymbolDictionary := make(map[int]int) 524 utf.generateSCCSDictionaries(runeCmapPosition, symbolCharDictionary, charSymbolDictionary) 525 526 utf.charSymbolDictionary = charSymbolDictionary 527 528 return symbolCharDictionary 529 } 530 531 func (utf *utf8FontFile) parseSymbols(usedRunes map[int]int) (map[int]int, map[int]int, map[int]int, []int) { 532 symbolCollection := map[int]int{0: 0} 533 charSymbolPairCollection := make(map[int]int) 534 for _, char := range usedRunes { 535 if _, OK := utf.charSymbolDictionary[char]; OK { 536 symbolCollection[utf.charSymbolDictionary[char]] = char 537 charSymbolPairCollection[char] = utf.charSymbolDictionary[char] 538 539 } 540 utf.LastRune = max(utf.LastRune, char) 541 } 542 543 begin := utf.tableDescriptions["glyf"].position 544 545 symbolArray := make(map[int]int) 546 symbolCollectionKeys := keySortInt(symbolCollection) 547 548 symbolCounter := 0 549 maxRune := 0 550 for _, oldSymbolIndex := range symbolCollectionKeys { 551 maxRune = max(maxRune, symbolCollection[oldSymbolIndex]) 552 symbolArray[oldSymbolIndex] = symbolCounter 553 symbolCounter++ 554 } 555 charSymbolPairCollectionKeys := keySortInt(charSymbolPairCollection) 556 runeSymbolPairCollection := make(map[int]int) 557 for _, runa := range charSymbolPairCollectionKeys { 558 runeSymbolPairCollection[runa] = symbolArray[charSymbolPairCollection[runa]] 559 } 560 utf.CodeSymbolDictionary = runeSymbolPairCollection 561 562 symbolCollectionKeys = keySortInt(symbolCollection) 563 for _, oldSymbolIndex := range symbolCollectionKeys { 564 _, symbolArray, symbolCollection, symbolCollectionKeys = utf.getSymbols(oldSymbolIndex, &begin, symbolArray, symbolCollection, symbolCollectionKeys) 565 } 566 567 return runeSymbolPairCollection, symbolArray, symbolCollection, symbolCollectionKeys 568 } 569 570 func (utf *utf8FontFile) generateCMAPTable(cidSymbolPairCollection map[int]int, numSymbols int) []byte { 571 cidSymbolPairCollectionKeys := keySortInt(cidSymbolPairCollection) 572 cidID := 0 573 cidArray := make(map[int][]int) 574 prevCid := -2 575 prevSymbol := -1 576 for _, cid := range cidSymbolPairCollectionKeys { 577 if cid == (prevCid+1) && cidSymbolPairCollection[cid] == (prevSymbol+1) { 578 if n, OK := cidArray[cidID]; !OK || n == nil { 579 cidArray[cidID] = make([]int, 0) 580 } 581 cidArray[cidID] = append(cidArray[cidID], cidSymbolPairCollection[cid]) 582 } else { 583 cidID = cid 584 cidArray[cidID] = make([]int, 0) 585 cidArray[cidID] = append(cidArray[cidID], cidSymbolPairCollection[cid]) 586 } 587 prevCid = cid 588 prevSymbol = cidSymbolPairCollection[cid] 589 } 590 cidArrayKeys := keySortArrayRangeMap(cidArray) 591 segCount := len(cidArray) + 1 592 593 searchRange := 1 594 entrySelector := 0 595 for searchRange*2 <= segCount { 596 searchRange = searchRange * 2 597 entrySelector = entrySelector + 1 598 } 599 searchRange = searchRange * 2 600 rangeShift := segCount*2 - searchRange 601 length := 16 + (8 * segCount) + (numSymbols + 1) 602 cmap := []int{0, 1, 3, 1, 0, 12, 4, length, 0, segCount * 2, searchRange, entrySelector, rangeShift} 603 604 for _, start := range cidArrayKeys { 605 endCode := start + (len(cidArray[start]) - 1) 606 cmap = append(cmap, endCode) 607 } 608 cmap = append(cmap, 0xFFFF) 609 cmap = append(cmap, 0) 610 611 for _, cidKey := range cidArrayKeys { 612 cmap = append(cmap, cidKey) 613 } 614 cmap = append(cmap, 0xFFFF) 615 for _, cidKey := range cidArrayKeys { 616 idDelta := -(cidKey - cidArray[cidKey][0]) 617 cmap = append(cmap, idDelta) 618 } 619 cmap = append(cmap, 1) 620 for range cidArray { 621 cmap = append(cmap, 0) 622 623 } 624 cmap = append(cmap, 0) 625 for _, start := range cidArrayKeys { 626 for _, glidx := range cidArray[start] { 627 cmap = append(cmap, glidx) 628 } 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, 0) 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 := utf.outTablesData 1000 tablesNames := keySortStrings(tables) 1001 1002 offset := 12 + tablesCount*16 1003 begin := 0 1004 1005 for _, name := range tablesNames { 1006 if name == "head" { 1007 begin = offset 1008 } 1009 answer = append(answer, []byte(name)...) 1010 checksum := utf.generateChecksum(tables[name]) 1011 answer = append(answer, pack2Uint16(checksum[0], checksum[1])...) 1012 answer = append(answer, pack2Uint32(offset, len(tables[name]))...) 1013 paddedLength := (len(tables[name]) + 3) &^ 3 1014 offset = offset + paddedLength 1015 } 1016 1017 for _, key := range tablesNames { 1018 data := append([]byte{}, tables[key]...) 1019 data = append(data, []byte{0, 0, 0}...) 1020 answer = append(answer, data[:(len(data)&^3)]...) 1021 } 1022 1023 checksum := utf.generateChecksum([]byte(answer)) 1024 checksum = utf.calcInt32([]int{0xB1B0, 0xAFBA}, checksum) 1025 answer = utf.splice(answer, (begin + 8), pack2Uint16(checksum[0], checksum[1])) 1026 return answer 1027 } 1028 1029 func unpackUint16Array(data []byte) []int { 1030 answer := make([]int, 1) 1031 r := bytes.NewReader(data) 1032 bs := make([]byte, 2) 1033 var e error 1034 var c int 1035 c, e = r.Read(bs) 1036 for e == nil && c > 0 { 1037 answer = append(answer, int(binary.BigEndian.Uint16(bs))) 1038 c, e = r.Read(bs) 1039 } 1040 return answer 1041 } 1042 1043 func unpackUint32Array(data []byte) []int { 1044 answer := make([]int, 1) 1045 r := bytes.NewReader(data) 1046 bs := make([]byte, 4) 1047 var e error 1048 var c int 1049 c, e = r.Read(bs) 1050 for e == nil && c > 0 { 1051 answer = append(answer, int(binary.BigEndian.Uint32(bs))) 1052 c, e = r.Read(bs) 1053 } 1054 return answer 1055 } 1056 1057 func unpackUint16(data []byte) int { 1058 return int(binary.BigEndian.Uint16(data)) 1059 } 1060 1061 func packHeader(N uint32, n1, n2, n3, n4 int) []byte { 1062 answer := make([]byte, 0) 1063 bs4 := make([]byte, 4) 1064 binary.BigEndian.PutUint32(bs4, N) 1065 answer = append(answer, bs4...) 1066 bs := make([]byte, 2) 1067 binary.BigEndian.PutUint16(bs, uint16(n1)) 1068 answer = append(answer, bs...) 1069 binary.BigEndian.PutUint16(bs, uint16(n2)) 1070 answer = append(answer, bs...) 1071 binary.BigEndian.PutUint16(bs, uint16(n3)) 1072 answer = append(answer, bs...) 1073 binary.BigEndian.PutUint16(bs, uint16(n4)) 1074 answer = append(answer, bs...) 1075 return answer 1076 } 1077 1078 func pack2Uint16(n1, n2 int) []byte { 1079 answer := make([]byte, 0) 1080 bs := make([]byte, 2) 1081 binary.BigEndian.PutUint16(bs, uint16(n1)) 1082 answer = append(answer, bs...) 1083 binary.BigEndian.PutUint16(bs, uint16(n2)) 1084 answer = append(answer, bs...) 1085 return answer 1086 } 1087 1088 func pack2Uint32(n1, n2 int) []byte { 1089 answer := make([]byte, 0) 1090 bs := make([]byte, 4) 1091 binary.BigEndian.PutUint32(bs, uint32(n1)) 1092 answer = append(answer, bs...) 1093 binary.BigEndian.PutUint32(bs, uint32(n2)) 1094 answer = append(answer, bs...) 1095 return answer 1096 } 1097 1098 func packUint32(n1 int) []byte { 1099 bs := make([]byte, 4) 1100 binary.BigEndian.PutUint32(bs, uint32(n1)) 1101 return bs 1102 } 1103 1104 func packUint16(n1 int) []byte { 1105 bs := make([]byte, 2) 1106 binary.BigEndian.PutUint16(bs, uint16(n1)) 1107 return bs 1108 } 1109 1110 func keySortStrings(s map[string][]byte) []string { 1111 keys := make([]string, len(s)) 1112 i := 0 1113 for key := range s { 1114 keys[i] = key 1115 i++ 1116 } 1117 sort.Strings(keys) 1118 return keys 1119 } 1120 1121 func keySortInt(s map[int]int) []int { 1122 keys := make([]int, len(s)) 1123 i := 0 1124 for key := range s { 1125 keys[i] = key 1126 i++ 1127 } 1128 sort.Ints(keys) 1129 return keys 1130 } 1131 1132 func keySortArrayRangeMap(s map[int][]int) []int { 1133 keys := make([]int, len(s)) 1134 i := 0 1135 for key := range s { 1136 keys[i] = key 1137 i++ 1138 } 1139 sort.Ints(keys) 1140 return keys 1141 } 1142 1143 // UTF8CutFont is a utility function that generates a TrueType font composed 1144 // only of the runes included in cutset. The rune glyphs are copied from This 1145 // function is demonstrated in ExampleUTF8CutFont(). 1146 func UTF8CutFont(inBuf []byte, cutset string) (outBuf []byte) { 1147 f := newUTF8Font(&fileReader{readerPosition: 0, array: inBuf}) 1148 runes := map[int]int{} 1149 for i, r := range cutset { 1150 runes[i] = int(r) 1151 } 1152 outBuf = f.GenerateCutFont(runes) 1153 return 1154 }