
简介面向PowerBuilder 12开发者的票据打印示例资源用于解决PB应用程序中票据、小票、凭证等自定义格式设计与打印输出需求尤其适合处理非标准纸张、多联票据或连续打印的商用场景。压缩包共18个文件、大小78KB包含PowerBuilder工程文件pbl/pbt/pbw、HTML演示页面、CSS样式、GIF/JPG图片素材及TXT使用说明其中pbl/pbt/pbw构成可运行的PB项目主体htm与css展示票据预览和样式控制图片辅助界面示意txt则提供使用说明与环境配置指引整体体积小巧便于下载后快速试运行。已有256人学习下载。通过该资源可了解PB12中数据窗口对象在票据打印中的典型应用包括自定义纸张、动态数据填充、多页连续打印、打印参数控制等并借鉴报表设计工具的分组、排序和计算字段思路。压缩包中的自定义票据打印示例与说明文档适合需要快速落地票据功能、复用打印模板、排查打印配置问题或理解PB12打印模块的开发者参考。 做PowerBuilder的人几乎早晚都会碰到票据打印这件事。不管是商超的小票、物流面单、医院就诊条还是厂里的领料单项目一上线这类“纸比系统重要”的需求就会找上门。PB项目里做票据打印说难不难DataWindow天生就是干这个的但说容易也真不容易纸张高度、走纸偏移、条码扫描率、表格线发虚这些坑我一个个都踩过。这篇就把我在pb12里落地票据打印的完整思路、关键参数和排错经验整理出来给还在跟“打印出来歪了”“第二张开始上下跑”搏斗的朋友一点参考。1. 直接把票据打印当成一个独立模块来设计1.1 为什么票据打印会被单独拎出来很多刚接触PB的朋友会奇怪打印不就是PrintDataWindow()一行代码吗为什么还要当成一个模块来设计我的经验是普通报表打印和票据打印对“准”的要求完全不是一个量级。A4报表多打几毫米没人盯着看但热敏纸票据第二张开始往下偏一毫米扫码枪就可能扫不出来金额小票切出来歪的客户当面就能看出来。票据打印还有一个特性纸张规格千奇百怪。58mm热敏、80mm热敏、241×140mm压感、A5三联、自定义长度的连续纸每种纸张对应的数据窗口设计、打印参数、走纸策略都不一样。同一套程序要兼容几种纸就必须把“打印”这件事从业务代码里独立出来用一个集中的模块处理数据窗口的打印参数和纸张计算。这正是pb12里值得专门做的一层。1.2 PB打印数据的完整链路上手之前先把PB打印的完整数据流理清楚。数据从数据库取出来后进DataWindow或DataStoreDataWindow本身维护一套“打印视图”的属性——包括纸张大小、边距、方向、DPI、预览模式这些属性可以通过Modify()动态修改也可以在数据窗口画板里直接配。真正触发打印时系统把这套视图交给GDI由Windows驱动转换成打印机语言。这个链路决定了三件事第一所有打印控制都要在DataWindow层面做绕开它去碰底层是不现实的第二数据窗口里的显示单位、Preview视图、打印DPI这三个环节如果设置不一致就会出现“屏幕看着正常、打出来不对”的经典问题第三打印驱动和自定义纸张设置会直接影响最终物理效果这部分PB控制不了但可以配合。理解这条链后面所有问题都能顺着找根源。2. 数据窗口设计是票据打印的第一关2.1 纸张参数别用默认值我见过太多票据项目死在一开始新建数据窗口时Paper Size用的默认值通常是A4。然后程序设计者把所有控件堆在左上角打印时靠打印机驱动自动裁切。这么做的结果往往是第二张纸的位置完全不可控因为PB以为自己在打A4实际纸只有十几厘米长。正确做法是在设计阶段就明确打印介质。现在用的热敏票据打印机一般需要在驱动里添加自定义纸张比如80mm×130mm的连续纸。添加好之后数据窗口的DataWindow.Print.Paper.Size设置成对应自定义纸张很多驱动是256或具体的IndexPaper.Length和Paper.Width再按实际尺寸设一遍。两个地方都设置了驱动才不容易中途弹“纸张不匹配”的对话框。2.2 常用打印参数速查放一张我在实际项目里反复用的参数表这些都是DataWindow对象Print属性组里的用Modify()可以动态设置比在画板里一个个点快得多。属性作用常用值建议DataWindow.Print.DPI打印分辨率基准300票据文字和条码都清晰DataWindow.Print.Paper.Size纸张类型256或自定义纸张Index不用默认DataWindow.Print.Paper.Length纸张高度按实际票据高度算单位是千分之一英寸DataWindow.Print.Paper.Width纸张宽度80mm热敏对应约3150DataWindow.Print.Orientation方向2纵向1横向票据基本都是纵向DataWindow.Print.Margin.Left等四周边距通常0或很小热敏纸边距大了浪费纸DataWindow.Print.Preview预览模式True进入预览False回到普通视图DataWindow.Print.Preview.Zoom预览缩放建议50~100太低看不清DataWindow.Print.Copies打印份数注意与驱动份数叠加的问题DataWindow.Print.Color彩色/单色1彩色2单色表格线发虚时可检查这里DataWindow.Print.DocumentName文档名显示在打印机队列里的名字2.3 用Modify动态覆盖默认打印参数画板里配好的参数只能管住一种纸。实际项目里往往是一台机器换纸就要换参数这时候就得在代码里动态设置。PB12里最稳定的是用Modify()直接改数据窗口属性我的习惯是写一个统一函数// 参数dw目标数据窗口as_width纸张宽as_height纸张高单位0.001英寸 // 返回修改是否成功 public function integer f_setup_print(datawindow adv_dw, integer ai_width, integer ai_height) string ls_modify integer li_ret ls_modify DataWindow.Print.Paper.Size256 li_ret adv_dw.Modify(ls_modify) if li_ret 0 then return li_ret ls_modify DataWindow.Print.Paper.Width String(ai_width) DataWindow.Print.Paper.Length String(ai_height) li_ret adv_dw.Modify(ls_modify) if li_ret 0 then return li_ret ls_modify DataWindow.Print.DPI300 DataWindow.Print.Orientation2 DataWindow.Print.Margin.Left0 DataWindow.Print.Margin.Right0 DataWindow.Print.Margin.Top0 DataWindow.Print.Margin.Bottom0 li_ret adv_dw.Modify(ls_modify) if li_ret 0 then return li_ret return 1 end function有一点必须提醒Modify()改完属性后数据窗口的显示不会立刻刷新特别是在预览和普通视图切换时。最好在后面跟一句adv_dw.SetRedraw(True)或者对数据窗口重新触发一次dw_1.Retrieve()。这一步忘了常常会出现参数改了但打印出来没变化的假象。3. 核心难点连续纸走纸不准的背后原理3.1 第二张开始歪的根源连续纸票据最典型的故障就是第一张正常第二张开始位置往下跑越打越歪。我排查下来绝大多数都是因为PB的打印引擎不知道“纸有多高”。它默认拿A4的高度来排版第一张纸如果内容不满一页打印机走纸到A4末尾才切纸打印头已经把第二张纸推出了初始位置自然就对不齐。解决办法就一句话把Paper.Length告诉PB而且要算得准。票据内容会有多少行明细是运行时才知道的——今天卖10件商品明天卖3件高度完全不同。用固定值做出来的票据要么内容多时被截断要么内容少时多走一段空白。这种动态高度需求正好对应很多人在问的“数据窗口自动高度怎么设置”——在票据打印这里它不是把Detail Band拉高就算了而是要反过来根据明细行数反推纸张高度。3.2 按明细行数动态计算纸张高度我的做法分三步。第一步把数据窗口里的Header、Detail、Footer三个Band的高度都取出来。第二步算出内容总高Header.Height Footer.Height 明细行数 × Detail.Height。第三步把总高换算成纸张长度写进Paper.Length。代码大概是这样的long ll_row, ll_header_h, ll_detail_h, ll_footer_h, ll_total_h long ll_paper_len ll_row dw_1.RowCount() ll_header_h Long(dw_1.Object.DataWindow.Header.Height) ll_detail_h Long(dw_1.Object.DataWindow.Detail.Height) ll_footer_h Long(dw_1.Object.DataWindow.Footer.Height) ll_total_h ll_header_h ll_footer_h ll_row * ll_detail_h // 加上下安全边距6000个单位1英寸 ll_paper_len ll_total_h 300 dw_1.Modify(DataWindow.Print.Paper.Length String(ll_paper_len)) dw_1.Modify(DataWindow.Print.PreviewTrue) dw_1.SetRedraw(True)这套逻辑看起来简单真正用的时候有几个细节要注意。第一明细行里如果某一列设置了AutoSize Height行高会在运行时变取到的Detail.Height只是设计值必须自己在代码里对每行重新算一次。第二Footer里如果放了合计之类的动态文本文本高度也可能变需要在itemchanged或数据窗口的rowchanged事件里重新触发高度计算。第三取出来的高度单位是数据窗口单位1英寸等于6000个单位如果之前有人把DPI调过单位换算会跟着变最好统一以6000为基准算。3.3 关于单位和DPI的换算问题这里顺便把单位换算一次说清楚。数据窗口里的坐标默认不是像素也不是毫米而是PowerBuilder自研的unit固定1英寸6000 units。打印时Print.DPI决定了输出分辨率基准300是票据设备的主流选择。如果数据窗口里的控件是按照屏幕96DPI摆放的打印时DPI改成300就会遇到“打印出来放大”的问题——因为两者的映射基准变了。我在项目里的经验是票据数据窗口从建表开始就把所有Band的高度、控件位置按实际毫米数换算成units来设计。一个80mm宽的票据打印区域大概72mm去掉驱动不可打印边距约2.835英寸换算成units就是17000左右。这样设计出来的数据窗口不管前面换什么DPI物理尺寸都不会跑偏。当然很多老项目的数据窗口是从A4报表改过来的那就要在打印前用脚本把整体缩放或重新排一遍工作量会大一些。4. 条码、表格线和明细行的打印细节4.1 条码的两种实现方式票据上最常见的条码是Code128和EAN-13。PB12里实现条码打印主流有两种方式。第一种是数据窗口自带的BarCode表达式在Column的Expression里写类似BarCode(Code128, 0, 0, 0, 0, 字符串列)的表达式PB自动生成可扫描的条码图形。这种方式不依赖外部字体打印出来稳定性最好但样式可调项少条码下方的数字、宽度比例都受表达式参数限制。第二种是装条码字体把数据显示成条码样式。优点是所见即所得在屏幕上就能直接看到效果缺点也很明显字体必须在每台打印机所在的电脑上安装否则打印出来就是一串普通数字或方框。部署时漏装字体现场排查起来很狼狈。我的习惯是优先用BarCode表达式。实际测试下来Code128在300DPI下效果很好扫描枪识别率很高。如果实在要用字体方案务必把字体文件跟程序一起分发并在安装脚本里加字体注册步骤。4.2 表格线打印发虚小票打印经常要打表格线但很多人发现屏幕上清清楚楚的细线打印出来要么变成虚线要么整条线都看不见。原因在PB里很典型数据窗口里Line控件默认线宽是整数像素屏幕显示时1像素就够了但在300DPI打印时1像素对应的物理尺寸太小接近驱动的最小可打印线宽于是驱动为了省事直接丢弃或者用抖动方式画出断断续续的线。解决方案有三个层次。第一层把表格线的Pen.Width从1改成2或3这样打印时线条有足够的物理宽度代价是屏幕上看会略粗。第二层把表格区域用Rectangle矩形控件代替Line矩形边框的打印稳定性通常比Line好。第三层如果对线条粗细特别敏感干脆用Picture控件放一张1像素宽的纯色图片横向拉长后作为表格线打印效果最接近设计稿。我做88mm热敏票据时一般是表格线用2像素外框用3像素配合DPI300清晰度基本满足客户要求。4.3 明细行超长分页处理商品名称太长、明细行数太多票据会碰到一页放不下的情况。连续纸小票一般是不希望分页的整张打出来就是一个长条。要处理好这个我认为有两个关键点。第一个关键点在数据准备阶段就做掉明细进入数据窗口之前先做文本截断。商品名超过固定字节数就截成两行实在超长就缩略不让Detail Band里出现动态超高。第二个关键点是确认驱动里选的纸张类型是“连续纸”或“卷纸”而不是“A4”之类的单页纸。连续纸模式下内容超出一页时打印头会继续往下走单页纸模式下打印机会强制进新纸两张小票之间就会夹一张空白页。如果客户确实要求多页小票每页都要有抬头和合计那就要在数据窗口里分组用Group的newpage属性强制每组分页并在每页的Footer放本页汇总。这个场景不如单页连续纸常见但一旦遇到直接改数据窗口分组会比在代码里拼多个DataWindow靠谱得多。5. 预览与实际打印对不上的排查思路5.1 差异从哪里来用户最常提的投诉是“预览好好的打出来怎么差这么多”这个问题的根源在于预览和打印走了两套坐标换算。预览是按屏幕分辨率显示的缩放比例再准也只是模拟打印是被驱动接管后按照驱动和纸张实际参数输出的。两个环节里任何一个参数不一致结果就必然有偏差。最常见的三个变量第一Paper.Width和Paper.Length有没有设第二驱动中选中的纸张尺寸是不是和数据窗口一致第三驱动有没有设置“缩放以适合页面”之类的选项。这三个地方只要有一处冲突预览和打印就会不一致。5.2 一个可靠的预览实现方案我自己项目中比较稳妥的做法是预览时用数据窗口自带的Print Preview模式而不是人为缩放控件。进入预览前先执行一次前面写的f_setup_print()把Paper参数都设好再设置DataWindow.Print.PreviewTrue。这样预览虽然不完全等于最终打印效果但纸张尺寸、边距、分页位置都是同一套参数在计算误差基本可以控制在1mm以内。另外有两点经验。第一预览模式里的Zoom不要拉得太小或太大太小时PB可能不做完整排版太大时又容易因为字体平滑导致线条位置错觉我一般设成80~100。第二预览状态下发现内容偏移优先检查数据窗口的Margin设置而不是在Print Preview里手拖控件位置。因为手拖的位置会写进数据窗口布局下次正式打印还是会乱。6. 票据打印常见问题速查表把这些年遇到的现场问题整理成一张表按“现象—原因—解法”的格式列出来排查时可以对着看。现象原因处理方式第一张正常第二张往下偏未设Paper.Length或长度不够按明细行数动态计算并设置纸张高度内容打不全底部被截断Paper.Length小于实际内容高度在总高基础上加安全边距重新计算预览正常打印整体偏大/偏小DPI设置与环境不一致统一设置DPI300按6000units/英寸换算尺寸表格线打印发虚或消失线宽过小驱动丢弃线条线宽改2~3像素或用Rectangle控件替代条码扫不出来条码类型/密度与扫描枪不匹配用Code128DPI保持300测试不同宽度比打印份数翻倍数据窗口Copies与打印机驱动份数叠加只在一处设置份数建议以驱动为准每张票据中间夹空白页纸张类型选了单页纸驱动里添加连续纸/卷纸并匹配Paper.Size打印时弹“纸张不匹配”自定义纸张未在驱动中添加在打印机属性里添加同尺寸的自定义纸张修改参数后打印无变化漏了刷新数据窗口Modify()后执行SetRedraw(True)或重新RetrievePreview模式一片空白预览进入后未触发重绘设置Preview后调用dw_1.SetRedraw(True)7. 一点个人经验做PB票据打印这几年我最大的体会是不要急着写代码先搞清楚目标打印机和纸张规格再去设计数据窗口。现场很多“打印问题”其实是环境问题——驱动没配好、自定义纸张没加、程序里写的参数和驱动里选的不一致。程序逻辑再严谨也扛不住环境参数全乱套。另外给新项目做票据打印时我会提前把打印参数集中封装成一个函数库所有数据窗口统一走一套设置。这样后续换打印机、换纸张规格只改函数里几个常量不用每个窗口逐一排查。如果你手头正好在改一个老模块建议从“先把纸的高度算准”这一步开始这一步对了至少一半的票据打印问题都能当场消失。本文还有配套的精品资源点击获取