tools: Modified the ICO render script to also render BMPs.
[wine] / tools / buildimage
1 #! /usr/bin/perl -w
2 #
3 # Render SVG files containing one or more images into an ICO or BMP.
4 #
5 # Copyright (C) 2010 Joel Holdsworth
6 #
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.
11 #
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.
16 #
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
20
21 use strict;
22 use warnings;
23 use XML::Parser;
24 use MIME::Base64;
25 use File::Copy;
26
27 # Parse the parameters
28 my $svgFileName = $ARGV[0];
29 my $outFileName = $ARGV[1];
30
31 die "Cannot open SVG file" unless defined($svgFileName);
32 die "Cannot open output file" unless defined($outFileName);
33
34 $outFileName =~ m/(.*)\.(.*)/;
35 my $outName = $1;
36 my $ext = lc($2);
37 die "Only BMP and ICO outputs are supported" unless $ext eq "bmp" or $ext eq "ico";
38
39 my $renderedSVGFileName = "$svgFileName.png";
40 my @pngFiles;
41
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";
46
47 # Be ready to abort
48 sub cleanup()
49 {
50     unlink $renderedSVGFileName;
51     unlink $_ foreach(@pngFiles);
52 }
53
54 $SIG{"INT"} = "cleanup";
55 $SIG{"HUP"} = "cleanup";
56 $SIG{"TERM"} = "cleanup";
57 $SIG{"__DIE__"} = "cleanup";
58
59 # run a shell command and die on error
60 sub shell(@)
61 {
62     my @args = @_;
63     system(@args) == 0 or die "@args failed: $?";
64 }
65
66 sub svg_element_start
67 {
68     my($expat, $element, %attr) = @_;
69
70     # Parse the id for icon/bitmap render directives
71     my $id = $attr{'id'};
72     return unless defined($id);
73
74     my $size = 0;
75     my $depth = 0;
76
77     if($ext eq "ico") {
78         return unless $id =~ /icon:(\d*)-(\d*)/;
79         $size = $1;
80         $depth = $2;
81     } elsif($ext eq "bmp") {
82         return unless $id =~ /bitmap:(\d*)-(\d*)/;
83         $size = $1;
84         $depth = $2;
85     }
86
87     return unless defined($size) and defined($depth);
88
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";
92
93     if($element eq "svg") {
94
95         # The whole file is tagged
96         copy($renderedSVGFileName, $pngFileName) or die "File could not be copied";
97
98     } elsif($element eq "rect") {
99
100         # Extract SVG vector images
101         my $x = $attr{'x'};
102         my $y = $attr{'y'};
103         my $width = $attr{'width'};
104         my $height = $attr{'height'};
105
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;
109             }
110         }
111
112     } elsif($element eq "image" ) {
113
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);
122                 close FILE;
123             }
124         }
125     } else {
126         return;
127     }
128
129     push(@pngFiles, $pngFileName);
130 }
131
132 # Render the SVG image
133 shell $rsvg, $svgFileName, $renderedSVGFileName;
134
135 # Render the images in the SVG
136 my $parser = new XML::Parser(
137     Handlers => {Start => \&svg_element_start});
138 $parser->parsefile("$svgFileName");
139
140 # If no render directives were found, take the full image as-is
141 unless(@pngFiles) {
142     my $pngFileName = "bmp$renderedSVGFileName";
143     copy($renderedSVGFileName, $pngFileName) or die "File could not be copied";
144     push(@pngFiles, $pngFileName);
145 }
146
147 # Combine the renderings into the output file
148 if($ext eq "ico") {
149
150     # Place images into the ICO
151     shell $icotool, "-c", "-o", $outFileName, @pngFiles;
152
153 } elsif($ext eq "bmp") {
154
155     # Only the first image becomes the final BMP
156     my $pngFile = $pngFiles[0];
157     $pngFile =~ /.*-\d*-(\d*)\.png/;
158     my $depth = $1;
159
160     # Convert it into a bmp
161     if($depth == 24) {
162         shell $convert, "png:$pngFile", "+matte", $outFileName;
163     } else {
164         shell $convert, "png:$pngFile", $outFileName;
165     }
166
167 }
168
169 # Delete the intermediate images
170 cleanup();