xml SVG 文本中的自动换行

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4991171/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 14:32:09  来源:igfitidea点击:

Auto line-wrapping in SVG text

xmltextsvgword-wrap

提问by tillda

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.

我想<text>在 SVG 中显示一个自动换行到容器<rect>的内容,就像 HTML 文本填充<div>元素一样。有没有办法做到这一点?我不想通过使用<tspan>s来定位线条。

采纳答案by Tangui

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

文本换行不是 SVG1.1(当前实现的规范)的一部分。您应该通过<foreignObject/>元素使用 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>

回答by Erik Dahlstr?m

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' elementin 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 Tiny 1.2中的“textArea”元素支持所有标准 svg 功能,例如高级填充等,并且您可以将宽度或高度指定为自动,这意味着文本可以在该方向上自由流动。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 开关元素规范

回答by user2856765

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>

回答by MSC

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 RECT 并希望文本在其中换行,这可能很方便:

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

};

回答by jbeard4

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/

回答by zenw0lf

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

如果您要使用 d3.js,这可能会有所帮助:https://bl.ocks.org/mbostock/7555321

回答by Peter

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>

回答by Mike Gledhill

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 Word Wrap - 显示塞子?

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”元素。下面是它的外观示例:

Example SVG

示例 SVG

Hope this helps !

希望这可以帮助 !