Package org.pentaho.reporting.libraries.fonts.registry

Source Code of org.pentaho.reporting.libraries.fonts.registry.AbstractFontFileRegistry

/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2006 - 2013 Pentaho Corporation and Contributors.  All rights reserved.
*/

package org.pentaho.reporting.libraries.fonts.registry;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.libraries.base.config.Configuration;
import org.pentaho.reporting.libraries.base.util.ObjectUtilities;
import org.pentaho.reporting.libraries.base.util.StringUtils;
import org.pentaho.reporting.libraries.fonts.LibFontBoot;
import org.pentaho.reporting.libraries.fonts.encoding.EncodingRegistry;
import org.pentaho.reporting.libraries.resourceloader.ResourceData;
import org.pentaho.reporting.libraries.resourceloader.ResourceKey;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;

/**
* Creation-Date: 21.07.2007, 17:01:15
*
* @author Thomas Morgner
* @noinspection HardCodedStringLiteral
*/
public abstract class AbstractFontFileRegistry implements FontRegistry
{
  private static final Log logger = LogFactory.getLog(AbstractFontFileRegistry.class);

  private HashMap<String, FontFileRecord> seenFiles;
  private HashMap<String, DefaultFontFamily> fontFamilies;
  private HashMap<String, DefaultFontFamily> alternateFamilyNames;
  private HashMap<String, DefaultFontFamily> fullFontNames;

  protected AbstractFontFileRegistry()
  {
    seenFiles = new HashMap<String, FontFileRecord>();
    this.fontFamilies = new HashMap<String, DefaultFontFamily>();
    this.alternateFamilyNames = new HashMap<String, DefaultFontFamily>();
    this.fullFontNames = new HashMap<String, DefaultFontFamily>();
  }

  protected HashMap<String, FontFileRecord> getSeenFiles()
  {
    return seenFiles;
  }

  protected abstract FileFilter getFileFilter();

  public void initialize()
  {
    registerDefaultFontPath();
    final Configuration configuration = LibFontBoot.getInstance().getGlobalConfig();
    final Iterator extraDirIt =
        configuration.findPropertyKeys("org.pentaho.reporting.libraries.fonts.extra-font-dirs.");
    while (extraDirIt.hasNext())
    {
      final String extraDirKey = (String) extraDirIt.next();
      final String extraDir = configuration.getConfigProperty(extraDirKey);
      final File extraDirFile = new File(extraDir);
      try
      {
        if (extraDirFile.isDirectory())
        {
          registerFontPath(extraDirFile, getDefaultEncoding());
        }
      }
      catch (Exception e)
      {
        logger.warn("Extra font path " + extraDir + " could not be fully registered.", e);
      }
    }
  }

  protected String getDefaultEncoding()
  {
    return LibFontBoot.getInstance().getGlobalConfig().getConfigProperty
        ("org.pentaho.reporting.libraries.fonts.itext.FontEncoding", EncodingRegistry.getPlatformDefaultEncoding());
  }

  /**
   * Register os-specific font paths to the PDF-FontFactory. For unix-like operating systems, X11 is searched in
   * /usr/X11R6 and the default truetype fontpath is added. For windows the system font path is added (%windir%/fonts)
   */
  public void registerDefaultFontPath()
  {
    final String encoding = getDefaultEncoding();
    loadFromCache(encoding);

    final String osname = safeSystemGetProperty("os.name", "<protected by system security>");
    final String jrepath = safeSystemGetProperty("java.home", ".");
    final String fs = safeSystemGetProperty("file.separator", File.separator);

    logger.debug("Running on operating system: " + osname);
    logger.debug("Character encoding used as default: " + encoding);

    if (StringUtils.startsWithIgnoreCase(osname, "mac os x"))
    {
      final String userhome = safeSystemGetProperty("user.home", ".");
      logger.debug("Detected MacOS.");
      registerFontPath(new File(userhome + "/Library/Fonts"), encoding);
      registerFontPath(new File("/Library/Fonts"), encoding);
      registerFontPath(new File("/Network/Library/Fonts"), encoding);
      registerFontPath(new File("/System/Library/Fonts"), encoding);
    }
    else if (StringUtils.startsWithIgnoreCase(osname, "windows"))
    {
      registerWindowsFontPath(encoding);
    }
    else
    {
      logger.debug("Assuming unix like file structures");
      // Assume X11 is installed in the default location.
      registerFontPath(new File("/usr/X11R6/lib/X11/fonts"), encoding);
      registerFontPath(new File("/usr/share/fonts"), encoding);
    }
    registerFontPath(new File(jrepath, "lib" + fs + "fonts"), encoding);

    storeToCache(encoding);
    logger.info("Completed font registration.");
  }

