#------------------------------------------------------------------------------ # File: GIMP.pm # # Description: Read meta information from GIMP XCF images # # Revisions: 2010/10/05 - P. Harvey Created # 2018/08/21 - PH Updated to current XCF specification (v013) # # References: 1) GIMP source code # 2) https://gitlab.gnome.org/GNOME/gimp/blob/master/devel-docs/xcf.txt #------------------------------------------------------------------------------ package Image::ExifTool::GIMP; use strict; use vars qw($VERSION); use Image::ExifTool qw(:DataAccess :Utils); $VERSION = '1.03'; sub ProcessParasites($$$); # GIMP XCF properties (ref 2) %Image::ExifTool::GIMP::Main = ( GROUPS => { 2 => 'Image' }, VARS => { ALPHA_FIRST => 1 }, NOTES => q{ The GNU Image Manipulation Program (GIMP) writes these tags in its native XCF (eXperimental Computing Facilty) images. }, header => { SubDirectory => { TagTable => 'Image::ExifTool::GIMP::Header' } }, # recognized properties # 1 - ColorMap # 17 - SamplePoints? (doc says 17 is also "PROP_SAMPLE_POINTS"??) 17 => { Name => 'Compression', Format => 'int8u', PrintConv => { 0 => 'None', 1 => 'RLE Encoding', 2 => 'Zlib', 3 => 'Fractal', }, }, # 18 - Guides 19 => { Name => 'Resolution', SubDirectory => { TagTable => 'Image::ExifTool::GIMP::Resolution' }, }, 20 => { Name => 'Tattoo', Format => 'int32u', }, 21 => { Name => 'Parasites', SubDirectory => { TagTable => 'Image::ExifTool::GIMP::Parasite' }, }, 22 => { Name => 'Units', Format => 'int32u', PrintConv => { 1 => 'Inches', 2 => 'mm', 3 => 'Points', 4 => 'Picas', }, }, # 23 Paths # 24 UserUnit # 25 Vectors ); # information extracted from the XCF file header (ref 2) %Image::ExifTool::GIMP::Header = ( GROUPS => { 2 => 'Image' }, PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData, 9 => { Name => 'XCFVersion', Format => 'string[5]', DataMember => 'XCFVersion', RawConv => '$$self{XCFVersion} = $val', PrintConv => { 'file' => '0', 'v001' => '1', 'v002' => '2', OTHER => sub { my $val = shift; $val =~ s/^v0*//; return $val }, }, }, 14 => { Name => 'ImageWidth', Format => 'int32u' }, 18 => { Name => 'ImageHeight', Format => 'int32u' }, 22 => { Name => 'ColorMode', Format => 'int32u', PrintConv => { 0 => 'RGB Color', 1 => 'Grayscale', 2 => 'Indexed Color', }, }, # 26 - [XCF 4 or later] Precision ); # XCF resolution data (property type 19) (ref 2) %Image::ExifTool::GIMP::Resolution = ( GROUPS => { 2 => 'Image' }, PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData, FORMAT => 'float', 0 => 'XResolution', 1 => 'YResolution', ); # XCF "Parasite" data (property type 21) (ref 1/PH) %Image::ExifTool::GIMP::Parasite = ( GROUPS => { 2 => 'Image' }, PROCESS_PROC => \&ProcessParasites, 'gimp-comment' => { Name => 'Comment', Format => 'string', }, 'exif-data' => { Name => 'ExifData', SubDirectory => { TagTable => 'Image::ExifTool::Exif::Main', ProcessProc => \&Image::ExifTool::ProcessTIFF, Start => 6, # starts after "Exif\0\0" header }, }, 'jpeg-exif-data' => { # (deprecated, untested) Name => 'JPEGExifData', SubDirectory => { TagTable => 'Image::ExifTool::Exif::Main', ProcessProc => \&Image::ExifTool::ProcessTIFF, Start => 6, }, }, 'iptc-data' => { # (untested) Name => 'IPTCData', SubDirectory => { TagTable => 'Image::ExifTool::IPTC::Main' }, }, 'icc-profile' => { Name => 'ICC_Profile', SubDirectory => { TagTable => 'Image::ExifTool::ICC_Profile::Main' }, }, 'icc-profile-name' => { Name => 'ICCProfileName', Format => 'string', }, 'gimp-metadata' => { Name => 'XMP', SubDirectory => { TagTable => 'Image::ExifTool::XMP::Main', Start => 10, # starts after "GIMP_XMP_1" header }, }, 'gimp-image-metadata' => { Name => 'XML', SubDirectory => { TagTable => 'Image::ExifTool::XMP::XML' }, }, # Seen, but not yet decoded: # gimp-image-grid # jpeg-settings ); #------------------------------------------------------------------------------ # Read information in a GIMP XCF parasite data (ref PH) # Inputs: 0) ExifTool ref, 1) dirInfo ref, 2) tag table ref # Returns: 1 on success sub ProcessParasites($$$) { my ($et, $dirInfo, $tagTablePtr) = @_; my $unknown = $et->Options('Unknown') || $et->Options('Verbose'); my $dataPt = $$dirInfo{DataPt}; my $pos = $$dirInfo{DirStart} || 0; my $end = length $$dataPt; $et->VerboseDir('Parasites', undef, $end); for (;;) { last if $pos + 4 > $end; my $size = Get32u($dataPt, $pos); # length of tag string $pos += 4; last if $pos + $size + 8 > $end; my $tag = substr($$dataPt, $pos, $size); $pos += $size; $tag =~ s/\0.*//s; # trim at null terminator # my $flags = Get32u($dataPt, $pos); (ignore flags) $size = Get32u($dataPt, $pos + 4); # length of data $pos += 8; last if $pos + $size > $end; if (not $$tagTablePtr{$tag} and $unknown) { my $name = $tag; $name =~ tr/-_A-Za-z0-9//dc; $name =~ s/^gimp-//; next unless length $name; $name = ucfirst $name; $name =~ s/([a-z])-([a-z])/$1\u$2/g; $name = "GIMP-$name" unless length($name) > 1; AddTagToTable($tagTablePtr, $tag, { Name => $name, Unknown => 1 }); } $et->HandleTag($tagTablePtr, $tag, undef, DataPt => $dataPt, Start => $pos, Size => $size, ); $pos += $size; } return 1; } #------------------------------------------------------------------------------ # Read information in a GIMP XCF document # Inputs: 0) ExifTool ref, 1) dirInfo ref # Returns: 1 on success, 0 if this wasn't a valid XCF file sub ProcessXCF($$) { my ($et, $dirInfo) = @_; my $raf = $$dirInfo{RAF}; my $buff; return 0 unless $raf->Read($buff, 26) == 26; return 0 unless $buff =~ /^gimp xcf /; my $tagTablePtr = GetTagTable('Image::ExifTool::GIMP::Main'); my $verbose = $et->Options('Verbose'); $et->SetFileType(); SetByteOrder('MM'); # process the XCF header $et->HandleTag($tagTablePtr, 'header', $buff); # skip over precision for XCV version 4 or later $raf->Seek(4, 1) if $$et{XCFVersion} =~ /^v0*(\d+)/ and $1 >= 4; # loop through image properties for (;;) { $raf->Read($buff, 8) == 8 or last; my $tag = Get32u(\$buff, 0) or last; my $size = Get32u(\$buff, 4); $verbose and $et->VPrint(0, "XCF property $tag ($size bytes):\n"); unless ($$tagTablePtr{$tag}) { $raf->Seek($size, 1); next; } $raf->Read($buff, $size) == $size or last; $et->HandleTag($tagTablePtr, $tag, undef, DataPt => \$buff, DataPos => $raf->Tell() - $size, Size => $size, ); } return 1; } 1; # end __END__ =head1 NAME Image::ExifTool::GIMP - Read meta information from GIMP XCF images =head1 SYNOPSIS This module is used by Image::ExifTool =head1 DESCRIPTION This module contains definitions required by Image::ExifTool to read meta information from GIMP (GNU Image Manipulation Program) XCF (eXperimental Computing Facility) images. This is the native image format used by the GIMP software. =head1 AUTHOR Copyright 2003-2018, Phil Harvey (phil at owl.phy.queensu.ca) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 REFERENCES =over 4 =item L =item L =back =head1 SEE ALSO L, L =cut