001/* 002 * $Id: FontFactoryImp.java 4863 2011-05-12 07:01:55Z redlab_b $ 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; 045 046import java.io.File; 047import java.io.IOException; 048import java.util.ArrayList; 049import java.util.Hashtable; 050import java.util.Set; 051 052import com.itextpdf.text.Font.FontFamily; 053import com.itextpdf.text.log.Level; 054import com.itextpdf.text.log.Logger; 055import com.itextpdf.text.log.LoggerFactory; 056import com.itextpdf.text.pdf.BaseFont; 057 058/** 059 * If you are using True Type fonts, you can declare the paths of the different ttf- and ttc-files 060 * to this class first and then create fonts in your code using one of the getFont method 061 * without having to enter a path as parameter. 062 * 063 * @author Bruno Lowagie 064 */ 065 066public class FontFactoryImp implements FontProvider { 067 068 private static final Logger LOGGER = LoggerFactory.getLogger(FontFactoryImp.class); 069/** This is a map of postscriptfontnames of True Type fonts and the path of their ttf- or ttc-file. */ 070 private final Hashtable<String, String> trueTypeFonts = new Hashtable<String, String>(); 071 072 private static String[] TTFamilyOrder = { 073 "3", "1", "1033", 074 "3", "0", "1033", 075 "1", "0", "0", 076 "0", "3", "0" 077 }; 078 079/** This is a map of fontfamilies. */ 080 private final Hashtable<String, ArrayList<String>> fontFamilies = new Hashtable<String, ArrayList<String>>(); 081 082/** This is the default encoding to use. */ 083 public String defaultEncoding = BaseFont.WINANSI; 084 085/** This is the default value of the <VAR>embedded</VAR> variable. */ 086 public boolean defaultEmbedding = BaseFont.NOT_EMBEDDED; 087 088/** Creates new FontFactory */ 089 public FontFactoryImp() { 090 trueTypeFonts.put(FontFactory.COURIER.toLowerCase(), FontFactory.COURIER); 091 trueTypeFonts.put(FontFactory.COURIER_BOLD.toLowerCase(), FontFactory.COURIER_BOLD); 092 trueTypeFonts.put(FontFactory.COURIER_OBLIQUE.toLowerCase(), FontFactory.COURIER_OBLIQUE); 093 trueTypeFonts.put(FontFactory.COURIER_BOLDOBLIQUE.toLowerCase(), FontFactory.COURIER_BOLDOBLIQUE); 094 trueTypeFonts.put(FontFactory.HELVETICA.toLowerCase(), FontFactory.HELVETICA); 095 trueTypeFonts.put(FontFactory.HELVETICA_BOLD.toLowerCase(), FontFactory.HELVETICA_BOLD); 096 trueTypeFonts.put(FontFactory.HELVETICA_OBLIQUE.toLowerCase(), FontFactory.HELVETICA_OBLIQUE); 097 trueTypeFonts.put(FontFactory.HELVETICA_BOLDOBLIQUE.toLowerCase(), FontFactory.HELVETICA_BOLDOBLIQUE); 098 trueTypeFonts.put(FontFactory.SYMBOL.toLowerCase(), FontFactory.SYMBOL); 099 trueTypeFonts.put(FontFactory.TIMES_ROMAN.toLowerCase(), FontFactory.TIMES_ROMAN); 100 trueTypeFonts.put(FontFactory.TIMES_BOLD.toLowerCase(), FontFactory.TIMES_BOLD); 101 trueTypeFonts.put(FontFactory.TIMES_ITALIC.toLowerCase(), FontFactory.TIMES_ITALIC); 102 trueTypeFonts.put(FontFactory.TIMES_BOLDITALIC.toLowerCase(), FontFactory.TIMES_BOLDITALIC); 103 trueTypeFonts.put(FontFactory.ZAPFDINGBATS.toLowerCase(), FontFactory.ZAPFDINGBATS); 104 105 ArrayList<String> tmp; 106 tmp = new ArrayList<String>(); 107 tmp.add(FontFactory.COURIER); 108 tmp.add(FontFactory.COURIER_BOLD); 109 tmp.add(FontFactory.COURIER_OBLIQUE); 110 tmp.add(FontFactory.COURIER_BOLDOBLIQUE); 111 fontFamilies.put(FontFactory.COURIER.toLowerCase(), tmp); 112 tmp = new ArrayList<String>(); 113 tmp.add(FontFactory.HELVETICA); 114 tmp.add(FontFactory.HELVETICA_BOLD); 115 tmp.add(FontFactory.HELVETICA_OBLIQUE); 116 tmp.add(FontFactory.HELVETICA_BOLDOBLIQUE); 117 fontFamilies.put(FontFactory.HELVETICA.toLowerCase(), tmp); 118 tmp = new ArrayList<String>(); 119 tmp.add(FontFactory.SYMBOL); 120 fontFamilies.put(FontFactory.SYMBOL.toLowerCase(), tmp); 121 tmp = new ArrayList<String>(); 122 tmp.add(FontFactory.TIMES_ROMAN); 123 tmp.add(FontFactory.TIMES_BOLD); 124 tmp.add(FontFactory.TIMES_ITALIC); 125 tmp.add(FontFactory.TIMES_BOLDITALIC); 126 fontFamilies.put(FontFactory.TIMES.toLowerCase(), tmp); 127 fontFamilies.put(FontFactory.TIMES_ROMAN.toLowerCase(), tmp); 128 tmp = new ArrayList<String>(); 129 tmp.add(FontFactory.ZAPFDINGBATS); 130 fontFamilies.put(FontFactory.ZAPFDINGBATS.toLowerCase(), tmp); 131 } 132 133 /** 134 * Constructs a <CODE>Font</CODE>-object. 135 * 136 * @param fontname the name of the font 137 * @param encoding the encoding of the font 138 * @param embedded true if the font is to be embedded in the PDF 139 * @param size the size of this font 140 * @param style the style of this font 141 * @param color the <CODE>BaseColor</CODE> of this font. 142 * @return the Font constructed based on the parameters 143 */ 144 public Font getFont(final String fontname, final String encoding, final boolean embedded, final float size, final int style, final BaseColor color) { 145 return getFont(fontname, encoding, embedded, size, style, color, true); 146 } 147 148 149 150 /** 151 * Constructs a <CODE>Font</CODE>-object. 152 * 153 * @param fontname the name of the font 154 * @param encoding the encoding of the font 155 * @param embedded true if the font is to be embedded in the PDF 156 * @param size the size of this font 157 * @param style the style of this font 158 * @param color the <CODE>BaseColor</CODE> of this font. 159 * @param cached true if the font comes from the cache or is added to 160 * the cache if new, false if the font is always created new 161 * @return the Font constructed based on the parameters 162 */ 163 public Font getFont(String fontname, final String encoding, final boolean embedded, final float size, int style, final BaseColor color, final boolean cached) { 164 if (fontname == null) return new Font(FontFamily.UNDEFINED, size, style, color); 165 String lowercasefontname = fontname.toLowerCase(); 166 ArrayList<String> tmp = fontFamilies.get(lowercasefontname); 167 if (tmp != null) { 168 // some bugs were fixed here by Daniel Marczisovszky 169 int s = style == Font.UNDEFINED ? Font.NORMAL : style; 170 int fs = Font.NORMAL; 171 boolean found = false; 172 for (String string : tmp) { 173 String f = string; 174 String lcf = f.toLowerCase(); 175 fs = Font.NORMAL; 176 if (lcf.toLowerCase().indexOf("bold") != -1) fs |= Font.BOLD; 177 if (lcf.toLowerCase().indexOf("italic") != -1 || lcf.toLowerCase().indexOf("oblique") != -1) fs |= Font.ITALIC; 178 if ((s & Font.BOLDITALIC) == fs) { 179 fontname = f; 180 found = true; 181 break; 182 } 183 } 184 if (style != Font.UNDEFINED && found) { 185 style &= ~fs; 186 } 187 } 188 BaseFont basefont = null; 189 try { 190 try { 191 // the font is a type 1 font or CJK font 192 basefont = BaseFont.createFont(fontname, encoding, embedded, cached, null, null, true); 193 } 194 catch(DocumentException de) { 195 } 196 if (basefont == null) { 197 // the font is a true type font or an unknown font 198 fontname = trueTypeFonts.get(fontname.toLowerCase()); 199 // the font is not registered as truetype font 200 if (fontname == null) return new Font(FontFamily.UNDEFINED, size, style, color); 201 // the font is registered as truetype font 202 basefont = BaseFont.createFont(fontname, encoding, embedded, cached, null, null); 203 } 204 } 205 catch(DocumentException de) { 206 // this shouldn't happen 207 throw new ExceptionConverter(de); 208 } 209 catch(IOException ioe) { 210 // the font is registered as a true type font, but the path was wrong 211 return new Font(FontFamily.UNDEFINED, size, style, color); 212 } 213 catch(NullPointerException npe) { 214 // null was entered as fontname and/or encoding 215 return new Font(FontFamily.UNDEFINED, size, style, color); 216 } 217 return new Font(basefont, size, style, color); 218 } 219 220/** 221 * Constructs a <CODE>Font</CODE>-object. 222 * 223 * @param fontname the name of the font 224 * @param encoding the encoding of the font 225 * @param embedded true if the font is to be embedded in the PDF 226 * @param size the size of this font 227 * @param style the style of this font 228 * @return the Font constructed based on the parameters 229 */ 230 231 public Font getFont(final String fontname, final String encoding, final boolean embedded, final float size, final int style) { 232 return getFont(fontname, encoding, embedded, size, style, null); 233 } 234 235/** 236 * Constructs a <CODE>Font</CODE>-object. 237 * 238 * @param fontname the name of the font 239 * @param encoding the encoding of the font 240 * @param embedded true if the font is to be embedded in the PDF 241 * @param size the size of this font 242 * @return the Font constructed based on the parameters 243 */ 244 245 public Font getFont(final String fontname, final String encoding, final boolean embedded, final float size) { 246 return getFont(fontname, encoding, embedded, size, Font.UNDEFINED, null); 247 } 248 249/** 250 * Constructs a <CODE>Font</CODE>-object. 251 * 252 * @param fontname the name of the font 253 * @param encoding the encoding of the font 254 * @param embedded true if the font is to be embedded in the PDF 255 * @return the Font constructed based on the parameters 256 */ 257 258 public Font getFont(final String fontname, final String encoding, final boolean embedded) { 259 return getFont(fontname, encoding, embedded, Font.UNDEFINED, Font.UNDEFINED, null); 260 } 261 262/** 263 * Constructs a <CODE>Font</CODE>-object. 264 * 265 * @param fontname the name of the font 266 * @param encoding the encoding of the font 267 * @param size the size of this font 268 * @param style the style of this font 269 * @param color the <CODE>BaseColor</CODE> of this font. 270 * @return the Font constructed based on the parameters 271 */ 272 273 public Font getFont(final String fontname, final String encoding, final float size, final int style, final BaseColor color) { 274 return getFont(fontname, encoding, defaultEmbedding, size, style, color); 275 } 276 277/** 278 * Constructs a <CODE>Font</CODE>-object. 279 * 280 * @param fontname the name of the font 281 * @param encoding the encoding of the font 282 * @param size the size of this font 283 * @param style the style of this font 284 * @return the Font constructed based on the parameters 285 */ 286 287 public Font getFont(final String fontname, final String encoding, final float size, final int style) { 288 return getFont(fontname, encoding, defaultEmbedding, size, style, null); 289 } 290 291/** 292 * Constructs a <CODE>Font</CODE>-object. 293 * 294 * @param fontname the name of the font 295 * @param encoding the encoding of the font 296 * @param size the size of this font 297 * @return the Font constructed based on the parameters 298 */ 299 300 public Font getFont(final String fontname, final String encoding, final float size) { 301 return getFont(fontname, encoding, defaultEmbedding, size, Font.UNDEFINED, null); 302 } 303 304 305/** 306 * Constructs a <CODE>Font</CODE>-object. 307 * 308 * @param fontname the name of the font 309 * @param size the size of this font 310 * @param color the <CODE>BaseColor</CODE> of this font. 311 * @return the Font constructed based on the parameters 312 * @since 2.1.0 313 */ 314 315 public Font getFont(final String fontname, final float size, final BaseColor color) { 316 return getFont(fontname, defaultEncoding, defaultEmbedding, size, Font.UNDEFINED, color); 317 } 318 319/** 320 * Constructs a <CODE>Font</CODE>-object. 321 * 322 * @param fontname the name of the font 323 * @param encoding the encoding of the font 324 * @return the Font constructed based on the parameters 325 */ 326 327 public Font getFont(final String fontname, final String encoding) { 328 return getFont(fontname, encoding, defaultEmbedding, Font.UNDEFINED, Font.UNDEFINED, null); 329 } 330 331/** 332 * Constructs a <CODE>Font</CODE>-object. 333 * 334 * @param fontname the name of the font 335 * @param size the size of this font 336 * @param style the style of this font 337 * @param color the <CODE>BaseColor</CODE> of this font. 338 * @return the Font constructed based on the parameters 339 */ 340 341 public Font getFont(final String fontname, final float size, final int style, final BaseColor color) { 342 return getFont(fontname, defaultEncoding, defaultEmbedding, size, style, color); 343 } 344 345/** 346 * Constructs a <CODE>Font</CODE>-object. 347 * 348 * @param fontname the name of the font 349 * @param size the size of this font 350 * @param style the style of this font 351 * @return the Font constructed based on the parameters 352 */ 353 354 public Font getFont(final String fontname, final float size, final int style) { 355 return getFont(fontname, defaultEncoding, defaultEmbedding, size, style, null); 356 } 357 358/** 359 * Constructs a <CODE>Font</CODE>-object. 360 * 361 * @param fontname the name of the font 362 * @param size the size of this font 363 * @return the Font constructed based on the parameters 364 */ 365 366 public Font getFont(final String fontname, final float size) { 367 return getFont(fontname, defaultEncoding, defaultEmbedding, size, Font.UNDEFINED, null); 368 } 369 370/** 371 * Constructs a <CODE>Font</CODE>-object. 372 * 373 * @param fontname the name of the font 374 * @return the Font constructed based on the parameters 375 */ 376 377 public Font getFont(final String fontname) { 378 return getFont(fontname, defaultEncoding, defaultEmbedding, Font.UNDEFINED, Font.UNDEFINED, null); 379 } 380 381 /** 382 * Register a font by giving explicitly the font family and name. 383 * @param familyName the font family 384 * @param fullName the font name 385 * @param path the font path 386 */ 387 public void registerFamily(final String familyName, final String fullName, final String path) { 388 if (path != null) 389 trueTypeFonts.put(fullName, path); 390 ArrayList<String> tmp = fontFamilies.get(familyName); 391 if (tmp == null) { 392 tmp = new ArrayList<String>(); 393 tmp.add(fullName); 394 fontFamilies.put(familyName, tmp); 395 } 396 else { 397 int fullNameLength = fullName.length(); 398 boolean inserted = false; 399 for (int j = 0; j < tmp.size(); ++j) { 400 if (tmp.get(j).length() >= fullNameLength) { 401 tmp.add(j, fullName); 402 inserted = true; 403 break; 404 } 405 } 406 if (!inserted) 407 tmp.add(fullName); 408 } 409 } 410 411/** 412 * Register a ttf- or a ttc-file. 413 * 414 * @param path the path to a ttf- or ttc-file 415 */ 416 417 public void register(final String path) { 418 register(path, null); 419 } 420 421/** 422 * Register a font file and use an alias for the font contained in it. 423 * 424 * @param path the path to a font file 425 * @param alias the alias you want to use for the font 426 */ 427 428 public void register(final String path, final String alias) { 429 try { 430 if (path.toLowerCase().endsWith(".ttf") || path.toLowerCase().endsWith(".otf") || path.toLowerCase().indexOf(".ttc,") > 0) { 431 Object allNames[] = BaseFont.getAllFontNames(path, BaseFont.WINANSI, null); 432 trueTypeFonts.put(((String)allNames[0]).toLowerCase(), path); 433 if (alias != null) { 434 trueTypeFonts.put(alias.toLowerCase(), path); 435 } 436 // register all the font names with all the locales 437 String[][] names = (String[][])allNames[2]; //full name 438 for (String[] name : names) { 439 trueTypeFonts.put(name[3].toLowerCase(), path); 440 } 441 String fullName = null; 442 String familyName = null; 443 names = (String[][])allNames[1]; //family name 444 for (int k = 0; k < TTFamilyOrder.length; k += 3) { 445 for (String[] name : names) { 446 if (TTFamilyOrder[k].equals(name[0]) && TTFamilyOrder[k + 1].equals(name[1]) && TTFamilyOrder[k + 2].equals(name[2])) { 447 familyName = name[3].toLowerCase(); 448 k = TTFamilyOrder.length; 449 break; 450 } 451 } 452 } 453 if (familyName != null) { 454 String lastName = ""; 455 names = (String[][])allNames[2]; //full name 456 for (String[] name : names) { 457 for (int k = 0; k < TTFamilyOrder.length; k += 3) { 458 if (TTFamilyOrder[k].equals(name[0]) && TTFamilyOrder[k + 1].equals(name[1]) && TTFamilyOrder[k + 2].equals(name[2])) { 459 fullName = name[3]; 460 if (fullName.equals(lastName)) 461 continue; 462 lastName = fullName; 463 registerFamily(familyName, fullName, null); 464 break; 465 } 466 } 467 } 468 } 469 } 470 else if (path.toLowerCase().endsWith(".ttc")) { 471 if (alias != null) 472 LOGGER.error("You can't define an alias for a true type collection."); 473 String[] names = BaseFont.enumerateTTCNames(path); 474 for (int i = 0; i < names.length; i++) { 475 register(path + "," + i); 476 } 477 } 478 else if (path.toLowerCase().endsWith(".afm") || path.toLowerCase().endsWith(".pfm")) { 479 BaseFont bf = BaseFont.createFont(path, BaseFont.CP1252, false); 480 String fullName = bf.getFullFontName()[0][3].toLowerCase(); 481 String familyName = bf.getFamilyFontName()[0][3].toLowerCase(); 482 String psName = bf.getPostscriptFontName().toLowerCase(); 483 registerFamily(familyName, fullName, null); 484 trueTypeFonts.put(psName, path); 485 trueTypeFonts.put(fullName, path); 486 } 487 if (LOGGER.isLogging(Level.TRACE)) { 488 LOGGER.trace(String.format("Registered %s", path)); 489 } 490 } 491 catch(DocumentException de) { 492 // this shouldn't happen 493 throw new ExceptionConverter(de); 494 } 495 catch(IOException ioe) { 496 throw new ExceptionConverter(ioe); 497 } 498 } 499 500 /** Register all the fonts in a directory. 501 * @param dir the directory 502 * @return the number of fonts registered 503 */ 504 public int registerDirectory(final String dir) { 505 return registerDirectory(dir, false); 506 } 507 508 /** 509 * Register all the fonts in a directory and possibly its subdirectories. 510 * @param dir the directory 511 * @param scanSubdirectories recursively scan subdirectories if <code>true</true> 512 * @return the number of fonts registered 513 * @since 2.1.2 514 */ 515 public int registerDirectory(final String dir, final boolean scanSubdirectories) { 516 if (LOGGER.isLogging(Level.DEBUG)) { 517 LOGGER.debug(String.format("Registering directory %s, looking for fonts", dir)); 518 } 519 int count = 0; 520 try { 521 File file = new File(dir); 522 if (!file.exists() || !file.isDirectory()) 523 return 0; 524 String files[] = file.list(); 525 if (files == null) 526 return 0; 527 for (int k = 0; k < files.length; ++k) { 528 try { 529 file = new File(dir, files[k]); 530 if (file.isDirectory()) { 531 if (scanSubdirectories) { 532 count += registerDirectory(file.getAbsolutePath(), true); 533 } 534 } else { 535 String name = file.getPath(); 536 String suffix = name.length() < 4 ? null : name.substring(name.length() - 4).toLowerCase(); 537 if (".afm".equals(suffix) || ".pfm".equals(suffix)) { 538 /* Only register Type 1 fonts with matching .pfb files */ 539 File pfb = new File(name.substring(0, name.length() - 4) + ".pfb"); 540 if (pfb.exists()) { 541 register(name, null); 542 ++count; 543 } 544 } else if (".ttf".equals(suffix) || ".otf".equals(suffix) || ".ttc".equals(suffix)) { 545 register(name, null); 546 ++count; 547 } 548 } 549 } 550 catch (Exception e) { 551 //empty on purpose 552 } 553 } 554 } 555 catch (Exception e) { 556 //empty on purpose 557 } 558 return count; 559 } 560 561 /** Register fonts in some probable directories. It usually works in Windows, 562 * Linux and Solaris. 563 * @return the number of fonts registered 564 */ 565 public int registerDirectories() { 566 int count = 0; 567 String windir = System.getenv("windir"); 568 String fileseparator = System.getProperty("file.separator"); 569 if (windir != null && fileseparator != null) { 570 count += registerDirectory(windir + fileseparator + "fonts"); 571 } 572 count += registerDirectory("/usr/share/X11/fonts", true); 573 count += registerDirectory("/usr/X/lib/X11/fonts", true); 574 count += registerDirectory("/usr/openwin/lib/X11/fonts", true); 575 count += registerDirectory("/usr/share/fonts", true); 576 count += registerDirectory("/usr/X11R6/lib/X11/fonts", true); 577 count += registerDirectory("/Library/Fonts"); 578 count += registerDirectory("/System/Library/Fonts"); 579 return count; 580 } 581 582/** 583 * Gets a set of registered fontnames. 584 * @return a set of registered fonts 585 */ 586 587 public Set<String> getRegisteredFonts() { 588 return trueTypeFonts.keySet(); 589 } 590 591/** 592 * Gets a set of registered fontnames. 593 * @return a set of registered font families 594 */ 595 596 public Set<String> getRegisteredFamilies() { 597 return fontFamilies.keySet(); 598 } 599 600/** 601 * Checks if a certain font is registered. 602 * 603 * @param fontname the name of the font that has to be checked. 604 * @return true if the font is found 605 */ 606 public boolean isRegistered(final String fontname) { 607 return trueTypeFonts.containsKey(fontname.toLowerCase()); 608 } 609}