如何从SVG文件提取嵌入式图像?


26

我有一个SVG文件,其中至少包含一个嵌入式JPG / PNG图像。我想从该SVG文件中提取JPG / PNG图像并将其保存在磁盘上。

我添加了inkscape标记,因为它是我用来编辑SVG文件的程序,但是我也接受使用其他工具的解决方案。


1
如果没有别的,Python可能可以使用lxml和PIL(或等效方法)使用一些自定义胶水来实现。
基思

@Keith,确实,我刚刚编写了Python脚本来解决这个问题。它使用内置xml.etree库。
DenilsonSáMaia

Answers:


30

我自己的解决方案(或解决方法):

  1. 在Inkscape中选择图像
  2. 打开内置的XML EditorShift+ Ctrl+ X
  3. 选择xlink:href将包含图像作为数据的属性:URI
  4. 复制整个data:URI
  5. 将该data:URI 粘贴到浏览器中,然后从那里保存它。

另外,我可以在任何文本编辑器中打开SVG文件,找到data:URI并从那里复制它。

尽管此解决方案有效,但它有点麻烦,我很想学习更好的解决方案。


2
+1-我使用这种方法导出了3.5 MB的图像,该图像花费了一段时间,但是可以正常工作。不知何故,“提取图像”功能对我不起作用。
马丁


17

而是有一个更好的解决方案:

转到Extensions -> Images -> Extract Image...,您可以在其中将选定的光栅图像另存为文件。但是,此扩展名很奇怪,并且以某种方式工作得很慢(但效果很好)。

另一个注意事项:此扩展繁琐,并且在各种大型图像上都无声地消失。同样,对于大量的光栅图像,它可能使inkscape的内存使用率达到惊人的水平(例如,仅提取少量图像后就达到了3GB)。

因为我有大约20个svg文件,每个文件中包含约70个光栅图像,每个图像的大小至少为1MB,所以我需要一个不同的解决方案。在使用DenilsonSá技巧进行简短检查之后,我设计了以下php脚本,该脚本从svg文件提取图像:

#!/usr/bin/env php
<?php

$svgs = glob('*.svg');

$existing = array();

foreach ($svgs as $svg){
    mkdir("./{$svg}.images");
    $lines = file($svg);
    $img = 0;
    foreach ($lines as $line){
        if (preg_match('%xlink:href="data:([a-z0-9-/]+);base64,([^"]+)"%i', $line, $regs)) {
            $type = $regs[1];
            $data = $regs[2];
            $md5 = md5($data);
            if (!in_array($md5, $existing)) {
                $data = str_replace(' ', "\r\n", $data);
                $data = base64_decode($data);
                $type = explode('/', $type);
                $save = "./{$svg}.images/{$img}.{$type[1]}";
                file_put_contents($save, $data);
                $img++;
                $existing[] = $md5;
            }
        } else {
            $result = "";
        }
    }
}

echo count($existing);

这样我就可以获取所有想要的图像,而md5可以避免重复获取图像。

我敢肯定,必须有另一种简单得多的方法,但是这取决于inkscape开发人员如何做得更好。


注意:您的脚本仅支持data:每行一个URL,并且不支持href属性内的换行符(inkscape将它们添加为数据URL,并且base64规范甚至要求该行的长度不得超过76个字符)。不错的脚本,可以快速破解,但不适用于所有SVG。
丹尼尔森·萨玛亚

@Johnny_Bit +1用于使用md5 sum来防止文件重复。我在下面改进了您的脚本。
Ivan Z

好,2019年3月,工作轻松,形象大。还有相当老的笔记本电脑/ ubuntu / inkscape 0.48.4。谢谢!
gaoithe

9

终于,几年后,我编写了一个脚本,使用适当的XML库来解析SVG代码,从而从SVG文件中正确提取所有图像。

http://bitbucket.org/denilsonsa/small_scripts/src/tip/extract_embedded_images_from_svg.py

该脚本是为Python 2.7编写的,但应该很容易转换为Python3。更好的是,由于该版本引入了新功能,转换为Python 3.4后可以删除大约50行。


谢谢,因为它有效。但这比PDF解决方法要慢得多。您是否考虑过并行处理?目前,该脚本仅使用单个CPU内核/线程。
DanMan '18

