1

我目前正在尝试创建一个使用 LibXML 处理 SVG 字体数据的 perl 脚本。

在 SVG 字体中,每个字符都被定义为具有 unicode 属性的字形元素,该属性以 unicode 实体的形式定义其 unicode 地址;像这样:

<glyph unicode="&#x2000;" />

我想做的部分事情是获取每个字形元素的 unicode 属性的值,然后像字符串一样处理它。但是,当我使用 Element->getAttribute('unicode'); 针对字形节点,它返回一个显示为占位符矩形的“宽字符”,这让我相信它将 unicode 实体扩展为一个 unicode 字符并返回它。

当我创建我的解析器时,我将 expand_entities 设置为 0,所以我不确定我还能做些什么来防止这种情况。我对 XML 处理相当陌生,所以我不确定我是否真的了解正在发生的事情,或者这是否应该是可以预防的。

这是一个代码示例:

use utf8;
use open ':std', ':encoding(UTF-8)';
use strict;
use warnings;
use XML::LibXML;
$XML::LibXML::skipXMLDeclaration = 1;

my $xmlFile = $ARGV[0];

my $parser = XML::LibXML->new();
$parser->load_ext_dtd(0);
$parser->validation(0);
$parser->no_network(1);
$parser->recover(1);
$parser->expand_entities(0);

my $xmlDom = $parser->load_xml(location => $xmlFile);

my $xmlDomSvg = XML::LibXML::XPathContext->new();
$xmlDomSvg->registerNs('svg', 'http://www.w3.org/2000/svg');

foreach my $myGlyph ($xmlDomSvg->findnodes('/svg:svg/svg:defs/svg:font/svg:glyph', $xmlDom))
{
  my $myGlyphCode = $myGlyph->getAttribute('unicode');
  print $myGlyphCode . "\n";
}

注意:如果我运行 print $myGlyph->toString();,输出中的 unicode 实体不会被扩展,因此我得出结论,扩展发生在 getAttribute 方法中。

4

2 回答 2

2

这可能不是您正在寻找的答案,但恕我直言getAttribute,为您提供了足够的信息,即 Perl 字符串,以另一种方式解决您的问题。您正在尝试将该 Perl 字符串写入非 UTF8 文件,这就是您收到“宽字符”警告的原因。

U+xxxx如何获得您正在寻找的价值的精简示例:

use strict;
use warnings;
use open qw(:encoding(UTF-8) :std);

use XML::LibXML;

my $dom = XML::LibXML->load_xml(IO => \*DATA)
    or die "XML\n";
my $root = $dom->documentElement();
print $root->toString(), "\n";

my $attr = $root->getAttribute('unicode');
printf("'%s' is %d (U+%04X)\n", $attr, ord($attr), ord($attr));

exit 0;

__DATA__
<glyph unicode="&#x2000;" />

测试运行:

$ perl dummy.pl
<glyph unicode="&#x2000;"/>
' ' is 8192 (U+2000)

更新:恕我直言,文档expand_entities具有误导性。它谈到了“实体”,但它显然意味着ENTITY定义,即文档中引入的新实体。不幸的是, libxml2 文档不是很清楚。但是这条旧消息似乎表明您描述的行为是预期的,即。XML 解析器应该总是替换预定义的实体:

#!/usr/bin/perl
use warnings;
use strict;

use XML::LibXML;

my $parser = XML::LibXML->new({
    expand_entities => $ARGV[0] ? 1 : 0,
});

my $dom = $parser->load_xml(IO => \*DATA)
    or die "XML\n";

my $root = $dom->documentElement();
print "toString():  ", $root->toString(), "\n";
print "textContent: ", $root->textContent(), "\n";

my $attr = $root->getAttribute('test');
print "attribute:   ${attr}\n";

exit 0;

__DATA__
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY author "Fluffy Bunny">
]>
<tag test="&lt;&author;&gt;">&lt;&author;&gt;</tag>

测试运行:

$ perl dummy.pl 0
toString():  <tag test="&lt;&author;&gt;">&lt;&author;&gt;</tag>
textContent: <Fluffy Bunny>
attribute:   <Fluffy Bunny>

$ perl dummy.pl 1
toString():  <tag test="&lt;Fluffy Bunny&gt;">&lt;Fluffy Bunny&gt;</tag>
textContent: <Fluffy Bunny>
attribute:   <Fluffy Bunny>
于 2019-02-27T19:38:30.010 回答
1

serializeContent()方法可能会执行您想要的操作:

my $xml = '<doc>
  <glyph unicode="&#x2000;" />
</doc>';

my $dom = XML::LibXML->load_xml(
    string          => $xml,
    expand_entities => 0,
    no_network      => 1,
);

my($attr) = $dom->findnodes('//glyph[1]/@unicode');

say $attr->serializeContent();

哪个输出:

&#x2000;

我怀疑,该expand_entities选项不适用于数字字符实体。文档不清楚,我还没有查看源代码。

在更常见的情况下,您确实希望扩展所有实体并且只想要这些实体所代表的实际字符,您甚至不需要调用getAttribute(). 每个节点对象都使用一个绑定的哈希接口,所以你可以这样做:

my $text = $glyph->{unicode};
于 2019-02-27T21:23:43.483 回答