org.eclipse.birt.report.engine.layout.pdf.font.FontMappingManagerFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.birt.report.engine.layout.pdf.font.FontMappingManagerFactory.java

Source

/*******************************************************************************
 * Copyright (c) 2008 Actuate Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *  Actuate Corporation  - initial API and implementation
 *******************************************************************************/

package org.eclipse.birt.report.engine.layout.pdf.font;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.eclipse.birt.report.engine.util.SecurityUtil;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.osgi.framework.Bundle;

import com.lowagie.text.Font;
import com.lowagie.text.FontFactory;
import com.lowagie.text.pdf.BaseFont;

/**
 * FontMappingManagerFactory used to created all font mapping managers.
 * <p>
 * The user can define multiple font mapping configurations in
 * org.eclipse.birt.report.fonts fragment. The configuration has parent-child
 * relation ship. The configurations defined in the child configuration update
 * the one defined in the parent configurations. The inherit sequence of the
 * configurations are:
 * <li>format_os_language_country_variant</li>
 * <li>format_os_language_country</li>
 * <li>format_os_language</li>
 * <li>format_os</li>
 * <li>format</li>
 * <li>os_language_country_variant
 * <li>os_language_country</li>
 * <li>os_lanauge</li>
 * <li>os</li>
 * <li>fontsConfig</li>
 * </ul>
 * 
 * The OS name is getting from either osgi.os or os.name. If it is getting from
 * the osgi.os property, the names can be (see the
 * <code>Platform.knownOSValues()</code> ):
 * <ul>
 * <li>win32</li>
 * <li>linux</li>
 * <li>aix</li>
 * <li>solaris</li>
 * <li>hpux</li>
 * <li>qnx</li>
 * <li>macosx</li>
 * </ul>
 * 
 */
public class FontMappingManagerFactory {

    /** the logger logging the error, debug, warning messages. */
    protected static Logger logger = Logger.getLogger(FontConfigReader.class.getName());

    protected static FontMappingManagerFactory instance;

    public static synchronized FontMappingManagerFactory getInstance() {
        if (instance == null) {
            instance = new FontMappingManagerFactory();
        }
        return instance;
    }

    /**
     * the custom font config file path
     */
    private static URL customFontConfig = null;

    /**
     * all font paths registered by this factory
     */
    protected HashSet fontPathes = new HashSet();

    /**
     * font encodings, it is used by iText to load the Type1 fonts
     */
    protected HashMap fontEncodings = new HashMap();

    /**
     * all loaded configurations
     * 
     * the structure of the cache is:
     * <ul>
     * <li>key: configuration name</li>
     * <li>value: FontMappingConfig</li>
     * </ul>
     */
    protected HashMap cachedConfigs = new HashMap();

    /**
     * all created mapping managers.
     * 
     * <p>
     * The cache has two kind keys:
     * <p>
     * cached by the font mapping config
     * <ul>
     * <li>key: FontMappingConfig</li>
     * <li>value: each value is a HashMap
     * <ul>
     * <li>key: String[] sequence</li>
     * <li>value: FontMappingManager</li>
     * </ul>
     * </li>
     * </ul>
     * 
     * cached by the format.
     * <ul>
     * <li>key: format</li>
     * <li>value: HashMap
     * <ul>
     * <li>key: locale</li>
     * <li>value: FontMappingManager</li>
     * </ul>
     * </li>
     * </ul>
     * 
     */
    protected HashMap cachedManagers = new HashMap();

    protected FontMappingManagerFactory() {
        // Register java fonts.
        registerJavaFonts();

        // register the embedded font directories.
        String embeddedFonts = getEmbededFontPath();
        if (embeddedFonts != null) {
            registerFontPath(embeddedFonts);
        }

    }

    public synchronized FontMappingManager getFontMappingManager(String format, Locale locale) {
        HashMap managers = (HashMap) cachedManagers.get(format);
        if (managers == null) {
            managers = new HashMap();
            cachedManagers.put(format, managers);
        }
        FontMappingManager manager = (FontMappingManager) managers.get(locale);
        if (manager == null) {
            manager = createFontMappingManager(format, locale);
            managers.put(locale, manager);
        }
        return manager;
    }

    public FontMappingManager createFontMappingManager(FontMappingConfig config, Locale locale) {
        // Register the fonts defined in JRE fonts directory.
        registerJavaFonts();

        // register the fonts defined in the configuration
        Iterator iter = config.fontPaths.iterator();
        while (iter.hasNext()) {
            String fontPath = (String) iter.next();
            if (!fontPathes.contains(fontPath)) {
                fontPathes.add(fontPath);
                registerFontPath(fontPath);
            }
        }
        // add the font encodings to the global encoding
        fontEncodings.putAll(config.fontEncodings);

        return new FontMappingManager(this, null, config, locale);
    }

    public static void setCustomFontConfig(final URL customFontConfig) {
        FontMappingManagerFactory.customFontConfig = customFontConfig;
    }

