The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!perl -T
use strict;
use warnings;

use Test::More tests => 74;

use SVG;
use Test::Exception;
use SVG::Rasterize;

sub set_property {
    my $rasterize;
    my $svg;
    my $hook;
    my @expected;

    $rasterize = SVG::Rasterize->new;
    $svg       = SVG->new(width => 400, height => 300);
    $svg->firstChild->attrib('stroke-width' => '9pt');
    is($svg->firstChild->attrib('stroke-width'), '9pt', 'check attrib');
    $hook      = sub {
	my ($rasterize, %state_args) = @_;
	if($state_args{node_name} eq 'svg') {
	    is($state_args{node_attributes}->{'stroke-width'}, '9pt',
	       'xsl stroke-width');
	}
	return %state_args;
    };
    $rasterize->before_node_hook($hook);
    $rasterize->rasterize(svg => $svg);

    $rasterize = SVG::Rasterize->new;
    $svg       = SVG->new(width => 400, height => 300);
    $svg->firstChild->attrib('stroke-width' => '9pt');
    $svg->firstChild->attrib('id'           => 'svg');
    $svg->group(id => 'g01');
    is($svg->firstChild->attrib('stroke-width'), '9pt', 'check attrib');
    is($svg->firstChild->attrib('id'), 'svg', 'check attrib');
    $hook = sub {
	my ($rasterize, %state_args) = @_;
	if($state_args{node_attributes}->{id} eq 'svg') {
	    is($state_args{node_attributes}->{'stroke-width'}, '9pt',
	       'xsl stroke-width');
	}
	if($state_args{node_attributes}->{id} eq 'g01') {
	    ok(!defined($state_args{node_attributes}->{'stroke-width'}),
	       'xsl stroke-width not on group');
	}
	return %state_args;
    };
    $rasterize->before_node_hook($hook);
    $hook = sub {
	my ($render, $state) = @_;
	if($state->node_attributes->{id} eq 'svg') {
	    is($state->properties->{'stroke-width'}, 11.25,
	       'property stroke-width on svg');
	}	
	if($state->node_attributes->{id} eq 'g01') {
	    is($state->properties->{'stroke-width'}, 11.25,
	       'property stroke-width on g01');
	}	
    };
    $rasterize->start_node_hook($hook);
    $rasterize->rasterize(svg => $svg);

    $svg->group(id => 'g02', 'stroke-width' => '10px');
    @expected = ('svg', 'g01', 'g02');
    $hook = sub {
	my ($render, $state) = @_;
	is($state->node_attributes->{id}, shift(@expected),
	   'expected id');
	if($state->node_attributes->{id} eq 'svg') {
	    is($state->properties->{'stroke-width'}, 11.25,
	       'property stroke-width on svg');
	}	
	if($state->node_attributes->{id} eq 'g01') {
	    is($state->properties->{'stroke-width'}, 11.25,
	       'property stroke-width on g01');
	}	
	if($state->node_attributes->{id} eq 'g02') {
	    is($state->properties->{'stroke-width'}, 10,
	       'property stroke-width on g02');
	}	
    };
    $rasterize->start_node_hook($hook);
    $rasterize->rasterize(svg => $svg);

    $svg->group(id => 'g03', 'stroke-width' => '10px',
		style => 'stroke-width:1in');
    @expected = ('svg', 'g01', 'g02', 'g03');
    $hook = sub {
	my ($render, $state) = @_;
	is($state->node_attributes->{id}, shift(@expected),
	   'expected id');
	if($state->node_attributes->{id} eq 'svg') {
	    is($state->properties->{'stroke-width'}, 11.25,
	       'property stroke-width on svg');
	}	
	if($state->node_attributes->{id} eq 'g01') {
	    is($state->properties->{'stroke-width'}, 11.25,
	       'property stroke-width on g01');
	}	
	if($state->node_attributes->{id} eq 'g02') {
	    is($state->properties->{'stroke-width'}, 10,
	       'property stroke-width on g02');
	}	
	if($state->node_attributes->{id} eq 'g03') {
	    is($state->properties->{'stroke-width'}, 90,
	       'property stroke-width on g03');
	}
    };
    $rasterize->start_node_hook($hook);
    $rasterize->rasterize(svg => $svg);
}

