如何在JasperReport中将SVG字节数组显示为图像?

6

我有一张以byte[]形式保存的图片,我想在JasperReport中将其显示为图片。我尝试从Java方法中获取数据:

public InputStream getImage(){
  return new ByteArrayInputStream(getImageByteArray());
}

并且

public Image getImage() throws IOException{
    return ImageIO.read(new ByteArrayInputStream(getImageByteArray()));
}

并且

public String getImage(){
  return new String((new org.apache.commons.codec.binary.Base64()).encode(getImageByteArray()));
}

但是似乎它们都不起作用。

jrxml 文件如下:

<image hAlign="Center" vAlign="Middle" isUsingCache="true" isLazy="true">
   <reportElement positionType="Float" x="0" y="0" width="164" height="32" isRemoveLineWhenBlank="true" isPrintWhenDetailOverflows="true" uuid="c63c84a8-41c7-4ca3-8451-751d43fa8a9e"/>
   <imageExpression><![CDATA[$P{paramObject}.getImage()]]></imageExpression>
</image>

我尝试的一些事情会导致异常,而有些则能打印JasperReport,但图像应该呈现的区域是空白的。 我知道图像数据在那里,因为我可以在JSF页面中显示它。 图像数据是SVG数据。


这个答案有帮助吗,@cbender?绿色勾号? - Dave Jarvis
6个回答

7

自定义图像转码器

编写一个自定义的图像转码器,它可以读取SVG文件并将该资源转换为PNG或SVG文件。在导出PDF时,可以直接使用SVG文件。请考虑以下内容:

import java.awt.Color;
import java.io.*;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.util.JRLoader;
import org.apache.batik.transcoder.*;
import static org.apache.batik.transcoder.image.ImageTranscoder.*;
import org.apache.batik.transcoder.image.PNGTranscoder;

public class ImageTranscoder {
    public static InputStream asSVG(final String file) throws JRException {
        return new ByteArrayInputStream(load(file));
    }

    public static InputStream asPNG(final String file)
            throws TranscoderException, JRException {
        return asPNG(load(file));
    }

    public static InputStream asPNG(final byte[] svg)
            throws TranscoderException {
        final ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
        final ByteArrayInputStream inBytes = new ByteArrayInputStream(svg);

        final TranscoderInput input = new TranscoderInput(inBytes);
        final TranscoderOutput output = new TranscoderOutput(outBytes);
        final PNGTranscoder transcoder = new PNGTranscoder();

        transcoder.addTranscodingHint(KEY_BACKGROUND_COLOR, Color.white);
        transcoder.addTranscodingHint(KEY_FORCE_TRANSPARENT_WHITE, true);
        transcoder.transcode(input, output);

        final byte[] bytes = outBytes.toByteArray();
        return new ByteArrayInputStream(bytes);
    }

    private static byte[] load(final String file) throws JRException {
        return JRLoader.loadBytesFromResource(file);
    }
}

导入转码器

在JRXML文件中,导入完整限定类:

<import value="com.company.jasper.ImageTranscoder"/>

应用图像转码器

像往常一样,从调色板中拖拽一个图像并放置在报表中。将其表达式设置为:

ImageTranscoder.asSVG($P{IMAGES_PATH} + $P{IMAGE_FILENAME} + ".svg")

如果你确实需要一个PNG版本,那么可以即时转码:

ImageTranscoder.asPNG($P{IMAGES_PATH} + $P{IMAGE_FILENAME} + ".svg")

HTML vs PDF

在 HTML 中,通常仍然优先使用 PNG 图像。有许多方法可以区分 HTML(PNG)和 PDF(SVG)。一种简单的方法是为两个不同的图像元素分配两个不同的关键值。例如:

<image scaleImage="RetainShape" onErrorType="Blank">
    <reportElement key="IMAGE_PNG"/>
    <imageExpression><![CDATA[ImageTranscoder.asPNG(...)]]></imageExpression>
</image>
<image scaleImage="RetainShape" onErrorType="Blank">
    <reportElement key="IMAGE_SVG"/>
    <imageExpression><![CDATA[ImageTranscoder.asSVG(...)]]></imageExpression>
