> [Site index](https://perfectvector.com/llms.txt) — available pages and descriptions.

# Testo SVG a capo: crea righe misurate con tspan

> Manda a capo le etichette SVG misurando le parole con il font effettivo, poi crea tspans. Prova etichette strette, rifiuta gli eccessi e salva il risultato.

Source: https://perfectvector.com/it/blog/svg-text-wrap-tspan

Language: it
Author: Irene Kim
Published: 2026-10-06T08:00:00+09:00
Updated: 2026-10-06T08:00:00+09:00

Per dividere un’etichetta SVG variabile in righe esplicite, misura i gruppi candidati di parole con `getComputedTextLength()`, poi scrivi un `<tspan>` per ogni riga accettata. Attendi prima i font del documento e scegli cosa accade quando una parola è più larga dell’etichetta o il risultato supera il limite di righe.

L’esempio seguente usa parole separate da spazi bianchi e rifiuta entrambi gli eccessi senza produrre un’etichetta parziale. Mantiene una nostra illustrazione originale a rombo cavo separata dalle parole, quindi cambiare la larghezza dell’etichetta non ridisegna la grafica.

## Scegli una politica di ritorno a capo prima di posizionare le righe

L’[elemento `tspan`](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/tspan) permette di posizionare porzioni di testo SVG. La routine imposta lo stesso `x` su ogni riga e uno scostamento `dy` sulle successive. La routine decide dove interrompere le righe; l’elemento conserva il risultato.

È un approccio di layout esplicito. La [specifica del testo SVG 2](https://www.w3.org/TR/SVG2/text.html) definisce anche il ritorno a capo automatico tramite un’area di contenuto e il layout di testo CSS. L’articolo non prova quelle funzioni né sostiene che manchino in ogni browser.

Per un paragrafo HTML fuori dall’illustrazione, [fai scorrere il testo attorno a una sagoma SVG flottante con CSS](https://perfectvector.com/it/blog/css-text-around-svg-shape). Quel layout segue un contorno immagine mantenendo le parole come normale HTML, anziché trasformarle in righe SVG posizionate.

Per questo piccolo esempio di etichetta inglese, la politica è:

* Ridurre gli spazi bianchi e provare le parole nell’ordine originale, separate da singoli spazi.
* Conservare una riga candidata solo finché la lunghezza misurata entra nella larghezza disponibile.
* Rifiutare una parola che non entra da sola; non inventare un punto di sillabazione.
* Rifiutare l’intero risultato quando richiede più di `maxLines`; non eliminare silenziosamente le ultime parole.

Il limite di righe è una decisione di layout. Sceglilo insieme a linea di base iniziale, interlinea e altezza disponibile. Il sistema di a capo non calcola per te un riquadro verticale del contenuto.

**Cambia la larghezza, conserva la grafica**

![Confronto concettuale di un’illustrazione a rombo cavo accanto a righe lunghe in un’etichetta larga e righe corte in una stretta](https://perfectvector.com/blog/svg-text-wrap-tspan/measured-lines.webp)

*Illustrazione: un’etichetta più stretta può richiedere più righe. Barre astratte e cornici tratteggiate sono indizi concettuali; parole effettive, misure e stati di rifiuto sotto provengono dall’esempio browser indipendente.*

## Misura con il font che disegnerà l’etichetta

[`getComputedTextLength()`](https://developer.mozilla.org/en-US/docs/Web/API/SVGTextContentElement/getComputedTextLength) misura la lunghezza calcolata del testo SVG. L’esempio clona l’elemento testo dell’etichetta come sonda nascosta nello stesso SVG collegato, poi la rimuove dopo aver pianificato le righe. Entrambi usano esplicitamente `Arial, sans-serif` a 22 unità.

Il codice attende [`document.fonts.ready`](https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet/ready) prima di misurare. Questo attende il caricamento dei font del documento e il layout associato; non incorpora un font nell’SVG esportato. Un diverso font disponibile può produrre lunghezze diverse. Ripeti il layout quando cambiano parole, impostazioni del font o larghezza disponibile.

## Esegui l’esempio completo senza dipendenze

Salvalo come `starter.html` e aprilo in un browser. Crea sei casi, stampa le misure e serializza ogni SVG risultante. I primi due cambiano solo la larghezza dell’etichetta, il terzo cambia le parole. Gli altri esercitano rifiuto e testo letterale.

```html
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Measured SVG label lines</title>
<style>
  body { margin: 24px; font: 16px system-ui; background: #f6f2e8; }
  #cases { display: grid; grid-template-columns: repeat(2, 360px); gap: 20px; }
  figure { margin: 0; }
  svg { display: block; background: white; }
  figcaption { margin: 6px 0; }
  pre { white-space: pre-wrap; font-size: 12px; }
</style>
<h1>Keep artwork and label layout separate</h1>
<div id="cases"></div>
<pre id="report"></pre>
<script>
  const ns = 'http://www.w3.org/2000/svg';
  const emblem = 'M60 32 L100 72 L60 112 L20 72 Z M60 54 L78 72 L60 90 L42 72 Z';
  function element(name, attributes) {
    const node = document.createElementNS(ns, name);
    for (const [key, value] of Object.entries(attributes)) node.setAttribute(key, value);
    return node;
  }
  function wrapWords(text, source, { width, maxLines, lineHeight }) {
    text.replaceChildren();
    if (!(width > 0 && lineHeight > 0 && Number.isInteger(maxLines) && maxLines > 0)) {
      return { status: 'invalid-options', lines: [] };
    }
    const words = source.trim().split(/\s+/u).filter(Boolean);
    const probe = text.cloneNode(false);
    probe.setAttribute('visibility', 'hidden');
    text.parentNode.append(probe);
    const measure = value => { probe.textContent = value; return probe.getComputedTextLength(); };
    const lines = [];
    let line = '';
    try {
      for (const word of words) {
        const wordWidth = measure(word);
        if (wordWidth > width) {
          return { status: 'overlong-word', word, measuredWidth: wordWidth, lines: [] };
        }
        const candidate = line ? line + ' ' + word : word;
        if (line && measure(candidate) > width) {
          lines.push(line);
          line = word;
        } else line = candidate;
      }
      if (line) lines.push(line);
      if (lines.length > maxLines) {
        return { status: 'too-many-lines', requiredLines: lines.length, lines: [] };
      }
      const x = text.getAttribute('x');
      lines.forEach((value, index) => {
        const span = element('tspan', { x, dy: index ? lineHeight : 0 });
        span.textContent = value;
        text.append(span);
      });
      return { status: 'ok', lines,
        lengths: [...text.children].map(span => span.getComputedTextLength()) };
    } finally { probe.remove(); }
  }
  const cases = [
    { id: 'normal', source: 'Keep artwork and labels separate', width: 200, maxLines: 4 },
    { id: 'narrow', source: 'Keep artwork and labels separate', width: 110, maxLines: 5 },
    { id: 'changed', source: 'Keep the original artwork beside every exported label', width: 200, maxLines: 4 },
    { id: 'long-word', source: 'Artwork supercalifragilisticexpialidocious', width: 110, maxLines: 5 },
    { id: 'line-limit', source: 'Keep artwork and labels separate', width: 110, maxLines: 2 },
    { id: 'literal', source: 'Cove <g> & field', width: 200, maxLines: 4 },
  ];
  const entries = cases.map(test => {
    const figure = document.createElement('figure');
    const svg = element('svg', { width: 360, height: 240, viewBox: '0 0 360 240' });
    svg.append(element('path', { d: emblem, fill: '#176b5b', 'fill-rule': 'evenodd' }));
    svg.append(element('rect', { x: 140, y: 34, width: test.width, height: 170,
      fill: 'none', stroke: '#ce7b55', 'stroke-dasharray': '4 4' }));
    const text = element('text', { x: 140, y: 60, 'font-family': 'Arial, sans-serif',
      'font-size': 22, fill: '#172b34' });
    svg.append(text);
    const caption = document.createElement('figcaption');
    figure.append(svg, caption);
    document.getElementById('cases').append(figure);
    return { test, svg, text, caption };
  });
  async function run() {
    await document.fonts.ready;
    const results = entries.map(({ test, svg, text, caption }) => {
      const result = wrapWords(text, test.source, { ...test, lineHeight: 30 });
      caption.textContent = test.id + ': ' + result.status;
      return { ...test, ...result, fontFamily: getComputedStyle(text).fontFamily,
        fontSize: getComputedStyle(text).fontSize, tspanCount: text.children.length,
        pathD: svg.querySelector('path').getAttribute('d'),
        pathCount: svg.querySelectorAll('path').length,
        imageCount: svg.querySelectorAll('image').length,
        foreignObjectCount: svg.querySelectorAll('foreignObject').length,
        serialized: new XMLSerializer().serializeToString(svg) };
    });
    document.getElementById('report').textContent = JSON.stringify({
      fontStatus: document.fonts.status, policy: 'Whitespace words; reject a long word or too many lines without partial output', results,
    }, null, 2);
  }
  run();
</script>
</html>
```

La routine crea ogni riga con [`textContent`](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent), anziché inserire le parole come markup. Nel caso di testo letterale, `Cove <g> & field` resta testo normale: l’SVG serializzato contiene parentesi angolari e una e commerciale con escape, non un elemento gruppo aggiuntivo.

Il clone di misura è collegato ma nascosto, con gli stessi attributi del font dell’etichetta visibile. Viene rimosso in `finally`, anche in caso di rifiuto. Le righe vengono aggiunte solo dopo aver superato i controlli di larghezza delle parole e numero massimo di righe. La routine cancella prima l’etichetta precedente, quindi un aggiornamento rifiutato non lascia testo vecchio o parziale; gestisci lo stato restituito nell’interfaccia dell’applicazione.

## Cosa hanno prodotto i sei casi originali

Abbiamo eseguito esattamente questo esempio in Chrome con i font del documento nello stato `loaded`. Le misure seguenti sono lunghezze del testo SVG per quell’ambiente, non limiti dei glifi dipinti o una garanzia di portabilità dei font.

| Caso                                         | Larghezza / righe massime | Risultato                                          | Riga più lunga osservata   |
| -------------------------------------------- | ------------------------- | -------------------------------------------------- | -------------------------- |
| Frase originale                              | 200 / 4                   | Due righe: Keep artwork and; labels separate       | 172.44                     |
| Stessa frase, riquadro stretto               | 110 / 5                   | Quattro righe: Keep; artwork; and labels; separate | 100.30                     |
| Testo più lungo                              | 200 / 4                   | Tre righe                                          | 198.13                     |
| Parola troppo lunga                          | 110 / 5                   | overlong-word; zero tspans                         | Parola misurata 311.82     |
| Frase stretta, limite di due righe           | 110 / 2                   | too-many-lines; ne servono quattro; zero tspans    | Nessuna etichetta prodotta |
| Parentesi angolari e e commerciale letterali | 200 / 4                   | Una riga letterale                                 | 162.66                     |

La frase originale è `Keep artwork and labels separate`. La versione più lunga è `Keep the original artwork beside every exported label`. Ogni riga accettata rientrava nella larghezza scelta. I casi rifiutati lasciavano visibile l’illustrazione senza etichetta, rendendo facile ispezionare lo stato di errore.

Tutti e sei i file serializzati conservavano l’identico tracciato originale del rombo, un elemento testo nativo e nessuna immagine o `foreignObject`. I casi accettati contenevano rispettivamente due, quattro, tre e un tspan; entrambi i casi rifiutati zero. Abbiamo anche riaperto i sei SVG serializzati come immagini nello stesso browser, dove tutti caricavano a 360×240. È un controllo di questi file e browser, non di ogni editor o renderer.

## Salva l’SVG impaginato, poi verifica la consegna

[`XMLSerializer.serializeToString()`](https://developer.mozilla.org/en-US/docs/Web/API/XMLSerializer/serializeToString) crea il markup serializzato nel rapporto. Salva la stringa `serialized` di un caso riuscito come file `.svg`. Il file contiene già righe esplicite e non richiede questa routine JavaScript per ricrearle.

Le parole sono ancora testo attivo. Il file salvato conserva la richiesta della famiglia di font, non un file font o glifi convertiti in contorni. Una destinazione con un altro font può cambiare le lettere anche se stringhe e posizioni delle righe restano. Conserva parole originali e input di layout accanto alla copia esportata e verifica la destinazione effettiva prima della consegna. Per una forma delle lettere fissa, crea una copia di consegna convertita in contorni in un editor adeguato, mantenendo testo attivo nel master.

La [guida al testo foreignObject](https://perfectvector.com/it/blog/svg-foreignobject-text) affronta le etichette HTML che scompaiono in altri renderer. Questo esempio crea invece testo nativo dall’inizio. La [guida textLength](https://perfectvector.com/it/blog/svg-textlength-fit) tratta l’adattamento di una singola riga regolandone la lunghezza; è una decisione diversa dall’aggiungere righe misurate. Usa la [guida all’allineamento verticale](https://perfectvector.com/it/blog/svg-text-vertical-alignment) per posizionare l’intera etichetta rispetto alla grafica circostante.

## Esplicita i limiti del sistema di a capo

Dividere sugli spazi bianchi è una politica volutamente piccola. Riduce spazi ripetuti, tabulazioni e nuove righe esplicite e non gestisce regole di interruzione specifiche delle lingue, sillabazione, paragrafi bidirezionali o porzioni ricche con stili misti. Scegli un altro sistema di layout oppure estendi e prova la politica quando il testo richiede queste funzioni.

Il controllo della larghezza non garantisce neppure che le sporgenze decorative dei glifi restino in una cornice disegnata o che cinque linee di base entrino nell’altezza scelta. Esamina le lettere visibili alla dimensione di consegna. Il test usa etichette orizzontali, attributi del font fissi e un passo di riga di 30 unità; non trasferire i numeri misurati a un altro carattere senza misurare di nuovo.

Se l’illustrazione esiste solo come PNG o JPG e ti serve grafica modificabile, [prepara un SVG di clipart con PerfectVector](https://perfectvector.com/it/clipart-to-svg), esamina bordi e aperture interne e separa la grafica dall’etichetta. Riscrivi le parole come testo nativo, esegui la politica di a capo e controlla l’esportazione assemblata. La vettorializzazione prepara i tracciati dell’illustrazione; non sceglie le interruzioni né recupera parole modificabili da lettere ricalcate. I tracciati buoni esistenti possono restare tali. La [guida alla vettorializzazione](https://perfectvector.com/it/blog/what-is-image-vectorization) spiega quel recupero della sorgente.

## Domande frequenti

**tspan decide automaticamente dove interrompere ogni riga?**
Questo esempio decide misurando gruppi di parole, poi crea un tspan posizionato per ogni riga accettata. Non prova il ritorno a capo automatico di SVG 2 basato su CSS.

**Cosa accade a una parola più larga dell’etichetta?**
La routine restituisce overlong-word e non produce etichetta. Allarga l’area, accorcia il testo o fornisci una politica di interruzione provata separatamente invece di ritagliarlo silenziosamente.

**maxLines elimina le parole rimanenti?**
No. Questa politica rifiuta il risultato quando servono troppe righe, riporta il numero necessario e lascia zero tspans. L’applicazione deve gestire lo stato.

**Le righe esportate appariranno identiche con un altro font?**
Parole e posizioni esplicite restano, ma il file non incorpora un font né converte i glifi in contorni. Controlla il font disponibile e le lettere nella destinazione reale.

## Fonti

1. [MDN elemento tspan](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/tspan) — sottotesto posizionato e attributi di scostamento della riga.
2. [W3C testo SVG 2](https://www.w3.org/TR/SVG2/text.html) — posizionamento esplicito e modello separato di ritorno a capo automatico.
3. [MDN getComputedTextLength](https://developer.mozilla.org/en-US/docs/Web/API/SVGTextContentElement/getComputedTextLength) — misura del testo SVG effettivo.
4. [MDN FontFaceSet.ready](https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet/ready) — attesa di caricamento font e layout.
5. [MDN textContent](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent) — inserimento di testo semplice anziché markup.
6. [MDN XMLSerializer](https://developer.mozilla.org/en-US/docs/Web/API/XMLSerializer/serializeToString) — salvataggio del markup SVG impaginato.

***

Per la tua illustrazione raster, [prepara grafica clipart modificabile](https://perfectvector.com/it/clipart-to-svg), esamina le forme e conserva il master. Aggiungi le parole separatamente, poi verifica righe accettate e lettere visibili nell’SVG salvato prima della consegna.

## Explore

- [Read the HTML article](https://perfectvector.com/it/blog/svg-text-wrap-tspan)
- [SVG.js: importa grafica, modifica ed esporta SVG](https://perfectvector.com/it/blog/svgjs-import-export-artwork.md)
- [Marcatori SVG Leaflet: allinea la punta con iconAnchor](https://perfectvector.com/it/blog/leaflet-svg-marker-anchor.md)
- [Product facts](https://perfectvector.com/product-facts.md)
- [FAQ](https://perfectvector.com/faq.md)
- [Pricing and credits](https://perfectvector.com/pricing.md)
