Package org.jfree.layouting.modules.output.pdf.itext

Source Code of org.jfree.layouting.modules.output.pdf.itext.BaseFontSupport

/**
* ===========================================
* LibLayout : a free Java layouting library
* ===========================================
*
* Project Info:  http://reporting.pentaho.org/liblayout/
*
* (C) Copyright 2006-2007, by Pentaho Corporation and Contributors.
*
* This library is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation;
* either version 2.1 of the License, or (at your option) any later version.
*
* This library 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.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
* ------------
* $Id: BaseFontSupport.java,v 1.6 2007/05/14 09:01:01 taqua Exp $
* ------------
* (C) Copyright 2006-2007, by Pentaho Corporation.
*/

package org.jfree.layouting.modules.output.pdf.itext;

import java.awt.Font;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import com.lowagie.text.DocumentException;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.FontMapper;
import org.jfree.fonts.registry.FontRecord;
import org.jfree.fonts.registry.FontSource;
import org.jfree.fonts.truetype.TrueTypeFontRecord;
import org.jfree.layouting.LibLayoutBoot;
import org.jfree.resourceloader.ResourceManager;
import org.jfree.util.ExtendedConfiguration;
import org.jfree.util.Log;
import org.jfree.util.StringUtils;

/**
* iText font support.
*
* @author Thomas Morgner
*/
public class BaseFontSupport implements FontMapper
{
  private static ResourceManager resourceManager;

  protected static synchronized ResourceManager getResourceManager ()
  {
    if (resourceManager == null)
    {
      resourceManager = new ResourceManager();
      resourceManager.registerDefaults();
      resourceManager.registerFactory(new BaseFontResourceFactory());
    }
    return resourceManager;
  }

  /**
   * Storage for BaseFont objects created.
   */
  private final Map baseFonts;

  private String defaultEncoding;

  private boolean useGlobalCache;
  private boolean embedFonts;
  private BaseFontFactory baseFontFactory;

  /**
   * Creates a new support instance.
   */
  public BaseFontSupport ()
  {
    this(BaseFont.IDENTITY_H);
  }

  public BaseFontSupport (final String defaultEncoding)
  {
    this.baseFonts = new HashMap();
    this.defaultEncoding = defaultEncoding;
    final ExtendedConfiguration extendedConfig = LibLayoutBoot.getInstance()
            .getExtendedConfig();
    this.useGlobalCache = extendedConfig.getBoolProperty
            ("org.jfree.layouting.modules.output.pdf.itext.UseGlobalFontCache");

    this.baseFontFactory = new BaseFontFactory();
    this.baseFontFactory.registerDefaultFontPath(defaultEncoding);
  }

  public String getDefaultEncoding ()
  {
    return defaultEncoding;
  }

  public void setDefaultEncoding (final String defaultEncoding)
  {
    if (defaultEncoding == null)
    {
      throw new NullPointerException("DefaultEncoding is null.");
    }
    this.defaultEncoding = defaultEncoding;
  }

  public boolean isEmbedFonts()
  {
    return embedFonts;
  }

  public void setEmbedFonts(final boolean embedFonts)
  {
    this.embedFonts = embedFonts;
  }

  /**
   * Close the font support.
   */
  public void close ()
  {
    this.baseFonts.clear();
  }

