Como extrair uma imagem incorporada de um arquivo SVG?


26

Eu tenho um arquivo SVG que contém pelo menos uma imagem JPG / PNG incorporada. Quero extrair as imagens JPG / PNG desse arquivo SVG e salvá-las no disco.

Estou adicionando a inkscapetag, pois é o programa que uso para editar arquivos SVG, mas também aceito soluções usando outras ferramentas.


1
Se nada mais, o Python provavelmente poderia fazê-lo com alguma cola personalizada usando lxml e PIL (ou equivalente).
Keith

@ Keith, de fato, acabei de escrever um script Python para resolver esta questão. Ele usa a xml.etreebiblioteca interna.
Denilson Sá Maia

Respostas:


30

Minha própria solução (ou ... solução alternativa):

  1. Selecione a imagem no Inkscape
  2. Abra o built-in XML Editor( Shift+ Ctrl+ X)
  3. Selecione o xlink:hrefatributo, que conterá a imagem como dados: URI
  4. Copie o data:URI inteiro
  5. Cole esse data:URI em um navegador e salve-o a partir daí.

Como alternativa, posso abrir o arquivo SVG em qualquer editor de texto, localizar o data:URI e copiá-lo a partir daí.

Embora essa solução funcione, é meio complicado e eu adoraria aprender uma melhor.


2
+1 - Exportei uma imagem de 3,5 MB usando esse método, que demorou um pouco, mas funcionou. De alguma forma, a função "Extrair imagem" não funcionou para mim.
Martin

Consulte também um script Python da linha de comando para esse fim.
Denilson Sá Maia

17

Há uma solução melhor:

vá para Extensions -> Images -> Extract Image...lá, você pode salvar a imagem raster selecionada como um arquivo. No entanto, esta extensão funciona de maneira estranha e, de alguma forma, funciona bem devagar (mas perfeitamente bem).

Outra observação: esta extensão é complicada e morre silenciosamente em várias imagens grandes. Além disso, com um grande número de imagens rasterizadas, ele pode elevar o uso da memória do inkscape para níveis horrendos (como 3 GB após apenas algumas imagens extraídas).

Como tenho cerca de 20 arquivos svg com cerca de 70 imagens raster cada, cada imagem com pelo menos 1 MB de tamanho, eu precisava de uma solução diferente. Após uma breve verificação usando a dica de Denilson Sá , criei o seguinte script php, que extrai imagens de arquivos 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);

Dessa forma, posso obter todas as imagens desejadas e o md5 me impede de obter imagens repetidas.

Aposto que deve haver outra maneira que seja muito mais simples, mas cabe aos desenvolvedores do inkscape fazê-lo melhor.


Nota: Seu script suporta apenas uma única data:URL por linha e não oferece suporte a novas linhas dentro do atributo href (o inkscape as adiciona para URLs de dados, e a especificação base64 exige que as linhas não devam ter mais de 76 caracteres ). Bom script para um hack rápido, mas não funciona com todos os tipos de SVG.
Denilson Sá Maia

@Johnny_Bit +1 pelo uso da soma md5 para evitar a duplicação de arquivos. Eu melhorei seu script abaixo .
Ivan Z

bom, março de 2019 e trabalhou muito bem com uma imagem razoavelmente grande. E muito velho laptop / ubuntu / inkscape 0.48.4. Obrigado!
gaoithe 3/03

9

Finalmente, anos depois, escrevi um script para extrair corretamente todas as imagens de um arquivo SVG, usando uma biblioteca XML adequada para analisar o código SVG.

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

Este script foi escrito para o Python 2.7, mas deve ser bastante fácil de converter para o Python 3. Melhor ainda, cerca de 50 linhas podem ser excluídas após a conversão para o Python 3.4, devido aos novos recursos introduzidos nessa versão.


Obrigado, uma vez que funciona. Mas é muito mais lento que a solução alternativa em PDF. Você já pensou em processamento paralelo? No momento, o script usa apenas um único núcleo / thread da CPU.
DanMan 30/08/19

@DanMan Infelizmente, torná-lo paralelo não é uma solução mágica para acelerar qualquer coisa. Eu precisaria criar um perfil do código para identificar o gargalo. Se o gargalo é a análise de XML, desculpe, essa parte não pode ser feita em paralelo. Você pode me enviar por e-mail os arquivos SVG exatos que são muito lentos? Sempre que tiver algum tempo, posso investigar o desempenho.
Denilson Sá Maia

Sim, eu tentei fazer isso sozinho e descobriu que a análise de XML é a parte mais lenta, não decodificando as imagens. Dito isto, cElementTreeé suposto ser mais rápido. Mas talvez algo como o Sax funcione melhor também.
DanMan

@DanMan cElementTreeé provavelmente mais rápido. No entanto, no Python 3.3, ambos são iguais . Em algum momento eu provavelmente vou atualizar esse script para Python 3.
Denilson Sá Maia

5

Como outra solução alternativa, você pode salvar como PDF e abrir esse documento com o Inkscape.

Desmarque "incorporar imagens" e bingo, todos os pngs / jpegs serão expelidos para o diretório inicial.

Confuso, mas mais rápido do que brincando com os dados: URL.


Onde você encontrou a opção "incorporar imagens"?
mik01aj

1
Quando você abre o documento PDF no inkscape, ele fica na próxima caixa de diálogo.
Nicholas Wilson

Eu tinha um PDF do qual tentei extrair uma imagem importando-a no Inkscape. Nesse caso, poder fazer isso na importação e não após a importação é ainda mais útil.
user149408

Não tenho certeza, mas dessa maneira qualquer perfil ICC incorporado parece se perder no processo. As imagens que extraí diretamente do SVG por meio do script Python têm perfis ICC incorporados.
DanMan

1

I melhorar o php-script de @Johnny_Bit . A nova versão do script pode usar svg com novas linhas. Extrai várias imagens do arquivo svg e as salva em arquivos png externos. Os arquivos SVG e PNG estão no diretório 'svg', mas você pode alterá-lo na constante '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

Abra seu arquivo no Inkscape e selecione o bitmap que deseja exportar. Clique em Arquivo-> Exportar Bitmap (Ctrl + Shift + E) e ele deverá exportar apenas o bitmap selecionado.


Não gosto desta solução porque ela recodificará a imagem. Eu preferiria uma solução que extraia a imagem em seu formato original.
Denilson Sá Maia

1
Sim, parece que o Inkscape recodifica a imagem, mas salva imagens PNG por padrão. Portanto, estou assumindo que a recodificação é pelo menos sem perdas.
Chris

1
Bem, na verdade não. A imagem incorporada pode ter sofrido transformações (redimensionamento, rotação ...), pode ter sido cortada ou até outra coisa que eu não saiba. O Inkscape certamente exportará o objeto selecionado após aplicar todas essas transformações, o que significa que esta solução não é exatamente sem perdas.
Denilson Sá Maia
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.