【问题标题】:Perl: parse SVG filePerl:解析 SVG 文件
【发布时间】:2019-05-19 07:25:39
【问题描述】:

我想在 Perl 中解析一个 SVG 文件,但我看到建议不要使用某些库(XML::Simple、XML::XPath)出于不同的原因。下面的线程建议 XML::LibXML::XPathContext;

Perl XML/SVG Parser unable to findnodes

假设我使用 XML::LibXML::XPathContext,我仍然不确定如何提取我感兴趣的节点: 1) 那些“id”包含“Drawing...”、它们的大小(路径填充... d=".. 等)和文本(“tspan”) 2) 不属于任何“Drawing_”节点的“路径”节点(位于 SVG 底部)及其位置 (d="...)

use XML::LibXML;
use XML::LibXML::XPathContext;

my $doc = XML::LibXML->load_xml( location => $file);
my $xpc = XML::LibXML::XPathContext->new( $doc);
$xpc->registerNs(x => 'http://www.w3.org/2000/svg');

foreach my $drawing ($xpc->findnodes( ??? ) {
    print "Found drawing\n";
}

foreach my $path ($xpc->findnodes( ??? ) {
    print "Found path\n";
}

我的 SVG:

<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.2">
 <g visibility="visible" id="Master" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve">
  <rect fill="none" stroke="none" x="0" y="0" width="86360" height="55880"/>
 </g>
 <g visibility="visible" id="Page1">
  <g id="Drawing_1">
   <path fill="rgb(255,211,32)" stroke="none" d="M 15350,3285 L 31988,3285 31988,4937 15350,4937 15350,3285 15350,3285 Z"/>
   <path fill="none" stroke="rgb(128,128,128)" stroke-width="102" stroke-linejoin="round" d="M 15350,3285 L 31988,3285 31988,4937 15350,4937 15350,3285 15350,3285 Z"/>
   <g fill="rgb(0,0,0)" stroke="none" font-family="Arial Narrow embedded" font-size="635" font-style="normal" font-weight="700">
    <text x="19327" y="3967">
     <tspan x="19327 19471 19788 19962">Info</tspan></text>
    <text fill="rgb(0,0,0)" stroke="none" x="17558" y="4699">
     <tspan x="17558">I</tspan></text>
   </g>
  </g>
  <g id="Drawing_2">
   <path fill="rgb(207,231,245)" stroke="none" d="M 8747,10525 L 4810,10525 4810,8239 12684,8239 12684,10525 8747,10525 Z"/>
   <path fill="none" stroke="rgb(128,128,128)" stroke-width="102" stroke-linejoin="round" d="M 8747,10525 L 4810,10525 4810,8239 12684,8239 12684,10525 8747,10525 Z"/>
   <g fill="rgb(0,0,0)" stroke="none" font-family="Arial Narrow embedded" font-size="635" font-style="normal" font-weight="700">
    <text x="5547" y="8872">
     <tspan x="5547 6030">OK</tspan></text>
    <text fill="rgb(0,0,0)" stroke="none" x="5215" y="9604">
     <tspan x="5215 5359 5676 5850">Info</tspan></text>
   </g>
  </g>
  ...
  <g>
   <path fill="none" stroke="rgb(51,153,255)" id="Drawing_78_0" stroke-width="102" stroke-linejoin="round" d="M 47291,16367 C 47291,17129 48093,16793 48482,17017"/>
   <path fill="rgb(51,153,255)" stroke="none" id="Drawing_78_1" d="M 48688,17383 L 48598,16917 48337,17064 48688,17383 Z"/>
  </g>
  <g>
   <path fill="none" stroke="rgb(51,153,255)" id="Drawing_79_0" stroke-width="102" stroke-linejoin="round" d="M 39417,4937 C 39417,14271 23887,8230 23425,16977"/>
   <path fill="rgb(51,153,255)" stroke="none" id="Drawing_79_1" d="M 23415,17383 L 23577,16937 23277,16929 23415,17383 Z"/>
  </g>
  ...
 </g>
</svg>

【问题讨论】:

标签: xml perl svg


【解决方案1】:

首先,您不需要使用XML::LibXML::XPathContext,因为您的XML 没有使用命名空间。

但是,您必须遍历所有节点属性并检查它们。 一种方法是遍历节点属性,一旦找到您想要的节点,就可以对它们进行处理(如提取属性值、获取子节点等)使用XML::LibXML::Node中的方法

use v5.10;
use strict;
use warnings;

use XML::LibXML;

my $doc = XML::LibXML->load_xml( location => $ARGV[0] );

NODES: for my $node ($doc->findnodes('//g')) {
    for my $attr ($node->attributes) {
        if ($attr->nodeName eq 'id' && $attr->value =~ /^Drawing/) {
            # it's a drawing node
            # do stuff
            next NODES;
        }
    }
    # it's not a drawing node
    for my $pathnode ($node->findnodes('path')) {
        # do stuff
    }
}

您也可以使用纯 XPath 来查找节点。

my @drawings = $doc->findnodes('//g[starts-with(@id,"Drawing")]');
my @paths = $doc->findnodes('//path[not(ancestor::g[starts-with(@id,"Drawing")])]');

感谢这些帖子以供 XPath 参考:

XPath Select Nodes where all parent nodes do not contain specific attribute and value
XPath: using regex in contains function

【讨论】:

  • 谢谢,虽然我必须使用 XML::LibXML::XPathContext (如我帖子顶部的链接文章中所述),但我使用您的代码让它工作:即节点:对于我的 $node ($xpc->findnodes('//x:g')) { ...
  • @MrSparkly 但是您的文档不会像链接的问题那样混淆命名空间(这正是那里的问题)。您是否按原样尝试过,和/或我错过了什么?
  • @zdim 是的,我按原样尝试过。从字面上看,在原始 $doc->findnodes.. 行和 $xpc->findnodes... 行之间切换不会产生任何结果,或者产生我想要的结果。
  • @MrSparkly 嗯,它对我有用——当我添加打印(而不是评论 # do stuff)时,我得到了所有你想要的东西。即使只是打印 $attr 而不是第一个 # do stuff$pathnode 而不是第二个也可以显示一切。
  • @zdim 我刚刚又试了一次:原始代码对我不起作用。我在赢,也许它会有所作为。我试过单斜杠('/g'),双斜杠('//g'):什么都没有。
猜你喜欢
  • 2016-01-22
  • 2012-06-08
  • 1970-01-01
  • 2012-10-09
  • 1970-01-01
  • 2019-10-10
  • 2016-07-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多