# SVG textPath mit Batik



## FI_Phil (14. Aug 2009)

Hallo,

ich habe Probleme mit dem Erstellen eines Textpfades mit Batik.
Die Forensuche nach textPath ergab keine Treffer, deswegen erstelle ich ein neues Thema.
Ich würde mich definitiv als Java-Neuling bezeichnen, deswegen entschuldigt bitte Anfänger-Fehler und -Fragen . Mit dem ganzen folgenden Post, SVG und Batik hätte ich vor einer Woche noch überhaupt nichts anfangen können.

Nun zur Sache:
Bei Beginn der Entwicklung meines Programms habe ich mich entschieden, die Grafik mit dem SVGGenerator zu erzeugen und nicht mit der DOM API. Die Grafik soll eine Art Kreisdiagramm sein. Wichtig an dieser Stelle ist, dass ich Teilabschnitte kreisförmig beschriften möchte.
Zuerst habe ich mich mit SVG beschäftigt und manuell folgendes erstellt:

[XML]
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg width="20cm" height="20cm" viewBox="0 0 2001 2001"
     xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink">
  <title>test title</title>
  <desc>test desc</desc>

  <!-- Quadratische Umrandung -->
  <rect x="1" y="1" width="2000" height="2000"
      fill="lightgrey" stroke="black" stroke-width="1" />

  <!-- Kreis -->
  <circle cx="1000" cy="1000" r="1000"
  	fill="rgb(5,90,141)"/>

  <!-- Pfade für Texte -->
  <path id="Beschriftung1" fill="none" stroke="red" stroke-width="10"
  	d="M 1000,0 A 1000,1000 0 0,1 1642.7876096865393263226434099073,233.95555688102196479760734944458" />
  <path id="Beschriftung2" fill="none" stroke="yellow" stroke-width="10"
  	d="M 1642.7876096865393263226434099073,233.95555688102196479760734944458 A 1000,1000 0 0,1 1866.0254037844386467637231707529,1500" />
  <path id="Beschriftung3" fill="none" stroke="red" stroke-width="10"
  	d="M 1342.0201433256687330440996146823,1939.6926207859083840541092773247 A 1000,1000 0 0,0 1866.0254037844386467637231707529,1500" />
  <path id="Beschriftung4" fill="none" stroke="yellow" stroke-width="10"
  	d="M 357.21239031346067367735659009274,1766.0444431189780352023926505554 A 1000,1000 0 0,0 1342.0201433256687330440996146823,1939.6926207859083840541092773247" />
  <path id="Beschriftung5" fill="none" stroke="red" stroke-width="10"
  	d="M 15.192246987791940633256975410477,826.35182233306965114828337323069 A 1000,1000 0 0,0 357.21239031346067367735659009274,1766.0444431189780352023926505554" />
  <path id="Beschriftung6" fill="none" stroke="yellow" stroke-width="10"
  	d="M 15.192246987791940633256975410477,826.35182233306965114828337323069 A 1000,1000 0 0,1 1000,0" />

	<!-- Textpfade -->
  <text font-family="Verdana" font-size="50" fill="white" text-anchor="middle" >
    <textPath xlink:href="#Beschriftung1" startOffset="50%">
      <tspan dy="60" >Beschriftung1</tspan>
    </textPath>
    <textPath xlink:href="#Beschriftung2" startOffset="50%">
      <tspan dy="0" >Beschriftung2</tspan> <!-- warum dy="0" ? -->
    </textPath>
    <textPath xlink:href="#Beschriftung3" startOffset="50%">
      <tspan dy="-90" >Beschriftung3</tspan>
    </textPath>
    <textPath xlink:href="#Beschriftung4" startOffset="50%">
      <tspan dy="0" >Beschriftung4</tspan> <!-- warum dy="0" ? -->
    </textPath>
    <textPath xlink:href="#Beschriftung5" startOffset="50%">
      <tspan dy="0" >Beschriftung5</tspan> <!-- warum dy="0" ? -->
    </textPath>
    <textPath xlink:href="#Beschriftung6" startOffset="50%">
      <tspan dy="90" >Beschriftung6</tspan> <!-- warum dy="90" statt 60? -->
    </textPath>
  </text>
