指令打印与驱动打印随笔
本文對(duì)指令打印和驅(qū)動(dòng)打印做了一個(gè)簡(jiǎn)要的介紹,分享了在開發(fā)客戶端打印組件時(shí)的一些過(guò)程并提出了一個(gè)新輪子用于解決老的問(wèn)題并引出更多的新問(wèn)題。全文大概 3500 字無(wú)圖,閱讀大概需要 7 分鐘。
驅(qū)動(dòng)打印是指:使用 PrintDocument 進(jìn)行打印。通過(guò)注冊(cè)其 PrintPage 方法拿到 Graphics 對(duì)象使用 GDI+ 畫圖打印。
指令打印是指:利用打印機(jī)廠商提供的指令協(xié)議控制打印機(jī)直接打印。
驅(qū)動(dòng)打印和 Windows 平臺(tái)關(guān)聯(lián)緊密,所以不能做到跨平臺(tái)。驅(qū)動(dòng)打印大部分情況不能即插即用,在第一次將某打印機(jī)鏈接到電腦時(shí),可能需要安裝對(duì)應(yīng)的驅(qū)動(dòng)程序系統(tǒng)才能正確的識(shí)別到該打印機(jī)。
絕大部分小票打印機(jī)都支持 ESC 指令,除了 ESC 外常見的還有 TSC、TSPL,PPLA等這與打印機(jī)廠商和型號(hào)相關(guān)。指令打印可以跨平臺(tái),且在不同的平臺(tái)要向硬件發(fā)出的指令是相同的,無(wú)論鏈接方式是 USB、串口還是藍(lán)牙。
從開發(fā)的角度來(lái)說(shuō),如果我們想兼容市面上大多數(shù)打印機(jī)并且想支持跨平臺(tái),那么這就會(huì)是一件需要仔細(xì)斟酌和權(quán)衡的事情:
1、僅采用驅(qū)動(dòng)打印。那么我們不得不放棄對(duì)跨平臺(tái)的支持。如果遇到過(guò)老的設(shè)備,它很可能沒有提供對(duì)最新的操作系統(tǒng)(比如 Windows 10)的支持,所以單純的驅(qū)動(dòng)打印是玩兒不贏的。
2、僅采用指令打印。我們可以做到跨平臺(tái),無(wú)懼系統(tǒng)升級(jí),但仍有無(wú)解的情況:如果客戶的打印機(jī)沒有指令打印或者指令協(xié)議很小眾沒必要做支持怎么辦?這是真實(shí)發(fā)生的事情,有客戶需要用傳統(tǒng)的辦公用打印機(jī)打印小票,真·謎一般的操作。
3、驅(qū)動(dòng)打印和指令打印并行。這當(dāng)然會(huì)解決上述問(wèn)題,但同時(shí)會(huì)引入新的問(wèn)題:你不得不寫出多套不同的代碼去完成一件相同的事情,更可怕的是在修改一個(gè)問(wèn)題時(shí)很可能會(huì)改了這一套忘了那一套。
在項(xiàng)目起初,因?yàn)閷?duì)各種打印方案并不熟悉所以帶你部分經(jīng)過(guò)了上述三個(gè)階段的演變。當(dāng)支持的打印機(jī)和打印格式越來(lái)越多,維護(hù)這部分代碼就成為一件苦力活兒,而且非常容易出錯(cuò)。接手這部分代碼的人會(huì)被懷疑是否能力有問(wèn)題,畢竟開始的時(shí)候時(shí)那么的簡(jiǎn)單。
大概 2019 年 7 月份時(shí),項(xiàng)目組對(duì)驅(qū)動(dòng)打印進(jìn)行了封裝,該封裝參考了網(wǎng)上的開源組件,構(gòu)建出了一個(gè)名為 TicketDocument 的類型,并添加了一些基礎(chǔ)操作:
TicketDocument 可以序列化為 JSON 字符串用于在網(wǎng)絡(luò)間傳輸。所以可以將 TicketDocument 的生成放置在服務(wù)端,這樣對(duì)打印格式進(jìn)行微調(diào)時(shí)不需要更新客戶端。
項(xiàng)目中對(duì) TicketDocument 的調(diào)用類似如下,其中 doc 變量即 TicketDocument 實(shí)例:
doc.AddText($"來(lái)源:{g.SName}"); doc.AddNewRow(); doc.AddText($"出廠時(shí)間:{g.CommandDate:yyyy/MM/dd}"); doc.AddNewRow(); doc.AddText($"產(chǎn)品:{g.Items.Count(i => i.FXashId == 0)}件", width: 0.4f); doc.AddText($"附件:{g.Items.Count(i => i.FXashId != 0)}件", width: 0.3f, offset: 0.4f, alignment: StringAlignment.Center); doc.AddText($"共計(jì):{g.Items.Count}件", width: 0.3f, offset: 0.7f, alignment: StringAlignment.Far);當(dāng)項(xiàng)目不得不支持指令打印時(shí), TicketDocument 的抽象定義就不能滿足需求了:因?yàn)橹噶畲蛴〔⒉荒芴峁╊愃朴?GDI+ 這種強(qiáng)大的控制力。
驅(qū)動(dòng)打印和指令打印并行的事情必須上馬。因?yàn)橹噶罡鞑幌嗤?#xff0c;所以就編寫了不同的代碼對(duì)應(yīng)不同的打印機(jī),業(yè)務(wù)應(yīng)用調(diào)用打印宿主時(shí)也采用多種不同的協(xié)議格式,因項(xiàng)目不同沒有使用 TicketDocument 。這對(duì)驅(qū)動(dòng)打印部分造成了影響,滿天飛的硬編碼,寫死的數(shù)組下標(biāo),接著在對(duì)打印格式進(jìn)行調(diào)整時(shí),驅(qū)動(dòng)打印罷工了。
于是,我們需要一個(gè)新的輪子:
它應(yīng)該滿足跨平臺(tái)打印的需求,在 Windows、Android、iOS 中有相同的行為表現(xiàn)。
它應(yīng)該同時(shí)支持驅(qū)動(dòng)打印和指令打印。
在滿足前兩條的同時(shí),它應(yīng)該盡量減少新增格式時(shí)的工作量。
All problems in computer science can be solved by another level of indirection .
計(jì)算機(jī)科學(xué)中的所有問(wèn)題都可以通過(guò)間接的另一個(gè)層次來(lái)解決。
出自:David Wheeler這是軟件工程學(xué)中的一個(gè)真理,我們可以引入一種新的自定義指令來(lái)決絕上述的問(wèn)題:
這種指令是一種高級(jí)指令,它對(duì)驅(qū)動(dòng)打印和大部分目前受支持的指令打印行為進(jìn)行了封裝。
這種高級(jí)指令最終會(huì)被翻譯成對(duì) Graphics 的操作或打印機(jī)指令。
這種高級(jí)指令由業(yè)務(wù)系統(tǒng)生成并可以在網(wǎng)絡(luò)中進(jìn)行傳播。
這種高級(jí)指令可以使用目前的主流編程語(yǔ)言生成,比如 C#、Java、Python、PHP、JavaScript 等。
這種高級(jí)指令應(yīng)該易于識(shí)別,并盡量減少在網(wǎng)絡(luò)傳輸中的流量消耗。
TicketDocument 似乎是一個(gè)不錯(cuò)的先驅(qū)者,目前為止它滿足了 3、4、5 這三個(gè)條件。但設(shè)計(jì)一種高級(jí)指令并不是唯一需要的事情,仍有許多工作要做比如這種高級(jí)指令的解析和轉(zhuǎn)換等。
目前為止我并沒有完成對(duì)這個(gè)輪子的全部設(shè)計(jì),以上是對(duì)這個(gè)輪子的設(shè)想。這個(gè)輪子在設(shè)計(jì)上還不完整,有許多空白的部分需要填上。如果您對(duì)這個(gè)輪子感興趣,可以收藏本站,在文章下留言或打賞作者,謝謝支持!
總結(jié)
以上是生活随笔為你收集整理的指令打印与驱动打印随笔的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 如何运用领域驱动设计 - 领域服务
- 下一篇: WeihanLi.Npoi 1.7.0