3 # Render SVG files containing one or more images into an ICO or BMP.
5 # Copyright (C) 2010 Joel Holdsworth
7 # This library is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU Lesser General Public
9 # License as published by the Free Software Foundation; either
10 # version 2.1 of the License, or (at your option) any later version.
12 # This library is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 # Lesser General Public License for more details.
17 # You should have received a copy of the GNU Lesser General Public
18 # License along with this library; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 # Parse the parameters
28 my $svgFileName = $ARGV[0];
29 my $outFileName = $ARGV[1];
31 die "Cannot open SVG file" unless defined($svgFileName);
32 die "Cannot open output file" unless defined($outFileName);
34 $outFileName =~ m/(.*)\.(.*)/;
37 die "Only BMP and ICO outputs are supported" unless $ext eq "bmp" or $ext eq "ico";
39 my $renderedSVGFileName = "$svgFileName.png";
42 # Get the programs from the environment variables
43 my $convert = $ENV{"CONVERT"} || "convert";
44 my $rsvg = $ENV{"RSVG"} || "rsvg";
45 my $icotool = $ENV{"ICOTOOL"} || "icotool";
50 unlink $renderedSVGFileName;
51 unlink $_ foreach(@pngFiles);
54 $SIG{"INT"} = "cleanup";
55 $SIG{"HUP"} = "cleanup";
56 $SIG{"TERM"} = "cleanup";
57 $SIG{"__DIE__"} = "cleanup";
59 # run a shell command and die on error
63 system(@args) == 0 or die "@args failed: $?";
68 my($expat, $element, %attr) = @_;
70 # Parse the id for icon/bitmap render directives
72 return unless defined($id);
78 return unless $id =~ /icon:(\d*)-(\d*)/;
81 } elsif($ext eq "bmp") {
82 return unless $id =~ /bitmap:(\d*)-(\d*)/;
87 return unless defined($size) and defined($depth);
89 warn "Unexpected icon depth" unless
90 $depth == 4 or $depth == 8 or $depth == 24 or $depth == 32;
91 my $pngFileName = "$outName-$size-$depth.png";
93 if($element eq "svg") {
95 # The whole file is tagged
96 copy($renderedSVGFileName, $pngFileName) or die "File could not be copied";
98 } elsif($element eq "rect") {
100 # Extract SVG vector images
103 my $width = $attr{'width'};
104 my $height = $attr{'height'};
106 if(defined($x) and defined($x)) {
107 if($x =~ /\d*/ and $y =~ /\d*/) {
108 shell $convert, $renderedSVGFileName, "-crop", "${width}x${height}+$x+$y", $pngFileName;
112 } elsif($element eq "image" ) {
114 # Extract Base64 encoded PNG data to files
115 my $xlinkHref = $attr{'xlink:href'};
116 if(defined($xlinkHref)) {
117 $xlinkHref =~ /data:image\/png;base64(.*)/;
118 my $imageEncodedData = $1;
119 if(defined $imageEncodedData) {
120 open(FILE, '>' . $pngFileName) or die "$!";
121 print FILE decode_base64($imageEncodedData);
129 push(@pngFiles, $pngFileName);
132 # Render the SVG image
133 shell $rsvg, $svgFileName, $renderedSVGFileName;
135 # Render the images in the SVG
136 my $parser = new XML::Parser(
137 Handlers => {Start => \&svg_element_start});
138 $parser->parsefile("$svgFileName");
140 # If no render directives were found, take the full image as-is
142 my $pngFileName = "bmp$renderedSVGFileName";
143 copy($renderedSVGFileName, $pngFileName) or die "File could not be copied";
144 push(@pngFiles, $pngFileName);
147 # Combine the renderings into the output file
150 # Place images into the ICO
151 shell $icotool, "-c", "-o", $outFileName, @pngFiles;
153 } elsif($ext eq "bmp") {
155 # Only the first image becomes the final BMP
156 my $pngFile = $pngFiles[0];
157 $pngFile =~ /.*-\d*-(\d*)\.png/;
160 # Convert it into a bmp
162 shell $convert, "png:$pngFile", "+matte", $outFileName;
164 shell $convert, "png:$pngFile", $outFileName;
169 # Delete the intermediate images