org.pz.platypus.plugin.pdf.PdfFontFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.pz.platypus.plugin.pdf.PdfFontFactory.java

Source

/***
 *  Platypus: Page Layout and Typesetting Software (free at platypus.pz.org)
 *
 *  Platypus is (c) Copyright 2011 Pacific Data Works LLC. All Rights Reserved.
 *  Licensed under Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0.html)
 */

package org.pz.platypus.plugin.pdf;

import com.lowagie.text.Font;
import com.lowagie.text.FontFactory;
import com.lowagie.text.DocumentException;
import com.lowagie.text.pdf.BaseFont;
import org.pz.platypus.GDD;
import org.pz.platypus.TypefaceMap;

import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.util.Set;

/**
 * Handles the complexities of creating a font for iText
 *
 * @author alb
 */
public class PdfFontFactory {
    private GDD gdd;
    private PdfData pdfData;

    public PdfFontFactory(GDD Gdd, PdfData pdd) {
        gdd = Gdd;
        pdfData = pdd;
    }

    /**
     * Creates an iText Font object based on a passed-in PdfFont
     *
     * @param f the PdfFont containing the parameters for the font
     * @return the iText Font object
     */
    public Font createItextFont(final PdfFont f) {
        PdfFont pf = f;

        int style = 0;

        Font font = null;

        if (pf == null) {
            pf = new PdfFont(pdfData);
        }

        Color col = new Color(pf.getColor().getR(), pf.getColor().getG(), pf.getColor().getB());
        String iTextFontName = createItextFontName(pf);

        if (!isBase14Font(pf.getFace())) {
            style = computeItextStyle(pf);
            font = getIdentityHFont(iTextFontName, pf.getSize(), style, col);
        }

        if (font == null) { //TODO: identify when this would be the case.
            font = getCp1252Font(iTextFontName, pf.getSize(), style, col);
        }

        if (font == null || font.getBaseFont() == null) { //TODO: Make error msg use literals
            gdd.logWarning("iText could not find font for: " + iTextFontName + ". Using Times-Roman");
            font = FontFactory.getFont(BaseFont.TIMES_ROMAN, BaseFont.CP1252, BaseFont.EMBEDDED, pf.getSize(),
                    style, col);
        }

        // #if debug
        //        Set fonts = FontFactory.getRegisteredFonts();
        //        Set families = FontFactory.getRegisteredFamilies();

        return (font);
    }

    /**
     * iText font style captures bold, italic, strikethru, underline. Since we handle
     * strikethrough and underline ourselves, we use it to communicate italic and bold
     * only. This computation done here.
     *
     * @param f the PdfFont whose style is being checked
     * @return the iText Style
     */
    int computeItextStyle(PdfFont f) {
        int style = 0;

        if (f.getItalics() && f.getBold()) {
            style |= Font.BOLDITALIC;
        } else if (f.getItalics()) {
            style |= Font.ITALIC;
        } else if (f.getBold()) {
            style |= Font.BOLD;
        }

        return (style);
    }

    /**
     * Gets the iText font name for any of the Base14 fonts.
     *
     * @param font the PdfFont whose name we're looking up.
     * @return the iText name or null if an error has occured
     */
    String computeBase14ItextFontName(final PdfFont font) {
        final String typefaceName;
        String iTextFontName = null;
        PdfFont f;

        // in the impossible event this gets passed a null, then
        // replace it with the default font. It might be better
        // to just throw an exception. Should revisit this later.
        if (font == null) {
            f = new PdfFont(pdfData);
        } else {
            f = font;
        }

        typefaceName = f.getFace();

        if (typefaceName.equals("COURIER")) {
            if (f.getBold()) {
                if (f.getItalics()) {
                    iTextFontName = BaseFont.COURIER_BOLDOBLIQUE;
                } else {
                    iTextFontName = BaseFont.COURIER_BOLD;
                }
            } else if (f.getItalics()) {
                iTextFontName = BaseFont.COURIER_OBLIQUE;
            } else
                iTextFontName = BaseFont.COURIER;

            return (iTextFontName);
        }

        if (typefaceName.equals("HELVETICA")) {
            if (f.getBold()) {
                if (f.getItalics()) {
                    iTextFontName = BaseFont.HELVETICA_BOLDOBLIQUE;
                } else {
                    iTextFontName = BaseFont.HELVETICA_BOLD;
                }
            } else if (f.getItalics()) {
                iTextFontName = BaseFont.HELVETICA_OBLIQUE;
            } else {
                iTextFontName = BaseFont.HELVETICA;
            }

            return (iTextFontName);
        }

        if (typefaceName.equals("TIMES_ROMAN")) {
            if (f.getBold()) {
                if (f.getItalics())
                    iTextFontName = BaseFont.TIMES_BOLDITALIC;
                else
                    iTextFontName = BaseFont.TIMES_BOLD;
            } else if (f.getItalics())
                iTextFontName = BaseFont.TIMES_ITALIC;
            else
                iTextFontName = BaseFont.TIMES_ROMAN;

            return (iTextFontName);
        }

        if (typefaceName.equals("SYMBOL")) {
            iTextFontName = BaseFont.SYMBOL;
            return (iTextFontName);
        }

        if (typefaceName.equals("DINGBATS")) {
            iTextFontName = BaseFont.ZAPFDINGBATS;
            return (iTextFontName);
        }

        // in theory, impossible, since the font is validated before the function is called.
        return (iTextFontName);
    }

