9.6. 進階印表機設定

這一節我們要討論的是處理特定檔案格式的過濾程式、header pages、 用網路印表機列印以及印表機存取權限及列印情形統計的設定方式。

9.6.1. 過濾程式

雖然 LPD 負責處理網路通訊定、佇列、存取權限控制、 以及其他許多列印相關的工作,但,最重要 的工作還是由過濾程式來做。過濾程式是負責與印表機溝通、處理裝置依存關係、 以及一些其他特殊的需求。我們在之前設定印表機時安裝了一個 --最簡單但是可以和配合大部份印表機工作的文字過濾程式(請參見安裝文字過濾程式 這一節).

然而,若我們想要更方便的轉換格式、統計印表機使用情形、 列印特別的印表機字體等等,我們應該先了解過濾程式是如何運作的。 這些事情都是由過濾程式處理的。不幸的是,通常 得自己提供特定的過濾程式。不過值得慶幸的是, 有很多一般用途的過濾程式可以用,如果沒有的話, 要自己做應該也很容易。

FreeBSD 提供了一個過濾程式 /usr/libexec/lpr/lpf。 這個過濾程式可以讓大部份的印表機可以列印純文字 (他只處理檔案中的倒退字元以及 TAB 字元,並不做統計等工作)。在 FreeBSD 的 ports 裡亦搜集了許多種不同的過濾程式。

在這一節裡,我們將要討論下面這些東西:

9.6.1.1. 過濾程式的運作方式

上面曾經提到,過濾程式是一個由 LPD 執行處理印表機與通訊埠之間溝通 的程式。

當 LPD 要列印檔案時,他會執行過濾程式, 同時從過濾程式的標準輸入要列印的檔案, 而將過濾程式的標準輸出給印表機列印, 並將過濾程式的錯誤導向至紀錄裡(在 /etc/printcaplf 關鍵字設定,或是使用預設的 /dev/console)。

