1 50 51 package com.lowagie.text.pdf; 52 53 import java.io.ByteArrayOutputStream ; 54 import java.io.IOException ; 55 import java.io.InputStream ; 56 import java.util.HashMap ; 57 import java.util.StringTokenizer ; 58 59 import com.lowagie.text.DocumentException; 60 import com.lowagie.text.pdf.fonts.FontsResourceAnchor; 61 62 66 class Type1Font extends BaseFont 67 { 68 private static FontsResourceAnchor resourceAnchor; 69 70 72 protected byte pfb[]; 73 75 private String FontName; 76 78 private String FullName; 79 81 private String FamilyName; 82 84 private String Weight = ""; 85 87 private float ItalicAngle = 0.0f; 88 91 private boolean IsFixedPitch = false; 92 94 private String CharacterSet; 95 97 private int llx = -50; 98 100 private int lly = -200; 101 103 private int urx = 1000; 104 106 private int ury = 900; 107 109 private int UnderlinePosition = -100; 110 112 private int UnderlineThickness = 50; 113 118 private String EncodingScheme = "FontSpecific"; 119 121 private int CapHeight = 700; 122 124 private int XHeight = 480; 125 127 private int Ascender = 800; 128 130 private int Descender = -200; 131 133 private int StdHW; 134 136 private int StdVW = 80; 137 138 143 private HashMap CharMetrics = new HashMap (); 144 150 private HashMap KernPairs = new HashMap (); 151 153 private String fileName; 154 156 private boolean builtinFont = false; 157 160 private static final int PFB_TYPES[] = {1, 2, 1}; 161 162 171 Type1Font(String afmFile, String enc, boolean emb, byte ttfAfm[], byte pfb[]) throws DocumentException, IOException 172 { 173 if (emb && ttfAfm != null && pfb == null) 174 throw new DocumentException("Two byte arrays are needed if the Type1 font is embedded."); 175 if (emb && ttfAfm != null) 176 this.pfb = pfb; 177 encoding = enc; 178 embedded = emb; 179 fileName = afmFile; 180 fontType = FONT_TYPE_T1; 181 RandomAccessFileOrArray rf = null; 182 InputStream is = null; 183 if (BuiltinFonts14.containsKey(afmFile)) { 184 embedded = false; 185 builtinFont = true; 186 byte buf[] = new byte[1024]; 187 try { 188 if (resourceAnchor == null) 189 resourceAnchor = new FontsResourceAnchor(); 190 is = getResourceStream(RESOURCE_PATH + afmFile + ".afm", resourceAnchor.getClass().getClassLoader()); 191 if (is == null) { 192 String msg = afmFile + " not found as resource. (The *.afm files must exist as resources in the package com.lowagie.text.pdf.fonts)"; 193 System.err.println(msg); 194 throw new DocumentException(msg); 195 } 196 ByteArrayOutputStream out = new ByteArrayOutputStream (); 197 while (true) { 198 int size = is.read(buf); 199 if (size < 0) 200 break; 201 out.write(buf, 0, size); 202 } 203 buf = out.toByteArray(); 204 } 205 finally { 206 if (is != null) { 207 try { 208 is.close(); 209 } 210 catch (Exception e) { 211 } 213 } 214 } 215 try { 216 rf = new RandomAccessFileOrArray(buf); 217 process(rf); 218 } 219 finally { 220 if (rf != null) { 221 try { 222 rf.close(); 223 } 224 catch (Exception e) { 225 } 227 } 228 } 229 } 230 else if (afmFile.toLowerCase().endsWith(".afm")) { 231 try { 232 if (ttfAfm == null) 233 rf = new RandomAccessFileOrArray(afmFile); 234 else 235 rf = new RandomAccessFileOrArray(ttfAfm); 236 process(rf); 237 } 238 finally { 239 if (rf != null) { 240 try { 241 rf.close(); 242 } 243 catch (Exception e) { 244 } 246 } 247 } 248 } 249 else if (afmFile.toLowerCase().endsWith(".pfm")) { 250 try { 251 ByteArrayOutputStream ba = new ByteArrayOutputStream (); 252 if (ttfAfm == null) 253 rf = new RandomAccessFileOrArray(afmFile); 254 else 255 rf = new RandomAccessFileOrArray(ttfAfm); 256 Pfm2afm.convert(rf, ba); 257 rf.close(); 258 rf = new RandomAccessFileOrArray(ba.toByteArray()); 259 process(rf); 260 } 261 finally { 262 if (rf != null) { 263 try { 264 rf.close(); 265 } 266 catch (Exception e) { 267 } 269 } 270 } 271 } 272 else 273 throw new DocumentException(afmFile + " is not an AFM or PFM font file."); 274 275 EncodingScheme = EncodingScheme.trim(); 276 if (EncodingScheme.equals("AdobeStandardEncoding") || EncodingScheme.equals("StandardEncoding")) { 277 fontSpecific = false; 278 } 279 if (!encoding.startsWith("#")) 280 PdfEncodings.convertToBytes(" ", enc); createEncoding(); 282 } 283 284 291 int getRawWidth(int c, String name) { 292 Object metrics[]; 293 if (name == null) { metrics = (Object [])CharMetrics.get(new Integer (c)); 295 } 296 else { 297 if (name.equals(".notdef")) 298 return 0; 299 metrics = (Object [])CharMetrics.get(name); 300 } 301 if (metrics != null) 302 return ((Integer )(metrics[1])).intValue(); 303 return 0; 304 } 305 306 313 public int getKerning(char char1, char char2) 314 { 315 String first = GlyphList.unicodeToName((int)char1); 316 if (first == null) 317 return 0; 318 String second = GlyphList.unicodeToName((int)char2); 319 if (second == null) 320 return 0; 321 Object obj[] = (Object [])KernPairs.get(first); 322 if (obj == null) 323 return 0; 324 for (int k = 0; k < obj.length; k += 2) { 325 if (second.equals(obj[k])) 326 return ((Integer )obj[k + 1]).intValue(); 327 } 328 return 0; 329 } 330 331 332 337 public void process(RandomAccessFileOrArray rf) throws DocumentException, IOException 338 { 339 String line; 340 boolean isMetrics = false; 341 while ((line = rf.readLine()) != null) 342 { 343 StringTokenizer tok = new StringTokenizer (line, " ,\n\r\t\f"); 344 if (!tok.hasMoreTokens()) 345 continue; 346 String ident = tok.nextToken(); 347 if (ident.equals("FontName")) 348 FontName = tok.nextToken("\u00ff").substring(1); 349 else if (ident.equals("FullName")) 350 FullName = tok.nextToken("\u00ff").substring(1); 351 else if (ident.equals("FamilyName")) 352 FamilyName = tok.nextToken("\u00ff").substring(1); 353 else if (ident.equals("Weight")) 354 Weight = tok.nextToken("\u00ff").substring(1); 355 else if (ident.equals("ItalicAngle")) 356 ItalicAngle = Float.parseFloat(tok.nextToken()); 357 else if (ident.equals("IsFixedPitch")) 358 IsFixedPitch = tok.nextToken().equals("true"); 359 else if (ident.equals("CharacterSet")) 360 CharacterSet = tok.nextToken("\u00ff").substring(1); 361 else if (ident.equals("FontBBox")) 362 { 363 llx = (int)Float.parseFloat(tok.nextToken()); 364 lly = (int)Float.parseFloat(tok.nextToken()); 365 urx = (int)Float.parseFloat(tok.nextToken()); 366 ury = (int)Float.parseFloat(tok.nextToken()); 367 } 368 else if (ident.equals("UnderlinePosition")) 369 UnderlinePosition = (int)Float.parseFloat(tok.nextToken()); 370 else if (ident.equals("UnderlineThickness")) 371 UnderlineThickness = (int)Float.parseFloat(tok.nextToken()); 372 else if (ident.equals("EncodingScheme")) 373 EncodingScheme = tok.nextToken("\u00ff").substring(1); 374 else if (ident.equals("CapHeight")) 375 CapHeight = (int)Float.parseFloat(tok.nextToken()); 376 else if (ident.equals("XHeight")) 377 XHeight = (int)Float.parseFloat(tok.nextToken()); 378 else if (ident.equals("Ascender")) 379 Ascender = (int)Float.parseFloat(tok.nextToken()); 380 else if (ident.equals("Descender")) 381 Descender = (int)Float.parseFloat(tok.nextToken()); 382 else if (ident.equals("StdHW")) 383 StdHW = (int)Float.parseFloat(tok.nextToken()); 384 else if (ident.equals("StdVW")) 385 StdVW = (int)Float.parseFloat(tok.nextToken()); 386 else if (ident.equals("StartCharMetrics")) 387 { 388 isMetrics = true; 389 break; 390 } 391 } 392 if (!isMetrics) 393 throw new DocumentException("Missing StartCharMetrics in " + fileName); 394 while ((line = rf.readLine()) != null) 395 { 396 StringTokenizer tok = new StringTokenizer (line); 397 if (!tok.hasMoreTokens()) 398 continue; 399 String ident = tok.nextToken(); 400 if (ident.equals("EndCharMetrics")) 401 { 402 isMetrics = false; 403 break; 404 } 405 Integer C = new Integer (-1); 406 Integer WX = new Integer (250); 407 String N = ""; 408 int B[] = null; 409 410 tok = new StringTokenizer (line, ";"); 411 while (tok.hasMoreTokens()) 412 { 413 StringTokenizer tokc = new StringTokenizer (tok.nextToken()); 414 if (!tokc.hasMoreTokens()) 415 continue; 416 ident = tokc.nextToken(); 417 if (ident.equals("C")) 418 C = Integer.valueOf(tokc.nextToken()); 419 else if (ident.equals("WX")) 420 WX = new Integer ((int)Float.parseFloat(tokc.nextToken())); 421 else if (ident.equals("N")) 422 N = tokc.nextToken(); 423 else if (ident.equals("B")) { 424 B = new int[]{Integer.parseInt(tokc.nextToken()), 425 Integer.parseInt(tokc.nextToken()), 426 Integer.parseInt(tokc.nextToken()), 427 Integer.parseInt(tokc.nextToken())}; 428 } 429 } 430 Object metrics[] = new Object []{C, WX, N, B}; 431 if (C.intValue() >= 0) 432 CharMetrics.put(C, metrics); 433 CharMetrics.put(N, metrics); 434 } 435 if (isMetrics) 436 throw new DocumentException("Missing EndCharMetrics in " + fileName); 437 if (!CharMetrics.containsKey("nonbreakingspace")) { 438 Object [] space = (Object [])CharMetrics.get("space"); 439 if (space != null) 440 CharMetrics.put("nonbreakingspace", space); 441 } 442 while ((line = rf.readLine()) != null) 443 { 444 StringTokenizer tok = new StringTokenizer (line); 445 if (!tok.hasMoreTokens()) 446 continue; 447 String ident = tok.nextToken(); 448 if (ident.equals("EndFontMetrics")) 449 return; 450 if (ident.equals("StartKernPairs")) 451 { 452 isMetrics = true; 453 break; 454 } 455 } 456 if (!isMetrics) 457 throw new DocumentException("Missing EndFontMetrics in " + fileName); 458 while ((line = rf.readLine()) != null) 459 { 460 StringTokenizer tok = new StringTokenizer (line); 461 if (!tok.hasMoreTokens()) 462 continue; 463 String ident = tok.nextToken(); 464 if (ident.equals("KPX")) 465 { 466 String first = tok.nextToken(); 467 String second = tok.nextToken(); 468 Integer width = new Integer ((int)Float.parseFloat(tok.nextToken())); 469 Object relates[] = (Object [])KernPairs.get(first); 470 if (relates == null) 471 KernPairs.put(first, new Object []{second, width}); 472 else 473 { 474 int n = relates.length; 475 Object relates2[] = new Object [n + 2]; 476 System.arraycopy(relates, 0, relates2, 0, n); 477 relates2[n] = second; 478 relates2[n + 1] = width; 479 KernPairs.put(first, relates2); 480 } 481 } 482 else if (ident.equals("EndKernPairs")) 483 { 484 isMetrics = false; 485 break; 486 } 487 } 488 if (isMetrics) 489 throw new DocumentException("Missing EndKernPairs in " + fileName); 490 rf.close(); 491 } 492 493 499 private PdfStream getFontStream() throws DocumentException 500 { 501 if (builtinFont || !embedded) 502 return null; 503 RandomAccessFileOrArray rf = null; 504 try { 505 String filePfb = fileName.substring(0, fileName.length() - 3) + "pfb"; 506 if (pfb == null) 507 rf = new RandomAccessFileOrArray(filePfb); 508 else 509 rf = new RandomAccessFileOrArray(pfb); 510 int fileLength = rf.length(); 511 byte st[] = new byte[fileLength - 18]; 512 int lengths[] = new int[3]; 513 int bytePtr = 0; 514 for (int k = 0; k < 3; ++k) { 515 if (rf.read() != 0x80) 516 throw new DocumentException("Start marker missing in " + filePfb); 517 if (rf.read() != PFB_TYPES[k]) 518 throw new DocumentException("Incorrect segment type in " + filePfb); 519 int size = rf.read(); 520 size += rf.read() << 8; 521 size += rf.read() << 16; 522 size += rf.read() << 24; 523 lengths[k] = size; 524 while (size != 0) { 525 int got = rf.read(st, bytePtr, size); 526 if (got < 0) 527 throw new DocumentException("Premature end in " + filePfb); 528 bytePtr += got; 529 size -= got; 530 } 531 } 532 return new StreamFont(st, lengths); 533 } 534 catch (Exception e) { 535 throw new DocumentException(e); 536 } 537 finally { 538 if (rf != null) { 539 try { 540 rf.close(); 541 } 542 catch (Exception e) { 543 } 545 } 546 } 547 } 548 549 554 private PdfDictionary getFontDescriptor(PdfIndirectReference fontStream) 555 { 556 if (builtinFont) 557 return null; 558 PdfDictionary dic = new PdfDictionary(PdfName.FONTDESCRIPTOR); 559 dic.put(PdfName.ASCENT, new PdfNumber(Ascender)); 560 dic.put(PdfName.CAPHEIGHT, new PdfNumber(CapHeight)); 561 dic.put(PdfName.DESCENT, new PdfNumber(Descender)); 562 dic.put(PdfName.FONTBBOX, new PdfRectangle(llx, lly, urx, ury)); 563 dic.put(PdfName.FONTNAME, new PdfName(FontName)); 564 dic.put(PdfName.ITALICANGLE, new PdfNumber(ItalicAngle)); 565 dic.put(PdfName.STEMV, new PdfNumber(StdVW)); 566 if (fontStream != null) 567 dic.put(PdfName.FONTFILE, fontStream); 568 int flags = 0; 569 if (IsFixedPitch) 570 flags |= 1; 571 flags |= fontSpecific ? 4 : 32; 572 if (ItalicAngle < 0) 573 flags |= 64; 574 if (FontName.indexOf("Caps") >= 0 || FontName.endsWith("SC")) 575 flags |= 131072; 576 if (Weight.equals("Bold")) 577 flags |= 262144; 578 dic.put(PdfName.FLAGS, new PdfNumber(flags)); 579 580 return dic; 581 } 582 583 590 private PdfDictionary getFontBaseType(PdfIndirectReference fontDescriptor, int firstChar, int lastChar, byte shortTag[]) 591 { 592 PdfDictionary dic = new PdfDictionary(PdfName.FONT); 593 dic.put(PdfName.SUBTYPE, PdfName.TYPE1); 594 dic.put(PdfName.BASEFONT, new PdfName(FontName)); 595 boolean stdEncoding = encoding.equals("Cp1252") || encoding.equals("MacRoman"); 596 if (!fontSpecific || specialMap != null) { 597 for (int k = firstChar; k <= lastChar; ++k) { 598 if (!differences[k].equals(notdef)) { 599 firstChar = k; 600 break; 601 } 602 } 603 if (stdEncoding) 604 dic.put(PdfName.ENCODING, encoding.equals("Cp1252") ? PdfName.WIN_ANSI_ENCODING : PdfName.MAC_ROMAN_ENCODING); 605 else { 606 PdfDictionary enc = new PdfDictionary(PdfName.ENCODING); 607 PdfArray dif = new PdfArray(); 608 boolean gap = true; 609 for (int k = firstChar; k <= lastChar; ++k) { 610 if (shortTag[k] != 0) { 611 if (gap) { 612 dif.add(new PdfNumber(k)); 613 gap = false; 614 } 615 dif.add(new PdfName(differences[k])); 616 } 617 else 618 gap = true; 619 } 620 enc.put(PdfName.DIFFERENCES, dif); 621 dic.put(PdfName.ENCODING, enc); 622 } 623 } 624 if (specialMap != null || forceWidthsOutput || !(builtinFont && (fontSpecific || stdEncoding))) { 625 dic.put(PdfName.FIRSTCHAR, new PdfNumber(firstChar)); 626 dic.put(PdfName.LASTCHAR, new PdfNumber(lastChar)); 627 PdfArray wd = new PdfArray(); 628 for (int k = firstChar; k <= lastChar; ++k) { 629 if (shortTag[k] == 0) 630 wd.add(new PdfNumber(0)); 631 else 632 wd.add(new PdfNumber(widths[k])); 633 } 634 dic.put(PdfName.WIDTHS, wd); 635 } 636 if (!builtinFont && fontDescriptor != null) 637 dic.put(PdfName.FONTDESCRIPTOR, fontDescriptor); 638 return dic; 639 } 640 641 648 void writeFont(PdfWriter writer, PdfIndirectReference ref, Object params[]) throws DocumentException, IOException { 649 int firstChar = ((Integer )params[0]).intValue(); 650 int lastChar = ((Integer )params[1]).intValue(); 651 byte shortTag[] = (byte[])params[2]; 652 boolean subsetp = ((Boolean )params[3]).booleanValue() && subset; 653 if (!subsetp) { 654 firstChar = 0; 655 lastChar = shortTag.length - 1; 656 for (int k = 0; k < shortTag.length; ++k) 657 shortTag[k] = 1; 658 } 659 PdfIndirectReference ind_font = null; 660 PdfObject pobj = null; 661 PdfIndirectObject obj = null; 662 pobj = getFontStream(); 663 if (pobj != null){ 664 obj = writer.addToBody(pobj); 665 ind_font = obj.getIndirectReference(); 666 } 667 pobj = getFontDescriptor(ind_font); 668 if (pobj != null){ 669 obj = writer.addToBody(pobj); 670 ind_font = obj.getIndirectReference(); 671 } 672 pobj = getFontBaseType(ind_font, firstChar, lastChar, shortTag); 673 writer.addToBody(pobj, ref); 674 } 675 676 684 public float getFontDescriptor(int key, float fontSize) { 685 switch (key) { 686 case AWT_ASCENT: 687 case ASCENT: 688 return Ascender * fontSize / 1000; 689 case CAPHEIGHT: 690 return CapHeight * fontSize / 1000; 691 case AWT_DESCENT: 692 case DESCENT: 693 return Descender * fontSize / 1000; 694 case ITALICANGLE: 695 return ItalicAngle; 696 case BBOXLLX: 697 return llx * fontSize / 1000; 698 case BBOXLLY: 699 return lly * fontSize / 1000; 700 case BBOXURX: 701 return urx * fontSize / 1000; 702 case BBOXURY: 703 return ury * fontSize / 1000; 704 case AWT_LEADING: 705 return 0; 706 case AWT_MAXADVANCE: 707 return (urx - llx) * fontSize / 1000; 708 } 709 return 0; 710 } 711 712 715 public String getPostscriptFontName() { 716 return FontName; 717 } 718 719 727 public String [][] getFullFontName() { 728 return new String [][]{{"", "", "", FullName}}; 729 } 730 731 739 public String [][] getFamilyFontName() { 740 return new String [][]{{"", "", "", FamilyName}}; 741 } 742 743 746 public boolean hasKernPairs() { 747 return !KernPairs.isEmpty(); 748 } 749 750 755 public void setPostscriptFontName(String name) { 756 FontName = name; 757 } 758 759 766 public boolean setKerning(char char1, char char2, int kern) { 767 String first = GlyphList.unicodeToName((int)char1); 768 if (first == null) 769 return false; 770 String second = GlyphList.unicodeToName((int)char2); 771 if (second == null) 772 return false; 773 Object obj[] = (Object [])KernPairs.get(first); 774 if (obj == null) { 775 obj = new Object []{second, new Integer (kern)}; 776 KernPairs.put(first, obj); 777 return true; 778 } 779 for (int k = 0; k < obj.length; k += 2) { 780 if (second.equals(obj[k])) { 781 obj[k + 1] = new Integer (kern); 782 return true; 783 } 784 } 785 int size = obj.length; 786 Object obj2[] = new Object [size + 2]; 787 System.arraycopy(obj, 0, obj2, 0, size); 788 obj2[size] = second; 789 obj2[size + 1] = new Integer (kern); 790 KernPairs.put(first, obj2); 791 return true; 792 } 793 794 protected int[] getRawCharBBox(int c, String name) { 795 Object metrics[]; 796 if (name == null) { metrics = (Object [])CharMetrics.get(new Integer (c)); 798 } 799 else { 800 if (name.equals(".notdef")) 801 return null; 802 metrics = (Object [])CharMetrics.get(name); 803 } 804 if (metrics != null) 805 return ((int[])(metrics[3])); 806 return null; 807 } 808 809 } 810 | Popular Tags |