Next Previous Contents

9. Достъп до принтер на Windows от машина работеща под Linux

За достъп до принтера на Windows машина вие трябва да направите следното:

  1. Вие трябва да имате правилни записи във файла /etc/printcap те трябва да съответстват на локалната структ на директориитериза буферна директория и т.н.)
  2. Трябва да имате скриптипт /usr/bin/smbprintТой се доставя заедно с изходните кодове на Samba но не със всички двоични дистрибутиви на Samba. Неговo малко модифицирано копие се обсъжда по-долу.
  3. Ако вие искате да преобразувате ASCII фaйлове в Postscrip вие трябва да имате пограмата nenscript, или нейн эквивалент. nenscript това е конвертор на Postscrip той обикновенно се разполага в директорията /usr/bin.
  4. Вие може да поискате да направите печатането през Samba по-лесно, използвайки програми-надстройки. Прост скрипт на perl който обработва ASCII, Postscript или преобразуван Postscript е приведен по-долу.
  5. Вие също така можете да използвате MagicFilter за да изпълните, описаното по-горе. Подробности за настройката на MagicFiltе е приведена по-долу. MagicFilter има преимущества, защото той знае как да преобразува автоматически достатъчноно голямо количество формати на файлове.

Записа във файла /etc/printcap показан по-долу е направена за принтера HP 5MP нa сървър Windows NT. Използват се следните полета на файла /etc/printcap:


        cm - коментарий
        lp -име на устройството, отворено за въвеждане
        sd - директория на спунла на принтера (на локалната машина)
        af - файл за отчет за използването на принтера
        mx - максмалнияй разер мна файл (науль --без ограничений)
        if - име на входния филтър (скрипта)

За по-детайлна информация по повод печатането гледайтете Printing HOWTO или спраoчните страници на printcap.


# /etc/printcap
#
# //zimmerman/oreilly via smbprint
#
lp:\
        :cm=HP 5MP Postscript OReilly on zimmerman:\
        :lp=/dev/lp1:\
        :sd=/var/spool/lpd/lp:\
        :af=/var/spool/lpd/lp/acct:\
        :mx#0:\
        :if=/usr/bin/smbprint:

Убедете се, че буферните директории и директорията използвана за отчет за ползването, съществуват и са с права за запис. Убедете се, че реда 'if' съдържа правилния път към скрипта smbprint (даден по-долу) и убедете се, че записите сочат към правилното устройство за въвеждане (специалния файл /dev).

По-нататък е самия скрипт smbprint. Той обикновенон се намира в директорията /usr/bin и е написан от Andrew Tridgell, човека, който създаде Samba, доколкото знам. Този скрипт се намира в пакета с изходния код на Samba, но осъства в някои бинарни дистрибутиви, така че аз го пресъздадох тук.

Вие може да поискате да погледнете на него по-внимателно. Има някой малки изменения, които се оказаха полезни.


#!/bin/sh -x

# Този скрипт се явява входен филтър за основаната на printcap
# печат на unix-машини. Той използва програмата smbclient за
# печат на файла на посочения smb-сървър и сервиз.
# Например вие можете да имате запис в printcap подобна на тази
#
# smb:lp=/dev/null:sd=/usr/spool/smb:sh:if=/usr/local/samba/smbprint
#
# която създава unix-принтер наречен "smb", който ще
# печата с помоща на този скрипт. На вас ви е необходимо да създадете директория
# на спула /usr/spool/smb със съответстващи права и притежател

# Сложете тук сървър и сервиз на, които искате да печатате.В този
# пример аз имам PC с WfWg PC, наречена "lapland", която има
# експортируем принтер, наричан "printer" без парола

