#!/usr/bin/perl

# Copyright 2011 Andrey Bayrak.
# This script is a part of SVG Cleaner.
# SVG Cleaner is licensed under the GNU General Public License, Version 3.
# The GNU General Public License is a free, copyleft license for software and other kinds of works.
# http://www.gnu.org/copyleft/gpl.html

use strict;
use warnings;

use XML::Twig;
use Term::ANSIColor;

my %args = map { split("=",$_) } split(":",$ARGV[1]);
my @defs_elts = (
  'altGlyphDef',
  'clipPath',
  'cursor',
  'filter',
  'linearGradient',
  'marker',
  'mask',
  'pattern',
  'radialGradient',
  'symbol');
my %default_atts = (
  'baseline-shift' => 'baseline',
  'clip-path' => 'none',
  'clipPathUnits' => 'userSpaceOnUse',
  'clip-rule' => 'nonzero',
  'color' => '#000000',
  'color-interpolation-filters' => 'linearRGB',
  'color-interpolation' => 'sRGB',
  'direction' => 'ltr',
  'display' => 'inline',
  'enable-background' => 'accumulate',
  'fill' => '#000000',
  'fill-opacity' => '1',
  'fill-rule' => 'nonzero',
  'filter' => 'none',
  'flood-color' => '#000000',
  'flood-opacity' => '1',
  'font-size-adjust' => 'none',
  'font-size' => 'medium',
  'font-stretch' => 'normal',
  'font-style' => 'normal',
  'font-variant' => 'normal',
  'font-weight' => 'normal',
  'glyph-orientation-horizontal' => '0deg',
  'letter-spacing' => 'normal',
  'lighting-color' => '#ffffff',
  'marker' => 'none',
  'marker-start' => 'none',
  'marker-mid' => 'none',
  'marker-end' => 'none',
  'mask' => 'none',
  'opacity' => '1',
  'overflow' => 'visible',
  'pointer-events' => 'visiblePainted',
  'stop-color' => '#000000',
  'stop-opacity' => '1',
  'stroke' => 'none',
  'stroke-dasharray' => 'none',
  'stroke-dashoffset' => '0',
  'stroke-linecap' => 'butt',
  'stroke-linejoin' => 'miter',
  'stroke-miterlimit' => '4',
  'stroke-opacity' => '1',
  'stroke-width' => '1',
  'text-anchor' => 'start',
  'text-decoration' => 'none',
  'unicode-bidi' => 'normal',
  'visibility' => 'visible',
  'word-spacing' => 'normal',
  'writing-mode' => 'lr-tb',
  'audio-level' => '1',
  'solid-color' => '#000000',
  'solid-opacity' => '1',
  'text-align' => 'start',
  'vector-effect' => 'none',
  'viewport-fill' => 'none',
  'viewport-fill-opacity' => '1');
my @adobe_ns = (
  'http://ns.adobe.com/AdobeIllustrator/10.0/',
  'http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/',
  'http://ns.adobe.com/Extensibility/1.0/',
  'http://ns.adobe.com/Flows/1.0/',
  'http://ns.adobe.com/Graphs/1.0/',
  'http://ns.adobe.com/GenericCustomNamespace/1.0/',
  'http://ns.adobe.com/ImageReplacement/1.0/',
  'http://ns.adobe.com/SaveForWeb/1.0/',
  'http://ns.adobe.com/Variables/1.0/',
  'http://ns.adobe.com/XPath/1.0/',
  'http://ns.adobe.com/xap/1.0/sType/ResourceRef#',
  'adobe:ns:meta/',
  'http://ns.adobe.com/xap/1.0/',
  'http://ns.adobe.com/xap/1.0/g/img/',
  'http://ns.adobe.com/xap/1.0/mm/');
my @text_atts = (
  'alignment-baseline',
  'baseline-shift',
  'block-progression',
  'direction',
  'dominant-baseline',
  'dx',
  'dy',
  'font-family',
  'font-size',
  'font-size-adjust',
  'font-stretch',
  'font-style',
  'font-variant',
  'font-weight',
  'glyph-orientation-horizontal',
  'glyph-orientation-vertical',
  'kerning',
  'lengthAdjust',
  'letter-spacing',
  'line-height',
  'rotate',
  'text-anchor',
  'text-decoration',
  'text-indent',
  'textLength',
  'text-rendering',
  'text-transform',
  'unicode-bidi',
  'word-spacing',
  'writing-mode');
my %colors = (
  'aliceblue' => '#f0f8ff',
  'antiquewhite' => '#faebd7',
  'aqua' => '#00ffff',
  'aquamarine' => '#7fffd4',
  'azure' => '#f0ffff',
  'beige' => '#f5f5dc',
  'bisque' => '#ffe4c4',
  'black' => '#000000',
  'blanchedalmond' => '#ffebcd',
  'blue' => '#0000ff',
  'blueviolet' => '#8a2be2',
  'brown' => '#a52a2a',
  'burlywood' => '#deb887',
  'cadetblue' => '#5f9ea0',
  'chartreuse' => '#7fff00',
  'chocolate' => '#d2691e',
  'coral' => '#ff7f50',
  'cornflowerblue' => '#6495ed',
  'cornsilk' => '#fff8dc',
  'crimson' => '#dc143c',
  'cyan' => '#00ffff',
  'darkblue' => '#00008b',
  'darkcyan' => '#008b8b',
  'darkgoldenrod' => '#b8860b',
  'darkgray' => '#a9a9a9',
  'darkgreen' => '#006400',
  'darkkhaki' => '#bdb76b',
  'darkmagenta' => '#8b008b',
  'darkolivegreen' => '#556b2f',
  'darkorange' => '#ff8c00',
  'darkorchid' => '#9932cc',
  'darkred' => '#8b0000',
  'darksalmon' => '#e9967a',
  'darkseagreen' => '#8fbc8f',
  'darkslateblue' => '#483d8b',
  'darkslategray' => '#2f4f4f',
  'darkturquoise' => '#00ced1',
  'darkviolet' => '#9400d3',
  'deeppink' => '#ff1493',
  'deepskyblue' => '#00bfff',
  'dimgray' => '#696969',
  'dodgerblue' => '#1e90ff',
  'firebrick' => '#b22222',
  'floralwhite' => '#fffaf0',
  'forestgreen' => '#228b22',
  'fuchsia' => '#ff00ff',
  'gainsboro' => '#dcdcdc',
  'ghostwhite' => '#f8f8ff',
  'gold' => '#ffd700',
  'goldenrod' => '#daa520',
  'gray' => '#808080',
  'green' => '#008000',
  'greenyellow' => '#adff2f',
  'honeydew' => '#f0fff0',
  'hotpink' => '#ff69b4',
  'indianred' => '#cd5c5c',
  'indigo' => '#4b0082',
  'ivory' => '#fffff0',
  'khaki' => '#f0e68c',
  'lavender' => '#e6e6fa',
  'lavenderblush' => '#fff0f5',
  'lawngreen' => '#7cfc00',
  'lemonchiffon' => '#fffacd',
  'lightblue' => '#add8e6',
  'lightcoral' => '#f08080',
  'lightcyan' => '#e0ffff',
  'lightgoldenrodyellow' => '#fafad2',
  'lightgreen' => '#90ee90',
  'lightgrey' => '#d3d3d3',
  'lightpink' => '#ffb6c1',
  'lightsalmon' => '#ffa07a',
  'lightseagreen' => '#20b2aa',
  'lightskyblue' => '#87cefa',
  'lightslategray' => '#778899',
  'lightsteelblue' => '#b0c4de',
  'lightyellow' => '#ffffe0',
  'lime' => '#00ff00',
  'limegreen' => '#32cd32',
  'linen' => '#faf0e6',
  'magenta' => '#ff00ff',
  'maroon' => '#800000',
  'mediumaquamarine' => '#66cdaa',
  'mediumblue' => '#0000cd',
  'mediumorchid' => '#ba55d3',
  'mediumpurple' => '#9370db',
  'mediumseagreen' => '#3cb371',
  'mediumslateblue' => '#7b68ee',
  'mediumspringgreen' => '#00fa9a',
  'mediumturquoise' => '#48d1cc',
  'mediumvioletred' => '#c71585',
  'midnightblue' => '#191970',
  'mintcream' => '#f5fffa',
  'mistyrose' => '#ffe4e1',
  'moccasin' => '#ffe4b5',
  'navajowhite' => '#ffdead',
  'navy' => '#000080',
  'oldlace' => '#fdf5e6',
  'olive' => '#808000',
  'olivedrab' => '#6b8e23',
  'orange' => '#ffa500',
  'orangered' => '#ff4500',
  'orchid' => '#da70d6',
  'palegoldenrod' => '#eee8aa',
  'palegreen' => '#98fb98',
  'paleturquoise' => '#afeeee',
  'palevioletred' => '#db7093',
  'papayawhip' => '#ffefd5',
  'peachpuff' => '#ffdab9',
  'peru' => '#cd853f',
  'pink' => '#ffc0cb',
  'plum' => '#dda0dd',
  'powderblue' => '#b0e0e6',
  'purple' => '#800080',
  'red' => '#ff0000',
  'rosybrown' => '#bc8f8f',
  'royalblue' => '#4169e1',
  'saddlebrown' => '#8b4513',
  'salmon' => '#fa8072',
  'sandybrown' => '#f4a460',
  'seagreen' => '#2e8b57',
  'seashell' => '#fff5ee',
  'sienna' => '#a0522d',
  'silver' => '#c0c0c0',
  'skyblue' => '#87ceeb',
  'slateblue' => '#6a5acd',
  'slategray' => '#708090',
  'snow' => '#fffafa',
  'springgreen' => '#00ff7f',
  'steelblue' => '#4682b4',
  'tan' => '#d2b48c',
  'teal' => '#008080',
  'thistle' => '#d8bfd8',
  'tomato' => '#ff6347',
  'turquoise' => '#40e0d0',
  'violet' => '#ee82ee',
  'wheat' => '#f5deb3',
  'white' => '#ffffff',
  'whitesmoke' => '#f5f5f5',
  'yellow' => '#ffff00',
  'yellowgreen' => '#9acd32');
my @clip_atts = (
  'cx',
  'cy',
  'd',
  'fill-rule',
  'height',
  'id',
  'points',
  'r',
  'rx',
  'ry',
  'transform',
  'width',
  'x',
  'x1',
  'x2',
  'y',
  'y1',
  'y2',
  'xlink:href');
my @keep_fill = ('g','path','rect','circle','ellipse','line','polyline','polygon');