  protected void registerPrimaryName(final String name, final DefaultFontFamily family)
  {
    this.fontFamilies.put(name, family);
  }

  protected void registerAlternativeName(final String name, final DefaultFontFamily family)
  {
    this.alternateFamilyNames.put(name, family);
  }

  protected void registerFullName(final String name, final DefaultFontFamily family)
  {
    this.fullFontNames.put(name, family);
  }

  protected DefaultFontFamily createFamily(final String name)
  {
    final DefaultFontFamily fontFamily = this.fontFamilies.get(name);
    if (fontFamily != null)
    {
      return fontFamily;
    }

    final DefaultFontFamily createdFamily = new DefaultFontFamily(name);
    this.fontFamilies.put(name, createdFamily);
    return createdFamily;
  }

  public String[] getRegisteredFamilies()
  {
    return fontFamilies.keySet().toArray(new String[fontFamilies.size()]);
  }

  public String[] getAllRegisteredFamilies()
  {
    return alternateFamilyNames.keySet().toArray(new String[alternateFamilyNames.size()]);
  }

  public FontFamily getFontFamily(final String name)
  {
    final FontFamily primary = this.fontFamilies.get(name);
    if (primary != null)
    {
      return primary;
    }
    final FontFamily secondary = this.alternateFamilyNames.get(name);
    if (secondary != null)
    {
      return secondary;
    }
    return this.fullFontNames.get(name);
  }

  protected void loadFromCache(final String encoding)
  {
    final String fileName = getCacheFileName();
    if (fileName == null)
    {
      return;
    }
    loadFromCache(encoding, fileName);
  }

  protected void populateFromCache(final HashMap<String, DefaultFontFamily> cachedFontFamilies,
                                   final HashMap<String, DefaultFontFamily> cachedFullFontNames,
                                   final HashMap<String, DefaultFontFamily> cachedAlternateNames)
  {
    this.fontFamilies.putAll(cachedFontFamilies);
    this.fullFontNames.putAll(cachedFullFontNames);
    this.alternateFamilyNames.putAll(cachedAlternateNames);
  }

  protected void loadFromCache(final String encoding, final String filename)
  {
    final ResourceManager resourceManager = new ResourceManager();
    final File location = createStorageLocation();
    if (location == null)
    {
      return;
    }
    final File ttfCache = new File(location, filename);
    try
    {
      final ResourceKey resourceKey = resourceManager.createKey(ttfCache);
      final ResourceData data = resourceManager.load(resourceKey);
      final InputStream stream = data.getResourceAsStream(resourceManager);

      final HashMap<String, FontFileRecord> cachedSeenFiles;
      final HashMap<String, DefaultFontFamily> cachedFontFamilies;
      final HashMap<String, DefaultFontFamily> cachedFullFontNames;
      final HashMap<String, DefaultFontFamily> cachedAlternateNames;

      try
      {
        final ObjectInputStream oin = new ObjectInputStream(stream);
        final Object[] cache = (Object[]) oin.readObject();
        if (cache.length != 5)
        {
          return;
        }
        if (ObjectUtilities.equal(encoding, cache[0]) == false)
        {
          return;
        }
        cachedSeenFiles = (HashMap<String, FontFileRecord>) cache[1];
        cachedFontFamilies = (HashMap<String, DefaultFontFamily>) cache[2];
        cachedFullFontNames = (HashMap<String, DefaultFontFamily>) cache[3];
        cachedAlternateNames = (HashMap<String, DefaultFontFamily>) cache[4];
      }
      finally
      {
        stream.close();
      }

      // next; check the font-cache for validity. We cannot cleanly remove
      // entries from the cache once they become invalid, so we have to rebuild
      // the cache from scratch, if it is invalid.
      //
      // This should not matter that much, as font installations do not happen
      // every day.
      if (isCacheValid(cachedSeenFiles))
      {
        this.getSeenFiles().putAll(cachedSeenFiles);
        populateFromCache(cachedFontFamilies, cachedFullFontNames, cachedAlternateNames);
      }
    }
    catch (final ClassNotFoundException cnfe)
    {
      // ignore the exception.
      logger.debug("Failed to restore the cache: Cache was created by a different version of LibFonts");
    }
    catch (Exception e)
    {
      logger.debug("Non-Fatal: Failed to restore the cache. The cache will be rebuilt.", e);
    }
  }