  /**
   * Creates a BaseFontRecord for an AWT font.  If no basefont could be created, an
   * OutputTargetException is thrown.
   *
   * @param font     the new font (null not permitted).
   * @param encoding the encoding.
   * @param embedded a flag indicating whether to embed the font glyphs in the generated
   *                 documents.
   * @return the base font record.
   *
   * @throws BaseFontCreateException if there was a problem setting the font for the
   *                                 target.
   */
  public BaseFontRecord createBaseFont (final String logicalName,
                                        final boolean bold,
                                        final boolean italic,
                                        final String encoding,
                                        final boolean embedded)
          throws BaseFontCreateException
  {
    if (logicalName == null)
    {
      throw new NullPointerException("Font definition is null.");
    }
    if (encoding == null)
    {
      throw new NullPointerException("Encoding is null");
    }

    final boolean builtInFont;
    final String fontKey;
    if (isCourier(logicalName))
    {
      fontKey = createCourierName(bold, italic);
      builtInFont = true;
    }
    else if (isSerif(logicalName))
    {
      fontKey = createSerifName(bold, italic);
      builtInFont = true;
    }
    else if (isSansSerif(logicalName))
    { // default, this catches Dialog and SansSerif
      fontKey = createSansSerifName(bold, italic);
      builtInFont = true;
    }
    else
    {
      fontKey = logicalName;
      builtInFont = false;
    }

    // iText uses some weird mapping between IDENTY-H/V and java supported encoding, IDENTITY-H/V is
    // used to recognize TrueType fonts, but the real JavaEncoding is used to encode Type1 fonts
    String stringEncoding = encoding;

    // Correct the encoding for truetype fonts
    // iText will crash if IDENTITY_H is used to create a base font ...
    if (encoding.equalsIgnoreCase(BaseFont.IDENTITY_H) ||
        encoding.equalsIgnoreCase(BaseFont.IDENTITY_V))
    {
      //changed to UTF to support all unicode characters ..
      stringEncoding = "utf-8";
    }

    try
    {
      final FontRecord registryFontRecord =
              baseFontFactory.getFontForName(fontKey, bold, italic);
      if (registryFontRecord != null && builtInFont == false)
      {
        // Try one: We do have a registered TrueType file. The cache is checked
        // first, creating fonts is expensive. If no cached version is found,
        // we create one..

        boolean embeddedOverride = embedded;
        if (embedded == true && registryFontRecord instanceof FontSource)
        {
          final FontSource source = (FontSource) registryFontRecord;
          if (source.isEmbeddable() == false)
          {
            Log.warn("License of font forbids embedded usage for font: " + fontKey);
            // strict mode here?
            embeddedOverride = false;
          }
        }
        final BaseFontRecord fontRecord = createFontFromTTF
                (registryFontRecord, bold, italic,
                        encoding, stringEncoding, embeddedOverride);
        if (fontRecord != null)
        {
          return fontRecord;
        }
      }
      else
      {
        // Alternative: No Registered TrueType font was found. OK; don't panic,
        // we try to create a font anyway..

        BaseFontRecord fontRecord = getFromCache(fontKey, encoding, embedded);
        if (fontRecord != null)
        {
          return fontRecord;
        }
        fontRecord = getFromCache(fontKey, stringEncoding, embedded);
        if (fontRecord != null)
        {
          return fontRecord;
        }

        // filename is null, so no ttf file registered for the fontname, maybe this is
        // one of the internal fonts ...
        final BaseFont f = BaseFont.createFont(fontKey, stringEncoding, embedded,
                useGlobalCache, null, null);
        if (f != null)
        {
          fontRecord = new BaseFontRecord(fontKey, false, embedded, f, bold, italic);
          putToCache(fontRecord);
          return fontRecord;
        }
      }
    }
    catch (Exception e)
    {
      if (Log.isDebugEnabled())
      {
        Log.debug(new Log.SimpleMessage
              ("BaseFont.createFont failed. Key = ", fontKey, ": ", e.getMessage()), e);
      }
      else if (Log.isWarningEnabled())
      {
        Log.warn(new Log.SimpleMessage
              ("BaseFont.createFont failed. Key = ", fontKey, ": ", e.getMessage()));
      }
    }
    // fallback .. use BaseFont.HELVETICA as default
    try
    {
      // check, whether HELVETICA is already created - yes, then return cached instance instead
      BaseFontRecord fontRecord = getFromCache(BaseFont.HELVETICA, stringEncoding, embedded);
      if (fontRecord != null)
      {
        // map all font references of the invalid font to the default font..
        // this might be not very nice, but at least the report can go on..
        putToCache(new BaseFontRecordKey(fontKey, encoding, embedded), fontRecord);
        return fontRecord;
      }

      // no helvetica created, so do this now ...
      final BaseFont f = BaseFont.createFont(BaseFont.HELVETICA, stringEncoding, embedded,
              useGlobalCache, null, null);
      if (f != null)
      {
        fontRecord = new BaseFontRecord
                (BaseFont.HELVETICA, false, embedded, f, bold, italic);
        putToCache(fontRecord);
        putToCache(new BaseFontRecordKey(fontKey, encoding, embedded), fontRecord);
        return fontRecord;
      }
    }
    catch (Exception e)
    {
      Log.warn("BaseFont.createFont for FALLBACK failed.", e);
      throw new BaseFontCreateException("Null font = " + fontKey);
    }
    throw new BaseFontCreateException("BaseFont creation failed, null font: " + fontKey);
  }