</svg>
[/XML]

Für die Erstellung der Pfade mit den Ellipsen-Befehlen (w3c Beschreibung) habe ich mich entschieden, da ich die Winkel zwischen den Linien kenne, welche quasi vom Kreismittelpunkt zum Rand verlaufen und "Tortenstücke" bilden. Zwischen den Endpunkten dieser Linien sollen die Pfade verlaufen. Die Pfade anhand einer Bézier-Kurve zu erstellen schien mir zu umständlich. Im obigen Fall habe ich die Endpunkte mit einem Hilfsdreieck und den guten alten sin(Winkel) = Gegenkathete/Hypotenuse etc. Sätzen errechnet. Je nach Viertel des Kreises ist die Berechnung ein bisschen anders und large-arc-flag und sweep-flag müssen unterschiedlich gesetzt sein. Später soll die Errechnung im Programm dynamisch erfolgen.

Nachdem ich Kreise und andere Elemente bereits mit dem SVGGenerator erzeugt hatte, habe ich dann nach Grafikelementen von Java gesucht, die mir bei der Darstellung der Pfade helfen und bin auf Path2D.Float gekommen. Hier fand ich jedoch nur die curveTo()-Methode, welche eine Bézier-Kurve erzeugt.
Da ich schnell zu einem vorzeitigen Ergebnis kommen wollte und wusste, wie man die Darstellung in SVG erzeugt, habe ich doch zur DOM API gegriffen und wollte erstmal beide Methoden mixen.
Die Erstellung des Pfades, der für den Textpfad benötigt wird, hat auch (fast - der Pfad ist zu lang, etwas muss bei der Berechnung noch nicht stimmen) wunderbar geklappt:


```
String svgNS = SVGDOMImplementation.SVG_NAMESPACE_URI;
		Element svgRoot = document.getDocumentElement();
		Element path = document.createElementNS(svgNS, "path");
		path.setAttribute("id", "Gruppe1");
		path.setAttribute("stroke", "yellow");
		path.setAttribute("stroke-width", "10");
		/*
		x = 250 + (cos(50°) * 250) = 1642,7876096865393263226434099073
		y = 250 - (sin(50°) * 250) = 233,95555688102196479760734944458
		Textpath Verschiebung: 60
		 */
		path.setAttribute("d", "M 250,0 A 250,250 0 0,1 " + String.valueOf(250+Math.cos(50)*250) + "," + String.valueOf(250-Math.sin(50)*250));
		svgRoot.appendChild(path);
```
Aber dann ging es mit den Schwierigkeiten los. Hier versuche ich, den Textpfad zu erstellen:

```
/*
        <text font-family="Verdana" font-size="50" fill="white" text-anchor="middle" >
        	<textPath xlink:href="#Beschriftung1" startOffset="50%">
        		<tspan dy="60" >Beschriftung1</tspan>
        	</textPath>
        </text>
		 */
		Element tspan = document.createElementNS(svgNS, "tspan");
		tspan.setAttribute("dy", "60");
		tspan.setNodeValue("Beschriftung1");
		
		Element textPath = document.createElementNS(svgNS, "textPath");
		textPath.setAttribute("xlink:href", "#Beschriftung1");
		textPath.setAttribute("startOffset", "50%");
		System.out.println(textPath.getNodeValue()); //test
		System.out.println(textPath.getTextContent()); //test
		textPath.setNodeValue("Beschriftung1"); // wie setze ich den Text?
		textPath.setTextContent("Beschriftung1"); // wie setze ich den Text?
		textPath.appendChild(tspan);
				
		Element text = document.createElementNS(svgNS, "path");
		text.setAttribute("font-size", "50");
		text.setAttribute("fill", "black");
		text.setAttribute("text-anchor", "middle");
		text.appendChild(textPath);
		svgRoot.appendChild(text);
```
Der Text wird jedoch nicht angezeigt.
Das Erstellen und Hinzufügen der Nodes müsste richtig sein, zumindest habe ich einige Beispiele im Internet (nicht auf der Batik-Webseite, die ist meiner Meinung nach nicht sehr umfangreich) gefunden, bei denen es so gemacht wird. Wahrscheinlich ist hier schlicht das setzen des Textes falsch.
Hier also meine erste Frage: *Wie setze ich den Text, der zwischen XML-Tags sitzt?*

