Try to fix duplicate IDs
[svg-spritemaker.git] / lib / SVG / SpriteMaker.pm
index c27a301e68b493d797ccbc14bd804c9d7647d43e..e24b5d9e8931166f89f977e2d2e99cc85cd11f47 100644 (file)
@@ -27,6 +27,7 @@ sub make_sprite {
        my $parser = SVG::Parser->new;
        @images = map {[ $sub->($_) => $parser->parse_file($_) ]} @images;
        my ($x, $mh) = (0, 0);
+       my %ids = map { $_->[0] => 1 } @images; # start with image names
 
        for (@images) {
                my ($img, $doc) = @$_;
@@ -38,6 +39,43 @@ sub make_sprite {
                $svg->attr(version => undef);
                my $view = $sprite->view(id => $img, viewBox => "$x 0 $w $h");
                $x += $w + 5;
+
+               my @all_elems = $svg->getElements;
+               my @duplicate_ids;
+               for my $elem (@all_elems) {
+                       my $id = $elem->attr('id');
+                       next unless $id;
+                       if ($ids{$id}) {
+                               push @duplicate_ids, $id;
+                       } else {
+                               $ids{$id} = 1;
+                       }
+               }
+
+               warn <<"EOF" if @duplicate_ids && !$ENV{SVG_SPRITEMAKER_NO_DUPLICATE_WARNINGS};
+Some IDs (@duplicate_ids) in $img also exist in previous images.
+Trying to fix automatically, but this might produce a broken SVG.
+Fix IDs manually to avoid incorrect output.
+EOF
+
+               for my $oid (@duplicate_ids) {
+                       my $nid = $oid;
+                       $nid .= '_' while $ids{$nid};
+                       $svg->getElementByID($oid)->attr(id => $nid);
+                       for my $elem (@all_elems) {
+                               my %attribs = %{$elem->getAttributes};
+                               for my $key (keys %attribs) {
+                                       if ($attribs{$key} =~ /#$oid\b/) {
+                                               $attribs{$key} =~ s/#$oid\b/#$nid/g;
+                                               $elem->attr($key => $attribs{$key});
+                                       }
+                               }
+                               if ($elem->cdata =~ /#$oid\b/) {
+                                       $elem->cdata($elem->cdata =~ s/#$oid\b/#$nid/gr);
+                               }
+                       }
+               }
+
                $view->getParent->insertAfter($svg, $view);
        }
 
@@ -108,6 +146,16 @@ used:
 
 where I<$prefix> is the value of the first argument.
 
+If an ID is shared between two or more input files, this module will
+try to rename each occurence except for the first one. This operation
+might have false positives (attributes/cdatas that are mistakenly
+identified to contain the ID-to-be-renamed) and false negatives
+(attributes/cdatas that actually contain the ID-to-be-renamed but this
+is missed by the module), and as such SVG::SpriteMaker will warn if
+duplicate IDs are detected. You can suppress this warning by setting
+the C<SVG_SPRITEMAKER_NO_DUPLICATE_WARNINGS> environment variable to a
+true value.
+
 =head1 SEE ALSO
 
 L<svg-spritemaker>, L<https://css-tricks.com/svg-fragment-identifiers-work/>
This page took 0.009826 seconds and 4 git commands to generate.