net.sf.jasperreports.renderers.util.SvgFontProcessor Maven / Gradle / Ivy
/*
* JasperReports - Free Java Reporting Library.
* Copyright (C) 2001 - 2016 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.renderers.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Locale;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.batik.anim.dom.SAXSVGDocumentFactory;
import org.apache.batik.bridge.UserAgent;
import org.apache.batik.dom.svg.SVGDocumentFactory;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.svg.SVGDocument;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.JasperReportsContext;
import net.sf.jasperreports.engine.export.HtmlFontFamily;
import net.sf.jasperreports.renderers.BatikUserAgent;
/**
* @author Teodor Danciu ([email protected])
*/
public abstract class SvgFontProcessor
{
public static final String SVG_ELEMENT_svg = "svg";
public static final String SVG_ELEMENT_g = "g";
public static final String SVG_ELEMENT_text = "text";
public static final String SVG_ATTRIBUTE_style = "style";
public static final String SVG_ATTRIBUTE_fontFamily = "font-family";
private final SVGDocumentFactory documentFactory;
private final Locale locale;
/**
*
*/
protected SvgFontProcessor(
JasperReportsContext jasperReportsContext,
Locale locale
)
{
UserAgent userAgent = new BatikUserAgent(jasperReportsContext);
documentFactory =
new SAXSVGDocumentFactory(userAgent.getXMLParserClassName(), true);
documentFactory.setValidating(userAgent.isXMLParserValidating());
this.locale = locale;
}
/**
*
*/
public byte[] process(byte[] svgData)
{
try
{
SVGDocument document =
documentFactory.createSVGDocument(
null,
new ByteArrayInputStream(svgData)
);
process(document);
TransformerFactory tFactory =
TransformerFactory.newInstance();
Transformer transformer =
tFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
DOMSource source = new DOMSource(document);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
StreamResult result = new StreamResult(baos);
transformer.transform(source, result);
return baos.toByteArray();
}
catch (TransformerConfigurationException e)
{
throw new JRRuntimeException(e);
}
catch (TransformerException e)
{
throw new JRRuntimeException(e);
}
catch (IOException e)
{
throw new JRRuntimeException(e);
}
}
private void process(Node element)
{
NodeList children = element.getChildNodes();
for (int i = 0; i < children.getLength(); i++)
{
Node child = children.item(i);
if (Node.ELEMENT_NODE == child.getNodeType())
{
if (
SVG_ELEMENT_svg.equals(child.getNodeName())
|| SVG_ELEMENT_g.equals(child.getNodeName())
|| SVG_ELEMENT_text.equals(child.getNodeName())
)
{
processFontAttributes((Element)child);
}
process(child);
}
}
}
private void processFontAttributes(Element styleElement)
{
String fontFamilyAttrValue = null;
NamedNodeMap attributes = styleElement.getAttributes();
Node fontFamilyAttrNode = attributes.getNamedItem(SVG_ATTRIBUTE_fontFamily);
if (fontFamilyAttrNode != null)
{
fontFamilyAttrValue = fontFamilyAttrNode.getNodeValue();
}
// values from style property takes precedence over values from attributes
Node styleAttrNode = attributes.getNamedItem(SVG_ATTRIBUTE_style);
if (styleAttrNode != null)
{
String styleFontFamily = getFontFamily(styleAttrNode.getNodeValue());
if (styleFontFamily != null)
{
fontFamilyAttrValue = styleFontFamily;
}
}
if (fontFamilyAttrValue != null)
{
StringBuilder newFamilyNameAttrValue = new StringBuilder();
boolean firstToken = true;
String[] fontFamilyTokens = fontFamilyAttrValue.split(",");
for (String fontFamilyToken : fontFamilyTokens)
{
if (!firstToken)
{
newFamilyNameAttrValue.append(",");
}
String fontFamily = fontFamilyToken.trim();
if (
fontFamily.startsWith("'")
&& fontFamily.endsWith("'")
)
{
fontFamily = fontFamily.substring(1, fontFamily.length() - 1);
}
// svg font-family could have locale suffix because it is needed in svg measured by phantomjs
int localeSeparatorPos = fontFamily.lastIndexOf(HtmlFontFamily.LOCALE_SEPARATOR);
if (localeSeparatorPos > 0)
{
fontFamily = fontFamily.substring(0, localeSeparatorPos);
}
fontFamily =
getFontFamily(
fontFamily,
locale //FIXMEBATIK could use locale from svg above
);
newFamilyNameAttrValue.append(" " + fontFamily);
firstToken = false;
}
if (styleAttrNode == null)
{
// do not put single quotes around family name here because the value might already contain quotes,
// especially if it is coming from font extension export configuration
styleElement.setAttribute(SVG_ATTRIBUTE_fontFamily, newFamilyNameAttrValue.toString());
}
else
{
String newStyleAttr = replaceOrAddFontFamilyInStyle(styleAttrNode.getNodeValue(), newFamilyNameAttrValue.toString());
styleElement.setAttribute(SVG_ATTRIBUTE_style, newStyleAttr);
}
}
}
private String getFontFamily(String style)
{
String fontFamily = null;
{
int fontFamilyStart = style.indexOf(SVG_ATTRIBUTE_fontFamily + ":");
if (fontFamilyStart >= 0)
{
fontFamilyStart = fontFamilyStart + SVG_ATTRIBUTE_fontFamily.length() + 1;
int fontFamilyEnd = style.indexOf(";", fontFamilyStart);
if (fontFamilyEnd >= 0)
{
fontFamily = style.substring(fontFamilyStart, fontFamilyEnd).trim();
}
else
{
fontFamily = style.substring(fontFamilyStart).trim();
}
}
}
return fontFamily;
}
private String replaceOrAddFontFamilyInStyle(String style, String fontFamily)
{
StringBuilder sb = new StringBuilder();
int fontFamilyStart = style.indexOf(SVG_ATTRIBUTE_fontFamily + ":");
if (fontFamilyStart >= 0)
{
fontFamilyStart = fontFamilyStart + SVG_ATTRIBUTE_fontFamily.length() + 1;
sb.append(style.substring(0, fontFamilyStart));
// do not put single quotes around family name here because the value might already contain quotes,
// especially if it is coming from font extension export configuration
sb.append(fontFamily);
int fontFamilyEnd = style.indexOf(";", fontFamilyStart);
if (fontFamilyEnd >= 0)
{
sb.append(style.substring(fontFamilyEnd));
}
}
else
{
sb.append(style);
if (!style.trim().endsWith(";"))
{
sb.append(";");
}
// do not put single quotes around family name here because the value might already contain quotes,
// especially if it is coming from font extension export configuration
sb.append(SVG_ATTRIBUTE_fontFamily + ": " + fontFamily + ";");
}
return sb.toString();
}
/**
*
*/
public abstract String getFontFamily(String fontFamily, Locale locale);
}