Damit aber nicht genug. Der Mix aus der Nutzung des SVGGenerators und der DOM API führt mich zu einem weiteren Problem:
Bei der Anzeige auf einem JSVGCanvas wird der Pfad korrekt dargestellt, beim Export in ein SVG-Dokument jedoch nicht. Als Quelle des Fehlers erscheint mir folgender Befehl:

```
svgGenerator.getRoot(document.getDocumentElement());
```
Diesen nutze ich nur beim Darstellen der Grafik auf dem Canvas-Objekt.
Ohne diesen Befehl wird die Grafik nicht auf dem Canvas-Objekt dargestellt. Beim Erzeugen des Dokuments ist es genau andersrum.

Falls es relevant ist: So stelle ich die Grafik auf dem Canvas dar:

```
svgGenerator.getRoot(document.getDocumentElement()); // wird für das Anzeigen benötigt
		canvas.setSVGDocument(document);
```
Und so erzeuge ich das Dokument:

```
//svgGenerator.getRoot(document.getDocumentElement()); // würde dazu führen, dass der folgende Stream nicht funktioniert

			svgGenerator.stream(file.getAbsolutePath() + ".svg"); // Schreibt die SVG-Datei
```

Damit lautet meine zweite Frage entweder: *Wie sorge ich dafür, dass mit der Benutzung des SVGGenerators und der DOM API quasi an EINER Grafik gearbeitet wird?*
oder: *Wie vermeide ich die Nutzung der DOM API? / Mit welchem Objekt kann ich SVG Textpfade darstellen?*
Ich habe mir schon Arc2D angesehen. Kann man mit getPathIterator() einen Pfad erzeugen? Wie kriege ich dann den Text auf diesen Pfad? Oder soll ich komplett mit der DOM API arbeiten?

Vielen Dank im Voraus!

Gruß,
Phil


----------



## FI_Phil (30. Sep 2009)

Hallo,

ich habe gerade gesehen, dass mein Thread als erledigt markiert wurde. Leider ist dem aber nicht so - mein Problem ist noch nicht gelöst.
Ich könnte mir vorstellen, dass die am Ende meines Posts erwähnte Möglichkeit mit Arc2D die einafchste Lösung ist, aber ich habe noch keine Quellen gefunden, die eine Benutzung mit diesem Element auf die Weise beschrieben, wie ich sie benötige. Falls also jemand einen Link zu einer guten Quelle hierzu kennt, dann würde ich mich dort nochmal umsehen und versuchen das Problem selber zu lösen. Sobald ich eine Lösung gefunden habe, sage ich natürlich Bescheid.

Gruß,
Phil


----------



## FI_Phil (5. Okt 2009)

Einige meiner Fragen konnte ich jetzt selber beantworten.
Mit Arc2D habe ich jetzt noch nicht weiter versucht, einen Pfad zu erstellen. Und wenn ich hier einen Pfad hätte, wüsste ich nicht, wie ich diesem Pfad Text hinzufügen könnte.
Aber das Mixen von der SVG Erstellung mit Graphics2D einer- und dem DOM andererseits funktioniert nun. Bisher hat ja nur das Anzeigen auf dem Canvas funktioniert und beim Export als Datei existierten nur die Elemente, welche mit Graphics2D erzeugt wurden.
Lösung:
Statt

