| Image-Kimdaba documentation | Contained in the Image-Kimdaba distribution. |
Image::Kimdaba - Parser for the KDE Image Database
See here : http://ktown.kde.org/kimdaba
use Image::Kimdaba;
use English qw( -no_match_vars ) ;
my @ListOfPictures;
my $folder=getRootFolder();
parseDB( "$folder" );
print "Your actual Kimdaba settings are :\n";
while( my ($attr, $value) = each %kimdabaconfig)
{
print "\t$attr => $value\n";
}
print "\n";
my $nb1= scalar keys %imageattributes;
my $nb2= scalar keys %imageoptions;
print "Following options were present in your $nb1 pictures :\n";
while( my ($option,$r_values) = each %alloptions )
{
my $nb = scalar @$r_values;
print "\t$nb $option\n";
}
print "\n";
local $, = "\n" ; # print bla,bla prints "bla\nbla"
print "\n\n== NO Keywords (ten first) ==\n";
@ListOfPictures=matchAnyOption( "Keywords" => [] );
print sort(@ListOfPictures[0..9]);
print "\n\n== Holiday ==\n";
@ListOfPictures=matchAnyOption( "Keywords" => [ "holiday" ] );
print sort(@ListOfPictures);
print "\n\n== ANNE HELENE ==\n";
@ListOfPictures=matchAnyOption( "Persons" => [ "Anne Helene" ] );
print sort(@ListOfPictures);
print "\n\n== ANY OF (JESPER, ANNE HELEN) ==\n";
@ListOfPictures=matchAnyOption( "Persons" => [ "Jesper" , "Anne Helene" ] );
print sort(@ListOfPictures);
print "\n\n== ALL OF (JESPER, ANNE HELEN) ==\n";
@ListOfPictures=matchAllOptions( "Persons" => [ "Jesper" , "Anne Helene" ] );
print sort(@ListOfPictures);
print "\n\n== PERSONS=Jesper, Locations=Mallorca ==\n";
@ListOfPictures=matchAllOptions(
"Persons" => [ "Jesper" ],
"Locations" => [ "Mallorca" ]
);
print sort(@ListOfPictures);
$, = "" ; # print bla,bla prints "blabla"
print "\n\n==Print all infos known about specific pictures\n";
print "\n\n== Drag&Drop pictures from Kimdaba ==\n";
@ListOfPictures=letMeDraganddropPictures();
printImage( $_ ) foreach @ListOfPictures;
From the website : http://ktown.kde.org/kimdaba
KimDaBa or KDE Image Database is a tool which you can use to easily sort your images. It provides many functionnalities to sort them and find them easily.
The infos available in the database are directly translated in following perl datastructures. (See the index.xml file to see how it looks like)
note : the reading of man perllol is highly recommended
%imageattributes HASH OF (url of the picture, REF. HASH OF (attribute, value) )
Now and in the rest of the document, url is given locally from the root directory, such as "Folder1/Subfolder/img001.jpg", it's neither file:/home/user/Images/Folder1/Subfolder1/img001.jpg nor http://www.google.com/images/logo.gif
An HASH corresponding to this url could be
( monthFrom=>"1", dayFrom=>"18", hourFrom=>"19", yearTo=>"0", monthTo=>"0", md5sum=>"7f120e3cfb698ce0d7bb6e4e454c1a8b", minuteFrom=>"29", file=>"2005-01-09-Gif/img_0290.jpg", label=>"img_0290", angle=>"0", dayTo=>"0", secondFrom=>"46", yearFrom=>"2005", description=>"" )
%imageoptionsHASH of (url, REF. HASH OF (optoin, REF. LIST OF value) )
url is given locally from the root directory, such as "Folder1/Subfolder/img001.jpg"
An HASH corresponding to this url could be
( Keywords => [ "holiday" ], Locations => [ "Mallorca" ], Persons => [ "Anne Helene", "Jesper" ] )
%alloptionsHASH of (option, REF. LIST of values)
Could be something like :
( Keywords => [ "beers", "holiday", "new wave", "silo falls over", "Anne Helene's 30 years birthday" ]m Locations => ..., Persons => ..., OtherCategory => ... )
%membergroupsmembergroups are called categories depending on your version of Kimdaba.
HASH : (Locations => REF (HASH : USA => [ Chicago, Los Angeles ] ) )
Beware, you can have loops between membergroups.
%kimdabaconfigHASH of (attributes, values)
Fast all KimDaBa settings are stored in the index.xml file, as attribute of the "KimDaBA/config" XML element. So using this hash you can access many of the user preferences, for example it could be something like :
( viewSortTye=>>"0", passwd=>"", ensureImageWindowsOnScreen=>"1", viewerCacheSize=>"25", albumCategory=>"", showDrawings=>"1", htmlBaseURL=>"file:///home2/jmfayard/public_html", previewSize=>"256", thumbSize=>"64", displayLabels=>"1", launchViewerFullScreen=>"0", windowWidth-0=>"800", autoShowThumbnailView=>"0", showInfoBox=>"1", windowWidth-1=>"800", slideShowWidth_1280=>"600", viewerHeight_1280=>"450", fromDate=>"2005-01-01", htmlDestURL=>"file:///home2/jmfayard/public_html", trustTimeStamps=>"0", windowHeight-0=>"600", thumbNailBackgroundColor=>"#000000", windowHeight-1=>"600", slideShowInterval=>"5", toDate=>"2006-01-01", exclude=>"1", infoBoxPosition=>"6", locked=>"0", showDate=>"1", imageDirectory=>"/tmp/kimdaba-demo-jmfayard", searchForImagesOnStartup=>"1", autoSave=>"5", version=>"1", viewerWidth_1280=>"600", launchSlideShowFullScreen=>"0", showDescription=>"1", maxImages=>"100", useEXIFComments=>"1", useEXIFRotate=>"1", showTime=>"1", htmlBaseDir=>"/home2/jmfayard/public_html", slideShowHeight_1280=>"450", )
&letMeDraganddropPictures()print "\n\n== Drag&Drop pictures from Kimdaba ==\n"; @ListOfPictures = &letMeDraganddropPictures();
Wait until the user drag and drop pictures from Kimdaba and Konqueror and return a list of url.
&matchAllOptions(HASH of (option => REF List of values))Returns a list of urls. See the example in the synopsis.
&matchAnyOption(HASH of (option => REF List of values))Returns a list of urls. See the example in the synopsis.
&getRootFolder()my $folder=getRootFolder(); parseDB( "$folder" );
Returns the absolute path of the root directory. You should run the demo, keep the files, and use /tmp/kimdaba-demo-$USER when you are experimenting. Thanks to this function, the root directory can - passed as first argument on the command line ($ kim_script /tmp/kimdaba-demo-$USER ) - or will be asked to the user
&parseDB( $folder )Readonly access to most information available in the index.xml file. To modify a database, see &makeKimFile()
&printImage( url )printImage( $url );
Interesting to debug. Its code also shows how to access the hashes %imageattributes and %imageoptions :
sub printImage {
my ($file)= @_;
print "=== $file ===\n" ;
print "Attributes : ";
my %attributes = %{ $imageattributes{$file} } ;
while( my ($attr, $value) = each( %attributes ) )
{
print " $attr=>$value ; ";
}
print "\n";
my %options = %{ $imageoptions{$file} };
print "Options: \n" ;
while( my ($key, $r_values) = each( %options ) )
{
print "\t$key ==> ", join('; ', @$r_values ) , "\n";
}
print "\n";
}
&makeKimFile( $destdir, $name, @list )Instead of modifying directly the database (which could easily be dangerous for your data), you write a kimdaba export file (*.kim) then you use the import fonction in kimdaba (no dangerous, you are in control)
A .kim file is a zip archive containning an index.xml file, and a Thumbnail directory. You just have to create the index.xml file (say in '/tmp') then you call :
C<makeKimFile( "/tmp", "perl_output.kim", @ListOfPictures );>
where
/tmp/index.xml is the file created by you /tmp/perl_output.kim is the resulting kimdaba import ile @ListOfPictures is a list of urls present in /tmp/index.xml
Not that the KimDaBa import feature has some limitations.
Example :
use Image::Kimdaba;
my @ListOfPictures;
my $folder=getRootFolder();
parseDB( "$folder" );
print "\n\n== Drag&Drop pictures from Kimdaba ==\n";
@ListOfPictures=letMeDraganddropPictures();
print join("\n", sort(@ListOfPictures));
print "--\n";
my $destdir="/tmp";
open( EXPORT, "> ${destdir}/index.xml");
print EXPORT <<FIN
<?xml version="1.0" encoding="UTF-8"?>
<KimDaBa-export location="external" >
FIN
;
for my $url (@ListOfPictures)
{
my $description="yeah! I changed the description";
my $md5sum="";
if (
(exists $imageattributes{$url})
&&
(exists $imageattributes{$url}{'md5sum'})
&&
(! $imageattributes{$url}{'md5sum'} eq "")
)
{
$md5sum="md5sum=\"$imageattributes{$url}{'md5sum'}\" ";
}
my $value="Test Add Another Keyword";
print EXPORT <<FIN
<image description="$description" $md5sum file="$url" >
<options>
<option name="Keywords" >
<value value="Test Add a keyword" />
<value value="$value" />
</option>
</options>
</image>
FIN
;
}
print EXPORT <<FIN
</KimDaBa-export>
FIN
;
close( EXPORT );
makeKimFile( $destdir, "perl_export.kim", @ListOfPictures);
A lot ;-)
Jean-Michel Fayard ; jmfayard{at}moufrei.de
perllol
| Image-Kimdaba documentation | Contained in the Image-Kimdaba distribution. |
# Copyright 2005 Jean-Michel Fayard jmfayard_at_gmail.com # Put into the public domain. # package Image::Kimdaba; use strict; use warnings; use XML::Parser; use Carp;
BEGIN { use Exporter (); our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS); $VERSION = 0.5; @ISA = qw(Exporter); @EXPORT = qw( %alloptions %kimdabaconfig %membergroups %imageoptions %imageattributes &printImage &getRootFolder &parseDB &matchAllOptions &matchAnyOption &letMeDraganddropPictures &askForPictures &makeKimFile ); %EXPORT_TAGS = ( ); # eg: TAG => [ qw!name1 name2! ], # your exported package globals go here, # as well as any optionally exported functions #@EXPORT_OK = qw(%imageoptions %imageattributes); } our @EXPORT_OK; # exported package globals go here our %imageattributes; our %imageoptions; our %alloptions; our %kimdabaconfig; our %membergroups; # non-exported package globals go here # initialize package globals, first exported ones %imageattributes=(); %imageoptions=(); %alloptions=(); %kimdabaconfig=(); %membergroups=(); # then the others (which are still accessible as $Some::Module::stuff) # all file-scoped lexicals must be created before # the functions below that use them. # file-private lexicals go here my $option="" ; my $image=""; # image element that we currently handle my $optionname=""; # currently handle option with name $option for image $image my @values=(); # currently found values my %alloptionshashed; my $folder; # I need it for askForPictures
sub letMeDraganddropPictures { my @res=(); my $line=<STDIN>; chomp $line; # for pictures having a "'" in their filename $line=~s#'\\''#\\#g; my @a = ( $line =~ m/'[^']+'/g ); my $folder2 = $kimdabaconfig{"imageDirectory"}; foreach (@a) { # Change '/autre/Photos/USA/2004-08-09_Monument_Valley/Monument_Valley_05.JPG' # in USA/2004-08-09_Monument_Valley/Monument_Valley_05.JPG s#$folder##; s#$folder2##; # in case where $folder="." or similar s#(file:)?/+##; s#^'## ; s#'$## ; s#\\#'#g; push @res, $_; } # Now check which urls are really correct : @res =grep { exists $imageoptions{$_} } @res; return @res; } ##########
sub matchAllOptions { return @{ matchOptions( 1, @_) }; } sub matchAnyOption { return @{ matchOptions( 0, @_) }; } sub matchOptions { my ($matchall, %request)=@_; my @urlsfound=(); my @checkoptions=keys %request; URL: for my $url (keys %imageattributes) { my %options = (); %options= %{ $imageoptions{$url} } if (exists $imageoptions{$url} ); OPTION: for my $option ( @checkoptions ) { unless (exists $options{$option} ) { if ( scalar @{ $request{$option} } == 0 ) { next OPTION; } else { next URL; } } my @values_image =@{ $options{$option} }; # (Anne Helen) my @values_searched=@{ $request{$option} }; # (Jesper, Anne Helen) for my $req (@values_searched) { my $res = scalar grep { $_ eq $req } @values_image; if ( ($res == 0) && ($matchall) ) { next URL; } elsif ( ($res!=0) && (!$matchall) ) { # if trouvé, here next OPTION; } } # If we went this far, this means that we... if ($matchall) { next OPTION; # ... found a value corresponding to each of the options } else { next URL; # ... never found a value corresponding to one of the options } } push @urlsfound, $url; } return [ @urlsfound ]; }
sub getRootFolder { no warnings; ($folder) = grep { -d } @main::ARGV; $folder = "~/Images" unless (-d $folder ); $folder = $ENV{PWD} unless (-d $folder ); until ( (-d "$folder") && (-r "$folder/index.xml") ) { print "In which folder are your pictures stored ?\n"; chomp( $folder=<STDIN>); } return $folder; }
sub printImage { my ($file)= @_; print "=== $file ===\n" ; print "Attributes : "; my %attributes = %{ $imageattributes{$file} } ; while( my ($attr, $value) = each( %attributes ) ) # for my $attr ( keys %{ $imageattributes{$file} } ) { print " $attr=>$value ; "; } print "\n"; my %options = %{ $imageoptions{$file} }; print "Options: \n" ; # for my $key ( sort keys %options ) while( my ($key, $r_values) = each( %options ) ) { print "\t$key ==> ", join('; ', @$r_values ) , "\n"; } print "\n"; } ##### <Parsing of the database goes here> ###### sub parseDB ($) { my ($folder)=@_; my $p1 = new XML::Parser( Style => 'Subs' ); croak "Can not find KimDaBa's database" unless (-r "$folder/index.xml"); $p1->parsefile( "$folder/index.xml"); } sub member { my ( $p, $el, %attrs ) = @_ ; my ($groupname,$member) = ( $attrs{"group-name"}, $attrs{"member"} ); # index.xml format has changed at Fri Dec 3 my $category=$attrs{"option-group"} if ( exists $attrs{"option-group"} ); $category=$attrs{"category"} if ( exists $attrs{"category"} ); if (! exists( $membergroups{$category} ) ) { $membergroups{$category} = { $groupname => [ $member ] }; } elsif (! exists( $membergroups{$category}{$groupname} ) ) { $membergroups{$category}{$groupname} = [ $member ] ; } else { my $r_list = $membergroups{$category}{$groupname}; push @$r_list, $member; } } sub config { my ( $p, $el, %attrs ) = @_ ; %kimdabaconfig=%attrs; } sub image { my ( $p, $el, %attrs ) = @_ ; $image = $attrs{"file"} ; $imageattributes{$image} = \%attrs; } sub image_ { my ( $p, $el ) = @_; $image = ""; } sub options { my ( $p, $el, %attrs ) = @_ ; return if ($image eq "") ; # We are in KimDaBa>config>SearchInfo>Options>Option # or in KimDaBa>Options $imageoptions{$image} = {} ; } sub option { my ( $p, $el, %attrs ) = @_ ; return if ($image eq "") ; # We are in KimDaBa>config>SearchInfo>Options>Option # or in KimDaBa>Options $optionname=$attrs{"name"}; @values=(); } sub option_ { my ( $p, $el ) = @_; if ($image eq "") { $optionname=""; return; } $imageoptions{$image}->{$optionname} = [ @values ]; $alloptionshashed{$optionname} = {} unless( exists $alloptionshashed{$optionname} ); for my $value (@values) { $alloptionshashed{$optionname}{$value}=1; } $optionname=""; } sub value { my ( $p, $el, %attrs ) = @_ ; return if ( $optionname eq "" ) ; push @values, $attrs{"value"}; } sub KimDaBa_ { # %alloptionshashed is hash of hash for efficiency reasons, but we want to return # a more clean hash of list. my $nb= scalar %alloptionshashed; for (keys %alloptionshashed) { $alloptions{ $_ } = [ keys %{ $alloptionshashed{$_} } ]; } } ##### <Parsing of the database ends here> ######
sub makeKimFile { my ($destdir,$name,@ListOfPictures)=@_; system( "rm -rf ${destdir}/Thumbnails" ); system( "mkdir -p ${destdir}/Thumbnails" ); for my $url (@ListOfPictures) { next unless( -e "${folder}/${url}" ); # ( my $dest = $url) =~ s#(.*)/(.*)#\2#; my ($dirname,$basename) = ( $url =~ m#(.*)/(.*)# ); my $thumb="${folder}/${dirname}/ThumbNails/" . "$kimdabaconfig{'thumbSize'}x$kimdabaconfig{'thumbSize'}" . "-$imageattributes{$url}{'angle'}" . "-$basename"; if (-e $thumb) { my $a=symlink $thumb, "${destdir}/Thumbnails/$basename"; next; } print "Creating thumbnail for $url...\n"; $url=~s/'/'\\''/g; $basename=~s/'/'\\''/g; system( "convert -size 128x128 '$folder/$url' -resize 128x128 '${destdir}/Thumbnails/$basename'" ); } chdir $destdir or croak "$!"; unlink $name; system( "zip", "-r", $name, "index.xml", "Thumbnails" ); print "KimDaBa export file created : ${destdir}/${name}\n"; } 1; # don't forget to return a true value from the file # Desactived for now, because it's rather pointless and adds a dependcy on Term::Readline ##sub askForPictures ##{ ## my @res; ## print <<EOF ##Now specify a list of urls of pictures that this script will handle. ##You can write any perl code. Then Ctrl-D when you are done. ##Common examples: ## # simple list ## \@res=( "img004.jpg" , "img006.jpg" , "img010.jpg" ); ## # pictures not on disk ## \@res=grep { ! -e "$folder/\$_" } keys \%imageoptions; ## # pictures rotated in Kimdaba but not in the real life ## @res=grep { $imageattributes{$_}{"angle"}!=0 } keys %imageattributes; ## # kimdaba's queries ## \@res=matchAllOptions( "Persons" => [ "Jesper" , "Anne Helene" ] ); ## \@res=matchAllOptions( "Persons" => [ "Jesper" ], "Locations" => [ "Mallorca" ]); ## \@res=matchAnyOption( "Keywords" => [ "ForMyScript" ] ); ## ##EOF ##; ## use Term::ReadLine; ## my $term = new Term::ReadLine 'Kimdaba Query'; ## my $prompt = "Kim> "; ## my $OUT = $term->OUT || \*STDOUT; ## ## while(1) ## { ## while ( defined (my $perlcode = $term->readline($prompt)) ) { ## my $res = eval ($perlcode); ## warn $@ if $@; ## print $OUT $res, "\n" unless $@; ## $term->addhistory($_) if /\S/; ## } ## ### Now check which urls are really correct : ## @res=grep { exists $imageoptions{$_} } @res; ## print "Following pictures were found\n"; ## print join("\n",@res),"\n\n"; ## print "Continue with those picutres? [yes]/no : "; ## $_=<STDIN>; ## /no/ or last; ## } ## return @res; ##}