[轉]Java 實現 POS 打印機無驅打印

來源:http://www.ibm.com/developerworks/cn/java/j-lo-pos/

 

Java 實現 POS 打印機無驅打印

developerWorks
文檔選項
將此頁作爲電子郵件發送

將此 頁作爲電子郵件發送


級 別: 初級

於 丙超 ([email protected] ), 總經理, 北京周服科技有限公司

2009 年 6 月 29 日

Java 對硬件的控制一直以來都不是其強項,特別是打印,Java 很難實現對 POS 打印機的直接控制,並判斷打印是否成功。本文將探討一種方法,講述如何使用 Java 技術與網口的 POS 打印機連接,通過 Socket 技術直接將愛普生指令寫入打印機端口,打印出相應的內容或者條形碼,並實現字體的放大,打印完畢自動走紙等功能。

行業需求

我們是一家專業做酒店餐飲軟件的公司,餐飲軟件一個重要的功能就是後廚打印問題,前臺點菜完畢,後廚立刻打印出單子,這樣就減少人工遞單的麻 煩,節省時間,提高翻檯率。這種信息化解決方案對打印技術要求很高,理論上最好 100% 不丟單,也就是每次點菜後廚都會相應出單子,但是實際上行不通,爲什麼呢?因爲網線、打印機、網卡等都有可能有問題,別說打印機等硬件因爲廚房油煙問題損 壞,我們甚至碰到過網線被老鼠咬斷的情況,總之硬件網絡故障防不勝防,所以只能退而求其次,就是有問題不可怕,程序能夠判斷是否出了問題,並能給出提示, 便於服務員處理,及時補單。

如果我們用安裝 Windows 驅動的方法來實現後廚打印,那麼肯定是不行的,因爲我們只能單向向驅動程序拋包,不能從驅動程序獲得任何返回值,沒有辦法瞭解是否打印成功。而且更爲嚴重 的是,有時候因爲後廚打印機過多,Windows 驅動甚至會因爲網絡堵塞自作主張將包丟棄,沒有任何提示。

這在行業應用中是不行的,會給用戶帶來損失,所以想到了繞過 Windows 驅動,直接寫端口的方法。





回頁首


無驅打印的可行性

所謂直接寫端口的方法,就是不用安裝打印機驅動,不使用 PrinterJob 獲得打印機的名字的方法進行打印。

衆所周知,之所以安裝打印機驅動,一個重要的原因就是打印機廠商千差萬別,不同的打印機往往都有各自的驅動,很難實現萬能驅動。但是,在 POS 打印機行業卻有一條捷徑,就是現在市面上的 POS 打印機基本上都支持愛普生指令,也就是說,只要將程序和打印機聯通,直接向端口裏面寫愛普生指令就可以控制打印機。

打印機接受到愛普生指令以後,自行進行解析,然後打印出相應的內容。





回頁首


愛普生指令

日本的 EPSON 公司在目前的 POS 打印機市場,尤其是針式打印機市場佔有很大一部分份額。它所推行的 ESC 打印控制命令 (EPSON StandardCode for Pr5nter) 已經成爲了針式打印機控制語言事實上的工業標準,ESC/POS 打印命令集是 ESC 打印控制命令的簡化版本,現在大多數 POS 打印都採用 ESC/POS 指令集。絕大多數打印機都有 EPSON ESC 的軟件命令仿真功能,而且其它打印控制命令的格式和功能也都與 ESC 代碼集類似。

由於早期的操作系統 DOS 與現在 Windows 的結構不同,在打印機內部軟件和應用軟件之間沒有由硬件廠商提供的打印驅動程序,必須由應用軟件直接通過硬件接口來控制打印機,所以從 ESC 指令出現開始,它就是公開的,否則沒有應用軟件可以使用它,而除了標準的 ESC 指令外,每種型號的打印機其指令又不太一樣,所以在 DOS 軟件中,你可以看到每個應用軟件都只是支持爲數不多的幾種常用打印機。

ESC 指令在形式上分爲兩種格式,一種是文本方式控制碼,一種是 Escape 轉義序列碼。文本方式控制碼由一字節字符碼錶示,實現的是與打印機硬件操作有關的指令,Escape 序列碼由轉義字符和參數字符或打印數據組成。