LPD 要使用哪個過濾程式或是要給過濾程式哪些參數是由 /etc/printcap 檔案以及使用者給 lpr(1) 命令的參數決定。舉例來說,如果使用者輸入 lpr -t, 那麼 LPD 將會使用由 tf 關鍵字為特定印表機所指定的 troff 過濾程式。 如果使用者想列印的是純文字,那麼 LPD 將會使用由 if 所指定的(大部份的時候是這樣子的,詳情請參見 輸出用的過濾程式這一節。

/etc/printcap 檔裡你可以設定三種不同種類的過濾程式:

  • 文字過濾程式,在 LPD 的文件中常混稱為輸入用過濾程式。 通常將他做為預設的過濾程式,負責處理一般的文字列印。LPD 將所有印表機都視為可以直接列印純文字, 所以這個過濾程式的任務就是將印表機無法處理的字元及格式, 轉換為印表機所能處理的。如果你需要做列印情形的統計, 那麼過濾程式必需要負責根據總行數及每頁行數來統計頁數。 使用文字過濾程式的方式為:

    filter-name [-c] -wwidth -llength -iindent -n login -h host acct-file

    其中

    -c

    如果工作以 lpr -l 命令送出, 那麼就會有這個參數

    width

    的值是由 /etc/printcap 檔中的 pw (每頁的字元寬度)關鍵字所指定。 預設為 132。

    length

    是由 pl (每頁的行數)關鍵字所指定,預設為 66。

    indent

    是由 lpr -i 命令所指定的縮排字數,預設是 0。

    login

    是列印該檔案的使用者帳號名稱。

    host

    是送出工作的機器名稱。

    acct-file

    是由 af 關鍵字所指定的統計資料檔檔案名稱。

  • 轉換用的過濾程式 負責將指定檔案格式轉換成印表機可以印出的格式。舉例來說 ditroff 排版資料無法直接交由印表機列印,不過你可以安裝負責轉換 ditroff 檔案的過濾程式將 ditroff 資料轉換會印表機可以列印及處理的格式。 轉換用的過濾程式 這一節將會更詳細的說明。如果你需要做列印情形的統計, 那麼轉換用的過濾程式也需要做統計的工作。 使用轉換用的過濾程式的方式為:

    filter-name -xpixel-width -ypixel-height -n login -h host acct-file

    其中 pixel-width 是由 px 關鍵字所指定(預設為 0) 而 pixel-height 是由 py 關鍵字所指定(預設為 0)。

  • 輸出用過濾程式 是當你沒有文字過濾程式, 或是要列印 header pages 時才使用。 在我的經驗裡,輸出用過濾程式是很少用到的。輸出用的過濾程式 這一節 將介紹這些過濾程式。輸出用過濾程式只接受兩個參數,如下所示:

    filter-name -wwidth -llength

    參數 -w-l 的意思和文字過濾程式中的意思是一樣的。

而過濾程式也應該要以下列的結束碼結束程式

exit 0

如果過濾程式成功的把檔案印出。

exit 1

如果過濾程式列印失敗了而想要讓 LPD 再呼叫一次過濾程式重新列印,那麼就以這做值結束。LPD 收到這個值之後,就會再重試列印。

exit 2

如果過濾程式列印失敗且不想再嘗試了,那麼就以這個值結束。 LPD 收到這個值將不會再重試同時放棄列印這個檔。

FreeBSD 裡提供的過濾程式: /usr/libexec/lpr/lpf 利用每頁字元寬度及每頁行數來判斷什麼時候要送出 form feed 字元以及統計印表機列印情形。 而以使用者帳號、機器以統計資料檔來紀錄列印的情形。

如果你想要購買過濾程式,得先了解他們是否相容於 LPD。 如果要相容的話,他們必需要能處理上面所列的參數。 如果你想要自己寫過濾程式來用, 那麼他們一樣得要處理上面所列的參數以及程式結束碼。

9.6.1.2. 在 PostScript 印表機上列印純文字

如果你的電腦只有你一個人在用而且你使用的是 PostScript (或是其他的印表機語言)印表機, 你也確定你決不會將純文字的資料交給你的印表機列印, 且你不會使用其他會將純文字送給你的印表機的程式, 那麼你就不需要閱讀這一節。

但是,如果你需要能處理 PostScript 及純文字的資料, 那麼你就得對你的印表機再做一翻設定。 我們首先要有一個文字過濾程式可以偵測出送過來的資料是純文字還是 PostScript。所有的 PostScript 文件都是 %! (對於其他的印表機語言,請參考你的印表機手冊) 如果一份工作的最前面兩個字元是這樣的話,我們收到的就是 PostScript 的工作,而我們可以直接把這份工作交給印表機處理。如果不是的話, 那麼過濾程式就得把這些文字轉換成 PostScript 然後再列印這些結果。

那我們要怎麼設定呢?

如果你是用序列埠上的印表機,有一個好方法就是安裝 lprpslprps 是給 PoscScript 印表機用的過濾程式,他負責做印表機的雙向溝通。 他會將印表機的狀態詳細的更新到紀錄檔裡, 如此使用者及管理者都可以清楚的看到印表機的狀態。 (如如說``toner low''或是``卡紙'')。 更重要的是,他還包括了一個叫做psif的程式, 若偵測出輸入的工作是純文字,則呼叫 textps 這支程式(這是由 lprps 所提供的)將純文字轉換成 PostScript。最後,他會呼叫 lprps 將工作送給印表機列印。

你可以在 FreeBSD ports 裡找到 lprps(請參見 The Ports Collection這一節)。當然, 你可以自己取得、編譯及安裝。安裝 lprps 之後, 只要指定 psif (lprps 的程式之一) 之路徑即可。如果你從 ports 裡安裝了 lprps,那麼在 /etc/printcap 檔案裡為序列埠的 PostScript 印表機設定:

    :if=/usr/local/libexec/psif:

你應該要使用 rw 關鍵字以告訴 LPD 將印表機開啟為讀寫模式。

如果你是使用接在並列埠的 PostScript 印表機(因此無法使用 lprps 和印表機雙向溝通),那麼你可以使用下列的 shell script 做為文字過濾程式:

    #!/bin/sh
    #
    #  psif - Print PostScript or plain text on a PostScript printer
    #  Script version; NOT the version that comes with lprps
    #  Installed in /usr/local/libexec/psif
    #
    
    read first_line
    first_two_chars=`expr "$first_line" : '\(..\)'`
    
    if [ "$first_two_chars" = "%!" ]; then
        #
        #  PostScript job, print it.
        #
        echo "$first_line" && cat && printf "\004" && exit 0
        exit 2
    else
        #
        #  Plain text, convert it, then print it.
        #
        ( echo "$first_line"; cat ) | /usr/local/bin/textps && printf "\004" && exit 0
        exit 2
    fi

在上面的 script 裡,textps 這個命令是我們個別安裝來將純文字轉成 PostScript 的程式。 你可以使用任何將文字轉換成 PostScript 程式。FreeBSD ports 裡(請參見The Ports Collection) 有一個叫做a2ps的程式能做文字轉成 PostScript 的工作,你也許可以試試看。

9.6.1.3. 用非 PostScript 印表機模擬 PostScript 印表機

PostScript 是高列印品質的de facto標準。 然而,PostScript 也是一個昂貴的標準。 值得慶幸的是,在 FreeBSD 下有個叫做 Ghostscript 的程式能模擬 PostScript 工作,這是由 Alladin Enterprises 所發展的免費程式。Ghostscript 可以讀取大部份 PostScript 檔案並且將其結果輸出至許多不同的裝置上, 包括許多非 PostScript 的印表機。只要安裝 Ghostscript 並為印表機設定一個特殊的文字過濾程式,你可以讓你的非 PostScript 印表機接受 PostScript 的命令。

如果你從 FreeBSD ports 裡安裝 Ghostscript, 那麼你一樣也可以很快的取得、編譯並安裝他。

要模擬 PostScript,我們要有一個過濾程式來判斷我們要列印的是不是 PostScript 檔案。如果不是,那麼這個檔案就直接交由印表機處理;否則, 我們得先用 Ghostscript 將檔案轉換成印表機可以處理的。

這裡有一個例子:下面的 script 是給 Hewlett Packard DeskJet 500 印表機用的文字過濾程式。如果是其他的印表機,將 gs (Ghostscript) 命令的 -sDEVICE 參數換成你能使用的裝置即可。(你可以用 gs -h 這個命令得到到目前系統安裝的 Ghostscript 支援哪些裝置)。

    #!/bin/sh
    #
    #  ifhp - Print Ghostscript-simulated PostScript on a DeskJet 500
    #  Installed in /usr/local/libexec/hpif
    
    #
    #  Treat LF as CR+LF:
    #
    printf "\033&k2G" || exit 2
    
    #
    #  Read first two characters of the file
    #
    read first_line
    first_two_chars=`expr "$first_line" : '\(..\)'`
    
    if [ "$first_two_chars" = "%!" ]; then
        #
        #  It is PostScript; use Ghostscript to scan-convert and print it.
        #
        #  Note that PostScript files are actually interpreted programs,
        #  and those programs are allowed to write to stdout, which will
        #  mess up the printed output.  So, we redirect stdout to stderr
        #  and then make descriptor 3 go to stdout, and have Ghostscript
        #  write its output there.  Exercise for the clever reader:
        #  capture the stderr output from Ghostscript and mail it back to
        #  the user originating the print job.
        #
        exec 3>&1 1>&2
        /usr/local/bin/gs -dSAFER -dNOPAUSE -q -sDEVICE=djet500 \
            -sOutputFile=/dev/fd/3 - && exit 0
    
        #
        /usr/local/bin/gs -dSAFER -dNOPAUSE -q -sDEVICE=djet500 -sOutputFile=- - \
            && exit 0
    else
        #
        #  Plain text or HP/PCL, so just print it directly; print a form
        #  at the end to eject the last page.
        #
        echo $first_line && cat && printf "\033&l0H" && exit 0
    fi
    
    exit 2

最後,你需要用 if 關鍵字指定這個過濾程式:

    :if=/usr/local/libexec/hpif:

這麼一來,你可以輸入 lpr plain.textlpr whatever.ps 而這兩者應該都要可以正確列印。

9.6.1.4. 轉換用的過濾程式

在完成上面 簡單的印表機設定 這一節所介紹的設定後,接下來要做的就是為我們還想要使用的檔案格式 (除了 ASCII 純文字外)安裝轉換用的過濾程式。

9.6.1.4.1. 為什麼要安裝轉換用的過濾程式?

轉換用的過濾程式讓印列不同格式的檔案變得很簡單。舉例來說,如果 我們常常用 TeX 來產生文件,但是我們的印表機是 PostScript 的。因此我們不能直接將由 TeX 產生的 DVI 檔由印表機印出,而要先轉換成 PostScript 的格式才能輸出。這些轉換的命令如下:

    % dvips seaweed-analysis.dvi
    % lpr seaweed-analysis.ps

若我們安裝了 DVI 檔案的轉換程式, 那麼我們每次要列印時就可以省去自己轉換格式的動作,而交由 LPD 自己轉。因此,我們要列印 DVI 檔時只要這一步就可以了:

    % lpr -d seaweed-analysis.dvi

只要在使用 LPD 時加上 -d 參數,LPD 就會將 DVI 檔轉換後再正確的印出。輸出格式及轉換的參數 這一節裡有詳細的列出轉換用的參數。

對於每一個轉換用的參數,我們必需為印表機安裝合適的 轉換用過濾程式,同時在 /etc/printcap 檔裡指定該過濾程式。轉換用 的過濾程式和文字過濾程式很相似(請參見 安裝文字過濾程式這一節),除了他不是列印純文字而是將檔案轉換成 印表機可以處理的格式。

9.6.1.4.2. 我應該安裝哪一種轉換用過濾程式?

你應該安裝你想使用的轉換用過濾程式。如果你要印 DVI 資料,那麼你應該要安裝 DVI 過濾程式。如果你要列印 troff 資料,那麼你應該要安裝 troff 過濾程式。

下面這張表整理出可以配合 LPD 工作的過濾程式和他們在要設定在 /etc/printcap 裡的關鍵字為何, 以及要使用這些過濾程式時在使用 lpr 時要加什麼參數:

檔案格式/etc/printcap 裡的關鍵字lpr 下的參數
cifplotcf-c
DVIdf-d
plotgf-g
ditroffnf-n
FORTRAN textrf-f
troffrf-f
rastervf-v
純文字ifnone, -p 或是 -l

在我們的例子裡,使用 lpr -d 意思就是說印表機會需要用到 /etc/printcap 檔裡由 df 關鍵字所指定的過濾程式。

不管別人怎麼說,像 FORTRAN 或是 plot 的資料可能都已經用不到了。 在你的機器上,你可以安裝其他格式的過濾程式, 如此一來你就可以將那些轉換用的參數用來轉換其他的格式。 舉例來說,你希望可以直接列印 Printerleaf 檔案(由 Interleaf desktop publishing program 產生的),而你用不到 plot 檔案。那麼你可以用 gf 關鍵字指定 Printerleaf 的轉換用過濾程式。 同時告訴你的使用者 lpr -g 命令就是``列印 Printerleaf 檔案''。

9.6.1.4.3. 安裝轉換用過濾程式

轉換用過濾程式不屬於 FreeBSD 本身的程式,因此他們可能被放在 /usr/local 目錄下。通常我們可以將他們放在 /usr/local/libexec 目錄下,因為這些程式是專門給 LPD 執行的,一般使用者並不會需要去直接執行他們。

要使用轉換用過濾程式,只要將過濾程式的路徑在 /etc/printcap 設定檔裡用合適的關鍵字指定即可。

在我們的範例裡,我們將為名為 bamboo 的印表機設定一個 DVI 轉換過濾程式。以下是 /etc/printcap 這個檔,其中印表機 bamboo 使用了 df 關鍵字。

    #
    #  /etc/printcap for host rose - added df filter for bamboo
    #
    rattan|line|diablo|lp|Diablo 630 Line Printer:\
            :sh:sd=/var/spool/lpd/rattan:\
            :lp=/dev/lpt0:\
            :if=/usr/local/libexec/if-simple:
    
    bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
            :sh:sd=/var/spool/lpd/bamboo:\
            :lp=/dev/ttyd5:fs#0x82000e1:xs#0x820:rw:\
            :if=/usr/local/libexec/psif:\
            :df=/usr/local/libexec/psdf:

而 DVI 過濾程式是一個 shell script: /usr/local/libexec/psdf。以下是他的原始碼:

    #!bin/sh
    #
    #  psdf - DVI to PostScript printer filter
    #  Installed in /usr/local/libexec/psdf
    #
    # Invoked by lpd when user runs lpr -d
    #
    exec /usr/local/bin/dvips -f | /usr/local/libexec/lprps "$@"

這個 script 以過濾模式執行 dvips (參數 -f) 在標準輸入讀入要列印的工作以處理。 然後啟動 PostScrpt 印表機文字過濾程式 lprps 同時也將 LPD 傳給這個 script 的參數傳給該過濾程式(請參見 在 PostScript 印表機上列印純文字)這一節。lprps 將根據這些參數來統列印的頁數統計。

9.6.1.4.4. 更多轉換用過濾程式的範例

因為安裝過濾程式沒有特別固定的方式, 所以我們將舉多一點例子來說明。 這些例子也許可以做為日後你自己做過濾程式時的參考, 或是如果合適的話,你也可以直接拿來用。

下面這個例子是一個將 raster 資料(嗯,其實只能給 GIF 檔使用)轉成 Hewlett Packard LaserJet III-Si 印表機所能處理的過濾程式:

    #!/bin/sh
    #
    #  hpvf - Convert GIF files into HP/PCL, then print
    #  Installed in /usr/local/libexec/hpvf
    		  
    PATH=/usr/X11R6/bin:$PATH; export PATH
    giftopnm | ppmtopgm | pgmtopbm | pbmtolj -resolution 300 \
        && exit 0 \
        || exit 2

他先將 GIF 格式轉成 portable anymap,然後再轉成 portable graymap, 接著再轉成 portable bitmap,最後再轉成與 LaserJet/PCL 相容的資料。

下面是使用這個過濾程式的 /etc/printcap 範例:

    #
    #  /etc/printcap for host orchid
    #
    teak|hp|laserjet|Hewlett Packard LaserJet 3Si:\
            :lp=/dev/lpt0:sh:sd=/var/spool/lpd/teak:mx#0:\
            :if=/usr/local/libexec/hpif:\
            :vf=/usr/local/libexec/hpvf:

下面的轉換用過濾程式是將由 grpff 排版系統所產生的 troff 資料轉成讓 PostScript 印表機 bamboo 所能列印的格式:

    #!/bin/sh
    #
    #  pstf - Convert groff's troff data into PS, then print.
    #  Installed in /usr/local/libexec/pstf
    #
    exec grops | /usr/local/libexec/lprps "$@"

上面的 script 再一次的使用 lprps 與印表機溝通。如果我們的印表機接在並列埠上, 那麼我們可以用下面這個 script 取代上面的 script:

    #!/bin/sh
    #
    #  pstf - Convert groff's troff data into PS, then print.
    #  Installed in /usr/local/libexec/pstf
    #
    exec grops

而若要使這個過濾程式,我們在 /etc/printcap 裡得設定:

    :tf=/usr/local/libexec/pstf:

這裡有一個讓熟悉 FORTRAN 的老手慚愧的例子。 這是一個讓所有可以列印純文字的印表機印出 FORTRAN-text 的過濾程式。 我們將在 teak 這台印表機上安裝:

    #!/bin/sh
    #
    # hprf - FORTRAN text filter for LaserJet 3si:
    # Installed in /usr/local/libexec/hprf
    #
    
    printf "\033&k2G" && fpr && printf "\033&l0H" && exit 0
    exit 2

我們只要將這個過濾程式在 /etc/printcap 檔案裡指定給 teak 這台印表機即可:

    :rf=/usr/local/libexec/hprf:

最後一個有點複雜的例子。我們要為先前提到的 teak 這台印表機新增一個可以將 DVI 轉成雷射印表機能處理的格式。首先,先修改 /etc/printcap 檔案中關於 DVI 過濾程式的位置:

    :df=/usr/local/libexec/hpdf:

然後,我們要做出這個過濾程式。我們需要一個能將 DVI 轉成雷射印表機 PCL 格式的程式。FreeBSD port 裡(請參考 The Ports Collection這一節)有一個 :dvi2xx 是這個程式的名稱。 安裝這個程式能提供我們 dvilj2p 這個能將 DVI 轉成 LaserJet IIp、LaserJet III 以及 LaserJet 2000 相容的格式。

dvilj2p 這個程式使得 hpdf 這個過濾程式變得十分的複雜,因為 dvilj2p 無法從標準輸入取得資料。 他得從檔案中讀資料。最糟糕的是,這個檔案還必需以 .dvi 結尾,所以用 /dev/fd/0 做為標準輸入是不行的。 我們可以用 symblic link 產生一個暫時的檔名(以 .dvi結尾)指向 /dev/fd/0 ,然後強迫 dvilj2p 從標準輸入讀取資料。

另一個問題就是我們無法用 /tmp 來存放我們暫時的連結。Symbolic links 的使用者及群組為 bin。而過濾程式是以使用者 daemon 的身份在執行。又 /tmp 目錄設定了 sticky bit。 因此雖然過濾程式建立了連結, 但是因為這個連結屬於其他的使用者而無法刪除。

因此,過濾程式將會把 symbolic link 建立在目前的目錄下,也就是 spooling 目錄(由 /etc/printcap 檔中的 sd 關鍵字所指定)。 這是讓過濾程式來做這件事最好的地方,因為(通常)這裡的可用空間會比 /tmp 下來得多。

所以,這個過濾程式應該要這麼寫:

    #!/bin/sh
    #
    #  hpdf - Print DVI data on HP/PCL printer
    #  Installed in /usr/local/libexec/hpdf
    
    PATH=/usr/local/bin:$PATH; export PATH
    
    #
    #  Define a function to clean up our temporary files.  These exist
    #  in the current directory, which will be the spooling directory
    #  for the printer.
    #
    cleanup() {
       rm -f hpdf$$.dvi
    }
    
    #
    #  Define a function to handle fatal errors: print the given message
    #  and exit 2.  Exiting with 2 tells LPD to do not try to reprint the
    #  job.
    #
    fatal() {
        echo "$@" 1>&2
        cleanup
        exit 2
    }
    
    #
    #  If user removes the job, LPD will send SIGINT, so trap SIGINT
    #  (and a few other signals) to clean up after ourselves.
    #
    trap cleanup 1 2 15 
    
    #
    #  Make sure we are not colliding with any existing files.
    #
    cleanup
    
    #
    #  Link the DVI input file to standard input (the file to print).
    #
    ln -s /dev/fd/0 hpdf$$.dvi || fatal "Cannot symlink /dev/fd/0"
    
    #
    #  Make LF = CR+LF
    #
    printf "\033&k2G" || fatal "Cannot initialize printer"
    
    # 
    #  Convert and print.  Return value from dvilj2p does not seem to be
    #  reliable, so we ignore it.
    #
    dvilj2p -M1 -q -e- dfhp$$.dvi
    
    #
    #  Clean up and exit
    #
    cleanup
    exit 0

9.6.1.4.5. 自動轉換:轉換用過濾程式外的另一種選擇

上面這些過濾程式完整的建立了你的列印環境, 但是使用者們必需自己決定要使用哪一個過濾程式(對 lpr(1) 命令下參數)。如果你的使用者們並不擅長於使用電腦, 要他們自己選擇適當的過濾程式來使用可能會造成他們的困擾。 更嚴重的是,萬一使用到不正確的過濾程式來轉換格式, 那麼可能會讓你的印表機浪費許多不必要紙。

除了安裝所有的轉換用過濾程式外, 你也許會想要試試文字過濾程式(預設的過濾程式)自動偵測要列印的檔案格式, 同時自動呼叫其他的轉換用過濾程式做適當的轉換。像 file 這樣的工具對於這方面可能對這會有些幫助。 然而,要區分出某些檔案格式可能會有點困難 --當然,你也可以提供專門轉換這些格式的過濾程式。

FreeBSD ports 裡有一個叫做 apsfilter 的文字過濾程式可以做檔案類型的自動偵測及轉換。他可以分辨出純文字、 PostScript 以及 DVI 檔案,然後做適當的格式轉換後再印出。

9.6.1.5. 輸出用的過濾程式

LPD spooling 系統還支援一種我們還未介紹的過濾程式: 輸出用過濾程式。輸出用過濾程式和文字過濾程式一樣, 只有列印純文字才使用的,但是他簡單多了。 如果你用了輸出用過濾程式但是沒有用文字過濾程式,那麼:

  • LPD 在一份工作裡將只執行一次輸出用過濾程式, 而不是為每一個檔案都執行一次。

  • LPD 不會提供任何辨認檔案開始或結束的資訊給輸出用的過濾程式 。

  • LPD 不會將使用者的帳號及機器名稱傳給過濾程式, 因此他不能做統計的動作。事實上,他只有得到兩個參數:

    filter-name -wwidth -llength

    其中 width 是由印表機設定裡的 pw 關鍵字所設定,而 length 是由 pl 關鍵字所設定。

請不要被輸出用過濾程式簡單的特性誤導了。 如果你想要用輸出用過濾程式讓一份工作裡每個檔都從新的一頁開始印, 那是行不通的。 你應該用的是文字過濾程式(也稱為輸入用過濾程式),請參考 安裝文字過濾程式這一節。 嚴格的說,輸出用過濾程式實際上是較複雜的, 因為他必需檢查送給他的資料是否含有特殊的字元,並代替 LPD 送訊號給自己。

如果你需要列印 header pages 或是需要送控制字元等其他的初始化字串使得可以列印 header page, 那麼一個輸出用過濾程式是必要的。 (但是如果你是為了要向使用者收取列印 header page 的費用的話,那麼這是沒有用的。因為 LPD 不會將任何使用者及機器的資訊傳給輸出用過濾程式。)

LPD 允許一台印表機上同時擁有輸出用過濾程式和文字過濾程式。 在這個情況之下,LPD 會執行輸出用過濾程式列印 header page(請參見 Header Pages 這一節),然後 LPD 會送 2 個位元組給這個過濾程式:ASCII 031 以及 ASCII 001 時期望輸出用過濾程式將自己停下來。 當輸出用過濾程式看到這兩個位元組(031,001)時,他應該要送 SIGSTOP 這個訊號給自己。當 LPD 執行完其他的過濾程式後,會送 SIGCONT 這個訊號將輸出用過濾程式重新啟動。

如果只有輸出用過濾程式但 沒有文字過濾程式, 且 LPD 是要列印純文字,那麼 LPD 將會用輸出用過濾程式來做這些工作。 輸出用過濾程式將會直將的將列印工作中的檔案序輸出到印表機而不會送出 form feed 或是其他控制紙張移動的命令。因此這些潔果可能 不是你想要的。幾手在所有的情況之下, 你都需要一個文字過濾程式。

我們之前介給的 lpf 程式, 不但可以做為文字過濾程式,也可以當做輸出用過濾程式使用。 如果你需要快速的設定好輸出用過濾程式, 但你不想自己寫偵測資料字元及發送訊號的程式碼,那麼你可以試試 lpf。你也可以在 shell script 中使用 lpf 將必要的初始化控制碼送給印表機。

9.6.1.6. 文字過濾程式:lpf

FreeBSD 裡附的 /usr/libexec/lpr/lpf 是一個文字過濾程式(輸入用過濾程式)。他可以處理縮排(命令 lpr -i)、原封不動的印出資料(以 lpr -l 命令送出的工作)、根據倒退字元及 TAB 字元調整列印的位置,以及統計列印張數。 他也可以拿來當做輸出用過濾程式。

lpf 適合在許多列印環境下使用。 雖然他無法送初始化控制碼給印表機,但是我們可以很容易的寫一個 shell script 送出需要的初始化控制碼後再執行 lpf

為了讓 lpf 可以正確的做列印張數統計, 我們需要在 /etc/printcap 檔裡為 pwpl 關鍵字填入適當的值。 他根據這些值來計算一張紙上可以印多少字, 以及列印使用者的工作需要用幾張紙。關於印表機的統計問題,請參見 統計印表機使用情形 裡更詳細的說明。

9.6.2. Header Pages

如果你有很多使用者使用不同的印表機, 那麼你也許會覺得需要列印 header pages

Header pages,也稱做 bannerburst pages,是用來辨認印出來的工作是屬於誰的。 通常 header pages 以粗體的大字,可能再加上外框印出來的,如此, 使用者才能快速的在一堆印出的結果找到屬於自己的部份。不過 header page 的缺點就是每個工作都會多使用一張紙來列印 header page。 他的功用可能不會超過一分鐘,而最後的下場都是丟進資源回收箱或是垃圾筒。 (不過 header pages 是一份工作一張,而不是一個檔案一張, 所以紙張的浪費可能沒有你想像的那麼嚴重)。

如果你的印表機可以直接列印純文字的話,那麼 LPD 可以自動為你的輸出加上 header pages。如果你的印表機是 PostScript 的,那麼你將會需要額外的程式來產生 header page;請參見 在 PostScript 印表機上列印 Header Pages 這一節。

9.6.2.1. 要求列印 Header Pages

簡單的印表機設定 這一節裡,我們在 /etc/printcap 檔裡利用設定 sh 以不列印 header page (sh 即 ``suppress header'' 的意思)。如果某一台印表機要列印 header pages 的話,那麼只要將 sh 關鍵字移除就行了。

看起來是不是很簡單?

沒錯。你也許 必需要供一個輸出用過濾程式將初始化控制碼送給印表機。 下面是一個適用於 Hewlett Packard PCL 相容印表機使用的輸出用過濾程式:

    #!/bin/sh
    #
    #  hpof - Output filter for Hewlett Packard PCL-compatible printers
    #  Installed in /usr/local/libexec/hpof
    
    printf "\033&k2G" || exit 2
    exec /usr/libexec/lpr/lpf

我們用 of 關鍵字來指定輸出用過濾程式的路徑。 請參見 輸出用的過濾程式 這一節以取得更多的資訊。

下面是一個為我們先前提到的印表機 teakteak 檔裡設定要列印 header pages 及輸出用過濾程式的範例:

    #
    #  /etc/printcap for host orchid
    #
    teak|hp|laserjet|Hewlett Packard LaserJet 3Si:\
            :lp=/dev/lpt0:sd=/var/spool/lpd/teak:mx#0:\
            :if=/usr/local/libexec/hpif:\
            :vf=/usr/local/libexec/hpvf:\
            :of=/usr/local/libexec/hpof:

現在,當使用者用 teak 列印時, 他們每個工作都會有一張 header page。 如果使用者們願意花時間自己找出自己列印出的東西, 那麼他們可以用 lpr -h 命令設定不印出 header pages;請參見 Header Page 參數 以得知更多 lpr(1) 的參數。

Note: LPD 印完 header page 後會送出 form feed 字元。 如果你的印表機送出紙張的指令不是用這個字元,你可以在 /etc/printcap 檔案中用 ff 關鍵字指定。

9.6.2.2. 控制 Header Pages

若選擇要列印 header pages,LPD 將會產生出 long header, 這將使用者、機器以及工作名稱以大字列印在一整頁上。 這裡有一個範例(kelly 從 rose 這台印機列印 outline 這個工作):

          k                   ll       ll
          k                    l        l
          k                    l        l
          k   k     eeee       l        l     y    y
          k  k     e    e      l        l     y    y
          k k      eeeeee      l        l     y    y
          kk k     e           l        l     y    y
          k   k    e    e      l        l     y   yy
          k    k    eeee      lll      lll     yyy y
                                                   y
                                              y    y
                                               yyyy
    
    
                                       ll
                              t         l        i
                              t         l
           oooo    u    u   ttttt       l       ii     n nnn     eeee
          o    o   u    u     t         l        i     nn   n   e    e
          o    o   u    u     t         l        i     n    n   eeeeee
          o    o   u    u     t         l        i     n    n   e
          o    o   u   uu     t  t      l        i     n    n   e    e
           oooo     uuu u      tt      lll      iii    n    n    eeee
    
    
    
    
    
    
    
    
    
          r rrr     oooo     ssss     eeee
          rr   r   o    o   s    s   e    e
          r        o    o    ss      eeeeee
          r        o    o      ss    e
          r        o    o   s    s   e    e
          r         oooo     ssss     eeee
    
    
    
    
    
    
    
                                                  Job:  outline
                                                  Date: Sun Sep 17 11:04:58 1995

LPD 在印完這些後會送出 form feed 字元,使得真正的列印工作會從新的一頁開始列印(除非你在 /etc/printcap 檔案為指定的印表機設定了 sf (supress form feeds))。

如果你希望 LPD 列印 short header,那麼在 /etc/printcap 檔裡使用 sb (short banner) 即可。這樣印出來的 header page 看起來會像下面這個樣子:

    rose:kelly  Job: outline  Date: Sun Sep 17 11:07:51 1995

LPD 的預設值是先列印 header page 再列印工作。如果你想要調換順序, 那麼在 /etc/printcap 裡使用 hl (header last) 即可。

9.6.2.3. 統計列印 Header Pages

如果我們列印 LPD 內建的 header pages, 那麼可們在做統計時可能會遇到一個目題:header pages 得免費提供。

為什麼?

因為只有控制列印 header page 的輸出用過濾程式可以在列印 header pages 時做列印統計,但是這個過濾程式卻沒有 使用者及機器的資料或是一個統計資料檔, 所以無法向使用者收錢。我們也不能在文字過濾程式或是轉換用過濾程式 (這些過濾程式可以直接取得使用者及機的資料)將統計結果都 ``多加上一頁'',因為使用者可以用 lpr -h 命令設定不要列印 header page,而你可能就向這些使用者多收了列印 header page 的費用。基本上, lpr -h 是許多較環保的使用者喜好的參數, 你無法鼓勵別人不去使用他。

而修改每個過濾程式使他們都可以列印出 header page 也是 不可行的(雖然這麼做就可以正確的計費)。因為 LPD 並不會把使用者有沒有使用 -h 的資訊傳給過濾程式,這麼一來,即使使用者下了 lpr -h 的命令要求不列印 header page,他仍然會因為得到一張 header page 而多收取了這個費用。

那麼,有什麼好辦法呢?

你可以:

  • 接受 LPD 的缺點,不收取列印 header page 的費用。

  • 安裝可以取代 LPD 的程式,比如說 LPRng 或是 PLP。請參考 標準 Spooler 之外的選擇這一節所介紹其他可以替代 LPD 的程式。

  • 撰寫一個聰明的輸出用過濾程式。在列印 header page 及純文字時,若沒有文字(輸入用)過濾程式, 那麼一般的輸出用過濾程式只做初始化印表機以及一些簡單的字元轉換工作。 若有設定文字過濾程式,那麼 LPD 只會用輸出用過濾程式來做列印 header page 的工作。事實上,輸出用過濾程式可以根據從 LPD 產生的 header page 資料來判斷列印工作的使用者及機器而加以收費。 唯一的問題就是輸出用過濾程式會無法取得紀錄使用情形的紀錄檔(由 af 關鍵字所指用的紀錄檔不會被傳入), 但是如果你的紀錄檔是固定的, 你可以這個檔案直接設定在輸出用過濾程式裡。 若你考慮要用這種方式來處理的話,那麼在 /etc/printcap 檔案使用 sh (short header) 關鍵字使判斷的工作較容易。 上述的做法其實可能是很麻煩的,比較起來,使用者應該會較感謝讓列印 header page 免費的管理者。

9.6.2.4. 在 PostScript 印表機上列印 Header Pages

之前提到 LPD 可以為印表機產生合適的純文字 header page,當然, 因為 PostScript 不能直接列印純文字,所以 LPD 產生 header page 的工能在 PostScript 印表機上是用不到的--或是幾乎用不到。

一個直接產生 header page 的方法是讓所有的轉換用過濾程式及文字過濾程式產生 header page。 過濾程式可以用使用者及機器的參數產生合適的 header page。 這個方法的缺點就是即使使用者以 lpr -h 的方式送出工作,他們仍然會得到 header pages。

讓我們來試試這個方法。下面的 script 可以接受三個參數(使用者名稱、 機器名稱以及工作名稱)並產生 PostScript 印表機能列印的 header page:

    #!/bin/sh
    #
    #  make-ps-header - make a PostScript header page on stdout
    #  Installed in /usr/local/libexec/make-ps-header
    #
    
    #
    #  These are PostScript units (72 to the inch).  Modify for A4 or
    #  whatever size paper you are using:
    #
    page_width=612
    page_height=792
    border=72
    
    #
    #  Check arguments
    #
    if [ $# -ne 3 ]; then
        echo "Usage: `basename $0` <user> <host> <job>" 1>&2
        exit 1
    fi
    
    #
    #  Save these, mostly for readability in the PostScript, below.
    #
    user=$1
    host=$2
    job=$3
    date=`date`
    
    #
    #  Send the PostScript code to stdout.
    #
    exec cat <<EOF
    %!PS
    
    %
    %  Make sure we do not interfere with user's job that will follow
    %
    save
    
    %
    %  Make a thick, unpleasant border around the edge of the paper.
    %
    $border $border moveto
    $page_width $border 2 mul sub 0 rlineto
    0 $page_height $border 2 mul sub rlineto
    currentscreen 3 -1 roll pop 100 3 1 roll setscreen
    $border 2 mul $page_width sub 0 rlineto closepath
    0.8 setgray 10 setlinewidth stroke 0 setgray
    
    %
    %  Display user's login name, nice and large and prominent
    %
    /Helvetica-Bold findfont 64 scalefont setfont
    $page_width ($user) stringwidth pop sub 2 div $page_height 200 sub moveto
    ($user) show
    
    %
    %  Now show the boring particulars
    %
    /Helvetica findfont 14 scalefont setfont
    /y 200 def
    [ (Job:) (Host:) (Date:) ] {
    200 y moveto show /y y 18 sub def }
    forall
    
    /Helvetica-Bold findfont 14 scalefont setfont
    /y 200 def
    [ ($job) ($host) ($date) ] {
            270 y moveto show /y y 18 sub def
    } forall
    
    %
    % That is it
    %
    restore
    showpage
    EOF

現在,每個轉換用過濾程式以及文字過濾程式可以在列印前先呼叫這個 script 印產生出 header page 然後再列印工作的內容。我們修改之前提過的 DVI 轉換用過濾程式使他可以列印 header page:

    #!/bin/sh
    #
    #  psdf - DVI to PostScript printer filter
    #  Installed in /usr/local/libexec/psdf
    #
    #  Invoked by lpd when user runs lpr -d
    #
    		
    orig_args="$@"
    
    fail() {
        echo "$@" 1>&2
        exit 2
    }
    
    while getopts "x:y:n:h:" option; do
        case $option in
            x|y)  ;; # Ignore
            n)    login=$OPTARG ;;
            h)    host=$OPTARG ;;
            *)    echo "LPD started `basename $0` wrong." 1>&2
                  exit 2
                  ;;
        esac
    done
    
    [ "$login" ] || fail "No login name"
    [ "$host" ] || fail "No host name"
    
    ( /usr/local/libexec/make-ps-header $login $host "DVI File"
      /usr/local/bin/dvips -f ) | eval /usr/local/libexec/lprps $orig_args

