如何从类路径加载 ICC 配置文件?
How to load ICC profile from classpath?
在尝试使用 Jasper Reports 生成符合 PDF/A 标准的 PDF 时,我得到了
net.sf.jasperreports.engine.util.JRPdfaIccProfileNotFoundException.
这可以通过配置
轻松解决
<property name="net.sf.jasperreports.export.pdfa.icc.profile.path" value="/stuff/myicc.icc"/>
但我需要将 icc 配置文件与 WAR 的其余部分捆绑在一起,就像报告嵌入的字体一样。如何才能做到这一点?我看起来像
<property name="net.sf.jasperreports.export.pdfa.icc.profile.path" value="classpath:/jasper/someicc.icc"/>
但这没有用,相对于报告本身的 icc 配置文件也没有用。我可以将 InputStream 作为参数传递给 PDFGenerator 吗?
如果您从 java 代码导出,您可以使用 getClassLoader().getResource()
获取文件的绝对路径。
JRPdfExporter 示例
JRPdfExporter exporter = new JRPdfExporter();
//... your input and output
SimplePdfExporterConfiguration configuration = new SimplePdfExporterConfiguration();
URL path = this.getClass().getClassLoader().getResource("jasper/someicc.icc");
configuration.setIccProfilePath(path.getFile());
//other settings
exporter.setConfiguration(configuration);
exporter.exportReport();
我们最终创建了自己的扩展来处理类路径加载,使其更加集中。
实现类路径扩展的代码在这里:
package com.somepackage;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import net.sf.jasperreports.engine.JRPropertiesMap;
import net.sf.jasperreports.extensions.ExtensionsRegistry;
import net.sf.jasperreports.extensions.ExtensionsRegistryFactory;
import net.sf.jasperreports.repo.InputStreamResource;
import net.sf.jasperreports.repo.RepositoryService;
import net.sf.jasperreports.repo.Resource;
/**
* JasperReports extension factory that enables to read from classpath. Only tries to resolve URLs that start with "classpath:".
*/
public class ClasspathExtensionsRegistryFactory implements ExtensionsRegistryFactory {
@Override
public ExtensionsRegistry createRegistry(String registryId, JRPropertiesMap properties) {
return extFactory;
}
private static final ClasspathRepositoryService service = new ClasspathRepositoryService();
private static final List<ClasspathRepositoryService> services = Collections.singletonList(service);
private static final ExtensionsRegistry extFactory = new ExtensionsRegistry() {
@SuppressWarnings("unchecked")
@Override
public <T> List<T> getExtensions(Class<T> extensionType) {
if (RepositoryService.class.equals(extensionType)) {
return (List<T>) services;
}
return null;
}
};
/**
* RepositoryService that reads resources from classpath.
*/
public static class ClasspathRepositoryService implements RepositoryService {
private static final String CLASSPATH_PREFIX = "classpath:";
private static final int CLASSPATH_PREFIX_LENGTH = CLASSPATH_PREFIX.length();
@Override
public Resource getResource(String uri) {
return getResource(uri, InputStreamResource.class);
}
@Override
public void saveResource(String uri, Resource resource) {
//No-op
}
@SuppressWarnings("unchecked")
@Override
public <K extends Resource> K getResource(String uri, Class<K> resourceType) {
if (resourceType != null && InputStreamResource.class.equals(resourceType) && uri != null && uri.trim().startsWith(CLASSPATH_PREFIX)) {
InputStream is = this.getClass().getResourceAsStream(uri.trim().substring(CLASSPATH_PREFIX_LENGTH));
if (is != null) {
InputStreamResource isr = new InputStreamResource();
isr.setInputStream(is);
isr.setName(uri);
return (K)isr;
}
}
return null;
}
}
}
之后唯一要做的就是添加 jasperreports_extension.properties 文件,其中包含
net.sf.jasperreports.extension.registry.factory.classpathresourcereader=com.somepackage.ClasspathExtensionsRegistryFactory
这样可以像这样配置导出器:
SimplePdfExporterConfiguration pec = new SimplePdfExporterConfiguration();
.
.
.
pec.setIccProfilePath("classpath:/somefolder/iccprofile.icc");
在尝试使用 Jasper Reports 生成符合 PDF/A 标准的 PDF 时,我得到了
net.sf.jasperreports.engine.util.JRPdfaIccProfileNotFoundException.
这可以通过配置
轻松解决<property name="net.sf.jasperreports.export.pdfa.icc.profile.path" value="/stuff/myicc.icc"/>
但我需要将 icc 配置文件与 WAR 的其余部分捆绑在一起,就像报告嵌入的字体一样。如何才能做到这一点?我看起来像
<property name="net.sf.jasperreports.export.pdfa.icc.profile.path" value="classpath:/jasper/someicc.icc"/>
但这没有用,相对于报告本身的 icc 配置文件也没有用。我可以将 InputStream 作为参数传递给 PDFGenerator 吗?
如果您从 java 代码导出,您可以使用 getClassLoader().getResource()
获取文件的绝对路径。
JRPdfExporter 示例
JRPdfExporter exporter = new JRPdfExporter();
//... your input and output
SimplePdfExporterConfiguration configuration = new SimplePdfExporterConfiguration();
URL path = this.getClass().getClassLoader().getResource("jasper/someicc.icc");
configuration.setIccProfilePath(path.getFile());
//other settings
exporter.setConfiguration(configuration);
exporter.exportReport();
我们最终创建了自己的扩展来处理类路径加载,使其更加集中。
实现类路径扩展的代码在这里:
package com.somepackage;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import net.sf.jasperreports.engine.JRPropertiesMap;
import net.sf.jasperreports.extensions.ExtensionsRegistry;
import net.sf.jasperreports.extensions.ExtensionsRegistryFactory;
import net.sf.jasperreports.repo.InputStreamResource;
import net.sf.jasperreports.repo.RepositoryService;
import net.sf.jasperreports.repo.Resource;
/**
* JasperReports extension factory that enables to read from classpath. Only tries to resolve URLs that start with "classpath:".
*/
public class ClasspathExtensionsRegistryFactory implements ExtensionsRegistryFactory {
@Override
public ExtensionsRegistry createRegistry(String registryId, JRPropertiesMap properties) {
return extFactory;
}
private static final ClasspathRepositoryService service = new ClasspathRepositoryService();
private static final List<ClasspathRepositoryService> services = Collections.singletonList(service);
private static final ExtensionsRegistry extFactory = new ExtensionsRegistry() {
@SuppressWarnings("unchecked")
@Override
public <T> List<T> getExtensions(Class<T> extensionType) {
if (RepositoryService.class.equals(extensionType)) {
return (List<T>) services;
}
return null;
}
};
/**
* RepositoryService that reads resources from classpath.
*/
public static class ClasspathRepositoryService implements RepositoryService {
private static final String CLASSPATH_PREFIX = "classpath:";
private static final int CLASSPATH_PREFIX_LENGTH = CLASSPATH_PREFIX.length();
@Override
public Resource getResource(String uri) {
return getResource(uri, InputStreamResource.class);
}
@Override
public void saveResource(String uri, Resource resource) {
//No-op
}
@SuppressWarnings("unchecked")
@Override
public <K extends Resource> K getResource(String uri, Class<K> resourceType) {
if (resourceType != null && InputStreamResource.class.equals(resourceType) && uri != null && uri.trim().startsWith(CLASSPATH_PREFIX)) {
InputStream is = this.getClass().getResourceAsStream(uri.trim().substring(CLASSPATH_PREFIX_LENGTH));
if (is != null) {
InputStreamResource isr = new InputStreamResource();
isr.setInputStream(is);
isr.setName(uri);
return (K)isr;
}
}
return null;
}
}
}
之后唯一要做的就是添加 jasperreports_extension.properties 文件,其中包含 net.sf.jasperreports.extension.registry.factory.classpathresourcereader=com.somepackage.ClasspathExtensionsRegistryFactory
这样可以像这样配置导出器:
SimplePdfExporterConfiguration pec = new SimplePdfExporterConfiguration();
.
.
.
pec.setIccProfilePath("classpath:/somefolder/iccprofile.icc");