Pembungkusan baris otomatis dalam teks SVG

108

Saya ingin menampilkan <text>di SVG apa yang akan otomatis dibungkus ke wadah <rect>dengan cara yang sama seperti teks HTML mengisi <div>elemen. Apakah ada cara untuk melakukannya? Saya tidak ingin memposisikan garis secara terpisah dengan menggunakan <tspan>s.

Tillda
sumber

Jawaban:

89

Pembungkusan teks bukan bagian dari SVG1.1, spesifikasi yang saat ini diterapkan. Anda sebaiknya menggunakan HTML melalui <foreignObject/>elemen.

<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
sumber
5
Itu adalah cara yang salah untuk menggunakan sakelar, ia perlu menggunakan salah satu pegas fitur yang ditentukan dalam spesifikasi svg. Pengganti tidak akan pernah digunakan dalam contoh Anda. Lihat w3.org/TR/SVG11/feature.html dan w3.org/TR/SVG11/struct.html#SwitchElement .
Erik Dahlström
22
Juga <foreignObject /> tidak didukung di IE
Doug Amos
3
Namun perlu diketahui bahwa tidak semua mesin dapat merender asingObjects. Khususnya batik tidak.
hrabinowitz
69

Berikut alternatifnya:

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

Memperhatikan bahwa meskipun foreignObject mungkin dilaporkan didukung dengan featureestring itu, tidak ada jaminan bahwa HTML dapat ditampilkan karena itu tidak diwajibkan oleh spesifikasi SVG 1.1. Saat ini tidak ada fitur uji fitur untuk html-in-foreignobject support. Namun, fitur ini masih didukung di banyak browser, jadi fitur ini kemungkinan akan dibutuhkan di masa mendatang, mungkin dengan tanda fitur yang sesuai.

Perhatikan bahwa elemen 'textArea' di SVG Tiny 1.2 mendukung semua fitur svg standar, misalnya pengisian lanjutan dll, dan Anda dapat menentukan lebar atau tinggi sebagai otomatis, yang berarti teks dapat mengalir dengan bebas ke arah itu. ForeignObject bertindak sebagai kliping area pandang.

Catatan: meskipun contoh di atas adalah konten SVG 1.1 yang valid, di SVG 2 atribut 'requiredFeatures' telah dihapus, yang berarti elemen 'switch' akan mencoba merender elemen 'g' pertama terlepas dari dukungan untuk SVG 1.2 'textArea 'elemen. Lihat spesifikasi elemen sakelar SVG2 .

Erik Dahlström
sumber
1
Saya menguji kode ini di FF, browser tidak menunjukkan kepada saya elemen textArea atau anak foreignObject. Kemudian setelah membaca spesifikasi, ditemukan bahwa atribut requiredFeatures berperilaku sedemikian rupa sehingga, ketika daftarnya bernilai false, elemen yang memiliki atribut requiredFeatures dan turunannya tidak diproses. Jadi tidak akan ada kebutuhan untuk elemen sakelar. Setelah saya menghapus elemen switch, anak foreignObject terlihat (karena browser saya (FF, 8.01) mendukung svg1.1). Jadi saya pikir tidak perlu elemen sakelar di sini. Tolong beritahu saya.
Rajkamal Subramanian
Diperbarui sekarang untuk menggunakan elemen <g>. Spesifikasi svg tidak memberi tahu pemirsa untuk melihat 'requiredFeatures' pada elemen yang tidak diketahui, jadi seseorang harus menggunakan elemen svg yang dikenal agar berfungsi sebagaimana mestinya.
Erik Dahlström
Terima kasih! Saya perlu menggunakan xhtml:divalih-alih div, tetapi itu bisa jadi karena d3.js. Saya tidak dapat menemukan referensi yang berguna tentang TextFlow, apakah itu (masih) ada atau hanya dalam beberapa draf?
johndodo
2
Perlu dicatat bahwa textarea tampaknya tidak didukung ke depan bugzilla.mozilla.org/show_bug.cgi?id=413360
George Mauer
1
Contoh tidak berfungsi di Chrome. Belum diuji di browser lain.
posfan12
15

TextPath mungkin bagus untuk beberapa kasus.

<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>
pengguna2856765
sumber
3
Hanya dalam kasus di mana membungkus kata tengah (dan bukan tanda hubung) dapat diterima. Saya tidak bisa memikirkan banyak kasus di luar proyek seni di mana tidak apa-apa. http://jsfiddle.net/nilloc/vL3zj/
Nilloc
4
@Nilloc Tidak semua orang menggunakan bahasa Inggris, metode ini baik-baik saja untuk bahasa Mandarin, Jepang atau Korea.
Zang MingJie
@ZangMingJie Membungkus bahasa berbasis karakter (logografik) tampaknya merupakan kasus penggunaan yang sama sekali berbeda daripada memisahkan kata. Yang penting dalam semua bahasa romantis / latin / cyrillic / arab (fonograf), itulah maksud saya.
Nilloc
11

Membangun kode @Mike Gledhill, saya telah mengambil langkah lebih jauh dan menambahkan lebih banyak parameter. Jika Anda memiliki SVG RECT dan ingin teks dibungkus di dalamnya, ini mungkin berguna:

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
sumber
Terima kasih. yang berfungsi sempurna di Chrome. Tetapi itu tidak berfungsi di firefox. Tercantum di tautan demo. Nilai tak terduga NaN parsing dy atribut. svgtext_clean2.htm: 117 mencoba mencari solusi.
akshayb
Saya kemudian membuatnya berfungsi di Firefox. Ini dia:
MSC
1
(Baru saja menekan ENTER terlalu cepat.) Saya kemudian membuatnya berfungsi di Firefox dan IE. Jika Anda membutuhkan bantuan, lihat democra.me/wrap_8_may_2014.htm . Ada komentar tentang Firefox di dalam kodenya.
MSC
Seperti yang Anda lihat, saya telah banyak memperluas kode untuk mengecilkan kotak pembatas ke atas atau ke bawah atau memotong dengan elipsis di tempat yang tepat.
MSC
Saya akan mengubah baris dalam kode MSC:, boxwidth = parseInt(boxObject.getAttribute('width'))hanya akan menerima lebar dalam piksel, sementara boxwidth = parseInt(boxObject.getBBox().width), akan menerima semua jenis satuan ukuran
Massimiliano Caniparoli
7

Kode berikut berfungsi dengan baik. Jalankan cuplikan kode apa yang dilakukannya.

Mungkin itu bisa dibersihkan atau membuatnya otomatis bekerja dengan semua tag teks di 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
sumber
1
Pembungkusan baris otomatis dalam teks SVG :) Kode javascript saya membuat baris jika teks terlalu panjang. Alangkah baiknya jika saya mengerjakan semua tag teks di dalam SVG. otomatis tanpa mengubah id = "" di javascript. Untuk SVG yang buruk doenst memiliki multi-baris dengan sendirinya.
Peter
Solusi bagus, tetapi Anda dapat menyelaraskannya di tengah?
Krešimir Galić
Harus diterima jawaban tbh. Solusi javascript cukup minimal dan masuk akal.
Zac
4

Saya telah memposting panduan berikut untuk menambahkan beberapa pembungkusan kata palsu ke elemen "teks" SVG di sini:

SVG Word Wrap - Tampilkan stopper?

Anda hanya perlu menambahkan fungsi JavaScript sederhana, yang membagi string Anda menjadi elemen "tspan" yang lebih pendek. Berikut adalah contoh tampilannya:

Contoh SVG

Semoga ini membantu !

Mike Gledhill
sumber