#!/usr/bin/perl #---------------------------------------------------------------------------- # mpp - a preprocessor for movie frames. # # # $Id: mpp,v 1.8 2001/05/29 08:30:55 dominik Exp $ #---------------------------------------------------------------------------- require 5.001; use strict; use Getopt::Long; #============================================================================ #============================================================================ # START OF CONFIGURATION SECTION #============================================================================ #============================================================================ # # The hash %convert contains information about how to convert # different image formats to PPM format. The key to the hash is # the format. All formats recognized by MPP are given below - thus # it makes no sense to add new keys unless you know how to recognize these # files and implement it in the subroutine 'file_info'. You can, however, # change the system command associated with a file type. # # The hash values are system commands to do the conversion. # The commands should formally convert a file 'INFILE' into a # PPM file with name 'frame.ppm'. # If the output file is 'redo.img' instead of 'frame.ppm', file conversion # will be done again on this file. Thus you can convert a file by a # chain of commands if it proves difficult to do it with a pipe. # Be careful not to built infinite loops this way (MPP will allow up # to $maxconvert steps before it dies with an error message). my(%convert) = ( # DON'T CHANGE NEXT LINE "PPM" => "ln INFILE frame.ppm", # DON'T CHANGE PREVIOUS LINE "PGM" => "pgmtoppm rgbi:1.0/1.0/1.0 INFILE > frame.ppm", "PBM" => "pgmtoppm rgbi:1.0/1.0/1.0 INFILE > frame.ppm", "GIF" => "giftoppm INFILE > frame.ppm", "JPEG" => "jpegtoppm INFILE > frame.ppm", "PM" => "pmtoppm INFILE > frame.ppm", "XBM" => "xbmtopbm INFILE > redo.img", "SUNRAS" => "rasttopnm INFILE > redo.img", "BMP" => "bmptoppm INFILE > frame.ppm", "UTAHRLE" => "rletoppm INFILE > frame.ppm", "IRIS" => "iristoppm INFILE > frame.ppm", "PCX" => "pcxtoppm INFILE > frame.ppm", "TIFF" => "tifftopnm INFILE > redo.img", "PS" => "pstopnm INFILE > redo.img", "PDSVICAR" => "pdstoppm INFILE > frame.ppm", "MTV" => "mtvtoppm INFILE > frame.ppm" ); my($maxconvert) = 3; # maximum number of conversions steps #---------------------------------------------------------------------------- # # The hash %uncompress contains information about how to find compressed # versions of files and how to decompress them. # When an image file is not found, MPP checks if the file with any # of the extensions specified in %uncompress does exist. If it does, the # file is decompressed and used. # # The commands should formally decompress a file 'INFILE.EXT' # into a file named 'INFILE'. 'EXT' is the extension given as the key. # If the commands are written non-destructively (i.e. decompression # does not remove the compressed version), the decompressed version # will be thrown away when it's no longer needed. my(%uncompress) = ( ".bz2" => "bunzip2 -n -c INFILE.bz2 > INFILE", ".bz" => "bunzip2 -n -c INFILE.bz > INFILE", ".gz" => "gunzip -n -c INFILE.gz > INFILE", ".Z" => "zcat INFILE.Z > INFILE", ".z" => "pcat INFILE.z > INFILE" ); #---------------------------------------------------------------------------- # # The hash %product contains information on processing of the final # ppm frames into a specific product. # The entries in %product define system commands that will transform # individual ppm files into something else. The key to the hash is the # argument given to the -product command line switch. The value is an array # with the following entries: # 0: The EXTension of the product (as in frame.EXT) # 1: A system command that formally converts 'frame.ppm' into 'frame.EXT'. my(%product) = ( "ppm" => [ 'ppm', '' ], "pgm" => [ 'pgm', 'ppmtopgm frame.ppm > frame.pgm' ], "pbm" => [ 'pbm', 'ppmtopgm frame.ppm | pgmtopbm > frame.pbm' ], "gif" => [ 'gif', 'ppmquant 256 frame.ppm 2>/dev/null |' . 'ppmtogif 2>/dev/null > frame.gif' ], "tiff" => [ 'tif', 'pnmtotiff frame.ppm > frame.tif' ], "bz2" => [ 'ppm.bz2','bzip2 frame.ppm' ], "gz" => [ 'ppm.gz', 'gzip -f frame.ppm' ], "Z" => [ 'ppm.Z', 'compress frame.ppm' ], "z" => [ 'ppm.z', 'pack frame.ppm' ] ); #---------------------------------------------------------------------------- # # CONFIGURATIONS FOR THE MPEG PROGRAM # # First the name for the encoder program. 'mpeg_encode', or, # on some systems, 'ppmtompeg'. my $mpeg_encode_program_name = "mpeg_encode"; # The hash %mpeg_input_convert contains the INPUT_CONVERT parameter # for each key of the %product hash (see above). my(%mpeg_input_convert) = ( "ppm" => '*', "pgm" => 'pgmtoppm rgbi:1.0/1.0/1.0 *', "pbm" => 'pgmtoppm rgbi:1.0/1.0/1.0 *', "gif" => 'giftoppm *', "tiff" => 'tifftopnm *', "bz2" => 'bunzip2 -c *', "bz" => 'bunzip2 -c *', "gz" => 'gunzip -c *', "Z" => 'zcat *', "z" => 'pcat *' ); # The hash %mpeg_parameters contains default values for many of the # parameters in the parameter files used by mpeg_encode. All parameters # mentioned here will end up in the parameter file written by MPP. # The following parameters MUST NOT have an entry in this hash because they # are handled directly by mpp: # BASE_FILE_FORMAT, INPUT, INPUT_DIR, INPUT_CONVERT my(%mpeg_parameters) = ( # frame pattern 'PATTERN' => 'IBBPBBPBBPBBPBBP', # output file name 'OUTPUT' => 'movie.mpg', # Size of frame groups. '' means infinite (only one group, saves space) 'GOP_SIZE' => '', # Motion search parameters 'SLICES_PER_FRAME' => '1', 'PIXEL' => 'HALF', 'RANGE' => '10', 'PSEARCH_ALG' => 'LOGARITHMIC', 'BSEARCH_ALG' => 'CROSS2', # Quality specifications for the JPEG compression step 'IQSCALE' => '8', 'PQSCALE' => '10', 'BQSCALE' => '25', # Reference frame 'REFERENCE_FRAME' => 'ORIGINAL', # Should last frame alway be there? 'FORCE_ENCODE_LAST_FRAME' => '1' ); #---------------------------------------------------------------------------- # # The hash %named_colors contains the rgbi values for some colors. # You can use these color names whenever a color is required. my(%named_colors) = ( '20gray' => '0.200/0.200/0.200', '40gray' => '0.400/0.400/0.400', '60gray' => '0.600/0.600/0.600', '80gray' => '0.800/0.800/0.800', 'aquamarine' => '0.498/1.000/0.831', 'azure' => '0.941/1.000/1.000', 'beige' => '0.961/0.961/0.863', 'bisque' => '1.000/0.894/0.769', 'black' => '0.000/0.000/0.000', 'blue' => '0.000/0.000/1.000', 'brown' => '0.647/0.165/0.165', 'crimson' => '0.863/0.078/0.235', 'cyan' => '0.000/1.000/1.000', 'dimgray' => '0.412/0.412/0.412', 'firebrick' => '0.698/0.133/0.133', 'gold' => '1.000/0.843/0.000', 'gray' => '0.502/0.502/0.502', 'green' => '0.000/0.502/0.000', 'hotpink' => '1.000/0.412/0.706', 'indigo' => '0.294/0.000/0.510', 'ivory' => '1.000/1.000/0.941', 'khaki' => '0.941/0.902/0.549', 'lavender' => '0.902/0.902/0.980', 'lime' => '0.000/1.000/0.000', 'magenta' => '1.000/0.000/1.000', 'maroon' => '0.502/0.000/0.000', 'navy' => '0.000/0.000/0.502', 'olive' => '0.502/0.502/0.000', 'orange' => '1.000/0.647/0.000', 'pink' => '1.000/0.753/0.796', 'purple' => '0.502/0.000/0.502', 'red' => '1.000/0.000/0.000', 'salmon' => '0.980/0.502/0.447', 'silver' => '0.753/0.753/0.753', 'teal' => '0.000/0.502/0.502', 'tomato' => '1.000/0.388/0.278', 'turquoise' => '0.251/0.878/0.816', 'violet' => '0.933/0.510/0.933', 'white' => '1.000/1.000/1.000', 'yellow' => '1.000/1.000/0.000', ); #---------------------------------------------------------------------------- # # For some of the paste operations we need to make masks. The pbmmask # utility obviously tries something clever by making the mask somewhat # larger than the exact pixels would indicate. E.g. a mask for rendered # text does not includes the small hole in some letters. MPP implements # its own mask mechanism which just makes background white and forground # black period. You can configure here if you want to use it. my($use_mpps_own_mask) = 1; #============================================================================ #============================================================================ # END OF CONFIGURATION SECTION #============================================================================ #============================================================================ # GLOBAL VARIABLES AND DEFAULT VALUES # ----------------------------------- my(%opt); # Options hash # Subdirectories for temporary files. Careful, they will be deleted # without sympathy my($FDIR) = 'MPP_frames'; # Subdirectory for frames my($TDIR) = 'MPP_tmp'; # Subdirectory for temporary files # Global variables which can be set with commands my($WIDTH) = 0; # Width of frames my($HEIGHT) = 0; # Height of frames my($PASTEOFFSET)= 0.05; # Pasteoffset from side, fraction of frame size my($SCALE) = ''; # Scaling method my($TEXTCOLOR) = "1/1/1"; # Foreground color for text my($TEXTFONT) = ''; # Font file for text my($FRAMECOLOR) = '0/0/0'; # Color of frames around too small images my($PREFIX) = ''; # Prefix for the name of image files my($POSTPROCESS)= ''; # Post processing command my($PRODUCT_EXT)= ''; # Extension of product file type # Internal global variables my(%macro); # Macro definitions like in C my($index); # Index of current frame my(%paste); # Array for info about paste items my($cmdlinenr); # Current line number in command file my($cmdfile); # Command file my($errors) = 0; # Counting warnings my($warnings) = 0; # Counting errors my($cmd); # A command my($pastechanged); # Flag my($nompeg); # Flag # Look for options on the command line # ------------------------------------ # First, some defaults $opt{'links'} = 1; $opt{'product'} = 'ppm'; GetOptions( \%opt , qw( -quiet -verbose -force -check -clean -mpeg -nolinks -colors -product=s) ) || die <<"EOF"; usage: mpp [ OPTIONS ] COMMAND-FILE -check Check for required system commands -colors List builtin color names -clean Remove all files created by mpp -product KEY Produce the product labeled KEY (ppm,pgm,pbm,gif,tiff,gz,z,Z) -mpeg Run mpeg_encode (Berkeley) -nolinks Use cp instead of ln to duplicate files -quiet Make less noise -verbose Make more noise -force Ignore all recoverable errors EOF # Open log and error files &open_log_and_err; # Check -product option if ($opt{'product'} =~ /frame\.ppm/) { $POSTPROCESS = $opt{'product'}; $PRODUCT_EXT = 'img'; } elsif (defined $product{$opt{'product'}}) { $POSTPROCESS = $product{$opt{'product'}}[1]; $PRODUCT_EXT = $product{$opt{'product'}}[0]; } else { &err("-product ... is neither a key nor a command"); exit(1); } &log("MSG: Setting POSTPROCESS to: $POSTPROCESS"); &log("MSG: Setting PRODUCT_EXT to: $PRODUCT_EXT"); if ($opt{'check'}) { # Only checking for programs &check_for_programs; exit(0); } if ($opt{'colors'}) { # Only list the colors &list_colors; exit(0); } if ($opt{'clean'}) { # Only cleaning up &cleanup; exit(0); } if ($opt{'nolinks'}) { $opt{'links'} = 0; } # Name of command file if (@ARGV) { $cmdfile = shift(@ARGV); } else { die "usage: mpp [ OPTIONS ] COMMAND-FILE\n"; } # Open command file open CMD,$cmdfile or die "Can't open cmd file $cmdfile $!\n"; # Signals $SIG{'INT'} = 'HANDLER'; $SIG{'QUIT'} = 'HANDLER'; $SIG{'HUP'} = 'HANDLER'; $SIG{'TERM'} = 'HANDLER'; # Create subdirectories for frames and temporary files mysystem("rm -rf $FDIR $TDIR; mkdir $FDIR $TDIR"); $index = 0; # Go through the command file COMMAND: while ($cmd = ) { $cmdlinenr = $.; next if $cmd =~ /^\s*$/; # empty line # Add any continuation lines $cmd .= while (/\\\s*$/); $cmd =~ s/\\\s*\n//g; # delete newlines # Interpolate macros $cmd =~ s/ (\b\w+\b) / defined($macro{$1}) ? $macro{$1} : $1 /xge; # Macro definition if ($cmd =~ /^\#define\s+/) { my($key,$value); if ( ($key,$value) = $cmd =~ (/^ \#define \s+ (\w+) \s+ (\S.*) $/x)) { $macro{$key} = $value; next COMMAND; } } # Macro undefine if ($cmd =~ /^\#undef\s+/) { my($key); if ( ($key) = $cmd =~ (/^ \#undef \s+ (\w+) \s/x)) { delete $macro{$key}; next COMMAND; } } next if $cmd =~ /^\#/; # comment chomp($cmd); &log("CMD: $cmd"); print"CMD: $cmd\n" unless $opt{'quiet'} || $opt{'verbose'}; last if $cmd =~ /^EXIT\s*$/; # exit # Trim $cmd =~ s/^\s*//; $cmd =~ s/\s*$//; # Parse # SIZE if ($cmd =~ /^SIZE \s+ (\S+) /x ) { set_size($1); } # SCALE elsif ($cmd =~ /^SCALE\s+(\w+)/) { set_scale($1); } # FRAMECOLOR elsif ($cmd =~ /^FRAMECOLOR\s+(\S+)/ ) { $FRAMECOLOR = verify_color($1,'0/0/0'); } # TEXTCOLOR elsif ($cmd =~ /^TEXTCOLOR (?: \s+ (\S+))? /x) { if ($1) { $TEXTCOLOR = verify_color($1,'1/1/1'); } else { $TEXTCOLOR = '1/1/1'; } } # TEXTFONT elsif ($cmd =~ /^TEXTFONT \s* (\S*)/x) { $TEXTFONT = verify_font($1); } # PREFIX elsif ($cmd =~ /^PREFIX \s* (\S*)/x) { $PREFIX = $1; $PREFIX =~ s/%/%%/g; # Duplicte % to avoid sprintf interpretation } # TEXT elsif ($cmd =~ /^TEXT\s+/) { if ($cmd =~ /^(.*)<<(\w+)(\s*)$/) { $cmd = "$1$3\n"; # Concatenate here document lines my $eof = $2; while () { last if /^\Q$eof\E\s*$/; $cmd .= $_; } chomp($cmd); } &store_text($cmd); $pastechanged = 1; } # PASTE elsif ($cmd =~ /^PASTE\s+/) { &store_paste($cmd); $pastechanged = 1; } # CLEAR elsif ($cmd =~ /^CLEAR\b\s*(.*)/) { if ($1) { # clear some paste positions foreach (split(/\s+/,$1)) { delete $paste{$_}; } } else { # clear all paste items undef %paste; } $pastechanged = 1; } # FRAME PRODUCING COMMAND else { my($repeat,$filefmt,$min,$max,$step); if ( ($repeat,$filefmt,$min,$max,$step) = ($cmd =~ / (?:(\d+)x\s+)? (\S+) \s* (\d*) \s* (\d*) \s* (\d*) /x) ) { $step = 1 unless $step; $repeat = 1 unless $repeat; &do_frames($repeat,$filefmt,$min,$max,$step); } else { # OOOPS &err("Ignoring cmd line: $cmd\n"); } } } # Write the parameter file for an mpeg_encode run &write_berkeley_parameter_file; # Run the mpeg program if ($opt{'mpeg'} && !$nompeg) { my(@options); # Options if ($opt{'verbose'}) { # Don't do nothing } else { push(@options,'-no_frame_summary'); if ($opt{'quiet'}) { push(@options,'-realquiet'); } else { push(@options,'-quiet 60'); } } # Run it print STDERR "MSG: Now running $mpeg_encode_program_name. May take a while...\n" unless $opt{'quiet'}; &log("MSG: Running MPEG"); mysystem("$mpeg_encode_program_name @options mpeg.par"); &log("MSG: MPEG is done"); } # Remove some files &cleanup; if (($errors || $warnings)) { $errors = sprintf("%8d",$errors); $warnings = sprintf("%8d",$warnings); warn <<"EOF" ***************************** * Please check file mpp.err * * ------------------------- * * No. of errors: $errors * * No. of warnings: $warnings * ***************************** EOF } ############################################################################# ############################################################################# ############################################################################# sub cleanup { unlink 'frame.ppm'; &log("MSG: Removing temporary files"); mysystem("rm -rf $TDIR"); if ($opt{'clean'}) { mysystem("rm -rf $FDIR"); unlink 'frame.ppm'; unlink 'redo.img'; unlink 'mpp.log'; unlink 'mpeg.par'; unlink 'mpp.err'; if (-e 'movie.mpg') { print "Delete file movie.mpeg (y/n) [n]"; my $reply = ; unlink 'movie.mpeg' if $reply =~ /^y/i; } } } sub HANDLER { my($signal) = @_; &wrn("Received signal SIG$signal. Cleanup and exit."); &cleanup; exit(1); } sub mysystem { my($cmd) = @_; &log("SYS: $cmd"); my $r = (system $cmd)/256; if ($r) { &err("exit($r) from $cmd"); } } sub open_log_and_err { my $date = `date`; chomp($date); open LOG,">mpp.log" or die "Can't open log file mpp.log $!\n"; print LOG <<"EOF"; Logfile of mpp run on $date Current working directory $ENV{'PWD'} Command file: $cmdfile The leading numbers in the following messages refer to the command file. ***************************************************************************** EOF open ERR,">mpp.err" or die "Can't open error file mpp.err $!\n"; print ERR <<"EOF"; Errorfile of mpp run on $date Current working directory $ENV{'PWD'} Command file: $cmdfile The leading numbers in the following messages refer to the command file. ***************************************************************************** EOF } sub log { # Logbook entry my ($message) = @_; printf LOG "%4d: %s\n",$cmdlinenr,$message; print STDERR "$message\n" if $opt{'verbose'}; } sub err { # Error file entry: ERROR my ($message) = @_; printf LOG "%4d: ERR: %s\n",$cmdlinenr,$message; printf ERR "%4d: ERR: %s\n",$cmdlinenr,$message; print STDERR "ERR: $message\n" unless $opt{'quiet'}; $errors++; exit(1) unless $opt{'force'}; } sub wrn { # Error file entry: WARNING my ($message) = @_; printf LOG "%4d: WRN: %s\n",$cmdlinenr,$message; printf ERR "%4d: WRN: %s\n",$cmdlinenr,$message; print STDERR "WRN: $message\n" unless $opt{'quiet'}; $warnings++; } sub xy { my($index) = @_; # return x and y (0,1,2) for keypad position return ( (0,0,1,2,0,1,2,0,1,2)[$index], (0,2,2,2,1,1,1,0,0,0)[$index]); } sub locopt { # Extract a local option from a string. # Return 1 or value of option if found. # The option will be removed from the string. my($key,$cmd) = @_; my($first,$rest) = split(/\n/,$cmd,2); my($found,$ret); # This routine is rather picky. We want to parse as exactly as possible # in order to disambiguate things. # Matching is done in a paranoid way. We look only in the first # line in case $cmd has more than one (like in here-document text). if ($key eq 'color') { $found = $first =~ s{ (?:^|\s) -c(?:olor)? \s+ (\S+) (?=\s|$ ) }{}sx; $ret = $found ? $1 : undef; } elsif ($key eq 'size') { $found = $first =~ s{ (?:^|\s) -s(?:ize)? \s+ (\d+x\d+) (?=\s|$ ) }{}sx; $ret = $found ? $1 : undef; } elsif ($key eq 'font') { $found = $first =~ s{ (?:^|\s) -f(?:ont)? \s+ (\S+) (?=\s|$ ) }{}sx; $ret = $found ? $1 : undef; } elsif ($key eq 'scale') { $found = $first =~ s{ (?:^|\s) -s(?:cale)? \s+ ([a-zA-Z]+) (?=\s|$ ) }{}sx; $ret = $found ? $1 : undef; } elsif ($key eq 'mask') { $found = $first =~ s{ (?:^|\s) -m(?:ask)? (?: \s+ (0*\.\d+) )? (?=\s|$ ) }{}sx; $ret = $found ? ($1 || 0.5) : undef; } else { $found = $first =~ s{ (?:^|\s) -$key (?=\s|$ ) }{}sx; $ret = $found ? 1 : undef; } # Change the original cmd and return if ($found) { $_[1] = $first; $_[1] .= "\n$rest" if $rest; return $ret; } else { return undef; } } sub store_paste { # Store a paste item and create the mask for it my($cmd) = @_; my($x,$y,$file); my($pos,$w,$h); my($prefix,$threshold); # Extract local options from command my $color = locopt('color',$cmd); my $scale = locopt('scale',$cmd) || 'reduce'; my $size = locopt('size',$cmd) || "${WIDTH}x{HEIGHT}"; my $crop = locopt('crop',$cmd); my $mask = locopt('mask',$cmd); # Verify some stuff $color = verify_color($color,'1/1/1') if $color; if ( ($pos,$file) = ($cmd =~ /^ PASTE \s+ ([1-9]) \s+ (\S+) /x) ) { ($x,$y) = xy($pos); # does the file exist? unless (-e $file) { &err("Paste file $file does not exist"); } # Convert the file to PPM &convert($file); # Crop the file if required if ($crop) { &log("MSG: Cropping paste file $file"); mysystem("pnmcrop frame.ppm 2>/dev/null >$TDIR/tmp.ppm"); rename "$TDIR/tmp.ppm",'frame.ppm'; } # Scale it if required if ($scale) { my($w,$h) = split(/x/,$size); if ($w == 0 || $h == 0) { $w = $WIDTH * (1.0 - 2*$PASTEOFFSET); $h = $HEIGHT * (1.0 - 2*$PASTEOFFSET); } if ($w <= 0 || $h <= 0) { &wrn("Can't scale a paste file to unknown size. Ignored."); } else { scale($scale,"frame.ppm",$w,$h); } } # Store some information $prefix = "$TDIR/" . sprintf("paste%d%d",$x,$y); (undef,$w,$h) = file_info('frame.ppm'); $paste{$pos}{'prefix'} = "$prefix"; $paste{$pos}{'x'} = $x; $paste{$pos}{'y'} = $y; $paste{$pos}{'w'} = $w; $paste{$pos}{'h'} = $h; $paste{$pos}{'type'} = 'image'; # Make the mask and the paste pixmap if ($color) { # A colored black/white paste my $threshold = $mask || 0.9; mysystem("ppmtopgm frame.ppm | " . "pgmtopbm -threshold -value $threshold > $prefix.pbm"); if (pbm_background("$prefix.pbm")) { # needs to be inverted mysystem("pnminvert $prefix.pbm > $TDIR/tmp.pbm"); rename "$TDIR/tmp.pbm","$prefix.pbm"; } mysystem("pgmtoppm rgbi:$color-rgbi:0/0/0 $prefix.pbm > $prefix.ppm"); } elsif ($mask) { # A masked paste if ($use_mpps_own_mask) { mysystem("ppmtopgm frame.ppm | " . "pgmtopbm -threshold -value $mask > $prefix.pbm "); if (pbm_background("$prefix.pbm")) { # needs to be inverted mysystem("pnminvert $prefix.pbm > $TDIR/tmp.pbm"); rename "$TDIR/tmp.pbm","$prefix.pbm"; } } else { mysystem("ppmtopgm frame.ppm | " . "pgmtopbm -threshold -value $mask | " . "pbmmask >$prefix.pbm"); } mysystem("pnminvert $prefix.pbm | " . "pnmarith -multiply frame.ppm - 2>/dev/null> $prefix.ppm"); } else { # Just paste it as it is mysystem("pbmmake -black $w $h >$prefix.pbm"); rename 'frame.ppm',"$prefix.ppm"; } } else { &err("Ignoring cmd line: $cmd\n"); } } sub store_text { # Store text, create a bitmap and remember positions my($cmd) = @_; my($text,$pos,$prefix); my($x,$y,$w,$h); # Extract local options from command my $color = locopt('color',$cmd) || $TEXTCOLOR; my $font = locopt('font',$cmd) || $TEXTFONT || ''; # Verify some stuff $color = verify_color($color,'1/1/1'); $font = verify_font($font); if ( ($pos,$text) = ($cmd =~ /^ TEXT \s+ ([1-9]) [ ]+ \n? ([\000-\377]*) $ /x) ) { ($x,$y) = xy($pos); # Store some information $prefix = "$TDIR/" . sprintf('paste%d%d',$x,$y); $paste{$pos}{'prefix'} = "$prefix"; $paste{$pos}{'x'} = $x; $paste{$pos}{'y'} = $y; $paste{$pos}{'type'} = 'text'; # Create bitmap and pixmap of the text $font = $font ? "-font $font" : ""; # Make a pbm file with the text open PBMTEXT,"| pbmtext $font | pnmcrop > $prefix.pbm 2>/dev/null" or die "Can't open pipe to pbmtext\n"; print PBMTEXT $text; close PBMTEXT; # Get the size (undef,$w,$h) = &file_info("$prefix.pbm"); # Scale if bigger than the movie image size if ($WIDTH && $HEIGHT && ( $w>$WIDTH*(1-2*$PASTEOFFSET) || $h>$HEIGHT*(1-2*$PASTEOFFSET))) { my($w1) = $WIDTH*(1-2*$PASTEOFFSET); my($h1) = $HEIGHT*(1-2*$PASTEOFFSET); my($Nw) = int($w/$w1); my($Nh) = int($h/$h1); my($N) = $Nw > $Nh ? $Nw : $Nh; $N++; &wrn("TEXT too large. Reducing $N times."); mysystem("pbmreduce -threshold $N $prefix.pbm >$TDIR/tmp.pbm"); rename "$TDIR/tmp.pbm","$prefix.pbm"; (undef,$w,$h) = &file_info("$prefix.pbm"); } # Store sizes $paste{$pos}{'w'} = $w; $paste{$pos}{'h'} = $h; # Make the ppm file mysystem("pgmtoppm rgbi:$color-rgbi:0/0/0 $prefix.pbm > $prefix.ppm"); } else { &err("Ignoring cmd line: $cmd\n"); } } sub paste_position { # Calculate the paste position of a $length long item in a $max long space my ($max,$length,$where) = @_; if ($where == 0) { return int($PASTEOFFSET*$max); } elsif ($where == 2) { return $max-$length - int($PASTEOFFSET*$max); } else { return int(($max-$length)/2); } } sub create_font_file { # Try to find a font file in the data section # If yes, extract it and put it in the $TDIR directory # Return the location of the file my ($file) = @_; my ($enlarge,$ret,$foundas); local ($_); if (-e "$TDIR/$file") { # Oh, we got this already. Return immediately return "$TDIR/$file"; } # If the font name ends with *NN, it means enlage ($file,$enlarge) = ($file =~ /^(\S+?) (?:\*(\d+))?$/x); # Let's see ... if (-e $file) { $foundas = "$file"; } elsif (-e "$TDIR/$file") { $foundas = "$TDIR/$file"; } else { seek(DATA,0,0); DATALINE: while () { if (/^begin \s+ \d+ \s+ (\S+)/x) { next DATALINE unless $1 eq $file; # There it is. Extract ... &log("MSG: Extracting font file $file"); open FONT,">$TDIR/$file" or die "Can't open file $TDIR/$file for write $!\n"; while () { last if /^end/; next if /a-z/; next unless int((((ord()-32) & 077) +2) / 3) == int(length() / 4); print FONT unpack("u",$_); } close(FONT); $foundas = "$TDIR/$file"; } } } if ($foundas) { if ($enlarge) { $file = "$file*$enlarge"; my $cmd = "pnmenlarge $enlarge $foundas > $TDIR/$file"; mysystem($cmd); } return "$TDIR/$file"; } else { # Not found return ''; } } sub update_paste { # Updates frame and mask for pasting # This contains both pasted text and images local($_); my($x,$y,$pos,$w,$h,$prefix); my(@pastepbm,@pasteppm); &log("MSG: Updating paste frame"); # Walk through all paste items and prepare pipes for $pos (keys %paste) { ($w,$h) = ($paste{$pos}{'w'},$paste{$pos}{'h'}); $prefix = $paste{$pos}{'prefix'}; if ( $w>$WIDTH || $h > $HEIGHT ) { # Arrgh! Too big. Cut some of it off. &wrn("Image or text at $pos too large for pasting. Cutting it."); $w = int($WIDTH*(1-2*$PASTEOFFSET)); $h = int($HEIGHT*(1-2*$PASTEOFFSET)); scale('cut',"$prefix.pbm",$w,$h); scale('cut',"$prefix.ppm",$w,$h); ($w,$h) = ($paste{$pos}{'w'},$paste{$pos}{'h'}) = (file_info("$prefix.pbm"))[1,2]; } $x = paste_position($WIDTH,$paste{$pos}{'w'},$paste{$pos}{'x'}); $y = paste_position($HEIGHT,$paste{$pos}{'h'},$paste{$pos}{'y'}); push(@pastepbm,"| pnmpaste $prefix.pbm $x $y"); push(@pasteppm,"| pnmpaste $prefix.ppm $x $y"); } # create paste frame and mask mysystem("pbmmake -black $WIDTH $HEIGHT @pasteppm > $TDIR/paste.ppm"); mysystem("pbmmake -white $WIDTH $HEIGHT @pastepbm > $TDIR/paste.pbm"); # Unset flag $pastechanged = 0; } sub do_frames { # Use the filefmt to create file names and make the frames my($repeat,$filefmt,$min,$max,$step) = @_; my($cmd,$ti,$file,$remove,$type,$width,$height); my($ext,$uncompress,$i); $filefmt = $named_colors{$filefmt} if defined $named_colors{$filefmt}; if ($filefmt =~ m{ ([\d\.]+) / ([\d\.]+) / ([\d\.]+) }x) { # Filename is a color specification for an empty field $min = $max = 0; $filefmt = "$TDIR/empty.ppm"; # Create an empty frame of the correct size die "Size of empty frames must be known. Use a SIZE command\n" if ($HEIGHT*$WIDTH == 0); open PPM,">$filefmt" or die "Can't open $filefmt for write $!\n"; print PPM "P6\n$WIDTH $HEIGHT\n255\n"; my($r) = int(255*$1); my($g) = int(255*$2); my($b) = int(255*$3); print PPM pack("CCC",$r,$g,$b) x ($WIDTH*$HEIGHT); close(PPM); } else { $filefmt = "$PREFIX$filefmt"; } FILE: for ($i=$min; $i<= $max; $i += $step) { # Make a file name $file = sprintf($filefmt,$i); &log("MSG: doing file $file $repeat time(s)"); $remove = 0; if ( ! -e $file) { # Try if there is a compressed version of the file foreach $ext (keys %uncompress) { if (-e "$file$ext") { ($uncompress = $uncompress{$ext} ) =~ s/INFILE/$file/g; &log("MSG: expanding: $file$ext"); mysystem($uncompress); if (-e $file && -e "$file$ext") { # The uncompressed file can be removed later $remove = $file; } last; } } } # Die or warn if file cannot be found unless (-e $file) { &err("File $file does not exist"); next FILE; } # Convert the file to PPM format &convert($file); # Check frame size ($type,$width,$height) = &file_info($file); if ($WIDTH == 0 && $HEIGHT == 0) { # frame size not yet fixed. Current size will become the default set_size("${width}x${height}"); } # Check if frame size is still the same. Scale if necessary if ($height != $HEIGHT || $width != $WIDTH) { # New size is different unless ($SCALE) { # SCALE was not set - use default, but issue error message. $SCALE = 'reduce'; &wrn("Frame sizes appear to vary. Scaling enabled (method $SCALE)."); } my $meth = $SCALE; &scale($meth,'frame.ppm',$WIDTH,$HEIGHT); &frame_image('frame.ppm',$WIDTH,$HEIGHT,$FRAMECOLOR) if $meth eq 'best' || $meth eq 'cut' || $meth eq 'reduce'; } # Remove file if it was optained by decompression unlink $remove if $remove; # Paste text and images into the file if (%paste) { &update_paste if $pastechanged; &log("MSG: Pasting text"); mysystem("pnmarith -multiply frame.ppm $TDIR/paste.pbm 2>/dev/null" . "| pnmarith -add $TDIR/paste.ppm - > $TDIR/tmp.ppm"); rename "$TDIR/tmp.ppm",'frame.ppm'; } # Postprocessing if ($POSTPROCESS) { mysystem($POSTPROCESS); } &add_frames("frame.$PRODUCT_EXT",$PRODUCT_EXT,$repeat); } } sub convert { # Convert any image file to a PPM file with name frame.ppm my($file) = @_; my($type,$width,$height) = &file_info($file); my($loop,$convert) = 0; unlink "frame.ppm","redo.img"; # Loop in case there are several commands to execute while (!-e 'frame.ppm') { if ($convert{$type}) { # Yes, we have a command defined ($convert = $convert{$type}) =~ s/INFILE/$file/g; &log("MSG: Converting file type $type"); mysystem($convert); if ($convert =~ /\b redo.img \b/x && -e 'redo.img') { rename('redo.img',"$TDIR/tmp.img"); $file = "$TDIR/tmp.img"; } ($type,$width,$height) = &file_info($file); } else { # No way die "Sorry, don't know how to convert file type $type\n"; } if (++$loop >= $maxconvert) { &err("More than $maxconvert conversion atempts."); } } } sub pbm_background { # Determine the background color of a pbm file my($file) = @_; local($_); my($w,$bcount,$magic,$height,$width,$i); open PBM,"$file" or die "Can't open file $file $!"; $_ = ; ($magic) = split; return undef unless $magic eq 'P4'; $_ = ; while (/^\#/) {$_ = } ($width,$height) = split; $w = int($width/8+.99); $bcount = 0; for $i (1..$height) { read(PBM,$_,$w); $_ = substr(unpack("B*",$_),0,$width); if ($i == 1 || $i == $height) { $bcount += tr/1/1/; } else { $bcount += substr($_,0,1) + substr($_,-1,1); } } return $bcount > $height+$width-2 ? 1: 0; } sub set_size { # set global variables $WIDTH and $HEIGHT with validity check my ($key) = @_; my ($w,$h); &log("MSG: Setting movie size to: $key"); unless ($WIDTH == 0 && $HEIGHT == 0) { &err("Inconsistent SIZE spec. $key (was ${WIDTH}x$HEIGHT)"); } if ("\U$key" eq 'NTSC') {$w = 352; $h = 240} elsif ("\U$key" eq 'CIF') {$w = 352; $h = 288} elsif ("\U$key" eq 'QCIF') {$w = 176; $h = 144} elsif (-e $key) { # it's a file &log("MSG: Getting size from file $key"); &convert($key); (undef,$w,$h) = file_info('frame.ppm'); unless ($w && $h) { &err("Cannot take size from file $key."); return; } } elsif ( ($w,$h) = $key =~ /(\d+)x(\d+)/ ) { # nothing to do here } else { &err("Don't understand SIZE specification $key."); return; } if ($w == 0 || $h == 0) { &err("Degenerate size specification ${w}x${h}."); return; } if ( ($w % 2) == 1) { &wrn("Width $w is uneven. Changed to " . ($w-1)) . "."; $w--; } if ( ($h % 2) == 1) { &wrn("Height $h is uneven. Changed to " . ($h-1)) . "."; $h--; } # Set the global variables $WIDTH = $w; $HEIGHT = $h; } sub set_scale { # set global variable $SCALE and check validity my($s) = @_; if ( $s eq 'cut' || $s eq 'reduce' || $s eq 'exact' || $s eq 'best' || $s eq 'not' ) { $SCALE = $s; } else { &err("Illegal value \'$s\' for SCALE."); } } sub verify_color { # verify a color specification my($color,$default) = @_; local($_); $color = $named_colors{$color} if defined $named_colors{$color}; my(@c) = split(/\//,$color); foreach (@c) { unless ($_ >= 0 && $_ <= 1 && /^\d\.?\d*|\.\d+/) { &wrn("Illegal color specification \`$color\`. Using $default instead."); return $default; } } return $color; } sub verify_font { # Verify the availability of a font file my($font) = @_; if ($font) { $font = &create_font_file($font) unless (-e $font); unless ($font && -e $font) { &wrn("Can't find or create text font file $font." . "Using default font"); $font = ""; } } return $font; } sub add_frames { # Add a number of frames to the list # Use links to multiplicate multiple frames my($name,$ext,$repeat) = @_; my($n); local($_); die "$name does not exist\n" unless -e "$name"; REPEAT: foreach (1..$repeat) { $n = "$FDIR/" . sprintf("f%d",++$index); if (-e "$n.$ext") { unlink("$n.$ext") or die "Can't unlink $n.$ext\n"; } if ($opt{'links'}) { link("$name","$n.$ext") or die "Can't link $name to $n.$ext\n"; } else { # Ouch! wasting disk space mysystem "cp $name $n.$ext"; } } unlink("$name") or die "Can't unlink $name\n"; } sub file_info { # Get file type and image size. Return list with $id. # For PBM/PGM/PPM return list with $id,$width,$height. # Supported formats: # JPEG/JFIF, GIF, PPM, PGM, PBM, PM, XBM, SUNRAS, BMP, UTAHRLE, # IRIS, PCX, TIFF, PS, PDSVICAR, MTV my ($file) = @_; my $header; my ($id,$width,$height); open(F, $file) or die "Can't open $file: $!\n"; sysread(F, $header, 1024) || die "Reading problem with file $file\n"; # Let's see what file type we have got if ($header =~ /^P[1-6]\n/) { # PPM file of some sort $header =~ s/^#.*//mg; ($id, $width, $height) = ($header =~ /^(P[1-6])\s+(\d+)\s+(\d+)/); $id = "PBM" if $id eq "P1" || $id eq "P4"; $id = "PGM" if $id eq "P2" || $id eq "P5"; $id = "PPM" if $id eq "P3" || $id eq "P6"; } elsif (substr($header, 0, 3) eq "GIF") { ($id, $width, $height) = ('GIF',0,0); } elsif ($header =~ /^(VIEW|WEIV)/) { ($id,$width,$height) = ('PM',0,0); } elsif (substr($header,0,7) eq '#define') { ($id,$width,$height) = ('XBM',0,0); } elsif ((substr($header,0,4) & "\377\177\377\177") eq "\131\046\152\025") { ($id,$width,$height) = ('SUNRAS',0,0); } elsif (substr($header,0,2) eq 'BM') { ($id,$width,$height) = ('BMP',0,0); } elsif (substr($header,0,2) eq "\122\314") { ($id,$width,$height) = ('UTAHRLE',0,0); } elsif ($header =~ /^(\001\332|\332\001)/) { ($id,$width,$height) = ('IRIS',0,0); } elsif ($header =~ /^\012[\000-\005]/) { ($id,$width,$height) = ('PCX',0,0); } elsif ($header =~ /^(MM|II)/) { ($id,$width,$height) = ('TIFF',0,0); } elsif ($header =~ /^%!/) { ($id,$width,$height) = ('PS',0,0); } elsif ($header =~ /^ ( NJPL1I | CCSD3Z | LBLSIZE= ) /x) { ($id,$width,$height) = ('PDSVICAR',0,0); } elsif (substr($header, 0, 4) eq "ÿØÿà" && substr($header, 6, 5) eq "JFIF\0") { ($id,$width,$height) = ('JPEG',0,0); } elsif ($header =~ /\s*\d+\s+\d+\s+/ && !-T $file) { ($id,$width,$height) = ('MTV',0,0); } else { # I have no idea $id = "UNKNOWN"; } return $id,$width,$height; } sub frame_image { # frame image to $Wx$H with color $color my($file,$W,$H,$color) = @_; my($type,$w,$h); my($x,$y); # learn about the file ($type,$w,$h) = file_info($file); # black as default color $color = '0/0/0' unless $color; &log("MSG: Framing file $file into ${W}x$H frame"); $x = int(($W-$w)/2); $y = int(($H-$h)/2); mysystem("pbmmake -white $W $H | pgmtoppm rgbi:$color | " . "pnmpaste $file $x $y > $TDIR/tmp.ppm"); rename "$TDIR/tmp.ppm",$file; } sub scale { # Scale an image with method $key my($key,$file,$W,$H) = @_; my($type,$w,$h); # learn about the file ($type,$w,$h) = file_info($file); if ($key eq 'reduce') { return if $w<=$W && $h <= $H; &log("MSG: Scaling file $file to less than ${W}x$H"); mysystem("pnmscale -xysize $W $H $file>$TDIR/tmp.ppm"); } elsif ($key eq 'cut') { return if $w<=$W && $h <= $H; my($w1,$h1,$x,$y); &log("MSG: Cutting file $file to less than ${W}x$H"); $w1 = $w > $W ? $W : $w; $h1 = $h > $H ? $H : $h; $x = int(($w-$w1)/2); $y = int(($h-$h1)/2); $x = 0 if $x<0; $y = 0 if $y<0; mysystem("pnmcut $x $y $w1 $h1 $file > $TDIR/tmp.ppm"); } elsif ($key eq 'exact') { &log("MSG: Scaling file $file to exactly ${W}x$H"); mysystem("pnmscale -xsize $W -ysize $H $file > $TDIR/tmp.ppm"); } elsif ($key eq 'best') { &log("MSG: Scaling file $file with constant aspect to ${W}x$H"); mysystem("pnmscale -xysize $W $H $file > $TDIR/tmp.ppm"); } else { &err("Can't scale with method $key."); } rename "$TDIR/tmp.ppm",$file; } sub write_berkeley_parameter_file { # Write a parameter file for Berkeley mpeg_encode my ($MPEG_BASE_FILE_FORMAT,$MPEG_INPUT_CONVERT); # Set variables according to -product option $MPEG_BASE_FILE_FORMAT = 'PPM'; $MPEG_INPUT_CONVERT = $mpeg_input_convert{$opt{'product'}}; unless ($MPEG_INPUT_CONVERT) { # Disable mpeg_encode run since parameter file is incomplete &wrn("Can't specify INPUT_CONVERT. Parameter file mpeg.par uncomplete"); $nompeg = 1; } open PARAMS,">mpeg.par"; print PARAMS <<"EOF"; # Parameter file for Berkeley mpeg_encode. Written by Mpp. INPUT_DIR ./$FDIR BASE_FILE_FORMAT $MPEG_BASE_FILE_FORMAT INPUT_CONVERT $MPEG_INPUT_CONVERT INPUT f*.$PRODUCT_EXT [1-$index] END_INPUT EOF # frame group size unless ($mpeg_parameters{'GOP_SIZE'}) { $mpeg_parameters{'GOP_SIZE'} = $index; } # write other parameters foreach (keys %mpeg_parameters) { printf PARAMS "%-40s%s\n\n",$_,$mpeg_parameters{$_}; } close PARAMS; } sub check_for_programs { # Check for many programs on the system and show where they are my($n,$title,$name,$fullname,@names,%names,$path,$key); # mpeg program print "\n",<= 80) { print "\n"; $len=0; } else { print " "; $len++; } print $color; $len += length($color) } print "\n"; } #---------------------------------------------------------------------------- #---------------------------------------------------------------------------- #---------------------------------------------------------------------------- # POD DOCUMENTATION FOLLOWS HERE #---------------------------------------------------------------------------- #---------------------------------------------------------------------------- #---------------------------------------------------------------------------- =head1 NAME mpp - a preprocessor for movie frames =head1 SYNOPSIS C =head1 DESCRIPTION B stands for Movie PreProcessor. It prepares a set of images for inclusion into a movie. B was written to allow the processing of heterogeneous material (e.g. images with different sizes and file formats, text pages in yet another format) into a homogenous set of frame files. It also does some basic title generation. Features: =over 5 =item o Convert images from different formats into a single common format. The recognition of many image formats is automatic. = item o Scale, cut or frame images of different sizes to a common size. =item o Create empty frames of any color, as background for titles etc. =item o Paste text into frames. =item o Paste images into frames. Pasting can be masked, so that the movie frames become the background for the pasted image. =item o Multiplicate a single frame (with links -> no extra disk space required) in order to have it incorporated several times in a movie (still frames and/or slow-motion effects). =back B creates movie frames according to the specifications in COMMAND-FILE (or STDIN). The frames are put into a subdirectory F. A logfile F and an error file F are written for reference. B also writes a parameter file F suitable for a run of the UCB MPEG-I encoder. With the B<-mpeg> switch, the B program runs automatically and produces F. B will digest many different image formats. Currently the following formats are recognized and converted: - JPEG/JFIF JPEG compressed images in JFIF format - GIF GIF files - PBM/PGM/PPM Jef Poskanzers portable formats - PM Pixmap - XBM X bitmap - SUN RASTER Sun Microsystems raster format - BMP BMP files - UTAH RLE The UTAH RLE format - IRIS RGB files from an SGI workstation - PCX PCX files - TIFF TIFF files - PS Postscript files - PDS VICAR PDS/VICAR files - MTV The format from the MTV raytracer B will also find compressed images if they have one of the extensions I<.bz2>, I<.bz>, I<.gz>, I<.z> or I<.Z>. To use B, the following programs must be available on your machine. =over 4 =item - Jef Poskanzers B library. B makes heavy use of it. =item - Conversion programs to convert your file format into PPM pixmaps. Many of these programs come with the B library. B also needs to know how these programs are used. If necessary, you can configure this and other information in the configuration section at the top of the B program. More documentation can be found there. =item - The MPEG encoder from the University of California, Berkeley, if you want to produce MPEG files. It's available at ftp://mm-ftp.cs.berkeley.edu:/pub/multimedia/mpeg On some systems, this program is installed as F. If this is the case on your system, edit B and change the name in the configuration section. =back If you are not sure what you have installed, you can check with mpp -check. =head1 THE COMMAND FILE The command file is a script in which you describe the movie frames. Below follows a list of the available commands. Several commands accept a color as an argument. Colors can be specified by I (e.g. 'red', 'green', 'orange') or in I format where the contributions of red, green and blue are specified by numbers between 0 and 1 (e.g. '1/0/0', '0/1/0', '1/0.65/0/'). =over 5 =item SIZE size-spec Set the size of the movie frames. All images will be scaled to this size if necessary. Size-spec is the size in pixels, given as WWWxHHH. It can also be the name of a file containing an image of the correct size, or any of the keywords NTSC (=352x240), CIF (352x288), QCIF (176x144). If no SIZE command is issued early in the command file, the size of the first processed image is used. =item SCALE method Scale all images to equal size using the specified method. The following methods are available. cut If the image is too big, use the center piece and clip anything outside the frame size. reduce Reduce images that are too big. The aspect ratio is preserved. This is the default. best Reduce large images and enlarge small images to fit into the movie size. The aspect ratio is preserved. exact Scale the images to exactly the movie frame size. The aspect ratio is NOT preserved in this operation. not Forbid scaling altogether. This will cause mpp to abort if frame sizes are found to be inconsistent. Unless the method is 'exact', the pictures will be framed as needed to meet the exact movie size. =item FRAMECOLOR color Set the color of the frame around images to I. Color can be a name or an r/g/b specification. The default framecolor is black (0/0/0). =item PREFIX prefix Use this prefix for all filenames containing images. This can be used to indicate a path or a longish prefix, e.g. PREFIX frames/ Prefix will be applied only to frame files, not to font or paste files (see below). =item NNx FILE%dNAME min max step The frame producing command. NNx is an integer I followed by I saying how often each frame should be included into the movie. Use this to hold a single frame for some time, or to create a slow-motion effect by showing each frame a few times. If no repetition factor is given, 1 is the default. The second item is a filename template which can contain a %d (or %04d or whatever). It is fed as a format into sprintf to produce the filenames. The values from MIN to MAX in steps of STEP are used. For example image%d.gif 1 10 3 is equivalent to image1.gif image4.gif image7.gif image10.gif When a file does not exist, B looks for a compressed version of it with extension .bz, .bz2, .Z, .gz, or .z and uses that one. If those don't exist either, B aborts (see, however, B<-force> option). Filenames that look like a color specification (name or C) will produce empty frames of the specified color. =item TEXTCOLOR color Specifies the color for subsequent TEXT rendering. The default is white. =item TEXTFONT F Specifies the font for subsequent text rendering. B has built-in two sets of fonts. These are (size specification is character height in pixels) FONT TYPE | AVAILABLE SIZES -----------|------------------ standard | 8 10 12 14 18 24 cubistic | c6 c8 The 'standard' fonts are bold courier fonts. The 'cubistic' fonts have no oblique lines in them and look a little awkward. They do, however, have the advantage of being scalable by arbirary integer factors without quality loss. Font scaling is activated by appending C<*N> to the name. You may also provide your own fonts. To do that, a special font file needs to be prepared. See the B man page on how to make one. When the F parameter in the TEXTFONT command corresponds to an existing pbm file, it is used as the font file. Examples: TEXTFONT 18 # Select standard font 18 pixels high TEXTFONT c8*8 # Select cubistic font 64 pixels high TEXTFONT my_font.pbm # Select font defined in this font file =item TEXT POS some text for pasting This command specifies text which will be inserted in the following frames. POS is a single digid 1..9 and determines the position of the text in the frame like the numbers on a keypad. --------------- |7 8 9| | | |4 5 6| | | |1 2 3| --------------- If the text runs over several lines, a here-document syntax may be used: TEXT 5 < and I<-font> options. For example TEXT 3 -color 1/0/0.5 -font 14 render this text Should any of the text items turn out to be too large to fit into a movie frame, it will be scaled appropriately. =item PASTE POS F Paste the image in file F at position POS into the following frames. POS has the same meaning as in the TEXT command. You can have either text or an image at a specific location. Like the TEXT command, PASTE recognizes embedded options. These are: -crop Crop away background around the "interesting" part of the image. -scale METHOD Use METHOD to scale the image into a movie frame. METHOD can be any of the ones described for the SCALE command above. The default is 'reduce'. -size WWxHH Use this size limit rather than the full movie frame size when scaling. -mask [ THRESHOLD ] Use a mask when pasting. This way, only the foreground in image will be pasted into the frames. THRESHOLD, if specified, marks the intensity divide between foreground and background in the image. It has to be between 0 and 1. The default 0.5 works well for strong contrast. For weak contrast, use .99 if the background is white, .01 if it is black. -color color Like -mask, but the color of the entire foreground is changed to the specified color (name or r/g/b). Thus, you could make a text frame on some text processing program and save the page in an image format. In order to paste this text in green and as large as possible, the following command could be used: PASTE -crop -scale best -color green textimage or shorter as PASTE -c -s best -c green textimage =item CLEAR [ POS ] [ POS ] ... This will remove text or images from the specified positions. If no positions are given, all text and image items will be cleared. =back =head1 OPTIONS =over 5 =item B<-check> Check the system for the programs required to run B. =item B<-colors> List all color names known to B. =item B<-clean> Remove all files created by mpp. =item B<-force> Ignore recoverable errors. =item B<-quiet> Make less verbose output. This should give hardly any messages on the terminal. You are supposed to check the error file, though. =item B<-verbose> Make more verbose output. This copies all the stuff headed for the log file to STDERR. It will also show the messages from the B program (several lines per frame). =item B<-product> KEY Defines the file format of the produced frames. The default is I. KEY may be any of the following. ppm keep the PPM versions of the frame files pgm produce PGM versions of the frame files pbm produce PBM versions of the frame files gif produce GIF versions of the frame files tiff produce TIFF versions of the frame files bz2 compress ppm files with 'bzip2' gz compress ppm files with 'gzip' Z compress ppm files with 'compress' z compress ppm files with 'pack' Thus, mpp -product gif would produce all frames in GIF format, and mpp -p bz2 would save a lot of disk space by compressing the frames. KEY may also be a system command that formally converts the file 'frame.ppm' into 'frame.img'. For example mpp -product 'ppmtogif frame.ppm > frame.img' would also do a conversion to GIF format. =item B<-mpeg> Call the B program to produce an MPEG movie file F. You may use the -mpeg switch together with any of the -product specifications. =back All options may be abbreviated to uniqueness. =head1 EXAMPLES Here is an example of a command file. # Specify the size of the movie SIZE my_frame01.gif # Set default text color to yellow. TEXTCOLOR yellow # The titlepage in file title.tiff should be masked and pasted # into the first frames. PASTE 5 title.tiff -mask # Ten empty blue frames (r/g/b specification or color name in place # of a filename). title.tiff will be pasted into these frames 10x blue # Terminate pasting of title.tiff CLEAR # First frame 20 times 20x my_frame01.gif # For the moving frames, show "MOVE" in upper left corner TEXT 7 MOVE # The frames 02 .. 50 each only once my_frame%02d.gif 2 50 # The same backwards again, and show "BACK" in the upper right corner TEXT 9 -c red BACK my_frame%02d.gif 50 2 -1 # ... And forward in slow motion (each frame three times) # Show "SLOW" instad of "BACK". TEXT 9 -c orange SLOW 3x my_frame%02d.gif 2 50 # Clear the "MOVE" and "SLOW" labels. CLEAR 7 9 # Repeat last frame with BIG centered green THE END message TEXT 5 -font 24 -color green THE END 10x my_frame50.gif =head1 FILES The following files and directories are used by B and will be deleted or overwritten without warning. The major files written by B are: ./mpp.log ./mpp.err ./mpeg.par ./movie.mpg Some temporary files are used in the current directory but will be deleted when B is done: ./frame.* ./redo.img All other files are placed into two subdirectories: ./MPP_frames/ The frame files ./MPP_tmp/ Temporary files =head1 BUGS Not all of the file formats claimed to be recognized have been tested. Most of them have, however. Image names must be different from color names or B will get confused. You can change the supported color names in the configuration section of B. B needs to run on a UNIX system and is not easily ported to other operating systems. The movie SIZE must be known before the first TEXT or PASTE item is specified. B has a configuration section near the top of the program. If things don't work as advertised, especially file conversion and file decompression, check it out. Send bug reports and other feedback to Carsten Dominik =head1 AUTHOR Carsten Dominik (c) 1995 1996 2001 Carsten Dominik B may be copied, changed, and redistributed under the same terms as PERL. =cut __END__ # This data section contains uuencoded versions of fonts. If you want to add # your favorite font here, produce a font file following the directions in # the pbmtext man page. Then uuencode the file with # # uuencode myfont.pbm myfontname > tmp.uu # # and place the resulting file into this data section, e.g. # # cat tmp.uu >> mpp # # Then you can use your font with # # TEXTFONT myfontname # # in the command script. begin 644 c6 M4#0*(R!#4D5!5$]2.B!85B!697)S:6]N(#,N,3!A("!2978Z(#$R+S(Y+SDT M"CDU(#$Q.0KX!0 P@!P@0 @#ZH!0 CX! @ @"JH!0 B(! @S[Z(@"JH M @ ! 2**(@"J( @ ! 2**(@"*( @@ ! 3[[X@"*( A@ !P 2 (( M@"( @ #X 2 *( !R +X M M M P A10@C@AQP P@ A13[B@A 1P M@ @@ A3ZBB@A 0@@ @@ @!3[C@! 3[X @@ @#XH[P! 0@@ @@ !3XJ0 M! 1P@ A@ @!0@[X!QP !@ M SYC[Z3[[X X B(@(*2" ( X@ B(@(*2" ( C@ M #X B(CX[[[X(#@@ B(B (0*(( @X @!@B(B (0*(( X @!ACYS[X3[X M( @ #[X # M[[[[X #@"*( X#@**)* #@@"*)A@CX@*Z)* @X#[YAC@ XZKYZ X@"(( M CX@BZ)* C@"()@@X#@"")* #@ #[Y@@ CZ+[X @ M #X#[[[Z)PZ:#[KX#Z(!*""*(@*2"JJ M("*X!*"""(@*2"JJ("ZH!+SR#X@+R"JJ("JX!*"":(@*2"*J("Z !*""*(@* M2"*J("#X#[Z#Z)QZ;Z*[X#X M #[[[[Z****+YP "***(B**+Z() "*** B**(B() M#Z+[XB**HC[Y ""*0(B**H@B! "":2(B+ZKXB! "#Z;XCXCZ(CY MP #X ( #X X!AP@ @" M (!P X@ @3X @" (! @@ @2( C[[[[Y#X A@ @0 **"*+R(!@@ @0 M #Z*"+Y#X @@ @0 "**"*! ( @X QP #[[[[Y!X X #X !( M !X #@" @2!@ #@@" " @ M @@#Y@R8C[[[[[SX @P"(@20BJ****2 P@"(@3PBJ****#X @@"(@20BJ* M+[Z ( C@")P29RJ+Z *#X#@ 0 " ( !P " ( M #H! XCCH #JH! @@BH "JX#R**K:+X@@BX "X M!"**I2()@@P !"**IR+X@@@ !"+ZI3Z @@@ !SXC[8+XXC@ M "( #X M M M #X!0 P@!P@0 @#ZH!0 CX! @ M@"JH!0 B(! @S[Z(@"JH @ ! 2**(@"J( @ ! 2**(@"*( @@ ! I3[[X@"*( A@ !P 2 ((@"( @ #X 2 *( !R +X @ end begin 644 c8 M4#0*,3(W(#$V. H $ H #@0 #@ ! ! /X M* @_@ @$ 0 0 /Z2 "@ ((( (! $ "2D@ H """ " 0//[^@A MDI( @ @ 2"@H(0 )*2 ( ( $@H*"$ "2@@ " " !(*" M@A @H( @ @ 2"@H(0 (*" @( ( $@H*"$ ""@@ (" " M!/[^_A @@ "#@ X 2 @(0 @ #^ $@ ("$ M ?( "_@ M M M M M #@ ! H2! 0 X. #@@ 0 M*$A\\/@0( @ @( $"A(4+"($" (?! (" ! H_%#PB! @"! 0 " @ M 0 $A0$(@ ( @0$ @( $ !(?!#X " (_OX (" ! 2!0>_ @"! 0 M " @ /P4&H0 ( @0$ @( $ !(5!Z$ " (?! (" ! 2'P0_@ @ M" "#@ $@0 .#@ #@ M X M(/PP_/R(_/S\ ^ ""$$ 0$B(" ! ^( @A! $!(B @ 0 (" M ((00! 2(@( $ "#@ #^ ""$$/P\_/S\! #@( @A!" ! @$A 0 M(" ((00@ 0(!(0$ " ^ " ,""$$( $" 2$! ^ @ # @_'S\_ C\ M_ 0 ( X " M #\_ M _ #^_OX /@ A(0 #X ^ 3^@D* /@( (2$,# @ @$@H)"@ (" "$ MA# P(/X('+J"0H " X _/P . #A"J_GZ X( (0$ @ @0JH)"@ ( M" "$! (/X( +Z"0H "/@ A 0P$#X ^!" @D* /@ /S\,! 0_(+^ M_@ 0 $ M /[^_OZ" M?!R<@/[R_@ _@!"@(""@A $D("2DH( _H( 0H" @((0!)" DI*" (*Z $* M@(""$ 20@)*2@@"ZJ@!"^/B _A $\("2DH( JJH 0H" CH(0!)" @I*" *J^ M $* @(*"$ 20@(*2@@"^@ !"@(""@A $D(""DH( @/P _OZ _H)\_)S\@I[^ M /P M #@ _O[^_OZ" M@H*"@OX@ (*"@H(0@H*"_H("( ""@H* $(*"@A"" B @H*" M@!""@H(0@@(@ /Z"_OX0@H*2$/[^( " @H@"$(*"DA 0@" M@(*( A""_I(0$( @ ("&B((0@A"2_A" ( " _H[^$/X0_H(0_B M ( X #^ #^ M 8 . X$ ( @ ^ 8$ @"/X $ " M ( ( $! ( B" ! @ " " ! 0 " (@@ 0_O[\_OYX_@ 0$ @" M *"@(*"((( $# ( @ #^@H""@B"" # 0 " ( @H* @OX@@@ 0$ @ M" (*"@(* ((( $! ( @ ""@H""@"#^ ! 0 " ( _O[\_OX@ @ 0 M& X. #X & _@ B M/@ # @! $@' # 0 ( 0!( 0 M 0$ " " $ $! _G \C!#^_O[^_OS\ ! 0 ((0!(@0 MDH*"@H*$@ 0& ""$ 2($)*"@H*"@( &! @A $^!"2@H*"@H#\ ! 0 ((0 M!(@0DH*"@H* ! 0$ ""$ 2($)*"@H*"@ 0 $! @GP$C'R2@O[^_H#\ ! P M ! @ ( P 0 ( " !\ " @ M $ \@ @ &! P\@ \I( ( ! 0 M$)( )*> " 0$!"> "> #\@H*2[H+^$! 0 ((*"DBB" M A 0$ """@I(H@@(P$!@ @@H*2.(+^$! 0 ((+^ MDBB"@! 0$ """$)(H@H 0$! \_A#^[O[^$! 0 M " !@0, @ $ /X M M M M M M ! * X$ X 0 0 #^ "@ (/X (! $ M$ #^D@ H """ " 0 ! DI( * @@@ @$#S^_H(0 )*2 ( ( $ M@H*"$ "2D@ " " !(*"@A DH( @ @ 2"@H(0 (*" ( M( $@H*"$ ""@@ (" " !(*"@A @H( " @ @ 3^_OX0 (( @ LX . $@ ("$ ( _@ !( " A 'R OX @ end begin 644 8 M4#0*(R!#4D5!5$]2.B!85B!697)S:6]N(#,N,3!A("!2978Z(#$R+S(Y+SDT M"C$Q,2 Q,3D* (0 .&!@ ", 4 C@ P( 0 1M@!0 $; # 1 MX\-MQ !LV % 0 , !C9LV$ &SX " P &-FS80 ?*@ ( # 8V;% M! !4K !A , !CP\<$ %8 $$ X &, Q@0 @ /P !PX'L M M M M @ *'G 8$(" $" #!0HR5'! @0\$ M 00 ,%'SQXP(&!A@0 "! P4*'A!@ 8&)'P (( # H&//@!@8 $ ! @ M 'S96L &!@ 0 $$ P */!SX ($ @0 H( 0@ " M $<&'#@9\, P PP 'P (V&##C9L\& &!@ !#888#/P MS88 ,# $-AC9L&3-C 8 # !@@/#P P, -FP 8>#$R8XV; !@8 'V[A&W. &28 V8,&S8P,:,&SIL 3*@#9X\8/C QPP;.FP!4 MJ -F#!\V,+'C!\V; %2< V;,&S8PL;-E39L 3L0#U^X/MWCAV_5LC@!B< M #@ M . \./#[]NW5LV;XP #9LV9FFS94>9MC -FS9X8;%%0 MP\,, \;/!QAL&-C .#CMX\.""C,\^, M # X /P !^ 8 M @." !@ P #@ X ## " 8< " # & & 8, $!C8 $./#AXX^-@!A@ 0& M !LV;-FQAL ,# @8 #S9@V?&&P 8, "!@ ;-FS9@8; !@P $& !^ M\.'SSX\ &!@ 0X P , !^ > M , # 8,8/ !@8 , !@, #!@ \>/&PQXL./#;<\ M,# #88,>#'S9LV;'7 88 -A@QP,5-FS9L8/ #!@ V&#'@Q4V;-FQ@. ,& # M9^,;OU38X\//'P PP P # , & . .!X M & P!@ 8 &"# &@#]NW5NV[X M8(,&@ TL &&S94>;+# @8L %@ 8;''PQL8&"# !ML/#;<0 ;-@!0 $ # 8V;-A !L^ @ , !C9LV$ 'RH M " P &-FQ00 5*P 80 # 8\/'! !6 !! . !C ,8$ ( * #\ <.![ 0 end begin 644 10 M4#0*,34Y(#$W. H -@ 8 & , .. V !@( / 8 P M & ..8P #8 ,!P P P 8 8QW $@ P-@ # ! \.X=SCA@ ! MW'< & B , P'X /!F&8#A M@# 1@ !P P8P,&8!@>& 8 & !P< #!C P!@&!8?!L P M<' 8&,# &!P)AF'8# ' P ?P !@8P, P!AF 89@, P!P M ,!C P& &$8!AF P !P!P P&,# P 8?P&&8& !P!P !@ 8& -@ M,& 9@&&89@8 !P & !@8 <#\?@\ \/ \!@ P # M " , M / M\ !F&8 'P/!\/X'0 < &89@ !P M ' 9AB!P8PS < < 9AF!@& < '!&$X-AC&, < < \&8& 8' ?P' 86@ MV&,8 < 8 &89@ P &'!2"(?A@ 8' 9@^ !P !P8%H8QC& M'!P !F 8 !P?P< 3Q_&,8 !P< &8# 8!@!P ' &!@&,8PS < /! MX!@& 8#P][^!X P " M M M #P #\/\_P]/>?@?O<^#CN>/ #P8@ &88QC&<8P8 89A@&,8QF 8A. 8Q MC&,PQC!@!AL& =QS,, !.%H !C&@:# &,& &'@8!W',PP %H4@ &,>!X, ?P M8 8?!@&L:S# 4A: 8QH&@SYC!@QAF&,:QK,, !:$\ !C&,8##&,&#&&88Q MC&Y[ MGG\/ &,9AC& 8&V8S#&,Q@P#X ,!@ !C!@#!L!@;9C,,8S M&# P !@& &,& ,&8&!MF,9AS,X,!C & 8 ]Y^ PYQ^.V]X\&X=A^'X M8!@ # 8 & !@, , !@ 8 , M '@ / #P M P P& MP # & 8!@ ?CG/>P[G..?@8!@& M #( P&,8S;&88Q&!@& 8#( #(?@ # 8QC-L/!C P& 8!@?@ ?A, ,! MC#8VP8#8& P!@#!, !, P&,-A:#P-@P!@& 8 #,9P<&89@ M<&(& 8!@ '@[AP9CG!P?@8!@& & !@& M8 8 # 8# 'P M M M M M M M M -@ 8 & , M .. V !@( / 8 P & ..8P #8 ,!P P P 8 8QW $@ M P-@ # ! \.X=SCA@ !W'< & B , P 8 ,,88 X# , ,',,X'@& ##// . P # #!N!V MP!@ !YX !@8 P P8 !@8 8 P& _\/ ,& 8& M& ( /_ ^#X ?'P M M M M M M M M M M M , M P !@# # ,P; ,!X !@!@8 , 8!@ P#,&P/ MPS 8 P# # , 8 , S!L&,,P' & , P'^!@ # & # ,P;!@! MX#8# # , > 8 P# P#,?\< !PP @!@!@'@& 8 P , !L#X#P M& 8 8#,/\ & 8 # ; '#@#L & & #_ # & P ?\ P'AO M !@!@ & P!@ !L&,#,9@ 8 8 !@ , P # ;!^ S&8 M # , 8 & , P &P& '@_ P# !@& !L!@ M, P P!@ !@8 , M M M !@ 8'@& \!X ,/X#P M_@ ,#,'@9@S '# #@,8 P # S#8$,(8#PP P & M & \ P88& # ,!L, 8 # '@\ 8&&!@ P. S#\&X P M '@\ /\ &!A@8 8 P(PQAS 8 '@< #_ # 88& , &&, 888& # M@#P P&&!@& !A_@&&&!@ > / , S 8# (8 P!AA@P !X M \ < !P& ,P&!C#, ,,P,P, '@# ' /CX!X# 8 M P P 8 , M $ M M ' / M ^ #8&8 'P8P>#^ ] P !C## M , # #&,&!@8PYP & / (@PP / \ QC=@\&&,, !X \ !P&<' M. /'_#P 8LH)!C& '@#P V [!P#@/!_P/ <+*&8?A@ > . 8P P M' X,"RAF&,8 !P/ &, , \'_ \# LH?AAF !X/ !C & # MQ_P\ -\,,88PP !X/ -@#@< X / \ ,# ##&,.< !X# !P'@' . P M , # 8QY[^ \ 8 !@ #X P M ( M M M ^ 'P8P #\#_'^#T]X_P_GO/P M<.]\? #&,& 9P8PQCG&,!@!@Q@P##'&,8 !@S=@ &,& , PQC 8 8,P, Y MQYC& ;LLH !AAD#(8 8P& # .<:9@P %E+* 88? ^& '\!@!@\ P#_& MV8, !92R@ &&&0,ACYC 8 8/@, VQMF# 64LH !AA@# 8,8P&#&#,#&-L99 M@P %E-\ 8P8PP##&,!@Q@S QC#&>,8 !OC &<&,, XQC 8,8,8,8PQCC& M 8 8P #\#_'P#X]X_Q\'W/^>>^X? #&#X M ? M M M / M _@?#\ ]'^>>\]Y[SWGG\# &,,89@9Q,C#&&,,88PQC P ! MAC&&,,,3(PQAC;#,&89@, 89@QC#@$R,,,PVP>!F X# &, M8,9@> ,###,-L# / , P !^&#'P!X# PPS!^ P!@& , 8!@ MQF ' P,,'@9@> 8#@# & ,88PPP,##!X&8,P& S P !@#&& M,.8# 9@,!F&&!@8P, _ ?#[B\#\#P# 9CSQ^'\# #, M P _\ !^ / '_O_ M !_X M M !@ 8 8 > , !@ MX ' \ # , # !@'@ 8 & P 9@ !@# P 8#, & !@ M , & 8 P , &!A@ P 8 # !@ & , !@!@ $ \&X' MH.P/!_!W !@" 8 8 9AS#.&<&8& S@ 0!@ ' & 889AC M###!@88 , , P!@ !^&&8 PP_P8&& !@# , 8 QAAF ,, M, &!A@ 8 P !@& ,8 '_@ M 8 # !_X !\ M M M 8 . !P \ # # !@ 8 P, # M8 P 8 & ,# P & , & P , !@ M# !N!X'X-\#!VSN \.X'<]P?@ 8 0 >?P& , 8 X # ,888PPS# M#&8! # " X ' ;8 P#&#,-L'@9@, P P P;8 #; < , Q@S!: P&8 M& & , 8 < #@ # ,8'@?@> \# !@# & QC.!X&8,P/!C M 8 P!@ 'P=P,!F'.!@?P& , 8 P ! M@# & , , P# /@ M M M M M M M M M M M M # #, M P& \!@ 8 X< S 8#P , 8 8 & '#F& ,P M &!F # & & !@ PQS@ #, !@PP P P 8 .< 8 ,,\\ X# M , ,&X'8# & 'G@ &!@ # #!@ &!@!@ # 8 #_P B\ P8 !@8 8 @ _\ #X/@!\? end begin 644 14 M4#0*,3DQ(#(Q-0H !@ S !@, M / , & #AP S # > / , & , #ASAP S # M > , & , #AQS@ S & S , "!_#O ]SSP, !SAS@ B M & S , !!_#_A_SSP, !SA_@ B ,!A@ , #!QSCAA@, !_AM M@ ,!A@ , #!@S!AA@, !MAM@ 8 , #!@S!@S , M!MAM@ 8 , #!@S!@S , !MAA@ P , #!QSC@> M , !ACSP , P , #!_A_@> , #SSSP ,!@ , #!O M ]@, , #SP 8!@ , #!@ !@8 , 0# / M '!@ !@X , @# #_X/ !^#X 'S\ , #_ MX !\#X 'S\ M M M M M M M M M M !@ ; , !@! M@ . S ; , X & # P , !@# . S ; ?AL & # P , M # # . S ; _A$ > , & 8!_@, # & . S!_AAAL _ ( & 8!_ M@, & & . B!_A@ Y@S 0 , , , , & , . B V!X ' P , M , _ , , , , V _ \ 8 , ,!SC_P , 8 , !_@'AG ] M@ , , A#_P 8 8 , !_@!@-A_@ , , , 8 P V!C M@(AG , , , P P , V!_ -A_@ , , , P!@ , M V!^ ' ]@ & 8 , !@!@ V , & 8 !@# M , # P # # # P # M M !@ !@> , > > ' _@' MA_@ # _!\ _ _ / _@?A_@ P # S!\!C@S ? M P X!A@ P#P &!A@,!A@# ; P P # #P/ &!A@, ! M@' S _!N # / \ ,!A@, #@> S _A_ # \!P #_P ,!A M@, ' ?!C SAS@& !P \ #_P 8!A@, . #A_@!AA@& \ / M 8!A@, < !A_@!AA@& / #P P S , X!C@#!CAS@, #P P , M , P _!_A_A_ /A_ _ , P , ,!@ >!_A_@^ /@^ > , M 8 !@ 0 # M @ # M M > > > _ _ M ? S!\#^ >P !@ !SAA@ P !@ _AAA^#_!_P !@!X !AAA@ # MP !X QAG@> QAQP !X > S!A@, , /#_P> !AM@S QC@P > '@ >!C@, M , \#_P'@#AM@S _# '@!P _ _@ !P !P/!M@S _C !P'@ !S M@=@ \ '@,!MA_@PS '@> !A@!@ /#_P> ,!GQ_@PS@ >!X M !S@# #S_QX !@!A@PQPP !X!@ _!_ , , P !@ ,!@#S[_A_P M!@ >!\ , , , QCS[_ ? 8 ? M 0 @ M M > M > S #^#_S_P>SSQ_@?SWS\#ASGP> S!A@ #_C_S_ MQ_SSQ_@?SWS\#ASWQ_@ !AAG@ !C@PPPQQQA@, #!C P!SAQAS@ !GAM@ !A MPVPVS@QA@, #!F P!SAYCAP !MAM@ !@P^ ^# !_@, #!L P!_AIC P !MAM M@ !@P^ ^# !_@, #!\ P!MAMC P !MAM@ !@PV V#'YA@,##!^ PQMAEC P M!MAGP !APPPP#GYA@,##!G PQMAGCAP !GQ@ !C@PPP!PQA@,##!C@PQAAC MAS@ !@!@ #_C_S^!_SSQ_C^#Y[_SSS[A_@ !@ Q@ #^#_S^ ?CSQ_@\#Y[_ MSSSY@> Q@? ? M M M / #_ M >#_ >S_SW[S[S[SSSQ_@/ #_A_C_A_S_SW[S[S[SSSQ_@, M !AQSAAQAS,QAC S QAAAAC@, !@SAQ@Q@S,QAC S,PS S!G , M !AS QAQ\#,QAAAC,P> _ & , !_C Q_@_@,!AAAC>P, > , M , !_# Q_ 'P,!A@S#>P> , 8 , !@#AQCA@P,!A@S!S@S M , Y@, !@!SAAQPP,!S@>!SAA@,!Q@, #\!_C\Y_A_@_ > M!ACSP_!_@, #\ >3\YO!_@> ,!ACSP_!_@, _P M , S@ / #_X M / #_[_X #_X M !@ & M !@ \ , , #@ #@ /P & , P \ > , #@ #@ ?P M , , P , > & !@ !@ 8 , , 8 , S " _!O ]@]@>!_ M@]P , , 8 , S !!_A_A_A_A_A_A_P , , , ,!A@ !AAQSCCC MCAP8#C@ , 8 , ,!A@ !A@S!C!C P8#!@ 8 , & , !_A@ MS #!C_P8#!@ , , & , #_A@S #!C_P8#!@ , , # , M#!AQSASCC@ 8#C@ , , # , #_S_A_Q_Q_Q_A_@ , , !@, M !]SO _ ]P?Q_@]@ , , !@, !@ , , MP\ #@ , & P\ #_X _ & M #_X ^ M 8 #@ , &#@!\ M 8 , #@ , &#@!\ , , !@ !@ , M , , !O!\!_!G@,#;CO >#O ]SS@_@ , , !_A\!_!G@,#_S_ MA_C_A_S_Q_@ , , !Q@, #!N ,#NQQCAQQSC@\QA@ , & !A@, #!\ , M#,QAC Q@S!@X!X & , !A@, #!\ ,#,QAC Q@S!@P _ , , !A@, # M!N ,#,QAC Q@S!@P /@ , , !A@, #!G ,#,QACAQQSC@P!A@ , , #S MQ_@##CQ_CN[SQ_A_A_C_!_@ , , #SQ_@##CQ_CN[SP>!O ]C_!_ , , M # !@ !@ , , ' !@ !@ M , 8 !^ #X 'P 8 !\ #X 'P M M P & 8 P M , , , P , , , #_#GCSSQ[[ M[SQ_@, , , #_#GCSSQ[[[SQ_@, , , Y@ P!AAA MADPQAAAC , , , Y@ YA_@ P!AAAANP;!A@& 8 , &!_@ !_AO P M!A@S!NP. S , , , ,!O !O P!A@S![P; S 8 , , , M PQC@> [@Q@> Q@, , , _Q_P> QC[X>!_@, , , M ? ]P, QC[X,!_@, , , 8 , , , M X , , , #\ & , M 8 #\ M M M M M M M M M M ! M@ S !@, / , & #AP S M # > / , & , #ASAP S # > , & , #AQS M@ S & S , "!_#O ]SSP, !SAS@ B & S , !!_#_A_SSP, M!SA_@ B ,!A@ , #!QSCAA@, !_AM@ ,!A@ , #!@S!AA M@, !MAM@ 8 , #!@S!@S , !MAM@ 8 , #!@ MS!@S , !MAA@ P , #!QSC@> , !ACSP , P , M #!_A_@> , #SSSP ,!@ , #!O ]@, , #SP 8!@ M , #!@ !@8 , 0# / '!@ !@X , @# I #_X/ !^#X 'S\ , #_X !\#X 'S\ end begin 644 18 M4#0*,C4U(#,S, H 8 M =P !@# ^ . P \#P !W , , # MX < X # \#SP/ '< P!X , !P #@ , M #P/'AX =P & '@ P #@ P 'AX>'@ B M8 S # & # >'ALV "( # #, , , _P/? M#[Q\^ , !LV&S8 (@ , 88 P 0#_ __ __'SX P &S8;-@ M !@!A@ # , \<#CP,# # ;-AGF & , M P#@X<' X< , !GF&,8 P P # ,!A@,!A@ P M &,88Q@ # # , P&& P'. # 8QA@& 8 M , P# 88# ,P , !@&& 8 !@ P # .#AP M< W P & 88!@ X , # , \<#CP!X # 8!CX? M #@ P , P#_P/_ '@ , #X?/A\ < & P M # -\ /L , P /A\ !@ 8 # , P P!P # M , # , P# # & , @ , M /_^ ^ ' , , X __X#X /X#^ '\ M?P _ /X ?Q_ M M M M M M M M M M M M M M M M M M M M M M M M M !@ 9@ P !@ & # !W M!F # !@& # & P > '< &8 ]@#P . . < M , P # !X =P 9@'^ 9@ > X P P P # 8 M '@!W !F PX## #\ ' & !@ S, , 8 !@ > "( &8#!@,, 8 MP 8 8 & #_P P !@ , !X (@#_X, 9@!@ # !@ 8 #\ # M , P '@ B /_@X \ & ( , P # , P & , , MP!^ ' , P # _ P & 8 P S !^ #P X # , M#,!_^ 8 # # #, < X 'S , P ;\ P # !A@ P , !@ P '_P,# #,#'@ M # , # !@ & &8 X< 88,, , P , & M P , 9@#_@!A@QX P # P P # P !F M-\ #,#_X !@ 8 # # 8 # &8 # '@'S@ & !@ M 8 !@ 9@ , 8 & !@ , M !F P P P , P # M #@' P & 8 M M M M M M !@ & > M!P /P _ < ?\ 'P/_ P'^ _ !_@'^ #P!_P!_ _ M\ # 88#\ .' X< ? & / # P < M 8# P P P,# P!L 8 !P # < '@ !@,# # # P # , MP!@ & 8 '@!X , P, , # < S &\ X !@ !X > M P# P P < #@&, ?\#? & > '@ & ,# # M #@!\ 8P!QP/^ P '@!X !_^ 8 P, , < 'X## #@X< # M !X '@ '_X # # P P #@ !P,, &# P , '@ '@ M , ,# # < #!_\ 8,# !@ '@ '@ !@ P, , #@ ,'_P M !@P, & '@ '@ & # P P < & P , P.#@P 8 '@ '@ M . ' P && # #@ <' P#AP'' # '@ ' X < # ?X#_P M?_ _X ?P'_ ?X , ' ' !P 8 > /_!_\!_ !_ /P ? P M 8 !@ # M, ( P M M M M M M M > \ M ' '^ /\ /P!_ /P!_X ?8 M X< PP !_@&, _ '_P'_@ < # P&!@ M !P ' &' P8 > &#@X. < \ ,# 8& > / 8,#!@!X 8 M&# 8 \ \ P,!@8!P ' '@ / !@P,> ,P!@8 '_X / M<#=@'. ?\& \ \ ?X!QX '@ / '@-F 88!_X8 \ M #P #AP#]@ > \ X V8!A@&!Q@ #P \ ,# 'F > M '_X / # #=@/_ 8#& \ / P, 8 > ?_@#P , ,_ _\!@, M< / #P # P # > \ Q\' X& PP #P \ .' M< < !P > / # # 8!@8'#@8 \ ' ?X!_@!P ' < !P , M, #\_'_X'_@ ' _ 'X ' X P 88/S\?_ 'X M # !_@ 8 M !X ! M M M M M M M ' M ' !_ #_P'_X?_P'[/S\/_ /_/SX?X#P/'A\!\ !_ &, M /_P?_A__!_\_/P_\ _\_/A_@/ \>'P?\ &, P8 ,#@8&!@,.!PP, , M & PX P >'@\&#@X P8#!@ P&!@8& PP## P P 8#' # !X>#P8,!@ M #!@,> # <&-@8S' ,,# # !@,X , &S8-AAP' ,> SX , P8P!C M8 P, , & W P ;-@V&& , SX#=@ P#!_ '\!@ #_P P 8#X # !L MV#,88 P #=@-F # ,'\ ?P& /_ # !@/P , &>8,QA@# -F V8 M, P8P!C 8/PP, , & [@ P 8Q@QF& , V8#=@ P#!C &,!@_# P P!@ M8#' # !C&#&88 P #=@,_ # ,& P8 ' ,,# # &!@,. ,&& 8,-AP' M,_ Q\ ,!@8#!@ , PP, , 8& P8 P88!@PV# 8 Q\# P.!@,& X M'# P P!PX#!P#!A@&#!X.#@ # , /_P?_Q_@!_X_/P_\#_ _#Q_^/A\ M?'@?\ , 88 _^!__'^ !^#\_#_P'X#\/'_X^'Q\. ? 88!_@ M !_@!X M !X M M M M M M M M ^ '_@!\#_@ _8?_A\?/A\^'S\_/S\/_@#X ?_@? M\/_@'_A_^'Q\^'SX?/S\_/P_^ , 8'#@X,' X.&,8,!A@&& 8 M<#AP.# X P !@,,!@P,# 88Q@P&& 88Q@X<#AP,' # M & QP'# P,!AC&# 8,#!C&!S@&& PX , 8#& ,,# X &,8 M,!@P,&>8#\ ,P # P !@,8 PP .'!P. , .!@# ?X ?\/P\?_ ?X!_P!X X #P #^ !@ , 8 , !X M < 8 # < , P P P '@ X !@ , M!@ P # # # S !@ & P & # , & M , #, # 'X ;X _8#[ /P#_P#[P , P 8 P 88 $ _P!_X M/_@_\#_P/_ __ P # P # !A@ ##@'C@X>#CP.' & #CP # M < # , & <'' X<'!P. 8 <' < #@ & P M /X!@,8!A@,& 8!@!@, #@ ' 8 # #_@& Q@ & P?_@& M & P ' , P , <& 8#& 8#!_^ 8 8# , P # M P !@8!P<< !P<' !@!P< P # & # &#@'C@X M.#CP.#@& #CP # , 8 , ?_A_^#_X/_P_^#_P/_ , M P P P _>'O@#^ /O _@/_ /L P # # # M P # , & , M # , 8 8'P /_^ < 8 X ? M __X !_@ X M '\ M M M M M < \ ' . > ? < 8 M #P < X!X !\ 8 P # !P #@!@ P M P # , & # M # , P 8 , , P #? 'P _ MP!CP P#O>// #\#WP ^\// /L P # /^ ? #_ &/ # /_\]^ _\/_P M/_P_^!_P # , \< , , 9P , ./P_\/GN_/P/P#? #[!_X#?@ # , , M , , , P P P P M P & ' # # & !P /X M _@ !_ !P _ #^ '\ M M M M M M #@ P < !@ M 8 # 8 & # M , P 8 , P # M !@ P # , ?^#P\/S\^'Q\ M^'SX/_ # , P !_X/#P_/SX?'SX?/@_\ , P # M 'A@ !@ ,# P,&,8&& P,##@ P # , 'A@ 'A@_. & P M,# P8Q@,P#AP,< ' , X _. _.'/P 8 # P&& S, > && #@ X M P !P'/P '/P8> !@ ,# 88#>P P 8> M & P, S -[ '@ S #@ # , P 8 # P#, \\ S M#< <, , P # !QP.' '@!S@&& '@#@P P # , M #_ ?_ > &&!\^ > /_ # , P 'P ^\ M P 88'SX P _\ , P # ' P # M , 8 # , P M #@ & & !_ M . !P '\ M M M M M M M M M M M M M M M M M M M M M M M M M & M '< 8 P /@ #@ , / \ =P # # M ^ ' . P / \\#P !W , > # < X # M \#QX> '< !@!X , X , !X>'AX (@ M & ,P P !@ P 'AX;-@ B P S # # /\#W MP ^\?/@# ;-ALV "( # && , $ _P/_P/_Q\^ , !LV&S8 M 8 88 P # /' X\# P P &S89Y@ !@ # M , X.'!P.' # 9YAC& , , P# 88# 88 , M !C&&,8 P P # ,!A@,!S@ P &,88!@ & M # , P&& P#, # 8!A@& 8 , P#@X M<' -P , !@&& 8 . # P # /' X\ > P & 8^'P M X , # , _\#_P!X # ^'SX? ' !@ , M P#? #[ # , #X? 8 & P # , , < P M # P # , P P!@ # ( # M #__@/@ !P# # . /_^ ^ #^ _@ ! I_'\ /P#^ '\?P " end begin 644 24 M4#0*,S,V(#0T. H X M X ! M !X #@ #^ > !X #@ M #\ ?@ #C@ !P #@ #^ > !X #@ O@#_\ M ?@ #C@ !P 'P #^ / !X #@ O@#_^ _@ M #C@ #@ /X #@ / !X #@ OP'\^ ^ #C M@ #@ >\ #@ ' #@ 'P'P_!^ #C@ M '@ << #@ '@ #@ 'X/P_!^ #C@ ' M \> #@ #@!_^!^/@ /S\_Q_@#@ 'X/P[CN #C@ ' !X/ M #@ #@!_^!^_X __\_Q_@#@ '<=P[CN #C@ . !P' M#@ !P!_^!__\!__\_Q_@#@ '<=P[WN #C@ . !P' #@ M!@ . /@>#X?@/ . #@ '>]PYW. !! > #@ M. / /#@'@' < #@ '.YPYW. < #@ . . M''@'@'@< #@ '.YPX^. \ #@ . . '' # M@#@X #@ ''QPX^. X #@ . . '' #@#PX M #@ ''QPX^. X #@ . . '' #@!QP #@ M ''QPX<. !P #@ . . /'@'@!YP #@ ' M#APX<. #P !P #@ . / .#@'@ [@ #@ '#APX M . #P #P #@ . /@^#X?@ _@ #@ ' !S_!_@ M '@ #@ #@ . /_\!__@ ? #@ OX/__!_@ M '@ #@ #@ . ._P _[@ ? #@ OX/__!_@ ' M ' #@ . ./ /C@ . #@ OX/\ . ' M #@ . . #@ . #@ . / M #@ . . #@ < #@ < . M#^ . . #@ < #@ < . #^ M > . #@ X #@ 8 !___@#^ !_ M\!_X _\?_ #@ !___@ !_X!_X M _\?_ !___@ !_P!_X _ M\?_ M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M X .. < M ' X .. < M ' !X #@ .. < ? '@ !P X . / M!P 'P #C@ .. _8 _@ '@ !P X . . !P M 'P #C@ .. #_X Q@ / #P \ . . #@ M 'P #C@ .. '_X!@P ] / '@ > . #@ < #@ 'P M #C@ .. /!X!@P #_ . ' . &., #@ < '@ 'P #C@ M .. . X!@P '_ . ' . /N^ #@ \ ' #@ #C@!__@ M. X!QP@'C < / / '_\ #@ X ' #@ #C@!__@/ M_C@' < . ' !_P #@ X . #@ #C@!__@/X ?/@' M 8 . ' _@ #@ !P . #@ #C@ << '_ ^ '@ 8 M > '@ _@ #@ !P > #@ !! << #_P #X #@ > M '@ ![P'__P #P < #@ << ?X ? #P > '@ M #QX'__P #@ \ #@ << #\#\^ /YX < #@ '@\ M'__P '@ X #@ #__ \#Q_ /]X < #@ # 8 #@ M ' X #@ #__ . <##C@>=X > '@ #@ M' !P ! #__ . < #!@ '@ #@ . ! MP XX / \ #!@<'@ > '@ #@ . #P M XX /_X #!@<'@ . ' #@ > #@ M #@ XX /_P !C ?_X . ' #@ < #@ 'P M XX .> !_ /]X / / #@ < ' #@ M XX < ^ 'YX ' . X ' XX M < '@ > X / XX < M #@ < !X . XX < M #P \ !P . < M !P X !P ! MP X M M M M M M M M M ' M ' M / ^ P M^ 'X '@ __ 'X#__ . #_@ #P #_@ ? M\ /@ __ ?X#__ . . '_P ?P '_P!_^ / M@ __ _X#__ !P > < '!P _P /!X!X/ ?@ X M !\ #@/ #P !^ < /!X _P . X!P' _@ X #P M#@. /P '\ \ . X 9P . X ' [@ X '@ #@. M _@ ?P X . X !P . X ' ![@ X ' . M#^ _ X . X !P X / !S@ _X ' > 'X # M\ !P . X !P !P '^ #S@ _^ ./ < ?@ /P M !P . X !P #P '\ #C@ __ ._P < !^ _@ M #__@ #P . X !P '@ '^ '#@ X? /_P \ '\ !^ #__ M@ #@ . X !P / ? /#@ '@/AX X /P _@ #__@ M '@ . X !P > ' .#@ #@/ \ X '\ /P ' M. X !P \ #@?_X #@. < !X !^ #\ ' . X M!P !X #@?_X #@. < !P ?@ _ . . X !P # MP #@?_X #@/ < !P 'X ?P > . /!X !P '@ M'@ #@! '@' \ #P #^ '\ > < > '!P !P / #@/ # M@#P/ 'AX #@ _@ !^ \ ^ < '_P __@?_X#__ ?X#_^ M '_X '@ /P > \ ^ < #_@ __@?_X#_^ ?X!_^ #_P M ' #P . X < X ^ __@?_X _X ?X ?X _ ' M !P !P X M !P !X M #@ !P M #@ !P # M M M M M M M M M M M M M ^ 'P _ M #_@ ?\ /X !_@#_@ __@ '\P X '_ MP _^ . X _\ #AP#_P __X ?_P ' \ '!P \> M > \ !_^ ' P#_P __\ __P '@ _ . X!X/ M !^ _ !P? & P .X ' >!\'P 'X ?P . X!P' M '\ ?P !P' . P .X ' .#P#P #^ '\ . X!P'@ < !P ?P M '\ !P' , P .X ' .#@!P _@ !^ '!P!P'@ ^ #X _ '__P! M^ ' ,'P << ' .'@!P /P ?@ '_P!P'@ ^ #X #\ '__P ?@ M/ ,?P << ' >' #\ 'X #_@!X/@ < !P /P '__P 'X > ,8 MP << '_\' _ #^ #_P \?@ _@ #^ !^ ,PP X. M '_P' ?P _ 'SX ?[@ !^ _ #X ,PP X. '_X M' 'X #^ / \ ?S@ _@ #^ #P ,PP X. ' ^' M ?P 'X . < 'G@ /P '__P 'X #@ ,XP!__ ' /' M _ ?@ . < ' #\ '__P ?@ #@ ,?\!__ ' '' #\ ! M^ . < / _ '__P!^ ,.\#__@' ''@ /P '\ M . < . #P ?P '\ , #@#@' '#P!P _@ ?P /A M\ P^ < #P '\ ?P . #@#@' /#X#X #^ _ '_X!_\ M ^ '@ !^ _ #@ & /\?\__^!__P 'X \ #_P!_P ^ M ' > \ 'P ' P/\?\__\ __@ '@ X _ _ < / M . X #@ #AP/\?\__P /^ ' . M !_@ < M _ < M 8 M M M M M M M M M M M M M _ M 'X !_@ _^ '__@__^ #\P_W^!__ __'_/X_^ M /@#]\#_ 'X /\ #AP __@'__@__^ /_P_W^!__ __'_/X_^ /@# M]^#_ ?^ <. ' P __P'__@__^ ?_P_W^!__ __'_/X_^ /P']_#_ M __ X& & P .#X X#@' . \#P. X #@ !P X'@#@ 'P'P? #@ 'X/P?@<#@!P !@& , M'P . > X[@''.#P!P. X #@ !P X\ #@ '<=P=P<'@!X !@^ ,?P M . . XX '' #@ . X #@ !P YX #@ '<=P=X<' X !C^ ,8P . M. _X '_ #@ /_X #@ !P _P #@ '>]P<' X !F& ,PP . . XX '' #@_X M. X #@ #@.''QP X!P' #P!P. X #@ '@ X#@#@.' ! MP \#P'@.!X #@ ' .#P#P' >#@' X<.# M@#@< <' !P/!X X. .!P #@ ' .#@!P' .#@' X<.#@#@> M \'#AP'CP \> .#P #@ ' .'@!X' .#P' X<.#@#@. X'# MAP#W@ << .'@ #@ ' .' X' >#X X<.#@#@. X''QP!_ M >\ ./ #@ ' >' X' \!_ X<.#@#@'!P#GS@ ^ /X M . #@ ' \' X'_X _X < #@#@'!P#GS@ < 'P > M #@ '_X' X'_P /^ < #@#@'CP#N[@ ^ 'P \ #@ M '_P' X'_@ !_ < #@#@#C@#N[@!_ #@ !X #@ M '_ ' X'#P /@ < #@#@#C@#N[@#W@ #@ !P< #@ M ' ' X'!X#@'@ < #@#@#W@#\?@'CP #@ #P< #@ M ' #@!P' \#@#@ < #@#@!W #\?@'!P #@ '@< #@ ' M #P#P' >#@#@ < !P' !W !X/ /!X #@ / < #@ ' !X' M@' /#P'@ < !\? ^ !X/ > \ #@ . < #@ _^ __ _X/ MC__ /_X _^ ^ !X/!_C_ _^ ?_\ #@ _^ ?^ _X'C_^ M/_X ?\ ^ !P'!_C_ _^ ?_\ #@ _^ /X _X'CGX /_X M'P < !P'!_C_ _^ ?_\ #@ < M #@ _PX M #@ !__X M #^ !__X M #^ !___@ !P?@ M #^ /__]___@ M /__]___@ / M__\ M M M M M M M . M . ( M '@ / _@ < > !^ # M\ '_ \ /@ ' _@ < > !^ #\ M ?_ !\ /@ ' _@ ^ / !^ #\ ?_ M !\ > #@ #@ !_ / . < \ M #P < #@ #@ #W@ ' . < X M#@ < #P #@ #C@ '@ . < X #@ M< !P #@ 'CP #@ ?X ./@ 'Y@!^< 'X /_\ /GX #@ < M !P #@ /!X #@!_^ ._X ?_@'_\ ?^ /_\ __X #@ < X M #@ . X !P!__ /_\ __@/_\ __ /_\!__X #@ < X #@ M . X !@!P/ /P^!\/@?#\!\/@ X #X_ #@ < \ #@ M ' / .!P#@< \#P#P X #@/ #@ \ < #@ M ' / /#@#@\ \#@!P X '@/ '@ #X > #@ M/_ . '#@#@X <'__P X ' ' ? #X . #@ !__ . M '#@ X <'__P X ' ' ? #\ . #@ !__ . '#@ M X <'__P X ' ' ?@ < ' #@ #P' . '#@ X M<'@ X '@/ #@ < ' #@ #@' / .#P!@< \#P M X #@/ #@ < '@ #@ #@? /@>!X#P?#\#\!P X M#P_ #@ < #@ #@ #__Q__\!__@/__A__X/_\!__ M #@ < #@ #@ !_WQ__X __ '_?@__P/_\ _W M#@ < !P #@ _GQ^?P /\ !^?@/_ /_\ ?G #@ M< !P #@ ' #@ < M !X #@ ' #@ ?@ MX _@ / #\ /@ X _@ M > !\ '@ _@ M/__\ _^ \ /__\ M _\ /__\ M _P M M M M M M M M M #P _ M #P / 'X '^ > #X _ #P M / 'X '^ ? #X _ #P / M'X '^ ? \ ' #P / X M . '@ < ' X . M #@ < ' X . M #@ < '/@ _P /_P Y_@ . 'G'@?/@ ' M\!^/@ /S\/Q\ ']@ #@ < '?P _P /_P Y_@ . '__P??P ?_!^_ MX __\/W^ ?_@ #@ < '_X _P /_P Y_@ . '__X?_X __A__\!__ M\/__ ?_@ #@ < 'P\ !P !P X\ . #QXX'P\!X#P/@>#X?@!^. M \'@ #@ < '@< !P !P YX . #APX'@ ' < !P !P [P . #APX' <#P!X. ''@'@!X \ M#P /@ ' < !P !P _@ . #APX' <#@ X. '' #@!P _X !\ M/@ ' < !P !P _@ . #APX' <#@ X. '' #@!P ?_ !\ ?@ M ' < !P !P _P . #APX' <#@ X. '' #@!P '_@ #\ < ' M< !P !P [X . #APX' <#P!X. /'@'@!P !P'P #@ < ' < !P M !P Y\ . #APX' /_ !_X _\ M /^ !_X _\ M M M M M M M M M M \ < > M #@ !\ < ? M #@ !\ < ? M #@ #P < '@ M #@ #@ < #@ M #@ #@ < #@ M ?_X'X?A_C_/X/X_C^'^/\'_\ #@ < #@ ?_ MX'X?A_C_/X/X_C^'^/\'_\ #@ < #@ !@ ?_X'X? MA_C_/X/X_C^'^/\'_\ #@ < #@ !@ , 'X< #@ X#@> X M#CC@/!P!X!P'!X #@ < #@ 'X< _#@/\< #@ X#@. X#CC@ M'C@ X#@'#P #@ < #@ /\< !_C@>^\ #@ X#@/!P#GS@#W M\#@''@ '@ < #P >^\ #WW@' > ? M < !\