注意這個過濾程式如何從參數中取出使用者名稱及機器名稱。 雖然這個過濾程式使用了一些不同的參數(請參見 過濾程式的運作方式 這一節),不過同樣的技巧也可以應用在其他不同的過濾程式上。

不過就如我們之前所提到的,這個方式雖然很簡單,但是卻讓 lpr 命令無法``不列印 header page''(即 -h 參數無效)。如果使用者想要拯救一些樹 (或是少花些錢,如果列印 header pages 要收費的話), 那麼他們將無法做到。因為每個過濾程式都會為每個工作列印 header page。

若要允許使用者可以自己選擇是否列印 header pager,你得使用 統計列印 Header Pages 這一裡介紹的技巧:寫一個可以判斷由 LPD 產生的 header page 的輸出用過濾程式,並由他輸出 PostScript 印表機可以處理的資料。這麼一來,如果使用者以 lpr -h 送出工作,LPD 將不會產生 header page,且你的輸出用過濾程式也不會。否則,你的輸出用過濾程式將要根據 LPD 送出的資料然後以適當 PostScript 碼將 header page 送給印表機處理。

如果你的 PostScript 印表機是接在序列埠上,那麼你可以使用 lprps 裡提供的輸出用過濾程式, psof,這個過濾程式會做上述的動作。注意, psof 這個過濾程式將不會為 header pages 計費。