    /**
     * Get the name by which iText refers to this font. This routine is mostly occupied
     * with the special handling of the Base14 fonts.
     *
     * For all other fonts, this method makes sure the font is registered with iText and
     * returns its name as registered by iText (which is the family name for the font).
     *
     * @param f PdfFont whose iText name we're getting
     * @return a string containing the iText usable name for this font. If passed-in font is
     *             null or the font name is not recognized or found, TIMES_ROMAN is returned.
     */
    String createItextFontName(final PdfFont f) {
        if (f == null) {
            errFontNotFound("NULL");
            return (BaseFont.TIMES_ROMAN);
        }

        String typefaceName = f.getFace();

        // if it's a Base14 font, compute the name
        if (isBase14Font(typefaceName)) {
            return (computeBase14ItextFontName(f));
        }

        // It's not a base14 font, so is the font already registered in iText?
        if (isRegisteredWithItext(typefaceName)) {
            return (typefaceName);
        }

        // If not, load the font from Platypus into iText, then look up the
        // iText name and return that. In case of error, return TIMES_ROMAN.

        if (!findAndRegisterFont(typefaceName)) {
            errFontNotFound(typefaceName);
            return (BaseFont.TIMES_ROMAN);
        }

        if (isRegisteredWithItext(typefaceName)) {
            return (typefaceName);
        }

        // in theory, cannot get here.
        //        Set regFonts = FontFactory.getRegisteredFonts();   // debug statements, in case we do get here.
        //        Set regFamilies = FontFactory.getRegisteredFamilies();
        errFontNotFound(typefaceName);
        return (BaseFont.TIMES_ROMAN);
    }

    /**
     * This test whether a font is registered by name or by family name in iText. Either way is
     * good enough for our purposes.
     *
     * @param fontName the font we're checking
     * @return true if iText has the font registered by name or by family name, false otherwise.
     */
    boolean isRegisteredWithItext(final String fontName) {
        assert (fontName != null);
        if (FontFactory.isRegistered(fontName)) {
            return (true);
        }

        Set<String> regFamilies = (Set<String>) FontFactory.getRegisteredFamilies();
        if (regFamilies.contains(fontName.toLowerCase())) {
            return (true);
        }

        return (false);
    }

    /**
     * Get the filenames from the typefaceMap and register them in iText's FontFactory.
     *
     * @param typefaceName name of the typeface
     * @return true if the font is now registered, false if an error occurred. (Font was not in fontlist.)
     */
    boolean findAndRegisterFont(String typefaceName) {
        String[] fontFiles = lookupFontFilenames(typefaceName);
        if (fontFiles.length == 0) {
            errFontNotFound(typefaceName);
            return (false);
        } else {

            for (String fontFile : fontFiles) {
                registerOneFont(fontFile, typefaceName);
            }

            return (true);
        }
    }

    /**
     * Gets the font with CP1252 (aka WINANSI) encoding
     * @param fontName name of font to get
     * @param size  size in points
     * @param style bold, italic, etc.
     * @param color font color (RGB 0-255)
     * @return the font, or null if an error occurred.
     */
    Font getCp1252Font(final String fontName, float size, int style, Color color) {
        Font font;
        try {

            font = FontFactory.getFont(fontName, BaseFont.CP1252, BaseFont.EMBEDDED, size, style, color);
        } catch (Exception ex) {
            font = null;
        }

        return (font);
    }