    private void registerJavaFonts() {
        AccessController.doPrivileged(new PrivilegedAction<Object>() {

            public Object run() {
                String javaHome = System.getProperty("java.home");
                String fontsFolder = javaHome + File.separatorChar + "lib" + File.separatorChar + "fonts";
                FontFactory.registerDirectory(fontsFolder);
                return null;
            }
        });
    }

    protected FontMappingManager createFontMappingManager(String format, Locale locale) {
        String formatString = format.toLowerCase();
        // we have max 19 configs
        String[] configNames = new String[19];
        int count = 0;
        String osgiName = getOSGIOSName();
        String osName = getOSName();
        String language = locale.getLanguage();
        String country = locale.getCountry();
        String variant = locale.getVariant();

        StringBuffer sb = new StringBuffer();
        // fontsConfig.xml
        configNames[count++] = sb.append(CONFIG_NAME).toString();
        // fontsConfig_<osgi-os>.xml
        if (osgiName != null) {
            configNames[count++] = sb.append('_').append(osgiName).toString();
            configNames[count++] = sb.append('_').append(language).toString();
            configNames[count++] = sb.append('_').append(country).toString();
            configNames[count++] = sb.append('_').append(variant).toString();
        }
        // fontsConfig_<java-os>.xml
        if (osName != null && !osName.equals(osgiName)) {
            sb.setLength(0);
            sb.append(CONFIG_NAME);
            configNames[count++] = sb.append('_').append(osName).toString();
            configNames[count++] = sb.append('_').append(language).toString();
            configNames[count++] = sb.append('_').append(country).toString();
            configNames[count++] = sb.append('_').append(variant).toString();
        }
        sb.setLength(0);
        // fontsConfig_format_<osgi-os>
        configNames[count++] = sb.append(CONFIG_NAME).append('_').append(formatString).toString();
        if (osgiName != null) {
            configNames[count++] = sb.append('_').append(osgiName).toString();
            configNames[count++] = sb.append('_').append(language).toString();
            configNames[count++] = sb.append('_').append(country).toString();
            configNames[count++] = sb.append('_').append(variant).toString();
        }
        // fongsConfig_format_<java-os>
        if (osName != null && !osName.equals(osgiName)) {
            sb.setLength(0);
            sb.append(CONFIG_NAME).append('_').append(formatString);
            configNames[count++] = sb.append('_').append(osName).toString();
            configNames[count++] = sb.append('_').append(language).toString();
            configNames[count++] = sb.append('_').append(country).toString();
            configNames[count++] = sb.append('_').append(variant).toString();
        }

        FontMappingManager manager = null;
        for (int i = 0; i < count; i++) {
            FontMappingConfig config = loadFontMappingConfig(configNames[i]);
            if (config != null) {
                manager = createFontMappingManager(manager, config, locale);
            }
        }

        // custom font configuration
        FontMappingConfig customConfig = loadCustomFontConfig();
        if (customConfig != null) {
            manager = createFontMappingManager(manager, customConfig, locale);
        }
        return manager;
    }

    protected FontMappingManager createFontMappingManager(FontMappingManager parent, FontMappingConfig config,
            Locale locale) {
        HashMap managers = (HashMap) cachedManagers.get(config);
        if (managers == null) {
            managers = new HashMap();
            cachedManagers.put(config, managers);
        }
        FontMappingManager manager = (FontMappingManager) managers.get(locale);
        if (manager == null) {
            manager = new FontMappingManager(this, parent, config, locale);
            managers.put(locale, manager);
        }
        return manager;
    }

    static final String CONFIG_NAME = "fontsConfig";

    private String getOSName() {
        String osName = SecurityUtil.getSystemProperty("os.name");
        if (osName != null) {
            return osName.replace(' ', '_');
        }
        return null;
    }

    private String getOSGIOSName() {
        String osName = org.eclipse.birt.core.framework.Platform.getOS();
        if (Platform.OS_UNKNOWN.equals(osName)) {
            return null;
        }
        return osName;
    }

    protected FontMappingConfig getFontMappingConfig(String configName) {
        FontMappingConfig config = (FontMappingConfig) cachedConfigs.get(configName);
        if (config == null) {
            if (!cachedConfigs.containsKey(configName)) {
                config = loadFontMappingConfig(configName);
                cachedConfigs.put(configName, config);
            }
        }
        return config;
    }

    /**
     * load the configuration file.
     * 
     * @param configName
     */
    protected FontMappingConfig loadFontMappingConfig(String configName) {
        // try to load the format specific configuration
        URL url = getConfigURL(configName);
        if (url != null) {
            try {
                return loadFontMappingConfig(url);
            } catch (Exception ex) {
                logger.log(Level.WARNING, configName + ":" + ex.getMessage(), ex);
            }
        }
        return null;
    }

    /**
     * load the custom font config file.
     */
    protected FontMappingConfig loadCustomFontConfig() {
        // try to load the format specific configuration
        try {
            return loadFontMappingConfig(customFontConfig);
        } catch (Exception ex) {
            logger.log(Level.WARNING, customFontConfig + ":" + ex.getMessage(), ex);
        }
        return null;
    }

