Convert svg to embedded svg with XSLT

Here should go questions about transforming XML with XSLT and FOP.
Apollo102
Posts: 8
Joined: Mon Nov 26, 2018 12:56 am

Convert svg to embedded svg with XSLT

Post by Apollo102 »

Hi,
I would like to convert a svg file opened in Oxygen with xslt so that I can copy and paste it into a DITA svg container.
The xslt brings only limited success and some additional manual work is unfortunately required.

Example file:

Code: Select all

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 919 647" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
    <g transform="matrix(1,0,0,1,-214.985,-499.215)">
        <g id="Alles">
            <g id="Test">
                <rect x="535.908" y="574.988" width="387.783" height="222.864" style="fill:rgb(235,235,235);"/>
                <circle cx="976.064" cy="897.026" r="152.662" style="fill:rgb(235,235,235);"/>
            </g>
            <rect x="576.024" y="1020.72" width="557.159" height="124.804" style="fill:url(#_Linear1);"/>
        </g>
    </g>
    
    <text x="166.628px" y="336.867px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:33.333px;">Audit-T<tspan x="272.829px 283.929px " y="336.867px 336.867px ">ra</tspan>il</text>
      
    <defs>
        <linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(557.159,0,0,124.804,576.024,1083.12)"><stop offset="0" style="stop-color:black;stop-opacity:1"/><stop offset="1" style="stop-color:rgb(235,235,235);stop-opacity:1"/></linearGradient>
     </defs>
</svg>
Desired result:
1. remove namespace declartion in root element
2. add namespace prefix for all elements (svg:)
3. remove @id in all elements, except in linearGradient
4. remove tspan in text

Code: Select all

<svg:svg width="100%" height="100%" viewBox="0 0 919 647" version="1.1" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;" preserveAspectRatio="xMidYMid meet" zoomAndPan="magnify" contentScriptType="text/ecmascript" contentStyleType="text/css">
    <svg:g transform="matrix(1,0,0,1,-214.985,-499.215)">
        <svg:g>
            <svg:g>
                <svg:rect x="535.908" y="574.988" width="387.783" height="222.864" style="fill:rgb(235,235,235);"/>
                <svg:circle cx="976.064" cy="897.026" r="152.662" style="fill:rgb(235,235,235);"/>
            </svg:g>
            <svg:rect x="576.024" y="1020.72" width="557.159" height="124.804" style="fill:url(#_Linear1);"/>
        </svg:g>
    </svg:g>
    
    <svg:text x="166.628px" y="336.867px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:33.333px;">Audit-Trail</svg:text>
      
    <svg:defs>
        <svg:linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" xlink:type="simple" xlink:show="other" xlink:actuate="onLoad"><svg:stop offset="0" style="stop-color:black;stop-opacity:1"/><svg:stop offset="1" style="stop-color:rgb(235,235,235);stop-opacity:1"/></svg:linearGradient>
     </svg:defs>
</svg:svg>




Current result (namespace in root element and tspan are not removed):

Code: Select all

<svg:svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:serif="http://www.serif.com/" xmlns:svg="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 919 647" version="1.1" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;" preserveAspectRatio="xMidYMid meet" zoomAndPan="magnify" contentScriptType="text/ecmascript" contentStyleType="text/css">
    <svg:g transform="matrix(1,0,0,1,-214.985,-499.215)">
        <svg:g>
            <svg:g>
                <svg:rect x="535.908" y="574.988" width="387.783" height="222.864" style="fill:rgb(235,235,235);"/>
                <svg:circle cx="976.064" cy="897.026" r="152.662" style="fill:rgb(235,235,235);"/>
            </svg:g>
            <svg:rect x="576.024" y="1020.72" width="557.159" height="124.804" style="fill:url(#_Linear1);"/>
        </svg:g>
    </svg:g>
    
    <svg:text x="166.628px" y="336.867px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:33.333px;">Audit-T<svg:tspan x="272.829px 283.929px " y="336.867px 336.867px ">ra</svg:tspan>il</svg:text>
      
    <svg:defs>
        <svg:linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" xlink:type="simple" xlink:show="other" xlink:actuate="onLoad"><svg:stop offset="0" style="stop-color:black;stop-opacity:1"/><svg:stop offset="1" style="stop-color:rgb(235,235,235);stop-opacity:1"/></svg:linearGradient>
     </svg:defs>
</svg:svg>



My XSLT:

Code: Select all


