SVG Metin Kaydırma: tspan ile Ölçülmüş Satırlar Oluşturun
Gerçek yazı tipiyle sözcükleri ölçüp tspan oluşturarak SVG etiketlerini kaydırın. Dar etiketleri sınayın, taşan sözcükleri veya fazla satırları reddedin ve sonucu kaydedin.
Bu sayfada
Değişen SVG etiketini açık satırlara kaydırmak için aday sözcük gruplarını getComputedTextLength() ile ölçün, ardından kabul edilen her satır için bir <tspan> yazın. Önce belgenin yazı tiplerini bekleyin ve tek sözcük etiketten genişse veya sonuç satır sınırını aşarsa ne olacağını seçin.
Aşağıdaki başlangıç boşlukla ayrılmış sözcükler kullanır ve iki taşmayı da kısmi etiket üretmeden reddeder. Özgün delikli eşkenar dörtgen illüstrasyonunu metinden ayrı tutar; etiket genişliğini değiştirmek çizimi yeniden çizmez.
Satırları yerleştirmeden önce kaydırma politikasını seçin
tspan öğesi, SVG metninin bölümlerini konumlandırmanızı sağlar. Rutinimiz her satıra aynı x, sonraki satırlara dy ofseti ayarlar. Satır kırma kararını rutin verir; öğe sonucu tutar.
Bu, açık düzen yaklaşımlarından biridir. SVG 2 metin belirtimi, içerik alanı ve CSS metin düzeniyle otomatik kaydırmayı da tanımlar. Bu yazı o özellikleri sınamaz veya her tarayıcıda eksik olduklarını iddia etmez.
İllüstrasyonun dışındaki HTML paragrafı için CSS ile kayan SVG siluetinin çevresine metin sarın. Bu düzen görsel konturunu izlerken sözcükleri konumlandırılmış SVG etiket satırlarına dönüştürmek yerine normal HTML olarak tutar.
Bu küçük İngilizce etiket örneğinde politika şöyledir:
- Boşlukları birleştirin, sözcükleri özgün sırada tek boşlukla ayrılmış olarak deneyin.
- Aday satırı yalnızca ölçülen uzunluğu kullanılabilir genişliğe sığarken koruyun.
- Tek başına sığmayan sözcüğü reddedin; tireleme noktası uydurmayın.
- Sonuç
maxLinesdeğerinden çok satır gerektiriyorsa tamamını reddedin; son sözcükleri sessizce atmayın.
Satır sınırı düzen kararıdır. Başlangıç taban çizgisi, satır aralığı ve kullanılabilir yükseklikle birlikte seçin. Sarmalayıcı dikey içerik kutusunu sizin için hesaplamaz.

