otter 1.2.1

Otter game system; main data structures Rust crate.
Documentation
#!/usr/bin/perl -w
use strict;

use strict;

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

use Data::Dumper;

my $dom = XML::LibXML->load_xml(IO => \*STDIN) or die;
#print STDERR Dumper($dom);

my $xpc = XML::LibXML::XPathContext->new($dom) or die;

our ($label, $node);

sub p { print @_ or die $!; }

p "<!--made by dice/overlay-template-extractor-->\n";

sub process_node ($$) {
  my $fn;
  ($label, $fn) = @_;
  my $nodes = $xpc->findnodes('//*[@inkscape:label="'.$label.'"]');
  die "$label ?" unless @$nodes == 1;
  $node = $nodes->[0];
  $fn->($node);
  $node->toString. "\n";
}

our $nom_radius;

sub filter_element ($@) {
  my $exp_el = shift @_;
  my %keep_attr = map { $_ => 1 } (@_, qw(style));
  my $got_el = $node->nodeName; $got_el eq $exp_el or die "$label $got_el ?";
  my @attrs = $node->attributes();
  foreach my $attr (@attrs) {
    my $name = $attr->nodeName;
    next if $keep_attr{$name};
    $node->removeAttribute($name);
  }
  foreach my $desc_node ($node->getChildrenByTagName('desc')) {
    $node->removeChild($desc_node);
  }
}
sub filter_circle () {
  my @xy = qw(cx cy);
  foreach my $xy (@xy) {
    my $v = $node->getAttribute($xy);
    $v == '0' or die "$label $xy $v ?";
  }
  filter_element('circle', @xy);
  $node->setAttribute('r', "{{radius}}");
}

sub filter_text () {
  open ADJ, "src/SVG_FONT_Y_ADJUST_OF_FONT_SIZE.txt" or die $!;
  my $adj = <ADJ> // die $!;
  $adj =~ m/\d/ or die $!;

  die unless $node->getAttribute('x') eq '0';
  my $font_size = $node->getAttribute('font-size');
  die unless $font_size =~ s/px$//;
  my $got_y = $node->getAttribute('y');
  my $exp_y = $adj * $font_size;
  die "$got_y != $exp_y" unless $got_y == $exp_y;

  # the attributes will be put back by Rust code,
  # in TextOptions::start_element
  filter_element('text', qw(font-family));
  $node->removeChildNodes();
  $node->appendText('{{ label_text }}');
  $node->setNodeName('svg:text_tm3vljl79wqouvre');
}
sub process_text_node() {
  $_ = process_node('textlabel', \&filter_text);
  # This <text> node also acts as the sentinel for when the JS
  # wants to remove the cooldown timer.
  s/text_tm3vljl79wqouvre/{{ label_start_element }}/;
  s/text_tm3vljl79wqouvre/text/;
  die "$_ ?" if m/text_tm3vljl79wqouvre/;
  $_;
}
p process_text_node();

p "{% if cooldown_active %}\n";
p process_node('timeblack', sub {
  $nom_radius = $node->getAttribute('r');
  filter_circle();
});
p process_node('timewhite', \&filter_circle);
p process_node('timegrey', sub {
  filter_element('path');
  $node->setAttribute('d', "{{path_d}}");
  $node->setAttribute('id', "{{cd_elid}}");
  foreach my $d (qw(remprop total_ms radius)) {
    $node->setAttribute("data-$d", "{{$d}}");
  }
});
p "{% endif %}\n";