【问题标题】:From SVG to PNG with Batik: an issue with multiline text使用蜡染从 SVG 到 PNG:多行文本的问题
【发布时间】:2018-08-10 12:32:54
【问题描述】:

我正在使用 Batik 将 SVG 转换为 PNG 图像,但多行文本无法正确呈现。文本本身不再以最终图像为中心可能会以一种不确定的方式发生。

例如,考虑开始 SVG 的这一部分:

这是正确的渲染,我也希望在 PNG 中获得。顺便说一下,生成的 PNG 如下所示:

如您所见,顶部绿色框中的文本不再居中。

这是定义它的 SVG 片段(怀疑是字体问题,我也尝试删除所有 font-family="",但没有任何效果):

  <g transform="translate(184.68 -0.600364)">
     <text fill="rgb(255, 255, 255)" fill-opacity="1" font-family="Kievit Pro" font-size="10px" font-style="normal" font-weight="normal" text-anchor="middle" transform="translate(48 0.429996)">
        <tspan dy="1em" x="0">A simple activity, with</tspan>
        <tspan dy="1.5em" x="0">a text that goes on t</tspan>
     </text>
  </g>

如果有帮助,这是我用于转换的代码:

static byte[] convertToPng(byte[] byteSvgSmall) throws TranscoderException, IOException {
    if (byteSvgSmall != null) {

        byte[] byteImagePng = null;
        ByteArrayInputStream bais = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        InputStream targetStream = null;

        try {
            targetStream = new ByteArrayInputStream(byteSvgSmall);
            TranscoderInput input = new TranscoderInput(targetStream);

            PNGTranscoder transcoder = new PNGTranscoder();
            transcoder.addTranscodingHint(PNGTranscoder.KEY_WIDTH, new Float(diagramWidthInPixelsForPdf));

            TranscoderOutput output = new TranscoderOutput(baos);
            transcoder.transcode(input, output);

            byte[] byteOutput = baos.toByteArray();
            byteImagePng = new byte[byteOutput.length];
            bais = new ByteArrayInputStream(baos.toByteArray());
            bais.read(byteImagePng);
            targetStream.close();
            baos.flush();
            return byteImagePng;
        } catch (Exception e) {
            BeanFactory
                    .getLogger()
                    .error(SVGTransformer.class,
                            "An error occurred. A null byte[] will be returned",
                            e);
            return null;
        } finally {
            if (bais != null) bais.close();
            if (baos != null) baos.close();
            if (targetStream != null) targetStream.close();
        }
    }
    return null;
}

然后,使用返回的字节[]:

if (pngBytes != null) {
   BufferedImage final_img = ImageIO.read(new ByteArrayInputStream(pngBytes));
   File output_file = new File(imagepath);
   ImageIO.write(final_img, "png", output_file);
}

非常感谢您提供任何见解。

【问题讨论】:

  • @Andreaジーティーオー 我无法重现该问题。我正在使用 1.9.1 org.apache.xmlgraphicsbatik-all1.9.1版本> pom
  • @EzekielBaniaga:非常感谢您的尝试,非常感谢。也响应 Dave Jarvis,我正在使用 Java 1.6 进行编译,batik-all-1.7.jar;我不知道哪个工具生成了 SVG;我从另一个团队收到它,以便使用 Apache POI 将其放入 PDF。

标签: java svg batik


【解决方案1】:

我下载了您的 svg,但文件(至少在我的计算机上)与绿色框范围内的文本不太匹配(有一点溢出,被剪掉了)。我不确定这是否是平台问题,因为您的屏幕截图显示的文本有一点边距(而在我的情况下,使用 GIMP 打开,文本超出了范围)。

因此,我修改了您的 svg,使所有 10 像素的字体大小都减小到了 9 像素(以适应正方形中的所有内容)。

然后我使用 java-8-openjdk-amd64、提供的 svg 和以下依赖项运行您的代码:

compile group: 'org.apache.xmlgraphics', name: 'batik-transcoder', version: '1.9.1'
compile group: 'org.apache.xmlgraphics', name: 'xmlgraphics-commons', version: '2.2'
compile group: 'org.apache.xmlgraphics', name: 'batik-codec', version: '1.9.1'

TEST1 框在我的 png 和你的 png 中呈现相似但不相同:

A simple activity, with
  a text that goes on t

然后我修改了 xml 节点,将第二行从“a text that go on t”更改为“a text that”。这次第二行居中。

A simple activity, with
     a text that

然后我将原始文本放回节点中,并将框的字体系列更改为 Lucida Sans 并将大小更改为 7px(我在下面修改过的节点)。

<text fill="rgb(255, 255, 255)" fill-opacity="1" font-family="Lucida Sans" font-size="7px" font-style="normal" font-weight="normal" text-anchor="middle" transform="translate(48 0.429996)">
    <tspan dy="1em" x="0">A simple activity, with</tspan>
    <tspan dy="1.5em" x="0">a text that goes on t</tspan>
</text>

这一次是居中的。你试过什么?如果您尝试上述方法(更改字体大小和/或字体系列),您会得到什么结果?我还使用 1000px 作为KEY_WIDTH 转码提示,如果这可能会改变您的情况。

【讨论】:

  • 嗨,威廉,谢谢,非常感谢。可能在您的机器上,文本以不同的方式呈现,因为缺少 Kievit Pro 字体。即使在我的情况下,最终的渲染也有点不同,并且似乎在转换中丢失了该字体(如我的问题的第二张图片所示:这是生成的 png,使用 Apache POI 放入 pdf 中)。因此,如果我理解正确,您的建议是通过大量替换来减小字体大小。我试着让你知道。也许,Batik 遇到了一个对于它的容器来说太长的文本,并且在渲染它时遇到了问题。
  • 嗨,是的,我建议尝试不同的 font-family 和/或 font-size 值。我相应地更新了答案。这有点偶然,因为我无法重现您的确切起始条件,但这似乎是库中的渲染问题。我不确定Kevit Pro 是哪种字体,但Lucida Sans 应该是“java 友好的”。告诉我进展如何。
  • 是的,我不能确定,因为我不知道蜡染在幕后做了什么。但是,在转换之前操纵字体大小似乎可以解决问题。也许将文本放入不再“适合”它的容器中确实存在问题。我认为下一步将尝试在转换中保留字体系列。到现在为止...非常感谢您(以及其他试图提供帮助的人)!
【解决方案2】:

经过一段时间,我终于找到了真正的问题,感谢this Jira 上的帖子。 tspans' 文本中的尾随和结束空格会导致 Batik 光栅化器误解内容。

通过扫描 tspan 元素,并设置文本的修剪版本:

for (int tspanCounter = 0; tspanCounter < tspanList.getLength(); tspanCounter++) {
   Node tspan = tspanList.item(tspanCounter);
   tspan.setTextContent(actParts[tspanCounter].trim());
}

我得到了正确的结果。在下面的图片中,您可以看到带有和不带有...简单的trim() 调用的结果!

版本错误(tspan 内容中的空格)

“右”版本(在 tspan 的内容中修剪空格)

【讨论】:

    猜你喜欢
    • 2015-12-19
    • 1970-01-01
    • 2017-07-15
    • 2019-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多