最近由于做叽歪助手项目(一个基于微信的自助点餐系统)需要打印小票,公司的小同志门用JS调用Word控件去打印小票,功能是可以实现,不过通过Word控制格式,动态加载小票内容,还是挺麻烦的,而且稳定性很差,是真的很差,用Word的ActiveX控件打印的话,必须将*.doc,*.docx格式的打印模版下载到客户端本地,Word才能读(如果服务端要换模版,怎么办?),关键在打印的时候,ActiveX控件都要打开这个doc的模版文档,如果同事有多个打印启动on个怎么办?挂掉!还有,用Word如何在打印小票里面加一个二维码图片?一直找不到方法,或者说js读图片在客户端并插入到doc的模版,这个想法就是有毛病......
没办法,只能考虑自己做浏览器的插件或者自定义的ActiveX控件(如何做插件和控件这里不扯了),要做自己定定义的打印控件的话,就得一并解决这么几个问题:
1、易于控制内容和格式模版
2、解决并行打印问题(后来发现这不是个问题)
3、拆入图片二维码
为了解决这个问题,就找资料研究了下打印机,结论方案是:
1、用txt文档作为打印模版文档,可以从服务端直接读取文本内容,无需下载,易于部署和更新
2、自己做一个简单的模版标签解析,可以简单解决打印内容的动态变化,未来在基本不用改变C#代码的情况下,通过前端传递的名-值对参数,进行参数动态加载和替换。
3、将打印的图片文本直接画成图形,在从服务端读取动态的订单二维码图片留,插入到打印的图形
哦了,理论加时间,经过几轮调试,完美实现,并在产品项目中已经实施,非常好用。遗憾的是,现在只是实现ActiveX的控件,只能在IE里面跑~
下面放代码:
public partial class ClientPrinter: UserControl,IObjectSafety
{
private PrintDocument doc = new PrintDocument();
private string orderData;
private string templateStr;
private string imgSrc;
private int rowY = 0;
public ClientPrinter()
{
InitializeComponent();
this.doc.PrintPage += Doc_PrintPage;
}
public void DoPrint(string inData,string imgUrl,string tempUrl)
{
this.orderData = inData;
this.templateStr = this.getFileByText(tempUrl,"utf-8");
this.imgSrc = imgUrl;
this.doc.DefaultPageSettings.Margins = new Margins(0, 0, 0, 0);
//this.doc.DefaultPageSettings.PaperSize = new PaperSize("Custum", 300,300);
PrintController printController = new StandardPrintController();
this.doc.PrintController = printController;
#if DEBUG
PrintPreviewDialog dialog = new PrintPreviewDialog();
dialog.Document = doc;
DialogResult res = dialog.ShowDialog();
#endif
this.doc.Print();
}
private void Doc_PrintPage(object sender, PrintPageEventArgs e)
{
Font font = new Font(new FontFamily("黑体"), 8);
Font fontL = new Font(new FontFamily("黑体"), 12);
string[] parseData = this.orderData.Split('&');//把所有主体打印数据按key=value分段到数组
string[] proList;
Dictionary<string, string> contentData = new Dictionary<string, string>();//创建内容主体内容的Key,Value对象集合
List<Dictionary<string, string>> productData = new List<Dictionary<string, string>>();//创建订单详情信息的集合列表
//解析名值数组
foreach (string d in parseData)
{
if (d.Contains(","))//解析有多个产品的订单详情
{
if (d.Contains(";"))
{
proList = d.Split(';');
foreach (string p in proList)
{
string[] itemData = p.Split(',');
Dictionary<string, string> item = new Dictionary<string, string>();
foreach (string itemStr in itemData)
{
string[] iStr = itemStr.Split('=');
item.Add(iStr[0], iStr[1]);
}
productData.Add(item);
}
}
else//解析单一产品的订单详情
{
string[] itemData = d.Split(',');
Dictionary<string, string> item = new Dictionary<string, string>();
foreach (string itemStr in itemData)
{
string[] iStr = itemStr.Split('=');
item.Add(iStr[0], iStr[1]);
}
productData.Add(item);
}
}
else//基本订单信息
{
string[] oStr = d.Split('=');
contentData.Add(oStr[0], oStr[1]);
}
}
//根据名值对替换订单基本信息
foreach (KeyValuePair<string, string> item in contentData)
{
this.templateStr = this.templateStr.Replace("{"+item.Key+"}",item.Value);
}
string[] stringArr = this.templateStr.Split('\n');//将已经替换基本订单信息的打印模版列表拆分行
//遍历信息行,逐行建立打印图形内容
foreach (string s in stringArr)
{
//遇到标记二维码行替换打印二维码,并进入下一行
if (s.StartsWith("{qrcode}"))
{
//s.Replace("{qrcode}", "");
Image img = this.getImage(this.imgSrc);
e.Graphics.DrawImage(img, 40, getY(this.rowY));
continue;
}
//处理订单详情的行信息
if (s.StartsWith("#"))
{
string lineStr = s.Replace("#", "");
string outStr = "";
foreach (Dictionary<string, string> dic in productData)
{
outStr = lineStr;
foreach (KeyValuePair<string, string> item in dic)
{
outStr = outStr.Replace("{" + item.Key.ToLower() + "}", item.Value);
}
e.Graphics.DrawString(outStr, font, Brushes.Black, 10, getY(this.rowY));
}
continue;
}
if (this.rowY == 0)
e.Graphics.DrawString(s, fontL, Brushes.Black, 10, getY(this.rowY));
else
e.Graphics.DrawString(s, font, Brushes.Black, 10, getY(this.rowY));
}
}
private string getFileByText(string Url, string FileEncoding)
{
string sRslt = null;
WebResponse oWebRps = null;
WebRequest oWebRqst = WebRequest.Create(Url);
oWebRqst.Timeout = 50000;
oWebRps = oWebRqst.GetResponse();
StreamReader oStreamRd = new StreamReader(oWebRps.GetResponseStream(), Encoding.GetEncoding(FileEncoding));
sRslt = oStreamRd.ReadToEnd();
oStreamRd.Close();
oWebRps.Close();
return sRslt;
}
private Image getImage(string Url)
{
WebRequest myrequest = WebRequest.Create(Url);
WebResponse myresponse = myrequest.GetResponse();
Stream imgstream = myresponse.GetResponseStream();
System.Drawing.Image img = System.Drawing.Image.FromStream(imgstream);
return this.ToThumbnailImages(100, img);
//return img;
}
private Image ToThumbnailImages(int w, Image SrcImage)
{
int oWidth = w;
int oHeight = (SrcImage.Height * oWidth) / SrcImage.Width;
Image NewImage = SrcImage.GetThumbnailImage(oWidth, oHeight,new Image.GetThumbnailImageAbort(ThumbnailCallback), IntPtr.Zero);
return NewImage;
}
private bool ThumbnailCallback()
{
return false;
}
private float getY(int i)
{
string f = i.ToString() + ".00";
float row = float.Parse(f);
this.rowY++;
return (float)(18.00 *row);
}
}
}
前端JS的调用以及传参数:
function doTest()
{
var obj = document.getElementById("printer");
obj.DoPrint(
"title=茉莉咖啡&flag=001&ordertime=17/07/02 19:34&orderprice=100.00&paymoney=95.00&savemoney=5.00&orderid=ASFAFADAFASAFSASDFA&productname=娃哈哈,num=3,price=1.00;productname=娃哈哈,num=3,price=1.00;productname=娃哈哈,num=3,price=1.00",
"http://stg.wx.jonvie.com/staticfiles/qrcode?codeurl=http://stg.wx.jonvie.com/gyzhushou",
"http://stg.wx.jonvie.com/Test/print.txt"
);
}
打印的TXT模版:
{title}
===============================
标识:{flag} 日期:{ordertime}
产品 单价 数量
===============================
#{productname} {price} x {num}
总价:{orderprice} 实付:{paymoney}
优惠:{savemoney}
===============================
{orderid}
{qrcode}
小票当天有效
.