ChatGPT解决这个技术问题 Extra ChatGPT

SVG 文本中的自动换行

我想在 SVG 中显示一个 <text>,它将自动换行到容器 <rect>,就像 HTML 文本填充 <div> 元素一样。有没有办法做到这一点?我不想使用 <tspan> 单独定位行。

虽然这听起来很哲学,但文本换行最初并未包含在 SVG 标准中的原因可能是因为它是一种用于描述图形而不是内容的语言。对任何文本的支持已经“扩展”了该定义,可能是由于可访问性(屏幕阅读器)和可用性(文本选择)的原因,可能还有更多。但是,考虑到这个定义,您(或您的图像编辑器!)可能应该以您认为最终和美观的方式布置文本。

c
cubuspl42

文本换行不是当前实现的规范 SVG1.1 的一部分。

如果您要在 Web 上使用 SVG 图形,可以通过 <foreignObject/> 元素将 HTML 嵌入到 SVG 中。例子:

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

如果您的目标是一个不支持 HTML 的纯 SVG 渲染器,或者希望您的图形可以使用专业的矢量图形处理软件(Adobe Illustrator、Inkscape 等)进行编辑,那么此解决方案可能不适合您。


那是使用 switch 的错误方式,它需要使用 svg 规范中定义的特征字符串之一。您的示例中永远不会使用后备。请参阅 w3.org/TR/SVG11/feature.htmlw3.org/TR/SVG11/struct.html#SwitchElement
IE 也不支持
但请注意,并非所有引擎都可以渲染foreignObjects。特别是,蜡染没有。
万一其他人通过,foreignObjects 不会在 Illustrator 中渲染
Inkscape 或 ImageMagick convert 中没有异物。这会在尝试在 LaTeX 中使用此类 SVG 时产生问题。
E
Erik Dahlström

这是一个替代方案:

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

请注意,即使可能会报告 foreignObject 可能被该功能字符串支持,但不能保证 HTML 可以显示,因为 SVG 1.1 规范不要求这样做。目前没有用于 html-in-foreignobject 支持的功能字符串。但是,许多浏览器仍然支持它,因此将来可能会需要它,可能带有相应的特征字符串。

请注意,SVG Tiny 1.2 中的 'textArea' element 支持所有标准 svg 功能,例如高级填充等,并且您可以将宽度或高度指定为自动,这意味着文本可以在该方向上自由流动。 ForeignObject 充当剪辑视口。

注意: 虽然上面的示例是有效的 SVG 1.1 内容,但在 SVG 2 中,'requiredFeatures' 属性已被删除,这意味着 'switch' 元素将尝试渲染第一个 'g' 元素,而不管支持 SVG 1.2 'textArea' 元素。请参阅SVG2 switch element spec


我在 FF 中测试这段代码,浏览器没有向我显示 textArea 元素或 foreignObject 子元素。然后在阅读规范后,发现 requiredFeatures 属性的行为方式是,当其列表评估为 false 时,具有 requiredFeatures 属性的元素及其子元素不会被处理。因此,开关元件将没有任何必要性。在我删除 switch 元素后,foreignObject 孩子是可见的(因为我的浏览器(FF,8.01)支持 svg1.1 )。所以我认为这里不需要开关元件。请告诉我。
现在更新为使用 元素。 svg 规范没有告诉观众查看未知元素的“requiredFeatures”,因此必须使用已知的 svg 元素才能使其按预期工作。
谢谢!我需要使用 xhtml:div 而不是 div,但这可能是因为 d3.js。我找不到任何关于 TextFlow 的有用参考,它(仍然)存在还是只是在一些草稿中?
应该注意的是,今后似乎不再支持 textarea bugzilla.mozilla.org/show_bug.cgi?id=413360
示例在 Chrome 中不起作用。没有在其他浏览器中测试过。
u
user2856765

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>

仅在可以接受中间词(而不是连字符)的情况下。除了艺术项目之外,我想不出很多这样的情况。 http://jsfiddle.net/nilloc/vL3zj/
@Nilloc 不是每个人都使用英语,这种方法对于中文、日文或韩文来说完全没问题。
@ZangMingJie 基于字符(语标)语言的包装似乎与拆分单词完全不同。这在所有浪漫/拉丁/西里尔/阿拉伯(语音)语言中都很重要,这是我的观点。
请注意,根 svg 元素中的 xmlns:xlink="http://www.w3.org/1999/xlink" 属性对其工作至关重要。
M
MSC

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

};

