通过将元素列入白名单来清理SVG文档

时间:2011-02-02 23:56:32

标签: xml xslt svg xml-parsing

我想从一些SVG文档中提取大约20种元素类型以形成新的SVG。 rectcirclepolygontextpolyline,基本上是一组视觉部分在白名单中。 需要使用JavaScript,评论,动画和外部链接。

我想到了三种方法:

  1. 正则表达式:我完全熟悉,并且宁愿不去那里。
  2. PHP DOM:也许一年前使用过一次。
  3. XSLT:刚刚看了我的第一眼。

如果XSLT是正确的工作工具,我需要什么xsl:stylesheet? 否则,你会使用哪种方法?

示例输入:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" version="1.1" width="512" height="512" id="svg2">
<title>Mostly harmless</title>
  <metadata id="metadata7">Some metadata</metadata>

<script type="text/ecmascript">
<![CDATA[
alert('Hax!');
]]>
</script>
<style type="text/css">
<![CDATA[ svg{display:none} ]]>
</style>

  <defs id="defs4">
    <circle id="my_circle" cx="100" cy="50" r="40" fill="red"/> 
  </defs>

  <g id="layer1">
  <a xlink:href="www.hax.ru">
    <use xlink:href="#my_circle" x="20" y="20"/>
    <use xlink:href="#my_circle" x="100" y="50"/>
  </a>
  </g>
  <text>
    <tspan>It was the best of times</tspan>
    <tspan dx="-140" dy="15">It was the worst of times.</tspan>
  </text>
</svg>

示例输出。显示完全相同的图像:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512">
  <defs>
    <circle id="my_circle" cx="100" cy="50" r="40" fill="red"/> 
  </defs>
  <g id="layer1">
    <use xlink:href="#my_circle" x="20" y="20"/>
    <use xlink:href="#my_circle" x="100" y="50"/>
  </g>
  <text>
    <tspan>It was the best of times</tspan>
    <tspan dx="-140" dy="15">It was the worst of times.</tspan>
  </text>
</svg>

守护者元素的近似列表是:g, rect, circle, ellipse, line, polyline, polygon, path, text, tspan, tref, textpath, linearGradient+stop, radialGradient, defs, clippath, path

如果没有专门的SVG微小,那么肯定是SVG lite。

4 个答案:

答案 0 :(得分:4)

此转化

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:s="http://www.w3.org/2000/svg"
 >
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="*">
  <xsl:element name="{name()}" namespace="{namespace-uri()}">
   <xsl:copy-of select="namespace::xlink"/>

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

 <xsl:template match="@*">
  <xsl:attribute name="{name()}"
                 namespace="{namespace-uri()}">
   <xsl:value-of select="."/>
  </xsl:attribute>
 </xsl:template>

 <xsl:template match="s:a">
  <xsl:apply-templates/>
 </xsl:template>

 <xsl:template match=
 "s:title|s:metadata|s:script|s:style|
  s:svg/@version|s:svg/@id"/>
</xsl:stylesheet>

应用于提供的XML文档

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/"
     xmlns:cc="http://creativecommons.org/ns#"
     xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
     xmlns:svg="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink"
     xmlns="http://www.w3.org/2000/svg" version="1.1"
     width="512" height="512" id="svg2">
    <title>Mostly harmless</title>
    <metadata id="metadata7">Some metadata</metadata>
    <script type="text/ecmascript"><![CDATA[ alert('Hax!'); ]]></script>
    <style type="text/css"><![CDATA[ svg{display:none} ]]></style>
    <defs id="defs4">
        <circle id="my_circle" cx="100" cy="50" r="40" fill="red"/>
    </defs>
    <g id="layer1">
        <a xlink:href="www.hax.ru">
            <use xlink:href="#my_circle" x="20" y="20"/>
            <use xlink:href="#my_circle" x="100" y="50"/>
        </a>
    </g>
    <text>
        <tspan>It was the best of times</tspan>
        <tspan dx="-140" dy="15">It was the worst of times.</tspan>
    </text>
