Comment extraire une image incorporée d'un fichier SVG?

26

J'ai un fichier SVG qui contient au moins une image JPG / PNG intégrée à l'intérieur. Je veux extraire les images JPG / PNG de ce fichier SVG et les enregistrer sur le disque.

J'ajoute la inkscapebalise car c'est le programme que j'utilise pour éditer les fichiers SVG, mais j'accepte également les solutions utilisant d'autres outils.

Denilson Sá Maia
la source
1
Si rien d'autre, Python pourrait probablement le faire avec de la colle personnalisée en utilisant lxml et PIL (ou équivalent).
Keith
@ Keith, en effet, je viens d'écrire un script Python pour résoudre cette question. Il utilise la xml.etreebibliothèque intégrée.
Denilson Sá Maia

Réponses:

30

Ma propre solution (ou ... solution de contournement):

  1. Sélectionnez l'image dans Inkscape
  2. Ouvrez le intégré XML Editor( Shift+ Ctrl+ X)
  3. Sélectionnez l' xlink:hrefattribut, qui contiendra l'image en tant que données: URI
  4. Copiez l' data:URI entier
  5. Collez cet data:URI dans un navigateur et enregistrez-le à partir de là.

Alternativement, je peux ouvrir le fichier SVG dans n'importe quel éditeur de texte, localiser l' data:URI et le copier à partir de là.

Bien que cette solution fonctionne, elle est un peu lourde et j'aimerais en apprendre une meilleure.

Denilson Sá Maia
la source
2
+1 - J'ai exporté une image de 3,5 Mo en utilisant cette méthode qui a pris du temps mais qui a fonctionné. D'une manière ou d'une autre, la fonction "Extraire l'image" ne fonctionnait pas pour moi.
Martin
Veuillez également consulter un script Python en ligne de commande à cet effet.
Denilson Sá Maia
17

Il y a une meilleure solution à la place:

aller à Extensions -> Images -> Extract Image... , là, vous pouvez enregistrer l'image raster sélectionnée sous forme de fichier. Cependant, cette extension fonctionne bizarrement et fonctionne en quelque sorte assez lentement (mais parfaitement bien).

Autre remarque: cette extension est encombrante et meurt silencieusement sur des images grandes et variées. De plus, avec un grand nombre d'images raster, cela peut augmenter l'utilisation de la mémoire d'Inkscape à des niveaux horribles (comme 3 Go après seulement une poignée d'images extraites).

Parce que j'ai environ 20 fichiers svg avec environ 70 images raster chacune, chaque image d'au moins 1 Mo, j'avais besoin d'une solution différente. Après une courte vérification à l'aide de la pointe Denilson Sá, j'ai conçu le script php suivant, qui extrait les images des fichiers 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);

De cette façon, je peux obtenir toutes les images que je veux et md5 m'évite d'obtenir des images répétées.

Je parie qu'il doit y avoir un autre moyen beaucoup plus simple, mais c'est aux développeurs d'Inkscape de le faire mieux.

Johnny_Bit
la source
Remarque: Votre script ne prend en charge qu'une seule data:URL par ligne et ne prend pas en charge les sauts de ligne à l'intérieur de l'attribut href (inkscape les ajoute pour les URL de données et la spécification base64 exige même que les lignes ne dépassent pas 76 caractères ). Joli script pour un hack rapide, mais il ne fonctionne pas avec toutes sortes de SVG.
Denilson Sá Maia
@Johnny_Bit +1 pour l'utilisation de la somme md5 pour éviter la duplication des fichiers. J'améliore votre script ci-dessous .
Ivan Z
bon, mars 2019 et a travaillé facilement grand avec une image assez grande. Et assez vieux portable / ubuntu / inkscape 0.48.4. Merci!
Gaoithe
9

Enfin, des années plus tard, j'ai écrit un script pour extraire correctement toutes les images d'un fichier SVG, en utilisant une bibliothèque XML appropriée pour analyser le code SVG.

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