  protected String getCacheFileName()
  {
    return null;
  }

  protected void storeToCache(final String encoding)
  {
    final String cacheFileName = getCacheFileName();
    if (cacheFileName == null)
    {
      return;
    }

    final File location = createStorageLocation();
    if (location == null)
    {
      return;
    }
    location.mkdirs();
    if (location.exists() == false || location.isDirectory() == false)
    {
      return;
    }

    final File ttfCache = new File(location, cacheFileName);
    try
    {
      final FileOutputStream fout = new FileOutputStream(ttfCache);
      try
      {
        final Object[] map = new Object[5];
        map[0] = encoding;
        map[1] = getSeenFiles();
        map[2] = fontFamilies;
        map[3] = fullFontNames;
        map[4] = alternateFamilyNames;

        final ObjectOutputStream objectOut = new ObjectOutputStream(new BufferedOutputStream(fout));
        objectOut.writeObject(map);
        objectOut.close();
      }
      finally
      {
        try
        {
          fout.close();
        }
        catch (IOException e)
        {
          // ignore ..
          logger.debug("Failed to store cached font data", e);
        }
      }
    }
    catch (IOException e)
    {
      // should not happen
      logger.debug("Failed to store cached font data", e);
    }
  }

  /**
   * Registers the default windows font path. Once a font was found in the old seenFiles map and confirmed, that this
   * font still exists, it gets copied into the confirmedFiles map.
   *
   * @param encoding the default font encoding.
   */
  private void registerWindowsFontPath(final String encoding)
  {
    logger.debug("Found 'Windows' in the OS name, assuming DOS/Win32 structures");
    // Assume windows
    // If you are not using windows, ignore this. This just checks if a windows system
    // directory exist and includes a font dir.

    String fontPath = null;
    final String windirs = safeSystemGetProperty("java.library.path", null);
    final String fs = safeSystemGetProperty("file.separator", File.separator);

    if (windirs != null)
    {
      final StringTokenizer strtok = new StringTokenizer
          (windirs, safeSystemGetProperty("path.separator", File.pathSeparator));
      while (strtok.hasMoreTokens())
      {
        final String token = strtok.nextToken();

        if (StringUtils.endsWithIgnoreCase(token, "System32"))
        {
          // found windows folder ;-)
          final int lastBackslash = token.lastIndexOf(fs);
          if (lastBackslash != -1)
          {
            fontPath = token.substring(0, lastBackslash) + fs + "Fonts";
            break;
          }
          // try with forward slashs. Some systems may use the unix-semantics instead.
          // (Windows accepts both characters as path-separators for historical reasons)
          final int lastSlash = token.lastIndexOf('/');
          if (lastSlash != -1)
          {
            fontPath = token.substring(0, lastSlash) + lastSlash + "Fonts";
            break;
          }
        }
      }
    }
    logger.debug("Fonts located in \"" + fontPath + '\"');
    if (fontPath != null)
    {
      final File file = new File(fontPath);
      registerFontPath(file, encoding);
    }
  }

