From: Marius Gavrilescu <marius@ieval.ro>
Date: Fri, 24 Feb 2017 00:27:47 +0000 (+0000)
Subject: Try to fix duplicate IDs
X-Git-Tag: 0.002~2
X-Git-Url: http://git.ieval.ro/?a=commitdiff_plain;h=014f8c27d1123b5d338e675dcf5d06b4b269ba03;p=svg-spritemaker.git

Try to fix duplicate IDs
---

diff --git a/lib/SVG/SpriteMaker.pm b/lib/SVG/SpriteMaker.pm
index c27a301..e24b5d9 100644
--- a/lib/SVG/SpriteMaker.pm
+++ b/lib/SVG/SpriteMaker.pm
@@ -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/>
diff --git a/svg-spritemaker b/svg-spritemaker
index 3762063..22ed501 100755
--- a/svg-spritemaker
+++ b/svg-spritemaker
@@ -60,6 +60,16 @@ example in the SYNOPSIS).
 
 =back
 
+If an ID is shared between two or more input files, this program 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/>
diff --git a/t/SVG-SpriteMaker.t b/t/SVG-SpriteMaker.t
index b83a939..4c37199 100644
--- a/t/SVG-SpriteMaker.t
+++ b/t/SVG-SpriteMaker.t
@@ -2,11 +2,15 @@
 use strict;
 use warnings;
 
-use Test::More tests => 3;
+use Test::More tests => 5;
 BEGIN { use_ok('SVG::SpriteMaker') };
 
+# Don't warn, we intentionally have a duplicate ID
+local $ENV{SVG_SPRITEMAKER_NO_DUPLICATE_WARNINGS} = 1;
 my $sprite = make_sprite frag => <t/*.svg>;
 my @elements = $sprite->getFirstChild->getChildren;
 
 ok $sprite->getElementByID('frag-rect'), 'sprite contains #frag-rect';
 ok $sprite->getElementByID('frag-circle'), 'sprite contains #frag-circle';
+like $sprite->xmlify, qr/id="thing"/, 'sprite contains #thing';
+like $sprite->xmlify, qr/id="thing_"/, 'sprite contains #thing_';
diff --git a/t/circle.svg b/t/circle.svg
index 0e1ecb9..2affb79 100644
--- a/t/circle.svg
+++ b/t/circle.svg
@@ -1,3 +1,3 @@
 <svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 10 10">
-	<circle fill="red" stroke="none" cx="5" cy="5" r="4"/>
+	<circle fill="red" id="thing" stroke="none" cx="5" cy="5" r="4"/>
 </svg>
diff --git a/t/rect.svg b/t/rect.svg
index 955293d..803f33f 100644
--- a/t/rect.svg
+++ b/t/rect.svg
@@ -1,3 +1,3 @@
 <svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 10 10">
-	<rect fill="blue" stroke="none" x="1" y="1" width="8" height="8" rx="1" ry="1"/>
+	<rect fill="blue" id="thing" stroke="none" x="1" y="1" width="8" height="8" rx="1" ry="1"/>
 </svg>