@DanMan不幸的是,使其平行并不是加快任何速度的神奇解决方案。我需要分析代码以识别瓶颈。如果瓶颈是XML解析,很抱歉,这部分不能并行完成。您能通过电子邮件将太慢的确切SVG文件发送给我吗?每当我有时间的时候,我可能会调查一下性能。
丹尼尔森·萨迈亚

是的,我尝试自己做,结果发现XML解析是很慢的部分,而不是解码图像。也就是说,cElementTree应该更快。但是也许像萨克斯这样的东西也更好。
DanMan '18

@DanMan cElementTree可能更快。但是,在Python 3.3上,两者是相同的。在某些时候,我很可能会更新脚本到Python 3
德尼尔森Sá马亚

5

作为另一种解决方法,您可以另存为PDF,然后使用Inkscape打开该文档。

取消选中“嵌入图像”和宾果游戏,所有png / jpeg都会弹出到您的主目录中。

凌乱,但比弄乱数据的速度更快:URL。


您在哪里找到“嵌入图像”选项?
mik01aj

1
当您在inkscape中打开PDF文档时,它位于下一个对话框中。
尼古拉斯·威尔逊

我有一个PDF,我试图通过将其导入Inkscape来提取图像。在这种情况下,能够导入而不是导入之后执行此操作更加方便。
user149408 '16

我不确定,但是这种方式下所有嵌入式ICC配置文件似乎都会丢失。我通过该Python脚本直接从SVG中提取的图像已嵌入ICC配置文件。
DanMan '18

1

我改进了@Johnny_Bitphp脚本。新版本的脚本可以在新行中使用svg。它从svg文件提取多个图像并将其保存在外部png文件中。svg和png文件位于“ svg”目录中,但您可以在常量“ SVG_DIR”中进行更改。

<?php

define ( 'SVG_DIR', 'svg/' );
define ( 'SVG_PREFIX', 'new-' );

$svgs = glob(SVG_DIR.'*.svg');
$external = array();
$img = 1;

foreach ($svgs as $svg) {
    echo '<p>';
    $svg_data = file_get_contents( $svg );
    $svg_data = str_replace( array("\n\r","\n","\r"), "", $svg_data);
    $svg_file = substr($svg, strlen(SVG_DIR) );
    echo $svg_file.': '.strlen($svg_data).' ????';

    if ( preg_match_all( '|<image[^>]+>|', $svg_data, $images, PREG_SET_ORDER) ) {
        foreach ($images as $image_tag) {

            if ( preg_match('%xlink:href="data:([a-z0-9-/]+);base64,([^"]+)"%i', $image_tag[0], $regs) ) {
                echo '<br/>Embeded image has benn saved to file: ';

               $type = $old_type = $regs[1];
               $data = $old_data = $regs[2];
               $md5 = md5($data);
               if ( array_key_exists($md5, $external) ) {
                $image_file = $external[$md5];
               } else {
                    $data = str_replace(" ", "\r\n", $data);
                    $data = base64_decode($data);
                    $type = explode('/', $type);
                    $image_file = substr( $svg_file, 0, strlen($svg_file)-4 ) . '-' . ($img++) . '.png';
                    file_put_contents(SVG_DIR.$image_file, $data);
                    $external[$md5] = $image_file;
               }
               echo $image_file;
               $svg_data = str_replace('xlink:href="data:'.$old_type.';base64,'.$old_data.'"', 'xlink:href="'.$image_file.'"', $svg_data);
            }
        }
        file_put_contents(SVG_DIR.SVG_PREFIX.'.svg', $svg_data);
    }

   echo '</p>';
}

?>

0

在Inkscape中打开文件,然后选择要导出的位图。单击文件->导出位图(Ctrl + Shift + E),它将仅导出选定的位图。


我不喜欢这种解决方案,因为它会重新编码图像。我希望有一种以原始格式提取图像的解决方案。
丹尼尔森·萨迈亚

1
是的,Inkscape似乎重新编码了图像,但默认情况下会保存PNG图像。因此,我假设重新编码至少是无损的。
克里斯,

1
好吧,不是真的。嵌入式图像可能已进行了转换(缩放,旋转等),已被裁剪,甚至其他我不知道的内容。在应用所有这些转换之后,Inkscape肯定会导出所选对象,这意味着该解决方案并非完全无损。
DenilsonSáMaia
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.