my $actual_width;
my $actual_height;
my %desc_elts;
my %comp_elts;
my @del_elts;
my $id_removed;
my @adobe_pref = ();
my @recalc_grads = ();
my $del_id;
my @ref_id = ();
my $link;
my $i;
my $number = qr/[-+]?\d*\.?\d+/;
my $scinumber = qr/[-+]?\d*\.?\d+[eE][-+]?\d+/;
my $all_num = qr/$scinumber|$number/;
my $unit = qr/px|pt|pc|mm|cm|m|in|ft|em|ex|%/;
my $commawsp = qr/\s+,?\s*|,\s*/;
my $translate = qr/translate\s*\(\s*($all_num)$commawsp?($all_num)?\s*\)/;
my $scale = qr/scale\s*\(\s*($all_num)$commawsp?($all_num)?\s*\)/;
my $skewX = qr/skewX\s*\(\s*($all_num)\s*\)/;
my $skewY = qr/skewY\s*\(\s*($all_num)\s*\)/;
my $rotate_a = qr/rotate\s*\(\s*($all_num)\s*\)/;
my $rotate_axy = qr/rotate\s*\(\s*($all_num)$commawsp($all_num)$commawsp($all_num)\s*\)/;
my $rotate = qr/rotate\s*\(\s*($all_num)$commawsp?($all_num)?$commawsp?($all_num)?\s*\)/;
my $matrix = qr/matrix\s*\(\s*($all_num)$commawsp($all_num)$commawsp($all_num)$commawsp($all_num)$commawsp($all_num)$commawsp($all_num)\s*\)/;

sub del_elt {

  $_[0]->delete;
  $del_id = $_[2];
  push @del_elts, $del_id;
  if ($ARGV[2] && $ARGV[2] ne "quiet") {
    print colored (" <$_[1]", 'bold red');
    print " id=\"$_[2]\" ($_[3])\n\n";
  }
}

sub bs_path {

  print colored (" <$_[0]", 'bold red');
  print " id=\"$_[1]\":\n";

  print colored ("  <path", 'bold green');
  print " id=\"$_[1]\" (it's the equivalent path element)\n\n";
}

sub desc_elt {
  my $desc_elt="";
  foreach my $elt ($_[0]->descendants_or_self) {
    $desc_elt = $desc_elt.$elt->name;
    foreach my $att ($elt->att_names) {
      if ($att ne "id") {
	$desc_elt = $desc_elt.$att;
	$desc_elt = $desc_elt.$elt->att($att);
      }
    }
  }
  return $desc_elt;
}

sub del_att {
  $i++;
  $_[0]->del_att("$_[1]");
  if ($ARGV[2] && $ARGV[2] ne "quiet") {
    if ($_[3] == 1) { print colored (" <$_[4]", 'bold');print " id=\"$_[5]\":" };
    print colored ("\n  •$_[1]", 'red'); print " ($_[2])";
  }
}

sub crt_att {
  $i++;
  if ($ARGV[2] && $ARGV[2] ne "quiet") {

  if ($_[3] == 1) { print colored (" <$_[4]", 'bold');print " id=\"$_[5]\":" };
  print colored ("\n  •$_[1]", 'green'); print " ($_[2])";
  }
}

sub color_rrggbb {
  my $att_val = $_[0];

  if ($att_val=~ /^\s*rgb\(\s*(\d+)%?\s*,\s*(\d+)%?\s*,\s*(\d+)%?\s*\)\s*$/) {
    (my $r,my $g,my $b) = ($1,$2,$3);
    ($r,$g,$b) = ($r*255/100,$g*255/100,$b*255/100) if ($att_val=~ /%/);
    ($r,$g,$b) = split(' ',sprintf("%02x %02x %02x",int $r,int $g,int $b));
    $att_val = "#$r$g$b";
  }

  if (exists($colors{"\L$att_val"})) {
    $att_val = $colors{"\L$att_val"};
  }

  if ($att_val=~ /^#([\dA-F]){3}$|^#([\dA-F]){6}$/) {
    $att_val = "\L$att_val";
  }

  if ($_[1] eq "yes" &&
      $att_val=~ /^#([\da-f])([\da-f])([\da-f])([\da-f])([\da-f])([\da-f])$/ &&
      ($1 eq $2 && $3 eq $4 && $5 eq $6)) {
    $att_val = "#$1$3$5";
  }

  return $att_val;
}

sub round_num {
  my $att_val = $_[0];
  my $unit = $_[1];
  my $dp = $_[2];

  $att_val = sprintf("%.${dp}f", $att_val)+0 if ($att_val=~ /([\d]*\.[\d][\d]{$dp,})/);
  $att_val=~ s/^0\./\./ if ($att_val=~ /^0\.[\d]+/);
  $att_val=~ s/^-0\./-\./ if ($att_val=~ /^-0\.[\d]+/);
  $att_val = $att_val.$unit if ($unit);

  return $att_val;
}

sub units_px {
  my $elt = $_[0];
  my $att = $_[1];
  my $att_val = $_[2];
  my $unit = $_[3];

  if ($unit=~ /em|ex/) {

    my $font_size = $elt->parent('*[@font-size=~ /\d$/]')->att('font-size');
    $font_size = $elt->att('font-size') unless ($font_size);
    $att_val = $att_val*$font_size if ($font_size && $unit eq "em");

    $att_val = $att_val*$font_size/2 if ($font_size && $unit eq "ex");
    $att_val = 0 unless ($font_size);
  }

  if ($unit eq "%") {
    if ($att eq "offset") {
      $att_val = $att_val/100;
      $att_val = 0 if ($att_val < 0);
      $att_val = 1 if ($att_val > 1);
    }
    elsif ($att=~ /[Ww]idth$|^x$|^[cdfr]x$|^x[12]$|^refX$/) {
      $att_val = $att_val*$actual_width/100;
    }
    elsif ($att=~ /[Hh]eight$|^y$|^[cdfr]y$|^y[12]$|^refY$/) {
      $att_val = $att_val*$actual_height/100;
    }
    else {
      $att_val = sqrt($actual_width**2+$actual_height**2)*$att_val/(sqrt(2)*100);
    }
  }

  $att_val = $att_val if ($unit eq "px");
  $att_val = $att_val*1.25 if ($unit eq "pt");
  $att_val = $att_val*15 if ($unit eq "pc");
  $att_val = $att_val*3.543307 if ($unit eq "mm");
  $att_val = $att_val*35.43307 if ($unit eq "cm");
  $att_val = $att_val*3543.307 if ($unit eq "m");
  $att_val = $att_val*90 if ($unit eq "in");
  $att_val = $att_val*1080 if ($unit eq "ft");
  
  return $att_val;
}

sub nested_transform {
  my $a1;my $b1;my $c1;my $d1;my $e1;my $f1;
  if ($_[0]=~/^$matrix$/) {
    ($a1,$b1,$c1,$d1,$e1,$f1) = ($1,$2,$3,$4,$5,$6);
  }
  my $a2;my $b2;my $c2;my $d2;my $e2;my $f2;
  if ($_[1]=~/^$matrix$/) {
    ($a2,$b2,$c2,$d2,$e2,$f2) = ($1,$2,$3,$4,$5,$6);
  }
  my $a = $a1*$a2+$c1*$b2;
  my $b = $b1*$a2+$d1*$b2;
  my $c = $a1*$c2+$c1*$d2;
  my $d = $b1*$c2+$d1*$d2;
  my $e = $a1*$e2+$c1*$f2+$e1;
  my $f = $b1*$e2+$d1*$f2+$f1;

  return "matrix($a,$b,$c,$d,$e,$f)";
}

sub opt_angle {
  my $angle = shift;
  if ($angle < 0) {
    $angle += 360 while ($angle < 0);
  } elsif ($angle > 360) {
    $angle -= 360 while ($angle > 360);
  }
  return $angle;
}

sub angle_tg {
  my $deg = shift;
  my $rad = ($deg/180)*3.14159265358979;

  if ($deg!~ /^(90|270)$/) {
    return sprintf("%.8f", sin($rad)/cos($rad))+0;
  } else {
    return "N/A";
  }
}

sub angle_cos {
  my $deg = shift;
  my $rad = ($deg/180)*3.14159265358979;

  return sprintf("%.8f", cos($rad))+0;
}

sub angle_sin {
  my $deg = shift;
  my $rad = ($deg/180)*3.14159265358979;

  return sprintf("%.8f", sin($rad))+0;
}

sub asin { atan2($_[0],sqrt(1-$_[0]**2))*180/3.14159265358979 }
sub acos { atan2(sqrt(1-$_[0]**2),$_[0])*180/3.14159265358979 }
sub atan { atan2($_[0],1)*180/3.14159265358979 };

sub trans_matrix {

  my $transform = shift;
  my @matrix;

  CYCLE_TRANS:
  while ($transform) {

    if ($transform=~ /^$commawsp/) {
      $transform=~ s/^$commawsp//;
      next CYCLE_TRANS;
    }
    if ($transform=~ /^$rotate_axy/) {
      my $angle = $1;
      my $cx = $2;
      my $cy = $3;
      my $cx_neg = $cx*(-1);
      my $cy_neg = $cy*(-1);
      $angle = &opt_angle($angle) if ($angle < 0 || $angle > 360);
      $angle = 0 if ($angle == 360);

      $transform=~ s/^$rotate_axy//;
      $transform = "translate($cx,$cy) rotate($angle) translate($cx_neg,$cy_neg) ".$transform;
      next CYCLE_TRANS;
    }
    if ($transform=~ /^$translate/) {
      my $tx = $1;
      my $ty = $2; $ty = 0 unless ($ty);

      $transform=~ s/^$translate//;
      push @matrix, "matrix(1,0,0,1,$tx,$ty)";
      next CYCLE_TRANS;
    }
    if ($transform=~ /^$scale/) {
      my $sx = $1;
      my $sy = $2; $sy = $sx unless ($sy);

      $transform=~ s/^$scale//;
      push @matrix, "matrix($sx,0,0,$sy,0,0)";
      next CYCLE_TRANS;
    }
    if ($transform=~ /^$skewX/) {
      my $angle = $1;
      $angle = &opt_angle($angle) if ($angle < 0 || $angle > 360);
      $angle = 0 if ($angle == 360);
      my $tg = &angle_tg($angle);

      $transform=~ s/^$skewX//;
      push @matrix, "matrix(1,0,$tg,1,0,0)" unless ($tg eq "N/A");
      next CYCLE_TRANS;
    }
    if ($transform=~ /^$skewY/) {
      my $angle = $1;
      $angle = &opt_angle($angle) if ($angle < 0 || $angle > 360);
      $angle = 0 if ($angle == 360);
      my $tg = &angle_tg($angle);

      $transform=~ s/^$skewY//;
      push @matrix, "matrix(1,$tg,0,1,0,0)" unless ($tg eq "N/A");
      next CYCLE_TRANS;
    }
    if ($transform=~ /^$rotate_a/) {
      my $angle = $1;
      $angle = &opt_angle($angle) if ($angle < 0 || $angle > 360);
      $angle = 0 if ($angle == 360);
      my $cos = &angle_cos($angle);
      my $sin = &angle_sin($angle);
      my $sin_neg = $sin*(-1);

      $transform=~ s/^$rotate_a//;
      push @matrix, "matrix($cos,$sin,$sin_neg,$cos,0,0)";
      next CYCLE_TRANS;
    }
    if ($transform=~ /^($matrix)/) {
      push @matrix, $1;
      $transform=~ s/^($matrix)//;
      next CYCLE_TRANS;
    }
  }

  if (scalar @matrix == 1) {
    $transform = shift @matrix;
  } else {
    $transform = shift @matrix;
    while ($matrix[0]) {
      my $cur_matrix = shift @matrix;
      $transform = &nested_transform($transform,$cur_matrix);
    }
  }
  return $transform;
}

