Perl script for gamma-correcting an SVG file
Here is a Perl script I wrote for gamma-correcting an SVG file. It handles both line art (regular SVG stuff) and embedded raster images. You must have the ImageMagick "convert" utility installed. The present version lightens and color-corrects an SVG that was created on a Mac so that it looks right on a PC, but it could easily be modified to serve a more general purpose. Once Perl extensions are implemented in Inkscape, I can hook it in there, but for now you just have to run it from the command line. I'm guessing that the long lines are going to get mangled somewhat as this goes out via the mailing list; I'd be happy to stick this in the Inkscape CVS tree if people think that would be appropriate and if I knew where was a good directory to put it.
I've only tested this on some of my own files. Please don't trust it blindly on your own files, and let me know if you encounter any bugs.
============================================================================================
#!/usr/bin/perl
#---------------------------------------------------------------------------------------------- # gamma_correct_svg # B. Crowell, crowellXX at lightandmatter.com (replace XX with last two digits of current year) # (c) 2004 B. Crowell # This program is available under version 2 of the GPL License, http://www.gnu.org/copyleft/gpl.html, # or, at your option, under the same license as Perl. # For information about this program, scroll down in the source code, or invoke the program # without any command-line arguments. #----------------------------------------------------------------------------------------------
use strict;
use MIME::Base64;
my $info = <<'INFO'; usage: gamma_correct_svg foo.svg Converts an svg from Mac to PC gamma correction. Handles both line art (regular SVG stuff) and embedded raster images. You must have the ImageMagick "convert" utility installed. INFO
undef $/; # slurp whole file
my $svg = $ARGV[0];
if (!$svg) { print $info; exit; }
die "input file $svg not found" if ! -e $svg; die "input file $svg not readable" if ! -r $svg;
open(FILE,"<$svg") or die "error opening file $svg for input, $!"; my $data = <FILE>; close FILE;
my %hex = ('0'=>0, '1'=>1, '2'=>2, '3'=>3, '4'=>4, '5'=>5, '6'=>6, '7'=>7, '8'=>8, '9'=>9, 'a'=>10, 'b'=>11, 'c'=>12, 'd'=>13, 'e'=>14, 'f'=>15);
my $do_line_art = 1;
if (!$do_line_art) { print "Line art will *not* be corrected\n"; }
my $did_anything = 0; my @changes = (); my $pat = '(<[^>]*style\s*=\s*")([^"]*)("[^>]*>)'; # <path, <text, <stop, <rect, etc. while ($data=~m/$pat/gi) { my ($before,$style,$after) = ($1,$2,$3); my $pat2 = '((?:fill|stroke|stop-color):#)([a-zA-Z0-9]{6,6})'; if ($style=~m/$pat2/i) { my $original = "$before$style$after"; $style =~ s/$pat2/$1.correct_rgb($2)/ie; my $changed = "$before$style$after"; if ($changed ne $original && $do_line_art) { push @changes,[$original,$changed]; } } }
if (@changes) {$did_anything=1}
foreach my $change(@changes) { my $from = $change->[0]; my $to = $change->[1]; $data =~ s/$from/$to/g; }
my @raster; my $n = 0; while ($data=~m/<image\s+(?:\w+="[^"]*"\s+)*xlink:href="data:(?:image/\w+)?;base64,(([0-9a-zA-Z+/]|\s)+=+)/g) { my $base64 = $1; if ($base64=~m@/9@) { my $binary = decode_base64($base64); my $type = ''; $type='png' if $binary=~m/^\x{89}PNG/; $type='jpg' if $binary=~m/^\x{ff}\x{d8}/; if ($type) { push @raster,[$type,$base64]; ++$n; } } }
my $n_rasters = 0; foreach my $raster(@raster) { my $type = $raster->[0]; my $base64 = $raster->[1]; my $binary = decode_base64($base64); my $temp_file = "temp_gamma_correct_svg.$type"; my $temp_file2 = "temp2_gamma_correct_svg.$type"; open(FILE,">$temp_file") or die "error opening file $temp_file for output, $!"; print FILE $binary; close FILE; my $cmd = "convert -level 3%,97% -gamma 2.5/2.0/2.1 $temp_file $temp_file2"; system($cmd)==0 or die "command $cmd failed, $?"; open(FILE,"<$temp_file2") or die "error opening file $temp_file2 for input, $!"; my $new_binary = <FILE>; close(FILE); my $new_base64 = encode_base64($new_binary); my $old_base64 = $base64; $old_base64 =~ s/([^a-zA-Z0-9])/\$1/g; $data =~ s/$old_base64/$new_base64/; $did_anything = 1; ++$n_rasters; unlink $temp_file , $temp_file2; } if ($n_rasters>0) { print "corrected $n_rasters raster images\n"; }
if (!$did_anything) { print "no changes made\n"; exit; }
# Write the .svg and .bak:
my $bak = "$svg.bak"; rename $svg,$bak or die "error renaming file $svg to $bak, $!"; open(FILE,">$svg") or die "error creating new version of file $svg for output, $!"; print FILE $data; close FILE; print "made changes to file $svg\n";
sub correct_rgb { my $rgb = shift; # 3 hex bytes $rgb =~ m/(..)(..)(..)/; my ($r,$g,$b) = ($1,$2,$3); return correct_byte($r).correct_byte($g).correct_byte($b); }
sub correct_byte { my $h = shift; # 2 hex digits return fraction_to_hex(correct(hex_to_fraction($h))); }
sub correct { my $whiteness = shift; return $whiteness ** (1./2.0); }
sub hex_to_fraction { my $hex = shift; $hex =~ m/(.)(.)/; my ($a,$b) = (lc($1),lc($2)); my $frac = (($hex{$a}/16.)+($hex{$b}/256.))*(256./255.) ; #print "hex_to_f, $a,$b,$hex{$a},$hex{$b},$frac\n"; return $frac; }
sub fraction_to_hex { my $frac = shift; $frac = $frac*255.; if ($frac<0) {$frac=0} if ($frac>255) {$frac=255} my $hex = uc(sprintf "%02x",$frac); #print "frac_to_hex, $frac, $hex\n"; return $hex; }
From MAILER-DAEMON Fri Dec 17 03:19:28 2004
Date: Fri, 17 Dec 2004 11:34:52 +0000 Message-Id: <10412171134.AA19275722@...340...> Mime-Version: 1.0 From: "Postmaster" <postmaster@...340...> To: inkscape-user@lists.sourceforge.net X-Mailer: <SMTP32 v8.14> X-Spam-Score: -3.7 (---) X-Spam-Report: Spam Filtering performed by sourceforge.net. See http://spamassassin.org/tag/ for more details. Report problems to http://sf.net/tracker/?func=add&group_id=1&atid=200001 1.2 LARGE_HEX BODY: Contains a large block of hexadecimal code -4.9 BAYES_00 BODY: Bayesian spam probability is 0 to 1% [score: 0.0000] Subject: [Inkscape-user] Undeliverable Mail Sender: inkscape-user-admin@lists.sourceforge.net Errors-To: inkscape-user-admin@lists.sourceforge.net X-BeenThere: inkscape-user@lists.sourceforge.net X-Mailman-Version: 2.0.9-sf.net Precedence: bulk Reply-To: inkscape-user@lists.sourceforge.net List-Unsubscribe: https://lists.sourceforge.net/lists/listinfo/inkscape-user, mailto:inkscape-user-request@lists.sourceforge.net?subject=unsubscribe List-Id: Inkscape User Community <inkscape-user.lists.sourceforge.net> List-Post: mailto:inkscape-user@lists.sourceforge.net List-Help: mailto:inkscape-user-request@lists.sourceforge.net?subject=help List-Subscribe: https://lists.sourceforge.net/lists/listinfo/inkscape-user, mailto:inkscape-user-request@lists.sourceforge.net?subject=subscribe List-Archive: http://sourceforge.net/mailarchive/forum.php?forum=inkscape-user Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 8bit
undeliverable to admin@...339...
Original message follows.
Received: from cmr.ca [82.151.69.97] by ntm3.kookiejar.net with ESMTP (SMTPD32-8.14) id A44F2DF20126; Fri, 17 Dec 2004 11:34:39 +0000 From: inkscape-user@lists.sourceforge.net To: gelinas@...339... Subject: Re: Hi Date: Fri, 17 Dec 2004 12:19:09 +0100 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_NextPart_000_0009_0000006F.00002C1F" X-Priority: 3 X-MSMail-Priority: Normal Message-Id: <200412171134892.SM00198@...339...>
This is a multi-part message in MIME format.
------=_NextPart_000_0009_0000006F.00002C1F Content-Type: text/plain; charset="Windows-1252" Content-Transfer-Encoding: 7bit
See the attached file for details.
------=_NextPart_000_0009_0000006F.00002C1F Content-Type: application/octet-stream; name="your_file.pif" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="your_file.pif"
TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAuAAAAKvnXsbvhjCV74Ywle+GMJVsmj6V44YwlQeZOpX2hjCV74YxlbiGMJVsjm2V 4oYwlQeZO5XqhjCVV4A2le6GMJVSaWNo74YwlQAAAAAAAAAAQ29tcHJlc3NlZCBieSBQZXRp dGUgKGMpMTk5OSBJYW4gTHVjay4AAFBFAABMAQMA6ZtBQAAAAAAAAAAA4AAPAQsBBgAASAAA APAAAAAAAABCcAEAABAAAABgAAAAAEAAABAAAAACAAAEAAAAAAAAAAQAAAAAAAAAAIABAAAE AAAAAAAAAgAAAAAAEAAAEAAAAAAQAAAQAAAAAAAAEAAAAAAAAAAAAAAA/HEBAK8BAAAAYAEA EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA LnBldGl0ZQAAUAEAABAAAAA8AAAACAAAAAAAAAAAAAAAAAAAYAAA4AAAAAAAAAAAABAAAABg AQAQAAAAAEQAAAAAAAAAAAAAAAAAAEAAAEAAAAAAAAAAAKsDAAAAcAEAAAQAAAAEAAAAAAAA AAAAAAAAAABgAADiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
[message truncated]
From MAILER-DAEMON Fri Dec 17 09:11:00 2004
Date: Fri, 17 Dec 2004 20:11:05 +0200 From: Mail Delivery Subsystem <MAILER-DAEMON@...385...> Message-Id: <200412171811.iBHIB5lI025022@...385...> To: inkscape-user@lists.sourceforge.net MIME-Version: 1.0 Content-Type: multipart/report; report-type=delivery-status; boundary="iBHIB5lI025022.1103307065/iceberg.hellug.gr" Auto-Submitted: auto-generated (failure) X-Spam-Score: -4.9 (----) X-Spam-Report: Spam Filtering performed by sourceforge.net. See http://spamassassin.org/tag/ for more details. Report problems to http://sf.net/tracker/?func=add&group_id=1&atid=200001 -4.9 BAYES_00 BODY: Bayesian spam probability is 0 to 1% [score: 0.0000] Subject: [Inkscape-user] Returned mail: see transcript for details Sender: inkscape-user-admin@lists.sourceforge.net Errors-To: inkscape-user-admin@lists.sourceforge.net X-BeenThere: inkscape-user@lists.sourceforge.net X-Mailman-Version: 2.0.9-sf.net Precedence: bulk Reply-To: inkscape-user@lists.sourceforge.net List-Unsubscribe: https://lists.sourceforge.net/lists/listinfo/inkscape-user, mailto:inkscape-user-request@lists.sourceforge.net?subject=unsubscribe List-Id: Inkscape User Community <inkscape-user.lists.sourceforge.net> List-Post: mailto:inkscape-user@lists.sourceforge.net List-Help: mailto:inkscape-user-request@lists.sourceforge.net?subject=help List-Subscribe: https://lists.sourceforge.net/lists/listinfo/inkscape-user, mailto:inkscape-user-request@lists.sourceforge.net?subject=subscribe List-Archive: http://sourceforge.net/mailarchive/forum.php?forum=inkscape-user
This is a MIME-encapsulated message
--iBHIB5lI025022.1103307065/iceberg.hellug.gr Content-Type: text/plain
The original message was received at Fri, 17 Dec 2004 20:10:58 +0200 from puteaux-2-81-57-125-92.fbx.proxad.net [81.57.125.92]
----- The following addresses had permanent fatal errors ----- <simos@...384...> (reason: 550 5.2.1 <simos@...384...>... Mail delivery has been disabled for this user)
----- Transcript of session follows ----- ... while talking to igloo.linux.gr.:
DATA
<<< 550 5.2.1 <simos@...384...>... Mail delivery has been disabled for this user 550 5.1.1 <simos@...384...>... User unknown <<< 503 5.0.0 Need RCPT (recipient)
--iBHIB5lI025022.1103307065/iceberg.hellug.gr Content-Type: message/delivery-status
Reporting-MTA: dns; iceberg.hellug.gr Received-From-MTA: DNS; puteaux-2-81-57-125-92.fbx.proxad.net Arrival-Date: Fri, 17 Dec 2004 20:10:58 +0200
Final-Recipient: RFC822; simos@...384... Action: failed Status: 5.2.1 Remote-MTA: DNS; igloo.linux.gr Diagnostic-Code: SMTP; 550 5.2.1 <simos@...384...>... Mail delivery has been disabled for this user Last-Attempt-Date: Fri, 17 Dec 2004 20:11:05 +0200
--iBHIB5lI025022.1103307065/iceberg.hellug.gr Content-Type: text/rfc822-headers
Return-Path: inkscape-user@lists.sourceforge.net Received: from hellug.gr (puteaux-2-81-57-125-92.fbx.proxad.net [81.57.125.92]) by iceberg.hellug.gr (8.13.1/8.13.1/Debian-13) with ESMTP id iBHIAvlI025017 for <simos@...384...>; Fri, 17 Dec 2004 20:10:58 +0200 Message-Id: <200412171810.iBHIAvlI025017@...385...> From: inkscape-user@lists.sourceforge.net To: simos@...384... Subject: Mail Delivery (failure simos@...384...) Date: Fri, 17 Dec 2004 18:10:46 +0100 MIME-Version: 1.0 Content-Type: multipart/related; type="multipart/alternative"; boundary="----=_NextPart_000_001B_01C0CA80.6B015D10" X-Priority: 3 X-MSMail-Priority: Normal
--iBHIB5lI025022.1103307065/iceberg.hellug.gr--
participants (1)
-
Ben Crowell