Outils pour utilisateurs

Outils du site


issue148:inkscape

So far in this series, we’ve used some JavaScript to change the fill or stroke color of an object in an SVG file when loaded in a web browser. But JavaScript in SVG is the same language, powered by the same engine, as JavaScript in HTML. That makes it a powerful tool for doing far more than just tweaking some colors.

Jusqu'à présent dans cette série, nous avons utilisé un peu de JavaScript pour changer la couleur de remplissage ou de trait d'un objet dans un fichier SVG lorsqu'il est chargé dans un navigateur Web. Mais JavaScript au sein de SVG est le même langage, propulsé par le même moteur, que JavaScript en HTML. Cela en fait un outil puissant pour faire bien plus que simplement retoucher certaines couleurs.

First, a quick reminder of the structure of an XML tag, of the sort you might find in an SVG file:

Tout d'abord, un petit rappel de la structure d'une balise XML, du type de celle que l'on peut trouver dans un fichier SVG :

<tagName id=“uniqueID” attributeName=“attributeValue”>textContent</tagName>

Let’s look at each part of this individually:

Examinons chaque partie individuellement :

• tagName – The name of the tag or element. In SVG, this might be a ‘g’ for a group, or ‘rect’ for a rectangle or square, for example.

• tagName - Le nom de la balise ou de l'élément. Dans SVG, il peut s'agir d'un 'g' pour un groupe, ou d'un 'rect' pour un rectangle ou un carré, par exemple.

• id – This is just an attribute that happens to be named ‘id’, but the rules of XML dictate that IDs must be unique within a document. That makes them handy for targeting with the querySelector() function.

• id - C'est juste un attribut qui s'avère être nommé 'id', mais les règles du XML exigent que les ID doivent être uniques dans un document. Cela les rend pratiques pour le ciblage avec la fonction querySelector().

• attributeName – Each tag may have zero or more attributes which contain additional data associated with the element. In XML languages, these always take the form of attributeName=“attributeValue”, whereas HTML (confusingly) allows for some attributes that have no value associated with them. Each attributeName must be unique within the element, but may appear many times across different elements. The attributeValue will vary depending on what the attribute is actually used for.

• attributeName - Chaque balise peut avoir zéro attribut ou plus qui contiennent des données supplémentaires associées à l'élément. Dans les langages XML, ceux-ci prennent toujours la forme attributeName=“attributeValue”, alors que HTML (de façon confuse) permet certains attributs qui n'ont aucune valeur associée. Chaque attributeName doit être unique dans l'élément, mais peut apparaître plusieurs fois dans différents éléments. La valeur de l'attribut variera en fonction de l'utilisation réelle de l'attribut.

• textContent – This is not so common in XML. Usually, an element will contain zero or more child elements before the closing tag (the </tagName> in this example), but a few elements allow for plain text to be included. In SVG, the most common cases are <text> and <tspan> elements, where the plain text holds the text string that will be rendered.

• textContent - Ce n'est pas si commun en XML. Habituellement, un élément contiendra zéro ou plus d'éléments enfants avant la balise de fermeture (le </tagName> dans cet exemple), mais quelques éléments permettent d'inclure du texte brut. Dans SVG, les cas les plus courants sont des éléments <text> et <tspan>, où le texte brut contient la chaîne de texte qui sera traitée.

There are also a couple of variations to be aware of. Self-closing tags take the form <tagName … />. By definition these can have no children or text content. XML documents also make use of namespaces, which are defined in the main tag for the document (e.g. the <svg> tag), and may then crop up appended to tags and attributes with a colon. You won’t see these often: usually a default namespace is declared, in which case namespaces need to be added only to tags and attributes that are from ‘foreign’ XML languages.

Il y a aussi quelques variations à prendre en compte. Les balises à fermeture automatique prennent la forme <tagName… />. Par définition, elles ne peuvent pas avoir d'enfants ou de contenu textuel. Les documents XML utilisent également des espaces de noms (namespaces), qui sont définis dans la balise principale du document (par exemple la balise <svg>), et peuvent ensuite être ajoutés aux balises et attributs avec deux points. Vous ne les verrez pas souvent : en général, un espace de noms par défaut est déclaré, auquel cas les espaces de noms ne doivent être ajoutés qu'aux balises et attributs qui proviennent de langages XML “étrangers”.

