net.sf.jasperreports.engine.fonts.FontUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jasperreports Show documentation
Show all versions of jasperreports Show documentation
Free Java Reporting Library
/*
* JasperReports - Free Java Reporting Library.
* Copyright (C) 2001 - 2019 TIBCO Software Inc. All rights reserved.
* http://www.jaspersoft.com
*
* Unless you have purchased a commercial license agreement from Jaspersoft,
* the following license terms apply:
*
* This program is part of JasperReports.
*
* JasperReports 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 3 of the License, or
* (at your option) any later version.
*
* JasperReports 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 JasperReports. If not, see .
*/
package net.sf.jasperreports.engine.fonts;
import java.awt.Font;
import java.awt.font.TextAttribute;
import java.text.AttributedCharacterIterator.Attribute;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import net.sf.jasperreports.engine.JRFont;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.JasperReportsContext;
import net.sf.jasperreports.engine.util.JRFontNotFoundException;
import net.sf.jasperreports.engine.util.JRGraphEnvInitializer;
import net.sf.jasperreports.engine.util.JRTextAttribute;
/**
* @author Teodor Danciu ([email protected])
*/
public final class FontUtil
{
private static final Log log = LogFactory.getLog(FontUtil.class);
public static final String EXCEPTION_MESSAGE_KEY_NULL_FONT = "engine.fonts.null.font";
public static final String EXCEPTION_MESSAGE_KEY_FONT_SET_FAMILY_NOT_FOUND = "util.font.set.family.not.found";
private JasperReportsContext jasperReportsContext;
/**
*
*/
private FontUtil(JasperReportsContext jasperReportsContext)
{
this.jasperReportsContext = jasperReportsContext;
}
/**
*
*/
public static FontUtil getInstance(JasperReportsContext jasperReportsContext)
{
return new FontUtil(jasperReportsContext);
}
/**
*.
*/ //FIXMECONTEXT this should no longer be a thread local
private static final InheritableThreadLocal> threadMissingFontsCache = new InheritableThreadLocal>()
{
@Override
protected Set initialValue() {
return new HashSet();
}
};
/**
*
*/
public static void copyNonNullOwnProperties(JRFont srcFont, JRFont destFont)
{
if(srcFont != null && destFont != null)
{
if (srcFont.getOwnFontName() != null)
{
destFont.setFontName(srcFont.getOwnFontName());
}
if (srcFont.isOwnBold() != null)
{
destFont.setBold(srcFont.isOwnBold());
}
if (srcFont.isOwnItalic() != null)
{
destFont.setItalic(srcFont.isOwnItalic());
}
if (srcFont.isOwnUnderline() != null)
{
destFont.setUnderline(srcFont.isOwnUnderline());
}
if (srcFont.isOwnStrikeThrough() != null)
{
destFont.setStrikeThrough(srcFont.isOwnStrikeThrough());
}
if (srcFont.getOwnFontsize() != null)
{
destFont.setFontSize(srcFont.getOwnFontsize());
}
if (srcFont.getOwnPdfFontName() != null)
{
destFont.setPdfFontName(srcFont.getOwnPdfFontName());
}
if (srcFont.getOwnPdfEncoding() != null)
{
destFont.setPdfEncoding(srcFont.getOwnPdfEncoding());
}
if (srcFont.isOwnPdfEmbedded() != null)
{
destFont.setPdfEmbedded(srcFont.isOwnPdfEmbedded());
}
}
}
/**
*
*/
public Map getAttributesWithoutAwtFont(Map attributes, JRFont font)
{
attributes.put(TextAttribute.FAMILY, font.getFontName());
attributes.put(TextAttribute.SIZE, font.getFontsize());
if (font.isBold())
{
attributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD);
}
if (font.isItalic())
{
attributes.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE);
}
if (font.isUnderline())
{
attributes.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
}
if (font.isStrikeThrough())
{
attributes.put(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON);
}
attributes.put(JRTextAttribute.PDF_FONT_NAME, font.getPdfFontName());
attributes.put(JRTextAttribute.PDF_ENCODING, font.getPdfEncoding());
if (font.isPdfEmbedded())
{
attributes.put(JRTextAttribute.IS_PDF_EMBEDDED, Boolean.TRUE);
}
return attributes;
}
/**
* Returns font information containing the font family, font face and font style.
*
* @param name the font family or font face name
* @param ignoreCase the flag to specify if family names or face names are searched by ignoring case or not
* @param locale the locale
* @return a font info object
*/
public FontInfo getFontInfo(String name, boolean ignoreCase, Locale locale)
{
FontInfo awtFamilyMatchFontInfo = null;
//FIXMEFONT do some cache
List families = jasperReportsContext.getExtensions(FontFamily.class);
for (Iterator itf = families.iterator(); itf.hasNext();)
{
FontFamily family = itf.next();
if (locale == null || family.supportsLocale(locale))
{
if (equals(name, family.getName(), ignoreCase))
{
return new FontInfo(family, null, Font.PLAIN);
}
FontFace face = family.getNormalFace();
if (face != null)
{
if (equals(name, face.getName(), ignoreCase))
{
return new FontInfo(family, face, Font.PLAIN);
}
else if (
awtFamilyMatchFontInfo == null
&& face.getFont() != null
&& equals(name, face.getFont().getFamily(), ignoreCase)
)
{
awtFamilyMatchFontInfo = new FontInfo(family, face, Font.PLAIN);
}
}
face = family.getBoldFace();
if (face != null)
{
if (equals(name, face.getName(), ignoreCase))
{
return new FontInfo(family, face, Font.BOLD);
}
else if (
awtFamilyMatchFontInfo == null
&& face.getFont() != null
&& equals(name, face.getFont().getFamily(), ignoreCase)
)
{
awtFamilyMatchFontInfo = new FontInfo(family, face, Font.BOLD);
}
}
face = family.getItalicFace();
if (face != null)
{
if (equals(name, face.getName(), ignoreCase))
{
return new FontInfo(family, face, Font.ITALIC);
}
else if (
awtFamilyMatchFontInfo == null
&& face.getFont() != null
&& equals(name, face.getFont().getFamily(), ignoreCase)
)
{
awtFamilyMatchFontInfo = new FontInfo(family, face, Font.ITALIC);
}
}
face = family.getBoldItalicFace();
if (face != null)
{
if (equals(name, face.getName(), ignoreCase))
{
return new FontInfo(family, face, Font.BOLD | Font.ITALIC);
}
else if (
awtFamilyMatchFontInfo == null
&& face.getFont() != null
&& equals(name, face.getFont().getFamily(), ignoreCase)
)
{
awtFamilyMatchFontInfo = new FontInfo(family, face, Font.BOLD | Font.ITALIC);
}
}
}
}
return awtFamilyMatchFontInfo;
}
private static boolean equals(String value1, String value2, boolean ignoreCase)
{
return ignoreCase ? value1.equalsIgnoreCase(value2) : value1.equals(value2);
}
/**
* Returns font information containing the font family, font face and font style, searching for names case sensitive.
*
* @param name the font family or font face name
* @param locale the locale
* @return a font info object
*/
public FontInfo getFontInfo(String name, Locale locale)
{
return getFontInfo(name, false, locale);
}
/**
* Returns font information containing the font family, font face and font style, searching for names case insensitive.
*
* @param name the font family or font face name
* @param locale the locale
* @return a font info object
* @deprecated Replaced by {@link #getFontInfo(String, boolean, Locale)}.
*/
public FontInfo getFontInfoIgnoreCase(String name, Locale locale)
{
return getFontInfo(name, true, locale);
}
public FontSetInfo getFontSetInfo(String name, Locale locale, boolean ignoreMissingFonts)
{
//FIXMEFONT do some cache
List allFontFamilies = jasperReportsContext.getExtensions(FontFamily.class);
HashMap fontFamilies = new HashMap(allFontFamilies.size() * 4 / 3, .75f);
for (FontFamily family : allFontFamilies)
{
if (family.getName() != null
&& (locale == null || family.supportsLocale(locale)))
{
fontFamilies.put(family.getName(), family);
}
}
Map setFamilyInfos = new LinkedHashMap();
List allSets = jasperReportsContext.getExtensions(FontSet.class);
FontSet foundFontSet = null;
FontSetFamilyInfo primaryFamily = null;
for (FontSet fontSet : allSets)
{
if (name.equals(fontSet.getName()))
{
foundFontSet = fontSet;
List setFamilies = fontSet.getFamilies();
for (FontSetFamily fontSetFamily : setFamilies)
{
FontFamily fontFamily = fontFamilies.get(fontSetFamily.getFamilyName());
if (fontFamily != null)
{
FontSetFamilyInfo familyInfo = new FontSetFamilyInfo(fontSetFamily, fontFamily);
setFamilyInfos.put(fontSetFamily.getFamilyName(), familyInfo);
if (fontSetFamily.isPrimary())
{
primaryFamily = familyInfo;
}
}
else
{
if (ignoreMissingFonts)
{
if (log.isWarnEnabled())
{
log.warn("Font family " + fontSetFamily.getFamilyName()
+ " was not found for font set " + name);
}
}
else
{
throw new JRRuntimeException(EXCEPTION_MESSAGE_KEY_FONT_SET_FAMILY_NOT_FOUND,
new Object[]{fontSetFamily.getFamilyName(), name});
}
}
}
}
}
if (foundFontSet == null)
{
return null;
}
//TODO lucianc handle sets with no families
List familyInfoList = new ArrayList(setFamilyInfos.values());
if (primaryFamily == null && !familyInfoList.isEmpty())
{
primaryFamily = familyInfoList.get(0);
}
return new FontSetInfo(foundFontSet, primaryFamily, familyInfoList);
}
public String getExportFontFamily(String name, Locale locale, String exporterKey)
{
//FIXMEFONT do some cache
FontInfo fontInfo = getFontInfo(name, locale);
if (fontInfo != null)
{
FontFamily family = fontInfo.getFontFamily();
String exportFont = family.getExportFont(exporterKey);
return exportFont == null ? name : exportFont;
}
FontSetInfo fontSetInfo = getFontSetInfo(name, locale, true);
if (fontSetInfo != null)
{
String exportFont = fontSetInfo.getFontSet().getExportFont(exporterKey);
//TODO also look at the primary family?
return exportFont == null ? name : exportFont;
}
return name;
}
/**
* Returns the font family names available through extensions, in alphabetical order.
*/
public Collection getFontFamilyNames()
{
TreeSet familyNames = new TreeSet();//FIXMEFONT use collator for order?
//FIXMEFONT do some cache
collectFontFamilyNames(familyNames);
return familyNames;
}
protected void collectFontFamilyNames(Collection names)
{
List families = jasperReportsContext.getExtensions(FontFamily.class);
for (Iterator itf = families.iterator(); itf.hasNext();)
{
FontFamily family = itf.next();
if (family.isVisible())
{
names.add(family.getName());
}
}
}
/**
* Returns the font names available through extensions, in alphabetical order.
*
* @return the list of font names provided by extensions
*/
public Collection getFontNames()
{
TreeSet fontNames = new TreeSet();//FIXMEFONT use collator for order?
//FIXMEFONT do some cache
collectFontFamilyNames(fontNames);
collectFontSetNames(fontNames);
return fontNames;
}
protected void collectFontSetNames(Collection names)
{
List fontSets = jasperReportsContext.getExtensions(FontSet.class);
for (Iterator itf = fontSets.iterator(); itf.hasNext();)
{
FontSet fontSet = itf.next();
names.add(fontSet.getName());
}
}
/**
* @deprecated Replaced by {@link #getAwtFontFromBundles(String, int, float, Locale, boolean)}.
*/
public Font getAwtFontFromBundles(String name, int style, int size, Locale locale, boolean ignoreMissingFont)
{
return getAwtFontFromBundles(name, style, (float)size, locale, ignoreMissingFont);
}
/**
* Calls {@link #getAwtFontFromBundles(boolean, String, int, float, Locale, boolean)} with the ignoreCase parameter set to false.
*/
public Font getAwtFontFromBundles(String name, int style, float size, Locale locale, boolean ignoreMissingFont)
{
return getAwtFontFromBundles(false, name, style, size, locale, ignoreMissingFont);
}
/**
*
*/
public Font getAwtFontFromBundles(boolean ignoreCase, String name, int style, float size, Locale locale, boolean ignoreMissingFont)
{
Font awtFont = null;
FontInfo fontInfo = ignoreCase ? getFontInfoIgnoreCase(name, locale) : getFontInfo(name, locale);
if (fontInfo != null)
{
awtFont = getAwtFont(fontInfo, style, size, ignoreMissingFont);
}
return awtFont;
}
protected Font getAwtFont(FontInfo fontInfo, int style, float size, boolean ignoreMissingFont)
{
@SuppressWarnings("unused")
int faceStyle = Font.PLAIN;
FontFamily family = fontInfo.getFontFamily();
FontFace face = fontInfo.getFontFace();
if (face == null)
{
if (((style & Font.BOLD) > 0) && ((style & Font.ITALIC) > 0))
{
face = family.getBoldItalicFace();
faceStyle = Font.BOLD | Font.ITALIC;
}
if ((face == null || face.getFont() == null) && ((style & Font.BOLD) > 0))
{
face = family.getBoldFace();
faceStyle = Font.BOLD;
}
if ((face == null || face.getFont() == null) && ((style & Font.ITALIC) > 0))
{
face = family.getItalicFace();
faceStyle = Font.ITALIC;
}
if (face == null || face.getFont() == null)
{
face = family.getNormalFace();
faceStyle = Font.PLAIN;
}
// if (face == null)
// {
// throw new JRRuntimeException("Font family '" + family.getName() + "' does not have the normal font face.");
// }
}
else
{
faceStyle = fontInfo.getStyle();
}
Font awtFont;
if (face == null || face.getFont() == null)
{
// None of the family's font faces was found to match, neither by name, nor by style and the font family does not even specify a normal face font.
// In such case, we take the family name and consider it as JVM available font name.
checkAwtFont(family.getName(), ignoreMissingFont);
awtFont = new Font(family.getName(), style, (int)size);
awtFont = awtFont.deriveFont(size);
}
else
{
awtFont = face.getFont();
if (awtFont == null)
{
throw
new JRRuntimeException(
EXCEPTION_MESSAGE_KEY_NULL_FONT,
new Object[]{face.getName(), family.getName()});
}
//deriving with style and size in one call, because deriving with size and then style loses the float size
awtFont = awtFont.deriveFont(style, size);// & ~faceStyle);
}
return awtFont;
}
public Font getAwtFontFromBundles(AwtFontAttribute fontAttribute, int style, float size, Locale locale, boolean ignoreMissingFont)
{
FontInfo fontInfo = fontAttribute.getFontInfo();
if (fontInfo == null)
{
fontInfo = getFontInfo(fontAttribute.getFamily(), locale);
}
Font awtFont = null;
if (fontInfo != null)
{
awtFont = getAwtFont(fontInfo, style, size, ignoreMissingFont);
}
return awtFont;
}
/**
*
*/ //FIXMECONTEXT check how to make this cache effective again
public void resetThreadMissingFontsCache()
{
threadMissingFontsCache.set(new HashSet());
}
/**
*
*/
public void checkAwtFont(String name, boolean ignoreMissingFont)
{
if (!JRGraphEnvInitializer.isAwtFontAvailable(name))
{
if (ignoreMissingFont)
{
Set missingFontNames = threadMissingFontsCache.get();
if (!missingFontNames.contains(name))
{
missingFontNames.add(name);
if (log.isWarnEnabled())
{
log.warn("Font '" + name + "' is not available to the JVM. For more details, see http://jasperreports.sourceforge.net/api/net/sf/jasperreports/engine/util/JRFontNotFoundException.html");
}
}
}
else
{
throw new JRFontNotFoundException(name);
}
}
}
/**
* Returns a java.awt.Font instance by converting a JRFont instance.
* Mostly used in combination with third-party visualization packages such as JFreeChart (for chart themes).
* Unless the font parameter is null, this method always returns a non-null AWT font, regardless whether it was
* found in the font extensions or not. This is because we do need a font to draw with and there is no point
* in raising a font missing exception here, as it is not JasperReports who does the drawing.
*/
public Font getAwtFont(JRFont font, Locale locale)
{
if (font == null)
{
return null;
}
// ignoring missing font as explained in the Javadoc
Font awtFont =
getAwtFontFromBundles(
font.getFontName(),
((font.isBold()?Font.BOLD:Font.PLAIN)|(font.isItalic()?Font.ITALIC:Font.PLAIN)),
font.getFontsize(),
locale,
true
);
if (awtFont == null)
{
awtFont = new Font(getAttributesWithoutAwtFont(new HashMap(), font));
}
else
{
// add underline and strikethrough attributes since these are set at
// style/font level
Map attributes = new HashMap();
if (font.isUnderline())
{
attributes.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
}
if (font.isStrikeThrough())
{
attributes.put(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON);
}
if (!attributes.isEmpty())
{
awtFont = awtFont.deriveFont(attributes);
}
}
return awtFont;
}
private FontUtil()
{
}
}