1 /* 2 * $Id$ 3 * 4 * Copyright 2001-2006 Paulo Soares 5 * 6 * The contents of this file are subject to the Mozilla Public License Version 1.1 7 * (the "License"); you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at http://www.mozilla.org/MPL/ 9 * 10 * Software distributed under the License is distributed on an "AS IS" basis, 11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 12 * for the specific language governing rights and limitations under the License. 13 * 14 * The Original Code is 'iText, a free JAVA-PDF library'. 15 * 16 * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by 17 * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie. 18 * All Rights Reserved. 19 * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer 20 * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved. 21 * 22 * Contributor(s): all the names of the contributors are added in the source code 23 * where applicable. 24 * 25 * Alternatively, the contents of this file may be used under the terms of the 26 * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the 27 * provisions of LGPL are applicable instead of those above. If you wish to 28 * allow use of your version of this file only under the terms of the LGPL 29 * License and not to allow others to use your version of this file under 30 * the MPL, indicate your decision by deleting the provisions above and 31 * replace them with the notice and other provisions required by the LGPL. 32 * If you do not delete the provisions above, a recipient may use your version 33 * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE. 34 * 35 * This library is free software; you can redistribute it and/or modify it 36 * under the terms of the MPL as stated above or under the terms of the GNU 37 * Library General Public License as published by the Free Software Foundation; 38 * either version 2 of the License, or any later version. 39 * 40 * This library is distributed in the hope that it will be useful, but WITHOUT 41 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 42 * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more 43 * details. 44 * 45 * If you didn't download this code from the following link, you should check if 46 * you aren't using an obsolete version: 47 * http://www.lowagie.com/iText/ 48 */ 49 50 package com.lowagie.text.pdf; 51 52 import java.io.ByteArrayOutputStream; 53 import java.io.IOException; 54 import java.io.InputStream; 55 import java.util.HashMap; 56 import java.util.StringTokenizer; 57 import com.lowagie.text.error_messages.MessageLocalization; 58 59 import com.lowagie.text.Document; 60 import com.lowagie.text.DocumentException; 61 import com.lowagie.text.pdf.fonts.FontsResourceAnchor; 62 63 /** Reads a Type1 font 64 * 65 * @author Paulo Soares (psoares@consiste.pt) 66 */ 67 class Type1Font extends BaseFont 68 { 69 private static FontsResourceAnchor resourceAnchor; 70 71 /** The PFB file if the input was made with a <CODE>byte</CODE> array. 72 */ 73 protected byte pfb[]; 74 /** The Postscript font name. 75 */ 76 private String FontName; 77 /** The full name of the font. 78 */ 79 private String FullName; 80 /** The family name of the font. 81 */ 82 private String FamilyName; 83 /** The weight of the font: normal, bold, etc. 84 */ 85 private String Weight = ""; 86 /** The italic angle of the font, usually 0.0 or negative. 87 */ 88 private float ItalicAngle = 0.0f; 89 /** <CODE>true</CODE> if all the characters have the same 90 * width. 91 */ 92 private boolean IsFixedPitch = false; 93 /** The character set of the font. 94 */ 95 private String CharacterSet; 96 /** The llx of the FontBox. 97 */ 98 private int llx = -50; 99 /** The lly of the FontBox. 100 */ 101 private int lly = -200; 102 /** The lurx of the FontBox. 103 */ 104 private int urx = 1000; 105 /** The ury of the FontBox. 106 */ 107 private int ury = 900; 108 /** The underline position. 109 */ 110 private int UnderlinePosition = -100; 111 /** The underline thickness. 112 */ 113 private int UnderlineThickness = 50; 114 /** The font's encoding name. This encoding is 'StandardEncoding' or 115 * 'AdobeStandardEncoding' for a font that can be totally encoded 116 * according to the characters names. For all other names the 117 * font is treated as symbolic. 118 */ 119 private String EncodingScheme = "FontSpecific"; 120 /** A variable. 121 */ 122 private int CapHeight = 700; 123 /** A variable. 124 */ 125 private int XHeight = 480; 126 /** A variable. 127 */ 128 private int Ascender = 800; 129 /** A variable. 130 */ 131 private int Descender = -200; 132 /** A variable. 133 */ 134 private int StdHW; 135 /** A variable. 136 */ 137 private int StdVW = 80; 138 139 /** Represents the section CharMetrics in the AFM file. Each 140 * value of this array contains a <CODE>Object[4]</CODE> with an 141 * Integer, Integer, String and int[]. This is the code, width, name and char bbox. 142 * The key is the name of the char and also an Integer with the char number. 143 */ 144 private HashMap CharMetrics = new HashMap(); 145 /** Represents the section KernPairs in the AFM file. The key is 146 * the name of the first character and the value is a <CODE>Object[]</CODE> 147 * with 2 elements for each kern pair. Position 0 is the name of 148 * the second character and position 1 is the kerning distance. This is 149 * repeated for all the pairs. 150 */ 151 private HashMap KernPairs = new HashMap(); 152 /** The file in use. 153 */ 154 private String fileName; 155 /** <CODE>true</CODE> if this font is one of the 14 built in fonts. 156 */ 157 private boolean builtinFont = false; 158 /** Types of records in a PFB file. ASCII is 1 and BINARY is 2. 159 * They have to appear in the PFB file in this sequence. 160 */ 161 private static final int PFB_TYPES[] = {1, 2, 1}; 162 163 /** Creates a new Type1 font. 164 * @param ttfAfm the AFM file if the input is made with a <CODE>byte</CODE> array 165 * @param pfb the PFB file if the input is made with a <CODE>byte</CODE> array 166 * @param afmFile the name of one of the 14 built-in fonts or the location of an AFM file. The file must end in '.afm' 167 * @param enc the encoding to be applied to this font 168 * @param emb true if the font is to be embedded in the PDF 169 * @throws DocumentException the AFM file is invalid 170 * @throws IOException the AFM file could not be read 171 * @since 2.1.5 172 */ Type1Font(String afmFile, String enc, boolean emb, byte ttfAfm[], byte pfb[], boolean forceRead)173 Type1Font(String afmFile, String enc, boolean emb, byte ttfAfm[], byte pfb[], boolean forceRead) 174 throws DocumentException, IOException { 175 if (emb && ttfAfm != null && pfb == null) 176 throw new DocumentException(MessageLocalization.getComposedMessage("two.byte.arrays.are.needed.if.the.type1.font.is.embedded")); 177 if (emb && ttfAfm != null) 178 this.pfb = pfb; 179 encoding = enc; 180 embedded = emb; 181 fileName = afmFile; 182 fontType = FONT_TYPE_T1; 183 RandomAccessFileOrArray rf = null; 184 InputStream is = null; 185 if (BuiltinFonts14.containsKey(afmFile)) { 186 embedded = false; 187 builtinFont = true; 188 byte buf[] = new byte[1024]; 189 try { 190 if (resourceAnchor == null) 191 resourceAnchor = new FontsResourceAnchor(); 192 is = getResourceStream(RESOURCE_PATH + afmFile + ".afm", resourceAnchor.getClass().getClassLoader()); 193 if (is == null) { 194 String msg = MessageLocalization.getComposedMessage("1.not.found.as.resource", afmFile); 195 System.err.println(msg); 196 throw new DocumentException(msg); 197 } 198 ByteArrayOutputStream out = new ByteArrayOutputStream(); 199 while (true) { 200 int size = is.read(buf); 201 if (size < 0) 202 break; 203 out.write(buf, 0, size); 204 } 205 buf = out.toByteArray(); 206 } 207 finally { 208 if (is != null) { 209 try { 210 is.close(); 211 } 212 catch (Exception e) { 213 // empty on purpose 214 } 215 } 216 } 217 try { 218 rf = new RandomAccessFileOrArray(buf); 219 process(rf); 220 } 221 finally { 222 if (rf != null) { 223 try { 224 rf.close(); 225 } 226 catch (Exception e) { 227 // empty on purpose 228 } 229 } 230 } 231 } 232 else if (afmFile.toLowerCase().endsWith(".afm")) { 233 try { 234 if (ttfAfm == null) 235 rf = new RandomAccessFileOrArray(afmFile, forceRead, Document.plainRandomAccess); 236 else 237 rf = new RandomAccessFileOrArray(ttfAfm); 238 process(rf); 239 } 240 finally { 241 if (rf != null) { 242 try { 243 rf.close(); 244 } 245 catch (Exception e) { 246 // empty on purpose 247 } 248 } 249 } 250 } 251 else if (afmFile.toLowerCase().endsWith(".pfm")) { 252 try { 253 ByteArrayOutputStream ba = new ByteArrayOutputStream(); 254 if (ttfAfm == null) 255 rf = new RandomAccessFileOrArray(afmFile, forceRead, Document.plainRandomAccess); 256 else 257 rf = new RandomAccessFileOrArray(ttfAfm); 258 Pfm2afm.convert(rf, ba); 259 rf.close(); 260 rf = new RandomAccessFileOrArray(ba.toByteArray()); 261 process(rf); 262 } 263 finally { 264 if (rf != null) { 265 try { 266 rf.close(); 267 } 268 catch (Exception e) { 269 // empty on purpose 270 } 271 } 272 } 273 } 274 else 275 throw new DocumentException(MessageLocalization.getComposedMessage("1.is.not.an.afm.or.pfm.font.file", afmFile)); 276 277 EncodingScheme = EncodingScheme.trim(); 278 if (EncodingScheme.equals("AdobeStandardEncoding") || EncodingScheme.equals("StandardEncoding")) { 279 fontSpecific = false; 280 } 281 if (!encoding.startsWith("#")) 282 PdfEncodings.convertToBytes(" ", enc); // check if the encoding exists 283 createEncoding(); 284 } 285 286 /** Gets the width from the font according to the <CODE>name</CODE> or, 287 * if the <CODE>name</CODE> is null, meaning it is a symbolic font, 288 * the char <CODE>c</CODE>. 289 * @param c the char if the font is symbolic 290 * @param name the glyph name 291 * @return the width of the char 292 */ getRawWidth(int c, String name)293 int getRawWidth(int c, String name) { 294 Object metrics[]; 295 if (name == null) { // font specific 296 metrics = (Object[])CharMetrics.get(new Integer(c)); 297 } 298 else { 299 if (name.equals(".notdef")) 300 return 0; 301 metrics = (Object[])CharMetrics.get(name); 302 } 303 if (metrics != null) 304 return ((Integer)(metrics[1])).intValue(); 305 return 0; 306 } 307 308 /** Gets the kerning between two Unicode characters. The characters 309 * are converted to names and this names are used to find the kerning 310 * pairs in the <CODE>HashMap</CODE> <CODE>KernPairs</CODE>. 311 * @param char1 the first char 312 * @param char2 the second char 313 * @return the kerning to be applied 314 */ getKerning(int char1, int char2)315 public int getKerning(int char1, int char2) 316 { 317 String first = GlyphList.unicodeToName(char1); 318 if (first == null) 319 return 0; 320 String second = GlyphList.unicodeToName(char2); 321 if (second == null) 322 return 0; 323 Object obj[] = (Object[])KernPairs.get(first); 324 if (obj == null) 325 return 0; 326 for (int k = 0; k < obj.length; k += 2) { 327 if (second.equals(obj[k])) 328 return ((Integer)obj[k + 1]).intValue(); 329 } 330 return 0; 331 } 332 333 334 /** Reads the font metrics 335 * @param rf the AFM file 336 * @throws DocumentException the AFM file is invalid 337 * @throws IOException the AFM file could not be read 338 */ process(RandomAccessFileOrArray rf)339 public void process(RandomAccessFileOrArray rf) throws DocumentException, IOException 340 { 341 String line; 342 boolean isMetrics = false; 343 while ((line = rf.readLine()) != null) 344 { 345 StringTokenizer tok = new StringTokenizer(line, " ,\n\r\t\f"); 346 if (!tok.hasMoreTokens()) 347 continue; 348 String ident = tok.nextToken(); 349 if (ident.equals("FontName")) 350 FontName = tok.nextToken("\u00ff").substring(1); 351 else if (ident.equals("FullName")) 352 FullName = tok.nextToken("\u00ff").substring(1); 353 else if (ident.equals("FamilyName")) 354 FamilyName = tok.nextToken("\u00ff").substring(1); 355 else if (ident.equals("Weight")) 356 Weight = tok.nextToken("\u00ff").substring(1); 357 else if (ident.equals("ItalicAngle")) 358 ItalicAngle = Float.parseFloat(tok.nextToken()); 359 else if (ident.equals("IsFixedPitch")) 360 IsFixedPitch = tok.nextToken().equals("true"); 361 else if (ident.equals("CharacterSet")) 362 CharacterSet = tok.nextToken("\u00ff").substring(1); 363 else if (ident.equals("FontBBox")) 364 { 365 llx = (int)Float.parseFloat(tok.nextToken()); 366 lly = (int)Float.parseFloat(tok.nextToken()); 367 urx = (int)Float.parseFloat(tok.nextToken()); 368 ury = (int)Float.parseFloat(tok.nextToken()); 369 } 370 else if (ident.equals("UnderlinePosition")) 371 UnderlinePosition = (int)Float.parseFloat(tok.nextToken()); 372 else if (ident.equals("UnderlineThickness")) 373 UnderlineThickness = (int)Float.parseFloat(tok.nextToken()); 374 else if (ident.equals("EncodingScheme")) 375 EncodingScheme = tok.nextToken("\u00ff").substring(1); 376 else if (ident.equals("CapHeight")) 377 CapHeight = (int)Float.parseFloat(tok.nextToken()); 378 else if (ident.equals("XHeight")) 379 XHeight = (int)Float.parseFloat(tok.nextToken()); 380 else if (ident.equals("Ascender")) 381 Ascender = (int)Float.parseFloat(tok.nextToken()); 382 else if (ident.equals("Descender")) 383 Descender = (int)Float.parseFloat(tok.nextToken()); 384 else if (ident.equals("StdHW")) 385 StdHW = (int)Float.parseFloat(tok.nextToken()); 386 else if (ident.equals("StdVW")) 387 StdVW = (int)Float.parseFloat(tok.nextToken()); 388 else if (ident.equals("StartCharMetrics")) 389 { 390 isMetrics = true; 391 break; 392 } 393 } 394 if (!isMetrics) 395 throw new DocumentException(MessageLocalization.getComposedMessage("missing.startcharmetrics.in.1", fileName)); 396 while ((line = rf.readLine()) != null) 397 { 398 StringTokenizer tok = new StringTokenizer(line); 399 if (!tok.hasMoreTokens()) 400 continue; 401 String ident = tok.nextToken(); 402 if (ident.equals("EndCharMetrics")) 403 { 404 isMetrics = false; 405 break; 406 } 407 Integer C = new Integer(-1); 408 Integer WX = new Integer(250); 409 String N = ""; 410 int B[] = null; 411 412 tok = new StringTokenizer(line, ";"); 413 while (tok.hasMoreTokens()) 414 { 415 StringTokenizer tokc = new StringTokenizer(tok.nextToken()); 416 if (!tokc.hasMoreTokens()) 417 continue; 418 ident = tokc.nextToken(); 419 if (ident.equals("C")) 420 C = Integer.valueOf(tokc.nextToken()); 421 else if (ident.equals("WX")) 422 WX = new Integer((int)Float.parseFloat(tokc.nextToken())); 423 else if (ident.equals("N")) 424 N = tokc.nextToken(); 425 else if (ident.equals("B")) { 426 B = new int[]{Integer.parseInt(tokc.nextToken()), 427 Integer.parseInt(tokc.nextToken()), 428 Integer.parseInt(tokc.nextToken()), 429 Integer.parseInt(tokc.nextToken())}; 430 } 431 } 432 Object metrics[] = new Object[]{C, WX, N, B}; 433 if (C.intValue() >= 0) 434 CharMetrics.put(C, metrics); 435 CharMetrics.put(N, metrics); 436 } 437 if (isMetrics) 438 throw new DocumentException(MessageLocalization.getComposedMessage("missing.endcharmetrics.in.1", fileName)); 439 if (!CharMetrics.containsKey("nonbreakingspace")) { 440 Object[] space = (Object[])CharMetrics.get("space"); 441 if (space != null) 442 CharMetrics.put("nonbreakingspace", space); 443 } 444 while ((line = rf.readLine()) != null) 445 { 446 StringTokenizer tok = new StringTokenizer(line); 447 if (!tok.hasMoreTokens()) 448 continue; 449 String ident = tok.nextToken(); 450 if (ident.equals("EndFontMetrics")) 451 return; 452 if (ident.equals("StartKernPairs")) 453 { 454 isMetrics = true; 455 break; 456 } 457 } 458 if (!isMetrics) 459 throw new DocumentException(MessageLocalization.getComposedMessage("missing.endfontmetrics.in.1", fileName)); 460 while ((line = rf.readLine()) != null) 461 { 462 StringTokenizer tok = new StringTokenizer(line); 463 if (!tok.hasMoreTokens()) 464 continue; 465 String ident = tok.nextToken(); 466 if (ident.equals("KPX")) 467 { 468 String first = tok.nextToken(); 469 String second = tok.nextToken(); 470 Integer width = new Integer((int)Float.parseFloat(tok.nextToken())); 471 Object relates[] = (Object[])KernPairs.get(first); 472 if (relates == null) 473 KernPairs.put(first, new Object[]{second, width}); 474 else 475 { 476 int n = relates.length; 477 Object relates2[] = new Object[n + 2]; 478 System.arraycopy(relates, 0, relates2, 0, n); 479 relates2[n] = second; 480 relates2[n + 1] = width; 481 KernPairs.put(first, relates2); 482 } 483 } 484 else if (ident.equals("EndKernPairs")) 485 { 486 isMetrics = false; 487 break; 488 } 489 } 490 if (isMetrics) 491 throw new DocumentException(MessageLocalization.getComposedMessage("missing.endkernpairs.in.1", fileName)); 492 rf.close(); 493 } 494 495 /** If the embedded flag is <CODE>false</CODE> or if the font is 496 * one of the 14 built in types, it returns <CODE>null</CODE>, 497 * otherwise the font is read and output in a PdfStream object. 498 * @return the PdfStream containing the font or <CODE>null</CODE> 499 * @throws DocumentException if there is an error reading the font 500 * @since 2.1.3 501 */ getFullFontStream()502 public PdfStream getFullFontStream() throws DocumentException 503 { 504 if (builtinFont || !embedded) 505 return null; 506 RandomAccessFileOrArray rf = null; 507 try { 508 String filePfb = fileName.substring(0, fileName.length() - 3) + "pfb"; 509 if (pfb == null) 510 rf = new RandomAccessFileOrArray(filePfb, true, Document.plainRandomAccess); 511 else 512 rf = new RandomAccessFileOrArray(pfb); 513 int fileLength = rf.length(); 514 byte st[] = new byte[fileLength - 18]; 515 int lengths[] = new int[3]; 516 int bytePtr = 0; 517 for (int k = 0; k < 3; ++k) { 518 if (rf.read() != 0x80) 519 throw new DocumentException(MessageLocalization.getComposedMessage("start.marker.missing.in.1", filePfb)); 520 if (rf.read() != PFB_TYPES[k]) 521 throw new DocumentException(MessageLocalization.getComposedMessage("incorrect.segment.type.in.1", filePfb)); 522 int size = rf.read(); 523 size += rf.read() << 8; 524 size += rf.read() << 16; 525 size += rf.read() << 24; 526 lengths[k] = size; 527 while (size != 0) { 528 int got = rf.read(st, bytePtr, size); 529 if (got < 0) 530 throw new DocumentException(MessageLocalization.getComposedMessage("premature.end.in.1", filePfb)); 531 bytePtr += got; 532 size -= got; 533 } 534 } 535 return new StreamFont(st, lengths, compressionLevel); 536 } 537 catch (Exception e) { 538 throw new DocumentException(e); 539 } 540 finally { 541 if (rf != null) { 542 try { 543 rf.close(); 544 } 545 catch (Exception e) { 546 // empty on purpose 547 } 548 } 549 } 550 } 551 552 /** Generates the font descriptor for this font or <CODE>null</CODE> if it is 553 * one of the 14 built in fonts. 554 * @param fontStream the indirect reference to a PdfStream containing the font or <CODE>null</CODE> 555 * @return the PdfDictionary containing the font descriptor or <CODE>null</CODE> 556 */ getFontDescriptor(PdfIndirectReference fontStream)557 private PdfDictionary getFontDescriptor(PdfIndirectReference fontStream) 558 { 559 if (builtinFont) 560 return null; 561 PdfDictionary dic = new PdfDictionary(PdfName.FONTDESCRIPTOR); 562 dic.put(PdfName.ASCENT, new PdfNumber(Ascender)); 563 dic.put(PdfName.CAPHEIGHT, new PdfNumber(CapHeight)); 564 dic.put(PdfName.DESCENT, new PdfNumber(Descender)); 565 dic.put(PdfName.FONTBBOX, new PdfRectangle(llx, lly, urx, ury)); 566 dic.put(PdfName.FONTNAME, new PdfName(FontName)); 567 dic.put(PdfName.ITALICANGLE, new PdfNumber(ItalicAngle)); 568 dic.put(PdfName.STEMV, new PdfNumber(StdVW)); 569 if (fontStream != null) 570 dic.put(PdfName.FONTFILE, fontStream); 571 int flags = 0; 572 if (IsFixedPitch) 573 flags |= 1; 574 flags |= fontSpecific ? 4 : 32; 575 if (ItalicAngle < 0) 576 flags |= 64; 577 if (FontName.indexOf("Caps") >= 0 || FontName.endsWith("SC")) 578 flags |= 131072; 579 if (Weight.equals("Bold")) 580 flags |= 262144; 581 dic.put(PdfName.FLAGS, new PdfNumber(flags)); 582 583 return dic; 584 } 585 586 /** Generates the font dictionary for this font. 587 * @return the PdfDictionary containing the font dictionary 588 * @param firstChar the first valid character 589 * @param lastChar the last valid character 590 * @param shortTag a 256 bytes long <CODE>byte</CODE> array where each unused byte is represented by 0 591 * @param fontDescriptor the indirect reference to a PdfDictionary containing the font descriptor or <CODE>null</CODE> 592 */ getFontBaseType(PdfIndirectReference fontDescriptor, int firstChar, int lastChar, byte shortTag[])593 private PdfDictionary getFontBaseType(PdfIndirectReference fontDescriptor, int firstChar, int lastChar, byte shortTag[]) 594 { 595 PdfDictionary dic = new PdfDictionary(PdfName.FONT); 596 dic.put(PdfName.SUBTYPE, PdfName.TYPE1); 597 dic.put(PdfName.BASEFONT, new PdfName(FontName)); 598 boolean stdEncoding = encoding.equals("Cp1252") || encoding.equals("MacRoman"); 599 if (!fontSpecific || specialMap != null) { 600 for (int k = firstChar; k <= lastChar; ++k) { 601 if (!differences[k].equals(notdef)) { 602 firstChar = k; 603 break; 604 } 605 } 606 if (stdEncoding) 607 dic.put(PdfName.ENCODING, encoding.equals("Cp1252") ? PdfName.WIN_ANSI_ENCODING : PdfName.MAC_ROMAN_ENCODING); 608 else { 609 PdfDictionary enc = new PdfDictionary(PdfName.ENCODING); 610 PdfArray dif = new PdfArray(); 611 boolean gap = true; 612 for (int k = firstChar; k <= lastChar; ++k) { 613 if (shortTag[k] != 0) { 614 if (gap) { 615 dif.add(new PdfNumber(k)); 616 gap = false; 617 } 618 dif.add(new PdfName(differences[k])); 619 } 620 else 621 gap = true; 622 } 623 enc.put(PdfName.DIFFERENCES, dif); 624 dic.put(PdfName.ENCODING, enc); 625 } 626 } 627 if (specialMap != null || forceWidthsOutput || !(builtinFont && (fontSpecific || stdEncoding))) { 628 dic.put(PdfName.FIRSTCHAR, new PdfNumber(firstChar)); 629 dic.put(PdfName.LASTCHAR, new PdfNumber(lastChar)); 630 PdfArray wd = new PdfArray(); 631 for (int k = firstChar; k <= lastChar; ++k) { 632 if (shortTag[k] == 0) 633 wd.add(new PdfNumber(0)); 634 else 635 wd.add(new PdfNumber(widths[k])); 636 } 637 dic.put(PdfName.WIDTHS, wd); 638 } 639 if (!builtinFont && fontDescriptor != null) 640 dic.put(PdfName.FONTDESCRIPTOR, fontDescriptor); 641 return dic; 642 } 643 644 /** Outputs to the writer the font dictionaries and streams. 645 * @param writer the writer for this document 646 * @param ref the font indirect reference 647 * @param params several parameters that depend on the font type 648 * @throws IOException on error 649 * @throws DocumentException error in generating the object 650 */ writeFont(PdfWriter writer, PdfIndirectReference ref, Object params[])651 void writeFont(PdfWriter writer, PdfIndirectReference ref, Object params[]) throws DocumentException, IOException { 652 int firstChar = ((Integer)params[0]).intValue(); 653 int lastChar = ((Integer)params[1]).intValue(); 654 byte shortTag[] = (byte[])params[2]; 655 boolean subsetp = ((Boolean)params[3]).booleanValue() && subset; 656 if (!subsetp) { 657 firstChar = 0; 658 lastChar = shortTag.length - 1; 659 for (int k = 0; k < shortTag.length; ++k) 660 shortTag[k] = 1; 661 } 662 PdfIndirectReference ind_font = null; 663 PdfObject pobj = null; 664 PdfIndirectObject obj = null; 665 pobj = getFullFontStream(); 666 if (pobj != null){ 667 obj = writer.addToBody(pobj); 668 ind_font = obj.getIndirectReference(); 669 } 670 pobj = getFontDescriptor(ind_font); 671 if (pobj != null){ 672 obj = writer.addToBody(pobj); 673 ind_font = obj.getIndirectReference(); 674 } 675 pobj = getFontBaseType(ind_font, firstChar, lastChar, shortTag); 676 writer.addToBody(pobj, ref); 677 } 678 679 /** Gets the font parameter identified by <CODE>key</CODE>. Valid values 680 * for <CODE>key</CODE> are <CODE>ASCENT</CODE>, <CODE>CAPHEIGHT</CODE>, <CODE>DESCENT</CODE>, 681 * <CODE>ITALICANGLE</CODE>, <CODE>BBOXLLX</CODE>, <CODE>BBOXLLY</CODE>, <CODE>BBOXURX</CODE> 682 * and <CODE>BBOXURY</CODE>. 683 * @param key the parameter to be extracted 684 * @param fontSize the font size in points 685 * @return the parameter in points 686 */ getFontDescriptor(int key, float fontSize)687 public float getFontDescriptor(int key, float fontSize) { 688 switch (key) { 689 case AWT_ASCENT: 690 case ASCENT: 691 return Ascender * fontSize / 1000; 692 case CAPHEIGHT: 693 return CapHeight * fontSize / 1000; 694 case AWT_DESCENT: 695 case DESCENT: 696 return Descender * fontSize / 1000; 697 case ITALICANGLE: 698 return ItalicAngle; 699 case BBOXLLX: 700 return llx * fontSize / 1000; 701 case BBOXLLY: 702 return lly * fontSize / 1000; 703 case BBOXURX: 704 return urx * fontSize / 1000; 705 case BBOXURY: 706 return ury * fontSize / 1000; 707 case AWT_LEADING: 708 return 0; 709 case AWT_MAXADVANCE: 710 return (urx - llx) * fontSize / 1000; 711 case UNDERLINE_POSITION: 712 return UnderlinePosition * fontSize / 1000; 713 case UNDERLINE_THICKNESS: 714 return UnderlineThickness * fontSize / 1000; 715 } 716 return 0; 717 } 718 719 /** Gets the postscript font name. 720 * @return the postscript font name 721 */ getPostscriptFontName()722 public String getPostscriptFontName() { 723 return FontName; 724 } 725 726 /** Gets the full name of the font. If it is a True Type font 727 * each array element will have {Platform ID, Platform Encoding ID, 728 * Language ID, font name}. The interpretation of this values can be 729 * found in the Open Type specification, chapter 2, in the 'name' table.<br> 730 * For the other fonts the array has a single element with {"", "", "", 731 * font name}. 732 * @return the full name of the font 733 */ getFullFontName()734 public String[][] getFullFontName() { 735 return new String[][]{{"", "", "", FullName}}; 736 } 737 738 /** Gets all the entries of the names-table. If it is a True Type font 739 * each array element will have {Name ID, Platform ID, Platform Encoding ID, 740 * Language ID, font name}. The interpretation of this values can be 741 * found in the Open Type specification, chapter 2, in the 'name' table.<br> 742 * For the other fonts the array has a single element with {"4", "", "", "", 743 * font name}. 744 * @return the full name of the font 745 */ getAllNameEntries()746 public String[][] getAllNameEntries() { 747 return new String[][]{{"4", "", "", "", FullName}}; 748 } 749 750 /** Gets the family name of the font. If it is a True Type font 751 * each array element will have {Platform ID, Platform Encoding ID, 752 * Language ID, font name}. The interpretation of this values can be 753 * found in the Open Type specification, chapter 2, in the 'name' table.<br> 754 * For the other fonts the array has a single element with {"", "", "", 755 * font name}. 756 * @return the family name of the font 757 */ getFamilyFontName()758 public String[][] getFamilyFontName() { 759 return new String[][]{{"", "", "", FamilyName}}; 760 } 761 762 /** Checks if the font has any kerning pairs. 763 * @return <CODE>true</CODE> if the font has any kerning pairs 764 */ hasKernPairs()765 public boolean hasKernPairs() { 766 return !KernPairs.isEmpty(); 767 } 768 769 /** 770 * Sets the font name that will appear in the pdf font dictionary. 771 * Use with care as it can easily make a font unreadable if not embedded. 772 * @param name the new font name 773 */ setPostscriptFontName(String name)774 public void setPostscriptFontName(String name) { 775 FontName = name; 776 } 777 778 /** 779 * Sets the kerning between two Unicode chars. 780 * @param char1 the first char 781 * @param char2 the second char 782 * @param kern the kerning to apply in normalized 1000 units 783 * @return <code>true</code> if the kerning was applied, <code>false</code> otherwise 784 */ setKerning(int char1, int char2, int kern)785 public boolean setKerning(int char1, int char2, int kern) { 786 String first = GlyphList.unicodeToName(char1); 787 if (first == null) 788 return false; 789 String second = GlyphList.unicodeToName(char2); 790 if (second == null) 791 return false; 792 Object obj[] = (Object[])KernPairs.get(first); 793 if (obj == null) { 794 obj = new Object[]{second, new Integer(kern)}; 795 KernPairs.put(first, obj); 796 return true; 797 } 798 for (int k = 0; k < obj.length; k += 2) { 799 if (second.equals(obj[k])) { 800 obj[k + 1] = new Integer(kern); 801 return true; 802 } 803 } 804 int size = obj.length; 805 Object obj2[] = new Object[size + 2]; 806 System.arraycopy(obj, 0, obj2, 0, size); 807 obj2[size] = second; 808 obj2[size + 1] = new Integer(kern); 809 KernPairs.put(first, obj2); 810 return true; 811 } 812 getRawCharBBox(int c, String name)813 protected int[] getRawCharBBox(int c, String name) { 814 Object metrics[]; 815 if (name == null) { // font specific 816 metrics = (Object[])CharMetrics.get(new Integer(c)); 817 } 818 else { 819 if (name.equals(".notdef")) 820 return null; 821 metrics = (Object[])CharMetrics.get(name); 822 } 823 if (metrics != null) 824 return ((int[])(metrics[3])); 825 return null; 826 } 827 828 } 829