sub inherit {
    my $rasterize;
    my $svg;
    my $hook;
    my @expected;

    $rasterize = SVG::Rasterize->new;
    $svg       = SVG->new(width => 400, height => 300);
    $svg->firstChild->attrib('stroke-width' => '9pt');
    $svg->firstChild->attrib('id'           => 'svg');
    $svg->firstChild->attrib('stroke'       => 'black');
    $svg->group(id             => 'g01',
		'stroke'       => 'none',
		'stroke-width' => ' inherit');
    $hook = sub {
	my ($rasterize, %state_args) = @_;
	if($state_args{node_attributes}->{id} eq 'svg') {
	    is($state_args{node_attributes}->{'stroke-width'}, '9pt',
	       'xsl stroke-width');
	}
	if($state_args{node_attributes}->{id} eq 'g01') {
	    is($state_args{node_attributes}->{'stroke-width'}, 'inherit',
	       'xsl stroke-width explicit inherit');
	    is($state_args{node_attributes}->{'stroke'}, 'none',
	       'xsl stroke explicit none');
	}
	return %state_args;
    };
    $rasterize->before_node_hook($hook);
    $hook = sub {
	my ($render, $state) = @_;
	if($state->node_attributes->{id} eq 'svg') {
	    is($state->properties->{'stroke-width'}, 11.25,
	       'property stroke-width on svg');
	}
	if($state->node_attributes->{id} eq 'g01') {
	    is($state->properties->{'stroke-width'}, 11.25,
	       'property stroke-width on g01');
	    ok(!defined($state->properties->{'stroke'}),
	       'property stroke undefined on g01');
	}
    };
    $rasterize->start_node_hook($hook);
    $rasterize->rasterize(svg => $svg);
}