9.6.3. 網路列印

FreeBSD 支援網路列印:將工作送給遠端的印表機處理。 網路列印有兩大類型:

9.6.3.1. 安裝遠端機器上的印表機

LPD spooling 系統內建有將工作送給網路上其他執行 LPD (或與 LPD 相容)程式的機器列印的功能。這個功能讓你可以將印表機安裝在一台機器上, 並讓網路上其他的機器可以來使用。這功能也可以直接配合支援 LPD 通訊協定的網路界面印表機使用。

要使用遠端列印,首先,我們要先按照簡單的印表機設定印表機伺服器上將印表機設定好。然後也參考 進階印表機設定設定你所需要的參數, 確定你的印表機可以和 LPD 配合運作。最後要確定 本機有在 LPD 的己授權使用 遠端機器列表裡(請參見 限制來自遠端印表機的工作 這一節)。

如果你是使用相容於 LPD 通訊協定的網路界面印表機, 那麼接下來討論的印表機伺服器就是這台印表機, 而印表機名稱則是你為印表機設定的名稱。 請查閱隨印表機或網路界面附的手冊來設定。

在其他要存取這些網路印表機的機器上,他們的 /etc/printcap 設定檔得這麼設定:

  1. 為你的網路印表機取個名字,為了簡單起見, 你可能會把印表機的名稱及別名和印表機伺服器取一樣的名字。

  2. 明確的將 lp 關鍵字設定為空的 (:lp=:)。

  3. 建立一個 spooling 目錄同時將其位置以 sd 關鍵字指定。LPD 在將工作送給印表機伺服器之前,會將這此工作放在這個目錄之下。

  4. 將印表機伺服器的名稱以 rm 關鍵字指定。

  5. 印表機伺服器上的印表機名稱用 rp 關鍵字指定。

