buildimage 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  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. use strict;
  21. use warnings;
  22. use XML::LibXML;
  23. use MIME::Base64;
  24. use File::Copy;
  25. # Parse the parameters
  26. my $svgFileName = $ARGV[0];
  27. my $outFileName = $ARGV[1];
  28. die "Cannot open SVG file" unless defined($svgFileName);
  29. die "Cannot open output file" unless defined($outFileName);
  30. $outFileName =~ m/(.*)\.(.*)/;
  31. my $outName = $1;
  32. my $ext = lc($2);
  33. die "Only BMP, ICO and CUR outputs are supported" unless $ext eq "bmp" or $ext eq "ico" or $ext eq "cur";
  34. my $renderedSVGFileName = "$svgFileName.png";
  35. my @pngFiles;
  36. my @hotspot;
  37. # Get the programs from the environment variables
  38. my $convert = $ENV{"CONVERT"} || "convert";
  39. my $rsvg = $ENV{"RSVG"} || "rsvg-convert";
  40. my @icotool_args = ($ENV{"ICOTOOL"} || "icotool", "--create",
  41. $ext eq "cur" ? "--cursor" : "--icon", "-o", $outFileName);
  42. # Be ready to abort
  43. sub cleanup()
  44. {
  45. unlink $renderedSVGFileName;
  46. unlink $_ foreach(@pngFiles);
  47. }
  48. $SIG{"INT"} = "cleanup";
  49. $SIG{"HUP"} = "cleanup";
  50. $SIG{"TERM"} = "cleanup";
  51. $SIG{"__DIE__"} = "cleanup";
  52. my %label =
  53. (
  54. 'ico' => 'icon:(\d*)-(\d*)',
  55. 'cur' => 'cursor:(\d*)-(\d*)',
  56. 'bmp' => 'bitmap:(\d*)-(\d*)',
  57. );
  58. # run a shell command and die on error
  59. sub shell(@)
  60. {
  61. my @args = @_;
  62. system(@args) == 0 or die "@args failed: $?";
  63. }
  64. # add an image to the icon/cursor
  65. sub add_image($$)
  66. {
  67. my ($file, $size) = @_;
  68. if (defined $hotspot[$size])
  69. {
  70. my @coords = @{$hotspot[$size]};
  71. push @icotool_args, "--hotspot-x=$coords[0]", "--hotspot-y=$coords[1]";
  72. }
  73. push @icotool_args, $size >= 128 ? "--raw=$file" : $file;
  74. push @pngFiles, $file;
  75. }
  76. # Render the SVG image
  77. my @rsvgCmd;
  78. push(@rsvgCmd, $rsvg);
  79. push(@rsvgCmd, $svgFileName);
  80. push(@rsvgCmd, "-o") if ($rsvg eq "rsvg-convert");
  81. push(@rsvgCmd, $renderedSVGFileName);
  82. shell @rsvgCmd;
  83. # Render the images in the SVG
  84. my $xml = XML::LibXML->load_xml( location => $svgFileName );
  85. my $xc = XML::LibXML::XPathContext->new($xml);
  86. $xc->registerNs('x', 'http://www.w3.org/2000/svg');
  87. if ($ext eq "bmp")
  88. {
  89. foreach my $element ($xc->findnodes("/x:svg"))
  90. {
  91. next unless $element->{id} =~ /bitmap:(\d*)-(\d*)/;
  92. my $size = $1;
  93. my $depth = $2;
  94. if ($depth == 24) {
  95. shell $convert, $renderedSVGFileName, "+matte", $outFileName;
  96. } else {
  97. shell $convert, $renderedSVGFileName, $outFileName;
  98. }
  99. cleanup();
  100. exit(0);
  101. }
  102. }
  103. # fetch hotspot rectangles for the various sizes
  104. if ($ext eq "cur")
  105. {
  106. foreach my $element ($xc->findnodes("/x:svg/x:rect"))
  107. {
  108. next unless $element->{id} =~ /hotspot:(\d*)/;
  109. $hotspot[$1] = [ $element->{x}, $element->{y} ];
  110. }
  111. }
  112. # extract rectangles from the rendered svg
  113. foreach my $element ($xc->findnodes("/x:svg/*[\@id]"))
  114. {
  115. next unless $element->{id} =~ /$label{$ext}/;
  116. my $size = $1;
  117. my $depth = $2;
  118. warn "Unexpected depth" unless
  119. $depth == 1 or $depth == 4 or $depth == 8 or $depth == 24 or $depth == 32;
  120. my $file = "$outName-$size-$depth.png";
  121. my $x = $element->{x};
  122. my $y = $element->{y};
  123. my $width = $element->{width};
  124. my $height = $element->{height};
  125. if ($element->{'xlink:href'})
  126. {
  127. # extract base64-encoded png image
  128. (my $data = $element->{'xlink:href'}) =~ s/data:image\/png;base64//;
  129. open FILE, ">$file" or die "$!";
  130. print FILE decode_base64($data);
  131. close FILE;
  132. }
  133. else
  134. {
  135. shell $convert, $renderedSVGFileName, "-crop", "${width}x${height}+$x+$y", "-depth", $depth, $file;
  136. }
  137. add_image( $file, $size );
  138. }
  139. die "no render directive found in $svgFileName" unless @pngFiles;
  140. shell @icotool_args;
  141. # Delete the intermediate images
  142. cleanup();