0

My xsl sheet transforms plain text to svg. I get the plain text from a user input to my servlet, and after that I want to pass the input as a parameter to my xsl sheet. My last question was how to pass the parameter and is answered here (Pass user input to XSL stylesheet), but I realized I don't know how to call the transformation. My xsl doesn't take a xml input, so I can't use transformer.transform for instance. How is it possible to run the transformation with the parameter and get the result as a string that I can return to the user?

Any help would be appreciated. If you need more details let me know. Thanks!

Edit: If you are interested,the idea is that I can transform a plain text notation to svg and it works that the first part of the transformation transforms the plain text to a xml notation and after that the xml to svg(XSLT 2.0: Transform notation in plain text to svg). It works fine for simple symbols when the plain text is in a variable. But I want to expand a webservice with the the functionality and it should be possible that a user gives the input, that is why I want to pass a parameter to my xsl. The problem is that I donn't know how to call the transformation from java that I can get the result to return it to the user.

Here is the xsl:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Ansatz mit Hilfe von 
https://stackoverflow.com/questions/34682331/xslt-2-0-transform-notation-in-plain-text-to-svg -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:local="local"
    exclude-result-prefixes="xs"
    version="2.0"
    xmlns:svg="http://www.w3.org/2000/svg">
    <xsl:output indent="yes"/>
<!-- 
<xsl:variable name="drawing-text">
GRAPHREP
PEN color:$000000 w:2pt
FILL color:$ff7f00
ROUNDRECT x:0pt y:0pt w:114pt h:70pt rx:20pt ry:20pt

</xsl:variable>
 -->