sub color {
    my $rasterize;
    my $svg;
    my $node;
    my @expected;

    $rasterize = SVG::Rasterize->new;
    $svg       = SVG->new(width => 400, height => 300);
    $svg->firstChild->attrib('id' => 'svg');
    $svg->group(id => 'g01');
    $svg->group(id => 'g02', 'stroke' => 'rgb(20, 255, 1)');
    $svg->group(id => 'g03', 'stroke' => 'rgb(13%, -10%, 120%)',
		style => 'stroke-width:1in');
    @expected = ('svg', 'g01', 'g02', 'g03');
    $rasterize->start_node_hook(sub {
	my ($render, $state) = @_;
	is($state->node_attributes->{id}, shift(@expected),
	   'expected id');
	if($state->node_attributes->{id} eq 'g01') {
	    ok(!defined($state->properties->{'stroke'}),
	       'property stroke is undef on g01');
	}
	if($state->node_attributes->{id} eq 'g02') {
	    is_deeply($state->properties->{'stroke'}, [20, 255, 1],
	       'property stroke on g02');
	}
	if($state->node_attributes->{id} eq 'g03') {
	    is_deeply($state->properties->{'stroke'}, [33, -25, 306],
	       'property stroke on g03');
	    is($state->properties->{'stroke-width'}, 90,
	       'property stroke-width 90 g03');
	}
    });
    $rasterize->rasterize(svg => $svg);

    $rasterize = SVG::Rasterize->new;
    $svg       = SVG->new(width => 400, height => 300);
    $svg->firstChild->attrib('id' => 'svg');
    $svg->group(id => 'g01', 'style' => 'stroke:');
    throws_ok(sub { $rasterize->rasterize(svg => $svg) },
	      qr/process the css property string \'stroke\:\' correctly/,
	      'Error message invalid css property');

    $rasterize = SVG::Rasterize->new;
    $svg       = SVG->new(width => 400, height => 300);
    $svg->firstChild->attrib('id' => 'svg');
    $svg->group(id => 'g01', fill => '#FF00FF');
    $svg->group(id => 'g02', color => '#F0F', fill => 'currentColor');
    $node = $svg->group(id => 'g03', color => 'red');
    $node = $node->group(id => 'g04', fill => 'currentColor');
    $node->group(id => 'g05', color => 'blue');
    $svg->group(id => 'g06', fill => '#010 icc-color(foo, 1, 0.1)');
    $svg->group(id => 'g07', fill => 'rgb(20%, 40%, 60%)');
    $rasterize->start_node_hook(sub {
	my ($rasterize, $state) = @_;
	if($state->node_attributes->{id} eq 'g01') {
	    is_deeply($state->properties->{'fill'}, [255, 0, 255],
	       'property fill on g01');
	}
	if($state->node_attributes->{id} eq 'g02') {
	    is_deeply($state->properties->{'fill'}, [255, 0, 255],
	       'property fill on g02');
	}
	if($state->node_attributes->{id} eq 'g04') {
	    is_deeply($state->properties->{'fill'}, [255, 0, 0],
	       'property fill on g04');
	}
	# not entirely sure, if g05 should inherit the fill color
	# or inherit that the fill color is currentColor
	if($state->node_attributes->{id} eq 'g05') {
	    is_deeply($state->properties->{'fill'}, [255, 0, 0],
	       'property fill on g05, not entirely sure about this');
	}
	if($state->node_attributes->{id} eq 'g06') {
	    is_deeply($state->properties->{'fill'}, [0, 17, 0],
	       'property fill on g06');
	}
	if($state->node_attributes->{id} eq 'g07') {
	    is_deeply($state->properties->{'fill'}, [51, 102, 153],
	       'property fill on g07');
	}
    });
    {
	local $SIG{__WARN__} = sub {};
	$rasterize->rasterize(svg => $svg);
    }

    $rasterize = SVG::Rasterize->new;
    $svg       = SVG->new(width => 400, height => 300);
    $svg->firstChild->attrib('id' => 'svg');
    $svg->group(id => 'g01', stroke => 'rgb(10, 10%, 10)');
    throws_ok(sub { $rasterize->rasterize(svg => $svg) },
	      qr/Invalid color specification/,
	      'Error message invalid color specification');

    $rasterize = SVG::Rasterize->new;
    $svg       = SVG->new(width => 400, height => 300);
    $svg->firstChild->attrib('id' => 'svg');
    $svg->group(id => 'g01', stroke => 'rgb(10%, 10, 10)');
    throws_ok(sub { $rasterize->rasterize(svg => $svg) },
	      qr/Invalid color specification/,
	      'Error message invalid color specification');

    # external current color
    $rasterize = SVG::Rasterize->new(current_color => 'blue');
    $svg       = SVG->new(width => 400, height => 300);
    $svg->firstChild->attrib('id' => 'svg');
    $svg->group(id => 'g01', fill => 'currentColor');
    $rasterize->start_node_hook(sub {
	my ($rasterize, $state) = @_;
	if($state->node_attributes->{id} eq 'g01') {
	    is_deeply($state->properties->{'fill'}, [0, 0, 255],
	       'property fill on g01 by external current color');
	}
    });
    $rasterize->rasterize(svg => $svg);

    # external current color
    $rasterize = SVG::Rasterize->new(current_color => 'blue');
    $svg       = SVG->new(width => 400, height => 300);
    $svg->firstChild->attrib('id' => 'svg');
    $node = $svg->group(id => 'g01', fill => 'currentColor');
    $node->group(id => 'g02', fill => 'red');
    $rasterize->start_node_hook(sub {
	my ($rasterize, $state) = @_;
	if($state->node_attributes->{id} eq 'g01') {
	    is_deeply($state->properties->{'fill'}, [7, 8, 13],
	       'property fill on g01 by external current color');
	}
	if($state->node_attributes->{id} eq 'g02') {
	    is_deeply($state->properties->{'fill'}, [255, 0, 0],
	       'property fill on g02 overriding current color');
	}
    });
    $rasterize->rasterize(svg => $svg, current_color => 'rgb(7, 8, 13)');
}

sub whitespace {
    my $rasterize;
    my $svg;
    my $hook;
    my @expected;

    $rasterize = SVG::Rasterize->new;
    $svg       = SVG->new(width => 400, height => 300);
    $svg->firstChild->attrib('id' => 'svg');
    $svg->group(id => 'g01', 'stroke' => "\trgb(20, 255, 1) ");
    @expected = ('svg', 'g01');
    $hook = sub {
	my ($render, $state) = @_;
	is($state->node_attributes->{id}, shift(@expected),
	   'expected id');
	if($state->node_attributes->{id} eq 'g01') {
	    is_deeply($state->properties->{'stroke'}, [20, 255, 1],
	       'property stroke on g01');
	}
    };
    $rasterize->start_node_hook($hook);
    $rasterize->rasterize(svg => $svg);

    $rasterize->normalize_attributes(0);
    @expected = ('svg', 'g01');
    $hook = sub {
	my ($render, $state) = @_;
	is($state->node_attributes->{id}, shift(@expected),
	   'expected id');
	if($state->node_attributes->{id} eq 'g01') {
	    is_deeply($state->properties->{'stroke'}, [20, 255, 1],
	       'property stroke on g01 with attribute normalization');
	}
    };
    $rasterize->start_node_hook($hook);
    throws_ok(sub { $rasterize->rasterize(svg => $svg) },
	      qr/stroke/,
	      'without attribute normalization');
}

