"Parser" un fichier SVG

RocouRocou Membre
11 févr. modifié dans API SwiftUI #1

Bonjour,

Je cherche à manipuler un fichier SVG en SwiftUI.
J'utilise pour cela la bibliothèque SVGView (https://github.com/exyte/SVGView).
Tout fonctionne bien cependant pour aller plus loin. je dois "parser" le fichier SVG afin de récupérer les "path id", c'est à dire le nom (chaîne) donné aux formes.

J'ai beau scruter les sources de SVGView, je ne vois pas comment faire.

Quelqu'un a-t-il déjà réalisé cela ? Même autrement qu'avec SVGView, bien sûr.

Mots clés:

Réponses

  • LarmeLarme Membre
    11 févr. modifié #2

    Un SVG, c'est du XML normalement, c'est ce que fait le SVGView.
    Il faut le faire à la main je pense.
    Mais quand je vois view.getNode(byId: "part") dans ta library, j'ai l'impression qu'ils exposent une partie de leur parsing...
    Si tu as un exemple de SVG et un "path id" particulier, on pourra peut-être t'aider à le retrouver.

  • RocouRocou Membre
    11 févr. modifié #3

    Merci Larme.
    Voici l'exemple sur lequel je travaille:

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- Generated by Pixelmator Pro 3.5.6 -->
    <svg width="209" height="120" viewBox="0 0 209 120" xmlns="http://www.w3.org/2000/svg">
        <path id="Trac-copie-7" fill="#0f0f0f" fill-opacity="0.148325" fill-rule="evenodd" stroke="none" d="M 56 51 L 73 78 L 84 71 L 67 44 L 56 51 Z"/>
        <path id="Trac-copie-6" fill="#0f0f0f" fill-opacity="0.148325" fill-rule="evenodd" stroke="none" d="M 97 32 L 106 47 L 97 54 L 92 47 L 86 51 L 80 42 L 97 32 Z"/>
        <path id="Trac-copie-5" fill="#0f0f0f" fill-opacity="0.148325" fill-rule="evenodd" stroke="none" d="M 122 11 L 111 16 L 128 49 L 139 43 L 122 11 Z"/>
        <path id="Trac-copie-4" fill="#0f0f0f" fill-opacity="0.148325" fill-rule="evenodd" stroke="none" d="M 0 97 L 0 120 L 13 120 L 13 97 L 0 97 Z"/>
        <path id="Trac-copie-3" fill="#0f0f0f" fill-opacity="0.148325" fill-rule="evenodd" stroke="none" d="M 27 94 L 37 94 L 37 103 L 50 103 L 50 82 L 27 82 L 27 94 Z"/>
        <path id="Trac-copie-2" fill="#0f0f0f" fill-opacity="0.148325" fill-rule="evenodd" stroke="none" d="M 139 8 L 151 1 L 171 32 L 159 39 L 139 8 Z"/>
        <path id="Trac-copie" fill="#0f0f0f" fill-opacity="0.148325" fill-rule="evenodd" stroke="none" d="M 190 0 L 209 32 L 198 39 L 178 7 L 190 0 Z"/>
    </svg>
    

    Je voudrais récupérer et initialiser un tableau avec "Trac-copie-7", "Trac-copie-6", Trac-copie-5", etc.

  • RocouRocou Membre
    11 févr. modifié #4

    Bon, @Larme sur tes conseils, j'ai tout fait à la main. Finalement ce n'était pas difficile (je n'avais pas vu qu'un fichier SVG est en fait un fichier XML ... :D )

    Pour les débutants comme moi qui chercheraient la même chose, voici la soluce.

    Une classe:

    class SVGPathIDExtractor: NSObject, XMLParserDelegate {
        var pathIDs = [String]()
        var currentElement = ""
    
        func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String: String] = [:]) {
            currentElement = elementName
            if elementName == "path", let id = attributeDict["id"] {
                pathIDs.append(id)
            }
        }
    }
    

    Une fonction:

    func extractPathIDs(from svgURL: URL) -> [String]? {
        guard let parser = XMLParser(contentsOf: svgURL) else { return nil }
        let extractor = SVGPathIDExtractor()
        parser.delegate = extractor
        if parser.parse() {
            return extractor.pathIDs
        } else {
            print("Échec du parsing SVG")
            return nil
        }
    }
    

    En enfin l'utilisation:

    let zones = extractPathIDs(from: svgURL) ?? []

    Je ne suis pas très sûr de moi concernant l'opérateur de coalescence car zones n'est pas sensé être à nil.
    Il faudra que je pousse un peu plus la gestion de cette erreur potentielle.

  • LarmeLarme Membre

    @Rocou a dit :
    En enfin l'utilisation:

    let zones = extractPathIDs(from: svgURL) ?? []

    Je ne suis pas très sûr de moi concernant l'opérateur de coalescence car zones n'est pas sensé être à nil.
    Il faudra que je pousse un peu plus la gestion de cette erreur potentielle.

    Cela dépend si tu souhaites différencier 3 cas :
    nil : le parser se rate
    vide : pas d'ids retrouvés
    rempli : des ids retrouvés

    Si nil et vide te semblent être pareil, alors au lieu de faire else { return nil }, écrit else { return [] } et change la signature -> [String]? en -> [String].

  • LarmeLarme Membre
    12 févr. modifié #6

    Après test, dans ton cas (je ne suis pas expert en SVG, donc je ne sais pas si tous les paths auront cet attribut id=, mais...), sans avoir à parser toi-même, avec let view = SVGView(contentsOf: svgURL)

    if let group = view.svg as? SVGGroup {
        group.contents.forEach {
            print($0.id)
        }
    } else {
        print("view.svg is not a SVGGroup")
    }
    

    Donc en une seule ligne :

    let ids = (view.svg as? SVGGroup)?.contents.compactMap { $0.id }
    
  • RocouRocou Membre

    Ha oui, c'est plus élégant et plus simple. Merci @Larme

Connectez-vous ou Inscrivez-vous pour répondre.