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}