my $twig = XML::Twig->new(
  no_prolog => "$args{'no_prolog'}",
  comments => "$args{'comments'}",
  output_encoding => 'utf8',
  discard_spaces => 1);

$twig->parsefile("$ARGV[0]");

(my $file_name = $ARGV[0])=~ s/^.+\///;
(my $length = length ($file_name))+=2;
my $line = ('─' x $length);

if ($ARGV[2] && $ARGV[2] ne "quiet") {
  print "\n\n\n╓$line╖";
  print "\n║ "; print colored ("$file_name", 'bold'); print " ║";
  print "\n╙$line╜\n\n";
} else {
  print "\n$file_name\n\n";
}

my $root = $twig->root;

print colored ("\nPREPROCESSING\n\n", 'bold blue underline') if ($ARGV[2] && $ARGV[2] ne "quiet");

my $size_initial = length($twig->sprint);

my $elts_initial = scalar($root->descendants_or_self);

my $atts_initial = 0;
foreach my $elt ($root->descendants_or_self) {

  $atts_initial+=($elt->att_nb);
    $elt->set_tag($1) if ($elt->name=~ /^svg:(.+)$/);

  if ($elt->att('fill') &&
      $elt->att('fill')=~ /^\s*(url\(#[^\)]+\)).+|^\s+(url\(#[^\)]+\))\s*$/) {

    $elt->set_att('fill' => $1);
  }
  elsif ($elt->att('stroke') &&
	 $elt->att('stroke')=~ /^\s*(url\(#[^\)]+\)).+|^\s+(url\(#[^\)]+\))\s*$/) {

    $elt->set_att('stroke' => $1);
  }
  if ($elt->att('style')) {

    my %style = map { split(":",$_) } split(";",$elt->att('style'));

    $elt->del_att('style');
    while ((my $att, my $att_val) = each %style) {

      $att = $1 if ($att=~ /^\s+(.+)\s*$/);
      $att_val = $1 if ($att_val=~ /^\s+(.+)\s*$/);

      if ($att=~ /^fill$|^stroke$/ &&
	  $att_val=~ /^(url\(#[^\)]+\)).+$/) {

	$att_val = $1;
      }

	$elt->set_att($att => $att_val) if ($att=~ /^[a-z]/);
    }
  }
}
if ($ARGV[2] && $ARGV[2] ne "quiet") {

  print colored (" The initial file size is $size_initial bytes\n", 'bold');

  print colored (" The initial number of elements is $elts_initial\n", 'bold');

  print colored (" The initial number of attributes is $atts_initial\n\n", 'bold');
} else {
  print " The initial file size is $size_initial bytes\n";
  print " The initial number of elements is $elts_initial\n";
  print " The initial number of attributes is $atts_initial\n\n";
}

if ($root->att('width') && $root->att('height')) {

  $actual_width = $root->att('width');
  $actual_height = $root->att('height');
  if ($actual_width=~ /^($all_num)($unit)$/ && $2 ne "%") {

    $actual_width = &units_px($root,$actual_width,$1,$2);
  }

  if ($root->att('viewBox') &&
      $actual_width=~ /%/ &&
      $root->att('viewBox')=~ /($all_num)\s*,?\s*($all_num)$/) {

    $actual_width = substr($actual_width,0,-1)*$1/100;
  }

  if ($actual_height=~ /^($all_num)($unit)$/ && $2 ne "%") {

    $actual_height = &units_px($root,"$actual_height",$1,$2);
  }

  if ($root->att('viewBox') &&
      $actual_height=~ /%/ &&
      $root->att('viewBox')=~ /($all_num)\s*,?\s*($all_num)$/) {

    $actual_height = substr($actual_height,0,-1)*$2/100;
  }

} elsif (!$root->att('width') && !$root->att('height') &&
	 $root->att('viewBox')=~ /($all_num)\s*,?\s*($all_num)$/) {

  $actual_width = $1;
  $actual_height = $2;
}

if (($actual_width < 64 || $actual_height < 64) &&
    $args{'round_numbers'} eq "yes") {

  $args{'dp_d'} = 5 if ($args{'dp_d'} < 5);
  $args{'dp_tr'} = 6 if ($args{'dp_tr'} < 6);
}

if ($args{'adobe_elts'} eq "delete" ||
    $args{'adobe_atts'} eq "delete") {

  while ((my $att, my $att_val) = each %{$root->atts}) {

    if ($att_val~~@adobe_ns) {
      $att=~ s/^xmlns://;
      push @adobe_pref, $att;
    }
  }
}

my $defs = $twig->first_elt('defs');

CYCLE_DEF:
foreach ($root->descendants) {

  my $elt_name = $_->name;

  my $elt_id = $_->id;
  $elt_id = "none" unless ($elt_id);
  if ($elt_name ~~ @defs_elts &&
      !$_->parent('defs')) {
    unless ($defs) {
      $defs = $root->insert_new_elt('defs');
      $defs->set_id('defs1');

      if ($ARGV[2] && $ARGV[2] ne "quiet") {
	print colored (" <defs", 'bold green');
	print " id=\"defs1\" (there isn't the main defs section)\n\n";
      }
    }
    $_->move(last_child => $defs);

    if ($ARGV[2] && $ARGV[2] ne "quiet") {
      print colored (" <$elt_name", 'bold magenta');
      print " id=\"$elt_id\" (into the defs section)\n\n";
    }
  }
}
$defs->move(first_child => $root) if ($defs);

unless ($root->descendants('defs') == 1) {
  foreach ($root->get_xpath("//defs")) {
    unless ($_->cmp($defs) == 0) {
      my $defs_id = $_->id; $defs_id = "none" unless ($defs_id);

      foreach ($_->children) {
	my $elt_name = $_->name;
	my $elt_id = $_->id;
	$_->move(last_child => $defs);

	if ($ARGV[2] && $ARGV[2] ne "quiet") {
	  print colored (" <$elt_name", 'bold magenta');
	  print " id=\"$elt_id\" (into the main defs section)\n\n";
	}
      }

      $_->erase;

      if ($ARGV[2] && $ARGV[2] ne "quiet") {
	
	print colored (" <defs", 'bold red');
	print " id=\"$defs_id\" (it's not the main defs section)\n\n";
      }
    }
  }
}

print colored ("\nPROCESSING ELEMENTS\n\n", 'bold blue underline') if ($ARGV[2] && $ARGV[2] ne "quiet");

CYCLE_ELTS:
foreach my $elt ($root->descendants) {
  my $elt_name = $elt->name;
  my $elt_id = $elt->id;
  $elt_id = "none" unless ($elt_id);
  (my $elt_pref = $elt_name)=~ s/:.+$// if ($args{'adobe_elts'} eq "delete" && $elt_name=~ /:/);

  if ($del_id && $elt->parent("*[\@id=\"$del_id\"]")) {
    next CYCLE_ELTS;
  }

  if ($args{'metadata'} eq "delete" &&
      $elt_name eq "metadata") {

    &del_elt($elt,$elt_name,$elt_id,"it's a metadata element");
    next CYCLE_ELTS;
  }

  if ($args{'inkscape_elts'} eq "delete" &&
      $elt_name=~ /^inkscape:/ &&
      $elt_name ne "inkscape:path-effect") {

    &del_elt($elt,$elt_name,$elt_id,"it's an Inkscape element");
    next CYCLE_ELTS;
  }

  if ($args{'sodipodi_elts'} eq "delete" &&
      $elt_name=~ /^sodipodi:/) {

    &del_elt($elt,$elt_name,$elt_id,"it's a Sodipodi element");
    next CYCLE_ELTS;
  }

  if ($args{'adobe_elts'} eq "delete" && $elt_pref && $elt_pref~~@adobe_pref) {
    &del_elt($elt,$elt_name,$elt_id,"it's an Adobe Illustrator element");
    next CYCLE_ELTS;
  }

  if ($args{'adobe_elts'} eq "delete" &&
      $elt_name eq "foreignObject" &&
      $elt->att('requiredExtensions')~~@adobe_ns) {

    &del_elt($elt,$elt_name,$elt_id,"it's an Adobe Illustrator element");
    next CYCLE_ELTS;
  }

  if ($args{'invisible_elts'} eq "delete") {

    if ($elt->att('display') &&
	$elt->att('display') eq "none") {

      &del_elt($elt,$elt_name,$elt_id,"it's an invisible element");
      next CYCLE_ELTS;
    }

    if (defined $elt->att('opacity') &&
	$elt->att('opacity')=~ /^-|^\+?0(\.0+)?$/) {

      &del_elt($elt,$elt_name,$elt_id,"it's an invisible element");
      next CYCLE_ELTS;
    }

    if (($elt->att('fill') && $elt->att('fill') eq "none") ||
	(defined $elt->att('fill-opacity') && $elt->att('fill-opacity')=~ /^-|^\+?0(\.0+)?$/)) {

      if ((!$elt->att('stroke') && !$elt->parent('g[@stroke]')) ||
	  (!$elt->att('stroke') && $elt->parent("g[\@stroke=\"none\"]")) ||
	  ($elt->att('stroke') && $elt->att('stroke') eq "none") ||
	  (defined $elt->att('stroke-opacity') && $elt->att('stroke-opacity')=~ /^-|^\+?0(\.0+)?$/) ||
	  (defined $elt->att('stroke-width') && $elt->att('stroke-width')=~ /^-|^\+?0(\.0+)?$unit?$/)) {

	&del_elt($elt,$elt_name,$elt_id,"it's an invisible element");
	next CYCLE_ELTS;
      }
    }

    if ($elt_name~~['pattern','image', 'rect'] &&
	((defined $elt->att('height') && $elt->att('height')=~ /^-|^\+?0(\.0+)?$unit?$/) ||
	(defined $elt->att('width') && $elt->att('width')=~ /^-|^\+?0(\.0+)?$unit?$/))) {

      &del_elt($elt,$elt_name,$elt_id,"it's an invisible element");
      next CYCLE_ELTS;
    }
    elsif ($elt_name~~['pattern','image', 'rect'] &&
	   !($elt->att('height') || $elt->att('width')) &&
	   !$elt->att('xlink:href')) {

      &del_elt($elt,$elt_name,$elt_id,"it's an invisible element");
      next CYCLE_ELTS;
    }

    if ($elt_name eq "path" &&
	!$elt->att('d')) {

	&del_elt($elt,$elt_name,$elt_id,"it's an invisible element");
	next CYCLE_ELTS;
    }

    if ($elt_name eq "polygon" &&
	!$elt->att('points')) {

	&del_elt($elt,$elt_name,$elt_id,"it's an invisible element");
	next CYCLE_ELTS;
    }

    if ($elt_name eq "polyline" &&
	!$elt->att('points')) {

	&del_elt($elt,$elt_name,$elt_id,"it's an invisible element");
	next CYCLE_ELTS;
    }

    if ($elt_name eq "circle" &&
	defined $elt->att('r') &&
	$elt->att('r')=~ /^-|^\+?0(\.0+)?$unit?$/) {

      &del_elt($elt,$elt_name,$elt_id,"it's an invisible element");
      next CYCLE_ELTS;
    }

    if ($elt_name eq "ellipse" &&
	((defined $elt->att('rx') && $elt->att('rx')=~ /^-|^\+?0(\.0+)?$unit?$/) ||
	(defined $elt->att('ry') && $elt->att('ry')=~ /^-|^\+?0(\.0+)?$unit?$/))) {

      &del_elt($elt,$elt_name,$elt_id,"it's an invisible element");
      next CYCLE_ELTS;
    }

    if ($elt_name eq "text") {

      if ($elt->children('tspan')) {
	foreach ($elt->children('tspan')) {
	  
	  $_->delete if ($_->is_empty);
	}
      }

      if ($elt->children('tref')) {
	foreach ($elt->children('tref')) {
	  
	  $_->delete if ($_->is_empty && !$_->att('xlink:href'));
	}
      }

      if ($elt->children('textPath')) {
	foreach ($elt->children('textPath')) {
	  
	  $_->delete if ($_->is_empty);
	}
      }

      if (!$elt->children &&
	  !$elt->text_only) {

	&del_elt($elt,$elt_name,$elt_id,"it's an invisible element");
	next CYCLE_ELTS;
      }
    }
  }

  if ($args{'empty_groups'} eq "delete" &&
      $elt_name eq "g" && !$elt->children) {

      &del_elt($elt,$elt_name,$elt_id,"it's an empty group");
      next CYCLE_ELTS;
  }

  if ($args{'gaussian_blur'} eq "delete" &&
      $elt_name eq "filter" && $elt->children_count == 1 &&
      $elt->all_children_are ('feGaussianBlur') &&
      $elt->first_child->att('stdDeviation') < $args{'std_dev'}) {

    &del_elt($elt,$elt_name,$elt_id,"it's an unused filter");
    next CYCLE_ELTS;
  }

  if ($elt_name eq 'switch' &&
      ($elt->has_no_atts || ($elt->att_nb == 1 && $elt->id))) {

	$elt->erase;
  }

  if ($elt_name eq "rect" &&
      $elt->att('width') && $elt->att('height') &&
      ($elt->att('rx') || $elt->att('ry'))) {

      my $rx = $elt->att('rx');
      my $ry = $elt->att('ry');
      $rx = $ry if (!$rx && $ry);
      $ry = $rx if (!$ry && $rx);
      my $w = $elt->att('width');
      my $h = $elt->att('height');
      if ($rx > $w/2) {
	$rx = $w/2;
	$elt->set_att('rx' => $rx);
      }
      if ($ry > $h/2) {
	$ry = $h/2;
	$elt->set_att('ry' => $ry);
      }
      if ($rx == $ry) {
	$elt->set_att('rx' => $rx);
	$elt->del_att('ry');
      }
  }

  if ($args{'bs_path'} eq "yes") {
    if ($elt_name eq "circle" && $elt->att('r')) {
      my $x = $elt->att('cx');
      my $y = $elt->att('cy');
      my $r = $elt->att('r');

      $x = 0 unless ($x);
      $y = 0 unless ($y);
      $x = &units_px($elt,'cx',$1,$2) if ($x=~ /^($all_num)($unit)$/);
      $y = &units_px($elt,'cy',$1,$2) if ($y=~ /^($all_num)($unit)$/);
      $r = &units_px($elt,'r',$1,$2) if ($r=~ /^($all_num)($unit)$/);

      my $x1 = $x+$r;
      my $x2 = $x-$r;

      $elt->set_tag('path');
      $elt->del_att('cx', 'cy', 'r');
      $elt->set_att('d' => "M$x1,$y A$r,$r 0 1 0 $x2,$y A$r,$r 0 1 0 $x1,$y z");
      &bs_path($elt_name,$elt_id) if ($ARGV[2] && $ARGV[2] ne "quiet");
    }

    if ($elt_name eq "ellipse" && $elt->att('rx') && $elt->att('ry')) {
      my $x = $elt->att('cx');
      my $y = $elt->att('cy');
      my $rx = $elt->att('rx');
      my $ry = $elt->att('ry');

      $x = 0 unless ($x);
      $y = 0 unless ($y);
      $x = &units_px($elt,'cx',$1,$2) if ($x=~ /^($all_num)($unit)$/);
      $y = &units_px($elt,'cy',$1,$2) if ($y=~ /^($all_num)($unit)$/);
      $rx = &units_px($elt,'rx',$1,$2) if ($rx=~ /^($all_num)($unit)$/);
      $ry = &units_px($elt,'ry',$1,$2) if ($ry=~ /^($all_num)($unit)$/);

      my $x1 = $x+$rx;
      my $x2 = $x-$rx;

      $elt->set_tag('path');
      $elt->del_att('cx', 'cy', 'rx', 'ry');
      $elt->set_att('d' => "M$x1,$y A$rx,$ry 0 1 0 $x2,$y A$rx,$ry 0 1 0 $x1,$y z");
      &bs_path($elt_name,$elt_id) if ($ARGV[2] && $ARGV[2] ne "quiet");
    }

    if ($elt_name eq "polygon" && $elt->att('points')) {
      my $d = $elt->att('points');

      if ($d) {

	$elt->set_tag('path');
	$elt->del_att('points');
	$elt->set_att('d' => "M$d z");
	&bs_path($elt_name,$elt_id) if ($ARGV[2] && $ARGV[2] ne "quiet");
      }
    }

    if ($elt_name eq "polyline" && $elt->att('points')) {
      my $d = $elt->att('points');

      if ($d) {

	$elt->set_tag('path');
	$elt->del_att('points');
	$elt->set_att('d' => "M$d");
	&bs_path($elt_name,$elt_id) if ($ARGV[2] && $ARGV[2] ne "quiet");
      }
    }

    if ($elt_name eq "line") {
      my $x1 = $elt->att('x1');
      my $y1 = $elt->att('y1');
      my $x2 = $elt->att('x2');
      my $y2 = $elt->att('y2');

      $x1 = 0 unless ($x1);
      $y1 = 0 unless ($y1);
      $x2 = 0 unless ($x2);
      $y2 = 0 unless ($y2);
      $x1 = &units_px($elt,'x1',$1,$2) if ($x1=~ /^($all_num)($unit)$/);
      $y1 = &units_px($elt,'y1',$1,$2) if ($y1=~ /^($all_num)($unit)$/);
      $x2 = &units_px($elt,'x2',$1,$2) if ($x2=~ /^($all_num)($unit)$/);
      $y2 = &units_px($elt,'y2',$1,$2) if ($y2=~ /^($all_num)($unit)$/);

      $elt->set_tag('path');
      $elt->del_att('x1', 'y1', 'x2', 'y2');
      $elt->set_att('d' => "M$x1,$y1 $x2,$y2");
      &bs_path($elt_name,$elt_id) if ($ARGV[2] && $ARGV[2] ne "quiet");
    }

    if ($elt_name eq "rect" && $elt->att('height') && $elt->att('width')) {
      my $x = $elt->att('x');
      my $y = $elt->att('y');
      my $h = $elt->att('height');
      my $w = $elt->att('width');
      my $rx = $elt->att('rx');
      my $ry = $elt->att('ry');

      $x = 0 unless ($x);
      $y = 0 unless ($y);
      $rx = $ry if (!$rx && $ry);
      $ry = $rx if (!$ry && $rx);

      $x = &units_px($elt,'x',$1,$2) if ($x=~ /^($all_num)($unit)$/);
      $y = &units_px($elt,'y',$1,$2) if ($y=~ /^($all_num)($unit)$/);
      $h = &units_px($elt,'height',$1,$2) if ($h=~ /^($all_num)($unit)$/);
      $w = &units_px($elt,'width',$1,$2) if ($w=~ /^($all_num)($unit)$/);
      $rx = &units_px($elt,'rx',$1,$2) if ($rx && $rx=~ /^($all_num)($unit)$/);
      $ry = &units_px($elt,'ry',$1,$2) if ($ry && $ry=~ /^($all_num)($unit)$/);

      $elt->set_tag('path');
      $elt->del_att('x', 'y', 'rx', 'ry', 'height', 'width');

      unless ($rx && $ry) {
	my $x1 = $x+$w;
	my $y1 = $y+$h;
	$elt->set_att('d' => "M$x,$y H$x1 V$y1 H$x z");
      } else {
	my $x1 = $x+$rx;
	my $x2 = $x+$w-$rx;
	my $x3 = $x+$w;
	my $y1 = $y+$ry;
	my $y2 = $y+$h-$ry;
	my $y3 = $y+$h;
	$elt->set_att('d' => "M$x1,$y H$x2 A$rx,$ry 0 0 1 $x3,$y1 V$y2 A$rx,$ry 0 0 1 $x2,$y3 H$x1 A$rx,$ry 0 0 1 $x,$y2 V$y1 A$rx,$ry 0 0 1 $x1,$y z");
      }
      &bs_path($elt_name,$elt_id) if ($ARGV[2] && $ARGV[2] ne "quiet");
    }
  }

  if ($args{'conc_trans'} eq "yes" &&
      $elt_name eq "g" && !($twig->get_xpath("//*[\@xlink:href=\"#$elt_id\"]")) &&
      $elt->att('transform') && $elt->children &&
      $elt->all_children_are('*[@transform]')) {

    unless ($elt->att('mask') ||
	    $elt->att('clip-path') ||
	    $elt->att('filter') ||
	    ($elt->att('fill') && $elt->att('fill')=~ /^url/) ||
	    ($elt->att('stroke') && $elt->att('stroke')=~ /^url/)) {

      my $matrix1 = $elt->att('transform');

      if ($ARGV[2] && $ARGV[2] ne "quiet") {

      print colored (" <g",'bold'); print " id=$elt_id\n";
      print colored ("  •transform=$matrix1\n",'red');
      }

      $matrix1 = &trans_matrix($matrix1) unless ($matrix1=~ /^$matrix$/);
      $elt->del_att('transform');

      foreach ($elt->children) {

	my $ch_name = $_->name;
	my $ch_id = $_->id;

	my $matrix2 = $_->att('transform');
	$matrix2 = &trans_matrix($matrix2) unless ($matrix2=~ /^$matrix$/);

	$matrix2 = &nested_transform($matrix1,$matrix2);

	$_->set_att('transform' => $matrix2);

	if ($ARGV[2] && $ARGV[2] ne "quiet") {

	print colored ("   <$ch_name",'bold'); print " id=$ch_id\n";
	print colored ("    •transform=$matrix2\n",'green');
	}
      }
	print "\n" if ($ARGV[2] && $ARGV[2] ne "quiet");
    }
  }

  if (($args{'unused_def'} eq "delete" || $args{'unref_id'} eq "delete") &&
      !$elt->parent('defs')) {
    while ((my $att, my $att_val) = each %{$elt->atts}) {
      if ($att_val =~ /^url\(#(.+)\)$/ ||
	  ($att eq "xlink:href" && $att_val =~ /^#(.+)$/)) {
	if ($1 ~~ @del_elts || !$twig->elt_id($1)) {

	  $elt->del_att($att);
	  $elt->set_att('fill' => 'none') if ($att eq "fill");
	  $elt->delete if ($att eq "fill" && !$elt->att('stroke') && !$elt->parent('g[@stroke]'));
	  $elt->delete if ($att eq "fill" && $elt->att('stroke') eq "none");
	  $elt->delete if ($att eq "fill" && $elt->att('stroke-width') && $elt->att('stroke-width')=~ /^-|^0$unit?$/);
	} else {

	  push @ref_id, $1 unless ($1 ~~ @ref_id);
	}
      }
    }
  }
}
my @out_link = @ref_id;
if ($args{'unused_def'} eq "delete" ||
    $args{'unref_id'} eq "delete") {
  foreach (@ref_id) {

    foreach my $elt ($twig->elt_id("$_")->descendants_or_self){
      while ((my $att, my $att_val) = each %{$elt->atts}) {
	if ($att_val =~ /^url\(#(.+)\)$/ ||
	    ($att eq "xlink:href" && $att_val =~ /^#(.+)$/)) {
	  if ($1 ~~ @del_elts ||
	      !($twig->elt_id($1))) {

	    $elt->del_att($att);
	  }

	  elsif (!($1 ~~ @ref_id) &&
		 $args{'clip_atts'} eq "delete" && $elt->parent('clipPath') &&
		 $elt->name('use') && $att eq "xlink:href") {

	    push @ref_id, $1;
	  }

	  elsif (!($1 ~~ @ref_id) && 
		 !($args{'clip_atts'} eq "delete" && $elt->parent('clipPath'))) {

	    push @ref_id, $1;
	  }
	}
      }
    }
  }
}
if ($args{'unused_def'} eq "delete" && $defs) {
  foreach my $elt ($defs->children) {
    my $elt_id = $elt->id;
    $elt_id = "none" unless ($elt_id);
    unless ($elt_id ~~ @ref_id) {

      my $elt_name = $elt->name;
      &del_elt($elt,$elt_name,$elt_id,"it's an unused definition");
    }
  }
}

if ($args{'empty_defs'} eq "delete" &&
    $defs && !$defs->children) {

  my $elt_id = $defs->id; $elt_id = "none" unless ($elt_id);
  &del_elt($defs,"defs",$elt_id,"it's the empty defs section");
}

if ($args{'dupl_defs'} eq "yes" && $defs) {
  foreach my $elt ($defs->children) {
    my $elt_name = $elt->name;

    my $elt_id = $elt->id;
    if ($elt_id) {
      my $desc = &desc_elt($elt);
      if (exists($desc_elts{$desc})) {
	$comp_elts{$elt_id} = $desc_elts{$desc};
	$elt->delete;

	if ($ARGV[2] && $ARGV[2] ne "quiet") {

	print colored (" <$elt_name", 'bold red');
	print " id=\"$elt_id\" (it's a duplicated element)\n\n";
	}
      } else {
	$desc_elts{$desc} = $elt_id;
      }
    }
  }

  %desc_elts = ();
}
if ($args{'opt_trans'} eq "yes" && $defs) {

  foreach my $elt ($defs->get_xpath('./*[@gradientTransform]')) {

    my $elt_name = $elt->name;
    my $elt_id = $elt->id;
    my $trans = $elt->att('gradientTransform');
    my $grad_trans = $trans;
    unless ($trans=~/^$matrix$/) {

      $trans = &trans_matrix($trans);
    }
    if ($elt_name eq "linearGradient" &&
	$trans!~/$scinumber/ &&
	$trans=~/^$matrix$/ &&
	$2 == 0 && $3 == 0) {

      (my $a,my $d,my $e,my $f) = ($1,$4,$5,$6);

      my $x1 = $elt->att('x1');
      my $x2 = $elt->att('x2');
      my $y1 = $elt->att('y1');
      my $y2 = $elt->att('y2');

      $x1 = 0 unless ($x1);
      $x2 = "100%" unless ($x2);
      $y1 = 0 unless ($y1);
      $y2 = 0 unless ($y2);

      $x1 = &units_px($elt,'x1',$1,$2) if ($x1=~ /^($all_num)($unit)$/);
      $x2 = &units_px($elt,'x2',$1,$2) if ($x2=~ /^($all_num)($unit)$/);
      $y1 = &units_px($elt,'y1',$1,$2) if ($y1=~ /^($all_num)($unit)$/);
      $y2 = &units_px($elt,'y2',$1,$2) if ($y2=~ /^($all_num)($unit)$/);

      (my $x11,my $x22,my $y11,my $y22) = ($a*$x1+$e,$a*$x2+$e,$d*$y1+$f,$d*$y2+$f);

      if ($ARGV[2] && $ARGV[2] ne "quiet") {

	print colored (" <$elt_name", 'bold');
	print " id=$elt_id:\n";
	print colored ("  •gradientTransform=$grad_trans\n", 'red');
	print colored ("  •x1=$x1\n", 'red');
	print colored ("  •x2=$x2\n", 'red');
	print colored ("  •y1=$y1\n", 'red');
	print colored ("  •y2=$y2\n", 'red');

	print colored ("  •x1=$x11\n", 'green');
	print colored ("  •x2=$x22\n", 'green');
	print colored ("  •y1=$y11\n", 'green');
	print colored ("  •y2=$y22\n", 'green');
	print "\n";
      }

      $elt->set_att('x1' => $x11);
      $elt->set_att('x2' => $x22);
      $elt->set_att('y1' => $y11);
      $elt->set_att('y2' => $y22);
      $elt->del_att('gradientTransform');
      push @recalc_grads, $elt_id;
    }
    if ($elt_name eq "radialGradient" &&
	$trans!~/$scinumber/ &&
	$trans=~/^$matrix$/ &&
	$2 == 0 && $3 == 0 && $1 == $4) {

      (my $a,my $d,my $e,my $f) = ($1,$4,$5,$6);

      my $cx = $elt->att('cx');
      my $cy = $elt->att('cy');
      my $fx = $elt->att('fx');
      my $fy = $elt->att('fy');
      my $r = $elt->att('r');

      $cx = "50%" unless ($cx);
      $cy = "50%" unless ($cy);
      $r = "50%" unless ($r);

      $cx = &units_px($elt,'cx',$1,$2) if ($cx && $cx=~ /^($all_num)($unit)$/);
      $cy = &units_px($elt,'cy',$1,$2) if ($cy && $cy=~ /^($all_num)($unit)$/);
      $fx = &units_px($elt,'fx',$1,$2) if ($fx && $fx=~ /^($all_num)($unit)$/);
      $fy = &units_px($elt,'fy',$1,$2) if ($fy && $fy=~ /^($all_num)($unit)$/);
      $r = &units_px($elt,'r',$1,$2) if ($r && $r=~ /^($all_num)($unit)$/);

      my $r1 = $r*$a;
      my $cx1 = $a*$cx+$e;
      my $fx1 = $a*$fx+$e if ($fx);
      my $cy1 = $d*$cy+$f;
      my $fy1 = $d*$fy+$f if ($fy);

      if ($ARGV[2] && $ARGV[2] ne "quiet") {

	print colored (" <$elt_name", 'bold');
	print " id=$elt_id:\n";
	print colored ("  •gradientTransform=$grad_trans\n", 'red');
	print colored ("  •cx=$cx\n", 'red');
	print colored ("  •cy=$cy\n", 'red');
	print colored ("  •fx=$fx\n", 'red') if ($fx);
	print colored ("  •fy=$fy\n", 'red') if ($fy);
	print colored ("  •r=$r\n", 'red');

	print colored ("  •cx=$cx1\n", 'green');
	print colored ("  •cy=$cy1\n", 'green');
	print colored ("  •fx=$fx1\n", 'green') if ($fx);
	print colored ("  •fy=$fy1\n", 'green') if ($fy);
	print colored ("  •r=$r1\n", 'green');
	print "\n";
      }

      $elt->set_att('cx' => $cx1);
      $elt->set_att('cy' => $cy1);
      $elt->set_att('fx' => $fx1) if ($fx);
      $elt->set_att('fy' => $fy1) if ($fy);
      $elt->set_att('r' => $r1);
      $elt->del_att('gradientTransform');
      push @recalc_grads, $elt_id;
    }
  }
}
print colored ("\nPROCESSING ATTRIBUTES\n\n", 'bold blue underline') if ($ARGV[2] && $ARGV[2] ne "quiet");

foreach my $elt ($root->descendants_or_self) {

  $i = 0;
  my $elt_name = $elt->name;
  (my $elt_pref = $elt_name)=~ s/:.+$// if ($args{'adobe_atts'} eq "delete" && $elt_name=~ /:/);
  my $elt_id = $elt->id;
  $elt_id = "none" unless ($elt_id);

  if ($args{'viewbox'} eq "yes" &&
      $elt_name eq "svg") {

    if (!$root->att('viewBox')) {

      $root->set_att('viewBox' => "0 0 $actual_width $actual_height");
      &crt_att($elt,"viewBox","\"0 0 $actual_width $actual_height\"",$i,$elt_name,$elt_id);
      &del_att($elt,"height","viewBox is enabled",$i,$elt_name,$elt_id);
      &del_att($elt,"width","viewBox is enabled",$i,$elt_name,$elt_id);
    }
    elsif ($root->att('viewBox') &&
	   $root->att('height') && $root->att('width') &&
	   $root->att('viewBox')=~ /($all_num)\s*,?\s*($all_num)$/ &&
	   $1 == $actual_width && $2 == $actual_height) {

      &del_att($elt,"height","viewBox is enabled",$i,$elt_name,$elt_id);
      &del_att($elt,"width","viewBox is enabled",$i,$elt_name,$elt_id);
    }
  }

  CYCLE_ATTS:
  foreach my $att ($elt->att_names) {

    my $att_val = $elt->att($att);
    $att = $1 if ($att=~ /^\s+(.+)\s*$/);
    $att_val = $1 if ($att_val=~ /^\s+(.+)\s*$/);
    (my $att_pref = $att)=~ s/:.+$// if ($args{'adobe_atts'} eq "delete" && $att=~ /:/);

    if ($att_val=~ /^($number)($unit)$|^($scinumber)($unit)$/ &&
	$2 eq "px") {

      $att_val = $1;
      $elt->set_att($att => $att_val);
    }

    if ($att=~ /opacity$/ &&
	($att_val < 0 || $att_val > 1)) {

      $att_val = 0 if ($att_val < 0);
      $att_val = 1 if ($att_val > 1);
      $elt->set_att($att => $att_val);
    }

    if ($att=~ /^xml:space$|^space$/) {

      &del_att($elt,$att,"it's an unused attribute",$i,$elt_name,$elt_id);
      next CYCLE_ATTS;
    }

    if ($args{'unref_id'} eq "yes" &&
	$att eq "id" &&
	!($att_val ~~ @ref_id)) {

      next CYCLE_ATTS if ($args{'protect_id'} eq "yes" && $att_val=~ /^[a-zA-Z]+$/);

      &del_att($elt,$att,"it's an unreferenced id",$i,$elt_name,$elt_id);
      next CYCLE_ATTS; 
    }
    if ($args{'unused_ns'} eq "delete" &&
	$att=~ /^xmlns:/) {
      if ($args{'metadata'} eq "delete" &&
	  $att=~ /^xmlns:rdf$|^xmlns:cc$|^xmlns:dc$/) {

	&del_att($elt,$att,"it's an unused namespace",$i,$elt_name,$elt_id);
	next CYCLE_ATTS; 
      }
      if ($args{'inkscape_elts'} eq "delete" &&
	  $args{'inkscape_atts'} eq "delete" &&
	  $att eq "xmlns:inkscape") {

	&del_att($elt,$att,"it's an unused namespace",$i,$elt_name,$elt_id);
	next CYCLE_ATTS; 
      }
      if ($args{'sodipodi_elts'} eq "delete" &&
	  $args{'sodipodi_atts'} eq "delete" &&
	  $att eq "xmlns:sodipodi") {

	&del_att($elt,$att,"it's an unused namespace",$i,$elt_name,$elt_id);
	next CYCLE_ATTS; 
      }
      if ($args{'adobe_elts'} eq "delete" &&
	  $args{'adobe_atts'} eq "delete" &&
	  $att_val~~@adobe_ns) {

	&del_att($elt,$att,"it's an unused namespace",$i,$elt_name,$elt_id);
	next CYCLE_ATTS; 
      }
      if ($att eq "xmlns:xlink" &&
	  !$twig->get_xpath('//*[@xlink:href or @xlink:type or @xlink:role or @xlink:arcrole or @xlink:title or @xlink:show or @xlink:actuate]')) {

	&del_att($elt,$att,"it's an unused namespace",$i,$elt_name,$elt_id);
	next CYCLE_ATTS; 
      }
    }

    if ($args{'inkscape_elts'} ne "delete" &&
	$elt_name!~ /^inkscape:/ &&
	$args{'inkscape_atts'} eq "delete" &&
	$att=~ /^inkscape:/) {

      &del_att($elt,$att,"it's an Inkscape attribute",$i,$elt_name,$elt_id);
      next CYCLE_ATTS;
    }
    elsif ($args{'inkscape_elts'} eq "delete" &&
	   $elt_name ne "inkscape:path-effect" &&
	   $args{'inkscape_atts'} eq "delete" &&
	   $att=~ /^inkscape:/) {

      &del_att($elt,$att,"it's an Inkscape attribute",$i,$elt_name,$elt_id);
      next CYCLE_ATTS;
    }

    if ($args{'sodipodi_elts'} ne "delete" &&
	$elt_name!~ /^sodipodi:/ &&
	$args{'sodipodi_atts'} eq "delete" &&
	$att=~ /^sodipodi:/) {

      &del_att($elt,$att,"it's a Sodipodi attribute",$i,$elt_name,$elt_id);
      next CYCLE_ATTS;
    }
    elsif ($args{'sodipodi_elts'} eq "delete" &&
	   $args{'sodipodi_atts'} eq "delete" &&
	   $att=~ /^sodipodi:/) {

      &del_att($elt,$att,"it's a Sodipodi attribute",$i,$elt_name,$elt_id);
      next CYCLE_ATTS;
    }

    if ($args{'adobe_elts'} ne "delete" &&
	$args{'adobe_atts'} eq "delete" &&
	$att_pref && $att_pref~~@adobe_pref &&
	!($elt_pref~~@adobe_pref)) {

      &del_att($elt,$att,"it's an Adobe Illustrator attribute",$i,$elt_name,$elt_id);
      next CYCLE_ATTS;
    }
    elsif ($args{'adobe_elts'} eq "delete" &&
	   $args{'adobe_atts'} eq "delete" &&
	   $att_pref && $att_pref~~@adobe_pref) {

      &del_att($elt,$att,"it's an Adobe Illustrator attribute",$i,$elt_name,$elt_id);
      next CYCLE_ATTS;
    }

    if ($args{'stroke_atts'} eq "delete" &&
	$att=~ /^stroke-/ && !$elt->att('stroke')) {

	&del_att($elt,$att,"this element hasn't the stroke attribute",$i,$elt_name,$elt_id);
	next CYCLE_ATTS;
    }
    elsif ($args{'stroke_atts'} eq "delete" &&
	   $att=~ /^stroke-/ && $elt->att('stroke') &&
	   $elt->att('stroke') eq "none") {

      &del_att($elt,$att,"this element hasn't the stroke attribute",$i,$elt_name,$elt_id);
      next CYCLE_ATTS;
    }

    if ($args{'fill_atts'} eq "delete" &&
	$att=~ /^fill-/ && $elt->att('fill') &&
	$elt->att('fill') eq "none") {

      &del_att($elt,$att,"it's a non-used fill attribute",$i,$elt_name,$elt_id);
      next CYCLE_ATTS;
    }

    if ($args{'text_atts'} eq "delete" &&
	$att~~@text_atts &&
	!($elt_name eq "text" || $elt->parent('text') || $elt->descendants('text'))) {

	&del_att($elt,$att,"it's the only text attribute",$i,$elt_name,$elt_id);
	next CYCLE_ATTS;
    }

    if ($args{'d_atts'} eq "delete" &&
	$att eq "d" && $elt_name!~ /^path$|^glyph$|^missing-glyph$/) {

      &del_att($elt,$att,"it's the only path attribute",$i,$elt_name,$elt_id);
      next CYCLE_ATTS;
    }

    if ($args{'clip_atts'} eq "delete" &&
	$elt->parent('clipPath') && !($att~~@clip_atts)) {

	&del_att($elt,$att,"it's not a clipPath used attribute",$i,$elt_name,$elt_id);
	next CYCLE_ATTS;
    }

    if ($args{'inkdefault_atts'} eq "delete" &&
	$elt_name eq "filter" &&
	$att eq "color-interpolation-filters" &&
	$elt->att($att) eq "sRGB") {

      &del_att($elt,$att,"\"$att_val\" is the default value for this attribute",$i,$elt_name,$elt_id);
      next CYCLE_ATTS;
    }
    if ($args{'units_px'} eq "yes" &&
	$att_val=~ /^($number)($unit)$|^($scinumber)($unit)$/ &&
	$2 ne "px") {
      my $old_att = $att_val;

      $att_val = $actual_width if ($elt_name eq "svg" && $att eq "width");
      $att_val = $actual_height if ($elt_name eq "svg" && $att eq "height");
      $att_val = &units_px($elt,$att,$1,$2) unless ($elt_name eq "svg");
      if ("$att_val" ne "$old_att") {
	$elt->set_att($att => $att_val);
	&crt_att($elt,$att,"\"$old_att\" => \"$att_val\"",$i,$elt_name,$elt_id);
      }
    }

    if ($args{'round_numbers'} eq "yes" &&
	$elt_name ne "svg" &&
	$att_val=~ /\./ &&
	$att_val=~ /^($number)($unit)?$/) {

      my $number = $1;
      my $unit = $2;
      my $old_att = $att_val;
      if ($att=~ /[Hh]eight$|[Ww]idth$|^[xy]$|^[cdfr][xy]$|^[xy][12]$|^r$|^ref[XY]$/) {

	$att_val = &round_num($number,$unit,$args{'dp_d'});
      } else {

	$att_val = &round_num($number,$unit,$args{'dp_att'});
      }
      if ("$att_val" ne "$old_att") {
	$elt->set_att($att => $att_val);
	&crt_att($elt,$att,"\"$old_att\" => \"$att_val\"",$i,$elt_name,$elt_id);
      }
    }

    if ($att eq "clip-rule" &&
	!($elt_name eq "clipPath" || $elt->children('clipPath') || $elt->parent('clipPath'))) {

      &del_att($elt,'clip-rule',"in this case this is unused attribute",$i,$elt_name,$elt_id);
      next CYCLE_ATTS;	
    }

    if ($args{'default_atts'} eq "delete" && exists($default_atts{$att})) {

      if ($att eq "lighting-color" &&
	  $att_val=~ /^white$|^rgb\s*\(\s*(255|100%)\s*,\s*(255|100%)\s*,\s*(255|100%)\s*\)$|^#fff$/i) {

	$att_val = "#ffffff";
      }
      elsif ($att=~ /^color$|^fill$|^flood-color$|^stop-color$|^solid-color$/ &&
	     $att_val=~ /^black$|^rgb\s*\(\s*0%?\s*,\s*0%?\s*,\s*0%?\s*\)$|^#000$/i) {

	$att_val = "#000000";
      }
      if ($att_val eq $default_atts{$att} &&
	  $att eq "fill" &&
	  $elt_name~~@keep_fill &&
	  $elt->parent('defs')) {

	next CYCLE_ATTS;
      }
      if ($att_val eq $default_atts{$att} &&
	  $att eq "fill" &&
	  $elt->parent('g[@fill]')) {

	next CYCLE_ATTS;
      }
      if ($att_val eq $default_atts{$att} &&
	  $att eq "stroke" &&
	  $elt->parent('g[@stroke]')) {

	next CYCLE_ATTS;
      }

      if ($att_val eq $default_atts{$att}) {

	&del_att($elt,$att,"\"$att_val\" is the default value for this attribute",$i,$elt_name,$elt_id);
	next CYCLE_ATTS;
      }
    }

    if ($args{'default_atts'} eq "delete") {

      if ($elt_name eq 'linearGradient') {
	if (($att=~ /^x1$|^y1$|^y2$/ && $att_val=~ /^0$unit?$/) ||
	    ($att eq "x2" && $att_val eq '100%') ||
	    ($att eq "gradientUnits" && $att_val eq 'objectBoundingBox') ||
	    ($att eq "spreadMethod" && $att_val eq 'pad')) {

	  &del_att($elt,$att,"\"$att_val\" is the default value for this attribute",$i,$elt_name,$elt_id);
	  next CYCLE_ATTS;
	}
      }
      if ($elt_name eq 'radialGradient') {
	if (($att eq "gradientUnits" && $att_val eq 'objectBoundingBox') ||
	    ($att eq "spreadMethod" && $att_val eq 'pad')) {

	  &del_att($elt,$att,"\"$att_val\" is the default value for this attribute",$i,$elt_name,$elt_id);
	  next CYCLE_ATTS;
	}
      }

      if ($elt_name eq 'pattern') {
	if (($att=~ /^x$|^y$/ && $att_val eq '0') ||
	    ($att eq "patternUnits" && $att_val eq 'objectBoundingBox') ||
	    ($att eq "patternContentUnits" && $att_val eq 'userSpaceOnUse')) {

	  &del_att($elt,$att,"\"$att_val\" is the default value for this attribute",$i,$elt_name,$elt_id);
	  next CYCLE_ATTS;
	}
      }
      if ($elt_name eq 'rect' &&
	  ($att=~ /^x$|^y$/ && $att_val == 0)) {

	&del_att($elt,$att,"\"$att_val\" is the default value for this attribute",$i,$elt_name,$elt_id);
	next CYCLE_ATTS;
      }

      if ($elt_name=~ /^circle$|^ellipse$/ &&
	  ($att=~ /^cx$|^cy$/ && $att_val == 0)) {

	&del_att($elt,$att,"\"$att_val\" is the default value for this attribute",$i,$elt_name,$elt_id);
	next CYCLE_ATTS;
      }

      if ($elt_name eq 'line' &&
	  ($att=~ /^x1$|^x2$|^y1$|^y2$/ && $att_val == 0)) {

	&del_att($elt,$att,"\"$att_val\" is the default value for this attribute",$i,$elt_name,$elt_id);
	next CYCLE_ATTS;
      }

    }

    if ($args{'grad_coord'} eq "yes" &&
	$args{'singly_grads'} ne "yes") {
      if ($elt_name eq "linearGradient" &&
	  $att eq "x2") {

	my $x1 = $elt->att('x1');
	$x1 = 0 unless ($x1);
	if ("$att_val" eq "$x1") {

	  &del_att($elt,"x1","\'x1\' and \'x2\' are equal",$i,$elt_name,$elt_id);
	  &crt_att($elt,$att,"\"$att_val\" => \"0\"",$i,$elt_name,$elt_id);
	  $elt->set_att($att => '0');
	  next CYCLE_ATTS;
	}
      }

      if ($elt_name eq "linearGradient" &&
	  $att eq "y2") {

	my $y1 = $elt->att('y1');
	$y1 = 0 unless ($y1);
	if ("$att_val" eq "$y1") {

	  &del_att($elt,"y1","\'y1\' and \'y2\' are equal",$i,$elt_name,$elt_id);
	  &del_att($elt,"y2","\'y1\' and \'y2\' are equal",$i,$elt_name,$elt_id);
	  next CYCLE_ATTS;
	}
      }

      if ($elt_name eq "radialGradient" &&
	  $att eq "fx") {

	my $cx = $elt->att('cx');
	$cx = "50%" unless ($cx);
	if ("$att_val" eq "$cx") {

	  &del_att($elt,$att,"\'fx\' and \'cx\' are equal",$i,$elt_name,$elt_id);
	  next CYCLE_ATTS;
	}
      }

      if ($elt_name eq "radialGradient" &&
	  $att eq "fy") {

	my $cy = $elt->att('cy');
	$cy = "50%" unless ($cy);
	if ("$att_val" eq "$cy") {

	  &del_att($elt,$att,"\'fy\' and \'cy\' are equal",$i,$elt_name,$elt_id);
	  next CYCLE_ATTS;
	}
      }
    }

    if ($args{'color_rrggbb'} eq "yes" &&
	$att=~ /^fill$|^stroke$|color$/ &&
	$att_val!~ /^#([\da-f]){3}$|^url\(#/) {
      my $old_att = $att_val;
      $att_val = &color_rrggbb($att_val,$args{'color_rgb'});
      if ("$att_val" ne "$old_att") {
	$elt->set_att($att => $att_val);
	&crt_att($elt,$att,"\"$old_att\" => \"$att_val\"",$i,$elt_name,$elt_id);
      }
      next CYCLE_ATTS;
    }

    if ($args{'spf_space'} eq "delete" &&
	$att=~ /^d$|^points$/) {
      $att_val=~ s/^\s+|\s+$//g;

      $att_val=~ s/\s{2,}/ /g;

      $att_val=~ s/\s*([mlhvcsqtaz])\s*/$1/gi;

      $att_val=~ s/($all_num,$all_num)\s+-/$1-/g;

      $elt->set_att($att => $att_val);
    }

    if ($args{'round_numbers'} eq "yes" &&
	$att=~ /^d$|^points$/ &&
	$att_val=~ /\./) {

      my $data;

      while ($att_val || $att_val eq "0") {

	if ($att_val=~ /^($number)/) {
	  $att_val=~ s/^($number)//;
	  my $num = $1;
	  $num += 0 if ($num=~ /\.0+$/);
	  $num = &round_num($num,'',$args{'dp_d'}) if ($num=~ /\./);
	  $data = $data.$num;
	}
	elsif ($att_val=~ /^($scinumber)/) {
	  $att_val=~ s/^($scinumber)//;
	  $data = $data.$1;
	}
	elsif ($att_val=~ /^\./) {
	  $att_val=~ s/^\.//;
	  $data = $data;
	}
	elsif ($att_val=~ /^[a-z,\s]/i) {
	  $att_val=~ s/^([a-z,\s])//i;
	  $data = $data.$1;
	}
      }

      $elt->set_att($att => $data);
      &crt_att($elt,$att,"all numbers was rounded",$i,$elt_name,$elt_id);
      next CYCLE_ATTS;
    }

    if ($att=~ /^transform$|^gradientTransform$|^patternTransform$/) {
      my $old_att = $att_val;
      if ($att_val=~ /^$matrix$/) {
	$att_val = "translate($5,$6)" if ($1==1 && $2==0 && $3==0 && $4==1);
	$att_val = "scale($1,$4)" if ($2==0 && $3==0 && $5==0 && $6==0);
	if ($2==0 && $3==0 &&
	    $1==$4 && $5==0 && $6==0 &&
	    (&acos($1)-&asin($2))<0.01) {

	  my $angle = &asin($2);
	  $att_val = "rotate($angle)";
	}
	elsif ($2!=0 && $3!=0 && $2/$3==-1 &&
	    $1==$4 && $5==0 && $6==0 &&
	    (&acos($1)-&asin($2))<0.01) {

	  my $angle = &asin($2);
	  $att_val = "rotate($angle)";
	}
	if ($1==1 && $2==0 && $4==1 && $5==0 && $6==0) {

	  my $angle = &atan($3);
	  $att_val = "skewX($angle)";
	}
	if ($1==1 && $3==0 && $4==1 && $5==0 && $6==0) {

	  my $angle = &atan($2);
	  $att_val = "skewY($angle)";
	}
      }
	$att_val = &trans_matrix($att_val) if ($args{'conc_trans'} eq "yes" && $att_val=~ /\).+\)/);

      if ($att_val=~ /^$translate$/) {

	(my $a,my $b) = ($1,$2);

	$a = &round_num($a,'',$args{'dp_d'}) if ($args{'dp_d'} && $a!~ /^$scinumber$/ && $a=~ /\./);
	$b = &round_num($b,'',$args{'dp_d'}) if ($args{'dp_d'} && $b && $b!~ /^$scinumber$/ && $b=~ /\./);

	$att_val = "translate($a,$b)" if ($b);
	$att_val = "translate($a)" if (!$b || $b == 0);
      }
      if ($att_val=~ /^$scale$/) {

	(my $a,my $b) = ($1,$2);

	$a = &round_num($a,'',$args{'dp_att'}) if ($args{'dp_att'} && $a!~ /^$scinumber$/ && $a=~ /\./);
	$b = &round_num($b,'',$args{'dp_att'}) if ($args{'dp_att'} && $b && $b!~ /^$scinumber$/ && $b=~ /\./);

	$att_val = "scale($a,$b)" if ($b && $a != $b);
	$att_val = "scale($a)" if (!$b || $a == $b);
      }
      if ($att_val=~ /^$rotate$/) {

	(my $a,my $b,my $c) = ($1,$2,$3);

	$a = &opt_angle($a) if ($a < 0 || $a > 360);
	$a = 0 if ($a == 360);

	$a = &round_num($a,'',$args{'dp_att'}) if ($args{'dp_att'} && $a!~ /^$scinumber$/ && $a=~ /\./);
	$b = &round_num($b,'',$args{'dp_d'}) if ($args{'dp_d'} && $b && $b!~ /^$scinumber$/ && $b=~ /\./);
	$c = &round_num($c,'',$args{'dp_d'}) if ($args{'dp_d'} && $c && $c!~ /^$scinumber$/ && $c=~ /\./);

	$att_val = "rotate($a,$b,$c)" if ($b && $c);
	$att_val = "rotate($a)" unless ($b && $c);
      }
      if ($att_val=~ /^$skewX$/) {

	my $a = $1;

	$a = &opt_angle($a) if ($a < 0 || $a > 360);
	$a = 0 if ($a == 360);
	$a = &round_num($a,'',$args{'dp_att'}) if ($args{'dp_att'} && $a!~ /^$scinumber$/ && $a=~ /\./);

	$att_val = "skewX($a)";
      }
      if ($att_val=~ /^$skewY$/) {

	my $a = $1;

	$a = &opt_angle($a) if ($a < 0 || $a > 360);
	$a = 0 if ($a == 360);
	$a = &round_num($a,'',$args{'dp_att'}) if ($args{'dp_att'} && $a!~ /^$scinumber$/ && $a=~ /\./);

	$att_val = "skewY($a)";
      }
      if ($att_val=~ /^$matrix$/) {

	(my $a,my $b,my $c,my $d,my $e,my $f) = ($1,$2,$3,$4,$5,$6);

	if ($args{'dp_tr'}) {
	  $a = &round_num($a,'',$args{'dp_tr'}) if ($a!~ /^$scinumber$/ && $a=~ /\./);
	  $b = &round_num($b,'',$args{'dp_tr'}) if ($b!~ /^$scinumber$/ && $b=~ /\./);
	  $c = &round_num($c,'',$args{'dp_tr'}) if ($c!~ /^$scinumber$/ && $c=~ /\./);
	  $d = &round_num($d,'',$args{'dp_tr'}) if ($d!~ /^$scinumber$/ && $d=~ /\./);
	  $e = &round_num($e,'',$args{'dp_tr'}) if ($e!~ /^$scinumber$/ && $e=~ /\./);
	  $f = &round_num($f,'',$args{'dp_tr'}) if ($f!~ /^$scinumber$/ && $f=~ /\./);
	}

	$att_val = "matrix($a,$b,$c,$d,$e,$f)";
      }

      if (&trans_matrix($att_val) eq "matrix(1,0,0,1,0,0)") {

	&del_att($elt,$att,"it specifies the identity transformation",$i,$elt_name,$elt_id);
	next CYCLE_ATTS;
      }
      if ("$att_val" ne "$old_att") {
	$elt->set_att($att => $att_val);
	&crt_att($elt,$att,"\"$att_val\"",$i,$elt_name,$elt_id);
      }
      next CYCLE_ATTS;
    }

    if ($att_val =~ /^url\(#(.+)\)$/ ||
	($att eq "xlink:href" && $att_val =~ /^#(.+)$/)) {

      if (exists($comp_elts{$1})) {
	$elt->set_att($att => "url(#$comp_elts{$1})") if ($att ne "xlink:href");
	$elt->set_att($att => "#$comp_elts{$1}") if ($att eq "xlink:href");
      }
    }
  }
    print "\n\n" if ($i != 0 && $ARGV[2] && $ARGV[2] ne "quiet");
}

print colored ("\nPOST-PROCESSING\n\n", 'bold blue underline') if ($ARGV[2] && $ARGV[2] ne "quiet");

if ($args{'dupl_defs'} eq "yes" && $defs) {
  %comp_elts = ();
  foreach my $elt ($defs->children) {
    my $elt_name = $elt->name;

    my $elt_id = $elt->id;
    if ($elt_id) {
      if ($elt_name=~ /^linearGradient$|^radialGradient$/ &&
	  $elt->children('stop')) {

	my @desc_stop = ();
	foreach ($elt->children('stop')) {

	  my $stop = &desc_elt($_);

	  if ($stop~~@desc_stop) {
	    $_->delete;
	  } else {
	    push @desc_stop, $stop;
	  }
	}
      }
      my $desc = &desc_elt($elt);
      if (exists($desc_elts{$desc})) {
	$comp_elts{$elt_id} = $desc_elts{$desc};
	$elt->delete;

	if ($ARGV[2] && $ARGV[2] ne "quiet") {

	print colored (" <$elt_name", 'bold red');
	print " id=\"$elt_id\" (it's a duplicated element)\n\n";
	}
      } else {
	$desc_elts{$desc} = $elt_id;
      }
    }
  }
  %desc_elts = ();
  foreach my $elt ($root->descendants) {
    foreach my $att ($elt->att_names) {
    my $att_val = $elt->att($att);
      if ($att_val =~ /^url\(#(.+)\)$/ ||
	  ($att eq "xlink:href" && $att_val =~ /^#(.+)$/)) {

	if (exists($comp_elts{$1})) {
	  $elt->set_att($att => "url(#$comp_elts{$1})") if ($att ne "xlink:href");
	  $elt->set_att($att => "#$comp_elts{$1}") if ($att eq "xlink:href");
	}
      }
    }
  }
}
my @lingrad_atts = ('gradientUnits','spreadMethod','gradientTransform','x1','y1','x2','y2');
my @radgrad_atts = ('gradientUnits','spreadMethod','gradientTransform','fx','fy','cx','cy','r');
my %xlinks;
if ($args{'singly_grads'} eq "yes" && $defs) {
  foreach my $elt ($defs->get_xpath('./linearGradient[@xlink:href]'), $defs->get_xpath('./radialGradient[@xlink:href]')) {

    my $link = $elt->att('xlink:href');

    if (exists $xlinks{$link}) {
      $xlinks{$link} = 0;
    } else {
      $xlinks{$link} = 1;
    }
  }
  foreach my $elt ($defs->get_xpath('./linearGradient[@xlink:href]'), $defs->get_xpath('./radialGradient[@xlink:href]')) {

    my $link = $elt->att('xlink:href');

    if (exists $xlinks{$link} &&
	$xlinks{$link} == 1 &&
	!(substr($link, 1)~~@out_link)) {
      my $elt_name = $elt->name;
      my $elt_id = $elt->id;

      $link = substr($link, 1);

      if ($ARGV[2] && $ARGV[2] ne "quiet") {

	print colored (" <$elt_name", 'bold green');
	print " id=$elt_id:\n";
	print color 'bold red'; print "  <",$twig->elt_id($link)->name; print color 'reset';
	print " id=$link (it's the singly referenced gradient)\n\n";
      }

      foreach ($twig->elt_id($link)->children) {

	$_->move(last_child => $elt);
      }

      while((my $att, my $att_val) = each %{$twig->elt_id($link)->atts}) {

	if ($elt_name eq "linearGradient" &&
	    $att~~@lingrad_atts &&
	    !($att eq "gradientTransform" && $elt_id~~@recalc_grads) &&
	    !($elt->att($att))) {

	  $elt->set_att($att => $att_val); 
	}

	if ($elt_name eq "radialGradient" &&
	    $att~~@radgrad_atts &&
	    !($att eq "gradientTransform" && $elt_id~~@recalc_grads) &&
	    !($elt->att($att))) {

	  $elt->set_att($att => $att_val);
	}
      }

      $twig->elt_id($link)->delete;
      $elt->del_att('xlink:href');
    }
  }
}

if ($args{'erase_groups'} eq "yes") {

  foreach my $elt ($root->get_xpath('//g')) {

    my $elt_id = $elt->id;
    if ($elt->children &&
	$elt->all_children_are('g')) {

      foreach ($elt->children) {

	$_->delete unless ($_->children);
      }

      $elt->delete unless ($elt->children);
    }
    unless ($elt->parent('switch')) {

      if ($elt->att_nb == 1 && $elt->id &&
	  !($elt_id ~~ @ref_id) && $args{'protect_id'} ne "yes") {

	$elt->erase;
      } elsif ($elt->has_no_atts) {

	$elt->erase;
      }
    }
  }
}

if ($args{'sort_defs'} eq "yes" && 
    $root->children('defs')) {

  foreach my $defs_elt (@defs_elts) {
    if ($defs_elt eq "linearGradient") {
      foreach ($defs->get_xpath('//linearGradient[not @xlink:href]')) {
	$_->move(last_child => $defs);
      }
      foreach ($defs->get_xpath('//linearGradient[@xlink:href]')) {
	$_->move(last_child => $defs);
      }
    }
    elsif ($defs_elt eq "radialGradient") {
      foreach ($defs->get_xpath('//radialGradient[not @xlink:href]')) {
	$_->move(last_child => $defs);
      }
      foreach ($defs->get_xpath('//radialGradient[@xlink:href]')) {
	$_->move(last_child => $defs);
      }
    }
    elsif ($defs_elt eq "filter") {
      foreach ($defs->get_xpath('//filter[not @height and not @width]')) {
	$_->move(last_child => $defs);
      }
      foreach ($defs->get_xpath('//filter[@height or @width]')) {
	$_->move(last_child => $defs);
      }
    }
    else {
      foreach ($defs->get_xpath("//$defs_elt")) {
	$_->move(last_child => $defs);
      }
    }
  }
}

my $size_final = length($twig->sprint);
my $elts_final = scalar($root->descendants_or_self);
my $atts_final = 0;

foreach my $elt ($root->descendants_or_self) {
  $atts_final+=($elt->att_nb);
}
if ($ARGV[2] && $ARGV[2] ne "quiet") {

  print colored (" The final file size is $size_final bytes\n", 'bold');

  print colored (" The final number of elements is $elts_final\n", 'bold');

  print colored (" The final number of attributes is $atts_final\n\n", 'bold');
} else {
  print " The final file size is $size_final bytes\n";
  print " The final number of elements is $elts_final\n";
  print " The final number of attributes is $atts_final\n\n";
}
print colored ("\nREPORT\n\n", 'bold blue underline') if ($ARGV[2] && $ARGV[2] ne "quiet");
my $size_diff = sprintf("%.2f", $size_final/$size_initial*100);
my $elts_diff = $elts_initial-$elts_final;
my $atts_diff = $atts_initial-$atts_final;

if ($ARGV[2] && $ARGV[2] ne "quiet") {

  print colored (" The new file size is $size_diff%\n", 'bold');

  print colored (" The number of removed elements is $elts_diff\n", 'bold');

  print colored (" The number of removed attributes is $atts_diff\n\n", 'bold');
} else {
  print " The new file size is $size_diff%\n";
  print " The number of removed elements is $elts_diff\n";
  print " The number of removed attributes is $atts_diff\n\n";
}

$twig->set_pretty_print($args{'pretty_print'});
$twig->set_indent(' ' x $args{'indent'});
$twig->set_empty_tag_style($args{'empty_tags'});
$twig->set_quote($args{'quote'});
$twig->print_to_file($ARGV[0]);
$twig->purge;
exit;
__END__