這些就足夠了,你不需要在 /etc/printcap 檔裡將轉換用的過濾程式列出,或是指定紙張的大小。

接下來是一個範例。rose 這台機器有兩台印表機, 分別為 bamboorattan。 我們要讓 orchid 這台機器上的使用者可以使用這兩台印很機。下面是 orchid 這台機器上的 /etc/printcap 設定檔(回想要求列印 Header Pages這一節),這個檔裡面已經設定 teak 這台印表機了,我們將要再加入兩台連接在 rose 這台機器上的網路印表機:

    #
    #  /etc/printcap for host orchid - added (remote) printers on rose
    #
    
    #
    #  teak is local; it is connected directly to orchid:
    #
    teak|hp|laserjet|Hewlett Packard LaserJet 3Si:\
            :lp=/dev/lpt0:sd=/var/spool/lpd/teak:mx#0:\
            :if=/usr/local/libexec/ifhp:\
            :vf=/usr/local/libexec/vfhp:\
            :of=/usr/local/libexec/ofhp:
    
    #
    #  rattan is connected to rose; send jobs for rattan to rose:
    #
    rattan|line|diablo|lp|Diablo 630 Line Printer:\
            :lp=:rm=rose:rp=rattan:sd=/var/spool/lpd/rattan:
    
    #
    #  bamboo is connected to rose as well:
    #
    bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
            :lp=:rm=rose:rp=bamboo:sd=/var/spool/lpd/bamboo:

然後,我們只要在 orchid 建立 spooling 目錄:

    # mkdir -p /var/spool/lpd/rattan /var/spool/lpd/bamboo
    # chmod 770 /var/spool/lpd/rattan /var/spool/lpd/bamboo
    # chown daemon.daemon /var/spool/lpd/rattan /var/spool/lpd/bamboo

現在,orchid 上的使用者可以將資料交由 rattanbamboo 列印。 舉例來說,orchid 上的使用者可以輸入

    % lpr -P bamboo -d sushi-review.dvi
而 orchid 上的 LPD 系統將會將工作拷貝到 /var/spool/lpd/bamboo spooling 目錄下並註明他是要列印 DVI 的工作。當 rose 這台機器上給 bamboo 用的 spooling 目錄有空間時,這兩個 LPD 就會開始溝通將檔案傳給 rose。然後這工作便進入 rose 的佇列直到他被印出為止。而將 DVI 轉換成 PostScript(因為 bamboo 是 PostScript 印表機)的工作將會在 rose 這台機器上執行。

9.6.3.2. 使用網路資料傳輸界面的印表機

若你為你的印表機安裝了一塊網路卡界面,通常有兩種情形: 這個界面具有 spooler 的能力(較貴), 或是只是拿來取代序列埠或並列埠(較便宜)。 這一節要討論的是如何使用較便宜的那一種方式。如果你是選用較貴的界面 ,請參考前一節:安裝遠端機器上的印表機

/etc/printcap 設定檔裡允許你指定使用序列埠或是並列埠, 以及(若你使用序列埠)使用什麼傳輸速率、哪一種流量控制、 是否需要延及轉換換行字元等等。但是,卻沒有一種方法可以指定以 TCP/IP 或是其他的網路連接埠與印表機連結。

若要將資料送給這種網路印表機,你需要一種可以被文字及轉換用 過濾程式呼叫的資料傳輸程式。這裡有一個例子: netprint 這個 script 可以讀取標準輸入的資料並將這些資料送到用網路連接的印表機。我們在 netprint 命令的第一個參數得傳入該印表機的機器名稱, 並使用第二個參數傳送通訊埠的號碼。 注意,這個程式只支援單向的傳輸(即從 FreeBSD 到印表機), 而很多網路印表機都支援雙向傳輸, 而你可能會想要使用這些功能(如取得印表機狀態、統計列印情形等)。

    #!/usr/bin/perl
    #
    #  netprint - Text filter for printer attached to network
    #  Installed in /usr/local/libexec/netprint
    #
    $#ARGV eq 1 || die "Usage: $0 <printer-hostname> <port-number>";
    
    $printer_host = $ARGV[0];
    $printer_port = $ARGV[1];
    
    require 'sys/socket.ph';
    
    ($ignore, $ignore, $protocol) = getprotobyname('tcp');
    ($ignore, $ignore, $ignore, $ignore, $address)
        = gethostbyname($printer_host);
    
    $sockaddr = pack('S n a4 x8', &AF_INET, $printer_port, $address);
    
    socket(PRINTER, &PF_INET, &SOCK_STREAM, $protocol)
        || die "Can't create TCP/IP stream socket: $!";
    connect(PRINTER, $sockaddr) || die "Can't contact $printer_host: $!";
    while (<STDIN>) { print PRINTER; }
    exit 0;

然後我門可以在不同的過濾程式裡使用這個 script。 假設我們有一台連接在網路上的 Diablo 750-N 行式印表機。 這台印表機用通訊埠 5100 接收資料。這台印表機的名字叫做 scrivener。 以下是給這印表機使用的文字過濾程式:

    #!/bin/sh
    #
    #  diablo-if-net - Text filter for Diablo printer `scrivener' listening
    #  on port 5100.   Installed in /usr/local/libexec/diablo-if-net
    #
    exec /usr/libexec/lpr/lpf "$@" | /usr/local/libexec/netprint scrivener 5100

9.6.4. 限制印表機的使用

這一節討論如何限制印表機的使用。LPD 系統可以限制本機及遠端使用印表機的使用者, 設定他們是否可以列印多分、印工作大小的限制以及印表機佇列的上限。

9.6.4.1. 限制列印多份

LPD 系統讓使用者可以很容易的將一個檔案列印多份。舉例來說, 使用者只要以 lpr -#5 命令就可以將這個工作裡的每個檔案都列印 5 份。不論這些檔案裡是什麼東西。

如果你覺得列印多份會造成印表機無謂的傷害,你可以在 /etc/printcap 設定檔裡以 sc 關鍵字關閉 lpr(1) 命令的 -# 參數。如此,當使用者嘗試以 -# 參數送出工作時,他們將看到:

    lpr: multiple copies are not allowed

注意,如果你允許從遠端使用你的印表機(請參見 安裝遠端機器上的印表機 這一節),那麼你得在遠端機器上的 /etc/printcap 檔裡也設定 sc 關鍵字, 否則使用者仍然可以從遠端的機器上要求列印多份。

這裡有一個範例。這是 rose 這台機器的 /etc/printcap 設定檔。rattan 這台印表機十分夠力,所以我們允許在他上面列印多份,但是 bamboo 這台雷射印表機有點脆弱, 因此我們在他上面設定了 sc 關鍵字,不允許列印多份:

    #
    #  /etc/printcap for host rose - restrict multiple copies on bamboo
    #
    rattan|line|diablo|lp|Diablo 630 Line Printer:\
            :sh:sd=/var/spool/lpd/rattan:\
            :lp=/dev/lpt0:\
            :if=/usr/local/libexec/if-simple:
    
    bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
            :sh:sd=/var/spool/lpd/bamboo:sc:\
            :lp=/dev/ttyd5:fs#0x82000e1:xs#0x820:rw:\
            :if=/usr/local/libexec/psif:\
            :df=/usr/local/libexec/psdf:

然後,我們也需要在 orchid 這台機器上的 /etc/printcap 設定檔裡為為網路印表機加上 sc 關鍵字(同時,我們也不允許使用者用 teak 這台印表機來做列印多份的工作):

    #
    #  /etc/printcap for host orchid - no multiple copies for local
    #  printer teak or remote printer bamboo
    teak|hp|laserjet|Hewlett Packard LaserJet 3Si:\
            :lp=/dev/lpt0:sd=/var/spool/lpd/teak:mx#0:sc:\
            :if=/usr/local/libexec/ifhp:\
            :vf=/usr/local/libexec/vfhp:\
            :of=/usr/local/libexec/ofhp:
    
    rattan|line|diablo|lp|Diablo 630 Line Printer:\
            :lp=:rm=rose:rp=rattan:sd=/var/spool/lpd/rattan:
    
    bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
            :lp=:rm=rose:rp=bamboo:sd=/var/spool/lpd/bamboo:sc:

使用 sc 關鍵字之後,我們可以防止使用者使用 lpr -#,但是無法防止使用者執行多次 lpr(1), 或是在一個工作裡將同樣的檔案送好幾次,像這樣:

    % lpr forsale.sign forsale.sign forsale.sign forsale.sign forsale.sign

有很多方式可以防止這個濫用(包括忽略他),你可以自己嘗試看看。

9.6.4.2. 限制存取印表機

你可以使用 UNIX 下群組的機制及 /etc/printcap 設定檔的 rg 關鍵字來控制誰可以使用哪台印表機。 只要將允許存取印表機的使用者放在某一個群組中,然後用 rg 關鍵字指定這個群組名稱即可。

在這個群組之外的吏用者(包括 root)將會看到 ``lpr: Not a member of the restricted group'' 如果他們嘗試使用被限制使用的印表機。

sc (禁止列印多份)一樣,若你覺得需要限 制遠端使用者使用你的印表機,你也得在遠端機器上的設定檔裡設定 rg 關鍵字。(請參見安裝遠端機器上的印表機 這一節)。

我們將讓所有人都可以使用 rattan 這台印表機, 但是只有在 artists 群組裡的人可以使使用 bamboo 這台印表機。下面是 rose 這台機器的 /etc/printcap 設定檔:

    #
    #  /etc/printcap for host rose - restricted group for bamboo
    #
    rattan|line|diablo|lp|Diablo 630 Line Printer:\
            :sh:sd=/var/spool/lpd/rattan:\
            :lp=/dev/lpt0:\
            :if=/usr/local/libexec/if-simple:
    
    bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
            :sh:sd=/var/spool/lpd/bamboo:sc:rg=artists:\
            :lp=/dev/ttyd5:fs#0x82000e1:xs#0x820:rw:\
            :if=/usr/local/libexec/psif:\
            :df=/usr/local/libexec/psdf:

我們在這裡不修改其他的 /etc/printcap 設定檔 (比如說 orchid 機器上的)。這麼一來,所有 orchid 機器上的使用者都可以用 bamboo 來列印。通常這種情況是因為 orchid 上的使用者不多且希望他們都可以存取印表機。

Note: 每個印表機只能使用一個群組名稱來做限制。

9.6.4.3. 控制送出工作的大小

如果你有很多使用者在使用印表機, 你也許會想要設定使用者能傳過來列印的檔案大小限制。畢竟,在我們 spooling 目錄所在的檔案系統空間有限, 而你得確保有足夠的空間給其他的使用者列印時候用。

LPD 讓你可以用 mx 關鍵字限制一個列印工作中的檔案大小,以 BUFSIZ 區塊為單位,每一塊大小為 1024 位元組。如果你將值設定為 0,那麼表示對檔案大小沒有限制。如果沒有使用 mx 關鍵字的話,那麼預設的大小是 1000 個區塊。

Note: 這些限制將會套用在列印工作中的檔案, 而不是整個列印工作的大小。

LPD 不會拒絕接收一個檔案大小超過你為印表機設定上限的檔案。 而會儘量將該檔小於上限的資料放入佇例並印出,而超出上限的資料則捨棄。 這是不是一個正確的處理方式還有待爭議。

讓我們為我們的範例印表機 rattanbamboo 設定上限。既然這些藝術家的 PostScript 檔案都蠻大的,我們將上限設定為 5 MB。而我們在純文字行式印表機上不做任何限制:

    #
    #  /etc/printcap for host rose
    #
    
    #
    #  No limit on job size:
    #
    rattan|line|diablo|lp|Diablo 630 Line Printer:\
            :sh:mx#0:sd=/var/spool/lpd/rattan:\
            :lp=/dev/lpt0:\
            :if=/usr/local/libexec/if-simple:
    
    #
    #  Limit of five megabytes:
    #
    bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
            :sh:sd=/var/spool/lpd/bamboo:sc:rg=artists:mx#5000:\
            :lp=/dev/ttyd5:fs#0x82000e1:xs#0x820:rw:\
            :if=/usr/local/libexec/psif:\
            :df=/usr/local/libexec/psdf:

同樣的,這些限制僅對本機使用者有效。 如果有人透過網路使用這些印表機,那麼這些設定是不會產生效用的。 你得在遠端的 /etc/printcap 設定檔裡也設定 mx 關鍵字才行。請參見 安裝遠端機器上的印表機 這一節以取得更多關於遠端列印的訊息。

有另一種更好的方法來限定遠端印表機傳送過來的列印工作大小。 請參見 限制來自遠端印表機的工作 這一節的說明。

9.6.4.4. 限制來自遠端印表機的工作

LPD spooling 系統提供好幾種方法來限制從遠端送過來列印的工作:

限制機器來源

你可以用 /etc/hosts.equiv/etc/hosts.lpd 這兩個檔來限制 LPD 接受哪些遠端機器的要求。LPD 會檢查送出要求的機器是否列在這些檔案之中。如果沒有的話,那麼 LPD 會拒絕這些要求。

這兩個檔案的格式很簡單:一行寫一台機器名稱即可。不過你得 注意 /etc/hosts.equiv 這個檔也被 ruserok(3) 通訊協定所使用,因此會影響到像 rsh(1)rcp(1) 等程式,所以你得小心的設定。

舉例來說,rose/etc/hosts.lpd 設定檔是下面這樣:

    orchid
    violet
    madrigal.fishbaum.de

意思就是 rose 這台機器將會接受從 orchidviolet、以及 madrigal.fishbaum.de這三台機器 送過來的要求。如果其他的機器想要來使用 rose 這機器的 LPD,那麼 LPD 將會拒絕他們。

限制大小

你可以控制有多少空間得保留在 spooling 目錄所在的檔案系統上。只要在本機印表機的 spooling 目錄上建立一個叫做 minfree 的檔案就行了。 而這個檔案的內容是一個數字紀錄當檔案系統至少還有多少磁碟區塊(512 位元組)的剩餘空間時才接受遠端送來的列印工作。

這可以確保遠端的使用者不會將你的檔案系統空間用完。 而這塊空間給本機使用者一些保障:他們在檔案系統剩餘空間小於 minfree 檔案裡所指定的大小時, 還是可以將列印工作送進列印佇列。

舉例來說,我們若想要為 bamboo 設定 minfree 檔。我們得先查看 /etc/printcap 檔以找出這台印表機 spooling 目錄的位置。下面是關於 bamboo 的設定:

    bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
            :sh:sd=/var/spool/lpd/bamboo:sc:rg=artists:mx#5000:\
            :lp=/dev/ttyd5:fs#0x82000e1:xs#0x820:rw:mx#5000:\
            :if=/usr/local/libexec/psif:\
            :df=/usr/local/libexec/psdf:

Spooling 的目錄是以 sd 關鍵字設定。 我們可以設定當那個檔案系統的剩餘空間在 3 MB(即 6144 個磁碟區塊)之上時就接受遠端來的列印工作:

    # echo 6144 > /var/spool/lpd/bamboo/minfree
限制使用者

你可以在 /etc/printcap 裡用 rs 關鍵字設定哪些遠端使用者可以使用本機的印表機。 當你為本機的印表機使用了 rs 之後,LPD 只有在 遠端的使用者名稱在本機上也有同樣的使用者名稱時, 才會接受這個列印工作。否則 LPD 會拒絕接受該工作。

