如何防止我的PDF转SVG代码生成臃肿的内容?

59

我想将PDF转换为SVG。我已经使用Apache PDFBox和Batik库编写了自己的Java程序。

PDDocument document = PDDocument.load( pdfFile );
DOMImplementation domImpl =
    GenericDOMImplementation.getDOMImplementation();

// Create an instance of org.w3c.dom.Document.
String svgNS = "http://www.w3.org/2000/svg";
Document svgDocument = domImpl.createDocument(svgNS, "svg", null);
SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(svgDocument);
ctx.setEmbeddedFontsOn(true);

// Ask the test to render into the SVG Graphics2D implementation.

    for(int i = 0 ; i < document.getNumberOfPages() ; i++){
        String svgFName = svgDir+"page"+i+".svg";
        (new File(svgFName)).createNewFile();
        // Create an instance of the SVG Generator.
        SVGGraphics2D svgGenerator = new SVGGraphics2D(ctx,false);
        Printable page  = document.getPrintable(i);
        page.print(svgGenerator, document.getPageFormat(i), i);
        svgGenerator.stream(svgFName);
    }

这个解决方案是可行的,但生成的SVG文件大小巨大(比原始PDF文件大多倍)。通过在文本编辑器中查看SVG,我已经找到了问题所在:即使字符的字体属性相同,它也会将原始文档中的每个字符都封闭在自己的<text> </text>块中。

例如,单词“hello”将显示为6个不同的文本块。

有没有办法修复上述代码?或者是否有其他更有效的解决方案?


请注意,在 Stack Overflow 上,工具推荐请求是不被允许的话题。不幸的是,到目前为止下面每一个答案都是关于工具推荐的,因此完全从上面的帖子中删除该请求将使这些答案无效,这是不被允许的。希望我对问题的改进能够使其得以挽救,并同时防止更多的“使用 Inkscape”回复出现在这里。 - TylerH
7个回答

66
Inkscape也可以用来将PDF转换为SVG。它在这方面表现得非常出色,尽管它生成的代码有点臃肿,但至少它似乎没有你在程序中遇到的特定问题。我认为直接将其集成到Java中可能会很具有挑战性,但是Inkscape提供了一个方便的命令行界面来完成此功能,因此最简单的访问方式可能是通过系统调用来实现。
要使用Inkscape的命令行界面将PDF转换为SVG,请使用:
inkscape -l out.svg in.pdf

然后你可以使用以下方式调用:

Runtime.getRuntime().exec("inkscape -l out.svg in.pdf")

http://download.oracle.com/javase/1.4.2/docs/api/java/lang/Runtime.html#exec%28java.lang.String%29

我认为exec()是同步的,只有在进程完成后才会返回(虽然我不100%确定),所以你应该能够在此之后直接读取"out.svg"。无论如何,通过谷歌搜索"java系统调用"将会得到更多关于如何正确执行此部分的信息。


1
我不知道有什么方法可以做到这一点,并且inkscape的手册似乎并没有表明这个功能在命令行界面中是公开的。我想你的选择就是通过修改inkscape代码自己添加这个界面。或者,你可以做一些非常巧妙和创造性的事情,使用像ghostscript这样的程序将PDF拆分成多个单页文档,然后将每一页单独输入inkscape。 - jbeard4
1
那么最好的解决方案可能是将pdf文件拆分为每页一个文件。pdfjam和pdftk都可以做到这一点。 - hlovdal
@Koen的回答指向了pdf2svg,它可以处理多页:pdf2svg input.pdf output_page%d.svg all - alxndr
我知道这已经过时了,但是我也注意到在处理pdf文件时inkscape会出现膨胀(例如重复组),一旦你拥有了.svg文件,就有一个清理工具叫做scour 可能会有所帮助。 - Chris H
2
我使用了Inkscape的命令行方法,但是SVG中的字体看起来非常丑陋。有没有办法解决这个问题? - remus
显示剩余4条评论

47

请看 pdf2svg(也可在GitHub上找到):

使用方法:

pdf2svg <input.pdf> <output.svg> [<pdf page no. or "all" >]

当使用 all 选项时,需要给出一个包含 %d 的文件名(该标记将被替换为页码)。
pdf2svg input.pdf output_page%d.svg all

如果需要进行故障排除,请查看以下链接:http://www.calcmaster.net/personal_projects/pdf2svg/