The theory is fine, but let’s see how these parts manifest themselves with yet another super-simplified SVG file:

La théorie est bonne, mais voyons comment ces parties se présentent avec un autre fichier SVG super-simplifié :

<svg xmlns=“http://www.w3.org/2000/svg” viewBox=“0 0 100 100”>

<text id="text" x="50" y="50" text-anchor="middle">
  This is <tspan id="ts1">some</tspan> <tspan id="ts2">SVG text</tspan>
</text>

</svg>

Breaking this down, we have an <svg> tag containing a <text> tag with some further content. The <svg> tag has a couple of attributes. The first defines the default namespace, and is required so that the browser knows this is a document conforming to the W3C’s SVG spec, and not some other type of file that happens to have a tag name called ‘svg’. The second attribute sets up the coordinate space we’ll be using in this file – I usually stick with “0 0 100 100” for my hand-created files, as I can then treat my values as percentages within the image.

En décomposant cet exemple, nous avons une balise <svg> contenant une balise <text> avec un peu plus de contenu. La balise <svg> possède quelques attributs. Le premier définit l'espace de noms par défaut et est nécessaire pour que le navigateur sache qu'il s'agit d'un document conforme aux spécifications SVG du W3C, et non d'un autre type de fichier qui a un nom de balise appelé 'svg'. Le deuxième attribut définit l'espace de coordonnées que nous utiliserons dans ce fichier - je m'en tiens généralement à “0 0 100 100” pour mes fichiers créés à la main, car je peux alors traiter mes valeurs en pourcentage dans l'image.

The <text> tag also has some attributes. The ID is self-explanatory. The others set the ‘anchor point’ for the text to the middle of the image (50, 50), and indicate that the anchor point should be in the middle of the text (i.e. the text is centered, not left- or right-aligned).

La balise <text> possède également certains attributs. L’ID s'explique de lui-même. Les autres fixent le « anchor point » (point d'ancrage) du texte au milieu de l'image (50, 50) et indiquent que le point d'ancrage doit être au milieu du texte (c'est-à-dire que le texte est centré, et non aligné à gauche ou à droite).

Finally the <text> tag contains a mixture of text content and a couple of <tspan> elements with IDs, which will allow us to specifically target those parts of the text via JavaScript.

Enfin, la balise <text> contient un mélange de contenu texte et quelques éléments <tspan> avec ID, ce qui nous permettra de cibler spécifiquement ces parties du texte via JavaScript.

Save the file and load it into a web browser – preferably Firefox or Chrome, as they have better developer tools than most others. From the previous articles, you already know how to add JavaScript to your SVG file, either directly in Inkscape or by linking to an external JS file, but we won’t be doing that today. For the rest of this article, we’re going to rattle through a few ways you can affect your SVG, but we’ll do so within the browser’s developer tools. Any of these commands or techniques can be added to your own JavaScript if you want to create something less ephemeral.

Sauvegardez le fichier et chargez-le dans un navigateur Web - de préférence Firefox ou Chrome, car ils disposent de meilleurs outils de développement que la plupart des autres navigateurs. Dans les articles précédents, vous avez déjà vu comment ajouter du JavaScript à votre fichier SVG, soit directement dans Inkscape, soit en créant un lien vers un fichier JS externe, mais nous ne le ferons pas aujourd'hui. Pour la suite de cet article, nous allons vous présenter diférentes façons d'impacter votre SVG, mais nous allons le faire avec les outils de développement du navigateur. Chacune de ces commandes (ou techniques) peut être ajoutée à votre propre JavaScript si vous voulez créer quelque chose de moins éphémère.

Press F12 or use the menu to open your browser’s developer tools. Somewhere along the top should be a row of tabs (though they’re not always clearly styled as such). Make sure you have the “Console” tab selected. If the panel is already filled with text, find the button in the console’s toolbar to clear it, for clarity. Click inside the console area to give it the focus, and type the following (followed by the Enter key):

Appuyez sur F12 ou utilisez le menu pour ouvrir les outils de développement de votre navigateur. Quelque part en haut de l’écran, il devrait y avoir une rangée d'onglets (bien qu'ils ne soient pas toujours clairement stylisés comme tels). Assurez-vous d'avoir sélectionné l'onglet « Console ». Si le panneau est déjà rempli de texte, trouvez le bouton dans la barre d'outils de la console pour l'effacer, pour plus de clarté. Cliquez à l'intérieur de la console pour lui donner le focus, et tapez ce qui suit (suivi de la touche Entrée) :