    /**
     * load the configuration file.
     * 
     * @param url
     */
    protected FontMappingConfig loadFontMappingConfig(URL url) throws Exception {
        if (url != null) {
            long start = System.currentTimeMillis();
            FontMappingConfig config = new FontConfigReader().parseConfig(url);
            long end = System.currentTimeMillis();
            logger.info("load font config in " + url + " cost " + (end - start) + "ms");
            if (config != null) {
                // try to load the font in the fontPaths
                Iterator iter = config.fontPaths.iterator();
                while (iter.hasNext()) {
                    String fontPath = (String) iter.next();
                    if (!fontPathes.contains(fontPath)) {
                        fontPathes.add(fontPath);
                        registerFontPath(fontPath);
                    }
                }
                // add the font encodings to the global encoding
                fontEncodings.putAll(config.fontEncodings);
                return config;
            }
        }
        return null;
    }

    protected URL getConfigURL(String configName) {
        // try to load the format specific configuration
        String fileName = configName + ".xml";
        Bundle bundle = Platform.getBundle("org.eclipse.birt.report.engine.fonts"); //$NON-NLS-1$
        if (bundle != null) {
            return bundle.getEntry(fileName);
        }
        return getClass().getClassLoader().getResource(fileName);
    }

    /**
     * All generated composite fonts.
     * 
     * <p>
     * composite font are generated by the composite font configuration and the
     * search sequence. Each composite font also contains a parent.
     * 
     * <p>
     * the cache structures are:
     * <ul>
     * <li>key: composite font configuration</li>
     * <li>value: HashMap which contains:
     * <ul>
     * <li>key: String[] search sequence</li>
     * <li>value: Composite font object</li>
     * </ul>
     * </li>
     * </ul>
     */
    HashMap cachedCompositeFonts = new HashMap();

    CompositeFont createCompositeFont(FontMappingManager manager, CompositeFontConfig fontConfig,
            String[] sequence) {
        HashMap fonts = (HashMap) cachedCompositeFonts.get(fontConfig);
        if (fonts == null) {
            fonts = new HashMap();
            cachedCompositeFonts.put(fontConfig, fonts);
        }
        CompositeFont font = (CompositeFont) fonts.get(sequence);
        if (font == null) {
            font = new CompositeFont(manager, fontConfig, sequence);
            fonts.put(sequence, font);
        }
        return font;
    }

    private HashMap baseFonts = new HashMap();

    /**
     * Creates iText BaseFont with the given font family name.
     * 
     * @param ffn
     *            the specified font family name.
     * @return the created BaseFont.
     */
    public BaseFont createFont(String familyName, int fontStyle) {
        String key = familyName + fontStyle;
        BaseFont bf = null;
        synchronized (baseFonts) {
            if (baseFonts.containsKey(key)) {
                bf = (BaseFont) baseFonts.get(key);
            } else {
                try {
                    String fontEncoding = (String) fontEncodings.get(familyName);
                    if (fontEncoding == null) {
                        fontEncoding = BaseFont.IDENTITY_H;
                    }
                    bf = FontFactory.getFont(familyName, fontEncoding, BaseFont.EMBEDDED, 14, fontStyle)
                            .getBaseFont();
                } catch (Throwable de) {
                    logger.log(Level.WARNING, de.getMessage(), de);
                }
                baseFonts.put(key, bf);
            }
            if (bf == null && fontStyle != Font.NORMAL) {
                return createFont(familyName, Font.NORMAL);
            }
        }
        return bf;
    }

    private static void registerFontPath(final String fontPath) {
        AccessController.doPrivileged(new PrivilegedAction<Object>() {

            public Object run() {
                long start = System.currentTimeMillis();
                File file = new File(fontPath);
                if (file.exists()) {
                    if (file.isDirectory()) {
                        FontFactory.registerDirectory(fontPath);
                    } else {
                        FontFactory.register(fontPath);
                    }
                }
                long end = System.currentTimeMillis();
                logger.info("register fonts in " + fontPath + " cost:" + (end - start) + "ms");
                return null;
            }
        });
    }

    protected String getEmbededFontPath() {
        Bundle bundle = Platform.getBundle("org.eclipse.birt.report.engine.fonts"); //$NON-NLS-1$
        if (bundle == null) {
            return null;
        }
        Path path = new Path("/fonts"); //$NON-NLS-1$

        URL fileURL = FileLocator.find(bundle, path, null);
        if (null == fileURL)
            return null;
        String fontPath = null;
        try {
            // 171369 patch provided by Arne Degenring <public@degenring.de>
            fontPath = FileLocator.toFileURL(fileURL).getPath();
            if (fontPath != null && fontPath.length() >= 3 && fontPath.charAt(2) == ':') {
                // truncate the first '/';
                return fontPath.substring(1);
            } else {
                return fontPath;
            }
        } catch (IOException ioe) {
            logger.log(Level.WARNING, ioe.getMessage(), ioe);
            return null;
        }
    }
}