Ce script est écrit pour Python 2.7 mais devrait être assez facile à convertir en Python 3. Encore mieux, environ 50 lignes peuvent être supprimées après la conversion en Python 3.4, en raison des nouvelles fonctionnalités introduites dans cette version.

Denilson Sá Maia
la source
Merci, car cela fonctionne. Mais c'est beaucoup plus lent que la solution de contournement PDF. Avez-vous pensé au traitement parallèle? À l'heure actuelle, le script n'utilise qu'un seul cœur / thread de CPU.
DanMan
@DanMan Malheureusement, le rendre parallèle n'est pas une solution magique pour accélérer quoi que ce soit. J'aurais besoin de profiler le code afin d'identifier le goulot d'étranglement. Si le goulot d'étranglement est l'analyse XML, je suis désolé, cette partie ne peut pas être effectuée en parallèle. Pouvez-vous m'envoyer par e-mail les fichiers SVG exacts qui sont trop lents? Chaque fois que j'ai du temps, je peux enquêter sur les performances.
Denilson Sá Maia
Oui, j'ai essayé de le faire moi-même, et il s'est avéré que l'analyse XML est la partie lente, pas le décodage des images. Cela dit, cElementTreeest censé être plus rapide. Mais peut-être que quelque chose comme Sax fonctionne mieux aussi.
DanMan
@DanMan cElementTreeest probablement plus rapide. Cependant, sur Python 3.3, les deux sont identiques . À un moment donné, je mettrai probablement à jour ce script en Python 3.
Denilson Sá Maia
5

Encore une autre solution de contournement, vous pouvez enregistrer au format PDF, puis ouvrir ce document avec Inkscape.

Décochez "incorporer des images", et bingo, tous les pngs / jpeg seront rejetés dans votre répertoire personnel.

Désordonné, mais plus rapide que de se moquer des données: URL.

Nicholas Wilson
la source
Où avez-vous trouvé cette option "incorporer des images"?
mik01aj
1
Lorsque vous ouvrez le document PDF dans inkscape, il se trouve dans la boîte de dialogue suivante.
Nicholas Wilson
J'avais un PDF à partir duquel j'ai essayé d'extraire une image en l'important dans Inkscape. Dans ce cas, être en mesure de le faire à l' importation plutôt qu'après l' importation est encore plus pratique.
user149408
Je ne suis pas sûr, mais de cette façon, tous les profils ICC intégrés semblent se perdre dans le processus. Les images que j'ai extraites directement du SVG via ce script Python ont des profils ICC intégrés.
DanMan
1

J'améliore le script php-de @Johnny_Bit . La nouvelle version du script peut utiliser svg avec de nouvelles lignes. Il extrait plusieurs images sous forme de fichier svg et les enregistre dans des fichiers png externes. Les fichiers svg et png sont dans le répertoire 'svg', mais vous pouvez le changer dans la 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>';
}

?>
Ivan Z
la source
0

Ouvrez votre fichier dans Inkscape et sélectionnez le bitmap que vous souhaitez exporter. Cliquez sur Fichier-> Exporter le bitmap (Ctrl + Shift + E) et il devrait exporter uniquement le bitmap sélectionné.

Chris
la source
Je n'aime pas cette solution car elle réencodera l'image. Je préférerais une solution qui extrait l'image dans son format d'origine.
Denilson Sá Maia
1
Oui, il semble qu'Inkscape réencode l'image mais il enregistre les images PNG par défaut. Je suppose donc que le ré-encodage est au moins sans perte.
Chris
1
Eh bien pas vraiment. L'image incorporée peut avoir eu des transformations (mise à l'échelle, rotation…), peut avoir été écrêtée, ou même quelque chose d'autre que je ne connais pas. Inkscape exportera certainement l'objet sélectionné après avoir appliqué toutes ces transformations, ce qui signifie que cette solution n'est pas exactement sans perte.
Denilson Sá Maia