```
//svgGenerator.getRoot(document.getDocumentElement()); // würde dazu führen, dass der folgende Stream nicht funktioniert
 
            svgGenerator.stream(file.getAbsolutePath() + ".svg"); // Schreibt die SVG-Datei
```
mache ich folgendes:

```
FileOutputStream fos = new FileOutputStream(file.getAbsolutePath() + ".svg");
			Writer wout = new OutputStreamWriter(fos);
			svgGenerator.stream(svgGenerator.getRoot(document.getDocumentElement()), wout);
```

Auch habe ich es geschafft, den Text zwischen die tspan Tags zu setzen:

```
Element tspan = document.createElementNS(svgNS, "tspan");
		tspan.setAttribute("dy", "60");
		//tspan.setNodeValue("Beschriftung1"); // hat scheinbar keinen Effekt
		tspan.setTextContent("Beschriftung1"); // Hiermit wird der Text zwischen <tspan>..</tspan> gesetzt
```
Trotzdem wird die Beschriftung nicht korrekt angezeigt. Die Passage mit dem Textpath tritt aus mir noch unbekannten Gründen zwei mal in der Datei auf. Sie lautet:
[XML]
<path d="M 250,0 A 250,250 0 0,1 491.2415071230283,315.5937134259822" id="Beschriftung1" stroke-width="10" stroke="yellow"
  /><path text-anchor="middle" fill="black" font-size="50"
  ><textPath xlink:href="#Beschriftung1" xlink:actuate="onLoad" xlink:type="simple" xlink:show="other" xmlns:xlink="http://www.w3.org/1999/xlink" startOffset="50%"
    ><tspan dy="60"
      >Beschriftung1</tspan
    ></textPath
  ></path
  >
[/XML]

Ich kämpfe mich so Schritt für Schritt voran, aber bisher bin ich noch nicht zum Ziel gelangt. Ich bin mir sicher, dass es für einen Batik- oder auch XML-Experten eine Kleinigekeit ist, folgende immer noch geltende Kernfrage mit einem winzigen Code-Snippet zu beantworten:

*Wie erzeugt man einen Textpfad mit der DOM API von Batik?*

Vielen Dank und einen guten Start in die Woche,
Phil


----------



## SlaterB (5. Okt 2009)

nur nebenbei:
du nutzt eine komplizierte API, die dir alle möglichen Schwierigkeiten bereitet,
um aus vorhandenen Informationen wie "ich möchte einen Pfad x von a nach b" am Ende eine einfache Textdatei
a la "<path id="Beschriftung1" fill="none" stroke="red" .." zu erstellen?

an dem Punkt war ich nämlich auch und habe mich recht schnell entschlossen, das selber zu machen,
die paar Texte mit < und > und Parametern zusammenzusammeln..,
Verschachtelung mit Farben usw. wird etwas schwieriger, ne Baumstruktur ist dann angebracht

Unfälle wie "1342.0201433256687330440996146823,1939.6926207859083840541092773247 passieren dann auch nicht mehr, 
wobei man dies hier vielleicht durch Runden vor Übergabe an Batik verhindern kann


----------



## FI_Phil (6. Okt 2009)

Danke für deine Antwort!

Da die SVG-Datei auf jeden Fall dynamisch erzeugt, mit einem Java-Programm erzeugt und auf einer grafischen Oberfläche angezeigt werden soll, schien mir Batik die beste Möglichkeit zu sein, alle drei Anforderungen mit einer Klappe zu zu schlagen .
Ich könnte die Grafik mit JDOM, StAX oder ähnlichem (bisher hab ich mit keiner dieser Möglichkeiten gearbeitet) oder sogar komplett manuell Zeile für Zeile per Dateiausgabe erstellen, jedoch würde ich spätestens für die Anzeige auf der grafischen Oberfläche Batik nutzen müssen.
Aber vielleicht ist das gar nicht mal so eine schlechte Idee. Ich werde diese Möglichkeit mal genauer unter die Lupe nehmen.

Falls derweil jemand noch einen guten Tipp oder sonstige Anregungen hat, sind diese natürlich stets willkommen!


----------

