#!/usr/bin/perl -w #========================================================================= # # Copyright (c) Volker Heitz 2001 # If you like to read documentation, have a look at the bottom of this # file. # brought to you under the terms of the GPL # #========================================================================= eval 'exec /usr/bin/perl -S $0 ${1+"$@"}' if 0; # not running under some shell #eval 'exec /usr/local/bin/perl -S $0 ${1+"$@"}' # if 0; # not running under some shell #use Gimp qw(:auto __ N_); use Gimp qw/:auto/; use Gimp::Fu; use Gimp::Util; use Carp; #use POSIX; use strict; use vars qw/$help $VERSION $startdir $FontSize/; BEGIN { require POSIX; $startdir = POSIX::getcwd(); $VERSION = 0.08; } #Gimp::set_trace(TRACE_ALL); # $FontSize = 16;# should come from font selection in startup dialog! #BEGIN { #sub sortfunc { # return $a cmp $b; #} sub _index_maker { sub get_all_images { carp "called get_all_images!\n"; my( $path, $recursive, $xfilter, $ifilter, $recp, @I ) = ( shift, shift, shift, shift, "" ); "@_" && die "too much params\n"; $path =~ s#([^/]+)$#$1/#; opendir( D, $path ) or die "cannot open $path($!)\n"; my( @Fs ) = ( map{ $_; } readdir D ); closedir( D ) or die "cannot closedir $path($!)\n"; if( ref $recursive ) { $recp = $recursive->{path}; } foreach my $f ( @Fs ) { next if( $f =~ /^\./ ); #skip hidden and special dirs if( $xfilter && $xfilter =~ /\S+/ ) { if( $f =~ /$xfilter/i ) { printf "skipping %s, it matched $xfilter!\n*+*+*+*+*+\n\n\n", $f; next; } elsif( $path =~ /$xfilter/ ) { $ENV{DBG} && printf "skipping %s-%s, path matched $xfilter!\n". "*+*+*+*+*+\n\n\n", $path, $f; next; } else { #printf "taking %s, it didn't match $xfilter!\n*-*-*-*-*-\n\n\n", $f; } } if( $recursive && -d "$path$f" ) { printf "recursing with $path$f\n"; #$f =~ s#([^/]+)$#$1/#; push @I, get_all_images( "$path$f", {path=>"$recp$f/"}, $xfilter, $ifilter ); } elsif( -f "$path$f" ) { next unless( $f =~ /$ifilter/i ); push @I, ($recp.$f); } } return sort @I; } sub get_idx_name { my( $galleryfile, $i_pix ) = ( @_ ); my( $ngalleryfile, $type ); if( $galleryfile =~ /\.([^.,\/]+)$/ ) { $type = $1; } else { $galleryfile .= '.xcf'; $type = 'xcf'; } if( $galleryfile =~ /\%/ ) { $ngalleryfile = sprintf $galleryfile, $i_pix; } else { $galleryfile =~ s#\.([^.,/]+)$#%04d.$1#g; $ngalleryfile = sprintf $galleryfile, $i_pix; } unless( $type =~ /^(xcf|tif|tiff|jpg|jpeg)$/ ) { Gimp->message( "filetype $type is not supported!\n" ); exit main; } return $ngalleryfile, $type; } sub annotate_idx { my( $BI, $dir, $galleryfile, $i_pix, $font, $pheight, $n_pages, $annotate ) = ( @_ ); my( $idx_file, $text, $T ) = ( get_idx_name($galleryfile, $i_pix), ); # gimp_image_resize( $BI, gimp_image_width($BI), 50+gimp_image_height($BI), 0, 0 ); $text = get_idx_text( $dir, $idx_file, $i_pix, $n_pages, $annotate ); $T = get_text_extents( $text, $FontSize, $font ); $annotate && ( $T->{height}*=1.5 ); my $xlayer = ( gimp_layer_new( $BI, gimp_image_width($BI), $T->{height}, RGB, 'Indextext', 100, NORMAL_MODE ) ); gimp_image_add_layer( $BI, $xlayer, 0 ); gimp_layer_translate($xlayer, 0, $pheight ); gimp_drawable_fill( $xlayer, BG_IMAGE_FILL ); my $txtlayer = annotate_image( $BI, $xlayer, 20, $pheight, $text, $font ); #gimp_floating_sel_anchor( gimp_image_floating_selection($BI) ); #gimp_image_add_layer( $BI, $txtlayer, -1 ); } sub save_pic { my( $BI, $newlayer, $galleryfile, $i_pix, $annotate, $font, $pheight ) = ( @_ ); my( $ngalleryfile, $type ) = get_idx_name($galleryfile, $i_pix); print "saving index to $ngalleryfile(type:$type)\n"; if( $type eq q/xcf/ ) { gimp_file_save(RUN_NONINTERACTIVE,$BI, $newlayer, $ngalleryfile, $ngalleryfile ); } else { gimp_image_merge_visible_layers($BI,0); $newlayer = gimp_image_get_active_layer( $BI ); if( $type =~ /^(jpg|jpeg)$/ ) { file_jpeg_save(RUN_NONINTERACTIVE,$BI, $newlayer, $ngalleryfile, $ngalleryfile, 0.80, 0, 1, 0, '(c) www.promotec.lu',0,1,0,0 ); } elsif( $type =~ /^(tif|tiff)$/ ) { file_tiff_save(RUN_NONINTERACTIVE,$BI, $newlayer, $ngalleryfile, $ngalleryfile, 4 ); } } } sub new_image { my( $pwidth, $pheight, $annotate ) = ( @_ ); if( $annotate ) { $pheight += 50; } my $BI = gimp_image_new( $pwidth, $pheight, RGB ); my $bglayer = ( gimp_layer_new( $BI, $pwidth, $pheight, RGB, 'Background', 100, NORMAL_MODE ) ); gimp_image_add_layer( $BI, $bglayer, 0 ); gimp_drawable_fill( $bglayer, BG_IMAGE_FILL ); return $BI; } sub get_text_extents { my( $label, $size, $font, $absolute ) = ( @_ ); my %A; $ENV{DBG} && printf "*** get_text_extents args: ***\n%s\n*****************\n", (join "<\n>", @_); # fontsize:use 1 for Pointsizes (or 0 for pixelsizes) unless( $label=~/\S+/ ) { confess "NO TEXT!!!\n"; return {height=>0,width=>0}; } eval{ @A{qw/width height ascent descent/} = gimp_text_get_extents_fontname( $label, $size, 1, $font ); }; if( $@ ) { Gimp->message( "Error: $@ in gimp_text_get_extents_fontname" ); carp( sprintf "Error: $@ in gimp_text_get_extents_fontname\nArgs:%s\n**\n", (join "\n", @_) ); return {height=>0,width=>0}; } if( not $absolute ) { my $lines = ($label=~/\S+/)?1:0; #$lines++ while( $label =~ m/$/gs ); if( $lines > 0 ) { #Gimp->message( "$label has $lines lines" ); %A->{height} *= ($lines*1.1); } else { carp "zero lines?($label)\n"; } } else { %A->{height} *= 1.1; } return \%A; } #annotate_image takes following parameters: # image, layer, xpos, ypos, label, font sub annotate_image { my( $img, $layer, $xpos, $ypos, $label, $font ) = ( @_ ); #gimp_palette_set_foreground($labelcolor); my $txtlayer=gimp_text_fontname($img, $layer, $xpos, $ypos+2, $label, 0, 0, $FontSize, 0, $font); if( ref $layer ) { gimp_floating_sel_anchor( gimp_image_floating_selection($img) ); } # my( $txtwidth, $txthei ) = # gimp_drawable_width($txtlayer), gimp_drawable_height($txtlayer); #gimp_image_add_layer( $img, $txtlayer, 0 ); # gimp_layer_translate($txtlayer, $xpos+$wid, $ypos+$hei ); return $txtlayer; } sub human_time { my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(shift); return sprintf( "%02d/%02d/%02d-%02d:%02d:%02d", $mday, $mon+1, ($year%100), $hour, $min, $sec ); } sub get_image_text { my( $path, $image, $w, $h, ) = ( @_ ); my( $imagepath ); $imagepath = "$path$image"; if( -f $imagepath ) { # ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) my(undef,undef,$mode,$nlink,$uid,$gid,undef,$size,$atime,$mtime,$ctime,$blksize,$blocks) = stat $imagepath; my( $text ) = ( "$image, " ); ( $size /= 1024 ) =~ s/(\...).*$/$1/g; $mtime = human_time( $mtime ); $text .= join ", ", ("u:$uid", "g:$gid", $size."k", "\n$mtime,w:$w,h:$h" ); return $text; } else { die "file $imagepath does not exist, or is not readable\n"; } } sub get_idx_text { my( $dir, $fname, $nPage, $nPages, $level ) = ( @_ ); my $ret = sprintf "indexfile %s for directory %s:%s, page %02d from %02d, generated from %s, %s\n%s". "IndexMaker (c) 2001, volker\@promotec.lu", map{ chomp( $_ ); $_; } $fname, `hostname`, $dir, $nPage, $nPages, `whoami`, scalar localtime; if( $level ) { $ret .= "\nLegend: u:uid,g:gid,w:width,h:height"; } return $ret; } # real start of{ sub index_maker my ($path, $framecolor, $font, $fontsize, $recursive, $color, $foregroundcolor, $galleryfile, $columns,$rows, $annotate, $border, $pwidth, $pheight, $xfilter, $ifilter, $orient, $AlternateDir ) = ( @_ ); $FontSize = $fontsize; my( $oldforeground, $max_wid, $max_hei ) = ( gimp_palette_get_foreground(),0,0,0 ); my( $max_x, $max_y, $i_pix, $borderfact, $n_pages ) = ( abs( $pwidth / $columns ), abs( $pheight / $rows ), 1 ); gimp_palette_set_foreground($foregroundcolor); gimp_palette_set_background($color); while( 0 ){ #$annotate && (($pheight*$border/(100*$rows)) < (15*$annotate) ) ) { print( "increasing border($border) to get space for annotations\n" ); printf "real border is: %s\n******\n", ($pheight*$border/(100*$rows)); $border++; } if( $border <0 ||$border>25 ) { Gimp->message( "invalid border(0message("no outputfile specified\n"); return undef; } $path=$path.'/' unless( $path =~ /\/$/ ); if( $galleryfile ) { $galleryfile = "$path$galleryfile" unless( $galleryfile =~ /\// ); $galleryfile = "$galleryfile.xcf" unless( $galleryfile =~ /\.[^.,\/]+$/ ); } else { Gimp->message("no outputfile specified\n"); return undef; } if( -d $path ) { $ENV{IDX_PATH} = $path; chdir( $path ) or Gimp->message( "couldn't chdir to $path" ); if( $AlternateDir || not( -w $path ) ) { $AlternateDir ||= '/tmp'; $galleryfile =~ s#.*/([^/]+)$#$1#; $galleryfile = "$AlternateDir/$galleryfile"; Gimp->message( "cannot write to $path, generating idx in $galleryfile\n" ); } my( @Images, $i, $i_col, $i_row, $col_wid, $col_hei ) = ( get_all_images( $path, $recursive, $xfilter, $ifilter ) ); $i_row ||= 0; $ENV{DBG} && print "all images:\n", join "\n", map{ " $_"; } @Images; my( $BI, $newlayer, $col_wit ) = ( new_image( $pwidth, $pheight, $annotate ) ); $n_pages = ($#Images+1)/($rows*$columns); Gimp->progress_init("Generating index $galleryfile"); foreach my $image ( @Images ) { print "loading $path$image\n"; if( $i_col && $i_col >= $columns ) {# new column $col_wid = 0; $col_hei = 0; $i_row++; $i_col = 0; } if( $i_row && $i_row >= $rows ) {# next page! ($annotate)&& annotate_idx( $BI, $path, $galleryfile, $i_pix, $font, $pheight, $n_pages, ($annotate>1) ); save_pic( $BI, $newlayer, $galleryfile, $i_pix, ); $i_pix++; $i_row = 0; gimp_image_delete( $BI ); $BI = new_image( $pwidth, $pheight, $annotate ); } unless( -f "$path$image" && -r "$path$image" ) { Gimp->message( "file $path$image does not exist!\n" ); next; } my( $img ); eval{ $img = gimp_file_load(RUN_NONINTERACTIVE, "$path$image", "$path$image" ); my $clayer = gimp_image_get_active_layer($img); my( $width, $height ) = ( gimp_image_width($img), gimp_image_height($img) ); if( $orient ) { my $rotate; if( $orient == 1 ) {#Landscape $rotate = 1 if( $height > $width ); } elsif( $orient == 2 ) {# Portrait $rotate = 1 if( $width > $height ); } else { die "what's that: orientation is $orient\n"; } if( $rotate ) { print "rotating |/-\\|/=-)\n"; #$clayer = plug_in_rotate(RUN_NONINTERACTIVE,$img,$clayer,$rotate,0); plug_in_rotate(RUN_NONINTERACTIVE,$img,$clayer,1,0); gimp_layer_set_offsets( $clayer, 0, 0 ); gimp_image_resize( $img, $height, $width, 0, 0 ); gimp_layer_resize( $clayer, $height, $width, 0, 0 ); $width ^= $height;# change width/height $height ^= $width; $width ^= $height; } } ($max_wid<$width) || ( $max_wid = $width ); ($max_hei<$height) || ( $max_hei = $height ); $col_wid += $width;# count overall width gimp_selection_all( $img ); gimp_edit_copy( $clayer ); gimp_image_delete $img; $newlayer = ( gimp_layer_new( $BI, $width, $height, RGB, $image, 100, NORMAL_MODE ) ); gimp_edit_paste( $newlayer, 1 ); gimp_floating_sel_anchor( gimp_image_floating_selection($BI) ); my( $fact, $text, $T ) = ( $max_x/$width ); if( $annotate ) { $text = ( ($annotate>1)? get_image_text( $path, $image, $width, $height ) : $image ); $T = get_text_extents( $text, $FontSize, $font ); if( ( $height*$fact )> ($max_y-$T->{height}) ) { $fact = (($max_y-$T->{height})/$height); } } else { if( ( $height*$fact )> $max_y ) { $fact = ($max_y/$height); } } $ENV{DBG} && printf "scaling %s,%s with factor %s(border:%s) to: %s,%s\n", $width, $height, $fact, $borderfact, $width*$fact*$borderfact, $height*$fact*$borderfact; gimp_image_add_layer( $BI, $newlayer, -1 ); gimp_layer_scale( $newlayer, $width*$fact*$borderfact, $height*$fact*$borderfact, 0 ); # gimp_layer_translate($newlayer, $max_x*$i_col, $max_y*$i_row ); gimp_layer_set_offsets( $newlayer, $max_x*$i_col, $max_y*$i_row ); if( $annotate ) { gimp_layer_resize( $newlayer,$width*$fact, (($T->{height})+$height*$fact*$borderfact), 0, 0 ); annotate_image( $BI, $newlayer, $max_x*$i_col, $max_y*$i_row+$height*$fact*$borderfact, $text, $font ); } $ENV{DBG} && printf "translating %s,%s\n", $max_x*$i_col, $max_y*$i_row; $i++; $i_col++; }; if( $@ || not( ref $img ) ) { Gimp->message( "error while loading $path$image: $@\n" ); next; } #ASDF #ASDF if( $#Images>-1 ) { Gimp->progress_update ($i/($#Images+1)); } else { Gimp->progress_update (0.5); } } Gimp->progress_update (1); if( $i_col || $i_row ) {#only if necessary ($annotate) && annotate_idx( $BI, $path, $galleryfile, $i_pix, $font, $pheight, $n_pages, ($annotate>1) ); save_pic( $BI, $newlayer, $galleryfile, $i_pix, $annotate); } } else { Gimp->message("Path $path does not exist!\n"); return undef; } } #}#END_BEGIN?-) $help=<", "(c) 2001 Volker Heitz", "20011030", N_"/Xtns/Render/IndexMaker", undef, [ [PF_FILE, "path", "Destination path .... ", $startdir||'/tmp'], [PF_COLOR, "frame_color", "Color of the marks on the index", [0,0,0]], [PF_FONT, "displayfont", "Select font", undef ], [PF_INT32, "fontsize", "Fontsize", 16], [PF_RADIO, "recursive", "scan dir recursive?", 0 ,[ no => 0, yes => 1 ]], [PF_COLOR, "background_color","Gallery background color", [255,255,255]], [PF_COLOR, "foreground_color","Gallery background color", [100,100,100,]], [PF_STRING, "gallery_file", "name of the index file generated",'idx.xcf' ], [PF_INT32, "columns", "columns on paper", 3], [PF_INT32, "rows", "rows on paper", 3], [PF_RADIO, "annotate", "annotate images?yes=name,full=filestats", 1 ,[ no=>0, yes=>1, full=>2]], [PF_INT32, "border_percentage", "amount of space between pix(in %)", 1], [PF_INT32, "paper_width", "paper width in pixels", 3072], [PF_INT32, "paper_height", "paper height in pixels", 2304], [PF_STRING, "x_filter", "excludefilter(regexp)",'^idx\S+\.\w+$' ], [PF_STRING, "i_filter", "includefilter(regexp)",'\.(tif|tiff|jpg|jpeg)$' ], [PF_RADIO, "orientation", "how to arrange pix?", 1 ,[ none=>0,landscape=>1, portrait=>2, ]], [PF_STRING, "alt_dir", "TargetDirectory(only if different from the one you are scanning",'' ], ], \&_index_maker); exit main; __END__ =head1 NAME Index Maker =head1 SYNOPSIS gimp::Xtns::Render::IndexMaker =head1 DESCRIPTION Index Maker is a Gimp plugin.It generates a picture file from all pictures it finds in a specified path.A thumbnail is generated from each picture with a desired global scale or X or Y fixed... =head1 API Description The script contains some functions that implement a very rudimentary API. =head2 functions here are some helper functions for the main function: =over 4 =item get_all_images scans a directory for images and returns everything found as array of filenames, with relative pathnames from the topdir specified parameters: 1. directoryname to scan 2. switch to indicate if it should run in recursive manner returns: Array of filenames =item save_pic saves picture to specified filename parameters: 1. image handle 2. active layer 3. name template for the file 4. count of index files to include in filename TODO: remove param 1+2, they are not realy necessary, when switching to OO returns: nothing =item new_image creates new image and a base layer in the background color parameters: 1. width of the new image 2. height " " " " returns: Handle to newly created image. =item index_maker the function doing it all. parameters: 1. path to scan 2. unused framecolor 3. unused font 4. recursive switch 5. paint color 6. background color 7. nametemplate for the idx files 8. columns of thumbnails per page 9. rows of thumbnails per page 10. border percentage 11. paper height in pixel 12. paper width " " returns: nothing of interest =back 4 =head1 AUTHOR Written by Volker Heitz (mailto:perl@promotec.lu), (c) 2001 see http://promotec.lu for further information, i will add a download topic, where you can find information about this script and other toys and libraries I have written. Made public under the terms of GPL(http://www.gpl.org)