'!X '\ '@< #@ < #@ M #_^ __X ^ X. _C^ #X '_\ #@ < #@ M !_\ ?_X \ X. _C^ #X '_\ #@ < #@ M ?@ /[X < X. _C^ !P '_\ #@ < #@ M !P #@ < #@ M #@ #@ < #@ M #@ #\ < ?@ M ' !\ < ? M #_X \ < > # M_X #_X M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M X M X ! M !X #@ #^ > !X M #@ #\ ?@ #C@ !P #@ #^ > !X #@ M O@#_\ ?@ #C@ !P 'P #^ / !X #@ O M@#_^ _@ #C@ #@ /X #@ / !X #@ OP'\^ M ^ #C@ #@ >\ #@ ' #@ 'P'P_!^ M #C@ '@ << #@ '@ #@ 'X/P_!^ #C M@ ' \> #@ #@!_^!^/@ /S\_Q_@#@ 'X/P[CN #C@ M ' !X/ #@ #@!_^!^_X __\_Q_@#@ '<=P[CN #C@ . M!P' #@ !P!_^!__\!__\_Q_@#@ '<=P[WN #C@ . !P' M #@ !@ . /@>#X?@/ . #@ '>]PYW. !! > M#@ . / /#@'@' < #@ '.YPYW. < #@ M . . ''@'@'@< #@ '.YPX^. \ #@ M. . '' #@#@X #@ ''QPX^. X #@ . . M'' #@#PX #@ ''QPX^. X #@ . . '' # M@!QP #@ ''QPX<. !P #@ . . /'@'@!YP M #@ '#APX<. #P !P #@ . / .#@'@ [@ #@ M '#APX . #P #P #@ . /@^#X?@ _@ #@ ' M !S_!_@ '@ #@ #@ . /_\!__@ ? #@ OX/__ M!_@ '@ #@ #@ . ._P _[@ ? #@ OX/__!_@ M ' ' #@ . ./ /C@ . #@ OX/\ M . ' #@ . . #@ . #@ . M / #@ . . #@ < #@ < . M #^ . . #@ < #@ < . M #^ > . #@ X #@ 8 !___@ M#^ !_\!_X _\?_ #@ !___@ M !_X!_X _\?_ !___@ !_ 1P!_X _\?_ end