
简介面向PB12开发者的票据打印解决方案以数据窗口对象为核心解决PB应用中票据设计、动态数据填充与精确打印输出问题适合需要自定义票据模板、报表格式及多页连续打印的企业级开发场景。压缩包共18个文件、约78KB以PBL/PBT/PBW工程文件为主体搭配HTML使用说明、GIF/JPG操作截图和CSS样式可直观对比票据模板布局与打印流程。已有256人浏览学习说明该主题在PB12开发者中具有实际参考价值。包内提供完整可运行的自定义票据打印示例覆盖数据窗口中文本、图像、条形码等控件的设计方法报表预览、分组排序、计算字段等报表处理功能以及纸张大小、页边距、打印方向等参数控制细节同时涉及动态数据获取、脚本逻辑判断、多页连续打印和错误调试思路并附有下载使用说明与资源使用特别说明文档便于初学者从零搭建环境并按步骤上手也方便二次开发者直接参考改造。 前阵子群里有个老哥发消息“PB12打印出库单纸是订做的A5尺寸边距调了一圈打印出来位置就是不对谁能救一下”后面跟了张截图预览里看着挺正常打到纸上就是整体右偏了两毫米。这种问题我见过太多不怪他——PowerBuilder里的票据打印从来就不是“排个版点打印”这么简单它牵扯到数据窗口的Band布局、打印机驱动的自定义纸张、千分之一英寸的坐标换算还有自动高度这种让人又爱又恨的设置。标题里说的“pb票据打印FOR pb12”说白了就是在PowerBuilder 12环境下用数据窗口DataWindow来做票据、单据、小票这类打印需求。它能解决三类典型问题预印票据套打、连续纸批量打印、动态行高单据打印。适合正在维护老系统、或者刚接手PB项目的新人参考。这篇文章我按自己实际调打印的经验来写配置步骤和坑都会说清楚。1. 票据打印的需求到底卡在哪先看清这三件事1.1 票据打印不是普通报表打印普通报表打印比如周报月报数据堆上去就行用A4纸随便排不对齐也不影响阅读。但票据打印完全是另一个逻辑快递面单、发票、出库单、POS小票、缴费回执这些纸在进打印机之前就已经印好了框线、字段名、Logo程序只负责把数据填进对应的格子里不能越界不能压线更不能把预先印好的内容盖住。我做过一个仓储客户的出库单纸是定制的上面有红蓝条纹和手写签字区域程序里唯一要做的事情就是把“品名、规格、数量”打到指定的空白表格里。一旦对位不准轻则看着别扭重则整个单据作废。所以票据打印的难点从来不在“能不能打印出数据”而在“怎么让数据和纸上的格子严丝合缝”。1.2 为什么PB12里绕不开数据窗口PowerBuilder最核心的东西就是DataWindow它一个控件同时干了三件事绑定数据、展示界面、输出打印。不像其它报表工具要单独维护一个模板文件PB里同一个数据窗口对象既能用来录入查询也能直接切到打印预览。好处很明显——录入界面和打印格式共用一套数据源省了“再做一套报表”的工作量。但坏处也在这里很多人把录入界面的样式直接拿去打印。屏幕上看字体挺清楚密密码码的格线在显示器上也能接受可一到纸上字体发虚、线条丢失、间距错位这些问题全部暴露。加上PB12PowerBuilder 12是不少公司跑了几年的存量环境Classic版本保留了传统PowerScript写法新项目虽然选它的越来越少但金融、物流、政务这些系统的运维开发还是绕不开。所以这套打印逻辑值得好好捋一遍。2. 数据窗口的票据版式设计把打印纸当画布2.1 Band结构怎么摆数据窗口默认有Header、Detail、Footer三个区还可以加Summary、Group区。票据打印最常见的问题是有人把所有内容全堆在Detail里。你看他运行起来也能显示但文件一行一行重复表格线、备注、签字栏全跟着重复打印出来一塌糊涂。正确的安排应该是Header区放票据名称、单号、日期、客户名称、收货地址这些固定信息。Detail区只放明细行行数不定自动循环。Footer区放合计金额、大写金额、经手人签字栏。Summary区放票据底部的固定说明文字比如“本单联次说明”这种只在最后一页出现的内容。特别留意票据的合计金额位置一般是固定的不会因为明细行多就往下跑所以这里用Footer而不是Summary更合适。Summary会跟着最后一页的数据走一旦明细超过一页Summary内容会排到第二页末尾这跟纸质票据的固定版式就对不上了。2.2 套打版式与透明化处理预印票据不需要程序把框线也打一遍否则会跟纸上印刷的线条重叠成“重影”。设计数据窗口时要把控件边框设为No Border线条尽量不画如果必须用底色区分区域用浅灰或者干脆透明。我一个经验是把预印好的票据扫描成图片放到数据窗口背景里做对位参照。设计阶段用这个底图拉开每个字段的位置等调好之后正式打印前用一行代码把背景清掉防止打出一层底图dw_1.Modify(DataWindow.BackgroundPicture.ImageName ) dw_1.Modify(DataWindow.BackgroundPicture.Filename )这样做的好处是你不需要拿尺子去量纸质票据上的坐标直接在屏幕上对着扫描图拖动控件就行效率高不少。但记得最终给客户交付时一定把这条清底图的逻辑加到打印按钮里否则演示的时候出了底图就很尴尬。2.3 单位、网格与坐标换算PB数据窗口的坐标单位是千分之一英寸不是毫米也不是像素。很多人直接在属性面板里填“5”以为移动了5毫米实际差了十万八千里。换算公式很简单千分之一英寸值 毫米数 ÷ 25.4 × 1000比如10mm就是 10 / 25.4 * 1000 ≈ 394 个单位。这个单位体系和Windows打印体系是一致的所以数据窗口里的控件位置能精确映射到纸上。常用数值我列一下省得每次现算毫米千分之一英寸1mm395mm19710mm39420mm78750mm1969100mm3937设计数据窗口时把Design Grid的网格间距调到最小1/1000英寸确保控件能精确停在你想要的位置。网格设太粗的话控件自动吸附到网格线几毫米的误差在屏幕上根本看不出来但打到票据上就是一次明显的偏移。3. 票据打印的四项核心配置从纸张到自动高度3.1 自定义纸张的设置与代码票据纸很少是A4或A5标准尺寸。快递单一般是100×180POS小票是76×200我遇到过最离谱的是客户做服装挂签纸宽只有60mm。系统默认纸张列表里根本没有这些尺寸所以第一步不是写代码而是去操作系统里把纸张建出来。操作路径控制面板 → 打印机和扫描仪 → 打印服务器属性 → 创建新纸张格式把“纸张名称”和“尺寸”填好。有些机型需要在打印机驱动自带的“Printing Preferences”里自定义。这一步绕不开因为很多打印机驱动只认系统纸张列表里的尺寸你不建好程序里再怎么设置都会被驱动忽略。接下来在数据窗口里设置纸张。一种方式是在DataWindow的Print Specifications面板里把Paper Size选为Custom另一个更可控的方式是直接在代码里用Modify动态指定// 256表示自定义纸张Custom Paper Size dw_1.Modify(DataWindow.Print.Paper.Size 256) // 以76mm × 200mm小票纸为例 // 宽度76 / 25.4 * 1000 ≈ 2992 // 高度200 / 25.4 * 1000 ≈ 7874 dw_1.Modify(DataWindow.Print.Paper.Width 2992) dw_1.Modify(DataWindow.Print.Paper.Length 7874)Modify返回空字符串表示成功如果返回了错误描述多半是属性名拼错或者当前驱动不支持。这里有个容易踩的坑Paper.Length和Paper.Width这两个属性名不是Height和Width我见过不少同事在这里卡半天。3.2 打印边距与起始坐标调整预览看着正常、打印出来整体偏移是票据打印里最常见的现象。原因多数是打印机驱动有默认的“不可打印区域”比如上下左右各留5mm或者系统里默认纸张被改成了Letter美国信纸尺寸比A4窄导致数据窗口居中或缩放的方式变了。排查的时候先做一件事在数据窗口的Print Specifications里把所有Margin都设为0重新打印一张测试页。如果偏得更多说明是驱动默认加边距如果变正了说明是数据窗口本身的边距问题。设置边距的写法dw_1.Object.DataWindow.Print.Margin.Left 300 dw_1.Object.DataWindow.Print.Margin.Right 300 dw_1.Object.DataWindow.Print.Margin.Top 300 dw_1.Object.DataWindow.Print.Margin.Bottom 300我实际调的时候更喜欢把边距先归零然后用整体坐标偏移来做定位。因为数据窗口里所有控件的坐标都是相对左上角的你只要整体把控件往右或者往下挪一定单位打印位置就会跟着平移。这个调整步进比单独调Margin更可控也很好回退。3.3 自动高度设置让备注行随内容变高“PB数据窗口自动高度怎么设置”这个问题基本每隔一段时间就有人问一次尤其是在做单据打印的时候。最常见的场景是一张退货单备注区域可能写一行也可能写五句话打印时希望这一行的高度跟随内容自动扩展后面明细行整体下移而不是把多出来的文字截断在固定的框里。设置分两步缺一不可第一步选中备注列右键打开Properties在General选项卡里勾选Auto Height。这会让该列的高度根据文本内容自动撑高。第二步选中Detail Band点击数据窗口左边沿那个灰色区域在Band Properties里把Height Auto Size设为Yes。只有列自动高了Band不跟着自动扩展打印的时候行数一多就会被截断。代码方式等价于dw_1.Modify(remark.AutoHeight Yes) dw_1.Modify(DataWindow.Detail.Height.AutoSize Yes)这里有一个很容易翻车的细节开了Auto Height的列水平方向必须留出足够空间左右不能和同行的其他列挤在一起。因为PB计算换行高度时会先看这个列在水平方向能放多少字如果旁边紧贴着别的控件它会认为没有空间容纳多行文字自动高度就不生效。所以设计时要么把备注列单独占一整行要么给它身边留出几十个单位的空隙。另一个坑是勾完Auto Height之后在开发界面里手动拖动Detail Band高度是不生效的因为它已经完全交给运行时的文本内容去计算。遇到这种情况别怀疑自己操作不对这是正常现象。3.4 字体、边框与打印分辨率的耦合关系预览和打印不一致除了纸张和边距字体也是大头。我的建议是优先用宋体、微软雅黑、黑体这类系统自带中文字体不要在票据里用生僻字体。有些字体在屏幕上渲染正常但打印机驱动不认识会fallback到默认字体导致字体变大变小、位置偏移。边框线也要注意不要设置太细。很多针式打印机对0.1mm以下的细线根本打不出来或者打出来断断续续。票据打印里细线用实线加粗或者干脆把线宽调到1pt以上。另外字号尽量别低于9pt针式打印机打小字号容易糊成一团客户核对金额的时候会很痛苦。4. 打印调用流程与PB模型的封装实践4.1 从预览到输出的标准调用链打印之前先让用户确认版式这是所有票据打印的基本流程。数据窗口自带打印预览一行代码就能切换dw_1.Object.DataWindow.Print.Preview True // 进入预览 dw_1.Object.DataWindow.Print.Preview False // 退出预览真正打印输出有两种方式。第一种最简单直接用数据窗口的Print方法dw_1.Print()它会自动弹出一个标准的打印对话框让用户选择打印机、份数、范围。适合单数据窗口打印的场景。第二种方式适合要精细控制的场景用底层的Print系列函数拼一个打印任务long ll_job ll_job PrintOpen(出库单打印) if ll_job 0 then PrintDataWindow(ll_job, dw_1) PrintClose(ll_job) end ifPrintOpen返回一个打印任务句柄PrintDataWindow会把这个数据窗口的内容画到一个打印任务里PrintClose结束任务。这种写法最大的好处是可以连续打印多个数据窗口到同一张纸上比如先打印单头信息dw_head再打印明细表dw_detail最后打印签字栏dw_footer。三个数据窗口拼成一个任务正好契合一式多联的票据场景。4.2 用NVO封装一套打印模型有人问过“PB里怎么调用模型”在PowerBuilder语境下“模型”通常指我们自定义封装的非可视对象NVO。票据打印涉及一堆参数纸张尺寸、边距、标题、份数、是否弹对话框。如果每个窗口里都重复写这些Modify后期尺寸一改就是几十处的改动维护成本非常高。我的做法是抽一个叫n_invoice_print的NVO把所有打印参数和Modify逻辑放进去// 类n_invoice_print // 公有函数of_print_invoice(datawindow adw_data, string as_title) public function boolean of_print_invoice(datawindow adw_data, string as_title) adw_data.Object.DataWindow.Print.Paper.Size 256 adw_data.Object.DataWindow.Print.Paper.Width 2992 adw_data.Object.DataWindow.Print.Paper.Length 7874 adw_data.Object.DataWindow.Print.Margin.Left 300 adw_data.Object.DataWindow.Print.Margin.Right 300 adw_data.Object.DataWindow.Print.Margin.Top 300 adw_data.Object.DataWindow.Print.Margin.Bottom 300 long ll_job ll_job PrintOpen(as_title) if ll_job 0 then PrintDataWindow(ll_job, adw_data) PrintClose(ll_job) return true end if return false end function这样窗口里调用只需要几行n_invoice_print inv_printer inv_printer create n_invoice_print inv_printer.of_print_invoice(dw_1, 销售出库单) destroy inv_printer封装还有一个隐形好处可以统一处理异常和打日志。比如打印失败时是弹提示还是写日志不用在每个窗口里重复写一遍判断逻辑。等到项目后面要接多台不同型号的打印机只需要在NVO里做打印机名称的适配窗口代码一行不用改。4.3 动态修改打印参数的实战套路并不是所有票据都用同一套纸张比如同一张出库单平时打到70mm宽的联纸客户临时要求用A5纸出一个精简版。所以不要把纸张参数写死在NVO里最好让调用方传一个只读的参数对象或者直接在调用前用Modify覆盖// 用户选择A5纸时动态覆写 dw_1.Object.DataWindow.Print.Paper.Size 5 // 5代表A5多份打印也可以用循环来处理for li_count 1 to ii_copies dw_1.Object.DataWindow.Print.Preview false PrintDataWindow(ll_job, dw_1) next注意循环里不要重复PrintOpen和PrintClose否则每一份都会变成一个独立的打印任务打印机排队会出问题一个任务里循环PrintDataWindow才是正确的多份打印姿势。打完一份稍作停顿再打下一份的话中间可以加个Yield()避免界面无响应。5. 常见问题与排查技巧实录5.1 打印对齐偏移的排查票据打印对不准我一般的排查顺序是第一步确认打印机默认纸张没被改掉。很多办公室的电脑上一次打印A4文档时驱动会自动把默认纸张切到Letter造成数据窗口按A4宽度排版但回程时用的是自定义小纸整体往左或往右偏移。第二步把数据窗口的边距全部归零用黑色方块做定位测试。在左上角加一个10mm×10mm的黑色块打印出来用直尺量如果实际边长不是10mm说明数据窗口被缩放了。重点查Print Scale属性或者数据窗口里有没有误勾“Fit to Page”。第三步排除打印机驱动本身的偏移。部分老型号针式打印机在驱动里有一个“打印偏移量”的配置项这个值被改过的话无论怎么调数据窗口都没用。5.2 自动高度不生效的核对清单遇到自动高度不生效按这个清单逐项核对列的AutoHeight是否设置为Yes。代码里是dw_1.Modify(列名.AutoHeight Yes)。Detail Band的Height AutoSize是否为Yes。开启自动高度的列左右有没有被其他列贴死。这是最常见的坑留出的水平空间不足PB认为放不下换行文本。是否在运行时代码里手动给该列赋过Height值覆盖了自动高度计算的结果。5.3 连续纸跳页和空白页联纸打印经常出现“明明是5行数据却打出来3张纸中间还有空白页”。这个问题通常是两个原因。一是Footer区域残留了看不见的控件或空行分页时PB会计入那条空行的高度每页多出一个白边。解决办法是把Footer里所有内容删掉或者调整Footer的Band高度到最小。二是纸张高度设置和实际纸高不一致。比如实际纸高是139mm你在代码里设置成140mm打印机驱动按140mm分页只要数据稍微多一点就会因为“差一毫米”多出一页空白。设置自定义纸张高度时宁可稍微比实际纸高低0.5mm也不要多。5.4 常见问题速查表现象可能原因处理方式打印整体右偏/左偏打印机默认纸张被改或者驱动有偏移量检查默认纸张、清零边距、校准驱动偏移预览正常打印错位数据窗口坐标单位理解错误使用千分之一英寸换算网格调到1/1000备注文字被截断AutoHeight未开或列左右间距不够开启列AutoHeight和Band AutoSize留出水平空间每页都多出空白Footer区残留控件或纸张高度不精确删空Footer纸张高度略小于实际值打印出来的线很淡线宽太细或字体驱动不支持线宽加到1pt以上用系统中文字体多联纸打印乱序每份用了独立PrintOpen一个Job中循环PrintDataWindow6. 一些调打印实验中的私人体会调试票据打印最实用的是“打黑点定位法”。我在数据窗口四个角各放一个10mm×10mm的黑色方块然后用普通A4纸打一张直尺量出来的偏差一目了然。这样定位一次比盯着屏幕猜坐标有用得多而且不浪费定制的票据纸。等所有位置都调对了再把这些测试方块删掉或者设为白色。还有一点老系统的打印问题十有七八不是代码变了而是打印机驱动被换过。Windows更新、重装驱动、甚至换了一台同型号但固件版本不同的打印机都会让原来调好的坐标偏移。遇到客户报“以前好好的现在打偏了”我先让他看驱动而不是急着改代码。这也是做维护项目的一个基本准则先排查运行环境再动代码。调PB打印这件事说白了就是吃透数据窗口的坐标体系和打印参数之间的关系把纸上的格子当成一个坐标系去对位。一旦你习惯了用千分之一英寸去思考布局票据打印的很多问题都会变得有条理。希望这篇能帮少走几个弯路。本文还有配套的精品资源点击获取