Etiketi çizecek yazı tipiyle ölçün
getComputedTextLength(), SVG metninin hesaplanan uzunluğunu ölçer. Bu başlangıç, etiketin metin öğesini aynı bağlı SVG içinde gizli ölçüm öğesi olarak klonlar, satırları planladıktan sonra kaldırır. İkisi de 22 birimde açık Arial, sans-serif kullanır.
Kod ölçmeden önce document.fonts.ready bekler. Bu, belgenin yazı tipi yüklemesini ve ilgili düzeni bekler; dışa aktarılan SVG'ye yazı tipi gömmez. Kullanılabilir farklı yazı tipi farklı satır uzunlukları üretebilir. Metin, yazı tipi ayarları veya kullanılabilir genişlik değiştiğinde düzeni yeniden çalıştırın.
Bağımlılıksız eksiksiz başlangıcı çalıştırın
Bunu starter.html olarak kaydedip tarayıcıda açın. Altı durum oluşturur, ölçümlerini yazdırır ve oluşan her SVG'yi serileştirir. İlk iki durum yalnızca etiket genişliğini, üçüncü metni değiştirir. Diğerleri reddetmeyi ve düz metni sınar.
<!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>Rutin metni işaretleme olarak eklemek yerine her satırı textContent ile oluşturur. Düz metin durumunda Cove <g> & field normal metin olarak kalır: serileştirilmiş SVG ek grup öğesi değil, kaçış uygulanmış açılı ayraçlar ve ve işareti içerir.
Ölçüm klonu bağlı ancak gizlidir; görünür etiketle aynı yazı tipi özniteliklerine sahiptir. Reddedilme dâhil finally içinde kaldırılır. Satırlar ancak sözcük genişliği ve maksimum satır kontrolleri geçince eklenir. Rutin önce eski etiketi temizler; reddedilen güncelleme eski veya kısmi metin bırakmaz. Dönen durumu uygulamanızın arayüzünde işleyin.
Altı özgün durum ne üretti?
Bu başlangıcı, belgenin yazı tipleri loaded bildirirken Chrome'da aynen çalıştırdık. Aşağıdaki ölçülen uzunluklar o ortamın SVG metin uzunluklarıdır; boyalı glif sınırları veya yazı tipi taşınabilirlik garantisi değildir.
| Durum | Genişlik / en çok satır | Sonuç | Gözlenen en uzun satır |
|---|---|---|---|
| Özgün ifade | 200 / 4 | İki satır: Keep artwork and; labels separate | 172.44 |
| Aynı ifade, dar kutu | 110 / 5 | Dört satır: Keep; artwork; and labels; separate | 100.30 |
| Daha uzun metin | 200 / 4 | Üç satır | 198.13 |
| Fazla uzun sözcük | 110 / 5 | overlong-word; sıfır tspan | Sözcük ölçümü 311.82 |
| Dar ifade, iki satır sınırı | 110 / 2 | too-many-lines; dört gerekli; sıfır tspan | Etiket üretilmedi |
| Düz açılı ayraçlar ve ve işareti | 200 / 4 | Tek düz metin satırı | 162.66 |
Özgün ifade Keep artwork and labels separate değeridir. Uzun sürüm Keep the original artwork beside every exported label değeridir. Kabul edilen her satır seçilen genişliğe sığdı. Reddedilen durumlar etiketi atarken illüstrasyonu görünür tuttu; hata durumunu incelemek kolaylaştı.
Serileştirilen altı dosyanın tamamı özdeş özgün eşkenar dörtgen yolunu, bir yerel metin öğesini korudu ve image veya foreignObject içermedi. Kabul edilen durumlar sırasıyla iki, dört, üç ve bir tspan içerdi; iki reddedilen durumda sıfır vardı. Ayrıca altı serileştirilmiş SVG'yi aynı tarayıcıda görsel olarak yeniden açtık; hepsi 360×240'ta yüklendi. Bu, bu dosyalar ve tarayıcının kontrolüdür; her düzenleyici veya işleyicinin değil.
Düzenlenmiş SVG'yi kaydedip teslimi sınayın
XMLSerializer.serializeToString(), rapordaki serileştirilmiş işaretlemeyi oluşturur. Başarılı durumun serialized dizgesini .svg dosyası olarak kaydedin. Dosya zaten açık satırlar içerir; yeniden oluşturmak için bu JavaScript rutinine ihtiyaç duymaz.
Sözcükleri hâlâ canlı metindir. Kayıtlı dosya yazı tipi dosyası veya dış hatlı glifler değil, font-family isteğini korur. Başka yazı tipine sahip hedef, satır dizgeleri ve konumları kalsa bile harfleri değiştirebilir. Özgün metni ve düzen girdilerini çıktı kopyasının yanında saklayın, teslimden önce gerçek hedefi inceleyin. Sabit harf şekli için ana dosyada canlı metni korurken uygun kaynak düzenleyicide dış hatlı teslim kopyası oluşturun.
foreignObject metin kılavuzu, başka işleyicide kaybolan HTML etiketlerini ele alır. Bu başlangıç en baştan yerel metin oluşturur. textLength kılavuzu, uzunluğu ayarlayarak tek satırı sığdırmayı ele alır; ölçülmüş satırlar eklemekten farklı karar sağlar. Tüm etiketi çevre çizime göre konumlandırmanız gerektiğinde dikey hizalama kılavuzunu kullanın.
Sarmalayıcının sınırlarını açık tutun
Boşluktan bölmek bilinçli olarak küçük bir politikadır. Yinelenen boşlukları, sekmeleri ve açık satır sonlarını birleştirir; dile özgü kırma kurallarını, tirelemeyi, çift yönlü paragrafları veya karışık stilli zengin metin aralıklarını işlemez. Metniniz bu özellikleri gerektiriyorsa başka düzen sistemi seçin veya politikayı genişletip sınayın.
Genişlik kontrolü, dekoratif glif taşmalarının çizili çerçevede kalacağını veya beş taban çizgisinin seçilen yüksekliğe sığacağını da garanti etmez. Görünür yazıları teslim boyutunda inceleyin. Testimiz yatay etiketler, sabit yazı tipi öznitelikleri ve 30 birim satır adımı kullanır; yeniden ölçmeden sayıları başka yazı tipine taşımayın.
İllüstrasyonunuz yalnızca PNG veya JPG ise ve düzenlenebilir çizim gerekiyorsa PerfectVector ile küçük resim SVG'si hazırlayın, kenarlarını ve iç açıklıklarını inceleyin, çizimi etiketten ayrı tutun. Metni yerel metin olarak yeniden yazın, kaydırma politikasını çalıştırın ve birleştirilmiş çıktıyı inceleyin. Vektörleştirme illüstrasyon yollarını hazırlar; satır kırmalarını seçmez veya izlenmiş yazılardan düzenlenebilir sözcükleri geri getirmez. Mevcut iyi yollar olduğu gibi kalabilir. Vektörleştirme kılavuzu bu kaynak geri kazanma adımını açıklar.
Sık sorulan sorular
tspan her satırın nereden kırılacağını otomatik belirler mi? Bu başlangıç sözcük gruplarını ölçerek karar verir, sonra kabul edilen her satır için konumlandırılmış bir tspan oluşturur. SVG 2'nin CSS tabanlı otomatik kaydırmasını sınamaz.
Etiketten geniş sözcüğe ne olur? Rutin overlong-word döndürür ve etiket üretmez. Sessizce kırpmak yerine alanı genişletin, metni kısaltın veya ayrı sınanmış kırma politikası sağlayın.
maxLines kalan sözcükleri kaldırır mı? Hayır. Bu politika fazla satır gerektiğinde sonucu reddeder, gereken sayıyı bildirir ve sıfır tspan bırakır. Uygulama bu durumu işlemelidir.
Dışa aktarılan satırlar başka yazı tipiyle özdeş görünür mü? Açık sözcükler ve konumlar kalır, ancak dosya yazı tipi gömmez veya glifleri dış hatlara dönüştürmez. Gerçek hedefte kullanılabilir yazı tipini ve harfleri inceleyin.
Kaynaklar
- MDN tspan öğesi — konumlandırılmış alt metin ve satır ofseti öznitelikleri.
- W3C SVG 2 metni — açık konumlandırma ve ayrı otomatik kaydırma modeli.
- MDN getComputedTextLength — gerçek SVG metnini ölçme.
- MDN FontFaceSet.ready — yazı tipi yüklemesini ve düzeni bekleme.
- MDN textContent — işaretleme yerine düz metin ekleme.
- MDN XMLSerializer — düzenlenmiş SVG işaretlemesini kaydetme.
Kendi raster illüstrasyonunuz için düzenlenebilir küçük resim hazırlayın, şekillerini inceleyin ve ana dosyayı saklayın. Metni ayrı ekleyin, ardından teslimden önce kayıtlı SVG'de kabul edilen satırları ve görünür yazıları doğrulayın.
Bloga daha fazlası

SVG.js: Çizim Alın, Yolu Düzenleyin, SVG Dışa Aktarın
Güvenilir SVG işaretlemesini SVG.js'ye alın, adlandırılmış yolu renklendirin, tam kök ve yalnız alt öğe çıktılarını kontrol edin. Özgün geometriyi koruyup kayıtlı dosyaları inceleyin.

Leaflet SVG İşaretçileri: Ucu iconAnchor ile Hizalayın
Görünür ucu iconAnchor'a eşleyerek Leaflet'te özel SVG işaretçilerini konumlandırın. viewBox boşluğunu, en boy oranı sığdırmasını ve simge boyutu değişikliklerini hesaba katın.
