基于Win服务的标签打印(模板套打)
2021-01-25 15:14
标签:read 方案 body 配置文件 net string for 产品 ble 最近做了几个项目,都有在产品贴标的需求 基本就是有个证卡类打印机,然后把产品的信息打印在标签上。 然后通过机器人把标签贴到产品上面 标签信息包括文本,二维码,条形码之类的,要根据对应的数据生成二维码,条形码。 打印标签的需求接到手后,开始了我的填坑之旅。 打印3.0源代码:https://github.com/zeqp/ZEQP.Print 打印1.0 第一个项目开始,因为原来没有研究过打印,所以在Bing上查了一下.Net打印机关的资料 发现基本上都是基于.net的 大家都用这个做打印,我想按理也没有问题。 所以开始了我的代码。 PrintDocument去做打印,无非就是设置好打印机名称, 代码一写完,用VS调试的时候。跑得飞起。、 所有的字体,要打印数据的位置也通过配置文件可以动态的调整。感觉还算完美。 幸好客户方面没有什么要求,而且生产的时候会有一台专门的上位机可以做这个事,所以做了一个无界面的WinForm。在电脑启动的时候运行 从而解决了不能以服务的方式运行的问题。 打印2.0 做完打印1.0后,又接到了一个项目。又是有打印相关的功能,自然又分配到我这里来了。 但是对于上一个版本的打印。不能做为服务运行,做为自己写的一个程序,居然有这么大的瑕疵。总感觉心里不爽 想去解决这个问题,但是在Bing上找到.Net的所有打印都是这样做的。也找不到什么更好的方法。 只到问了很多相关的相关人士。最后给了我一个第三方的商业解决方案BarTender 有自己的SDK,有编辑器,功能也非学强大。不愧是商业打印解决方案。 根据他的SDK,同时安装了相关程序,写下几句打印代码。一个基于Win服务的打印出来了 于是。打印2.0出来了。 打印3.0 就是对于一般公司的小项目。挣的钱还不够买这个商业套件的License 而且对于一个只会使用别人家的SDK的程序。不是一个有灵魂的程序。 因为你都不知道人家背后是怎么实现的。原理是什么都不知道。 对于我,虽然能把这个项目用BarTender完成。但是总是对这个打印方案不是很满意。 因为我只在这个上面加了一层壳。不知道后面做了什么。 所以我一直想自己开发一个可以基于Win服务运行的打印程序。最好也要有自己的模板编辑器。 只到有一天。无意找到一篇文章 https://docs.aspose.com/display/wordsnet/Print+a+Document 他这里也解释了有关基于服务的打印有关的问题不能解决。 并且他们已经找到了对应的解决方案。基于他的解决方案。写了对应一个打印帮助类。 这个是基于Windows的XPS文档API打印。 XPS是在Win 7后就是windows支持的打印文档类型 类比PDF 基本 XpsPrint API 的相关说明 同时基本他的XPS打印帮助类。我做了测试。可以完美的在Windows服务里面运行关打印。 到此,基于windows服务的打印已经解决。 就只有模板编辑器的事情了。 对于原来做过基于Word的邮件合并域的经验。自己开发一个编辑器来说工程量有点大 所以选择了一个现有的,功能又强大的文档编辑器。Word来做为我的标签编辑器了。 Word可以完美的解决纸张,格式,位置等问题。只是在对应的地方用“文本域”来做占位符 然后用自定义的数据填充就可以了。 下图为Word模板编辑 编辑占位符(域) 这样的话。一个模板就出来了 如果是图片的话。就在域名前加Image: 如果是表格的话。在表格的开始加上TableStart:表名 协议的话。走的是所有语言都支持的http,对于以后开发SDK也方便 对于上面的模板,只要发送这样的请球POST 对于Get请求 然后打印出来的效果 到此,打印3.0已经完成。 关键代码 文档邮件合并域
System.Drawing.Printing.PrintDocument
这个类来做自定义打印
DefaultPageSettings.PrinterSettings.PrinterName
打印份数
DefaultPageSettings.PrinterSettings.Copies
纸张方向
DefaultPageSettings.Landscape
然后打印的具体的信息就是事件PrintPage写进去
然后调用
Graphics.DrawString,Graphics.DrawImage来写入具体的文本与图片
Graphics.Draw的时候要指定字体,颜色,位置等数据
我把这些做成配置数据。
然后1.0版本就成了。
下图为位置的配置文件
但是现实很骨感,马上就拍拍打脸了
PrintDocument类只能以WinForm的方式运行,不能以服务的方式运行。
具体可以参考:https://docs.microsoft.com/zh-cn/dotnet/api/system.drawing.printing?redirectedfrom=MSDN&view=netframework-4.8
相关参考:https://www.bartendersoftware.com/
这个有自己的模板编辑器,
但是对于一个基于第三方的商业打印方案,所有功能都是很强大。实现也简单。 1 namespace ZEQP.Print.Framework
2 {
3 ///
在表格的未尾加上TableEnd:表名
根据请求数据生成打印实体 1 private PrintModel GetPrintModel(HttpListenerRequest request)
2 {
3 var result = new PrintModel();
4 result.PrintName = ConfigurationManager.AppSettings["PrintName"];
5 result.Template = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Template", "Default.docx");
6 result.Action = PrintActionType.Print;
7
8 var query = request.Url.Query;
9 var dicQuery = this.ToNameValueDictionary(query);
10 if (dicQuery.ContainsKey("PrintName")) result.PrintName = dicQuery["PrintName"];
11 if (dicQuery.ContainsKey("Copies")) result.Copies = int.Parse(dicQuery["Copies"]);
12 if (dicQuery.ContainsKey("Template"))
13 {
14 var tempPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Template", dicQuery["Template"]);
15 if (File.Exists(tempPath))
16 result.Template = tempPath;
17 }
18 if (dicQuery.ContainsKey("Action")) result.Action = (PrintActionType)Enum.Parse(typeof(PrintActionType), dicQuery["Action"]);
19
20 foreach (var item in dicQuery)
21 {
22 if (item.Key.StartsWith("Image:"))
23 {
24 var keyName = item.Key.Replace("Image:", "");
25 if (result.ImageContent.ContainsKey(keyName)) continue;
26 var imageModel = item.Value.ToObject
1 public class MergeDocument : IDisposable
2 {
3 public PrintModel Model { get; set; }
4 public Document Doc { get; set; }
5 private PrintFieldMergingCallback FieldCallback { get; set; }
6 public MergeDocument(PrintModel model)
7 {
8 this.Model = model;
9 this.Doc = new Document(model.Template);
10 this.FieldCallback = new PrintFieldMergingCallback(this.Model);
11 this.Doc.MailMerge.FieldMergingCallback = this.FieldCallback;
12 }
13 public Stream MergeToStream()
14 {
15 if (this.Model.FieldCotent.Count > 0)
16 this.Doc.MailMerge.Execute(this.Model.FieldCotent.Keys.ToArray(), this.Model.FieldCotent.Values.ToArray());
17 if (this.Model.ImageContent.Count > 0)
18 {
19 this.Doc.MailMerge.Execute(this.Model.ImageContent.Keys.ToArray(), this.Model.ImageContent.Values.Select(s => s.Value).ToArray());
20 };
21 if (this.Model.TableContent.Count > 0)
22 {
23 foreach (var item in this.Model.TableContent)
24 {
25 var table = item.Value;
26 table.TableName = item.Key;
27 this.Doc.MailMerge.ExecuteWithRegions(table);
28 }
29 }
30 this.Doc.UpdateFields();
31
32 var fileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PrintDoc", $"{DateTime.Now.ToString("yyMMddHHmmssfff")}.docx");
33 var ms = new MemoryStream();
34 this.Doc.Save(ms, SaveFormat.Xps);
35 return ms;
36 }
37
38 public void Dispose()
39 {
40 this.FieldCallback.Dispose();
41 }
42
43 private class PrintFieldMergingCallback : IFieldMergingCallback, IDisposable
44 {
45 public HttpClient Client { get; set; }
46 public PrintModel Model { get; set; }
47 public PrintFieldMergingCallback(PrintModel model)
48 {
49 this.Model = model;
50 this.Client = new HttpClient();
51 }
52 public void FieldMerging(FieldMergingArgs args)
53 {
54 }
55
56 public void ImageFieldMerging(ImageFieldMergingArgs field)
57 {
58 var fieldName = field.FieldName;
59 if (!this.Model.ImageContent.ContainsKey(fieldName)) return;
60 var imageModel = this.Model.ImageContent[fieldName];
61 switch (imageModel.Type)
62 {
63 case ImageType.Local:
64 {
65 field.Image = Image.FromFile(imageModel.Value);
66 field.ImageWidth = new MergeFieldImageDimension(imageModel.Width);
67 field.ImageHeight = new MergeFieldImageDimension(imageModel.Height);
68 };
69 break;
70 case ImageType.Network:
71 {
72 var imageStream = this.Client.GetStreamAsync(imageModel.Value).Result;
73 var ms = new MemoryStream();
74 imageStream.CopyTo(ms);
75 ms.Position = 0;
76 field.ImageStream = ms;
77 field.ImageWidth = new MergeFieldImageDimension(imageModel.Width);
78 field.ImageHeight = new MergeFieldImageDimension(imageModel.Height);
79 }; break;
80 case ImageType.BarCode:
81 {
82 var barImage = this.GenerateImage(BarcodeFormat.CODE_128, imageModel.Value, imageModel.Width, imageModel.Height);
83 field.Image = barImage;
84 }; break;
85 case ImageType.QRCode:
86 {
87 var qrImage = this.GenerateImage(BarcodeFormat.QR_CODE, imageModel.Value, imageModel.Width, imageModel.Height);
88 field.Image = qrImage;
89 }; break;
90 default: break;
91 }
92 }
93 private Bitmap GenerateImage(BarcodeFormat format, string code, int width, int height)
94 {
95 var writer = new BarcodeWriter();
96 writer.Format = format;
97 EncodingOptions options = new EncodingOptions()
98 {
99 Width = width,
100 Height = height,
101 Margin = 2,
102 PureBarcode = false
上一篇:.Net Core的API网关Ocelot的使用(二)[负载,限流,熔断,Header转换]
下一篇:Docker Toolbox-centos:7.2.1511-docker 镜像生成(环境:win10-家庭版-Docker Toolbox)