回頁首


建立打印連接

通過上面的介紹,瞭解了實現無驅打印原來只是一層窗戶紙,具體的方法就是首先建立打印機連接,然後寫入愛普生指令即可。那麼如何建立打印機連 接?以網口 POS 打印機舉例。

第一步,首先要給網口打印機賦一個 IP 地址,例如叫做 192.168.0.18 。

第二步,編寫連接代碼。

Socket client=new java.net.Socket(); 
PrintWriter socketWriter;
client.connect(new InetSocketAddress("192.168.0.18" , 9100),1000); // 創建一個 socket
socketWriter = new PrintWriter(client.getOutputStream());// 創建輸入輸出數據流

看起來跟一般的 socket 連接沒有很大的區別,就是賦一個 IP 地址和一個端口號,並設置一下超時時間即可,只需要說明的是,一般 POS 打印機的端口都是 9100 。





回頁首


寫入打印內容

連接建立完畢,寫入內容就非常容易,只要使用 write 或者 println 方法寫入即可,其中 write 方法是寫入數字或字符,println 寫入一行字符串。

例如:寫入數字 socketWriter.write(0);

寫入一行字符串 socketWriter.println( “巧富餐飲軟件後廚單據” );

再入一行字符串 socketWriter.println( “桌位 14 桌,人數 3 ” );

再入一行字符串 socketWriter.println( “跺腳魚頭 1 份” );

您或許有疑問?內容已經成功寫入,好像我們還沒有用到愛普生指令。是的,如果只是普通的寫入內容,不需要用到愛普生指令,愛普生指令主要幫助 實現放大字體,自動走紙,打印條形碼等功能。





回頁首


放大字體

放大字體需要用到愛普生的 0x1c 指令,使用愛普生指令的方法很簡單,只要向端口寫入指令即可,例如:

socketWriter.write(0x1c);

注意 0x1c,是 16 進制的數字,當然也可以轉換成 10 進制來寫。需要說明的是,使用愛普生指令放大字體不能隨意放大,因爲它不是圖形化打印,而是文本化打印,所以縱向或者橫向只能按照倍數放大,不能矢量放 大。例如在 POS58 打印機上將“巧富餐飲軟件”幾個字放大打印,可以有如下放大方法。

/* 橫向放大一倍 */ 
socketWriter.write(0x1c);
socketWriter.write(0x21);
socketWriter.write(4);
/* 縱向放大一倍 */
socketWriter.write(0x1c);
socketWriter.write(0x21);
socketWriter.write(8);
/* 橫向縱向都放大一倍 */
socketWriter.write(0x1c);
socketWriter.write(0x21);
socketWriter.write(12);

一般情況下,我們傾向採用縱向放大一倍的方法,放大後的字體看起來有點像仿宋體,視覺效果還不錯。





回頁首


兼容多種類型打印機

現在知道了使用愛普生指令的方法,所以只要有一本愛普生指令手冊在手裏,就可以用 Java 控制打印機進行無驅打印。但是現在問題是,同樣是愛普生指令,不同的 pos 打印機可能不一樣,就拿放大字體來說,pos58 打印機和 pos80 打印機指令就不盡相同。這時候怎麼辦呢?如何兼容多種類型打印機?

比如說,有的打印機並不是使用 0x1c 作爲放大指令,而是使用 0x1b 作爲放大指令,怎麼辦?容易。

/* 橫向放大一倍 */ 
socketWriter.write(0x1c);
socketWriter.write(0x21);
socketWriter.write(4);
socketWriter.write(0x1b);
socketWriter.write(0x21);
socketWriter.write(4);
/* 縱向放大一倍 */
socketWriter.write(0x1c);
socketWriter.write(0x21);
socketWriter.write(8);
socketWriter.write(0x1b);
socketWriter.write(0x21);
socketWriter.write(8);
/* 橫向縱向都放大一倍 */
socketWriter.write(0x1c);
socketWriter.write(0x21);
socketWriter.write(12);
socketWriter.write(0x1b);
socketWriter.write(0x21);
socketWriter.write(12);

