在SVG文本中自动换行。

时间:2021-04-13 22:20:27

I would like to display a <text> in SVG what would auto-line-wrap to the container <rect> the same way as HTML text fills <div> elements. Is there a way to do it? I don't want to position lines sparately by using <tspan>s.

我想在SVG中显示一个 ,它可以像填充

元素的HTML文本一样自动地对容器 进行换行。有办法吗?我不想使用 s来精确地定位线条。

8 个解决方案

#1


68  

Text wrapping is not part of SVG1.1, the currently implemented spec. You should rather use HTML via the <foreignObject/> element.

文本包装不是当前实现的SVG1.1规范的一部分,您应该通过 元素来使用HTML。

<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>

#2


59  

Here's an 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>

Noting that even though foreignObject may be reported as being supported with that featurestring, there's no guarantee that HTML can be displayed because that's not required by the SVG 1.1 specification. There is no featurestring for html-in-foreignobject support at the moment. However, it is still supported in many browsers, so it's likely to become required in the future, perhaps with a corresponding featurestring.

注意到,尽管foreignObject可能会被报告为支持该特性字符串,但不能保证HTML能够被显示出来,因为SVG 1.1规范并不要求这样做。目前还没有html-in-foreignobject支持的特性字符串。但是,许多浏览器仍然支持它,因此将来可能需要它,可能需要一个相应的特性字符串。

Note that the 'textArea' element in SVG Tiny 1.2 supports all the standard svg features, e.g advanced filling etc, and that you can specify either of width or height as auto, meaning that the text can flow freely in that direction. ForeignObject acts as clipping viewport.

请注意,SVG微小1.2中的“textArea”元素支持所有标准的SVG特性。g高级填充等等,你可以将宽度或高度指定为auto,这意味着文本可以沿着这个方向*流动。ForeignObject则充当剪切视图。

Note: while the above example is valid SVG 1.1 content, in SVG 2 the 'requiredFeatures' attribute has been removed, which means the 'switch' element will try to render the first 'g' element regardless of having support for SVG 1.2 'textArea' elements. See SVG2 switch element spec.

注意:虽然上面的示例是有效的SVG 1.1内容,但是在SVG 2中,“requiredFeatures”属性已经被删除,这意味着“switch”元素将尝试呈现第一个“g”元素,而不需要支持SVG 1.2“textArea”元素。参见SVG2开关元件规范。

#3


8  

This functionality can also be added using JavaScript. Carto.net has an example:

还可以使用JavaScript添加此功能。有一个例子:Carto.net

http://old.carto.net/papers/svg/textFlow/

http://old.carto.net/papers/svg/textFlow/

Something else that also might be useful to are you are editable text areas:

另一个有用的东西是你是可编辑的文本区域:

http://old.carto.net/papers/svg/gui/textbox/

http://old.carto.net/papers/svg/gui/textbox/

#4


7  

The textPath may be good for some case.

对于某些情况,textPath可能是好的。

<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>

#5


7  

Building on @Mike Gledhill's code, I've taken it a step further and added more parameters. If you have a SVG RECT and want text to wrap inside it, this may be handy:

在@Mike Gledhill的代码上,我更进一步,添加了更多的参数。如果您有一个SVG矩形,并希望在其中封装文本,这可能很方便:

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

};

#6


4  

I have posted the following walkthrough for adding some fake word-wrapping to an SVG "text" element here:

我已经发布了下面的演练,在这里向SVG“文本”元素添加了一些虚假的文字包装:

SVG Word Wrap - Show stopper?

SVG单词包装-显示塞子?

You just need to add a simple JavaScript function, which splits your string into shorter "tspan" elements. Here's an example of what it looks like:

您只需添加一个简单的JavaScript函数,它将字符串分割为更短的“tspan”元素。这是它的一个例子:

在SVG文本中自动换行。

Hope this helps !

希望这可以帮助!

#7


4  

If you were to use d3.js, this could help: https://bl.ocks.org/mbostock/7555321

如果你使用d3。这可能有帮助:https://bl.ocks.org/mbostock/7555321

#8


