Retour à la ligne automatique dans le texte SVG

108

Je voudrais afficher un <text>en SVG ce qui encapsulerait automatiquement la ligne dans le conteneur de <rect>la même manière que le texte HTML remplit des <div>éléments. Y a-t-il un moyen de le faire? Je ne veux pas positionner les lignes avec parcimonie en utilisant <tspan>s.

tillda
la source

Réponses:

89

L'habillage de texte ne fait pas partie de SVG1.1, la spécification actuellement implémentée. Vous devriez plutôt utiliser HTML via l' <foreignObject/>élément.

<svg ...>

<switch>
<foreignObject x="20" y="90" width="150" height="200">
<p xmlns="http://www.w3.org/1999/xhtml">Text goes here</p>
</foreignObject>

<text x="20" y="20">Your SVG viewer cannot display html.</text>
</switch>

</svg>
Tangui
la source
5
Ce n'est pas la bonne façon d'utiliser switch, il doit utiliser l'une des chaînes de fonctionnalités définies dans la spécification svg. La solution de secours ne sera jamais utilisée dans votre exemple. Voir w3.org/TR/SVG11/feature.html et w3.org/TR/SVG11/struct.html#SwitchElement .
Erik Dahlström le
22
De plus, <ForeignObject /> n'est pas pris en charge dans IE
Doug Amos
3
Mais sachez que tous les moteurs ne peuvent pas afficher des objets étrangers. En particulier, le batik ne le fait pas.
hrabinowitz
69

Voici une alternative:

<svg ...>
  <switch>
    <g requiredFeatures="http://www.w3.org/Graphics/SVG/feature/1.2/#TextFlow">
      <textArea width="200" height="auto">
       Text goes here
      </textArea>
    </g>
    <foreignObject width="200" height="200" 
     requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
      <p xmlns="http://www.w3.org/1999/xhtml">Text goes here</p>
    </foreignObject>
    <text x="20" y="20">No automatic linewrapping.</text>
  </switch>
</svg>

Notant que même si ForeignObject peut être signalé comme étant pris en charge avec cette fonctionnalité, il n'y a aucune garantie que HTML puisse être affiché car cela n'est pas requis par la spécification SVG 1.1. Il n'y a pas de chaîne de fonctionnalités pour le support html-in-foreignobject pour le moment. Cependant, il est toujours pris en charge dans de nombreux navigateurs, il est donc susceptible de devenir nécessaire à l'avenir, peut-être avec une chaîne de fonctionnalités correspondante.

Notez que l' élément 'textArea' dans SVG Tiny 1.2 prend en charge toutes les fonctionnalités standard de svg, par exemple le remplissage avancé, etc., et que vous pouvez spécifier la largeur ou la hauteur comme auto, ce qui signifie que le texte peut circuler librement dans cette direction. ForeignObject agit comme une fenêtre de découpage.

Remarque: alors que l'exemple ci-dessus est un contenu SVG 1.1 valide, dans SVG 2 l'attribut 'requiredFeatures' a été supprimé, ce qui signifie que l'élément 'switch' essaiera de rendre le premier élément 'g' indépendamment de la prise en charge de SVG 1.2 'textArea ' éléments. Voir les spécifications des éléments de commutation SVG2 .

Erik Dahlström
la source
1
J'étais en train de tester ce code dans FF, le navigateur ne m'a montré ni l'élément textArea ni l'enfant ForeignObject. Ensuite, après avoir lu la spécification, nous avons constaté que l'attribut requiredFeatures se comporte de telle manière que, lorsque sa liste est évaluée à false, l'élément qui a l'attribut requiredFeatures et ses enfants ne sont pas traités. Il n'y aura donc aucune nécessité pour l'élément de commutation. Après avoir supprimé l'élément switch, les enfants de ForeignObject étaient visibles (car mon navigateur (FF, 8.01) supporte svg1.1). Donc, je pense qu'il n'y a pas besoin d'élément de commutation ici. S'il vous plaît, faites-moi savoir.
Rajkamal Subramanian
Mis à jour maintenant pour utiliser un élément <g>. La spécification svg n'indiquait pas aux téléspectateurs de regarder les 'requiredFeatures' sur des éléments inconnus, il faut donc utiliser un élément svg connu pour que cela fonctionne comme prévu.
Erik Dahlström
Merci! J'avais besoin d'utiliser à la xhtml:divplace de div, mais cela pourrait être à cause de d3.js. Je n'ai pas trouvé de référence utile sur TextFlow, existe-t-il (toujours) ou était-ce juste dans un brouillon?
johndodo du
2
Il convient de noter que textarea ne semble pas être pris en charge à l'avenir bugzilla.mozilla.org/show_bug.cgi?id=413360
George Mauer
1
L'exemple ne fonctionne pas dans Chrome. N'ont pas testé dans d'autres navigateurs.
posfan12
15