  /**
   * Creates a PDF font record from a true type font.
   *
   * @param font           the font.
   * @param filename       the filename.
   * @param encoding       the encoding.
   * @param stringEncoding the string encoding.
   * @param embedded       a flag indicating whether to embed the font glyphs in the
   *                       generated documents.
   * @return the PDF font record.
   *
   * @throws IOException       if there is an I/O problem.
   * @throws DocumentException if the BaseFont could not be created.
   */
  private BaseFontRecord createFontFromTTF (final FontRecord fontRecord,
                                            final boolean bold,
                                            final boolean italic,
                                            final String encoding,
                                            final String stringEncoding,
                                            final boolean embedded)
          throws DocumentException
  {
    final String rawFilename;
    if (fontRecord instanceof TrueTypeFontRecord)
    {
      final TrueTypeFontRecord ttfRecord = (TrueTypeFontRecord) fontRecord;
      if (ttfRecord.getCollectionIndex() >= 0)
      {
        rawFilename = ttfRecord.getFontFile() + "," + ttfRecord.getCollectionIndex();
      }
      else
      {
        rawFilename = ttfRecord.getFontFile();
      }
    }
    else if (fontRecord instanceof FontSource)
    {
      final FontSource source = (FontSource) fontRecord;
      rawFilename = source.getFontFile();
    }
    else
    {
      return null;
    }

    final String filename;
    // check, whether the the physical font does not provide some of the
    // required styles. We have to synthesize them, if neccessary
    if ((fontRecord.isBold() == false && bold) &&
        (fontRecord.isItalic() == false && italic))
    {
      filename = rawFilename + ",BoldItalic";
    }
    else if (fontRecord.isBold() == false && bold)
    {
      filename = rawFilename + ",Bold";
    }
    else if (fontRecord.isItalic() == false && italic)
    {
      filename = rawFilename + ",Italic";
    }
    else
    {
      filename = rawFilename;
    }

    final BaseFontRecord fontRec = getFromCache(filename, encoding, embedded);
    if (fontRec != null)
    {
      return fontRec;
    }

    BaseFont f;
    try
    {
      try
      {
        f = BaseFont.createFont(filename, encoding, embedded, false, null, null);
      }
      catch (DocumentException e)
      {
        f = BaseFont.createFont(filename, stringEncoding, embedded, false, null, null);
      }
    }
    catch(IOException ioe)
    {
      throw new DocumentException("Failed to read the font: " + ioe);
    }

    // no, we have to create a new instance
    final BaseFontRecord record = new BaseFontRecord
            (filename, true, embedded, f, fontRecord.isBold(), fontRecord.isItalic());
    putToCache(record);
    return record;
  }

  /**
   * Stores a record in the cache.
   *
   * @param record the record.
   */
  private void putToCache (final BaseFontRecord record)
  {
    final BaseFontRecordKey key = record.createKey();
    putToCache(key, record);
  }

  private void putToCache (final BaseFontRecordKey key, final BaseFontRecord record)
  {
    baseFonts.put(key, record);
  }

  /**
   * Retrieves a record from the cache.
   *
   * @param fontKey  the font key; never null.
   * @param encoding the encoding; never null.
   * @return the PDF font record or null, if not found.
   */
  private BaseFontRecord getFromCache (final String fileName,
                                       final String encoding,
                                       final boolean embedded)
  {
    final Object key = new BaseFontRecordKey(fileName, encoding, embedded);
    final BaseFontRecord r = (BaseFontRecord) baseFonts.get(key);
    if (r != null)
    {
      return r;
    }
    return null;
  }

  /**
   * Creates a sans-serif font name.
   *
   * @param bold   bold?
   * @param italic italic?
   * @return the font name.
   */
  private String createSansSerifName (final boolean bold, final boolean italic)
  {
    if (bold && italic)
    {
      return BaseFont.HELVETICA_BOLDOBLIQUE;
    }
    else if (bold)
    {
      return BaseFont.HELVETICA_BOLD;
    }
    else if (italic)
    {
      return BaseFont.HELVETICA_OBLIQUE;
    }
    else
    {
      return BaseFont.HELVETICA;
    }
  }

  /**
   * Creates a serif font name.
   *
   * @param bold   bold?
   * @param italic italic?
   * @return the font name.
   */
  private String createSerifName (final boolean bold, final boolean italic)
  {
    if (bold && italic)
    {
      return BaseFont.TIMES_BOLDITALIC;
    }
    else if (bold)
    {
      return BaseFont.TIMES_BOLD;
    }
    else if (italic)
    {
      return BaseFont.TIMES_ITALIC;
    }
    else
    {
      return BaseFont.TIMES_ROMAN;
    }
  }

