The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
############################################################################
############################################################################
##                                                                        ##
##    Copyright 2004 Stephen Patterson (steve@patter.mine.nu  )           ##
##                                                                        ##
##    A cross platform perl printer interface                             ##
##    This code is made available under the perl artistic licence         ##
##                                                                        ##
##    Documentation is at the end (search for __END__) or process with    ##
##    pod2man/pod2text/pod2html                                           ##
##                                                                        ##
############################################################################
############################################################################

# Win32 Routines

############################################################################
sub list_printers {
    # list available printers
    my $self = shift();
    my %printers;

    # look at registry to get printer names for local machine
    my $Register = 'SYSTEM\CurrentControlSet\Control\Print\Printers';
    my ($hkey, @key_list, @names, @ports);
    my $HKEY_LOCAL_MACHINE = $main::HKEY_LOCAL_MACHINE;
    $HKEY_LOCAL_MACHINE->Open($Register, $hkey) or 
      Carp::croak "Can't open registry key HKEY_LOCAL_MACHINE\\$Register: $!";
    $hkey->GetKeys(\@key_list);
    foreach my $key (@key_list) {
	my $path = $Register . '\\' . $key;
	my ($pkey, %values, $printers);
	$HKEY_LOCAL_MACHINE->Open($path, $pkey) or 
	  Carp::croak "Can't open registry key  HKEY_LOCAL_MACHINE\\$path: $!";
	$pkey->GetValues(\%values);
	push @ports, $values{Port}[2];
	push @names, $values{Name}[2];
    }
    $printers{name} = [ @names ];
    $printers{port} = [ @ports ];
    return %printers;
}
######################################################################
sub use_default {
    # select the default printer
    my $self = shift;
    my ($hkey, %values);

    # default name is the human readable printer name (not port)
    # look in the registry to find it
    if ($self->{winver} eq ('Win95' or 'Win98' or 'WinNT4')) {
	# the old routines, win95/nt4 tested
	my $register = 'Config\0001\SYSTEM\CurrentControlSet\Control\Print\Printers';
	my $HKLM = $main::HKEY_LOCAL_MACHINE;
	$HKLM->Open($register, $hkey) or 
	  Carp::croak "Can't open registry key " . $register
	      . "in use_default(): $EXTENDED_OS_ERROR\n";
	$hkey->GetValues(\%values);
	my $default = $values{Default}[2];
	# $default holds the long printer name, get the port
	$register = 'SYSTEM\CurrentControlSet\Control\Print\Printers\\';
	my $path = $register . $default;
	$HKLM->Open($path, $HKEY) or
	  Carp::croak "Can't open registry key $path in use_default() "
	      . $EXTENDED_OS_ERROR;
	$hkey->GetValues(\%values);
	$self->{'printer'}{$OSNAME} = $values{Port}[2];
    } elsif ($self->{winver} =~ /2000/) {
	# pull it from a different registry path
	my $register = 'Software\Microsoft\Windows NT\CurrentVersion\Windows';
	my $HKCU = $main::HKEY_CURRENT_USER;
	$HKCU->Open($register, $hkey) or
	  Carp::croak "Can't open registry key " . $register
	      . "in use_default(): $EXTENDED_OS_ERROR\n";
	$hkey->GetValues(\%values);
	my $default = $values{Device}[2];
	# $default holds the long printer name, get the port
	$register = 'SYSTEM\CurrentControlSet\Control\Print\Printers\\';
	my $path = $register . $default;
	my $HKLM = $main::HKEY_LOCAL_MACHINE;
	$HKLM->Open($path, $HKEY) or
	  Carp::croak "Can't open registry key $path in use_default() "
	      . $EXTENDED_OS_ERROR;
	$hkey->GetValues(\%values);
	$self->{'printer'}{$OSNAME} = $values{Port}[2];
    } elsif ($self->{winver} =~ /XP/) {
	# bugger. nothing appropriate in registry for windows XP
	# pick 1st available printer
	my %printers = $self->list_printers;
	$self->{printer}{$OSNAME} = $printers{port}[0];
    }
}
######################################################################
sub list_jobs {
    # list the current print queue
    my $self = shift;
    # return an empty queue (for compatibility)
    # Carp::croak 'list_jobs  hasn\'t yet been written for windows. Share and enjoy';
    my ($pHandle, @jobs, $start);
    $start = 0;

    $OpenPrinter = new Win32::API('Winspool.drv',
			      'OpenPrinter',
			      [P, P, P],
			      I);
    $ClosePrinter = new Win32::API('Winspool.drv',
			       'ClosePrinter',
			       [P],
			       I) || die $!;
    $GetLastError = new Win32::API('kernel32.dll',
			       'GetLastError',
			       I) || die $!;

    # human readable printer name
    my $pName = $self->{'printer'}{'MSWin32'};

    $pHandle = " " x 128; # init a char buffer
    $OpenPrinter->Call($pName, $pHandle, NULL) or Carp::croak 
      "couldn't open printer handle for list_jobs $GetLastError->Call";
    $pHandle =~ s/\0.*$//;

    GetJobs($pHandle, @jobs, $start) || die "Couldn't call GetJobs via xs\n";
    print @jobs;
}
######################################################################

######################################################################
##                                                                  ##
##    Extra windows drivers for specific data types                 ##
##                                                                  ##
######################################################################