  /**
   * Register all fonts (*.ttf files) in the given path.
   *
   * @param file     the directory that contains the font files.
   * @param encoding the encoding for the given font.
   */
  public void registerFontPath(final File file, final String encoding)
  {
    if (file.exists() && file.isDirectory() && file.canRead())
    {
      final File[] files = file.listFiles(getFileFilter());
      final int fileCount = files.length;
      for (int i = 0; i < fileCount; i++)
      {
        final File currentFile = files[i];
        if (currentFile.isDirectory())
        {
          registerFontPath(currentFile, encoding);
        }
        else
        {
          if (isCached(currentFile) == false)
          {
            registerFontFile(currentFile, encoding);
          }
        }
      }
    }
  }


  protected boolean isCached(final File file)
  {
    try
    {
      final FontFileRecord stored = seenFiles.get(file.getCanonicalPath());
      if (stored == null)
      {
        return false;
      }

      final FontFileRecord rec = new FontFileRecord(file);
      if (stored.equals(rec) == false)
      {
        seenFiles.remove(file.getCanonicalPath());
        return false;
      }
      return true;
    }
    catch (IOException e)
    {
      return false;
    }
  }

  /**
   * Register the font (must end this *.ttf) to the FontFactory.
   *
   * @param filename the filename.
   * @param encoding the encoding.
   */
  public void registerFontFile(final String filename,
                               final String encoding)
  {
    final File file = new File(filename);
    registerFontFile(file, encoding);
  }

  public synchronized void registerFontFile(final File file, final String encoding)
  {
    if (getFileFilter().accept(file) && file.exists() && file.isFile() && file.canRead())
    {
      try
      {
        if (file.length() == 0)
        {
          logger.warn("Font " + file + " is invalid [zero size].");
          return;
        }
        if (addFont(file, encoding))
        {
          final FontFileRecord value = new FontFileRecord(file);
          seenFiles.put(file.getCanonicalPath(), value);
        }
      }
      catch (Exception e)
      {
        logger.warn("Font " + file + " is invalid. Message:" + e.getMessage(), e);
      }
    }
  }


  /**
   * Adds the fontname by creating the basefont object. This method tries to load the fonts as embeddable fonts, if this
   * fails, it repeats the loading with the embedded-flag set to false.
   *
   * @param font     the font file name.
   * @param encoding the encoding.
   * @return true, if registration was successful, false otherwise.
   * @throws java.io.IOException if the base font file could not be read.
   */
  protected abstract boolean addFont(final File font, final String encoding)
      throws IOException;


  protected String safeSystemGetProperty(final String name,
                                         final String defaultValue)
  {
    try
    {
      return System.getProperty(name, defaultValue);
    }
    catch (SecurityException se)
    {
      return defaultValue;
    }
  }


  protected boolean isCacheValid(final HashMap cachedSeenFiles)
  {
    final Iterator iterator = cachedSeenFiles.entrySet().iterator();
    while (iterator.hasNext())
    {
      final Map.Entry entry = (Map.Entry) iterator.next();
      final String fullFileName = (String) entry.getKey();
      final FontFileRecord fontFileRecord = (FontFileRecord) entry.getValue();
      final File fontFile = new File(fullFileName);
      if (fontFile.isFile() == false || fontFile.exists() == false)
      {
        return false;
      }
      if (fontFile.length() != fontFileRecord.getFileSize())
      {
        return false;
      }
      if (fontFile.lastModified() != fontFileRecord.getLastAccessTime())
      {
        return false;
      }
    }
    return true;
  }

  protected File createStorageLocation()
  {
    if ("true".equals(LibFontBoot.getInstance().getGlobalConfig().getConfigProperty
        ("org.pentaho.reporting.libraries.fonts.CacheFontRegistration")) == false)
    {
      return null;
    }

    final String homeDirectory = safeSystemGetProperty("user.home", null);
    if (homeDirectory == null)
    {
      return null;
    }
    final File homeFile = new File(homeDirectory);
    if (homeFile.isDirectory() == false)
    {
      return null;
    }
    return new File(homeFile, ".pentaho/caches/libfonts2");
  }
}
TOP

Related Classes of org.pentaho.reporting.libraries.fonts.registry.AbstractFontFileRegistry

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.