<xsl:param name="drawing-text" />

    <!--matches sequences of UPPER-CASE letters -->
    <xsl:variable name="label-pattern" select="'[A-Z]+'"/>
    <!--matches the "attributes" in the line i.e. w:2pt,
        has two capture groups (1) => attribute name, (2) => attribute value -->
    <xsl:variable name="attribute-pattern" select="'\s?(\S+):(\S+)'"/> 
    <!--matches a line of data for the drawing text, 
        has two capture groups (1) => label, (2) attribute data-->
    <xsl:variable name="line-pattern" select="concat('(', $label-pattern, ')\s(.*)\n?')"/>
    <!-- Text in quotes holen-->
    <xsl:variable name="text-pattern" select="'&quot;(.*?)&quot;'"/>

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="/">
        <svg width="640" height="480">
            <g>
                <!-- Find the text patterns indicating the shape -->
                <!--Replaced unparsed-text() with local variable for testing                
                select="unparsed-text('drawing.txt')" -->
                <xsl:analyze-string select="$drawing-text"
                    regex="{concat('(', $label-pattern, ')\n((', $line-pattern, ')+)\n?')}">
                    <xsl:matching-substring>
                        <!--Convert text to XML -->
                        <xsl:variable name="drawing-markup" as="element()">
                            <!--Create an element for this group, using first matched pattern as the element name 
                                (i.e. GRAPHREP => <GRAPHREP>) -->
                            <xsl:element name="{regex-group(1)}">
                                <!--split the second matched group for this shape into lines by breaking on newline-->
                                <xsl:variable name="lines" select="tokenize(regex-group(2), '\n')"/>
                                <xsl:for-each select="$lines">
                                    <!--for each line, run through this process to create an element with attributes
                                        (e.g. FILL color:$frf7f00 => <FILL color=""/>
                                    -->
                                    <xsl:analyze-string select="." regex="{$line-pattern}">
                                        <xsl:matching-substring>
                                            <!--create an element using the UPPER-CASE label starting the line -->
                                            <xsl:element name="{regex-group(1)}">
                                                <!-- capture each of the attributes -->
                                                <xsl:analyze-string select="regex-group(2)" regex="\s?(\S+):(\S+)">
                                                  <xsl:matching-substring>
                                                  <!--convert foo:bar into attribute foo="bar", 
                                                            translate $ => # 
                                                            and remove the letters 'p' and 't' by translating into nothing"-->
                                                  <xsl:attribute name="{regex-group(1)}" select="translate(regex-group(2), '$pt', '#')"/>
                                                  </xsl:matching-substring>
                                                  <xsl:non-matching-substring/>
                                                </xsl:analyze-string>
                                            </xsl:element>
                                        </xsl:matching-substring>
                                        <xsl:non-matching-substring/>
                                    </xsl:analyze-string>
                                </xsl:for-each>
                            </xsl:element>
                        </xsl:variable>
                        <!--Uncomment the copy-of below if you want to see the intermediate XML $drawing-markup-->
                        <!--<xsl:copy-of select="$drawing-markup"/>-->

                        <!-- Transform XML into SVG -->
                        <xsl:apply-templates select="$drawing-markup"/>

                    </xsl:matching-substring>
                    <xsl:non-matching-substring/>
                </xsl:analyze-string>
            </g>
        </svg>
    </xsl:template>

    <!--==========================================-->
    <!-- Templates to convert the $drawing-markup -->
    <!--==========================================-->

    <!--for supported shapes, create the element using
        lower-case value, and change rectangle to rect
        for the svg element name   !!! if abfrage ob text-->
    <xsl:template match="GRAPHREP[ELLIPSE | RECTANGLE | ROUNDRECT | LINE | TEXT]">
        <xsl:if test="ELLIPSE | RECTANGLE | ROUNDRECT | LINE">
            <xsl:element name="{replace(lower-case(local-name(ELLIPSE | RECTANGLE | ROUNDRECT | LINE)), 'rectangle|roundrect', 'rect', 'i')}">
                <xsl:attribute name="id" select="concat('id_', generate-id())"/>
                <xsl:apply-templates />
            </xsl:element>
        </xsl:if>
        <xsl:if test="TEXT">
            <xsl:element name="{lower-case(local-name(TEXT))}">
                <xsl:attribute name="id" select="concat('id_', generate-id())"/>
                <xsl:apply-templates />
                <!-- Da muss der text aus den quotes rein -->
            </xsl:element>
         </xsl:if>
    </xsl:template>

    <xsl:template match="ELLIPSE | RECTANGLE | ROUNDRECT | LINE | TEXT"/>

    <!-- Just process the content of GRAPHREP.
        If there are multiple shapes and you want a new 
        <svg><g></g></svg> for each shape, 
        then move it from the template for "/" into this template-->
    <xsl:template match="GRAPHREP/*">
        <xsl:apply-templates select="@*"/>
    </xsl:template>

    <xsl:template match="PEN" priority="1">
        <!--TODO: test if these attributes exist, if they do, do not create these defaults.
            Hard-coding for now, to match desired output, since I don't know what the text
            attributes would be, but could wrap each with <xsl:if test="not(@dasharray)">-->
        <xsl:attribute name="stroke-dasharray" select="'null'"/>
        <xsl:attribute name="stroke-linjoin" select="'null'"/>
        <xsl:attribute name="stroke-linecap" select="'null'"/>
        <xsl:apply-templates select="@*"/>
    </xsl:template>

    <!-- conterts @color => @stroke -->
    <xsl:template match="PEN/@color">
        <xsl:attribute name="stroke" select="."/>
    </xsl:template>

    <!--converts @w => @stroke-width -->
    <xsl:template match="PEN/@w">
        <xsl:attribute name="stroke-width" select="."/>
    </xsl:template>

    <!--converts @color => @fill and replaces $ with # -->
    <xsl:template match="FILL/@color">
        <xsl:attribute name="fill" select="translate(., '$', '#')"/>
    </xsl:template>

    <!--converts @h => @font-size !!noch mit text verbinden -->
    <xsl:template match="FONT/@h">
        <xsl:attribute name="font-size" select="."/>
    </xsl:template>

    <!--converts @color => @fill !!noch mit text verbinden -->
    <xsl:template match="FONT/@color">
        <xsl:attribute name="fill" select="translate(., '$', '#')"/>
    </xsl:template>

    <!-- converts @x => @cx with hard-coded values. 
        May want to use value from text, but matching your example-->
    <xsl:template match="ELLIPSE/@x | ELLIPSE/@y">
        <!--not sure if there was a relationship between ELLIPSE x:0pt y:0pt, and why 0pt would be 250, 
            but just an example...-->
        <xsl:attribute name="c{name()}" select="250"/>
    </xsl:template>

    <xsl:template match="RECTANGLE/@w | ROUNDRECT/@w">
        <xsl:attribute name="{name()}idth" select="."/>
    </xsl:template>

    <xsl:template match="RECTANGLE/@h | ROUNDRECT/@h">
        <xsl:attribute name="{name()}eight" select="."/>
    </xsl:template>

    <xsl:template match="LINE/@x | LINE/@y">
        <xsl:attribute name="{name()}" select="."/>
    </xsl:template>

    <xsl:template match="TEXT/@x | TEXT/@y">
        <xsl:attribute name="{name()}" select="."/>
    </xsl:template>

</xsl:stylesheet>
5
  • Don't you have any <xsl:template match="..."> in your XSLT? It is an XML transformation language and requires an XML input. I'm curious to see your XSLT! Commented Feb 13, 2016 at 17:09
  • You can run a transformation on an empty XML document instance if your XSLT's output is not driven by the XML but actually by, say, <xsl:param> or calls to document(). Commented Feb 13, 2016 at 17:13
  • If all the data required to produce the output is in the passed parameter, why don't you use a dummy XML document as the input? It could be anything, e.g. a file containing only <input/>. Commented Feb 13, 2016 at 17:13
  • Which XSLT 2.0 processor do you use? It depends on its API how you call it and define your initial template. See saxonica.com/html/documentation9.6/using-xsl/embedding/… for Saxon 9.6 for example. Commented Feb 13, 2016 at 17:29
  • I use the Saxon9 API. Thx I will look into it. Commented Feb 13, 2016 at 17:42

2 Answers 2

4

In XSLT 2.0 you can use a named template (e.g. by changing <xsl:template match="/"> to <xsl:template match="/" name="main">) and then, for instance with Saxon 9.6 you could use the method http://saxonica.com/html/documentation9.6/javadoc/net/sf/saxon/s9api/XsltTransformer.html#setInitialTemplate%28net.sf.saxon.s9api.QName%29 to set myTransformer.setInitialTemplate(new QName("main")). See http://saxonica.com/html/documentation9.6/using-xsl/embedding/s9api-transformation.html on details on how to use its API.

The Java JAXP API is geared towards XSLT 1.0 and does not allow you to run a transformation without an input document, there, you would indeed need to provide a dummy document.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much. I will look into it!
1

There is no XSL transformation without XML input (in XSLT 1.0). As MartinHonnen pointed out it is possible in XSLT2.0. (XSLT1.0 is still widely used however since 2.0 is not supported natively in web browsers)

As Tomalak and Michael said you could use an empty XML document as input to the transformation and then pass the parameter.

Perhaps it would be event better to create an XML Document with your input data as the content, e.g. <dummy><input1>data from request</input1></dummy>

In the XSL document you would then create a new document, like so

<xsl:template match="/dummy">
This was the input: <xsl:value-of select="input1"/>
</xsl:template>

Note this is just one example there are many ways to achieve this.

Finally, if all you want your XSL for is to create an XML document with your input, probably you should look into creating the document directly without using XSL.

Checkout this tutorial for how to create the doc from scratch: http://examples.javacodegeeks.com/core-java/xml/dom/create-dom-document-from-scratch/

3 Comments

XSLT 2.0 allows you to run a stylesheet starting with a named template, you don't need an input document in that case.
@MartinHonnen good to know +1. Regarding this question though it feels cumbersome to use XSL (unless there is an existing sheet that has to be used).
I guess the solution in stackoverflow.com/questions/34682331/… is the existing stylesheet to be used.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.