SVG/Tutorials/Text/mehrzeiliger Text
Eines der großen Mankos von SVG ist die Tatsache, dass Sie längere Texte nicht einfach so notieren können, dass er sich mehrzeilig auf den verfügbaren Platz aufteilt. Genauso ist, anders als in HTML, eine Färbung und Umrahmung der Fläche hinter dem Text nur über Umwege möglich.
- In HTML bilden alle Elemente Blöcke (→ Box-Modell) mit einer Breite und Höhe. So kann Text eine Farbe, aber auch eine Hintergrundfarbe oder einen Rand erhalten.
- In SVG besteht Text nur aus den Zeichen oder Glyphen, denen eine Füllung (fill) oder Randlinie (stroke) geben kannst. Der „Hintergrund“ existiert nicht und muss erst erzeugt werden.
Inhaltsverzeichnis
mehrzeiliger Text in SVG 2
In SVG 2 erhalten text-Elemente einen Inhaltsbereich (content-area), der mit Breitenangaben oder Formen gestaltet werden kann. Das text
-Element gilt als Block-Element, evtl. darin enthaltene Elemente wie tspan und textPath gelten als inline-Elemente.
inline-size
Die inline-size-Eigenschaft ermöglicht es künftig, Breitenangaben für text festzulegen.
Folgende Werte sind möglich:
Längenangabe
: absolutProzentangabe
: relativ zur Viewport-Breite
Falls kein Wert angegeben wird, gilt es als nicht festgelegt und wird theoretisch unendlich groß, d. h. faktisch nimmt es die Länge des Textes ein.
<rect x="10" y="10" width="200" height="150" fill="#5a9905"/>
<text x="20" y="10" inline-size="180" role="heading">
Überschrift
</text>
<text x="20" y="60" inline-size="180" >
Dies ist ein Textabsatz mit einem
<tspan class="strong">fetten</tspan>
Wort und automatischem Zeilenumbruch.
</text>
In diesem Beispiel besteht der Text aus zwei Text-Elementen mit einer Breite von 180 Einheiten.
Die Überschrift erhält ein role-Attribut mit dem Wert heading
- das Aussehen wird mit CSS festgelegt.
Gleiches gilt für den Text, der sich nun automatisch an die festgelegte Breite anpasst und entsprechend umbricht.
shape-inside
Die shape-inside-Eigenschaft ermöglicht es, den Inhaltsbereich auf eine CSS-Form (shape) oder eine SVG-Grundform anzuwenden.
shape-outside
Die shape-outside-Eigenschaft definiert ein Element, um das Text herumgeführt wird.
Text mit mehreren tspan-Elementen
Bis dahin bleibt keine andere Möglichkeit als jede vorgesehene Textzeile in ein eigenes tspan-Element zu packen.
<text class="mehrzeilig" x="20" y="130">
<tspan x="10" dy="2em">Mehrzeiliger Text ist möglich, wenn Sie</tspan>
<tspan x="10" dy="2em">die einzelnen Zeilen in <tspan>-Elemente notieren.</tspan>
<tspan x="10" dy="2em">Allerdings sind diese Zeilen nicht responsiv.</tspan>
</text>
text.mehrzeilig tspan {
x: 20;
dy: 2em;
/* funktioniert nicht! */
}
Eine Festlegung mit CSS funktioniert nicht. Der X-Wert würde sich auf das jeweils vorhergehende tspan-Element beziehen. dy ist kein Präsentationsattribut.
Nachteile dieser Methode sind …
- der aufgeblähte Markup durch die zusätzlichen tspan-Elemente
- keine Möglichkeit prozentuale Breitenangaben (und damit einen automatischen Umbruch) zu verwenden.
Rahmen um Text erzeugen
Wie oben bereits erwähnt, besteht SVG-Text nur aus den Zeichen oder Glyphen. Der „Hintergrund“ existiert nicht, kann aber mit JavaScript dynamisch erzeugt werden.
<svg xmlns="http://www.w3.org/2000/svg">
<text id="txt" x="20" y="40" font-size="24" >Das ist der Text.</text>
</svg>
Mit Javascript können wir den Text selektieren und seine Dimensionen auslesen.
document.querySelector('button').addEventListener('click', drawBanner);
function drawBanner(){
const horizontalPadding = 1.05,
lineHeight = 1.25; //größer wegen der Unterlängen!
var textobj = document.getElementById('txt'),
textWidth = textobj.getComputedTextLength().toFixed(1),
textHeight = textobj.getAttribute("font-size"),
rectobj = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
rectobj.setAttribute('x', textobj.getAttribute('x') - 5);
rectobj.setAttribute('y', textobj.getAttribute('y') - textHeight);
rectobj.setAttribute('height', (lineHeight * textHeight).toFixed(1));
rectobj.setAttribute('width', (horizontalPadding * textWidth).toFixed(1));
textobj.parentNode.insertBefore(rectobj, textobj);
};
Mit getComputedLength()
wird die Länge des vorhandenen Texts ermittelt und der Variable textWidth
übergeben. Die Höhe wird aus der Schriftgröße, die im font-size-Attribut festgelegt wurde, ermittelt und textHeight
zugewiesen. (Wenn die Schriftgröße nur im CSS festgelegt wäre, müsste sie dort mit getPropertyCSSValue()
ermittelt werden.)
Jetzt wird mit createElementNS() ein neues SVG-Element erzeugt und ihm mit setAttribute() die Geometrie-Attribute mit den passenden Werten hinzugefügt. Die Breite und Höhe errechnet sich aus den ermittelten Werten des Textes, die mit horizontalPadding
und lineHeight
, die als Konstanten definiert werden, multipliziert. Da in der font-size Unterlängen nicht berücksichtigt wurden, ist der Wert für lineHeight
höher. Anschließend wird das neue rect-Element mit insertBefore vor dem Text gezeichnet, da es ja (noch) keinen z-index bei SVG gibt. So wird erst der Hintergrund und Rahmen und dann davor der Text geszeichnet.
durch ein
HTML in SVG
Leider ist es nicht möglich HTML-Elemente direkt in SVG einzubinden. Mit HTML5 wurde es zwar möglich, dass HTML-Browser auch SVG parsen – bei HTML innerhalb von SVG ist dies aber (noch) nicht der Fall.
<rect x="10" y="10" width="200" height="150" fill="#5a9905">
<h2>Überschrift</h2>
<p>Dies ist ein Textabsatz mit einem <strong>fetten</strong> Wort und automatischem Zeilenumbruch.</p>
</rect>
foreignObject
Mit dem foreignObject-Element können Sie jedoch SVG durch andere XML-Formate, unter anderem auch XHTML oder MathML, erweitern.
<rect x="10" y="10" width="200" height="150" fill="#5a9905"/>
<foreignobject x="20" y="10" width="180" height="150">
<body xmlns="http://www.w3.org/1999/xhtml">
<h2>Überschrift</h2>
<p>
Dies ist ein Textabsatz mit einem <strong>fetten</strong> Wort und automatischem Zeilenumbruch.
</p>
</body>
</foreignobject>
<circle cx="300" cy="200" r="100" fill="#dfac20"/>
<foreignobject x="230" y="120" width="180" height="180">
<h2>Überschrift</h2>
<ol>
<li>Listenelement</li>
<li>Listenelement</li>
<li>Listenelement</li>
</ol>
</foreignobject>
Wie Sie sehen, hat das zweite foreignObject
weder ein requiredExtensions
-Attribut noch einen Namensraum.
Wenn Sie SVG in Webseiten mit HTML5-Doctype einbetten, benötigen Sie keine Namensraumdeklaration für die SVG- und die foreignObject-Elemente mehr.
Achtung!