38

I have a SVG file that contains at least one embedded JPG/PNG image inside. I want to extract the JPG/PNG images from that SVG file and save them on disk.

I'm adding the inkscape tag as it is the program I use to edit SVG files, but I also accept solutions using other tools.

8 Answers8

33

My own solution (or... workaround):

  1. Select the image in Inkscape
  2. Open the built-in XML Editor (Shift+Ctrl+X)
  3. Select the xlink:href attribute, which will contain the image as data: URI
  4. Copy the entire data: URI
  5. Paste that data: URI into a browser, and save it from there.

Alternatively, I can open the SVG file in any text editor, locate the data: URI and copy it from there.

Although this solution works, it's kinda cumbersome and I'd love to learn a better one.

23

There's a better solution instead:

go to Extensions -> Images -> Extract Image..., there you can save selected raster image as a file. However this extension works weird and somehow works rather slowly (but perfectly well).

Another note: this extension is cumbersome and dies silently on vary large images. Also, with large number of raster images it can spike memory usage of inkscape to horrendous levels (like 3GB after only a handful of images extracted).

Because I've got about 20 svg files with about 70 raster images in them each, each image at least 1MB in size, I needed a different solution. After a short check using Denilson Sá tip I devised the following php script, that extracts images from svg files:

#!/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);

This way I can get all the images I want, and md5 saves me from getting repeated images.

I bet there must be another way that is a lot simpler, but it's up to inkscape devs to do it better.

14

Finally, years later, I've written a script to correctly extract all images from an SVG file, using a proper XML library to parse the SVG code.

https://github.com/denilsonsa/small_scripts/blob/master/extract_embedded_images_from_svg.py

This script requires Python 3.4 or newer. (Look at git history if you need to run on older Python versions.)

5

As yet another workaround, you can save as PDF, then open that document with Inkscape.

Uncheck "embed images", and bingo, all the pngs/jpegs will be spewed out into your home directory.

Messy, but quicker than goofing about with the data: URL.

mik01aj
  • 1,574
  • 2
  • 12
  • 16
Nicholas Wilson
  • 224
  • 2
  • 5
4

Open the image in Inkscape, right-click on it, in the contextual menu, choose Extract Image and you're done.

DevonDahon
  • 141
  • 3
4

Open the svg in Firefox, right-click on image, Save image as... — it will give you embedded image as file (png, jpg, etc).

Or, open the svg in Chrome, open DevTools (F12), go to Network tab, reload the page and you'll see query to data:img/png;base64.... Right-click it, select "Open in new tab", the file will be saved without extension. Then rename the saved file adding it proper extension like .png

Pavel
  • 349
1

I improve the php-script of @Johnny_Bit. New release of the script can use svg with new lines. It extracts multiple images form svg file and save them in external png files. Svg and png files are in 'svg' directory, but you can change it in constant '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>';
}

?>
Ivan Z
  • 131
0

Open your file in Inkscape and select the bitmap that you wish to export. Click File->Export Bitmap (Ctrl+Shift+E) and it should export only the selected bitmap.

zx485
  • 2,337
Chris
  • 963