6
我之前一直在使用pdf2svg,但我刚刚发现它的近似程度比inkscape要高得多。尤其是在呈现小圆圈时会失去细节(我正在处理数十万路径的pdf文件)。具体情况因人而异。 - Aidan Kane
7
另一方面,相较于Inkscape来说,pdf2svg在处理文本方面表现更佳;我用LaTeX输出文件时,在Inkscape输出的图像中没有看到文本。 - Mechanical snail
1
@Mechanicalsnail:我现在对这个有更多的经验了。你说得对,有时我发现inkscape转换中缺少一些东西,而pdf2svg就没问题。pdf2svg已更新,调用cairo中的不同函数进行渲染(解决了我之前描述的问题)。不幸的是,这样做的代价是在svg中没有文本-所有字形都被转换为路径。我修补了cairo和poppler以使文本再次正常工作,但我并不完全信任我的hack :) - Aidan Kane
1
Inkscape和dvisvgm都无法从LaTeX创建正确的SVG。pdf2svg可以。 - ivo Welch

11

pdftocairo可以用来将PDF转换为SVG。
它是poppler-utils的一部分,可以通过PyPI上的pip安装,也可以从git构建,或者通过您的操作系统包管理器安装(例如ubuntu/deb的名称相同)

例如,要将PDF的第二页转换为SVG,可以运行以下命令:

pdftocairo -svg -f 1 -l 1 input.pdf

在我看来,最佳答案,最简单的解决方案。 - deeenes
1
您的命令将转换“第一页”,而不是“第二页”。 - s.ouchene

4

我在使用inkscape、pdf2svg或pdftocairo工具时遇到了问题,同时也不建议使用convert和mutool工具,尝试将一些来自USGS的地形图等大型复杂PDF转换为SVG。有时它们会崩溃,有时它们会产生巨大的文件。

唯一能够正确处理所有PDF的SVG转换工具是dvisvgm。使用它非常简单:

dvisvgm --pdf --output=file.svg file.pdf

它有各种额外的选项来处理元素的转换,以及用于优化。如果需要,它的生成文件可以通过 svgcleaner 进一步压缩而不会引起感知质量损失。


要每页获得一个SVG文件:dvisvgm --pdf --page=1- file.pdf - adius
请注意,SVGCleaner代码库已于2021年10月存档。 - TylerH

2

在*nix环境中,您可以使用bash。

burst操作将PDF中的每个页面拆分为文件。to-svg.sh循环遍历这些单页PDF以生成相应的SVG文件。

pdftk 82page.pdf burst
sh to-svg.sh 

to-svg.sh的内容

#!/bin/bash
FILES=burst/*
for f in $FILES
do
  inkscape -l "$f.svg" "$f"
done

1
Inkscape不再支持-l选项。它会显示“无法打开文件:/out.svg(不存在)”。该选项的长格式在man页面中为--export-plain-svg,可以使用但会显示弃用警告。我能够通过在Inkscape 1.1.2-3ubuntu4上使用-o选项来修复和更新命令:
inkscape in.pdf -o out.svg

0

inkscape({{link1:@jbeard4}})对我来说生成的SVG文件中没有任何文本,但是我通过使用Ghostscript作为中介去转换成PostScript格式最终解决了这个问题。

for page in $(seq 1 `pdfinfo $1.pdf | awk '/^Pages:/ {print $2}'`)
do
    pdf2ps -dFirstPage=$page -dLastPage=$page -dNoOutputFonts $1.pdf $1_$page.ps
    inkscape -z -l $1_$page.svg $1_$page.ps
    rm $1_$page.ps
done

然而,这种方法有点麻烦,最方便使用的称号要给予pdf2svg(@Koen.),因为它有一个all标志,所以你不需要循环。

然而pdf2svg在CentOS 8上不可用,要安装它,你需要执行以下操作:

git clone https://github.com/dawbarton/pdf2svg.git && cd pdf2svg
#if you dont have development stuff specific to this project
sudo dnf config-manager --set-enabled powertools
sudo dnf install cairo-devel poppler-glib-devel
#git repo isn't quite ready to ./configure
touch README
autoreconf -f -i
./configure && make && sudo make install

它生成的SVG比上面的Ghostscript-Inkscape更美观,字体似乎更好地光栅化。

pdf2svg $1.pdf $1_%d.svg all

但是那个安装有点繁琐,即使你没有sudo权限也太多了。除此之外,pdf2svg不支持stdin/stdout,因此现成的pdftocairo (@SuperNova)在这些方面表现得非常好,以下是一个“高级”使用示例:

for page in $(seq 1 `pdfinfo $1.pdf | awk '/^Pages:/ {print $2}'`)
do
    pdftocairo -svg -f $page -l $page $1.pdf - | gzip -9 >$1_$page.svg.gz
done

这个工具生成的文件与 pdf2svg 的质量和大小(在压缩之前)相同,尽管它们不是二进制相同的(即使在视觉上,在两者的输出之间跳转时,一些字母像素会移动,但都看起来没有问题/瑕疵,就像 Inkscape 一样)。


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