sub dasharray {
    my $rasterize;
    my $svg;
    my $hook;
    my @expected;

    $rasterize = SVG::Rasterize->new;
    $svg       = SVG->new(width => 400, height => 300);
    $svg->firstChild->attrib('id' => 'svg');
    $svg->group(id => 'g01', 'stroke-dasharray' => '3,4');
    @expected = ('svg', 'g01');
    $hook = sub {
	my ($render, $state) = @_;
	is($state->node_attributes->{id}, shift(@expected),
	   'expected id');
	if($state->node_attributes->{id} eq 'g01') {
	    is_deeply($state->properties->{'stroke-dasharray'},
		      [3, 4],
		      'property stroke on g01');
	}
    };
    $rasterize->start_node_hook($hook);
    $rasterize->rasterize(svg => $svg);

    $rasterize = SVG::Rasterize->new;
    $svg       = SVG->new(width => 400, height => 300);
    $svg->firstChild->attrib('id' => 'svg');
    $svg->group(id => 'g01', 'stroke-dasharray' => '1in, 40px');
    @expected = ('svg', 'g01');
    $hook = sub {
	my ($render, $state) = @_;
	is($state->node_attributes->{id}, shift(@expected),
	   'expected id');
	if($state->node_attributes->{id} eq 'g01') {
	    is_deeply($state->properties->{'stroke-dasharray'},
		      [90, 40],
		      'property stroke on g01');
	}
    };
    $rasterize->start_node_hook($hook);
    $rasterize->rasterize(svg => $svg);

    $rasterize = SVG::Rasterize->new;
    $svg       = SVG->new(width => 400, height => 300);
    $svg->firstChild->attrib('id' => 'svg');
    $svg->group(id => 'g01', 'stroke-dasharray' => '1in,40px, 12');
    @expected = ('svg', 'g01');
    $hook = sub {
	my ($render, $state) = @_;
	is($state->node_attributes->{id}, shift(@expected),
	   'expected id');
	if($state->node_attributes->{id} eq 'g01') {
	    is_deeply($state->properties->{'stroke-dasharray'},
		      [90, 40, 12, 90, 40, 12],
		      'property stroke on g01');
	}
    };
    $rasterize->start_node_hook($hook);
    $rasterize->rasterize(svg => $svg);

    $rasterize = SVG::Rasterize->new;
    $svg       = SVG->new(width => 400, height => 300);
    $svg->firstChild->attrib('id' => 'svg');
    $svg->group(id => 'g01', 'stroke-dasharray' => '1in,-40px');
    throws_ok(sub { $rasterize->rasterize(svg => $svg) },
	      qr/Negative value \(\-40\) in stroke\-dasharray/,
	      'negative value in dasharray');
}

sub style_as_hash {
    my $rasterize;
    my $svg;
    my $hook;
    my @expected;

    $rasterize = SVG::Rasterize->new;
    $svg       = SVG->new(width => 400, height => 300);
    $svg->firstChild->attrib('id' => 'svg');
    $svg->group(id => 'g01', style => {'stroke-width' => '1in'});
    @expected = ('svg', 'g01');
    $hook = sub {
	my ($rasterize, $state) = @_;
	is($state->node_attributes->{id}, shift(@expected),
	   'expected id');
	if($state->node_attributes->{id} eq 'g01') {
	    is($state->properties->{'stroke-width'}, 90,
	       'property stroke-width 90 g01');
	}
    };
    $rasterize->start_node_hook($hook);
    $rasterize->rasterize(svg => $svg);

    $rasterize = SVG::Rasterize->new;
    $svg       = SVG->new(width => 400, height => 300);
    $svg->firstChild->attrib('id' => 'svg');
    $svg->group(id    => 'g01',
		style => {'stroke-width' => '1in',
			  'stroke'       => 'black'});
    @expected = ('svg', 'g01');
    $hook = sub {
	my ($rasterize, $state) = @_;
	is($state->node_attributes->{id}, shift(@expected),
	   'expected id');
	if($state->node_attributes->{id} eq 'g01') {
	    is($state->properties->{'stroke-width'}, 90,
	       'property stroke-width 90 g01');
	    is_deeply($state->properties->{'stroke'},
		      [0, 0, 0],
		      'property stroke processed black g01');
	}
    };
    $rasterize->start_node_hook($hook);
    $rasterize->rasterize(svg => $svg);
}

set_property;
inherit;
color;
whitespace;
dasharray;
style_as_hash;