|
2 | 2 |
|
3 | 3 | # Author: Trizen
|
4 | 4 | # Date: 10 August 2025
|
| 5 | +# Edit: 23 Setepmber 2025 |
5 | 6 | # https://github.com/trizen
|
6 | 7 |
|
7 | 8 | # Hard-rotate images that contain the "Orientation" EXIF tag specified as "Rotate 90 CW" or "Rotate 270 CW".
|
8 | 9 |
|
9 | 10 | use 5.036;
|
10 | 11 | use Imager;
|
11 | 12 | use Image::ExifTool qw(ImageInfo);
|
| 13 | +use File::Find qw(find); |
| 14 | +use Getopt::Long qw(GetOptions); |
12 | 15 |
|
13 |
| -foreach my $file (@ARGV) { |
| 16 | +my $img_formats = ''; |
| 17 | +my $preserve_attr = 0; |
14 | 18 |
|
15 |
| - say ":: Processing: $file"; |
| 19 | +my @img_formats = qw( |
| 20 | + jpeg |
| 21 | + jpg |
| 22 | +); |
| 23 | + |
| 24 | +sub usage ($code) { |
| 25 | + local $" = ","; |
| 26 | + print <<"EOT"; |
| 27 | +usage: $0 [options] [dirs | files] |
| 28 | +
|
| 29 | +options: |
| 30 | + -f --formats=s,s : specify more image formats (default: @img_formats) |
| 31 | + -p --preserve! : preserve original file timestamps and permissions |
| 32 | +
|
| 33 | +examples: |
| 34 | +
|
| 35 | + $0 -p *.jpg |
| 36 | +
|
| 37 | +EOT |
| 38 | + |
| 39 | + exit($code); |
| 40 | +} |
| 41 | + |
| 42 | +GetOptions( |
| 43 | + 'f|formats=s' => \$img_formats, |
| 44 | + 'p|preserve!' => \$preserve_attr, |
| 45 | + 'help' => sub { usage(0) }, |
| 46 | + ) |
| 47 | + or die("Error in command line arguments"); |
| 48 | + |
| 49 | +sub hard_rotate_image ($file) { |
16 | 50 |
|
17 | 51 | my $info = ImageInfo($file);
|
18 | 52 | my $orientation = $info->{Orientation};
|
|
24 | 58 |
|
25 | 59 | my $img = Imager->new(file => $file) or die Imager->errstr();
|
26 | 60 | $img = $img->rotate(degrees => $angle);
|
| 61 | + |
| 62 | + my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($file); |
| 63 | + |
27 | 64 | unlink($file);
|
28 |
| - $img->write(file => $file) or die $img->errstr; |
| 65 | + $img->write(file => $file) or do { |
| 66 | + warn "Failed to rewrite image: ", $img->errstr; |
| 67 | + return; |
| 68 | + }; |
| 69 | + |
| 70 | + # Set the original ownership of the image |
| 71 | + chown($uid, $gid, $file); |
| 72 | + |
| 73 | + if ($preserve_attr) { |
| 74 | + |
| 75 | + # Set the original modification time |
| 76 | + utime($atime, $mtime, $file) |
| 77 | + or warn "Can't change timestamp: $!\n"; |
| 78 | + |
| 79 | + # Set original permissions |
| 80 | + chmod($mode & 07777, $file) |
| 81 | + or warn "Can't change permissions: $!\n"; |
| 82 | + } |
| 83 | + |
29 | 84 | }
|
30 | 85 | }
|
| 86 | + |
| 87 | +@ARGV || usage(1); |
| 88 | + |
| 89 | +push @img_formats, map { quotemeta } split(/\s*,\s*/, $img_formats); |
| 90 | + |
| 91 | +my $img_formats_re = do { |
| 92 | + local $" = '|'; |
| 93 | + qr/\.(@img_formats)\z/i; |
| 94 | +}; |
| 95 | + |
| 96 | +find { |
| 97 | + no_chdir => 1, |
| 98 | + wanted => sub { |
| 99 | + (/$img_formats_re/o && -f) || return; |
| 100 | + say ":: Processing: $_"; |
| 101 | + hard_rotate_image($_); |
| 102 | + } |
| 103 | +} => @ARGV; |
0 commit comments