  /**
   * Creates a courier font name.
   *
   * @param bold   bold?
   * @param italic italic?
   * @return the font name.
   */
  private String createCourierName (final boolean bold, final boolean italic)
  {
    if (bold && italic)
    {
      return BaseFont.COURIER_BOLDOBLIQUE;
    }
    else if (bold)
    {
      return BaseFont.COURIER_BOLD;
    }
    else if (italic)
    {
      return BaseFont.COURIER_OBLIQUE;
    }
    else
    {
      return BaseFont.COURIER;
    }
  }

  /**
   * Returns a BaseFont which can be used to represent the given AWT Font
   *
   * @param  font    the font to be converted
   * @return  a BaseFont which has similar properties to the provided Font
   */

  public BaseFont awtToPdf (final Font font)
  {
    // this has to be defined in the element, an has to set as a default...
    final boolean embed = isEmbedFonts();
    final String encoding = getDefaultEncoding();

    try
    {
      final BaseFontRecord record = createBaseFont
          (font.getName(), font.isBold(), font.isItalic(), encoding, embed);
      return record.getBaseFont();
    }
    catch (Exception e)
    {
      // unable to handle font creation exceptions properly, all we can
      // do is throw a runtime exception and hope the best ..
      throw new BaseFontCreateException("Unable to create font: " + font, e);
    }
  }

  /**
   * Returns an AWT Font which can be used to represent the given BaseFont
   *
   * @param  font    the font to be converted
   * @param  size    the desired point size of the resulting font
   * @return  a Font which has similar properties to the provided BaseFont
   */

  public Font pdfToAwt (final BaseFont font, final int size)
  {
    final String logicalName = getFontName(font);
    final int style;
    if (StringUtils.endsWithIgnoreCase(logicalName, "bolditalic"))
    {
      style = Font.ITALIC | Font.BOLD;
    }
    else if (StringUtils.endsWithIgnoreCase(logicalName, "bold"))
    {
      style = Font.BOLD;
    }
    else if (StringUtils.endsWithIgnoreCase(logicalName, "italic"))
    {
      style = Font.ITALIC;
    }
    else
    {
      style = Font.PLAIN;
    }

    return new Font(logicalName, style, size);
  }

  private String getFontName (final BaseFont font)
  {
    final String[][] names = font.getFullFontName();
    if (names.length == 1)
    {
      return names[0][3];
    }

    String nameExtr = null;
    for (int k = 0; k < names.length; ++k)
    {
      final String[] name = names[k];
      // Macintosh language english
      if ("1".equals(name[0]) && "0".equals(name[1]))
      {
        nameExtr = name[3];
      }
      // Microsoft language code for US-English ...
      else if ("1033".equals(name[2]))
      {
        nameExtr = name[3];
        break;
      }
    }

    if (nameExtr != null)
    {
      return nameExtr;
    }
    return names[0][3];
  }


  /**
   * Returns true if the logical font name is equivalent to 'SansSerif', and false
   * otherwise.
   *
   * @return true or false.
   */
  public boolean isSansSerif (final String fontName)
  {
    return StringUtils.startsWithIgnoreCase(fontName, "SansSerif")
            || StringUtils.startsWithIgnoreCase(fontName, "Dialog")
            || StringUtils.startsWithIgnoreCase(fontName, "SanSerif");
    // is it a bug? Somewhere in the JDK this name is used (typo, but heck, we accept it anyway).
  }

  /**
   * Returns true if the logical font name is equivalent to 'Courier', and false
   * otherwise.
   *
   * @return true or false.
   */
  public boolean isCourier (final String fontName)
  {
    return (StringUtils.startsWithIgnoreCase(fontName, "dialoginput")
            || StringUtils.startsWithIgnoreCase(fontName, "monospaced"));
  }

  /**
   * Returns true if the logical font name is equivalent to 'Serif', and false otherwise.
   *
   * @return true or false.
   */
  public boolean isSerif (final String fontName)
  {
    return (StringUtils.startsWithIgnoreCase(fontName, "serif"));
  }

}
TOP

Related Classes of org.jfree.layouting.modules.output.pdf.itext.BaseFontSupport

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.