3  

The following code is working fine. Run the code snippet what it does.

下面的代码运行良好。运行代码片段。

Maybe it can be cleaned up or make it automatically work with all text tags in SVG.

也许它可以被清理,或者让它自动处理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>

#1


68  

Text wrapping is not part of SVG1.1, the currently implemented spec. You should rather use HTML via the <foreignObject/> element.

文本包装不是当前实现的SVG1.1规范的一部分,您应该通过 元素来使用HTML。

<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>

#2


59  

Here's an 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>

Noting that even though foreignObject may be reported as being supported with that featurestring, there's no guarantee that HTML can be displayed because that's not required by the SVG 1.1 specification. There is no featurestring for html-in-foreignobject support at the moment. However, it is still supported in many browsers, so it's likely to become required in the future, perhaps with a corresponding featurestring.

注意到,尽管foreignObject可能会被报告为支持该特性字符串,但不能保证HTML能够被显示出来,因为SVG 1.1规范并不要求这样做。目前还没有html-in-foreignobject支持的特性字符串。但是,许多浏览器仍然支持它,因此将来可能需要它,可能需要一个相应的特性字符串。

Note that the 'textArea' element in SVG Tiny 1.2 supports all the standard svg features, e.g advanced filling etc, and that you can specify either of width or height as auto, meaning that the text can flow freely in that direction. ForeignObject acts as clipping viewport.

请注意,SVG微小1.2中的“textArea”元素支持所有标准的SVG特性。g高级填充等等,你可以将宽度或高度指定为auto,这意味着文本可以沿着这个方向*流动。ForeignObject则充当剪切视图。

Note: while the above example is valid SVG 1.1 content, in SVG 2 the 'requiredFeatures' attribute has been removed, which means the 'switch' element will try to render the first 'g' element regardless of having support for SVG 1.2 'textArea' elements. See SVG2 switch element spec.

注意:虽然上面的示例是有效的SVG 1.1内容,但是在SVG 2中,“requiredFeatures”属性已经被删除,这意味着“switch”元素将尝试呈现第一个“g”元素,而不需要支持SVG 1.2“textArea”元素。参见SVG2开关元件规范。

#3


8  

This functionality can also be added using JavaScript. Carto.net has an example:

还可以使用JavaScript添加此功能。有一个例子:Carto.net

http://old.carto.net/papers/svg/textFlow/

http://old.carto.net/papers/svg/textFlow/

Something else that also might be useful to are you are editable text areas:

另一个有用的东西是你是可编辑的文本区域:

http://old.carto.net/papers/svg/gui/textbox/

http://old.carto.net/papers/svg/gui/textbox/

#4


7  

The textPath may be good for some case.

对于某些情况,textPath可能是好的。

<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>

#5


7  

Building on @Mike Gledhill's code, I've taken it a step further and added more parameters. If you have a SVG RECT and want text to wrap inside it, this may be handy:

在@Mike Gledhill的代码上,我更进一步,添加了更多的参数。如果您有一个SVG矩形,并希望在其中封装文本,这可能很方便:

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

};

#6


4  

I have posted the following walkthrough for adding some fake word-wrapping to an SVG "text" element here:

我已经发布了下面的演练,在这里向SVG“文本”元素添加了一些虚假的文字包装:

SVG Word Wrap - Show stopper?

SVG单词包装-显示塞子?

You just need to add a simple JavaScript function, which splits your string into shorter "tspan" elements. Here's an example of what it looks like:

您只需添加一个简单的JavaScript函数,它将字符串分割为更短的“tspan”元素。这是它的一个例子:

在SVG文本中自动换行。

Hope this helps !

希望这可以帮助!

#7


4  

If you were to use d3.js, this could help: https://bl.ocks.org/mbostock/7555321

如果你使用d3。这可能有帮助:https://bl.ocks.org/mbostock/7555321

#8


3  

The following code is working fine. Run the code snippet what it does.

下面的代码运行良好。运行代码片段。

Maybe it can be cleaned up or make it automatically work with all text tags in SVG.

也许它可以被清理,或者让它自动处理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>