2.3 套打功能常见的打印就是将打印内容,打印在一张白纸上,而套打则不同,它是把一些内容打印到已经印好的单据上,比如,快递单,增值税发票等。
套打有个最常见,也是最难解决的问题:对齐。
JCP的解决之道是提供开发时对齐功能、运行时对齐功能: 开发时对齐 提供打印模板设计器,开发人员可以利用这个工具,导入套打底图,在可视化的环境中,拖放对齐,最后得到 HTML模板代码。 运行时对齐 最终用户可以使用连续打印偏移调整和打印位置的运行时调整,解决对齐的问题。 提示: 我们还提供一种运行时的模板设计器,让最终用户可以创建,修改打印模板,不过,这是本公司的另外一个产品,叫点点打印,相关说明可参考: http://bbs.jatools.com/viewthread.php?tid=2660&extra=page%3D2
2.3.1 打印模板设计器开发人员可以利用这个工具,导入套打底图,在可视化的环境中,拖放对齐,最后得到 HTML模板代码。工具界面如下:
设计工具地址:http://d.jatools.com 相关文档:http://bbs.jatools.com/viewthread.php?tid=2655&extra=page%3D2
得到模板代码后,你可以用你所用的语言,填充其中动态的内容。 提示: 你可以自己写HTML模板代码,而不必借助这个工具。 2.3.2 打印位置的运行时调整打印位置的运行时调整 有时,即使是同一套程序,同一个页面,在不同的打印机上,打印出来的相对位置也有差别,有的打印机打印出来的内容凑得紧些,有的则离得开些,这种偏差不能通过设置打印机边距的方式进行调整。
借助JCP的打印位置的运行时调整,使用户可以自行调整打印内容的位置,并保存到注册表,下次打印时,JCP会自动使用新的位置打印。 这种微调后的位置信息只保存在本地注册表中,因此不会影响到其他用户的打印,这对有大量前台打印,并有不同类型打印机打印同一种票据的场合,非常实用。
你可以将 myDoc.ui属性设置成 draggable进入带拖放调整的打印预览界面,如下: 1. var myDoc = { 2. // 进入位置调整功能的打印预览式,即带"拖放调整..."按钮 3. ui : "draggable", 4. settings : { 5. paperWidth : 210, 6. paperHeight : 140, 7. orientation : 1 8. }, 9. documents : document, 10. noMargins : true, 11. // 必须设置一个id,否则JCP不知道调整后的位置,保存到注册表的哪一项中 12. settingsId : 'mydoc2', 13. copyrights : "杰创软件拥有版权 www.jatools.com" 14. } 15. getJCP().printPreview(myDoc); 执行这述代码,会出现如下打印预览界面,注意,比普通打印预览多出了一个拖放调整…按钮:
点击拖放调整按钮,可以进入调整界面,如下:
如果预览界面中有多页,在拖放界面中,也只取第一页的数据来做调整,可调整内容包括字型,字体大小,位置,但不能调整显示框大小。先选中一个可调整项,再拖动位置,或者用工具栏中的字体、字号变大变小按钮来设置。
点击确定即将当前修改,保存到注册表并退出,点取消,则不保存退出。
提示: 打印页面中有很多对象,有些可拖放,有些不可拖放,JCP如何知道?
同时满足如下条件的对象,JCP认为是可拖放对象: 1.page1 div中的直接子对象; 2.该子对象有id属性 3.该子对象绝对定位,即 position:absolute; 1. <style> 2. /*确保子对像绝对定位*/ 3. .child-abs >div { 4. position: absolute; 5. } 6. /*子对像初始位置*/ 7. #d1{left:195px;top:323px;} 8. #d2{left:136px;top:150px;} 9. #d3{left:264px;top:224px;} 10. </style> 11. <div id="page1" class="child-abs" style='position:relative;width:210mm;height:140mm;'> 12. <!--<img>没有id,因此不可调整--> 13. <img class='screen-only' src="pj.jpg" style='width:100%;height:100%;'/> 14. <div id='d1'>423051702880563</div> 15. <div id='d2'>13850775557</div> 16. <div id='d3'>¥30.00</div> 17. </div> 本例中,page div的直接子对象,有一个<img>和三个<div>,因为<img>没有id,因此不能调整,三个<div>有id,并因为代码中的3,4,5,11,四行代码确保其为绝对定位,所以是可拖整项。
注意,不应将调整项的初始位置,应设置到<style>中,而不应设置到style中,否则拖放后的位置不会生效,如: 1. <div id="page1" ...> 2. <!--错误,因left,right设置到了 style属性中,调整后的位置不会生效--> 3. <div id='d1' style='left:10px;top:200px;'/>423051702880563</div> 4. ... 5. </div> 2.3.3 连续打印的套打偏移校正连续打印的套打偏移校正连续打印的套打偏移校正2 套打程序经常出现上下偏移的问题,如图所示:
导致偏移的原因有两种: 1. 是打印纸张高度设置得不对,只要根据误差,调整纸张高度就行了。 2. 打印机走纸不够精准,少打几页还看不出有什么问题,但连续打印后,因为误差累积起来,打印到10几张时,就很明显了,如果打印到100多张,上千张,必然谬之毫厘,差之千里了。
一般地,在打印多页后,如果出现向下偏移的情况,说明纸张高度设置过大了,调小一点即可,如果出现向上偏移的情况,说明纸张高度过小,调大一点即可。但这里有个坏消息,比如,进过测试,发现如果纸张高度设置为 200.1mm向下偏移,如果设成200.0mm向上偏移,很简单,设成它们俩的中间值:200.05mm试试。遗憾的是,windows不接受这个值,因为windows能接受的纸张高度,其精度只能达到0.1mm,即使你设置了这个中间值,打印机可能仍然以两值中的一个进行打印,也就是说,你设了这个中间值,打印仍然会存在偏移!
好消息是,JCP采用插入校正页的方法,可完美解决这个问题,图示如下: JCP启动一个打印任务,当打印完几张正常高度的票据后,打印一张用来纠偏的、特别高度的票据(以下称之为校正页),这样可以降低对打印机走纸精度的要求,也达到了纠偏的目的。
用以下步骤设置一个可插入校正页的打印任务: 1. 先测量出,票据的实际高度,比如120mm(宽)*100mm(高); 2. 按所测票据大小,设置自定义纸张: 1. var myDoc = { 2. settings : { 3. paperWidth : 120, // 以毫米为单位 4. paperHeight : 100 5. }, 6. settingsId : 'mydoc1'// 必须设置一个唯一文档id号 7. }
当发现有连续偏移时,只要调用 setupNormalOffset 方法,设置相应的校正参数即可: 1. getJCP().setupNormalOffset('mydoc1');// 唯一参数,标识对哪一个打印文档进行校正设定 此方法将使控件弹出如下对话框:
你打印了多少张:表示你用来测试的页数,测试页数越多,误差越大,校正效果越好。 共偏移了多少毫米(向上偏移为负):表示你经过上一参数连续打印后,第一页与最后一页的偏移距离,以毫米为单位,当向下偏移时,设置为正,反之为负,也可以设置一个小数位。 每隔几张插入一个校正页:表示校正页的插入频度。此参数建议在5~15之间为好。 清除:可能换了好的打印机,不需要校正了,那就采用此按钮,清除校正设置。 确定:设置完了,保存。 取消:即不保存,也不清除,退出。
如果你设置了校正参数,按确定以后,控件将在本地注册表中保存这些参数,当下一次myDoc.settingsId为 mydoc1 的打印启动时,JCP将按这些参数自动插入校正页。
JCP打印与校正设置的示例代码: 1. function doPrint() { 2. var myDoc = { 3. settings : { 4. paperWidth : 1200, 5. paperHeight : 1000 6. }, 7. settingsId : 'mydoc1', 8. documents : document, 9. copyrights : '杰创软件拥有版权 www.jatools.com' 10. } 11. getJCP().print(myDoc, false); // 直接打印 12. } 13. function doNormalOffset() { 14. getJCP().setupNormalOffset('mydoc1'); // 设置校正参数 15. }} 16. ... 17. <intput type='button' value='打印' onclick='doPrint()'> 18. <intput type='button' value='偏移校正设置' onclick=' doNormalOffset ()'> 19. ...
提示: 1.上述代码还不够完美,因为,代码中出现两个按钮,一个是打印,一个是偏移校正,这个偏移校正按钮,对于大多数不存在偏移问题的用户来说,可能永远用不上,即使有少数用户存在偏移问题,也只需要设置一次,就可以了。这个按钮有点多余,下面是改进后的代码: 1. function doPrint() { 2. if (window.event.shiftKey) { 3. getJCP().setupNormalOffset('mydoc1'); // 设置校正参数 4. } else { 5. var myDoc = { 6. settings : { 7. paperWidth : 1200, 8. paperHeight : 1000 9. }, 10. settingsId : 'mydoc1', 11. documents : document, 12. copyrights : '杰创软件拥有版权 www.jatools.com' 13. }; 14. getJCP().print(myDoc, false); 15. } 16. } 17. ... 18. <intput type='button' value='打印' onclick='doPrint()'> 19. ... 删除了用于校正的按钮,但doPrint代码里,加入了是否按下shift键的判断,如果用户在按下打印按钮的同时,也按了shift,则调用控件的校正设定方法,否则,就以正常打印处理。 这样,作为程序开发人员,只需要当用户发现有连续偏移时,才告诉他使用这个方法,来校正偏差。
2.因为校正参数是按myDoc.settingsId来保存的,所以不会干扰到其他打印。 3.因为校正参数是保存在用户的本地注册表中,所以某一个用户的校正设置,不会影响到其他用户的。
2.3.4 零边距打印忽略页边距打印 每个打印机都会有一个最小边距(最小边距因打印机不同而不同),如果你在纸张设置对话框中,设置的边距小于这个最小边距,那么,你的设置将不会起作用。这使你在套打时会产生这样的问题:你已经设置了最小的左(上)边距,但打印出来的东西,仍然是偏右(偏下)。这时,你可以设置 myDoc.noMargins属性为true, 在此模式下,JCP将强制上、下、左、右边距为零,而打印预览时,页面设置对话框中的边距设置变为不可用,设置零边距的代码如下:
1. var myDoc = { 2. noMargins : true,//设置为零边距打印 3. documents : document, 4. copyrights : '杰创软件拥有版权 www.jatools.com' 5. }; 6. getJCP().print(myDoc);
在零边距打印模式下,页面中的打印内容定位,可使用 page div的padding-left,padding-top来整体移动被打印内容,实现相当于设置左、上边距的效果。
2.3.5 仅在预览时可见,不输出到打印机的套打底图底图仅在预览时可见,不输出到打印机 套打时,一般会先扫描事先印好的票据底图,便于对齐及预览,但打印时(调用print方法,或者调用printPreview方法后,按其中的打印按钮时),不希望把底图打印到打印机,这时,你可以设置myDoc.enableScreenOnlyClass 为true,在底图的那个<img>对象上,设置其screen-only样式类即可:
1. <script> 2. ... 3. //打印文档对象 4. var myDoc = { 5. // 使所有使用 screen-only 样式类的对象,只在预览、显示时可见,打印时隐藏 6. enableScreenOnlyClass : true, 7. documents : document, 8. copyrights : '杰创软件拥有版权 www.jatools.com' 9. }; 10. getJCP().print(myDoc); 11. ... 12. </script> 13. ... 14. <div id="page1"> 15. <img src="dt.jpg" class='screen-only'> 16. <div>培训费</div> 17. ... 18. </div> 只要设置了myDoc.enableScreenOnlyClass 为true, 则实际打印时,page div 中所有样式类为screen-only的元素,自动被设置为不可见 display:none。
2.3.6 表格线预览时可见,打印时不可见(控制显示、打印输出样式)表格线预览时可见,打印时不可见(控制显示、打印输出样式) 当你在myDoc.enableScreenOnlyClass为true时,实际打印时(调用print方法,或者调用printPreview方法后,按其中的打印按钮时),JCP会在打印页的div外,套一个有jatools-printing 样式类div容器,如: 1. <div id=page1>...</div> 2. <div id=page2>...</div> 被套jatools-printing 样式类容器时的情形: 1. <div class='jatools-printing'> 2. <div id=page1>...</div> 3. <div id=page2>...</div> 4. </div> 利用这个特点,就可以灵活地定制出,显示与打印的不同样式,如表格线在预览时可见,打印时不可见: 1. <script> 2. ... 3. var myDoc = { 4. enablePrintingClass : true,// 使JCP打印时,套上 jatools-printing类 5. documents : document, 6. copyrights : '杰创软件拥有版权 www.jatools.com' 7. }; 8. getJCP().print(myDoc); 9. ... 10. </script> 11. <style> 12. #sample td { 13. border: solid 1px black; 14. } 15. /*打印时,单元格边框设置为白色,即不可见*/ 16. .jatools-printing #sample td { 17. border: 0px solid white; 18. } 19. </style> 20. ... 21. <div id='page1'> 22. <table id='sample'>...</table> 23. </div>
当预览时,因为不存在 jatools-printing 类,所以根据<style>里的设置,单元格边框显示为solid 1px black 当打印时,因为page1外,存在有 jatools-printing 类的容器,因此,同样根据<style>里的设置,单元格边框显示为solid 0px white,即不可见。
实际上,上一节的打印时底图不可见,也可以通过本节所述方法实现。 2.3.7 录入后打印录入后打印 有这样一种打印需求,用户希望在录入一些数据后,即把该单据打印出来,比如以下页面,既有打印按钮,又有一些文本输入框,或下拉选择框,或者checkbox,但不希望打印时,把它们的边框打印出来,那么可以使用本功能。
如录入界面如下:
使用本功能后预览效果:
使用本功能,你可以用以下代码: 1. <html> 2. <head> 3. <script type="text/javascript" src="jcp.js"></script> 4. <style type="text/css"> 5. 6. .jatools-page input[type=text] { 7. border: none; 8. background-color: transparent; 9. } 10. 11. </style> 12. <script type="text/javascript"> 13. function doPrint(how) { 14. var myDoc = { 15. inputs : true, //录入打印 16. settings : { 17. paperName : 'A4' 18. }, 19. noMargins : true, //无边距打印 20. documents : document, 21. enableScreenOnlyClass : true, // 确保 42行的图片不会被打印出来 22. copyrights : "杰创软件拥有版权 www.jatools.com" 23. } 24. var jcp = getJCP(); 25. if (how == '打印预览...') 26. jcp.printPreview(myDoc, false); 27. 28. else if (how == '打印...') 29. jcp.print(myDoc, true); 30. 31. else 32. jcp.print(myDoc, false); 33. } 34. </script> 35. </head> 36. <body> 37. ... 38. <input type="button" value="打印预览..." onClick="doPrint('打印预览...')"> 39. <input type="button" value="打印..." onClick="doPrint('打印...')"> 40. <input type="button" value="打印" onClick="doPrint('打印')"> 41. <div id='page1' style="position: relative;; width: 210mm; height: 297mm;"> 42. <img src="image/sqs2.jpg" style="position: absolute; width: 100%; height: 100%" class="screen-only"> 43. <input type="text" style="position: absolute; width: 520px; left: 209px; top: 181px;" /> 44. <input type="checkbox" style="position: absolute; left: 295px; top: 350px;" /> 45. ... 46. </div> 47. </body> 48. </html>
第6行代码,设置一个样式规则,让文本输入框在打印预览或者打印时,显示成无边框,透明。在JCP打印预览,或者打印时,会自动在 page1 上设置样式类 jatools-page,因此,打印和预览时,这条规则会起作用,在录入界面中,这条规则不起作用,因此,行 43的 <input> 以文本输入框的普通样式显示,一般是有边框,且白色背景。
15行代码,设置 myDoc.inputs 为 true录入后打印,可以确保录入的信息能够打印出来。
提示: 1. 利用 jatools-page 样式类来创建样式规则,可以让输入组件在打印时,呈现与录入时不同的显示效果; 2. 如果想在打印成功后,保存打印状态到后台,可以用 done 回调,详见下节。
2.4 打印回调JCP在打印后,会回调你的js函数,在回调函数里,你可以处理一些打印后的任务,比如,往服务器写入一些标志,或者,重新启动一次打印任务。 2.4.1 打印结束回调脚本打印结束回调脚本 1. var myDoc = { 2. documents : document, 3. copyrights : '杰创软件拥有版权 www.jatools.com' 4. }; 5. //定义回调方法 6. myDoc.done = function() { 7. console.log("打印结束.") 8. } 9. getJCP().print(myDoc); 在JCP中,所有调用都是异步执行的,print方法也一样,该方法返回时,打印可能还没有结束,当打印结束时(指打印任务已经生成),JCP会检查myDoc上是否定义了done回调函数,如果有,则调用之。
回调的主要用处: ü 打印结束后通过 ajax写入已打印标志到数据库,避免重复打印等; ü 可以一次打印多个文档,并使用不同的参数,比如,一键打印到不同打印机。
注意,打印结束是指打印任务已经生成,并不是指打印机已经完成打印。即使你的打印机处于关机状态,JCP也会调用done,只不过,打印任务仅列在打印机任务列表中,等待发往打印机而已。
也就是说,你不能用done来判断是否真的打印出来了。
JCP不允许连续调用 print方法, 但你可以在done回调里,再次调用print方法。
错误的二次打印: 1. var myDoc1 = {...}; 2. getJCP().print(myDoc1); 3. var myDoc2 = {...}; 4. getJCP().print(myDoc2); // 连续打印,错误 !!! 5. // 结果不可预知,有可能myDoc1被打印两次,而myDoc2不打印, 6. // 或者,myDoc1 不打印, myDoc2 被打印两次, 7. // 或者 myDoc2比 myDoc1先打印
正确的二次打印: 1. var myDoc1 = { 2. ... 3. done : function() { 4. var myDoc2 = { 5. ... 6. }; 7. getJCP().print(myDoc2); 8. } 9. }; 10. getJCP().print(myDoc1); 采用done多次打印时,会生成多个系统打印任务。
2.4.2 一键打印到不同打印机一键打印到不同打印机 1. function doPrint() { 2. var myDoc1 = { 3. settings : { 4. printer : 'oki 5330' 5. }, 6. documents : 'pages1.jsp', 7. copyrights : '杰创软件拥有版权 www.jatools.com', 8. done : function() { 9. var myDoc2 = { 10. settings : { 11. printer : 'hp laser jet 1000' 12. }, 13. documents : 'pages2.jsp', 14. copyrights : '杰创软件拥有版权 www.jatools.com', 15. done : function() { 16. console.log("打印结束"); 17. } 18. }; 19. getJCP().print(myDoc2); 20. } 21. }; 22. getJCP().print(myDoc1); 23. }
本例利用回调函数done,将文档 pages1.jsp和pages2.jsp两个文档中的page1,page2,...,分别打印到 oki和hp打印机。
这里的打印机参数,是直接写成常量,实际项目中,你可以使用JCP的getPrinters取得打印机列表,并把它们显示在<select> 中,便于用户选择需要的打印机。
正如上节所说,本例使用回调来进行第二次打印,而不是采用连续调用。 提示: 虽然你可以将 myDoc.documents设置成数组,同时打印这两个文档,如下所示,但这种情况下,两个文档的所有打印参数是一样的,比如,同样的输出打印机,同样的纸张等等: 1. function doPrint() { 2. var myDoc1 = { 3. settings : { 4. printer : 'oki 5330' // 只能都打印到oki打印机 5. }, 6. documents : ['pages1.jsp', 'pages2.jsp'] 7. copyrights : '杰创软件拥有版权 www.jatools.com' 8. }; 9. getJCP().print(myDoc1); 10. }
2.4.3 一键打印多个 pdf 和 html 文档一键打印多个 pdf 和 html 文档 JCP 可以用 print方法,打印HTML文档,也可以用 printDocument方法,打印一个pdf文件,而且这两个方法,都提供了回调机制。
类似print方法,printDocument的第二个参数中,也可以设置一个回调函数,如下所示: 1. var myDoc = { 2. fileType : 'pdf', 3. done : function() { 4. console.log("pdf打印结束"); 5. } 6. } 7. getJCP().printDocument("a.pdf", myDoc); 这里你只需要关注 done回调,更详细的pdf打印介绍,请参照pdf打印部分。
以下示例,利用回调机制,一次性顺序打印一个HTML文档,一个pdf文档,再打印HTML文档: 1. //打印三个文档,html格式2个,pdf格式1个 2. var docs = [{ 3. type : 'html', 4. documents : document 5. // HTML文档, page1 ... div在本文档中 6. }, { 7. type : 'pdf', // pdf文件打印 8. url : 'pdf/apicpp.pdf' // pdf流位置 9. }, { 10. type : 'html', 11. documents : 'pages.htm' // HTML文档, page1 ... div在一个url文件中 12. }] 13. function main() { 14. doPrint(0); 15. } 16. function doPrint(current) { 17. var doc = docs[current]; 18. if (doc.type == 'html') { 19. var mydoc = { 20. documents : doc.documents, 21. copyrights : "杰创软件拥有版权 www.jatools.com", 22. done : function() { 23. if (current < docs.length - 1) { 24. doPrint(current + 1); 25. } 26. } 27. } 28. getJCP().print(mydoc); 29. } else if (doc.type == 'pdf') { 30. var mydoc = { 31. fileType : 'pdf', 32. done : function() { 33. if (current < docs.length - 1) { 34. doPrint(current + 1); 35. } 36. } 37. } 38. getJCP().printDocument(doc.url, mydoc); 39. } 40. }
|