Используйте perl LibXML Element->getAttribute() без расширения юникодных сущностей в значении

В настоящее время я пытаюсь создать Perl-скрипт, который использует LibXML для обработки данных шрифтом SVG.

В SVG-шрифте каждый символ определяется как глиф-элемент с атрибутом Unicode, который определяет его адрес Unicode в форме объекта Unicode; вот так:

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

Часть желания, которую я хочу сделать, это взять значение атрибута unicode каждого элемента глифа и обработать его как строку. Однако когда я использую Element->getAttribute('unicode'); против узла глифа он возвращает "широкий символ", который отображается в виде прямоугольника-заполнителя, что заставляет меня поверить, что он расширяет сущность юникода в символ юникода и возвращает его.

Когда я создаю свой синтаксический анализатор, я устанавливаю 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.

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>

Метод 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};
Другие вопросы по тегам