使用Batik转换WMF到SVG时出现NaN路径异常问题
问题
使用Batik的batik-transcoder-1.17和batik-codec-1.17版本将WMF转换为SVG时,生成的SVG路径中出现大量NaN值。转换代码如下:
package org.example; import java.io.*; import org.apache.batik.transcoder.TranscoderException; import org.apache.batik.transcoder.TranscoderInput; import org.apache.batik.transcoder.TranscoderOutput; import org.apache.batik.transcoder.wmf.tosvg.WMFTranscoder; public class WmfImagesToPng1 { public static void convert(String wmfFilePath, String svgFilePath) throws IOException, TranscoderException { File wmfFile = new File(wmfFilePath); File svgFile = new File(svgFilePath); WMFTranscoder transcoder = new WMFTranscoder(); // Set transcoder hints transcoder.addTranscodingHint(WMFTranscoder.KEY_WIDTH, (float) 800); // Set output width transcoder.addTranscodingHint(WMFTranscoder.KEY_HEIGHT, (float) 600); // Set output height TranscoderInput input = new TranscoderInput(new FileInputStream(wmfFile)); // Use StringWriter to capture SVG output StringWriter writer = new StringWriter(); TranscoderOutput output = new TranscoderOutput(writer); // Perform transcoding transcoder.transcode(input, output); // Write SVG content to file try (FileWriter fileWriter = new FileWriter(svgFile)) { fileWriter.write(writer.toString()); } } public static void main(String[] args) throws IOException, TranscoderException { // Replace these paths with your actual file paths String wmfFilePath = "029.wmf"; String svgFilePath = "output.svg"; convert(wmfFilePath, svgFilePath); System.out.println("WMF file converted to SVG successfully!"); } }
转换后的SVG片段示例:
<?xml version="1.0"?> <!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN' 'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'> <svg stroke-dasharray="none" shape-rendering="auto" xmlns="http://www.w3.org/2000/svg" font-family="'Dialog'" width="800" text-rendering="auto" fill-opacity="1" contentScriptType="text/ecmascript" color-interpolation="auto" color-rendering="auto" preserveAspectRatio="xMidYMid meet" font-size="12px" viewBox="0 0 800 619" fill="black" xmlns:xlink="http://www.w3.org/1999/xlink" stroke="black" image-rendering="auto" stroke-miterlimit="10" zoomAndPan="magnify" version="1.0" stroke-linecap="square" stroke-linejoin="miter" contentStyleType="text/css" font-style="normal" height="619" stroke-width="1" stroke-dashoffset="0" font-weight="normal" stroke-opacity="1" ><!--Generated by the Batik Graphics2D SVG Generator--><defs id="genericDefs" /><g ><g fill="rgb(102,51,0)" stroke-linecap="butt" stroke-linejoin="round" stroke="rgb(102,51,0)" ><path d="MNaN NaN LNaN NaN LNaN NaN LNaN NaN LNaN NaN.....
涉及的WMF文件在多个查看器中可正常渲染,且该问题并非特定图片独有,需明确:
- 输出SVG包含NaN的原因
- Batik是否支持所有WMF图片
- 可行的解决方案
解答
原因分析
- WMF格式支持局限性:Batik的WMF转码器对WMF标准的实现并不完整,尤其是处理复杂GDI指令、非标准WMF变体时,无法正确解析部分图形操作,导致生成无效的坐标值(NaN)。
- 强制尺寸引发计算错误:代码中手动指定
KEY_WIDTH和KEY_HEIGHT,若原始WMF的尺寸信息未被正确读取,转码器在缩放计算时会出现无效数值运算,进而产生NaN。 - 转码器版本bug:Batik 1.17的WMFTranscoder存在已知解析缺陷,部分图形指令的处理逻辑不完善,导致坐标计算失败。
解决方案
- 移除强制尺寸设置:让转码器自动读取WMF原始尺寸,避免手动赋值引发的计算错误,修改代码时删除以下两行:
// transcoder.addTranscodingHint(WMFTranscoder.KEY_WIDTH, (float) 800); // transcoder.addTranscodingHint(WMFTranscoder.KEY_HEIGHT, (float) 600); - 升级Batik版本:尝试使用Batik最新稳定版(如1.18+),新版本通常会修复旧版的WMF解析bug。
- 替换转码工具:若Batik无法满足需求,可使用
libwmf(通过JNI调用)或命令行工具inkscape(Java调用外部进程)完成WMF转SVG,这类工具对WMF的支持更全面。 - 预处理转码(牺牲矢量特性):先将WMF转成PNG,再将PNG嵌入SVG,此方法仅适用于无需编辑SVG矢量内容的场景。
内容的提问来源于stack exchange,提问作者logesh ramasamy
相关产品推荐
相关产品推荐