######################################################################
sub MS_word {
    # understood by MS Word
    require Win32::OLE;
#    require Win32::OLE::Const qq(Microsoft Word);
    my ($self, $spoolfile) = @_;
    my $Word = Win32::OLE->new('Word.Application', 'Quit');
    $Word->ActivePrinter($self->{'printer'}{$OSNAME});
    $Word->Documents->Open($spoolfile) or Carp::croak
      ("unable to open document", Win32::OLE->LastError());
    $Word->ActiveDocument->PrintOut({
				     Background => 0,
				     Append => 0,
				     Range => wdPrintAllDocument,
				     Item => wdPrintDocumentContent,
				     Copies => 1,
				     PageType => wdPrintAllPages});
    unlink $spoolfile;
}
######################################################################
sub MS_excel {
    # understood by MS Excel
    require Win32::OLE;
#    require Win32::OLE::Const 'Microsoft Excel';
    my ($self, $xlfile) = @_;
    my $xl_app = Win32::OLE->new('Excel.Application', 'Quit') or Carp::croak
      ("Cannot start excel", Win32::OLE->LastError);
    my $workbook = $xl_app->Workbooks->Open($xlfile) or Carp::croak
      ("Can't open file", Win32::OLE->LastError);
    my $worksheet = $workbook->Worksheets(1);
    $worksheet->PrintOut;
    $xl_app->Quit;
}
######################################################################
sub MS_ie {
    # internet explorer
    require Win32::OLE;
    my ($self, $spoolfile) = shift();
    my $IE = Win32::OLE->new('InternetExplorer.Application', 'Quit') or
      Carp::croak("Cannot start Internet Explorer", Win32::OLE->LastError);
    $IE->navigate($spoolfile);
    # or Window.Print
    # IE can't set printer or orientation
    sleep 1 while ($explorer->{ReadyState} < 4);
    $explorer ->{Visible} = 1;
#    sleep(5);
    $IE->ExecWB(6, 1); # print with prompt
    $IE->Quit();
}
######################################################################
sub print {
    # new style printing - just use stuff from wasx
    my ($self, $data) = @_;
    my $self = shift;
    my $data = join('', @_);
    # paper orientation
    my $orient;
    if ($self->{orientation} =~ /landscape/) {
	$orient = 2;
    } else {
	$orient = 1;
    }

    print "printing $data to $self->{printer}{$OSNAME}, orientation $orient\n";

    my $printer = new Win32::Printer( papersize   => 9, # USE a4
				      dialog      => 0,
				      orientation => $orient,
				      printer     => $self->{printer}{$OSNAME},
				      unit        => 'mm');
    my $font = $printer->Font('Arial', 10);
    $printer->Font($font);
    $printer->Write($data, 10, 10);
    $printer->Close;
}
######################################################################
sub print_orig {
    # print- old style - deprecated to use Win32::Printer from wasx,
    # Printer.pm versions 0.98 onwards
    my $self = shift;
    my $data = join("", @_);
    unless ($self->{print_command}->{$OSNAME}) {
	# default pipish method
	my $printer = $self->{'printer'}{$OSNAME};

	# Windows NT variations
	if ($self->{winver} =~ m/WinNT|Win2000/ ) {
	    open SPOOL, ">>$printer" or
	      Carp::croak "Can't open print spool " . $printer . ": $!" ;
	    print SPOOL $data or
	      Carp::croak "Can't write to print spool $self->{'printer'}: $!";
	    close SPOOL;
	} elsif ($self->{winver} =~ m/WinXP/) {
	    open SPOOL, ">>$printer" or
	      Carp::croak "Can't open print spool " . $printer . ": $!" ;
	    print SPOOL $data or
	      Carp::croak "Can't write to print spool $self->{'printer'}: $!";
	    close SPOOL;
	}

	# any other windows version
	# for win95, may work with ME
	else {
	    my $spoolfile = get_unique_spool();
	    open SPOOL, ">" . $spoolfile;
	    print SPOOL $data;
	    close SPOOL;
	    system("copy /B $spoolfile $self->{printer}{$OSNAME}");
	    unlink $spoolfile;
	}

    } else {
	# custom print command
	if ($self->{print_command}->{$OSNAME}->{type} eq 'command') {
	    # non-pipe accepting command - use a spoolfile
	    my $cmd = $self->{print_command}->{$OSNAME}->{command};
	    my @specials = qw(MS_word MS_excel MS_ie);
	    my $spoolfile = get_unique_spool();
	    $cmd =~ s/FILE/$spoolfile/;
	    open SPOOL, ">" . $spoolfile;
	    print SPOOL $data;
	    close SPOOL;

	    if (grep /$cmd/, @specials) {
		# run my new Win32^::OLE methods
		if ($cmd eq 'MS_word') {
		    $self->MS_word($spoolfile);
		} elsif ($cmd eq 'MS_excel') {
		    $self->MS_excel($spoolfile);
		} elsif ($cmd eq 'MS_ie') {
		    $self->MS_ie($spoolfile);
		}
	    } else {
		system($cmd) or die $OS_ERROR;
		unlink $spoolfile;
	    }
	} else {
	    # pipe accepting command
	    # can't use this - windows perl doesn't support pipes.
	}
    }
}
######################################################################
1;