這個功能(舉例來說)在不同部門共用印表機時, 另一個部門的某些使用者需要用到印表機。 你只要在你的機器上為這些使用者建立帳號, 那麼他們就可以在他們的部門裡使用你的印表機。 如果你只想要 讓他們使用你的印表機而不能使用你機器上的其他資源的話, 那麼你只要為他們建立 ``token'' 帳號,即沒有 home 目錄也沒有可用的 shell 像 /usr/bin/false 這樣子即可。

9.6.5. 統計印表機使用情形

紙張及墨水都是要錢的,管理也是需要花錢的 --印表機常常因為運轉負荷過重而故障, 為何不向使用者收取列印的費用呢?若你打算收錢, 你必需檢查你的印表機、訂定使用方式、以及訂定列印一張紙 (一英尺、一公尺,等等) 要收多少錢。 那麼你要如何統計列印了多少呢?

嗯,不幸的是,LPD spooling 系統在這方面的幫助並不大。 統計的方法與你所使用的印表機、列印的格式, 以及其他想收費的項目有關。

若要統計這個功能,你得修改印表機的文字過濾程式(以對列印純文字收費) 或轉換用過濾程式(以對列其他的格式收費), 以計算列印的張數或是如果無法計算的話,則向印表機查詢印出的張數。 你沒有辦法用輸出用的過濾程式來做統計的工作。請參見 過濾程式 這一節的說明。

一般而言,有兩種統計的方式:

LPD spooling 系統可以很容易的支援上述兩者: 因為(大部份的時候)你得提供你的過濾程式以及計算的程式。 這有一個好處就是你可以很有彈性的調整計算的方法。 舉例來說,不管你是使用定期統計或是即時統計。你可以選擇要紀錄哪些資訊: 使用者名稱、機器名稱、工作型態、列印的張數、紙張的大小、 列印工作所花的時間等等。而你只要調整你的過濾程式就可以了。

9.6.5.1. 快速設定印表機使用情形統計

FreeBSD 提供兩個程式讓你可以快速的設定好定期統計。他們是在 文字過濾程式:lpf 這一節所提到的 lpf 以及 pac(8), 一個可以從印表機列印紀錄檔抓取並統計資料的程式。

在前幾節(過濾程式的運作方式), 我們曾提到 LPD 會將紀錄檔名以參數的方式傳給文字過濾程式和轉換用過濾程式。 如此過濾程式就知道可以將計算結果紀錄到哪個檔裡。這個檔名在 /etc/printcap 檔裡是以 af 關鍵字指定,如果不是以絕對路徑來指定的話, 那麼這個檔案將會放在相對應於 spooling 目錄的位置下。

LPD 會將紙張的寬度及高度傳給 lpf 命令 (由 pwpl 關鍵字所指定)。 lpf 使用這些參數來計算將會用到多少張紙。 而將檔案送給印表機列印後,他會將這些結果紀錄在紀錄檔裡。 這些結果看起來是像這個樣子的:

    2.00 rose:andy
    3.00 rose:kelly
    3.00 orchid:mary
    5.00 orchid:mary
    2.00 orchid:zhang

由於 lpf 沒有使用檔案鎖定的相關動作, 因此每台印表機都要有獨立的統計紀錄檔,否則兩個 lpf 同時對同一個檔案做寫入的動作可能會使彼此的紀錄都不正確。 而最簡單的解決方式就是讓不同的印表機在 /etc/printcap 設定檔裡以 af=acct 關鍵字指定不同的統計紀錄檔。這些 acct 檔將會分別放置在不同的 spooling 目錄之下。

當你準備要向使用者收錢時,執行 pac(8) 這個程式。 只要將目錄切換到你想收錢的印表機 spooling 目錄下,然後打 pac 。你將會得到一份像下列的收費摘要:

      Login               pages/feet   runs    price
    orchid:kelly                5.00    1   $  0.10
    orchid:mary                31.00    3   $  0.62
    orchid:zhang                9.00    1   $  0.18
    rose:andy                   2.00    1   $  0.04
    rose:kelly                177.00  104   $  3.54
    rose:mary                  87.00   32   $  1.74
    rose:root                  26.00   12   $  0.52
    
    total                     337.00  154   $  6.74

pac(8) 有下列參數可以使用:

-Pprinter

指定要處理哪一台 printer 的統計資料。這個參數只有在 /etc/printcap 檔裡的 af 關鍵字是使用絕對路徑時才有用。

-c

將輸出的結果以費用排序,而不是以使用者的名字排序。

-m

忽略紀錄檔裡的機器名稱。若使用這個參數,那麼 alpha 機器上的使用者 smithgamma 機器上的使用者 smith 將會被視為同一個使用者。否則,他們會被視為不同的使用者。

-pprice

計算時以每一頁收取 price 元計算,而非使用 /etc/printcap 裡的 pc 所指定的費率或 2 分美金(預設的費率)計算。 你可以用浮點數來指定 price 這個參數。

-r

顛倒排序的順序。

-s

將計算的結果寫到一個摘要檔裡同時清除統計紀錄檔的內容。

name ...

只為指定的使用者 names 做統計。

pac(8) 預設的輸出裡,你可以看到不同機器上不同使用者所列印的張數。 如果你不在乎使用者使用的機器(比如說使用者可以用任何機器), 那麼你可以用 pac -m 產生下列的摘要輸出:

      Login               pages/feet   runs    price
    andy                        2.00    1   $  0.04
    kelly                     182.00  105   $  3.64
    mary                      118.00   35   $  2.36
    root                       26.00   12   $  0.52
    zhang                       9.00    1   $  0.18
    
    total                     337.00  154   $  6.74

pac(8) 計算應繳納的金額時,會使用 /etc/printcap 檔裡 pc 關鍵字所指定的費率(預設值為 2 分美金)。 即每一頁或是每一英尺你想要收多少錢就是在這裡指定。你可以在執行 pac(8) 時以參數 -p 改變這個費率。不過 -p 的單位是元,而不是分,舉例來說,

    # pac -p1.50
將使列印每一頁以 1 元 5 分美金計算。你可以用這個參調整你所收取的費用。

最後,若你使用 pac -s 將會把這些摘要資訊存在一個檔案裡,這個檔案的名稱會和統計紀錄檔相同, 不過檔名後面會再加上 _sum。 然後他會將統計紀錄檔清空。如果你再執行一次 pac(8) 命令,那麼他會先讀取之前統計的結果, 然後再加上從統計紀錄檔裡所計算出來的結果。

9.6.5.2. 如何統計有幾頁被印出?

為了要能精確的統計列印的情形,你必需要能得知每個工作用了幾張紙。 這是印表機使用情形統計最基本的問題。

對於純文字的工作,這個問題並不難解決: 你只要統計這個工作裡有幾行並知道你的印表機一張紙可以印幾行就行了。 別忘了考慮倒退字元以及因為同一行字元過多而換行的情形。

lpf文字過濾程式(在 文字過濾程式:lpf 這一節裡介給的)在做統計時會將這些情形都考慮進來。 如果你要自己撰寫有統計功能的文字過濾程式,你可以參考 lpf 的原始碼。

那麼要如何統計其他格式的文件呢?

嗯,對於 DVI 轉成 LaserJet 或 DVI 轉成 PostScript, 你可以讓你的過濾程式處理由 dviljdvips 的輸出以判斷一共轉換了幾頁。 你也許可以用類似的方式來處理其他的檔案格式以轉換程式。

但是這樣的方法所計算出來的和印表機實際印出來的可能不大相同, 舉例來說,印表機可能會卡紙、用完色帶碳粉或墨水、或是其他的故障 --而這些使用者仍然需要付費。

那麼,我該怎麼辦?

只有一種方式肯定是可以做出 精確的統計。 買一台可以告訴你一共印出多少紙張的印表機, 同時將他接在序列埠或是以網路連接。幾乎所有的 PostScript 印表機都支援這個功能。當然其他種類的印表機也有支援的(如網路 Imagen 雷射印表機)。為這些印表機修改你的過濾程式, 使得過濾程式在列印完成後可以從印表機取得這些資訊, 同時也根據這些紀錄來做統計。 不需要做行數統計或是其他可能發生錯誤的計算方式。

當然,你也可以大方的讓所有的紙張列印輸出都免費。