看明白了嗎?就是寫兩遍就行,因爲如果 0x1b 指令若不存在,打印機自動將其拋棄。





回頁首


實現自動走紙

POS 打印機因爲出紙口有一些深度,打印完畢爲了避免撕裂文字內容,一般需要適當走紙纔行,當然可以使用愛普生指令來走紙,但是這樣並不穩妥,爲什麼呢 ? 因爲要考慮 POS 機的兼容性,所以一般採用打印空行的方式實現走紙。

for(int i=0;i<10;i++){ 
socketWriter.println(" ");// 打印完畢自動走紙
}

顯然,打印空行的方式有更好地兼容性。





回頁首


打印條形碼

條形碼在各個行業中現在有廣泛的應用,所以讓打印機打印條形碼是非常重要的功能,不過你不需要費好多精力去研究條形碼知識,因爲愛普生指令中 有一個打印條形碼指令,例如我們要打印條形碼“ 091955826335 ”,只要使用如下命令即可。

socketWriter.write(0x1d); 
socketWriter.write(0x68);
socketWriter.write(120);
socketWriter.write(0x1d);
socketWriter.write(0x48);
socketWriter.write(0x01);
socketWriter.write(0x1d);
socketWriter.write(0x6B);
socketWriter.write(0x02);
socketWriter.println "091955826335" );
socketWriter.write(0x00);





回頁首


完整的代碼

好了,下面舉一個完整的例子,我們來建立一個叫做 print 的方法,向某個打印機打印一個字符和條形碼,並實現自動走紙,代碼如下:

private boolean print(String ip, int port, String str,String code,int skip) 
throws Exception{
Socket client=new java.net.Socket();
PrintWriter socketWriter;
client.connect(new InetSocketAddress(ip,port),1000); // 創建一個 socket
socketWriter = new PrintWriter(client.getOutputStream());// 創建輸入輸出數據流
/* 縱向放大一倍 */
socketWriter.write(0x1c);
socketWriter.write(0x21);
socketWriter.write(8);
socketWriter.write(0x1b);
socketWriter.write(0x21);
socketWriter.write(8);
socketWriter.println(str);
// 打印條形碼
socketWriter.write(0x1d);
socketWriter.write(0x68);
socketWriter.write(120);
socketWriter.write(0x1d);
socketWriter.write(0x48);
socketWriter.write(0x01);
socketWriter.write(0x1d);
socketWriter.write(0x6B);
socketWriter.write(0x02);
socketWriter.println(code);
socketWriter.write(0x00);
for(int i=0;i<skip;i++){
socketWriter.println(" ");// 打印完畢自動走紙
}
}





回頁首


小結

本文雖然只是講述了網口打印機的直接寫端口方式,似乎對並口打印機無效,其實不是這樣,並口打印機只要接一個打印服務器就可以用了,缺點就是 非一體機,然後還要安裝打印服務器驅動。

這種無驅打印在非常廣泛的範圍內可以得到應用,包括餐飲、超市、醫藥等等其他需要用到 POS 打印機的行業。



參考資料

  • Merlin 的魔力:在 JDK 1.4 中打印,第 1 部分 ”(developerWorks,2002 年 4 月):介紹 JDK 1.4 爲 Java 平臺帶來了另一套打印功能和技術。
  • Java 打印程序設計 ”(developerWorks,2002 年 10 月):介紹 JDK 1.4 提供的一套完整的“Java 打印服務 API”(Java Print Service API)。
  • 用 Java 設計面向對象的打印程序 ”(developerWorks,2002 年 12 月):如果將打印內容進行對象化分析,設計出面向對象的打印程序,則會更出色地完成打印要求,並且代碼很容易重用,事半功倍。
  • 您 可以在 developerWorks 的 Java 技術專區 中找到有關 Java 編程各方面知識的文章。


關於作者

於丙超,北京周服科技有限公司(http://www.chofo.com)創始人,法人,總經理,有十多年 Java 編程經驗,已出版各種 IT 專業書籍四本,在各類雜誌,期刊發表論文多篇。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章