    /**
     * Opens a font using the IDENTITY-H encoding.
     *
     * @param fontName  the name assigned to the font in the font list
     * @param size the size in points
     * @param style bold, italic, etc.
     * @param color font color (RGB 0-255)
     * @return the Font if opened; null if the file could not be opened or an error occurred.
     */
    Font getIdentityHFont(final String fontName, float size, int style, Color color) {
        Font font;
        try {
            font = FontFactory.getFont(fontName, BaseFont.IDENTITY_H, BaseFont.EMBEDDED, size, style, color);
        } catch (Exception ex) {
            font = null;
        }

        return (font);
    }

    /**
     * Determines whehter the current font is one of the Base14 Acrobat fonts, built into
     * every PDF reader. These fonts require special hanldling, and so this routine helps
     * identify them, based on their font face name.
     *
     * @param fontName name of the font face
     * @return true if it's a base14 font name, false otherwise.
     */
    boolean isBase14Font(final String fontName) {
        if (fontName.equals("COURIER") || fontName.equals("DINGBATS") || fontName.equals("HELVETICA")
                || fontName.equals("SYMBOL") || fontName.equals("TIMES_ROMAN")) {
            return (true);
        }

        return (false);
    }

    /**
     * Get the names of the actual font files that are the implementation of this typeface
     * @param typefaceName the typefaces
     * @return an array of strings containing the file names.
     */
    String[] lookupFontFilenames(String typefaceName) {
        assert pdfData != null;
        assert typefaceName != null;

        TypefaceMap typefaceMap = pdfData.getTypefaceMap();
        if (typefaceMap == null) {
            pdfData.loadTypefaceMap();
        }

        return (typefaceMap.getFamilyFilenames(typefaceName));
    }

    /**
     * Actual font registration. Takes care of specious warning emitted by iText registering
     * a TrueType collection (.ttc) file
     *
     * called from RegisterOneFont()
     *
     * @param fontFile the complete filename including the path
     */
    private void registerFont(String fontFile) {
        if (!fontFile.toLowerCase().endsWith(".ttc")) {
            FontFactory.register(fontFile);
            return;
        }

        // if the file is a TrueTypeCollection (.ttc), iText generates an error message
        // which it writes to stderr, and then registers the fonts. Since, there's no way
        // to prevent this unneeded message, we do the registration manually here to avoid
        // printing out the error message (about which the user can do nothing).

        String[] names;

        try {
            names = BaseFont.enumerateTTCNames(fontFile);
            if (names == null) {
                errTtcFileHandling(fontFile);
                return;
            }
        } catch (DocumentException de) {
            errTtcFileHandling(fontFile);
            return;
        } catch (IOException ioe) {
            errTtcFileHandling(fontFile);
            return;
        }

        for (int i = 0; i < names.length; i++) {
            FontFactory.register(fontFile + "," + i);
        }
    }

    /**
     * Unfortunately, iText does not pass along the exception if the file cannot be found.
     * Rather, iText rethrows the exception internally and prints the stack trace. So, the
     * only way to avoid this is to check for each file first before doing the registration.
     *
     * @param fontFile the file location and name
     * @param typeface the name of the typeface to register the font for
     */
    void registerOneFont(final String fontFile, final String typeface) {
        if (fontFile == null) {
            return;
        }

        File ff = new File(fontFile);
        if (ff.exists()) {
            registerFont(fontFile);
            gdd.log("Registered fonts for " + typeface + " in iText");
        } else {
            gdd.logWarning(
                    gdd.getLit("COULD_NOT_FIND_FONT") + " " + fontFile + " " + gdd.getLit("FROM_FONT_LIST") + ". ");
        }
    }

    // = = =  error messages, getters and setters = = = //

    void errFontNotFound(String typefaceName) {
        gdd.logWarning(gdd.getLit("COULD_NOT_FIND") + " " + typefaceName + " " + gdd.getLit("IN_FONT_REGISTER")
                + ". " + gdd.getLit("USING_TIMES_ROMAN") + ".");
    }

    void errTtcFileHandling(final String fontFile) {
        gdd.logWarning(gdd.getLit("ERROR.REGISTERING_TTC_FONT") + " " + fontFile);
    }
}