谢谢。在 Chrome 中完美运行。但它在Firefox中不起作用。它在演示链接上说。意外的值 NaN 解析 dy 属性。 svgtext_clean2.htm:117 试图找到解决方法。
我随后让它在 Firefox 中工作。干得好:
(刚才按 ENTER 太快了。)我随后让它在 Firefox 和 IE 中工作。如果您需要帮助,请查看 democra.me/wrap_8_may_2014.htm。代码中有一条关于 Firefox 的注释。
正如你所看到的,我已经扩展了很多代码以向上或向下缩小边界框或在正确的位置用省略号截断。
我将修改 MSC 代码中的一行:boxwidth = parseInt(boxObject.getAttribute('width')),将只接受以像素为单位的宽度,而 boxwidth = parseInt(boxObject.getBBox().width),将接受任何类型的度量单位
P
Peter

以下代码工作正常。运行代码片段它的作用。

也许它可以被清理或使其自动与 SVG 中的所有文本标签一起使用。

函数 svg_textMultiline() { var x = 0;变量 y = 20;可变宽度 = 360;变线高度 = 10; /* 获取文本 */ var element = document.getElementById('test'); var text = element.innerHTML; /* 将单词拆分成数组 */ var words = text.split(' '); varline = ''; /* 为测试创建一个 tspan */ element.innerHTML = 'busy'; for (var n = 0; n < words.length; n++) { var testLine = line + words[n] + ' '; var testElem = document.getElementById('PROCESSING'); /* 在 testElement 中添加行 */ testElem.innerHTML = testLine; /* 测量 textElement */ var metrics = testElem.getBoundingClientRect(); testWidth = metrics.width; if (testWidth > width && n > 0) { element.innerHTML += '' + line + '';行 = 单词 [n] + ' '; } 其他 { 线 = 测试线; } } element.innerHTML += '' + line + ''; document.getElementById("PROCESSING").remove(); } svg_textMultiline();正文 { 字体系列:宋体;字体大小:20px; } SVG { 背景:#dfdfdf;边框:1px 实心#aaa; } SVG 文本 { 填充:蓝色;中风:红色;笔画宽度:0.3;笔划线连接:圆形;笔划线帽:圆形; } CAST - Aa 和 Hunze 学院错误地拒绝了 Gieten 破产的 Braams 酒店的护理项目。 PvdA 派系在给学院的一封信中说明了这一点。该党希望对此事进行澄清,并已提交书面问题。错误的路线 PvdA 认为市议会应该在市议会否决该计划之前进行民意调查。 “在我们看来,选择了错误的路线,”PvdA 议员 Henk Santes 说。


SVG 文本中的自动换行 :) 当文本过长时,我的 javascript 代码会创建行。如果我可以处理 SVG 中的所有文本标签,那就太好了。自动而不更改javascript中的id =“”。糟糕的是,SVG 本身并没有多行。
不错的解决方案,但是您可以将其居中对齐吗?
应该接受答案。 javascript 解决方案足够小并且很有意义。
j
jbeard4

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

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

其他可能对您有用的东西是可编辑的文本区域:

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


404 -- 这些链接已损坏
C
Community

我已经发布了以下演练,用于在此处向 SVG“文本”元素添加一些虚假的自动换行:

SVG Word Wrap - Show stopper?

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

https://i.stack.imgur.com/Bpvui.png

希望这可以帮助 !


M
Mahmoud Magdy

我尝试了所有答案,只有我创建了菜鸟解决方案,但它会在没有未知代码行的情况下解决问题另一个文本标签等等。您只需要简单的 JavaScript if 语句并更改文本内容

  if (data['clinic']['cicovidcliniccity'].length > 35 && data['clinic']['cicovidcliniccity'].length < 75) {
    const cname = data['clinic']['cicovidcliniccity'];
    const ctext2_shodow = document.querySelector("#c_text2_shdow");
    ctext2.textContent = cname.substring(1, 35)
    ctext2_shodow.textContent = cname.substring(35, cname.length);

  }

  if (data['clinic']['cicovidcliniccity'].length > 75 && data['clinic']['cicovidcliniccity'].length < 110) {
    const cname1 = data['clinic']['cicovidcliniccity'];
    const ctext2_shodow = document.querySelector("#c_text2_shdow");
    const ctext3_shodow = document.querySelector("#c_text3_shdow");
    ctext2.textContent = cname1.substring(1, 35)
    ctext2_shodow.textContent = cname1.substring(35, 75);
    ctext3_shodow.textContent = cname1.substring(75, cname1.length);

  }