Le textPath peut être bon dans certains cas.

<svg width="200" height="200"
    xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
 <defs>
  <!-- define lines for text lies on -->
  <path id="path1" d="M10,30 H190 M10,60 H190 M10,90 H190 M10,120 H190"></path>
 </defs>
 <use xlink:href="#path1" x="0" y="35" stroke="blue" stroke-width="1" />
 <text transform="translate(0,35)" fill="red" font-size="20">
  <textPath xlink:href="#path1">This is a long long long text ......</textPath>
 </text>
</svg>
user2856765
la source
3
Uniquement dans le cas où l'enroulement au milieu du mot (et non la césure) est acceptable. Je ne peux pas penser à de nombreux cas au-delà des projets artistiques où ça va. http://jsfiddle.net/nilloc/vL3zj/
Nilloc
4
@Nilloc Tout le monde n'utilise pas l'anglais, cette méthode convient parfaitement au chinois, au japonais ou au coréen.
Zang MingJie
@ZangMingJie Le wrapping pour les langages basés sur des caractères (logographiques) semble être un cas d'utilisation totalement différent de celui de la division des mots. Ce qui est important dans toutes les langues romantiques / latines / cyrilliques / arabes (phonographiques), ce qui était mon point.
Nilloc
11

En me basant sur le code de @Mike Gledhill, je suis allé plus loin et j'ai ajouté plus de paramètres. Si vous avez un SVG RECT et que vous souhaitez que le texte soit enveloppé à l'intérieur, cela peut être pratique:

function wraptorect(textnode, boxObject, padding, linePadding) {

    var x_pos = parseInt(boxObject.getAttribute('x')),
    y_pos = parseInt(boxObject.getAttribute('y')),
    boxwidth = parseInt(boxObject.getAttribute('width')),
    fz = parseInt(window.getComputedStyle(textnode)['font-size']);  // We use this to calculate dy for each TSPAN.

    var line_height = fz + linePadding;

// Clone the original text node to store and display the final wrapping text.

   var wrapping = textnode.cloneNode(false);        // False means any TSPANs in the textnode will be discarded
   wrapping.setAttributeNS(null, 'x', x_pos + padding);
   wrapping.setAttributeNS(null, 'y', y_pos + padding);

// Make a copy of this node and hide it to progressively draw, measure and calculate line breaks.

   var testing = wrapping.cloneNode(false);
   testing.setAttributeNS(null, 'visibility', 'hidden');  // Comment this out to debug

   var testingTSPAN = document.createElementNS(null, 'tspan');
   var testingTEXTNODE = document.createTextNode(textnode.textContent);
   testingTSPAN.appendChild(testingTEXTNODE);

   testing.appendChild(testingTSPAN);
   var tester = document.getElementsByTagName('svg')[0].appendChild(testing);

   var words = textnode.textContent.split(" ");
   var line = line2 = "";
   var linecounter = 0;
   var testwidth;

   for (var n = 0; n < words.length; n++) {

      line2 = line + words[n] + " ";
      testing.textContent = line2;
      testwidth = testing.getBBox().width;

      if ((testwidth + 2*padding) > boxwidth) {

        testingTSPAN = document.createElementNS('http://www.w3.org/2000/svg', 'tspan');
        testingTSPAN.setAttributeNS(null, 'x', x_pos + padding);
        testingTSPAN.setAttributeNS(null, 'dy', line_height);

        testingTEXTNODE = document.createTextNode(line);
        testingTSPAN.appendChild(testingTEXTNODE);
        wrapping.appendChild(testingTSPAN);

        line = words[n] + " ";
        linecounter++;
      }
      else {
        line = line2;
      }
    }

    var testingTSPAN = document.createElementNS('http://www.w3.org/2000/svg', 'tspan');
    testingTSPAN.setAttributeNS(null, 'x', x_pos + padding);
    testingTSPAN.setAttributeNS(null, 'dy', line_height);

    var testingTEXTNODE = document.createTextNode(line);
    testingTSPAN.appendChild(testingTEXTNODE);

    wrapping.appendChild(testingTSPAN);

    testing.parentNode.removeChild(testing);
    textnode.parentNode.replaceChild(wrapping,textnode);

    return linecounter;
}