</image>

然后您可以根据导出类型排除其中一个:

<property name="net.sf.jasperreports.export.html.exclude.key.IMAGE_SVG"/>
<property name="net.sf.jasperreports.export.pdf.exclude.key.IMAGE_PNG"/>

概要

使用PNG图像虽然更简单,但是从SVG转换PNG图像是一个额外的步骤,可以避免。由于JasperReports库使用Batik引擎渲染图像,所以在生成报表时利用它将SVG文件转换为PNG。

这样,SVG作为所有格式的单一来源,无论报告中使用的是PNG还是SVG文件。

请确保根据需要设置IMAGES_PATHIMAGE_FILENAME参数。

HTML和Base64

使用以下方法强制嵌入图像:

<property name="net.sf.jasperreports.export.html.embed.image" value="true"/>

PNG图像变成Base64编码的字符串:
<img src="data:image/png;base64,..."/>

这将使报告加载速度更快(无需额外的图像HTTP请求),并简化架构,因为它消除了外部依赖项。也就是说,不再需要Web服务器提供图像,因为它完全嵌入。


5
假设您有以Base64编码的图像字节,可以使用以下内容:
<image>
    <reportElement/>
    <imageExpression>
        <![CDATA[javax.imageio.ImageIO.read(new java.io.ByteArrayInputStream(new sun.misc.BASE64Decoder().decodeBuffer("/9j/4AAQ .... "))) ]]>
    </imageExpression>
</image>

2
您可以尝试以下方法:
将您的图像参数(在这里是)设置为InputStream。
<parameter name="img" class="java.io.InputStream">
        <parameterDescription><![CDATA[]]></parameterDescription>
    </parameter>

然后将 onErrorType="Blank" 设置到您的图片元素中(实际上不知道这样做的原因 :))

<image onErrorType="Blank">
    <reportElement x="2" y="4" width="119" height="62" uuid="66857471-6aa2-4ff0-be59-e2e1b0214bfc"/>
    <imageExpression><![CDATA[$P{img}]]></imageExpression>
</image>

抱歉,但图像必须从参数映射中对象的getter中获取,该项目已经按照特定方式设置。但无论如何,使图像成为自己的参数而不是从参数对象访问它不应该有任何区别。 onErrorType =“Blank” 只是使得当将图像放入报告时发生错误而不是抛出异常时,它只是保留图像空间为空白。这并没有帮助显示图像。 - cbender
对我来说,这是比其他解决方案更容易的解决方案。谢谢。 - Thiha Maung

1

getImage() 应该返回 byte[]

我看不到你的 jasperreports 模板中 paramObject 的类型,但它应该是 String

而在没有任何不可用的 Oracle 库的情况下反序列化图片的最简单方法是:

<imageExpression><![CDATA[javax.imageio.ImageIO.read(new java.io.ByteArrayInputStream(java.util.Base64.getDecoder().decode($F{paramObject})))]]></imageExpression>

1
我提出一个简单的解决方案。它有效,我正在使用它。这样我们就可以避免中间代码和对应用程序的修改。
我们必须建立以下导入:
<import value="javax.swing.ImageIcon"/>

我们领域中关于图像的定义必须是对象类型:
<field name="myImage" class="java.lang.Object"/>

在报告正文中,我们必须使用以下表达式放置图片:
<image>
  <reportElement x="10" y="10" width="100" height="100" uuid="a791129e-a20d-4be3-bdcd-27528bf2edc4"/>
  <imageExpression><![CDATA[(new ImageIcon((byte[])$F{foto})).getImage()]]></imageExpression>
</image>

这就是全部。解决方案如何工作?我们导入了 javax.swing.ImageIcon 库,它允许我们从 byte[] 创建新的 ImageIcon,然后我们可以使用 getImage() 将 ImageIcon 转换为 Image。

-2
我们对于图像的字段定义必须是对象类型: 在报告正文中,我们必须使用以下表达式放置图像:

这段代码对我有效....


1
{btsdaf} - Duloren

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接