001/* 002 * $Id: Type1Font.java 4784 2011-03-15 08:33:00Z blowagie $ 003 * 004 * This file is part of the iText (R) project. 005 * Copyright (c) 1998-2011 1T3XT BVBA 006 * Authors: Bruno Lowagie, Paulo Soares, et al. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU Affero General Public License version 3 010 * as published by the Free Software Foundation with the addition of the 011 * following permission added to Section 15 as permitted in Section 7(a): 012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT, 013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. 014 * 015 * This program is distributed in the hope that it will be useful, but 016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 017 * or FITNESS FOR A PARTICULAR PURPOSE. 018 * See the GNU Affero General Public License for more details. 019 * You should have received a copy of the GNU Affero General Public License 020 * along with this program; if not, see http://www.gnu.org/licenses or write to 021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 022 * Boston, MA, 02110-1301 USA, or download the license from the following URL: 023 * http://itextpdf.com/terms-of-use/ 024 * 025 * The interactive user interfaces in modified source and object code versions 026 * of this program must display Appropriate Legal Notices, as required under 027 * Section 5 of the GNU Affero General Public License. 028 * 029 * In accordance with Section 7(b) of the GNU Affero General Public License, 030 * a covered work must retain the producer line in every PDF that is created 031 * or manipulated using iText. 032 * 033 * You can be released from the requirements of the license by purchasing 034 * a commercial license. Buying such a license is mandatory as soon as you 035 * develop commercial activities involving the iText software without 036 * disclosing the source code of your own applications. 037 * These activities include: offering paid services to customers as an ASP, 038 * serving PDFs on the fly in a web application, shipping iText with a closed 039 * source product. 040 * 041 * For more information, please contact iText Software Corp. at this 042 * address: sales@itextpdf.com 043 */ 044package com.itextpdf.text.pdf; 045 046import java.io.ByteArrayOutputStream; 047import java.io.IOException; 048import java.io.InputStream; 049import java.util.HashMap; 050import java.util.StringTokenizer; 051 052import com.itextpdf.text.Document; 053import com.itextpdf.text.DocumentException; 054import com.itextpdf.text.error_messages.MessageLocalization; 055import com.itextpdf.text.pdf.fonts.FontsResourceAnchor; 056 057/** Reads a Type1 font 058 * 059 * @author Paulo Soares 060 */ 061class Type1Font extends BaseFont 062{ 063 private static FontsResourceAnchor resourceAnchor; 064 065 /** The PFB file if the input was made with a <CODE>byte</CODE> array. 066 */ 067 protected byte pfb[]; 068/** The Postscript font name. 069 */ 070 private String FontName; 071/** The full name of the font. 072 */ 073 private String FullName; 074/** The family name of the font. 075 */ 076 private String FamilyName; 077/** The weight of the font: normal, bold, etc. 078 */ 079 private String Weight = ""; 080/** The italic angle of the font, usually 0.0 or negative. 081 */ 082 private float ItalicAngle = 0.0f; 083/** <CODE>true</CODE> if all the characters have the same 084 * width. 085 */ 086 private boolean IsFixedPitch = false; 087/** The character set of the font. 088 */ 089 private String CharacterSet; 090/** The llx of the FontBox. 091 */ 092 private int llx = -50; 093/** The lly of the FontBox. 094 */ 095 private int lly = -200; 096/** The lurx of the FontBox. 097 */ 098 private int urx = 1000; 099/** The ury of the FontBox. 100 */ 101 private int ury = 900; 102/** The underline position. 103 */ 104 private int UnderlinePosition = -100; 105/** The underline thickness. 106 */ 107 private int UnderlineThickness = 50; 108/** The font's encoding name. This encoding is 'StandardEncoding' or 109 * 'AdobeStandardEncoding' for a font that can be totally encoded 110 * according to the characters names. For all other names the 111 * font is treated as symbolic. 112 */ 113 private String EncodingScheme = "FontSpecific"; 114/** A variable. 115 */ 116 private int CapHeight = 700; 117/** A variable. 118 */ 119 private int XHeight = 480; 120/** A variable. 121 */ 122 private int Ascender = 800; 123/** A variable. 124 */ 125 private int Descender = -200; 126/** A variable. 127 */ 128 private int StdHW; 129/** A variable. 130 */ 131 private int StdVW = 80; 132 133/** Represents the section CharMetrics in the AFM file. Each 134 * value of this array contains a <CODE>Object[4]</CODE> with an 135 * Integer, Integer, String and int[]. This is the code, width, name and char bbox. 136 * The key is the name of the char and also an Integer with the char number. 137 */ 138 private HashMap<Object, Object[]> CharMetrics = new HashMap<Object, Object[]>(); 139/** Represents the section KernPairs in the AFM file. The key is 140 * the name of the first character and the value is a <CODE>Object[]</CODE> 141 * with 2 elements for each kern pair. Position 0 is the name of 142 * the second character and position 1 is the kerning distance. This is 143 * repeated for all the pairs. 144 */ 145 private HashMap<String, Object[]> KernPairs = new HashMap<String, Object[]>(); 146/** The file in use. 147 */ 148 private String fileName; 149/** <CODE>true</CODE> if this font is one of the 14 built in fonts. 150 */ 151 private boolean builtinFont = false; 152/** Types of records in a PFB file. ASCII is 1 and BINARY is 2. 153 * They have to appear in the PFB file in this sequence. 154 */ 155 private static final int PFB_TYPES[] = {1, 2, 1}; 156 157 /** Creates a new Type1 font. 158 * @param ttfAfm the AFM file if the input is made with a <CODE>byte</CODE> array 159 * @param pfb the PFB file if the input is made with a <CODE>byte</CODE> array 160 * @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' 161 * @param enc the encoding to be applied to this font 162 * @param emb true if the font is to be embedded in the PDF 163 * @throws DocumentException the AFM file is invalid 164 * @throws IOException the AFM file could not be read 165 * @since 2.1.5 166 */ 167 Type1Font(String afmFile, String enc, boolean emb, byte ttfAfm[], byte pfb[], boolean forceRead) 168 throws DocumentException, IOException { 169 if (emb && ttfAfm != null && pfb == null) 170 throw new DocumentException(MessageLocalization.getComposedMessage("two.byte.arrays.are.needed.if.the.type1.font.is.embedded")); 171 if (emb && ttfAfm != null) 172 this.pfb = pfb; 173 encoding = enc; 174 embedded = emb; 175 fileName = afmFile; 176 fontType = FONT_TYPE_T1; 177 RandomAccessFileOrArray rf = null; 178 InputStream is = null; 179 if (BuiltinFonts14.containsKey(afmFile)) { 180 embedded = false; 181 builtinFont = true; 182 byte buf[] = new byte[1024]; 183 try { 184 if (resourceAnchor == null) 185 resourceAnchor = new FontsResourceAnchor(); 186 is = getResourceStream(RESOURCE_PATH + afmFile + ".afm", resourceAnchor.getClass().getClassLoader()); 187 if (is == null) { 188 String msg = MessageLocalization.getComposedMessage("1.not.found.as.resource", afmFile); 189 System.err.println(msg); 190 throw new DocumentException(msg); 191 } 192 ByteArrayOutputStream out = new ByteArrayOutputStream(); 193 while (true) { 194 int size = is.read(buf); 195 if (size < 0) 196 break; 197 out.write(buf, 0, size); 198 } 199 buf = out.toByteArray(); 200 } 201 finally { 202 if (is != null) { 203 try { 204 is.close(); 205 } 206 catch (Exception e) { 207 // empty on purpose 208 } 209 } 210 } 211 try { 212 rf = new RandomAccessFileOrArray(buf); 213 process(rf); 214 } 215 finally { 216 if (rf != null) { 217 try { 218 rf.close(); 219 } 220 catch (Exception e) { 221 // empty on purpose 222 } 223 } 224 } 225 } 226 else if (afmFile.toLowerCase().endsWith(".afm")) { 227 try { 228 if (ttfAfm == null) 229 rf = new RandomAccessFileOrArray(afmFile, forceRead, Document.plainRandomAccess); 230 else 231 rf = new RandomAccessFileOrArray(ttfAfm); 232 process(rf); 233 } 234 finally { 235 if (rf != null) { 236 try { 237 rf.close(); 238 } 239 catch (Exception e) { 240 // empty on purpose 241 } 242 } 243 } 244 } 245 else if (afmFile.toLowerCase().endsWith(".pfm")) { 246 try { 247 ByteArrayOutputStream ba = new ByteArrayOutputStream(); 248 if (ttfAfm == null) 249 rf = new RandomAccessFileOrArray(afmFile, forceRead, Document.plainRandomAccess); 250 else 251 rf = new RandomAccessFileOrArray(ttfAfm); 252 Pfm2afm.convert(rf, ba); 253 rf.close(); 254 rf = new RandomAccessFileOrArray(ba.toByteArray()); 255 process(rf); 256 } 257 finally { 258 if (rf != null) { 259 try { 260 rf.close(); 261 } 262 catch (Exception e) { 263 // empty on purpose 264 } 265 } 266 } 267 } 268 else 269 throw new DocumentException(MessageLocalization.getComposedMessage("1.is.not.an.afm.or.pfm.font.file", afmFile)); 270 271 EncodingScheme = EncodingScheme.trim(); 272 if (EncodingScheme.equals("AdobeStandardEncoding") || EncodingScheme.equals("StandardEncoding")) { 273 fontSpecific = false; 274 } 275 if (!encoding.startsWith("#")) 276 PdfEncodings.convertToBytes(" ", enc); // check if the encoding exists 277 createEncoding(); 278 } 279 280/** Gets the width from the font according to the <CODE>name</CODE> or, 281 * if the <CODE>name</CODE> is null, meaning it is a symbolic font, 282 * the char <CODE>c</CODE>. 283 * @param c the char if the font is symbolic 284 * @param name the glyph name 285 * @return the width of the char 286 */ 287 @Override 288 int getRawWidth(int c, String name) { 289 Object metrics[]; 290 if (name == null) { // font specific 291 metrics = CharMetrics.get(Integer.valueOf(c)); 292 } 293 else { 294 if (name.equals(".notdef")) 295 return 0; 296 metrics = CharMetrics.get(name); 297 } 298 if (metrics != null) 299 return ((Integer)metrics[1]).intValue(); 300 return 0; 301 } 302 303/** Gets the kerning between two Unicode characters. The characters 304 * are converted to names and this names are used to find the kerning 305 * pairs in the <CODE>HashMap</CODE> <CODE>KernPairs</CODE>. 306 * @param char1 the first char 307 * @param char2 the second char 308 * @return the kerning to be applied 309 */ 310 @Override 311 public int getKerning(int char1, int char2) 312 { 313 String first = GlyphList.unicodeToName(char1); 314 if (first == null) 315 return 0; 316 String second = GlyphList.unicodeToName(char2); 317 if (second == null) 318 return 0; 319 Object obj[] = KernPairs.get(first); 320 if (obj == null) 321 return 0; 322 for (int k = 0; k < obj.length; k += 2) { 323 if (second.equals(obj[k])) 324 return ((Integer)obj[k + 1]).intValue(); 325 } 326 return 0; 327 } 328 329 330 /** Reads the font metrics 331 * @param rf the AFM file 332 * @throws DocumentException the AFM file is invalid 333 * @throws IOException the AFM file could not be read 334 */ 335 public void process(RandomAccessFileOrArray rf) throws DocumentException, IOException 336 { 337 String line; 338 boolean isMetrics = false; 339 while ((line = rf.readLine()) != null) 340 { 341 StringTokenizer tok = new StringTokenizer(line, " ,\n\r\t\f"); 342 if (!tok.hasMoreTokens()) 343 continue; 344 String ident = tok.nextToken(); 345 if (ident.equals("FontName")) 346 FontName = tok.nextToken("\u00ff").substring(1); 347 else if (ident.equals("FullName")) 348 FullName = tok.nextToken("\u00ff").substring(1); 349 else if (ident.equals("FamilyName")) 350 FamilyName = tok.nextToken("\u00ff").substring(1); 351 else if (ident.equals("Weight")) 352 Weight = tok.nextToken("\u00ff").substring(1); 353 else if (ident.equals("ItalicAngle")) 354 ItalicAngle = Float.parseFloat(tok.nextToken()); 355 else if (ident.equals("IsFixedPitch")) 356 IsFixedPitch = tok.nextToken().equals("true"); 357 else if (ident.equals("CharacterSet")) 358 CharacterSet = tok.nextToken("\u00ff").substring(1); 359 else if (ident.equals("FontBBox")) 360 { 361 llx = (int)Float.parseFloat(tok.nextToken()); 362 lly = (int)Float.parseFloat(tok.nextToken()); 363 urx = (int)Float.parseFloat(tok.nextToken()); 364 ury = (int)Float.parseFloat(tok.nextToken()); 365 } 366 else if (ident.equals("UnderlinePosition")) 367 UnderlinePosition = (int)Float.parseFloat(tok.nextToken()); 368 else if (ident.equals("UnderlineThickness")) 369 UnderlineThickness = (int)Float.parseFloat(tok.nextToken()); 370 else if (ident.equals("EncodingScheme")) 371 EncodingScheme = tok.nextToken("\u00ff").substring(1); 372 else if (ident.equals("CapHeight")) 373 CapHeight = (int)Float.parseFloat(tok.nextToken()); 374 else if (ident.equals("XHeight")) 375 XHeight = (int)Float.parseFloat(tok.nextToken()); 376 else if (ident.equals("Ascender")) 377 Ascender = (int)Float.parseFloat(tok.nextToken()); 378 else if (ident.equals("Descender")) 379 Descender = (int)Float.parseFloat(tok.nextToken()); 380 else if (ident.equals("StdHW")) 381 StdHW = (int)Float.parseFloat(tok.nextToken()); 382 else if (ident.equals("StdVW")) 383 StdVW = (int)Float.parseFloat(tok.nextToken()); 384 else if (ident.equals("StartCharMetrics")) 385 { 386 isMetrics = true; 387 break; 388 } 389 } 390 if (!isMetrics) 391 throw new DocumentException(MessageLocalization.getComposedMessage("missing.startcharmetrics.in.1", fileName)); 392 while ((line = rf.readLine()) != null) 393 { 394 StringTokenizer tok = new StringTokenizer(line); 395 if (!tok.hasMoreTokens()) 396 continue; 397 String ident = tok.nextToken(); 398 if (ident.equals("EndCharMetrics")) 399 { 400 isMetrics = false; 401 break; 402 } 403 Integer C = Integer.valueOf(-1); 404 Integer WX = Integer.valueOf(250); 405 String N = ""; 406 int B[] = null; 407 408 tok = new StringTokenizer(line, ";"); 409 while (tok.hasMoreTokens()) 410 { 411 StringTokenizer tokc = new StringTokenizer(tok.nextToken()); 412 if (!tokc.hasMoreTokens()) 413 continue; 414 ident = tokc.nextToken(); 415 if (ident.equals("C")) 416 C = Integer.valueOf(tokc.nextToken()); 417 else if (ident.equals("WX")) 418 WX = Integer.valueOf((int)Float.parseFloat(tokc.nextToken())); 419 else if (ident.equals("N")) 420 N = tokc.nextToken(); 421 else if (ident.equals("B")) { 422 B = new int[]{Integer.parseInt(tokc.nextToken()), 423 Integer.parseInt(tokc.nextToken()), 424 Integer.parseInt(tokc.nextToken()), 425 Integer.parseInt(tokc.nextToken())}; 426 } 427 } 428 Object metrics[] = new Object[]{C, WX, N, B}; 429 if (C.intValue() >= 0) 430 CharMetrics.put(C, metrics); 431 CharMetrics.put(N, metrics); 432 } 433 if (isMetrics) 434 throw new DocumentException(MessageLocalization.getComposedMessage("missing.endcharmetrics.in.1", fileName)); 435 if (!CharMetrics.containsKey("nonbreakingspace")) { 436 Object[] space = CharMetrics.get("space"); 437 if (space != null) 438 CharMetrics.put("nonbreakingspace", space); 439 } 440 while ((line = rf.readLine()) != null) 441 { 442 StringTokenizer tok = new StringTokenizer(line); 443 if (!tok.hasMoreTokens()) 444 continue; 445 String ident = tok.nextToken(); 446 if (ident.equals("EndFontMetrics")) 447 return; 448 if (ident.equals("StartKernPairs")) 449 { 450 isMetrics = true; 451 break; 452 } 453 } 454 if (!isMetrics) 455 throw new DocumentException(MessageLocalization.getComposedMessage("missing.endfontmetrics.in.1", fileName)); 456 while ((line = rf.readLine()) != null) 457 { 458 StringTokenizer tok = new StringTokenizer(line); 459 if (!tok.hasMoreTokens()) 460 continue; 461 String ident = tok.nextToken(); 462 if (ident.equals("KPX")) 463 { 464 String first = tok.nextToken(); 465 String second = tok.nextToken(); 466 Integer width = Integer.valueOf((int)Float.parseFloat(tok.nextToken())); 467 Object relates[] = KernPairs.get(first); 468 if (relates == null) 469 KernPairs.put(first, new Object[]{second, width}); 470 else 471 { 472 int n = relates.length; 473 Object relates2[] = new Object[n + 2]; 474 System.arraycopy(relates, 0, relates2, 0, n); 475 relates2[n] = second; 476 relates2[n + 1] = width; 477 KernPairs.put(first, relates2); 478 } 479 } 480 else if (ident.equals("EndKernPairs")) 481 { 482 isMetrics = false; 483 break; 484 } 485 } 486 if (isMetrics) 487 throw new DocumentException(MessageLocalization.getComposedMessage("missing.endkernpairs.in.1", fileName)); 488 rf.close(); 489 } 490 491/** If the embedded flag is <CODE>false</CODE> or if the font is 492 * one of the 14 built in types, it returns <CODE>null</CODE>, 493 * otherwise the font is read and output in a PdfStream object. 494 * @return the PdfStream containing the font or <CODE>null</CODE> 495 * @throws DocumentException if there is an error reading the font 496 * @since 2.1.3 497 */ 498 @Override 499 public PdfStream getFullFontStream() 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, true, Document.plainRandomAccess); 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(MessageLocalization.getComposedMessage("start.marker.missing.in.1", filePfb)); 517 if (rf.read() != PFB_TYPES[k]) 518 throw new DocumentException(MessageLocalization.getComposedMessage("incorrect.segment.type.in.1", 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(MessageLocalization.getComposedMessage("premature.end.in.1", filePfb)); 528 bytePtr += got; 529 size -= got; 530 } 531 } 532 return new StreamFont(st, lengths, compressionLevel); 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 // empty on purpose 544 } 545 } 546 } 547 } 548 549/** Generates the font descriptor for this font or <CODE>null</CODE> if it is 550 * one of the 14 built in fonts. 551 * @param fontStream the indirect reference to a PdfStream containing the font or <CODE>null</CODE> 552 * @return the PdfDictionary containing the font descriptor or <CODE>null</CODE> 553 */ 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 /** Generates the font dictionary for this font. 584 * @return the PdfDictionary containing the font dictionary 585 * @param firstChar the first valid character 586 * @param lastChar the last valid character 587 * @param shortTag a 256 bytes long <CODE>byte</CODE> array where each unused byte is represented by 0 588 * @param fontDescriptor the indirect reference to a PdfDictionary containing the font descriptor or <CODE>null</CODE> 589 */ 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 /** Outputs to the writer the font dictionaries and streams. 642 * @param writer the writer for this document 643 * @param ref the font indirect reference 644 * @param params several parameters that depend on the font type 645 * @throws IOException on error 646 * @throws DocumentException error in generating the object 647 */ 648 @Override 649 void writeFont(PdfWriter writer, PdfIndirectReference ref, Object params[]) throws DocumentException, IOException { 650 int firstChar = ((Integer)params[0]).intValue(); 651 int lastChar = ((Integer)params[1]).intValue(); 652 byte shortTag[] = (byte[])params[2]; 653 boolean subsetp = ((Boolean)params[3]).booleanValue() && subset; 654 if (!subsetp) { 655 firstChar = 0; 656 lastChar = shortTag.length - 1; 657 for (int k = 0; k < shortTag.length; ++k) 658 shortTag[k] = 1; 659 } 660 PdfIndirectReference ind_font = null; 661 PdfObject pobj = null; 662 PdfIndirectObject obj = null; 663 pobj = getFullFontStream(); 664 if (pobj != null){ 665 obj = writer.addToBody(pobj); 666 ind_font = obj.getIndirectReference(); 667 } 668 pobj = getFontDescriptor(ind_font); 669 if (pobj != null){ 670 obj = writer.addToBody(pobj); 671 ind_font = obj.getIndirectReference(); 672 } 673 pobj = getFontBaseType(ind_font, firstChar, lastChar, shortTag); 674 writer.addToBody(pobj, ref); 675 } 676 677 /** Gets the font parameter identified by <CODE>key</CODE>. Valid values 678 * for <CODE>key</CODE> are <CODE>ASCENT</CODE>, <CODE>CAPHEIGHT</CODE>, <CODE>DESCENT</CODE>, 679 * <CODE>ITALICANGLE</CODE>, <CODE>BBOXLLX</CODE>, <CODE>BBOXLLY</CODE>, <CODE>BBOXURX</CODE> 680 * and <CODE>BBOXURY</CODE>. 681 * @param key the parameter to be extracted 682 * @param fontSize the font size in points 683 * @return the parameter in points 684 */ 685 @Override 686 public float getFontDescriptor(int key, float fontSize) { 687 switch (key) { 688 case AWT_ASCENT: 689 case ASCENT: 690 return Ascender * fontSize / 1000; 691 case CAPHEIGHT: 692 return CapHeight * fontSize / 1000; 693 case AWT_DESCENT: 694 case DESCENT: 695 return Descender * fontSize / 1000; 696 case ITALICANGLE: 697 return ItalicAngle; 698 case BBOXLLX: 699 return llx * fontSize / 1000; 700 case BBOXLLY: 701 return lly * fontSize / 1000; 702 case BBOXURX: 703 return urx * fontSize / 1000; 704 case BBOXURY: 705 return ury * fontSize / 1000; 706 case AWT_LEADING: 707 return 0; 708 case AWT_MAXADVANCE: 709 return (urx - llx) * fontSize / 1000; 710 case UNDERLINE_POSITION: 711 return UnderlinePosition * fontSize / 1000; 712 case UNDERLINE_THICKNESS: 713 return UnderlineThickness * fontSize / 1000; 714 } 715 return 0; 716 } 717 718 /** Gets the postscript font name. 719 * @return the postscript font name 720 */ 721 @Override 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 */ 734 @Override 735 public String[][] getFullFontName() { 736 return new String[][]{{"", "", "", FullName}}; 737 } 738 739 /** Gets all the entries of the names-table. If it is a True Type font 740 * each array element will have {Name ID, Platform ID, Platform Encoding ID, 741 * Language ID, font name}. The interpretation of this values can be 742 * found in the Open Type specification, chapter 2, in the 'name' table.<br> 743 * For the other fonts the array has a single element with {"4", "", "", "", 744 * font name}. 745 * @return the full name of the font 746 */ 747 @Override 748 public String[][] getAllNameEntries() { 749 return new String[][]{{"4", "", "", "", FullName}}; 750 } 751 752 /** Gets the family name of the font. If it is a True Type font 753 * each array element will have {Platform ID, Platform Encoding ID, 754 * Language ID, font name}. The interpretation of this values can be 755 * found in the Open Type specification, chapter 2, in the 'name' table.<br> 756 * For the other fonts the array has a single element with {"", "", "", 757 * font name}. 758 * @return the family name of the font 759 */ 760 @Override 761 public String[][] getFamilyFontName() { 762 return new String[][]{{"", "", "", FamilyName}}; 763 } 764 765 /** Checks if the font has any kerning pairs. 766 * @return <CODE>true</CODE> if the font has any kerning pairs 767 */ 768 @Override 769 public boolean hasKernPairs() { 770 return !KernPairs.isEmpty(); 771 } 772 773 /** 774 * Sets the font name that will appear in the pdf font dictionary. 775 * Use with care as it can easily make a font unreadable if not embedded. 776 * @param name the new font name 777 */ 778 @Override 779 public void setPostscriptFontName(String name) { 780 FontName = name; 781 } 782 783 /** 784 * Sets the kerning between two Unicode chars. 785 * @param char1 the first char 786 * @param char2 the second char 787 * @param kern the kerning to apply in normalized 1000 units 788 * @return <code>true</code> if the kerning was applied, <code>false</code> otherwise 789 */ 790 @Override 791 public boolean setKerning(int char1, int char2, int kern) { 792 String first = GlyphList.unicodeToName(char1); 793 if (first == null) 794 return false; 795 String second = GlyphList.unicodeToName(char2); 796 if (second == null) 797 return false; 798 Object obj[] = KernPairs.get(first); 799 if (obj == null) { 800 obj = new Object[]{second, Integer.valueOf(kern)}; 801 KernPairs.put(first, obj); 802 return true; 803 } 804 for (int k = 0; k < obj.length; k += 2) { 805 if (second.equals(obj[k])) { 806 obj[k + 1] = Integer.valueOf(kern); 807 return true; 808 } 809 } 810 int size = obj.length; 811 Object obj2[] = new Object[size + 2]; 812 System.arraycopy(obj, 0, obj2, 0, size); 813 obj2[size] = second; 814 obj2[size + 1] = Integer.valueOf(kern); 815 KernPairs.put(first, obj2); 816 return true; 817 } 818 819 @Override 820 protected int[] getRawCharBBox(int c, String name) { 821 Object metrics[]; 822 if (name == null) { // font specific 823 metrics = CharMetrics.get(Integer.valueOf(c)); 824 } 825 else { 826 if (name.equals(".notdef")) 827 return null; 828 metrics = CharMetrics.get(name); 829 } 830 if (metrics != null) 831 return (int[])metrics[3]; 832 return null; 833 } 834 835}