#
# По-нататък скрипта беше променен от [email protected] (Michael Hamilton)
# така, че сървъра, сервиза и паролата могат да бъдат преписани от файла
# /usr/var/spool/lpd/PRINTNAME/.config 
#
# За да заработи това записа в /etc/printcap трябва да
# включва файл за отчитане на използването (af=...):
#
#   cdcolour:\
#       :cm=CD IBM Colorjet on 6th:\
#       :sd=/var/spool/lpd/cdcolour:\
#       :af=/var/spool/lpd/cdcolour/acct:\
#       :if=/usr/local/etc/smbprint:\
#       :mx=0:\
#       :lp=/dev/null:
#
# Файл /usr/var/spool/lpd/PRINTNAME/.config трябва да съдържа
#   server=PC_SERVER
#   service=PR_SHARENAME
#   password="password"
#
# Например,
#   server=PAULS_PC
#   service=CJET_371
#   password=""

#
# Debugging log file, change to /dev/null if you like.
#
logfile=/tmp/smb-print.log
# logfile=/dev/null


#
# The last parameter to the filter is the accounting file name.
#
spool_dir=/var/spool/lpd/lp
config_file=$spool_dir/.config

# Should read the following variables set in the config file:
#   server
#   service
#   password
#   user
eval `cat $config_file`

#
# Some debugging help, change the >> to > if you want to same space.
#
echo "server $server, service $service" >> $logfile

(
# NOTE You may wish to add the line `echo translate' if you want automatic
# CR/LF translation when printing.
        echo translate
        echo "print -"
        cat
) | /usr/bin/smbclient "\\\\$server\\$service" $password -U $user -N -P >> $logfile

Повече от диструбутивите на Linux се доставя с програмата nenscript за преобразуване на ASCII документи в Postscript. Следващия скрипт на perl прави живота на потребителя по-лесен, осигурявайки прост интерфейс за печатане използвайки smbprint.


Използване: print [-a|c|p] <filename>
       -a печата <filename> каот ASCII
       -c печата <filename> отформатиран като изходен код
       -p печата <filename> като Postscript
        Ако не са зададени опции, програмата ще се пробва да
        определи типа на файла и вида на печат съответно

Използвайки smbprint за печатане на ASCII файлове, скрипта следи за дълги редове. Ако е възможно, този скрипт разделя дългия редприа интервала (вместо по средата на думата).

Форматирането на изходния код се изпълнява с помоща на програмата nenscript. Тя взима ASCII-файл го форматира в 2 колонки със заглавие (дата, името на файла и т.н.). Тази програма също така номерира редовете. Използвайки този скрипт като пример, могат да бъдат добавени други видове форматиране.

Postscript-документите се явяват вече отформатирани, така че те се печатат веднага.


#!/usr/bin/perl

# Скрипт:   print
# Автори:   Brad Marshall, David Wood
#           Plugged In Communications
# Дата:   960808
#
# Използва се за печатане на сервиза oreilly, който е разположен на
# сървъра zimmerman
# Назначение: Взима файлове от различни типове като аргумент и ги обработва
# съответно за предаване на скрипта за печатане Samba.
#
# В настоящия момент се поддържат типове файлове:
# 
# ASCII      - Ако дължината на реда е по-дълга от $line_length символи, то
#              пренася реда при интервала
# Postscript - Взима без обработка
# Code        - Форматира в Postscript (използвайки nenscript), за да
#               изобразява правилно (албумен формат, фонт и т.н.)
#

# Определяне на максималната дължина на реда в ASCII текст
$line_length = 76;

# Определяне на пътя към скрипта за печат Samba
$print_prog = "/usr/bin/smbprint";

# Определяне на пътя и името на nenscript (конвертора на ASCII-->Postscript)
$nenscript = "/usr/bin/nenscript";

unless ( -f $print_prog ) {
        die "Can't find $print_prog!";
}
unless ( -f $nenscript ) {
        die "Can't find $nenscript!";
}

&ParseCmdLine(@ARGV);

# DBG
print "filetype is $filetype\n";

