1 50 51 package com.lowagie.text.pdf; 52 53 import java.io.File ; 54 import java.io.IOException ; 55 import java.util.ArrayList ; 56 import java.util.HashMap ; 57 import java.util.Iterator ; 58 import java.util.Map ; 59 60 import com.lowagie.text.DocumentException; 61 import com.lowagie.text.ExceptionConverter; 62 66 class TrueTypeFont extends BaseFont { 67 68 70 static final String codePages[] = { 71 "1252 Latin 1", 72 "1250 Latin 2: Eastern Europe", 73 "1251 Cyrillic", 74 "1253 Greek", 75 "1254 Turkish", 76 "1255 Hebrew", 77 "1256 Arabic", 78 "1257 Windows Baltic", 79 "1258 Vietnamese", 80 null, 81 null, 82 null, 83 null, 84 null, 85 null, 86 null, 87 "874 Thai", 88 "932 JIS/Japan", 89 "936 Chinese: Simplified chars--PRC and Singapore", 90 "949 Korean Wansung", 91 "950 Chinese: Traditional chars--Taiwan and Hong Kong", 92 "1361 Korean Johab", 93 null, 94 null, 95 null, 96 null, 97 null, 98 null, 99 null, 100 "Macintosh Character Set (US Roman)", 101 "OEM Character Set", 102 "Symbol Character Set", 103 null, 104 null, 105 null, 106 null, 107 null, 108 null, 109 null, 110 null, 111 null, 112 null, 113 null, 114 null, 115 null, 116 null, 117 null, 118 null, 119 "869 IBM Greek", 120 "866 MS-DOS Russian", 121 "865 MS-DOS Nordic", 122 "864 Arabic", 123 "863 MS-DOS Canadian French", 124 "862 Hebrew", 125 "861 MS-DOS Icelandic", 126 "860 MS-DOS Portuguese", 127 "857 IBM Turkish", 128 "855 IBM Cyrillic; primarily Russian", 129 "852 Latin 2", 130 "775 MS-DOS Baltic", 131 "737 Greek; former 437 G", 132 "708 Arabic; ASMO 708", 133 "850 WE/Latin 1", 134 "437 US"}; 135 136 protected boolean justNames = false; 137 142 protected HashMap tables; 143 145 protected RandomAccessFileOrArray rf; 146 148 protected String fileName; 149 150 protected boolean cff = false; 151 152 protected int cffOffset; 153 154 protected int cffLength; 155 156 159 protected int directoryOffset; 160 163 protected String ttcIndex; 164 165 protected String style = ""; 166 168 protected FontHeader head = new FontHeader(); 169 171 protected HorizontalHeader hhea = new HorizontalHeader(); 172 174 protected WindowsMetrics os_2 = new WindowsMetrics(); 175 178 protected int GlyphWidths[]; 179 180 protected int bboxes[][]; 181 186 protected HashMap cmap10; 187 194 protected HashMap cmap31; 195 201 protected IntHashtable kerning = new IntHashtable(); 202 207 protected String fontName; 208 209 211 protected String fullName[][]; 212 213 215 protected String familyName[][]; 216 223 protected double italicAngle; 224 226 protected boolean isFixedPitch = false; 227 228 230 protected static class FontHeader { 231 232 int flags; 233 234 int unitsPerEm; 235 236 short xMin; 237 238 short yMin; 239 240 short xMax; 241 242 short yMax; 243 244 int macStyle; 245 } 246 247 249 protected static class HorizontalHeader { 250 251 short Ascender; 252 253 short Descender; 254 255 short LineGap; 256 257 int advanceWidthMax; 258 259 short minLeftSideBearing; 260 261 short minRightSideBearing; 262 263 short xMaxExtent; 264 265 short caretSlopeRise; 266 267 short caretSlopeRun; 268 269 int numberOfHMetrics; 270 } 271 272 274 protected static class WindowsMetrics { 275 276 short xAvgCharWidth; 277 278 int usWeightClass; 279 280 int usWidthClass; 281 282 short fsType; 283 284 short ySubscriptXSize; 285 286 short ySubscriptYSize; 287 288 short ySubscriptXOffset; 289 290 short ySubscriptYOffset; 291 292 short ySuperscriptXSize; 293 294 short ySuperscriptYSize; 295 296 short ySuperscriptXOffset; 297 298 short ySuperscriptYOffset; 299 300 short yStrikeoutSize; 301 302 short yStrikeoutPosition; 303 304 short sFamilyClass; 305 306 byte panose[] = new byte[10]; 307 308 byte achVendID[] = new byte[4]; 309 310 int fsSelection; 311 312 int usFirstCharIndex; 313 314 int usLastCharIndex; 315 316 short sTypoAscender; 317 318 short sTypoDescender; 319 320 short sTypoLineGap; 321 322 int usWinAscent; 323 324 int usWinDescent; 325 326 int ulCodePageRange1; 327 328 int ulCodePageRange2; 329 330 int sCapHeight; 331 } 332 333 335 protected TrueTypeFont() { 336 } 337 338 TrueTypeFont(String ttFile, String enc, boolean emb, byte ttfAfm[]) throws DocumentException, IOException { 339 this(ttFile, enc, emb, ttfAfm, false); 340 } 341 342 351 TrueTypeFont(String ttFile, String enc, boolean emb, byte ttfAfm[], boolean justNames) throws DocumentException, IOException { 352 this.justNames = justNames; 353 String nameBase = getBaseName(ttFile); 354 String ttcName = getTTCName(nameBase); 355 if (nameBase.length() < ttFile.length()) { 356 style = ttFile.substring(nameBase.length()); 357 } 358 encoding = enc; 359 embedded = emb; 360 fileName = ttcName; 361 fontType = FONT_TYPE_TT; 362 ttcIndex = ""; 363 if (ttcName.length() < nameBase.length()) 364 ttcIndex = nameBase.substring(ttcName.length() + 1); 365 if (fileName.toLowerCase().endsWith(".ttf") || fileName.toLowerCase().endsWith(".otf") || fileName.toLowerCase().endsWith(".ttc")) { 366 process(ttfAfm); 367 if (!justNames && embedded && os_2.fsType == 2) 368 throw new DocumentException(fileName + style + " cannot be embedded due to licensing restrictions."); 369 } 370 else 371 throw new DocumentException(fileName + style + " is not a TTF, OTF or TTC font file."); 372 if (!encoding.startsWith("#")) 373 PdfEncodings.convertToBytes(" ", enc); createEncoding(); 375 } 376 377 383 protected static String getTTCName(String name) { 384 int idx = name.toLowerCase().indexOf(".ttc,"); 385 if (idx < 0) 386 return name; 387 else 388 return name.substring(0, idx + 4); 389 } 390 391 392 397 void fillTables() throws DocumentException, IOException { 398 int table_location[]; 399 table_location = (int[])tables.get("head"); 400 if (table_location == null) 401 throw new DocumentException("Table 'head' does not exist in " + fileName + style); 402 rf.seek(table_location[0] + 16); 403 head.flags = rf.readUnsignedShort(); 404 head.unitsPerEm = rf.readUnsignedShort(); 405 rf.skipBytes(16); 406 head.xMin = rf.readShort(); 407 head.yMin = rf.readShort(); 408 head.xMax = rf.readShort(); 409 head.yMax = rf.readShort(); 410 head.macStyle = rf.readUnsignedShort(); 411 412 table_location = (int[])tables.get("hhea"); 413 if (table_location == null) 414 throw new DocumentException("Table 'hhea' does not exist " + fileName + style); 415 rf.seek(table_location[0] + 4); 416 hhea.Ascender = rf.readShort(); 417 hhea.Descender = rf.readShort(); 418 hhea.LineGap = rf.readShort(); 419 hhea.advanceWidthMax = rf.readUnsignedShort(); 420 hhea.minLeftSideBearing = rf.readShort(); 421 hhea.minRightSideBearing = rf.readShort(); 422 hhea.xMaxExtent = rf.readShort(); 423 hhea.caretSlopeRise = rf.readShort(); 424 hhea.caretSlopeRun = rf.readShort(); 425 rf.skipBytes(12); 426 hhea.numberOfHMetrics = rf.readUnsignedShort(); 427 428 table_location = (int[])tables.get("OS/2"); 429 if (table_location == null) 430 throw new DocumentException("Table 'OS/2' does not exist in " + fileName + style); 431 rf.seek(table_location[0]); 432 int version = rf.readUnsignedShort(); 433 os_2.xAvgCharWidth = rf.readShort(); 434 os_2.usWeightClass = rf.readUnsignedShort(); 435 os_2.usWidthClass = rf.readUnsignedShort(); 436 os_2.fsType = rf.readShort(); 437 os_2.ySubscriptXSize = rf.readShort(); 438 os_2.ySubscriptYSize = rf.readShort(); 439 os_2.ySubscriptXOffset = rf.readShort(); 440 os_2.ySubscriptYOffset = rf.readShort(); 441 os_2.ySuperscriptXSize = rf.readShort(); 442 os_2.ySuperscriptYSize = rf.readShort(); 443 os_2.ySuperscriptXOffset = rf.readShort(); 444 os_2.ySuperscriptYOffset = rf.readShort(); 445 os_2.yStrikeoutSize = rf.readShort(); 446 os_2.yStrikeoutPosition = rf.readShort(); 447 os_2.sFamilyClass = rf.readShort(); 448 rf.readFully(os_2.panose); 449 rf.skipBytes(16); 450 rf.readFully(os_2.achVendID); 451 os_2.fsSelection = rf.readUnsignedShort(); 452 os_2.usFirstCharIndex = rf.readUnsignedShort(); 453 os_2.usLastCharIndex = rf.readUnsignedShort(); 454 os_2.sTypoAscender = rf.readShort(); 455 os_2.sTypoDescender = rf.readShort(); 456 if (os_2.sTypoDescender > 0) 457 os_2.sTypoDescender = (short)(-os_2.sTypoDescender); 458 os_2.sTypoLineGap = rf.readShort(); 459 os_2.usWinAscent = rf.readUnsignedShort(); 460 os_2.usWinDescent = rf.readUnsignedShort(); 461 os_2.ulCodePageRange1 = 0; 462 os_2.ulCodePageRange2 = 0; 463 if (version > 0) { 464 os_2.ulCodePageRange1 = rf.readInt(); 465 os_2.ulCodePageRange2 = rf.readInt(); 466 } 467 if (version > 1) { 468 rf.skipBytes(2); 469 os_2.sCapHeight = rf.readShort(); 470 } 471 else 472 os_2.sCapHeight = (int)(0.7 * head.unitsPerEm); 473 474 table_location = (int[])tables.get("post"); 475 if (table_location == null) { 476 italicAngle = -Math.atan2(hhea.caretSlopeRun, hhea.caretSlopeRise) * 180 / Math.PI; 477 return; 478 } 479 rf.seek(table_location[0] + 4); 480 short mantissa = rf.readShort(); 481 int fraction = rf.readUnsignedShort(); 482 italicAngle = (double)mantissa + (double)fraction / 16384.0; 483 rf.skipBytes(4); 484 isFixedPitch = rf.readInt() != 0; 485 } 486 487 493 String getBaseFont() throws DocumentException, IOException { 494 int table_location[]; 495 table_location = (int[])tables.get("name"); 496 if (table_location == null) 497 throw new DocumentException("Table 'name' does not exist in " + fileName + style); 498 rf.seek(table_location[0] + 2); 499 int numRecords = rf.readUnsignedShort(); 500 int startOfStorage = rf.readUnsignedShort(); 501 for (int k = 0; k < numRecords; ++k) { 502 int platformID = rf.readUnsignedShort(); 503 int platformEncodingID = rf.readUnsignedShort(); 504 int languageID = rf.readUnsignedShort(); 505 int nameID = rf.readUnsignedShort(); 506 int length = rf.readUnsignedShort(); 507 int offset = rf.readUnsignedShort(); 508 if (nameID == 6) { 509 rf.seek(table_location[0] + startOfStorage + offset); 510 if (platformID == 0 || platformID == 3) 511 return readUnicodeString(length); 512 else 513 return readStandardString(length); 514 } 515 } 516 File file = new File (fileName); 517 return file.getName().replace(' ', '-'); 518 } 519 520 525 String [][] getNames(int id) throws DocumentException, IOException { 526 int table_location[]; 527 table_location = (int[])tables.get("name"); 528 if (table_location == null) 529 throw new DocumentException("Table 'name' does not exist in " + fileName + style); 530 rf.seek(table_location[0] + 2); 531 int numRecords = rf.readUnsignedShort(); 532 int startOfStorage = rf.readUnsignedShort(); 533 ArrayList names = new ArrayList (); 534 for (int k = 0; k < numRecords; ++k) { 535 int platformID = rf.readUnsignedShort(); 536 int platformEncodingID = rf.readUnsignedShort(); 537 int languageID = rf.readUnsignedShort(); 538 int nameID = rf.readUnsignedShort(); 539 int length = rf.readUnsignedShort(); 540 int offset = rf.readUnsignedShort(); 541 if (nameID == id) { 542 int pos = rf.getFilePointer(); 543 rf.seek(table_location[0] + startOfStorage + offset); 544 String name; 545 if (platformID == 0 || platformID == 3 || (platformID == 2 && platformEncodingID == 1)){ 546 name = readUnicodeString(length); 547 } 548 else { 549 name = readStandardString(length); 550 } 551 names.add(new String []{String.valueOf(platformID), 552 String.valueOf(platformEncodingID), String.valueOf(languageID), name}); 553 rf.seek(pos); 554 } 555 } 556 String thisName[][] = new String [names.size()][]; 557 for (int k = 0; k < names.size(); ++k) 558 thisName[k] = (String [])names.get(k); 559 return thisName; 560 } 561 562 void checkCff() { 563 int table_location[]; 564 table_location = (int[])tables.get("CFF "); 565 if (table_location != null) { 566 cff = true; 567 cffOffset = table_location[0]; 568 cffLength = table_location[1]; 569 } 570 } 571 572 577 void process(byte ttfAfm[]) throws DocumentException, IOException { 578 tables = new HashMap (); 579 580 try { 581 if (ttfAfm == null) 582 rf = new RandomAccessFileOrArray(fileName); 583 else 584 rf = new RandomAccessFileOrArray(ttfAfm); 585 if (ttcIndex.length() > 0) { 586 int dirIdx = Integer.parseInt(ttcIndex); 587 if (dirIdx < 0) 588 throw new DocumentException("The font index for " + fileName + " must be positive."); 589 String mainTag = readStandardString(4); 590 if (!mainTag.equals("ttcf")) 591 throw new DocumentException(fileName + " is not a valid TTC file."); 592 rf.skipBytes(4); 593 int dirCount = rf.readInt(); 594 if (dirIdx >= dirCount) 595 throw new DocumentException("The font index for " + fileName + " must be between 0 and " + (dirCount - 1) + ". It was " + dirIdx + "."); 596 rf.skipBytes(dirIdx * 4); 597 directoryOffset = rf.readInt(); 598 } 599 rf.seek(directoryOffset); 600 int ttId = rf.readInt(); 601 if (ttId != 0x00010000 && ttId != 0x4F54544F) 602 throw new DocumentException(fileName + " is not a valid TTF or OTF file."); 603 int num_tables = rf.readUnsignedShort(); 604 rf.skipBytes(6); 605 for (int k = 0; k < num_tables; ++k) { 606 String tag = readStandardString(4); 607 rf.skipBytes(4); 608 int table_location[] = new int[2]; 609 table_location[0] = rf.readInt(); 610 table_location[1] = rf.readInt(); 611 tables.put(tag, table_location); 612 } 613 checkCff(); 614 fontName = getBaseFont(); 615 fullName = getNames(4); familyName = getNames(1); if (!justNames) { 618 fillTables(); 619 readGlyphWidths(); 620 readCMaps(); 621 readKerning(); 622 readBbox(); 623 GlyphWidths = null; 624 } 625 } 626 finally { 627 if (rf != null) { 628 rf.close(); 629 if (!embedded) 630 rf = null; 631 } 632 } 633 } 634 635 641 protected String readStandardString(int length) throws IOException { 642 byte buf[] = new byte[length]; 643 rf.readFully(buf); 644 try { 645 return new String (buf, WINANSI); 646 } 647 catch (Exception e) { 648 throw new ExceptionConverter(e); 649 } 650 } 651 652 659 protected String readUnicodeString(int length) throws IOException { 660 StringBuffer buf = new StringBuffer (); 661 length /= 2; 662 for (int k = 0; k < length; ++k) { 663 buf.append(rf.readChar()); 664 } 665 return buf.toString(); 666 } 667 668 673 protected void readGlyphWidths() throws DocumentException, IOException { 674 int table_location[]; 675 table_location = (int[])tables.get("hmtx"); 676 if (table_location == null) 677 throw new DocumentException("Table 'hmtx' does not exist in " + fileName + style); 678 rf.seek(table_location[0]); 679 GlyphWidths = new int[hhea.numberOfHMetrics]; 680 for (int k = 0; k < hhea.numberOfHMetrics; ++k) { 681 GlyphWidths[k] = (rf.readUnsignedShort() * 1000) / head.unitsPerEm; 682 rf.readUnsignedShort(); 683 } 684 } 685 686 690 protected int getGlyphWidth(int glyph) { 691 if (glyph >= GlyphWidths.length) 692 glyph = GlyphWidths.length - 1; 693 return GlyphWidths[glyph]; 694 } 695 696 private void readBbox() throws DocumentException, IOException { 697 int tableLocation[]; 698 tableLocation = (int[])tables.get("head"); 699 if (tableLocation == null) 700 throw new DocumentException("Table 'head' does not exist in " + fileName + style); 701 rf.seek(tableLocation[0] + TrueTypeFontSubSet.HEAD_LOCA_FORMAT_OFFSET); 702 boolean locaShortTable = (rf.readUnsignedShort() == 0); 703 tableLocation = (int[])tables.get("loca"); 704 if (tableLocation == null) 705 return; 706 rf.seek(tableLocation[0]); 707 int locaTable[]; 708 if (locaShortTable) { 709 int entries = tableLocation[1] / 2; 710 locaTable = new int[entries]; 711 for (int k = 0; k < entries; ++k) 712 locaTable[k] = rf.readUnsignedShort() * 2; 713 } 714 else { 715 int entries = tableLocation[1] / 4; 716 locaTable = new int[entries]; 717 for (int k = 0; k < entries; ++k) 718 locaTable[k] = rf.readInt(); 719 } 720 tableLocation = (int[])tables.get("glyf"); 721 if (tableLocation == null) 722 throw new DocumentException("Table 'glyf' does not exist in " + fileName + style); 723 int tableGlyphOffset = tableLocation[0]; 724 bboxes = new int[locaTable.length - 1][]; 725 for (int glyph = 0; glyph < locaTable.length - 1; ++glyph) { 726 int start = locaTable[glyph]; 727 if (start != locaTable[glyph + 1]) { 728 rf.seek(tableGlyphOffset + start + 2); 729 bboxes[glyph] = new int[]{ 730 (rf.readShort() * 1000) / head.unitsPerEm, 731 (rf.readShort() * 1000) / head.unitsPerEm, 732 (rf.readShort() * 1000) / head.unitsPerEm, 733 (rf.readShort() * 1000) / head.unitsPerEm}; 734 } 735 } 736 } 737 738 743 void readCMaps() throws DocumentException, IOException { 744 int table_location[]; 745 table_location = (int[])tables.get("cmap"); 746 if (table_location == null) 747 throw new DocumentException("Table 'cmap' does not exist in " + fileName + style); 748 rf.seek(table_location[0]); 749 rf.skipBytes(2); 750 int num_tables = rf.readUnsignedShort(); 751 fontSpecific = false; 752 int map10 = 0; 753 int map31 = 0; 754 int map30 = 0; 755 for (int k = 0; k < num_tables; ++k) { 756 int platId = rf.readUnsignedShort(); 757 int platSpecId = rf.readUnsignedShort(); 758 int offset = rf.readInt(); 759 if (platId == 3 && platSpecId == 0) { 760 fontSpecific = true; 761 map30 = offset; 762 } 763 else if (platId == 3 && platSpecId == 1) { 764 map31 = offset; 765 } 766 if (platId == 1 && platSpecId == 0) { 767 map10 = offset; 768 } 769 } 770 if (map10 > 0) { 771 rf.seek(table_location[0] + map10); 772 int format = rf.readUnsignedShort(); 773 switch (format) { 774 case 0: 775 cmap10 = readFormat0(); 776 break; 777 case 4: 778 cmap10 = readFormat4(); 779 break; 780 case 6: 781 cmap10 = readFormat6(); 782 break; 783 } 784 } 785 if (map31 > 0) { 786 rf.seek(table_location[0] + map31); 787 int format = rf.readUnsignedShort(); 788 if (format == 4) { 789 cmap31 = readFormat4(); 790 } 791 } 792 if (map30 > 0) { 793 rf.seek(table_location[0] + map30); 794 int format = rf.readUnsignedShort(); 795 if (format == 4) { 796 cmap10 = readFormat4(); 797 } 798 } 799 } 800 801 806 HashMap readFormat0() throws IOException { 807 HashMap h = new HashMap (); 808 rf.skipBytes(4); 809 for (int k = 0; k < 256; ++k) { 810 int r[] = new int[2]; 811 r[0] = rf.readUnsignedByte(); 812 r[1] = getGlyphWidth(r[0]); 813 h.put(new Integer (k), r); 814 } 815 return h; 816 } 817 818 823 HashMap readFormat4() throws IOException { 824 HashMap h = new HashMap (); 825 int table_lenght = rf.readUnsignedShort(); 826 rf.skipBytes(2); 827 int segCount = rf.readUnsignedShort() / 2; 828 rf.skipBytes(6); 829 int endCount[] = new int[segCount]; 830 for (int k = 0; k < segCount; ++k) { 831 endCount[k] = rf.readUnsignedShort(); 832 } 833 rf.skipBytes(2); 834 int startCount[] = new int[segCount]; 835 for (int k = 0; k < segCount; ++k) { 836 startCount[k] = rf.readUnsignedShort(); 837 } 838 int idDelta[] = new int[segCount]; 839 for (int k = 0; k < segCount; ++k) { 840 idDelta[k] = rf.readUnsignedShort(); 841 } 842 int idRO[] = new int[segCount]; 843 for (int k = 0; k < segCount; ++k) { 844 idRO[k] = rf.readUnsignedShort(); 845 } 846 int glyphId[] = new int[table_lenght / 2 - 8 - segCount * 4]; 847 for (int k = 0; k < glyphId.length; ++k) { 848 glyphId[k] = rf.readUnsignedShort(); 849 } 850 for (int k = 0; k < segCount; ++k) { 851 int glyph; 852 for (int j = startCount[k]; j <= endCount[k] && j != 0xFFFF; ++j) { 853 if (idRO[k] == 0) { 854 glyph = (j + idDelta[k]) & 0xFFFF; 855 } 856 else { 857 int idx = k + idRO[k] / 2 - segCount + j - startCount[k]; 858 if (idx >= glyphId.length) 859 continue; 860 glyph = (glyphId[idx] + idDelta[k]) & 0xFFFF; 861 } 862 int r[] = new int[2]; 863 r[0] = glyph; 864 r[1] = getGlyphWidth(r[0]); 865 h.put(new Integer (fontSpecific ? ((j & 0xff00) == 0xf000 ? j & 0xff : j) : j), r); 866 } 867 } 868 return h; 869 } 870 871 877 HashMap readFormat6() throws IOException { 878 HashMap h = new HashMap (); 879 rf.skipBytes(4); 880 int start_code = rf.readUnsignedShort(); 881 int code_count = rf.readUnsignedShort(); 882 for (int k = 0; k < code_count; ++k) { 883 int r[] = new int[2]; 884 r[0] = rf.readUnsignedShort(); 885 r[1] = getGlyphWidth(r[0]); 886 h.put(new Integer (k + start_code), r); 887 } 888 return h; 889 } 890 891 894 void readKerning() throws IOException { 895 int table_location[]; 896 table_location = (int[])tables.get("kern"); 897 if (table_location == null) 898 return; 899 rf.seek(table_location[0] + 2); 900 int nTables = rf.readUnsignedShort(); 901 int checkpoint = table_location[0] + 4; 902 int length = 0; 903 for (int k = 0; k < nTables; ++k) { 904 checkpoint += length; 905 rf.seek(checkpoint); 906 rf.skipBytes(2); 907 length = rf.readUnsignedShort(); 908 int coverage = rf.readUnsignedShort(); 909 if ((coverage & 0xfff7) == 0x0001) { 910 int nPairs = rf.readUnsignedShort(); 911 rf.skipBytes(6); 912 for (int j = 0; j < nPairs; ++j) { 913 int pair = rf.readInt(); 914 int value = ((int)rf.readShort() * 1000) / head.unitsPerEm; 915 kerning.put(pair, value); 916 } 917 } 918 } 919 } 920 921 926 public int getKerning(char char1, char char2) { 927 int metrics[] = getMetricsTT(char1); 928 if (metrics == null) 929 return 0; 930 int c1 = metrics[0]; 931 metrics = getMetricsTT(char2); 932 if (metrics == null) 933 return 0; 934 int c2 = metrics[0]; 935 return kerning.get((c1 << 16) + c2); 936 } 937 938 944 int getRawWidth(int c, String name) { 945 HashMap map = null; 946 if (name == null || cmap31 == null) 947 map = cmap10; 948 else 949 map = cmap31; 950 if (map == null) 951 return 0; 952 int metric[] = (int[])map.get(new Integer (c)); 953 if (metric == null) 954 return 0; 955 return metric[1]; 956 } 957 958 964 protected PdfDictionary getFontDescriptor(PdfIndirectReference fontStream, String subsetPrefix) { 965 PdfDictionary dic = new PdfDictionary(PdfName.FONTDESCRIPTOR); 966 dic.put(PdfName.ASCENT, new PdfNumber((int)os_2.sTypoAscender * 1000 / head.unitsPerEm)); 967 dic.put(PdfName.CAPHEIGHT, new PdfNumber((int)os_2.sCapHeight * 1000 / head.unitsPerEm)); 968 dic.put(PdfName.DESCENT, new PdfNumber((int)os_2.sTypoDescender * 1000 / head.unitsPerEm)); 969 dic.put(PdfName.FONTBBOX, new PdfRectangle( 970 (int)head.xMin * 1000 / head.unitsPerEm, 971 (int)head.yMin * 1000 / head.unitsPerEm, 972 (int)head.xMax * 1000 / head.unitsPerEm, 973 (int)head.yMax * 1000 / head.unitsPerEm)); 974 if (cff) { 975 if (encoding.startsWith("Identity-")) 976 dic.put(PdfName.FONTNAME, new PdfName(subsetPrefix + fontName+"-"+encoding)); 977 else 978 dic.put(PdfName.FONTNAME, new PdfName(subsetPrefix + fontName + style)); 979 } 980 else 981 dic.put(PdfName.FONTNAME, new PdfName(subsetPrefix + fontName + style)); 982 dic.put(PdfName.ITALICANGLE, new PdfNumber(italicAngle)); 983 dic.put(PdfName.STEMV, new PdfNumber(80)); 984 if (fontStream != null) { 985 if (cff) 986 dic.put(PdfName.FONTFILE3, fontStream); 987 else 988 dic.put(PdfName.FONTFILE2, fontStream); 989 } 990 int flags = 0; 991 if (isFixedPitch) 992 flags |= 1; 993 flags |= fontSpecific ? 4 : 32; 994 if ((head.macStyle & 2) != 0) 995 flags |= 64; 996 if ((head.macStyle & 1) != 0) 997 flags |= 262144; 998 dic.put(PdfName.FLAGS, new PdfNumber(flags)); 999 1000 return dic; 1001 } 1002 1003 1012 protected PdfDictionary getFontBaseType(PdfIndirectReference fontDescriptor, String subsetPrefix, int firstChar, int lastChar, byte shortTag[]) { 1013 PdfDictionary dic = new PdfDictionary(PdfName.FONT); 1014 if (cff) { 1015 dic.put(PdfName.SUBTYPE, PdfName.TYPE1); 1016 dic.put(PdfName.BASEFONT, new PdfName(fontName + style)); 1017 } 1018 else { 1019 dic.put(PdfName.SUBTYPE, PdfName.TRUETYPE); 1020 dic.put(PdfName.BASEFONT, new PdfName(subsetPrefix + fontName + style)); 1021 } 1022 dic.put(PdfName.BASEFONT, new PdfName(subsetPrefix + fontName + style)); 1023 if (!fontSpecific) { 1024 for (int k = firstChar; k <= lastChar; ++k) { 1025 if (!differences[k].equals(notdef)) { 1026 firstChar = k; 1027 break; 1028 } 1029 } 1030 if (encoding.equals("Cp1252") || encoding.equals("MacRoman")) 1031 dic.put(PdfName.ENCODING, encoding.equals("Cp1252") ? PdfName.WIN_ANSI_ENCODING : PdfName.MAC_ROMAN_ENCODING); 1032 else { 1033 PdfDictionary enc = new PdfDictionary(PdfName.ENCODING); 1034 PdfArray dif = new PdfArray(); 1035 boolean gap = true; 1036 for (int k = firstChar; k <= lastChar; ++k) { 1037 if (shortTag[k] != 0) { 1038 if (gap) { 1039 dif.add(new PdfNumber(k)); 1040 gap = false; 1041 } 1042 dif.add(new PdfName(differences[k])); 1043 } 1044 else 1045 gap = true; 1046 } 1047 enc.put(PdfName.DIFFERENCES, dif); 1048 dic.put(PdfName.ENCODING, enc); 1049 } 1050 } 1051 dic.put(PdfName.FIRSTCHAR, new PdfNumber(firstChar)); 1052 dic.put(PdfName.LASTCHAR, new PdfNumber(lastChar)); 1053 PdfArray wd = new PdfArray(); 1054 for (int k = firstChar; k <= lastChar; ++k) { 1055 if (shortTag[k] == 0) 1056 wd.add(new PdfNumber(0)); 1057 else 1058 wd.add(new PdfNumber(widths[k])); 1059 } 1060 dic.put(PdfName.WIDTHS, wd); 1061 if (fontDescriptor != null) 1062 dic.put(PdfName.FONTDESCRIPTOR, fontDescriptor); 1063 return dic; 1064 } 1065 1066 protected byte[] getFullFont() throws IOException { 1067 RandomAccessFileOrArray rf2 = null; 1068 try { 1069 rf2 = new RandomAccessFileOrArray(rf); 1070 rf2.reOpen(); 1071 byte b[] = new byte[rf2.length()]; 1072 rf2.readFully(b); 1073 return b; 1074 } 1075 finally { 1076 try {if (rf2 != null) {rf2.close();}} catch (Exception e) {} 1077 } 1078 } 1079 1080 protected static int[] compactRanges(ArrayList ranges) { 1081 ArrayList simp = new ArrayList (); 1082 for (int k = 0; k < ranges.size(); ++k) { 1083 int[] r = (int[])ranges.get(k); 1084 for (int j = 0; j < r.length; j += 2) { 1085 simp.add(new int[]{Math.max(0, Math.min(r[j], r[j + 1])), Math.min(0xffff, Math.max(r[j], r[j + 1]))}); 1086 } 1087 } 1088 for (int k1 = 0; k1 < simp.size() - 1; ++k1) { 1089 for (int k2 = k1 + 1; k2 < simp.size(); ++k2) { 1090 int[] r1 = (int[])simp.get(k1); 1091 int[] r2 = (int[])simp.get(k2); 1092 if ((r1[0] >= r2[0] && r1[0] <= r2[1]) || (r1[1] >= r2[0] && r1[0] <= r2[1])) { 1093 r1[0] = Math.min(r1[0], r2[0]); 1094 r1[1] = Math.max(r1[1], r2[1]); 1095 simp.remove(k2); 1096 --k2; 1097 } 1098 } 1099 } 1100 int[] s = new int[simp.size() * 2]; 1101 for (int k = 0; k < simp.size(); ++k) { 1102 int[] r = (int[])simp.get(k); 1103 s[k * 2] = r[0]; 1104 s[k * 2 + 1] = r[1]; 1105 } 1106 return s; 1107 } 1108 1109 protected void addRangeUni(HashMap longTag, boolean includeMetrics, boolean subsetp) { 1110 if (!subsetp && (subsetRanges != null || directoryOffset > 0)) { 1111 int[] rg = (subsetRanges == null && directoryOffset > 0) ? new int[]{0, 0xffff} : compactRanges(subsetRanges); 1112 HashMap usemap; 1113 if (!fontSpecific && cmap31 != null) 1114 usemap = cmap31; 1115 else if (fontSpecific && cmap10 != null) 1116 usemap = cmap10; 1117 else if (cmap31 != null) 1118 usemap = cmap31; 1119 else 1120 usemap = cmap10; 1121 for (Iterator it = usemap.entrySet().iterator(); it.hasNext();) { 1122 Map.Entry e = (Map.Entry )it.next(); 1123 int[] v = (int[])e.getValue(); 1124 Integer gi = new Integer (v[0]); 1125 if (longTag.containsKey(gi)) 1126 continue; 1127 int c = ((Integer )e.getKey()).intValue(); 1128 boolean skip = true; 1129 for (int k = 0; k < rg.length; k += 2) { 1130 if (c >= rg[k] && c <= rg[k + 1]) { 1131 skip = false; 1132 break; 1133 } 1134 } 1135 if (!skip) 1136 longTag.put(gi, includeMetrics ? new int[]{v[0], v[1], c} : null); 1137 } 1138 } 1139 } 1140 1141 1148 void writeFont(PdfWriter writer, PdfIndirectReference ref, Object params[]) throws DocumentException, IOException { 1149 int firstChar = ((Integer )params[0]).intValue(); 1150 int lastChar = ((Integer )params[1]).intValue(); 1151 byte shortTag[] = (byte[])params[2]; 1152 boolean subsetp = ((Boolean )params[3]).booleanValue() && subset; 1153 1154 if (!subsetp) { 1155 firstChar = 0; 1156 lastChar = shortTag.length - 1; 1157 for (int k = 0; k < shortTag.length; ++k) 1158 shortTag[k] = 1; 1159 } 1160 PdfIndirectReference ind_font = null; 1161 PdfObject pobj = null; 1162 PdfIndirectObject obj = null; 1163 String subsetPrefix = ""; 1164 if (embedded) { 1165 if (cff) { 1166 RandomAccessFileOrArray rf2 = new RandomAccessFileOrArray(rf); 1167 byte b[] = new byte[cffLength]; 1168 try { 1169 rf2.reOpen(); 1170 rf2.seek(cffOffset); 1171 rf2.readFully(b); 1172 } 1173 finally { 1174 try { 1175 rf2.close(); 1176 } 1177 catch (Exception e) { 1178 } 1180 } 1181 pobj = new StreamFont(b, "Type1C"); 1182 obj = writer.addToBody(pobj); 1183 ind_font = obj.getIndirectReference(); 1184 } 1185 else { 1186 if (subsetp) 1187 subsetPrefix = createSubsetPrefix(); 1188 HashMap glyphs = new HashMap (); 1189 for (int k = firstChar; k <= lastChar; ++k) { 1190 if (shortTag[k] != 0) { 1191 int[] metrics = null; 1192 if (specialMap != null) { 1193 int[] cd = GlyphList.nameToUnicode(differences[k]); 1194 if (cd != null) 1195 metrics = getMetricsTT(cd[0]); 1196 } 1197 else { 1198 if (fontSpecific) 1199 metrics = getMetricsTT(k); 1200 else 1201 metrics = getMetricsTT(unicodeDifferences[k]); 1202 } 1203 if (metrics != null) 1204 glyphs.put(new Integer (metrics[0]), null); 1205 } 1206 } 1207 addRangeUni(glyphs, false, subsetp); 1208 byte[] b = null; 1209 if (subsetp || directoryOffset != 0 || subsetRanges != null) { 1210 TrueTypeFontSubSet sb = new TrueTypeFontSubSet(fileName, new RandomAccessFileOrArray(rf), glyphs, directoryOffset, true, !subsetp); 1211 b = sb.process(); 1212 } 1213 else { 1214 b = getFullFont(); 1215 } 1216 int lengths[] = new int[]{b.length}; 1217 pobj = new StreamFont(b, lengths); 1218 obj = writer.addToBody(pobj); 1219 ind_font = obj.getIndirectReference(); 1220 } 1221 } 1222 pobj = getFontDescriptor(ind_font, subsetPrefix); 1223 if (pobj != null){ 1224 obj = writer.addToBody(pobj); 1225 ind_font = obj.getIndirectReference(); 1226 } 1227 pobj = getFontBaseType(ind_font, subsetPrefix, firstChar, lastChar, shortTag); 1228 writer.addToBody(pobj, ref); 1229 } 1230 1231 1238 public float getFontDescriptor(int key, float fontSize) { 1239 switch (key) { 1240 case ASCENT: 1241 return (float)os_2.sTypoAscender * fontSize / (float)head.unitsPerEm; 1242 case CAPHEIGHT: 1243 return (float)os_2.sCapHeight * fontSize / (float)head.unitsPerEm; 1244 case DESCENT: 1245 return (float)os_2.sTypoDescender * fontSize / (float)head.unitsPerEm; 1246 case ITALICANGLE: 1247 return (float)italicAngle; 1248 case BBOXLLX: 1249 return fontSize * (int)head.xMin / head.unitsPerEm; 1250 case BBOXLLY: 1251 return fontSize * (int)head.yMin / head.unitsPerEm; 1252 case BBOXURX: 1253 return fontSize * (int)head.xMax / head.unitsPerEm; 1254 case BBOXURY: 1255 return fontSize * (int)head.yMax / head.unitsPerEm; 1256 case AWT_ASCENT: 1257 return fontSize * (int)hhea.Ascender / head.unitsPerEm; 1258 case AWT_DESCENT: 1259 return fontSize * (int)hhea.Descender / head.unitsPerEm; 1260 case AWT_LEADING: 1261 return fontSize * (int)hhea.LineGap / head.unitsPerEm; 1262 case AWT_MAXADVANCE: 1263 return fontSize * (int)hhea.advanceWidthMax / head.unitsPerEm; 1264 } 1265 return 0; 1266 } 1267 1268 1272 public int[] getMetricsTT(int c) { 1273 if (!fontSpecific && cmap31 != null) 1274 return (int[])cmap31.get(new Integer (c)); 1275 if (fontSpecific && cmap10 != null) 1276 return (int[])cmap10.get(new Integer (c)); 1277 if (cmap31 != null) 1278 return (int[])cmap31.get(new Integer (c)); 1279 if (cmap10 != null) 1280 return (int[])cmap10.get(new Integer (c)); 1281 return null; 1282 } 1283 1284 1287 public String getPostscriptFontName() { 1288 return fontName; 1289 } 1290 1291 1294 public String [] getCodePagesSupported() { 1295 long cp = (((long)os_2.ulCodePageRange2) << 32) + ((long)os_2.ulCodePageRange1 & 0xffffffffL); 1296 int count = 0; 1297 long bit = 1; 1298 for (int k = 0; k < 64; ++k) { 1299 if ((cp & bit) != 0 && codePages[k] != null) 1300 ++count; 1301 bit <<= 1; 1302 } 1303 String ret[] = new String [count]; 1304 count = 0; 1305 bit = 1; 1306 for (int k = 0; k < 64; ++k) { 1307 if ((cp & bit) != 0 && codePages[k] != null) 1308 ret[count++] = codePages[k]; 1309 bit <<= 1; 1310 } 1311 return ret; 1312 } 1313 1314 1322 public String [][] getFullFontName() { 1323 return fullName; 1324 } 1325 1326 1334 public String [][] getFamilyFontName() { 1335 return familyName; 1336 } 1337 1338 1341 public boolean hasKernPairs() { 1342 return kerning.size() > 0; 1343 } 1344 1345 1350 public void setPostscriptFontName(String name) { 1351 fontName = name; 1352 } 1353 1354 1361 public boolean setKerning(char char1, char char2, int kern) { 1362 int metrics[] = getMetricsTT(char1); 1363 if (metrics == null) 1364 return false; 1365 int c1 = metrics[0]; 1366 metrics = getMetricsTT(char2); 1367 if (metrics == null) 1368 return false; 1369 int c2 = metrics[0]; 1370 kerning.put((c1 << 16) + c2, kern); 1371 return true; 1372 } 1373 1374 protected int[] getRawCharBBox(int c, String name) { 1375 HashMap map = null; 1376 if (name == null || cmap31 == null) 1377 map = cmap10; 1378 else 1379 map = cmap31; 1380 if (map == null) 1381 return null; 1382 int metric[] = (int[])map.get(new Integer (c)); 1383 if (metric == null || bboxes == null) 1384 return null; 1385 return bboxes[metric[0]]; 1386 } 1387} | Popular Tags |