Java tutorial
/* * 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 - 2017 Hitachi Vantara and Contributors. All rights reserved. */ package org.pentaho.reporting.libraries.fonts.itext; import com.lowagie.text.DocumentException; import com.lowagie.text.pdf.BaseFont; import com.lowagie.text.pdf.FontMapper; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.pentaho.reporting.libraries.base.config.ExtendedConfiguration; import org.pentaho.reporting.libraries.base.util.StringUtils; import org.pentaho.reporting.libraries.fonts.FontMappingUtility; import org.pentaho.reporting.libraries.fonts.LibFontBoot; import org.pentaho.reporting.libraries.fonts.merge.CompoundFontRecord; import org.pentaho.reporting.libraries.fonts.registry.FontFamily; import org.pentaho.reporting.libraries.fonts.registry.FontRecord; import org.pentaho.reporting.libraries.fonts.registry.FontSource; import org.pentaho.reporting.libraries.fonts.truetype.TrueTypeFontRecord; import java.awt.*; import java.io.IOException; import java.util.HashMap; import java.util.Map; /** * iText font support. * * @author Thomas Morgner */ public class BaseFontSupport implements FontMapper { private static final Log logger = LogFactory.getLog(BaseFontSupport.class); /** * Storage for BaseFont objects created. */ private final Map baseFonts; private String defaultEncoding; private boolean useGlobalCache; private boolean embedFonts; private ITextFontRegistry registry; /** * Creates a new support instance. */ public BaseFontSupport(final ITextFontRegistry registry) { this(registry, "UTF-8"); } public BaseFontSupport(final ITextFontRegistry registry, final String defaultEncoding) { this.baseFonts = new HashMap(); this.registry = registry; this.defaultEncoding = defaultEncoding; final ExtendedConfiguration extendedConfig = LibFontBoot.getInstance().getExtendedConfig(); this.useGlobalCache = extendedConfig .getBoolProperty("org.pentaho.reporting.libraries.fonts.itext.UseGlobalFontCache"); } 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 iText-BaseFont for an font. If no basefont could be created, an BaseFontCreateException is thrown. * * @param logicalName the name of the font (null not permitted). * @param bold a flag indicating whether the font is rendered as bold font. * @param italic a flag indicating whether the font is rendered as italic or cursive font. * @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 BaseFont createBaseFont(final String logicalName, final boolean bold, final boolean italic, final String encoding, final boolean embedded) throws BaseFontCreateException { return createBaseFontRecord(logicalName, bold, italic, encoding, embedded).getBaseFont(); } /** * Creates a BaseFontRecord for an font. If no basefont could be created, an BaseFontCreateException is thrown. * * @param logicalName the name of the font (null not permitted). * @param bold a flag indicating whether the font is rendered as bold font. * @param italic a flag indicating whether the font is rendered as italic or cursive font. * @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 createBaseFontRecord(final String logicalName, final boolean bold, final boolean italic, String encoding, final boolean embedded) throws BaseFontCreateException { if (logicalName == null) { throw new NullPointerException("Font definition is null."); } if (encoding == null) { encoding = getDefaultEncoding(); } // use the Java logical font name to map to a predefined iText font. final String fontKey; if (FontMappingUtility.isCourier(logicalName)) { fontKey = "Courier"; } else if (FontMappingUtility.isSymbol(logicalName)) { fontKey = "Symbol"; } else if (FontMappingUtility.isSerif(logicalName)) { fontKey = "Times"; } else if (FontMappingUtility.isSansSerif(logicalName)) { // default, this catches Dialog and SansSerif fontKey = "Helvetica"; } else { fontKey = logicalName; } // 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 final String stringEncoding; if ("utf-8".equalsIgnoreCase(encoding)) { stringEncoding = "utf-8"; encoding = BaseFont.IDENTITY_H; } else if ("utf-16".equalsIgnoreCase(encoding)) { stringEncoding = "utf-16"; encoding = BaseFont.IDENTITY_H; } else { // 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"; } else { stringEncoding = encoding; } } try { final FontFamily registryFontFamily = registry.getFontFamily(fontKey); FontRecord registryFontRecord = null; if (registryFontFamily != null) { registryFontRecord = registryFontFamily.getFontRecord(bold, italic); if (registryFontRecord instanceof CompoundFontRecord) { final CompoundFontRecord cfr = (CompoundFontRecord) registryFontRecord; registryFontRecord = cfr.getBase(); } } if (registryFontRecord != null) { // Check, whether this is an built-in font. If not, then the record points to a file. if ((registryFontRecord instanceof ITextBuiltInFontRecord) == false) { boolean embeddedOverride = embedded; if (embedded == true && registryFontRecord instanceof FontSource) { final FontSource source = (FontSource) registryFontRecord; if (source.isEmbeddable() == false) { logger.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 { final ITextBuiltInFontRecord buildInFontRecord = (ITextBuiltInFontRecord) registryFontRecord; // So this is one of the built-in records. final String fontName = buildInFontRecord.getFullName(); // Alternative: No Registered TrueType font was found. OK; don't panic, // we try to create a font anyway.. BaseFontRecord fontRecord = getFromCache(fontName, encoding, embedded); if (fontRecord != null) { return fontRecord; } fontRecord = getFromCache(fontName, 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(fontName, stringEncoding, embedded, useGlobalCache, null, null); if (f != null) { fontRecord = new BaseFontRecord(fontName, false, embedded, f, bold, italic); putToCache(fontRecord); return fontRecord; } } } // If we got to this point, then the font was not recognized as any known font. We will fall back // to Helvetica instead .. } catch (Exception e) { if (logger.isDebugEnabled()) { logger.debug("BaseFont.createFont failed. Key = " + fontKey + ": " + e.getMessage(), e); } else if (logger.isWarnEnabled()) { logger.warn("BaseFont.createFont failed. Key = " + fontKey + ": " + e.getMessage(), e); } } // 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) { logger.warn("BaseFont.createFont for FALLBACK failed.", e); throw new BaseFontCreateException("Null font = " + fontKey); } throw new BaseFontCreateException("BaseFont creation failed, null font: " + fontKey); } // // /** // * // * @param fileName // * @param fontName iTexts idea of mixing font meta data with filenames // * @param encoding // * @param embedded // * @return // */ // private BaseFont loadFromLibLoader (final String fileName, // final String fontName, // final String encoding, // final boolean embedded) // { // final HashMap map = new HashMap(); // map.put(BaseFontResourceFactory.FONTNAME, fontName); // map.put(BaseFontResourceFactory.ENCODING, encoding); // map.put(BaseFontResourceFactory.EMBEDDED, new Boolean(embedded)); // map.put(ResourceKey.CONTENT_KEY, new File (fileName)); // // try // { // final Resource res = // getResourceManager().createDirectly(map, BaseFont.class); // return (BaseFont) res.getResource(); // } // catch (ResourceException e) // { // return null; // } // } /** * Creates a PDF font record from a true type font. * * @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 com.lowagie.text.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 { // check if this font is in the cache ... //Log.warn ("TrueTypeFontKey : " + fontKey + " Font: " + font.isItalic() + " Encoding: " // + encoding); final String rawFilename; if (fontRecord instanceof TrueTypeFontRecord) { final TrueTypeFontRecord ttfRecord = (TrueTypeFontRecord) fontRecord; if (ttfRecord.getCollectionIndex() >= 0) { rawFilename = ttfRecord.getFontSource() + ',' + ttfRecord.getCollectionIndex(); } else { rawFilename = ttfRecord.getFontSource(); } } else if (fontRecord instanceof FontSource) { final FontSource source = (FontSource) fontRecord; rawFilename = source.getFontSource(); } 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 fileName the physical filename name of the font file. * @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; } /** * 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 { return createBaseFont(font.getName(), font.isBold(), font.isItalic(), encoding, embed); } 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); boolean bold = false; boolean italic = false; if (StringUtils.endsWithIgnoreCase(logicalName, "bolditalic")) { bold = true; italic = true; } else if (StringUtils.endsWithIgnoreCase(logicalName, "bold")) { bold = true; } else if (StringUtils.endsWithIgnoreCase(logicalName, "italic")) { italic = true; } int style = Font.PLAIN; if (bold) { style |= Font.BOLD; } if (italic) { style |= Font.ITALIC; } return new Font(logicalName, style, size); } private String getFontName(final BaseFont font) { final String[][] names = font.getFullFontName(); final int nameCount = names.length; if (nameCount == 1) { return names[0][3]; } String nameExtr = null; for (int k = 0; k < nameCount; ++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]; } }