if ($filetype eq "ASCII") {
        &wrap($line_length);
} elsif ($filetype eq "code") {
        &codeformat;
} elsif ($filetype eq "ps") {
        &createarray;
} else {
        print "Sorry..no known file type.\n";
        exit 0;
}
# Pipe the array to smbprint
open(PRINTER, "|$print_prog") || die "Can't open $print_prog: $!\n";
foreach $line (@newlines) {
        print PRINTER $line;
}
# Send an extra linefeed in case a file has an incomplete last line.
print PRINTER "\n";
close(PRINTER);
print "Completed\n";
exit 0;

# --------------------------------------------------- #
#        Everything below here is a subroutine        #
# --------------------------------------------------- #

sub ParseCmdLine {
        # Parses the command line, finding out what file type the file is

        # Gets $arg and $file to be the arguments (if the exists)
        # and the filename
        if ($#_ < 0) {
                &usage;
        }
        # DBG
#       foreach $element (@_) {
#               print "*$element* \n";
#       }

        $arg = shift(@_);
        if ($arg =~ /\-./) {
                $cmd = $arg;
        # DBG
#       print "\$cmd found.\n";

                $file = shift(@_);
        } else {
                $file = $arg;
        }
        
        # Defining the file type
        unless ($cmd) {
                # We have no arguments

                if ($file =~ /\.ps$/) {
                        $filetype = "ps";
                } elsif ($file =~ /\.java$|\.c$|\.h$|\.pl$|\.sh$|\.csh$|\.m4$|\.inc$|\.html$|\.htm$/) {
                        $filetype = "code";
                } else {
                        $filetype = "ASCII";
                }

                # Process $file for what type is it and return $filetype 
        } else {
                # We have what type it is in $arg
                if ($cmd =~ /^-p$/) {
                        $filetype = "ps";
                } elsif ($cmd =~ /^-c$/) {
                        $filetype = "code";
                } elsif ($cmd =~ /^-a$/) {
                        $filetype = "ASCII"
                }
        }
}

sub usage {
        print "
Използване: print [-a|c|p] <filename>
       -a печата <filename> коот ASCII
       -c печата <filename> отформатиран като изходен код
       -p печата <filename> като Postscript
        Ако не са зададени опции, програмата ще се пробва да
        определи типа на файла и вида на печат съответно\n
";
        exit(0);
}

sub wrap {
        # Create an array of file lines, where each line is < the 
        # number of characters specified, and wrapped only on whitespace

        # Get the number of characters to limit the line to.
        $limit = pop(@_);

        # DBG
        #print "Entering subroutine wrap\n";
        #print "The line length limit is $limit\n";

        # Read in the file, parse and put into an array.
        open(FILE, "<$file") || die "Can't open $file: $!\n";
        while(<FILE>) {
                $line = $_;
                
                # DBG
                #print "The line is:\n$line\n";

                # Wrap the line if it is over the limit.
                while ( length($line) > $limit ) {
                        
                        # DBG
                        #print "Wrapping...";

                        # Get the first $limit +1 characters.
                        $part = substr($line,0,$limit +1);

                        # DBG
                        #print "The partial line is:\n$part\n";

                        # Check to see if the last character is a space.
                        $last_char = substr($part,-1, 1);
                        if ( " " eq $last_char ) {
                            # If it is, print the rest.

                            # DBG
                            #print "The last character was a space\n";

                            substr($line,0,$limit + 1) = "";
                            substr($part,-1,1) = "";
                            push(@newlines,"$part\n");
                        } else {
                             # If it is not, find the last space in the 
                             # sub-line and print up to there.

                            # DBG
                            #print "The last character was not a space\n";

                             # Remove the character past $limit
                             substr($part,-1,1) = "";
                             # Reverse the line to make it easy to find
                             # the last space.
                             $revpart = reverse($part);
                             $index = index($revpart," ");
                             if ( $index > 0 ) {
                               substr($line,0,$limit-$index) = "";
                               push(@newlines,substr($part,0,$limit-$index) 
                                   . "\n");
                             } else {
                               # There was no space in the line, so
                               # print it up to $limit.
                               substr($line,0,$limit) = "";
                               push(@newlines,substr($part,0,$limit) 
                                   . "\n");
                             }
                        }
                }
                push(@newlines,$line);
        }
        close(FILE);
}