document.getElementById('original').onmouseover = function () {

    var container = document.getElementById('destination');
    var numberoflines = wraptorect(this,container,20,1);
    console.log(numberoflines);  // In case you need it

};
MSC
la source
Merci. cela fonctionne parfaitement dans Chrome. Mais cela ne fonctionne pas dans Firefox. Il dit sur le lien de démonstration. Valeur inattendue de l'attribut dy d'analyse NaN. svgtext_clean2.htm: 117 essayant de trouver une solution.
akshayb
Je l'ai ensuite fait fonctionner dans Firefox. Ici vous allez:
MSC
1
(J'ai appuyé sur ENTRÉE trop tôt à l'instant.) Je l'ai ensuite fait fonctionner dans Firefox et IE. Si vous avez besoin d'aide, jetez un œil à democra.me/wrap_8_may_2014.htm . Il y a un commentaire sur Firefox dans le code.
MSC
Comme vous pouvez le voir, j'ai beaucoup développé le code pour réduire le cadre de sélection vers le haut ou le bas ou pour le tronquer avec des points de suspension au bon endroit.
MSC
Je modifierais une ligne dans le code MSC boxwidth = parseInt(boxObject.getAttribute('width'))boxwidth = parseInt(boxObject.getBBox().width)
:,
7

Le code suivant fonctionne correctement. Exécutez l'extrait de code ce qu'il fait.

Peut-être qu'il peut être nettoyé ou le faire fonctionner automatiquement avec toutes les balises de texte en SVG.

function svg_textMultiline() {

  var x = 0;
  var y = 20;
  var width = 360;
  var lineHeight = 10;
  
  

  /* get the text */
  var element = document.getElementById('test');
  var text = element.innerHTML;

  /* split the words into array */
  var words = text.split(' ');
  var line = '';

  /* Make a tspan for testing */
  element.innerHTML = '<tspan id="PROCESSING">busy</tspan >';

  for (var n = 0; n < words.length; n++) {
    var testLine = line + words[n] + ' ';
    var testElem = document.getElementById('PROCESSING');
    /*  Add line in testElement */
    testElem.innerHTML = testLine;
    /* Messure textElement */
    var metrics = testElem.getBoundingClientRect();
    testWidth = metrics.width;

    if (testWidth > width && n > 0) {
      element.innerHTML += '<tspan x="0" dy="' + y + '">' + line + '</tspan>';
      line = words[n] + ' ';
    } else {
      line = testLine;
    }
  }
  
  element.innerHTML += '<tspan x="0" dy="' + y + '">' + line + '</tspan>';
  document.getElementById("PROCESSING").remove();
  
}


svg_textMultiline();
body {
  font-family: arial;
  font-size: 20px;
}
svg {
  background: #dfdfdf;
  border:1px solid #aaa;
}
svg text {
  fill: blue;
  stroke: red;
  stroke-width: 0.3;
  stroke-linejoin: round;
  stroke-linecap: round;
}
<svg height="300" width="500" xmlns="http://www.w3.org/2000/svg" version="1.1">

  <text id="test" y="0">GIETEN - Het college van Aa en Hunze is in de fout gegaan met het weigeren van een zorgproject in het failliete hotel Braams in Gieten. Dat stelt de PvdA-fractie in een brief aan het college. De partij wil opheldering over de kwestie en heeft schriftelijke
    vragen ingediend. Verkeerde route De PvdA vindt dat de gemeenteraad eerst gepolst had moeten worden, voordat het college het plan afwees. "Volgens ons is de verkeerde route gekozen", zegt PvdA-raadslid Henk Santes.</text>

</svg>

Peter
la source
1
Retour à la ligne automatique dans le texte SVG :) Mon code javascript crée des lignes lorsque le texte est trop long. Ce sera bien si je travaille sur toutes les balises de texte à l'intérieur de SVG. automatique sans changer l'id = "" en javascript. Dommage que SVG n'ait pas de multi-lignes par lui-même.
Peter le
Belle solution, mais vous pouvez l'aligner au centre?
Krešimir Galić
Devrait être acceptée réponse tbh. La solution javascript est suffisamment minimale et a du sens.
Zac
4

J'ai publié la procédure suivante pour ajouter un faux retour à la ligne à un élément "texte" SVG ici:

SVG Word Wrap - Afficher le bouchon?

Il vous suffit d'ajouter une simple fonction JavaScript, qui divise votre chaîne en éléments "tspan" plus courts. Voici un exemple de ce à quoi il ressemble:

Exemple SVG

J'espère que cela t'aides !

Mike Gledhill
la source