如何处理包含一个或多个项目的序列 [英] How to process a sequence with one or more items

查看:25
本文介绍了如何处理包含一个或多个项目的序列的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 XSLT 样式表,可以将纯文本表示法转换为 SVG.它工作得很好,但只能用一个项目处理一个符号.这例如:

I have a XSLT stylesheet that transforms a plain text notation to SVG. It works just fine, but it is only possible to process a symbol with one item. This works for example:

GRAPHREP
PEN color:$000000 w:2pt
FILL color:$ff7f00
ELLIPSE x:0pt y:0pt rx:50pt ry:50pt

当我想转换一个包含多个项目的符号时,

When I want to transform a symbol with more than one items like this,

GRAPHREP
PEN color:$000000 w:2pt
FILL color:$ff7f00
ELLIPSE x:0pt y:0pt rx:50pt ry:50pt
PEN color:$000000 w:2pt
FILL color:$ff7f00
ELLIPSE x:0pt y:0pt rx:30pt ry:30pt

我将收到此错误:XPTY0004:不允许将多于一项的序列作为 local-name() 的第一个参数.如何处理一件或多件物品?我在帖子的末尾添加了我的样式表.这可能是我对这个主题的最后一个问题.非常感谢您的帮助!

I will get this error: XPTY0004: A sequence of more than one item is not allowed as the first argument of local-name(). How would it be possible to process one or more items? I add my stylesheet at the end of post. This will probably my last question for this topic. Thank you so much for all your help!

<?xml version="1.0" encoding="UTF-8"?>
<!-- Ansatz mit Hilfe von 
http://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
ELLIPSE x:0pt y:0pt rx:50pt ry:50pt
PEN color:$000000 w:2pt
FILL color:$ff7f00
ELLIPSE x:0pt y:0pt rx:30pt ry:30pt

</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="/" name="main">
        <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="."/>
    </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>

第二个输入的期望输出是这样的:

The desired output with the second input would be like this:

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns:local="local"
     xmlns:svg="http://www.w3.org/2000/svg"
     width="640"
     height="480">
   <g>
      <ellipse id="id_d2e0"
               stroke-dasharray="null"
               stroke-linjoin="null"
               stroke-linecap="null"
               stroke="#000000"
               stroke-width="2"
               fill="#ff7f00"
               cx="0"
               cy="0"
               rx="50"
               ry="50"/>
      <ellipse id="id_d2e6"
               stroke-dasharray="null"
               stroke-linjoin="null"
               stroke-linecap="null"
               stroke="#000000"
               stroke-width="2"
               fill="#ff7f00"
               cx="0"
               cy="0"
               rx="30"
               ry="30"/>
   </g>
</svg>

SVG 应该有 2 个不同的椭圆.

The SVG should have 2 different ellipses.

推荐答案

以下是生成具有所需属性的两个 SVG 形状的示例:

Here is an example that produces the two SVG shapes with the required attributes:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Ansatz mit Hilfe von 
http://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
ELLIPSE x:0pt y:0pt rx:50pt ry:50pt
PEN color:$000000 w:2pt
FILL color:$ff7f00
ELLIPSE x:0pt y:0pt rx:30pt ry:30pt

</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="/" name="main">
        <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 -->
    <!--==========================================-->

    <xsl:template match="GRAPHREP">
        <xsl:for-each-group select="*" group-starting-with="PEN">
            <xsl:apply-templates select="current-group()[not(self::PEN | self::FILL)]"/>
        </xsl:for-each-group>
    </xsl:template>

    <xsl:template match="RECTANGLE | ROUNDRECT">
      <rect>
            <xsl:attribute name="id" select="concat('id_', generate-id())"/>
            <xsl:apply-templates select="@*, current-group()[self::PEN], current-group()[self::FILL]"/>          
      </rect>        
    </xsl:template>

    <xsl:template match="ELLIPSE | LINE | TEXT">
      <xsl:element name="{lower-case(local-name())}">
            <xsl:attribute name="id" select="concat('id_', generate-id())"/>
            <xsl:apply-templates select="@*, current-group()[self::PEN], current-group()[self::FILL]/@*"/>         
      </xsl:element>
    </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="."/>
    </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>

这篇关于如何处理包含一个或多个项目的序列的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