sub codeformat {
        # Call subroutine wrap then filter through nenscript
        &wrap($line_length);
        
        # Pipe the results through nenscript to create a Postscript
        # file that adheres to some decent format for printing
        # source code (landscape, Courier font, line numbers).
        # Print this to a temporary file first.
        $tmpfile = "/tmp/nenscript$$";
        open(FILE, "|$nenscript -2G -i$file -N -p$tmpfile -r") || 
                die "Can't open nenscript: $!\n";
        foreach $line (@newlines) {
                print FILE $line;
        }
        close(FILE);
        
        # Read the temporary file back into an array so it can be
        # passed to the Samba print script.
        @newlines = ("");
        open(FILE, "<$tmpfile") || die "Can't open $file: $!\n";
        while(<FILE>) {
                push(@newlines,$_);
        }
        close(FILE);
        system("rm $tmpfile");
}

sub createarray {
        # Create the array for postscript
        open(FILE, "<$file") || die "Can't open $file: $!\n";
        while(<FILE>) {
                push(@newlines,$_);
        }
        close(FILE);
}

Сега за използването на MagiFilter. Благодаря на Alberto Menegazzi ( [email protected] ) за неговата информация

Alberto съобщи:
--------------------------%<-----------------------------

1) Инсталирайте MagicFilter в /usr/bin/loаclфилтрите за необходимите принтери, но не запълвайте записа в в /etc/princap, както се предполага в документацията на MagicFilter.

2) Запишете в /etc/printcapпримерно ето такъв запис ( Това е направено за моя принтер LaserJet 4L):

lp|ljet4l:\ :cm=HP LaserJet 4L:\ :lp=/dev/null:\ # or /dev/lp1 :sd=/var/spool/lpd/ljet4l:\ :af=/var/spool/lpd/ljet4l/acct:\ :sh:mx#0:\ :if=/usr/local/bin/main-filter:

Вие трябва да обясните, че устройството lp=/dev/... се отваря за блокиране, така че за всеки отдалечен принтер се използва едно "виртуално устройство".

Пример за създаване: touch /dev/ljet4l

3) Напишите филтър /usr/local/bin/main-filter, със същия образ предполагайки използване на ljet4l-filter вместо cat.

Ето как е за мен.

#! /bin/sh logfile=/var/log/smb-print.log spool_dir=/var/spool/lpd/ljet4l ( echo "print -" /usr/local/bin/ljet4l-filter ) | /usr/bin/smbclient "\\\\SHIR\\HPLJ4" -N -P >> $logfile

P.S.: Това е цитт от Print2Win mini-Howto за блокирането, а също така за това, защо да се създават виртуални принтери.

--Начало тук---------

Съвет от Rick Bressler:

Хубав съвет. Аз използвам нещо подобно. Ето един полезен съвет макар и да не се явява хубава идея:

:lp=/dev/null:\

lpr прави 'монополно (exclusive)' отваряне на файла, който вие ще посочите в полето lp=. Той прави това за да предотврати опитите за множество процеси да се печатат едновременно на един и същ принтер. Страничен ефект на това, във вашия случаи се явява това, че eng и colour не могат да печатат едновременно, (обикновенно малко или повече прозрачно, доколкото те вероятно печатат бързо и доколкото вие не забелязвате, че те поставят заданията в ред), но всички други процеси, които се опитват да пишат в /dev/null няма да работят !

На едно потребителска система, това вероятно не се явява голям проблем. При мен има система с 50 принтера. В този случай това може да бъде проблем.

Решението на този проблем се заключаваше в създаването на устройства за всеки тяхен принтер. Например: touch /dev/eng.

Аз модифицирах записа на lp във файла printcap, показан по-горе, взимайки в предвид пожеланията на Rick. Аз направих следното:

#touch /dev/eng #touch /dev/color

---Край -----

--------------------------%<----------------------------------

-


Next Previous Contents I