var t = document.querySelector(“#text”);

The console will display the string “undefined” at this point. That’s nothing to worry about, it just indicates that the line you entered didn’t return a value. But what it has done is find the element with an ID of “text” and assign it to the variable “t”. You can confirm that by typing the letter “t” on its own, then pressing Enter. The console should show a representation of the <text> element, looking something like that shown above.

La console affichera la chaîne de caractères « undefined » à ce point. Il n'y a pas de quoi s'inquiéter, cela indique simplement que la ligne que vous avez saisie n'a pas retourné de valeur. Mais ce qu'il a fait, c'est de trouver l'élément avec un ID « text » et de l'affecter à la variable « t ». Vous pouvez le confirmer en tapant la lettre « t », puis en appuyant sur Entrée. La console doit afficher une représentation de l'élément <text>, ressemblant à celle illustrée ci-dessus.

Let’s use some JavaScript we already know to reduce the size of the font a little. Type this into the console:

Utilisons un peu de JavaScript que nous connaissons déjà pour réduire un peu la taille de la police. Tapez ceci dans la console :

t.style.fontSize = “10px”;

The SVG content should react as soon as you press the Enter key. Type the letter “t” again and you’ll see that the element now has a “style” attribute with the font-size property set. Notice that we set “fontSize” in JS, but the CSS in the attribute shows “font-size”. If you tried to use the latter in JavaScript, it would be interpreted as trying to subtract the “size” variable from the “font” variable, and would throw an error. As a general rule, any CSS property containing embedded hyphens is available as a JavaScript property by removing the hyphens and capitalising the first letter of all but the first word.

Le contenu SVG devrait réagir dès que vous appuyez sur la touche Entrée. Tapez à nouveau la lettre « t » et vous verrez que l'élément a maintenant un attribut « style » avec l'ensemble de propriétés « font-size ». Notez que nous avons défini « fontSize » dans JS, mais le CSS dans l'attribut montre « font-size ». Si vous essayez d'utiliser ce dernier en JavaScript, cela serait interprété comme une tentative de soustraire la variable « size » de la variable « font », et cela provoquerait une erreur. En règle générale, toute propriété CSS contenant des traits d'union intégrés est disponible en tant que propriété JavaScript en supprimant les traits d'union et en mettant en majuscule la première lettre de tous les mots sauf le premier.

Breaking down the line above, you know that “t” is a JavaScript representation of our XML node. The browser exposes various properties and methods (functions tied to a specific object) on that node, including the “style” property. This property, in turn, has a “fontSize” property, which we’ve set to a value of “10px”. But the browser treats the “style” property a little differently to most JavaScript properties, and instead also applies any changes to the “style” attribute in the XML. In this instance, it doesn’t matter whether you change the attribute or the property – but that’s not usually the case.

En décomposant la ligne ci-dessus, vous savez que « t » est une représentation JavaScript de notre nœud XML. Le navigateur expose diverses propriétés et méthodes (fonctions liées à un objet spécifique) sur ce nœud, dont la propriété « style ». Cette propriété, à son tour, a une propriété « fontSize », que nous avons réglée à une valeur de « 10px ». Mais le navigateur traite la propriété « style » un peu différemment de la plupart des propriétés JavaScript, et applique à la place tout changement à l'attribut « style » dans le XML. Dans ce cas, peu importe que vous changiez l'attribut ou la propriété, mais ce n'est généralement pas le cas.

To change most attributes, therefore, you can’t just set a correspondingly named JavaScript property. Instead, you have to use the setAttribute() method that we’ve looked at previously. Here’s how we might move the text up a little:

Pour changer la plupart des attributs, vous ne pouvez donc pas simplement définir une propriété JavaScript nommée en conséquence. Au lieu de ça, vous devez utiliser la méthode setAttribute() que nous avons examinée précédemment. Voici comment on pourrait remonter un peu le texte :

t.setAttribute(“y”, 20);

Type “t” again to see the XML, and you’ll notice the “y” attribute now has a value of “20”. We can also retrieve that value using the getAttribute() method:

Tapez à nouveau « t » pour voir le XML, et vous remarquerez que l'attribut « y » a maintenant une valeur de « 20 ». Nous pouvons aussi récupérer cette valeur en utilisant la méthode getAttribute() :

t.getAttribute(“y”); Returns “20” Remembering that the y-axis in SVG runs from the top of the screen to the bottom, you might be inclined to try some code like this to move the text down by 10 units: Rappelez-vous que l'axe des y dans SVG va du haut de l'écran vers le bas, vous pourriez être enclin à essayer un code comme celui-ci pour déplacer le texte de 10 unités vers le bas : var yPos = t.getAttribute(“y”); t.setAttribute(“y”, yPos + 10); Gah! Where did the text go!? Actually it’s still there, but it’s been positioned so far down in the image that it’s dropped out of the 100×100 viewBox, so isn’t visible. But why is that, when we just wanted to adjust the value from 20 to 30? Bah ! Où est passé le texte ? En fait, il est toujours là, mais il a été positionné si bas dans l'image qu'il est sorti de la viewBox 100×100 et n'est donc pas visible. Mais pourquoi cela, alors que nous voulions simplement ajuster la valeur de 20 à 30 ? The problem is that XML is a text-based system, and doesn’t really have a concept of different data types. All attributes are therefore text strings, regardless of the value you put in, so our call to getAttribute() returns the string “20”, not the number 20. JavaScript then tries to be ‘helpful’ by determining that we’re trying to ‘add’ the number 10 to the string “20”. Since you can’t add a number to a string, it automatically converts the number into a string (“10”), then concatenates the two, to give a result of “2010”. That’s the value we end up putting into the attribute in our setAttribute() call, so our text ends up being moved to a y-position of 2010 units! Le problème est que XML est un système basé sur le texte, et n'a pas vraiment un concept de différents types de données. Tous les attributs sont donc des chaînes de texte, quelle que soit la valeur que vous entrez, donc notre appel à getAttribute() renvoie la chaîne « 20 », pas le nombre 20. JavaScript essaie alors de se rendre « utile » en déterminant que nous essayons d'ajouter le chiffre 10 à la chaîne « 20 ». Comme vous ne pouvez pas ajouter un nombre à une chaîne, il convertit automatiquement le nombre en une chaîne (« 10 »), puis concatène les deux, pour donner un résultat de « 2010 ». C'est la valeur que nous finissons par mettre dans l'attribut dans notre appel setAttribute() et notre texte finit par être déplacé vers une position y avec une valeur égale à 2010 ! We can fix this by converting the value returned from getAttribute() into a number. We only want an integer value, so the parseInt() function is the tool to use – but there is also a parseFloat() if you need to deal with decimal fractions. parseInt() has a second parameter for the number base that you should always provide (with a value of 10 for a decimal conversion) to avoid some rare-but-odd corner case bugs when converting certain strings. Entering the following lines into the console should get us the result we were looking for: Nous pouvons corriger cela en convertissant la valeur retournée par getAttribute() en nombre. Nous voulons seulement une valeur entière, donc la fonction parseInt() est celle à utiliser, mais il y a aussi un parseFloat() si vous avez besoin de traiter des fractions décimales. parseInt() a un second paramètre pour la base de nombres que vous devriez toujours fournir (avec une valeur de 10 pour une conversion décimale) pour éviter certains bugs (cas rares, mais étranges), quand vous convertissez certaines chaînes. Le fait de saisir les lignes suivantes dans la console devrait nous donner le résultat que nous recherchons : t.setAttribute(“y”, 20); var yPosNumeric = 0; yPos = t.getAttribute(“y”); yPosNumeric = parseInt(yPos, 10); t.setAttribute(“y”, yPosNumeric + 10); You can run the last three lines repeatedly to move your text down by 10 units each time. Vous pouvez exécuter les trois dernières lignes plusieurs fois pour réduire votre texte de 10 unités à chaque fois. Now we know how to get and set attributes, but you can also remove them entirely. This will get rid of the “style” attribute we indirectly created earlier, returning the text to its ‘natural’ size: Nous savons maintenant comment obtenir et définir les attributs, mais vous pouvez aussi les supprimer entièrement. Cela nous débarrassera de l'attribut “style” que nous avons indirectement créé plus tôt, ce qui ramènera le texte à sa taille “naturelle” : t.removeAttribute(“style”); There’s no equivalent createAttribute() call - setting the value of a non-existent attribute using setAttribute() will automatically create it. Let’s get our style back by manipulating the attribute rather than the property: Il n'y a pas d'appel équivalent à createAttribute() - définir la valeur d'un attribut inexistant en utilisant setAttribute() le créera automatiquement. Récupérons notre style en manipulant l'attribut plutôt que la propriété : t.setAttribute(“style”, “font-size: 10px;”); As well as working with attributes, you can also dynamically change the text content of an element. Let’s type a few lines into the console to alter the first <tspan>: En plus d'utiliser les attributs, vous pouvez également modifier dynamiquement le contenu textuel d'un élément. Tapez quelques lignes dans la console pour modifier le premier <tspan> : var ts1 = document.querySelector(“#ts1”); ts1.style.fill = “#ff0000”; ts1.style.fontStyle = “italic”; ts1.textContent = “a bit of”; [relevant image shown above] [voir illustration ci-dessous] Being able to change the text content via JavaScript opens up a world of possibilities, including images with descriptions that can be switched between different languages, or ones that populate with data requested from a server somewhere such as live graphs and stock tickers. That degree of sophistication is a little beyond this series, but here’s a trivial example that prompts the user to enter their name, then updates the text on the page accordingly: Être capable de modifier le contenu du texte via JavaScript ouvre un monde de possibilités, y compris des images avec des descriptions qui peuvent être permutées entre différentes langues, ou des images qui contiennent des données demandées à un serveur, comme des graphiques en direct et des cotations boursières. Ce degré de sophistication va un peu au-delà de cette rubrique, mais voici un exemple trivial qui invite l'utilisateur à entrer son nom, puis met à jour le texte sur la page en conséquence : ts1.textContent = prompt(“What is your name?”) + “'s”; [relevant image shown below] [voir illustration ci-dessous] Modifying the properties, attributes and text content of existing elements is useful, but to have complete control over a document we also need to be able to add and remove elements using JavaScript. The removal part is trivial, provided you can get a reference to the element using querySelector() or some other mechanism. Let’s delete our first <tspan> entirely: Modifier les propriétés, les attributs et le contenu textuel des éléments existants est utile, mais pour avoir un contrôle complet sur un document, nous devons également pouvoir ajouter et supprimer des éléments en utilisant JavaScript. La partie de suppression est simple, à condition que vous puissiez obtenir une référence à l'élément en utilisant querySelector() ou un autre mécanisme. Supprimons notre premier <tspan> entièrement : ts1.remove(); Adding a new element to the page can be trivially easy, or it can be rather convoluted. Let’s start with the easy method, by adding another <tspan> to the <text> element, which is still assigned to our “t” variable: Ajouter un nouvel élément à la page peut être vraiment facile ou plutôt compliqué. Commençons par la méthode facile, en ajoutant un autre <tspan> à l'élément <text>, qui est toujours affecté à notre variable « t » : t.innerHTML += '<tspan id=“ts3” style=“fill: red;”>!!!</tspan>'; Even though we’re working on an SVG file, which is a form of XML document, we still have to use the “innerHTML” property. This returns all the descendants of the specified node as a string – basically a string of HTML (or XML in this case) much like the ones you type into a text editor. The “+=” operator essentially retrieves a value, adds or concatenates something to it, and puts the result back into the same place. In our case it has the effect of appending a new <tspan> to the end of the existing content. Même si nous travaillons sur un fichier SVG, qui est une forme de document XML, nous devons quand même utiliser la propriété « innerHTML ». Cela retourne tous les descendants du nœud spécifié sous la forme d'une chaîne de caractères - essentiellement une chaîne de caractères HTML (ou XML dans ce cas-ci) comme celles que vous tapez dans un éditeur de texte. Essentiellement, l'opérateur « += » récupère une valeur, lui ajoute ou concatène quelque chose, et remet le résultat au même endroit. Dans notre cas, cela a pour effet d'ajouter un nouveau <tspan> à la fin du contenu existant. Let’s do something similar, but with a more complex approach… Faisons quelque chose de similaire, mais avec une approche plus complexe… var ns = “http://www.w3.org/2000/svg”; var newTS = document.createElementNS(ns, “tspan”); newTS.id = “ts4”; newTS.setAttribute(“style”, “fill: blue”); newTS.textContent = “!!!”; t.appendChild(newTS); That’s a lot more lines to explain: Là, il y a un peu plus de lignes à expliquer : • We set up a variable, “ns”, that will hold our SVG namespace. Usually this is done once at the top of the JavaScript so you can use it in multiple places. • Nous avons mis en place une variable, « ns », qui contiendra notre espace de noms SVG. Habituellement, cela se fait une fois en haut du JavaScript pour que vous puissiez l'utiliser à plusieurs endroits. • We create a new <tspan> element. If you’ve ever done this in HTML, you might be familiar with document.createElement(), but, in the XML world, we need to use a namespace-aware equivalent, createElementNS(), and pass the namespace as the first parameter. • Nous créons un nouvel élément <tspan>. Si vous avez déjà fait cela en HTML, vous êtes peut-être familier avec document.createElement(), mais, dans le monde XML, nous devons utiliser un équivalent de l'espace de noms, createElementNS(), et passer l'espace de noms comme premier paramètre. • We give the element an ID to make it easier to get hold of later. We could have used setAttribute() for this, but the browser has an implicit mapping between the property and attribute in this case, in the same manner as we saw earlier with the ‘style’ property. • Nous donnons à l'élément un ID pour qu'il soit plus facile à récupérer plus tard. Nous aurions pu utiliser setAttribute() pour cela, mais le navigateur a une correspondance implicite entre la propriété et l'attribut dans ce cas, de la même manière que nous l'avons vu précédemment avec la propriété « style ». • Now we can set an attribute on the new element. We would need to repeat a line like this for each attribute we wish to set. • Nous pouvons maintenant définir un attribut sur le nouvel élément. Nous aurions besoin de répéter une ligne comme celle-ci pour chaque attribut que nous souhaitons définir. • We’ve created a <tspan>, so we won’t see much unless we also give it some text content. • Nous avons créé un <tspan>, donc nous ne verrons pas grand-chose à moins de lui donner aussi du contenu texte. • Finally, we append it as a child of the object referred to by the “t” variable – our <text> element. • Enfin, nous l'ajoutons en tant qu'enfant de l'objet auquel se réfère la variable “t” - notre élément <text>. Clearly that’s a lot more typing than the innerHTML version, so why would you ever want to take this approach? Precisely because it’s verbose, splitting the element, attributes and text content into separate lines, it lends itself to some types of looping or manipulation that can otherwise become unwieldy when using just a single chunk of text. De toute évidence, c'est beaucoup plus de code que la version innerHTML, alors pourquoi voudriez-vous adopter cette approche ? Précisément parce qu'il est verbeux, divisant l'élément, les attributs et le contenu du texte en lignes séparées, il se prête à certains types de bouclage ou de manipulation qui, autrement, peuvent devenir difficiles à manier en utilisant un seul morceau de code. Consider trying to plot a graph using SVG. Each point on the graph might be represented by a <circle> requiring several attributes: x, y, r and fill, for example. These values will be determined by some data source, and may need to be manipulated to get them into the right format for SVG. All of that is a little easier to arrange, and can lead to clearer code, if you deal with each attribute separately. Certainly it can be done with the “innerHTML” approach, but as the code and SVG content become more complex, an approach that relies on building and manipulating strings can become harder to follow, and less robust. Et si vous tentiez de tracer un graphique à l'aide de SVG ? Chaque point du graphique peut être représenté par un <cercle> nécessitant plusieurs attributs : x, y, r et fill, par exemple. Ces valeurs seront déterminées par une source de données, et doivent être manipulées peut-être pour qu'elles soient dans le bon format pour SVG. Tout cela est un peu plus facile à organiser, et peut conduire à un code plus clair, si vous traitez chaque attribut séparément. Certes, cela peut se faire avec l'approche « innerHTML », mais à mesure que le code et le contenu SVG deviennent plus complexes, une approche qui repose sur la construction et la manipulation de chaînes de caractères peut devenir plus difficile à suivre, et moins robuste. Next time, we’ll build on the techniques used in this instalment, to further investigate ways to manipulate the individual elements in an SVG document through JavaScript. La prochaine fois, nous nous appuierons sur les techniques utilisées dans cet article pour étudier, un peu plus en avant, les moyens de manipuler les éléments individuels d'un document SVG via JavaScript.

issue148/inkscape.txt · Dernière modification : 2019/09/14 15:09 de d52fr