</svg>

生成想要的正确结果

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512">
   <defs id="defs4">
      <circle id="my_circle" cx="100" cy="50" r="40" fill="red"/>
   </defs>
   <g id="layer1">
      <use xlink:href="#my_circle" x="20" y="20"/>
      <use xlink:href="#my_circle" x="100" y="50"/>
   </g>
   <text>
      <tspan>It was the best of times</tspan>
      <tspan dx="-140" dy="15">It was the worst of times.</tspan>
   </text>
</svg>

<强>解释

  1. 两个模板具有类似于身份规则的组合效果,匹配所有“白名单”并基本上复制它们(仅消除不需要的命名空间节点)。

  2. 没有正文的模板会匹配所有“黑名单”节点(元素和一些属性)。这些都被有效删除。

  3. 必须有与特定“灰色列表”节点匹配的模板(在我们的案例中匹配s:a的模板)。 “灰色列出的节点不会被完全删除 - 它可能会被重命名或以其他方式修改,或者至少其内容可能仍包含在输出中。

  4. 很可能随着您对问题的理解变得越来越清晰,三个列表将不断增长,因此黑名单删除模板的匹配模式将被修改容纳新发现的黑名单元素。新发现的白名单节点根本不需要工作。只处理新的灰色列出的元素(如果发现这些元素)将需要更多的工作。

答案 1 :(得分:2)

Dimitre Novatchev的解决方案更“干净”和优雅,但如果您需要“白名单”解决方案(因为您无法预测用户可能输入的内容,您需要“黑名单”),那么您需要完全充实“白名单”。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
    xmlns:svg="http://www.w3.org/2000/svg">
    <xsl:output indent="yes" />

    <!--The "whitelist" template that will copy matched nodes forward and apply-templates
        for any attributes or child nodes -->
    <xsl:template match="svg:svg 
        | svg:defs  | svg:defs/text()
        | svg:g  | svg:g/text()
        | svg:a  | svg:a/text()
        | svg:use   | svg:use/text()
        | svg:rect  | svg:rect/text()
        | svg:circle  | svg:circle/text()
        | svg:ellipse  | svg:ellipse/text()
        | svg:line  | svg:line/text()
        | svg:polyline  | svg:polyline/text()
        | svg:polygon  | svg:polygon/text()
        | svg:path  | svg:path/text()
        | svg:text  | svg:text/text()
        | svg:tspan  | svg:tspan/text()
        | svg:tref  | svg:tref/text()
        | svg:textpath  | svg:textpath/text()
        | svg:linearGradient  | svg:linearGradient/text()
        | svg:radialGradient  | svg:radialGradient/text()
        | svg:clippath  | svg:clippath/text()
        | svg:text | svg:text/text()">
        <xsl:copy>
            <xsl:copy-of select="@*" />
            <xsl:apply-templates select="node()" />
        </xsl:copy>
    </xsl:template>

    <!--The "blacklist" template, which does nothing except apply templates for the 
        matched node's attributes and child nodes -->
    <xsl:template match="@* | node()">
        <xsl:apply-templates select="@* | node()" />
    </xsl:template>

</xsl:stylesheet>

答案 2 :(得分:1)

svgfig是这项工作的好工具。您可以加载SVG文件并选择您喜欢的部分来制作新文档。或者您可以删除喜欢的部分并重新保存。

答案 3 :(得分:0)

作为已接受的XSLT答案的替代方案,您可以使用RubyNokogiri

require 'nokogiri'
svg = Nokogiri::XML( IO.read( "myfile.svg" ) )
svg.xpath( '//*[not(name()="rect" or name()="circle" or ...)]' ).each do |node|
  node.remove
end
File.open( "myfile_clean.svg", "w" ) do |file|
  file << svg.to_xml
end