<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:xlink="http://www.w3.org/1999/xlink/"    
    xmlns:svg="http://www.w3.org/2000/my/"    
    xmlns:serif="http://www.serif.com/"
    
        
    exclude-result-prefixes="svg serif xlink" >

    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"  exclude-result-prefixes="svg serif xlink"/>
    <xsl:strip-space elements="*"/>
    
    
    <xsl:template match="@xlink:noNamespaceSchemaLocation"/>
    <xsl:template match="@serif:noNamespaceSchemaLocation"/>
    <xsl:template match="@svg:noNamespaceSchemaLocation"/>
    
    
    <xsl:template match="tspan" priority="1000"/>
    
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    
    
    <xsl:template match="@serif:id"/>
    
    <xsl:template match="@gradientUnits"/>
    <xsl:template match="@gradientTransform"/>
    
    <xsl:template match="@id"/>
    
    <xsl:template match="*" priority="100">
          <xsl:element name="svg:{name()}" namespace="{namespace-uri()}">
            <xsl:copy-of select="namespace::*[not(. = 'http://www.w3.org/1999/xlink/')]" />
            <xsl:variable name="localName" select="local-name()"/>
              <!-- keep @id for linearGradient -->  
            <xsl:if test="$localName = 'linearGradient'"> 
                <xsl:attribute name="id"><xsl:value-of select="@id"/></xsl:attribute>
            </xsl:if>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:element>
    </xsl:template>
    

</xsl:stylesheet>



The if-query is certainly messy. But I couldn't do it any other way. Templates for concrete elements don't seem to do anything at all.

Can anyone give me tips on what I'm doing wrong?

Thanks a lot

Regards

Apollo
Radu
Posts: 9421
Joined: Fri Jul 09, 2004 5:18 pm

Re: Convert svg to embedded svg with XSLT

Post by Radu »

Hi Apollo,

So about your problems:
namespace in root element
An XSLT only generates namespace wellformed XML documents, so you cannot generate this with an XSLT:

Code: Select all

<svg:svg></svg:svg>
because it is not namespace wellformed, the svg prefix does not bind to a namespace.
So what you want cannot be achieved only with XSLT, you would need an extra regular expression find replace afterwards to remove the namespace declaration.
and tspan are not removed
So you got this template:

Code: Select all

 <xsl:template match="tspan" priority="1000"/>
The "tspan" element in the original SVG is in the namespace "http://www.w3.org/2000/svg".
The XPath "tspan" matches an element with local name "tspan" but in an empty namespace (or no namespace as we call this).

If you add an extra "xmlns="http://www.w3.org/2000/svg"" definition on the "xsl:stylesheet" root element, all Xpaths which do not specify a prefix will match elements in the "xmlns="http://www.w3.org/2000/svg"" namespace.

Or you can also do this:

Code: Select all

 <xsl:template match="*:tspan"/>
to match tspan in any namespace, or this:

Code: Select all

 <xsl:template match="s:tspan" xmlns:s="http://www.w3.org/2000/svg"/>
I hope this helps.


Regards,
Radu
Radu Coravu
<oXygen/> XML Editor
http://www.oxygenxml.com
Radu
Posts: 9421
Joined: Fri Jul 09, 2004 5:18 pm

Re: Convert svg to embedded svg with XSLT

Post by Radu »

I forgot,

About your original need, why don't you just refer to the SVG images using the DITA "image" element?
If you want the SVG to be embedded in the result HTML output (maybe it contains animation for example) there is also this older publishing plugin which does this dynamically at publishing time:
https://github.com/oxygenxml/oxygen-embed-images

Regards,
Radu
Radu Coravu
<oXygen/> XML Editor
http://www.oxygenxml.com
Apollo102
Posts: 8
Joined: Mon Nov 26, 2018 12:56 am

Re: Convert svg to embedded svg with XSLT

Post by Apollo102 »

Hi Radu,

thank you very much for your quick and detailed answer.

I use these embedded svg when it comes to illustrations with text. It's easier for the translation process. These texts are then part of the DITA file. External svg files would be much more complicated for this.
Of course, this also has limitations because there are no multi-line texts in svg. But it is sufficient for simple screen images or flowcharts.

I'll report back when I've tried everything.

Thanks

Regards

Apollo

P.S. I'm looking forward seeing you live on stage with your hat on again. :)

Radu wrote: Thu Nov 18, 2021 11:43 am I forgot,

About your original need, why don't you just refer to the SVG images using the DITA "image" element?
If you want the SVG to be embedded in the result HTML output (maybe it contains animation for example) there is also this older publishing plugin which does this dynamically at publishing time:
https://github.com/oxygenxml/oxygen-embed-images

Regards,
Radu
Post Reply