• 認識列印系統架構


     http://people.ofset.org/~ckhung/b/gnu/printing.php

    雖然現在許多版本的 linux 都有 printtool 等等工具可以自動設定印表機, 但運氣不好的時候還是會遇到無法自動偵測的機型 -- 例如我的 HP deskjet 3535。 更糟糕的是, 現代的列印系統非常複雜, 但出現的錯誤訊息卻不一定很充分, 有時候系統沒有任何錯誤訊息, 甚至連 log 檔也找不到有用的訊息, 但就是印不出東西。

    這篇文章是筆者在 HP 工程師協助下, 解決自身問題後的副產品, 希望可以幫助讀者認識 Linux 列印系統的架構。 簡單的 GUI 設定後面究竟發生了那些事? foomatic 與 cups 之間又有什麼關係? 又或者你的 Linux 無法列印, 卻又沒有錯誤訊息, 不知下一步該如何, 這篇文章或許也有幫助。 至於找到出問題的環節之後, 應該如何解決, 則不在此篇討論之列, 因為筆者自己並不熟悉硬體。 不過有心的讀者可能因此而比較容易針對問題焦點, 知道應該閱讀那些相關文件, 或是用什麼關鍵字上 google 查; 又或者上網提問題時, 可以把你的狀況描述得比較清楚, 不至於像筆者當初完全茫然沒有頭緒。 此外, 筆者並不熟悉 OO.o 或 Mozilla 等等軟體的列印, 所以可以列印之後, 如何讓輸出更美觀, 也不在本篇討論之列。

    要找到出問題的環節, 最簡單的方式就是盡量避開列印系統當中一層又一層的中間軟體, 單刀直入, 用最直接 (雖然未必是最簡單) 的方法列印。 確定印表機可以驅動之後, 再把較上層, 為簡化使用者介面設計的軟體加進去。

    陽春列印法

    對於我們來自 parallel port 介面時代的人而言, 現代的印表系統顯得太複雜, 也因而難以理解。 Parallel port 介面的古董機型, 完全不需要驅動程式就可以直接列印文字檔: cat 某文字檔名 > /dev/lp0

    這種方法只能印英文文字檔。 如果是 .html .xml 或是程式檔等等 (也都算是文字檔), 印出來的就是未經處理的原始碼, 就像在 nano 或記事本底下看到的一樣陽春。 如果裡面有中文, 那麼你會看到亂碼, 不過段落章節還可以分辨得出來。 用這種方式列印其他檔案 (例如圖檔) 有可能讓你的印表機暫時發瘋, 有時必須關掉再開。

    如果出現 Permission denied, 請用 ls -l /dev/lp0 看看是否需要改用 root 身份下 cat 指令。 通常一般使用者是沒有權限直接讀寫印表機的。

    如果出現 "device or resource busy" 之類的訊息, 表示有其他軟體佔據著印表機埠。 這時可以下 lsof /dev/lp0 檢查究竟是誰在佔用。 順便一提, lsof 指令非常有用, 大力推薦安裝這個套件 (就叫做 lsof); 這個指令可能放在 /usr/sbin/ 底下。

    如果印出來好像階梯一樣, 一路往右偏, 那是因為 Linux 下的換列字元是 \n, 但是印表機可能期待 DOS 的檔案, 以為換列字元是 \r\n (^M ^J)。 可以這樣修正: perl -pe 's/$/\015/' 某文字檔名 > /dev/lp0

    這種方法看似無用, 不過它完全不需要安裝軟體, 所以可以拿它來確認你的硬體正常運作。 如果連這樣都印不出來, 那麼先別急著安裝設定複雜的軟體, 倒是應該先檢查連線是否正常, 或是把這部印表機改接到其他機器上試印, 確認一下硬體沒有問題。

    Ghostscript: 萬能的印表機驅動程式

    [ghostscript and friends] 在 linux 下, 非文字類型的檔案 (例如圖檔) 幾乎都是先將轉成 postscript (.ps) 檔 然後用 ghostscript (gs) 印出。 如果拿上述的 cat 方式餵現代的 usb 介面印表機, 通常都沒有反應也沒有錯誤訊息, 所以 gs 算是最直接的測試方法了。

    例如要印一個中文文字檔 syllabus.txt, 先下: bg5ps < syllabus.txt > syllabus.ps 產生 .ps 檔; 英文文字檔則可以用 a2ps 轉成 .ps 檔。 這兩支程式都可在 rpmfind 找到; 說不定你的 Linux 光碟上本來就有。 可以先用 ghostview (gv) 開啟 .ps 檔看一下, 然後再根據你的印表機型號下 gs 指令:

        # 一部舊式, parallel port 介面的 HP DeskJet 400
    gs -q -dBATCH -dNOPAUSE -sOutputFile=/dev/lp0 \
    -sDEVICE=deskjet syllabus.ps

    # 一部 usb 介面的多功能事務機 HP OfficeJet 4200
    gs -q -dBATCH -dNOPAUSE -sOutputFile=/dev/usb/lp0 \
    -sDEVICE=ijs -sIjsServer=hpijs -dIjsUseOutputFD \
    -sDeviceManufacturer="HEWLETT-PACKARD" \
    -sDeviceModel="officejet 4200" syllabus.ps

    當然你的印表機型號可能與我的不同, 所以 -sDEVICE 後面可能需要指定不同的字串。 請下 gs --help | less 查看有那些型號字串可以選擇。

    至於 .jpg, .png, ... 等等各種圖檔, 則可用 netpbm 套件當中的對應指令, 先轉成 pnm 格式 (其實是 pbm, pgm, ppm 的概稱), 再用 pnmtops 轉成 ps, 即可列印。

    目前所有印表機廠商當中, 對 Linux 的支援最友善/完整/公開的廠商, 應屬 hp。 他們的 hpijs 驅動程式是自由軟體, 支援 幾乎所有型號 的 hp 噴墨印表機。 當你在 gs 命令列上指定 -sDEVICE=ijs 時, gs 變成只是一個前端; 大部分的工作其實都透過 ijs 介面, 由外部程式 hpijs 在做。 在 Mandrake 底下, 安裝 printer-filters 套件, 裡面就有 hpijs。 rpmfind 底下也可找到單獨包裝的 hpijs 套件。 透過 ijs 介面躲在 ghostscript 後面服務的驅動程式, 還有 GIMP-Print 等等。

    如果印不出東西, 可以試著分兩階段: 先用 ghostscript 列印到檔案裡面 (例如將 -sOutputFile 的參數改成 /tmp/output.prn), 再將這個檔案印到印表機裝置 (例如 cat /tmp/output.prn > /dev/usb/lp0) 看看問題到底出現在應用軟體層次以上 (gs 與 hpijs), 還是作業系統層次以下。 (欠缺 kernel module?)

    Foomatic: 型號與驅動方式的對照表 及 對照表管理員

    每次列印都要下那麼多參數, 有點麻煩。 如果你的電腦 (直接或透過網路) 接上好幾部印表機, 或是經常帶著筆記電腦在不同的地點使用不同的印表機, 就更頭大了。 如果可以為每部印表機取個名字, 並且只設定一次那些複雜的參數, 從此以後列印時, 只指定印表機名稱就好了, 這樣不是比較簡單嗎? Foomatic 的最基本功能就在提供這個服務。 它有一個很大的對照表, 記載這每一種印表機需要使用的驅動程式與參數。 每部印表機稱為一個 queue, 通常必須設定四個參數:

    • 印表機名稱: 就是手冊裡面所說的 "queue name", 這個名字隨便你自己取
    • 它的硬體介面: 現在的 usb 印表機通常是 "/dev/usb/lp0"; 舊的 parallel port 印表機通常是 "/dev/lp0"
    • 它的型號: 例如 "HP-OfficeJet_4200"
    • 採用那個驅動程式: 例如 "hpijs"

    「型號」 與 「驅動程式」 兩個字串是怎麼找到的呢? 下這個指令: foomatic-configure -O | less 在裡面搜尋你的印表機型號, 它的 id 欄及 driver 欄就是了。 以我另一部印表機 HP OfficeJet 4200 為例, 搜尋 "4200" 得知這個型號的 id 是 "HP-OfficeJet_4200", 而 driver 是 "hpijs"。 然後:

            foomatic-configure -s direct -Q # 增加印表機之前, 先查詢一下。
    foomatic-configure -s direct -n hp4200 -c usb:/dev/usb/lp0 \
    -p HP-OfficeJet_4200 -d hpijs
    foomatic-configure -s direct -Q # 增加印表機之後, 再查詢一下。
    foomatic-configure -s direct -n hp4200 -D
    foomatic-configure -s direct -Q # 指定內定印表機之後的確認。

    這裡的 -Q 是查詢; -D 是將之設為內定的印表機: 將來列印時, 如果不指明印到那一部, 就自動印到這一部。 從此以後, 可以這樣簡單列印: bg5ps < syllabus.txt | foomatic-rip 如果要從另外一部印表機列印, 只要在 foomatic-rip 後面用 -n 指定印表機名稱就可以了。 也就是說, foomatic 最大的功用就是簡化命令列 -- 使用者指定印表機名稱, foomatic 負責填入 gs (或是其他驅動程式) 命令列上的許多細節。

    [foomatic: 對照表及管理員]

    如果 gs 直接印沒有問題; 用 foomatic-rip 印卻無聲無息地失敗, 可以先將上面的 -c usb:/dev/usb/lp0 改成 -c stdout, 令 foomatic-rip 印到終端機視窗。 此時若不小心直接下 foomatic-rip 列印, 螢幕會接收到印表機的控制碼而亂掉。 這時可以下 reset 指令恢復正常; 如果還是亂碼, 可以再試試看 perl -e 'print chr(15)'。 當然我們的目的其實是要將列印動作分成兩步, 以便找出究竟是那一步出了問題:

            foomatic-rip syllabus.ps > /tmp/syllabus.prn
    cat /tmp/syllabus.prn > /dev/usb/lp0

    像我就曾經因此而發現 "device or resource busy", 再用上面所說的 lsof 查看 /dev/usb/lp0 而發現原來是先前實驗的 hpoj 套件佔據了印表機埠。

    Foomatic-configure 與 foomatic-rip 都是 perl script, 如果有必要 (例如覺得手冊寫得不夠詳盡), 也可以直接去讀它的原始碼; 甚至於光是從註解就可以找到有用的資訊, 像是上面的 -c stdout 就是在註解裡面發現的。 這就是自由軟體的好處 :-)

    其實可以驅動一部印表機的驅動程式 (driver) 可能不只一套軟體, 例如有很多印表機既可用 gs 驅動, 也可用 gimp-print 驅動。 不同的驅動程式, 在不同的列印狀況下, 效果可能有高下之別。 如果你不滿意內定的驅動程式, 可以在 foomatic-configure -O | less 裡面找到 drivers 一欄, 改用其他驅動程式當做 設定時 -d 的參數。 細節請見 foomatic 的 USAGE 文件。

    至於 -s direct 是什麼意思呢? 那就要談到 spooling 了。

    Spooler: 多人/多機共用印表機的救星

    反過來說, 如果許多人/許多部電腦共用一部印表機, 會發生什麼問題? 可能兩份文件緊接著到達, 前一份還沒有印完, 後一份的資料就到了。 如果沒有處理好, 兩份文件恐怕就要 「你儂我儂」 纏夾不清了。 因此需要有一支程式擋在應用軟體與印表機驅動程式之間, 令所有的列印工作 (稱為 print jobs) 按順序排好, 一整份印完才開始印下一份。 這支程式就叫做 spooler; 這也是為什麼每部印表機稱為一個 printer queue。 如果你習慣同時從好幾個應用軟體裡面列印, 即使印表機只有你一個人在用, 也不宜將列印工作直接導入硬體 (/dev/usb/lp0), 而應該交由 spooler 安排時間列印。

    話說回來, 如果印表機由一人獨享, 而且使用者總是習慣性地耐心等一個檔案印完才開始印下一個檔案, 其實就不需要再安裝更多軟體了 -- 這時使用者本身就扮演 spooler 的角色 :-) 像我就習慣如此, 所以上面設定 foomatic 時, 用 -s direct 指定不使用任何 spooler, 是我較常用的列印方式。

    lpd 是最古老的 spooler; 新的 spooler 有 CUPS, LPRng, PDQ, ...等等。 你只需要安裝其中一套。 本文以 CUPS 為例, 來說明 spooler 與 foomatic 的關係。

    CUPS

    [CUPS: 多功能 spooler] Common Unix Printing System (CUPS) 是目前最常用的 spooler 之一。 請確認一下已安裝 cups 套件。 然後用 root 的身份啟動 cups 服務: service cups start 它的設定介面叫做 cups web interface, 使用方式很簡單, 用瀏覽器打開 http://localhost:631 就可以操作。 選擇 "Do Administration Tasks", 用 root 的帳號密碼登入。 然後:

    1. 選擇 "Manage Available Printers": 如果這是第一次使用 cups, 此時應該沒有看到任何印表機。
    2. 選擇 "Add a New Printer": 這裡的欄位都可以隨意填寫, 自己要記得就是了。
    3. 選擇 "Device" (硬體裝置): 如果是 usb 印表機, 應該會在 USB Printer #1 後面看到系統偵測到的印表機回應字串。 不要高興得太早, 還不一定可以驅動。
    4. 選擇 "Make" (廠牌)
    5. 選擇 "model" (型號): 且慢按下 "Continue", 先用 foomatic-configure -s cups -Q 查一下 cups 目前的設定, 並且用 ls /etc/cups/ppds/ 查看目錄裡面有沒有東西, 又用 less /etc/cups/printers.conf 查看目前 cups 認得本機上那些印表機。 按下 "Continue" 之後, 再查一次, 看看有何變化。

    如果找不到適合的型號, 不必勉強將就。 可以到 linuxprinting 網站瀏覽完整的 印表機列表。 請找到正確的型號, 下載它的 ppd 檔。 用 gzip 壓縮, 並放到 /usr/share/cups/model 的某個子目錄底下 (依據它的廠牌)。 必須重新啟動 cups, 並且用瀏覽器重新登入, cups 才知道有新的 ppd 可以用。

    或者不從網站下載, 直接用 foomatic 替 cups 產生 ppd [1] [2] 更簡單: foomatic-configure -s cups -n hp4200 -c usb:/dev/usb/lp0 -p HP-OfficeJet_4200 -d hpijs 這背後其實已經發生了三件事情:

    1. foomatic 從它資料庫當中對應的 xml 檔產生出 ppd 檔
    2. 不經過 cups 的瀏覽器介面, 而是直接把 ppd 檔放到 /etc/cups/ppds/ 目錄下
    3. 不經過 cups 的瀏覽器介面, 而是直接修改 /etc/cups/printers.conf 告知 cups 有一部新的印表機

    所以只需要重新啟動: service cups restart, 不需要再設定, cups 就會認得了, 可以再用瀏覽器進去確認看看。 也就是說要設定 cups, 可以選擇用 foomatic 或用 cups web interface。 當然後者專門搭配 cups 而設計, 所以選項較多; 但是單就設定驅動程式而言, foomatic 可以說是天下無敵。

    不論是用那一種方式設定, 完成之後可以下 lpq 指令查看系統內印表機的狀態; 下 lpr 指令列印檔案; 下 lprm 指令刪除尚未列印完的 print job。 詳見 cups 套件的許多相關文件。 這種透過 spooler 列印的方式, 你可以從好幾個應用軟體甚至好幾部電腦放心地同時列印; 在下完 lpr 指令之後, 甚至直接刪除 .ps 檔也沒有關係, 因為 spooler 已經把檔案複製到系統的 printer queue 去了。

    如果細心查看, 會發現 /usr/bin/lpr 其實指向 /etc/alternatives/lpr 而後者又指向 /usr/bin/lpr-cups 其他類推。 為什麼要這麼麻煩呢? 前面說過, unix 底下的 spooler 系統有很多套。 這樣的安排, 方便系統管理員很容易將 CUPS 換成 LPRng, 或將 PDQ 換成 CUPS, 系統只需要改 symbolic link 就好; 至於使用者則不需要知道究竟用的是那一套 spooler, 可以一直用相同的列印指令。

    事實上 cups 不僅僅是一個 spooler, 底下牽涉的協助軟體也不只有 ghostscript 及眾多 ps 轉換程式。 它還可以:

    1. 假扮網路 postscript 印表機 (即使真實的印表機不支援 postscript)
    2. 列印各種文字檔/圖形檔/...
    3. 將輸出分組, 每組有不同的設定 (例如每幾頁合印在一張紙的兩面等等複雜規則)

    不過筆者也是外行。 Kurt Pfeifle 所寫的 "Dissecting The CUPS Filtering System: A Network Postscript RIP For non-PS Printers" 畫出架構圖, 一目瞭然, 特別大力推薦給有勇氣深究 cups 架構的朋友。

    結語

    今日的 GNU/Linux 作業系統日趨成熟, 系統與硬體設定方式也越來越簡單, 和 MS Windows 一樣有 GUI 介面; 但是筆者仍舊偏好使用命令列或文字選單工具。 這是因為這類工具對於特殊環境 (例如 KDE 或 GNOME) 依賴性較低, 吃的資源較少, 而且較容易自動化 (透過 scripting)。 如果讀者與我一樣希望可以在各種版本的 Linux 之間自由切換穿梭, 卻又懶得為同一項工作 (例如設定印表機) 學那麼多不同的選單, 筆者大力推薦這種學習方式。 這並不表示我們需要記很多指令 -- 把實驗出來的心得寫成筆記, 或是直接寫入 shell script, 甚至像這樣寫成文章投稿, 既可省去自己記憶的麻煩, 也可同時嘉惠他人, 開始對惠我良多的自由軟體界做一點微薄的回饋, 感覺很棒。 (運氣好的話, 還可以順便賺稿費 :-)

    對於這類技術文件的寫法, 筆者也有一點意見。 介紹 GUI 的文件, 經常太低估讀者, 描述太多操作細節; 另一方面有些文件似乎又太高估讀者, 寫得很像 man page, 列出所有指令, 及許多很少用到的選項, 卻較少解釋那種場合需要用那個指令。 筆者認為 HOWTO 文件 "任務/觀念導向" 的寫法非常值得參考, 對於願意動腦但無力深究的人最有幫助。 下每個指令時, 給讀者一點動機 ("我們為什麼要下這個指令?") 至於細節, 可以留給 man page。 如果能夠再加上 trouble shooting 的流程圖, 或解釋系統架構的觀念圖, 就更好了。 筆者正在朝這個方向努力。

    最後要感謝 hpijs 的 David Suffield 迅速, 耐心地來回數次回答我許多問題, 終於找出原因 -- 原來 3535 可以選 3528 的驅動程式。 新版的 foomatic 對照表裡面將反應這點。 希望這篇文章不只有助於解決印表機的問題, 也可以促使讀者多多向軟體作者回報自己的問題, 像筆者一樣用 bug report 的方式貢獻自己微薄的力量, 加入自由軟體利已利他的行列! 至於如何發問才不會浪費軟體作者時間, 這又是另一個話題, 以後有機會再談囉。

    相關網站及更多資訊

    1. http://www.linuxprinting.org/: Linux 列印文件的大本營, 本文參考此處多份文件
    2. http://netpbm.sourceforge.net/: 用 2n 套轉換程式解決 n*n 個圖檔轉換問題的圖檔處理工具集
    3. http://www.linuxprinting.org/ijs/: 讓 ghostscript 可以外掛驅動程式的介面
    4. http://hpinkjet.sourceforge.net/: HP 的開放原始碼印表機驅動程式, 採用 ijs 介面附掛在 ghostscript 之下

    自己電腦裡面 /usr/share/doc/ghostscript*/ 裡面的文件也很值得參考, 特別是 Use.html。


  • 相关阅读:
    OK335xS-Android mkmmc-android-ubifs.sh hacking
    OK335xS-Android pack-ubi-256M.sh hacking
    OK335xS Ubuntu 12.04.1 版本 Android 开发环境搭建
    Qt Quick Hello World hacking
    Qt QML referenceexamples attached Demo hacking
    QT 5.4.1 for Android Ubuntu QtWebView Demo
    I.MX6 working note for high efficiency
    QT 5.4.1 for Android Windows环境搭建
    mkbootimg hacking
    Generate And Play A Tone In Android hacking
  • 原文地址:https://www.cnblogs.com/leaven/p/1863463.html
Copyright © 2020-2023  润新知