二维码打印
1. 二维码简介
二维码(2-dimensional bar code),又称二维条码,它是用特定的几何图形按一定规律在平面(二维方向)上分布的黑白相间的图形,是所有信息数据的一把钥匙。在现代商业活动中,可实现的应用十分广泛,如:产品防伪/溯源、广告推送、网站链接、数据下载、商品交易、定位/导航、电子凭证、车辆管理等等。根据编码规则的不同,二维码主要有Data Matrix,MaxiCode, Aztec,QR Code, Vericode,PDF417,Ultracode, Code 49, Code 16K, Han Xin Code 等,现在使用最多的的是QR Code。
2.QR CODE码
QR Code码是由日本Denso公司于1994年9月研制的一种矩阵二维码符号,目的是用来追踪汽车零部件(从QRCode的诞生原因可以看出它跟ERP颇有渊源)。
2.1QRCode优点
快速:
在用CCD识读QR Code码时,整个QR Code码符号中信息的读取是通过QR Code码符号的位置探测图形,用硬件来实现,因此,信息识读过程所需时间很短,它具有超高速识读特点。
全方位:
QR Code码具有全方位(360°)识读特点,这是QR Code码优于行排式二维条码如四一七条码的另一主要特点,由于四一七条码是将一维条码符号在行排高度上的截短来实现的,因此,它很难实现全方位识读,其识读方位角仅为±10°。
信息容量大:
QRCode最大的数据容量是(冗余级别为L时):
数字数据:7,089个字符
字母数据: 4,296个字符
8位字节数据: 2,953个字符
中国/日本汉字数据:1,817个字符
容错率高:
根据不同的冗余级别,QRCode具有一定的容错率,二维码被污损的面积在一定的范围内时还可以被读取出来,二维码有4种冗余级别,分别具有不同的纠错能力:
L级:约可纠错7%的数据码字
M级:约可纠错15%的数据码字
Q级:约可纠错25%的数据码字
H级:约可纠错30%的数据码字
2.2QRCode原理
QRCode原理就是把信息以01编码打出来,深色模块表示二进制"1",浅色模块表示二进制"0",以下是QRCode的具体的编码信息分布图
位置探测图形、位置探测图形分隔符:
用于对二维码的定位,对每个QR码来说,位置都是固定存在的,只是大小规格会有所差异;这些黑白间隔的矩形块很容易进行图像处理的检测。
校正图形:
根据尺寸的不同,矫正图形的个数也不同。矫正图形主要用于QR码形状的矫正,尤其是当QR码印刷在不平坦的面上,或者拍照时候发生畸变等。
定位图形:
这些小的黑白相间的格子就好像坐标轴,在二维码上定义了网格。
格式信息:
表示该二维码的纠错级别,分为L、M、Q、H;
数据区域:
使用黑白的二进制网格编码内容。8个格子可以编码一个字节。
版本信息:
即二维码的规格,QR码符号共有40种规格的矩阵(一般为黑白色),从21x21(版本1),到177x177(版本40),每一版本符号比前一版本 每边增加4个模块。
纠错码字:
用于修正二维码损坏带来的错误。
2.3简要编码过程
1、 数据分析:确定编码的字符类型,按相应的字符集转换成符号字符; 选择纠错等级,在规格一定的条件下,纠错等级越高其真实数据的容量越小。
2、 数据编码:将数据字符转换为位流,每8位一个码字,整体构成一个数据的码字序列。其实知道这个数据码字序列就知道了二维码的数据内容。
3、 纠错编码:按需要将上面的码字序列分块,并根据纠错等级和分块的码字,产生纠错码字,并把纠错码字加入到数据码字序列后面,成为一个新的序列。
在二维码规格和纠错等级确定的情况下,其实它所能容纳的码字总数和纠错码字数也就确定了,比如:版本10,纠错等级时H时,总共能容纳346个码字,其中224个纠错码字。 就是说二维码区域中大约1/3的码字时冗余的。对于这224个纠错码字,它能够纠正112个替代错误(如黑白颠倒)或者224个拒读错误(无法读到或者无法译码), 这样纠错容量为:112/346=32.4%
4、 构造最终数据信息:在规格确定的条件下,将上面产生的序列按次序放入分块中 按规定把数据分块,然后对每一块进行计算,得出相应的纠错码字区块,把纠错码字区块 按顺序构成一个序列,添加到原先的数据码字序列后面。 如:D1, D12, D23, D35, D2, D13, D24, D36, ... D11, D22, D33, D45, D34, D46, E1, E23,E45, E67, E2, E24, E46, E68,...
5、 构造矩阵:将探测图形、分隔符、定位图形、校正图形和码字模块放入矩阵中。
把上面的完整序列填充到相应规格的二维码矩阵的区域中
6、 掩摸:将掩摸图形用于符号的编码区域,使得二维码图形中的深色和浅色(黑色和白色)区域能够比率最优的分布。
3.框架安装
1、上传qrcode1.5.jar至数据库服务器/home/oraprod/(路径随意)
2、telnet用oracle用户登录数据库服务器(oraprod)
3、进入qrcode1.5.jar所在目录:cd /home/oraprod
4、执行以下命令:loadjava -u apps/cpnds2014apps@PROD -r -v –f qrcode1.5.jar
4.二维码开发
4.1JSP
可直接编译一下代码,作用主要是调用encode2方法,通过jsp将要显示的二维码内容contents转换成矩阵二维码符号,即qrcode码。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 create or replace and compile java source named APPS.cux_qrcode as 2 import java.awt.image.BufferedImage; 3 import java.io.File; 4 import java.io.IOException; 5 import java.util.Hashtable; 6 import javax.imageio.ImageIO; 7 import com.google.zxing.BarcodeFormat; 8 import com.google.zxing.BinaryBitmap; 9 import com.google.zxing.DecodeHintType; 10 import com.google.zxing.LuminanceSource; 11 import com.google.zxing.MultiFormatReader; 12 import com.google.zxing.MultiFormatWriter; 13 import com.google.zxing.CuxMultiFormatWriter; 14 import com.google.zxing.Reader; 15 import com.google.zxing.ReaderException; 16 import com.google.zxing.Result; 17 import com.google.zxing.client.j2se.BufferedImageLuminanceSource; 18 import com.google.zxing.client.j2se.MatrixToImageWriter; 19 import com.google.zxing.common.ByteMatrix; 20 import com.google.zxing.common.HybridBinarizer; 21 import oracle.sql.* ; 22 import java.sql.*; 23 import java.io.OutputStream; 24 25 public class QRCode 26 { 27 28 public static String encode(String content,String path,int width,int height) 29 { 30 try { 31 ByteMatrix byteMatrix; 32 byteMatrix= new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height); 33 File file = new File(path); 34 MatrixToImageWriter.writeToFile(byteMatrix, "png", file); 35 return "S"; 36 } catch (Exception e) { 37 e.printStackTrace(); 38 return "-1@"+e.toString(); 39 } 40 } 41 42 public static BLOB encode2(String contents,int width, int height,int padding, String imageFormat) { 43 44 try { 45 46 ByteMatrix byteMatrix; 47 byteMatrix= new CuxMultiFormatWriter().encode(contents, BarcodeFormat.QR_CODE, width, height, padding); 48 49 Connection conn = DriverManager.getConnection( 50 "jdbc:default:connection:") ; 51 BLOB outBlob = BLOB.createTemporary( conn, true , BLOB.DURATION_CALL) ; 52 MatrixToImageWriter.writeToStream(byteMatrix, imageFormat, outBlob.getBinaryOutputStream()); 53 54 outBlob.getBinaryOutputStream().close(); 55 return outBlob ; 56 57 } catch (Exception e) { 58 e.printStackTrace(); 59 System.out.println(e.toString()); 60 } 61 return null; 62 } 63 64 public static String decode(String imgPath){ 65 try { 66 Reader reader = new MultiFormatReader(); 67 File file = new File(imgPath); 68 BufferedImage image; 69 try { 70 image = ImageIO.read(file); 71 if (image == null) { 72 System.out.println("Could not decode image"); 73 return "-1@Could not decode image"; 74 } 75 LuminanceSource source = new BufferedImageLuminanceSource(image); 76 BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer( 77 source)); 78 Result result; 79 Hashtable<DecodeHintType, String> hints = new Hashtable<DecodeHintType, String>(); 80 81 result = new MultiFormatReader().decode(bitmap, hints); 82 String resultStr = result.getText(); 83 return resultStr; 84 85 } catch (IOException ioe) { 86 return "-1@" + ioe.toString(); 87 } catch (ReaderException re) { 88 return "-1@" + re.toString(); 89 } 90 91 } catch (Exception ex) { 92 ex.printStackTrace(); 93 return "-1@" + ex.toString(); 94 } 95 } 96 } 97 /
4.2调用JSP方法,获取二维码
可直接编译一下代码,无需更改。主要是获取到qrcode码到一个BLOB对象。并通过blob_to_clob方法,将BLOB转化成CLOB。
所用到的主要代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 --包头 2 CREATE OR REPLACE PACKAGE cux_qrcode_util_pkg AS 3 4 g_qrcode_file_path VARCHAR2(400) := '/cfd/'; 5 g_return_error_code VARCHAR2(3) := '-1@'; 6 7 PROCEDURE decode(p_image_path IN VARCHAR2, 8 x_content OUT VARCHAR2, 9 x_status OUT VARCHAR2, 10 x_message OUT VARCHAR2); 11 12 PROCEDURE encode(p_contents IN VARCHAR2, 13 p_file_name IN VARCHAR2, 14 p_width IN NUMBER, 15 p_height IN NUMBER, 16 x_full_file_name OUT VARCHAR2, 17 x_status OUT VARCHAR2, 18 x_message OUT VARCHAR2); 19 20 FUNCTION encode2(p_contents IN VARCHAR2, 21 p_width IN NUMBER, 22 p_height IN NUMBER, 23 p_padding IN NUMBER, 24 p_imageformat IN VARCHAR2) RETURN BLOB AS 25 26 LANGUAGE JAVA NAME 'QRCode.encode2(java.lang.String,int,int,int,java.lang.String) return oracle.sql.BLOB'; 27 28 FUNCTION blob_to_clob(p_blob BLOB) RETURN CLOB; 29 30 END cux_qrcode_util_pkg; 31 --包体 32 CREATE OR REPLACE PACKAGE BODY cux_qrcode_util_pkg AS 33 34 g_debug VARCHAR2(1) := nvl(fnd_profile.value('AFLOG_ENABLED'), 'N'); 35 g_request_id NUMBER := fnd_global.conc_request_id; 36 g_success VARCHAR(1) := 'S'; 37 g_warning VARCHAR2(1) := 'W'; 38 g_error VARCHAR2(1) := 'E'; 39 g_date_format VARCHAR2(30) := 'YYYYMMDDHH24MISS'; 40 41 FUNCTION encode(contents IN VARCHAR2, 42 filename IN VARCHAR2, 43 width IN NUMBER, 44 height IN NUMBER) RETURN VARCHAR2 AS 45 LANGUAGE JAVA NAME 'QRCode.encode(java.lang.String,java.lang.String,int,int) return java.lang.String'; 46 47 FUNCTION decode(imgpath IN VARCHAR2) RETURN VARCHAR2 AS 48 LANGUAGE JAVA NAME 'QRCode.decode(java.lang.String) return java.lang.String'; 49 50 51 PROCEDURE encode(p_contents IN VARCHAR2, 52 p_file_name IN VARCHAR2, 53 p_width IN NUMBER, 54 p_height IN NUMBER, 55 x_full_file_name OUT VARCHAR2, 56 x_status OUT VARCHAR2, 57 x_message OUT VARCHAR2) IS 58 l_status VARCHAR2(1); 59 l_message VARCHAR2(4000); 60 l_full_file_name VARCHAR2(1000); 61 l_sysdate VARCHAR2(20) := to_char(SYSDATE, g_date_format); 62 BEGIN 63 x_status := g_success; 64 x_message := NULL; 65 66 l_full_file_name := g_qrcode_file_path || p_file_name || l_sysdate || 67 '.png'; 68 dbms_output.put_line('file_path: ' || l_full_file_name); 69 l_message := substr(encode(p_contents, 70 l_full_file_name, 71 p_width, 72 p_height), 73 0, 74 4000); 75 76 IF l_message = g_success THEN 77 l_status := g_success; 78 l_message := NULL; 79 ELSE 80 l_status := g_error; 81 END IF; 82 83 x_status := l_status; 84 x_message := l_message; 85 x_full_file_name := l_full_file_name; 86 EXCEPTION 87 WHEN fnd_api.g_exc_unexpected_error THEN 88 x_status := g_error; 89 x_message := 'encode Program Unexpected error.' || SQLERRM; 90 WHEN fnd_api.g_exc_error THEN 91 x_status := g_warning; 92 x_message := 'encode Program error.' || SQLERRM; 93 END encode; 94 95 PROCEDURE decode(p_image_path IN VARCHAR2, 96 x_content OUT VARCHAR2, 97 x_status OUT VARCHAR2, 98 x_message OUT VARCHAR2) IS 99 l_status VARCHAR2(1); 100 l_message VARCHAR2(4000); 101 l_content VARCHAR2(32767); 102 BEGIN 103 x_status := g_success; 104 x_message := NULL; 105 106 l_content := decode(p_image_path); 107 108 IF instr(l_content, g_return_error_code) = 1 THEN 109 l_status := g_error; 110 l_message := substr(l_content, length(g_return_error_code) + 1, 4000); 111 l_content := NULL; 112 ELSE 113 l_status := g_success; 114 END IF; 115 116 x_content := l_content; 117 x_status := l_status; 118 x_message := l_message; 119 EXCEPTION 120 WHEN fnd_api.g_exc_unexpected_error THEN 121 x_status := g_error; 122 x_message := 'decode Program Unexpected error.' || SQLERRM; 123 WHEN fnd_api.g_exc_error THEN 124 x_status := g_warning; 125 x_message := 'decode Program error.' || SQLERRM; 126 127 END decode; 128 129 FUNCTION blob_to_clob(p_blob BLOB) RETURN CLOB IS 130 l_result CLOB; 131 BEGIN 132 dbms_lob.createtemporary(lob_loc => l_result, cache => FALSE, dur => 0); 133 wf_mail_util.encodeblob(p_blob, l_result); 134 RETURN(l_result); 135 END blob_to_clob; 136 137 END cux_qrcode_util_pkg;
4.3定义报表,RTF模板。XML
报表示例,代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 CREATE OR REPLACE PACKAGE cux_fa_dual_pkg IS 2 /* 3 description:二维码打印测试 4 TIME:2015/08/10 wang.chen 5 */ 6 PROCEDURE main(errbuf OUT VARCHAR2, retcode OUT VARCHAR2); 7 8 END cux_fa_dual_pkg; 9 10 CREATE OR REPLACE PACKAGE BODY cux_fa_dual_pkg IS 11 12 PROCEDURE output(p_text IN VARCHAR2) IS 13 BEGIN 14 fnd_file.put_line(fnd_file.output, p_text); 15 dbms_output.put_line(p_text); 16 END output; 17 18 19 PROCEDURE process_request(x_return_status OUT VARCHAR2, x_return_msg OUT VARCHAR) IS 20 21 CURSOR cur_dual IS 22 SELECT 'www.hand-china.com' company, 23 '启用时间: ' || substr(to_char(SYSDATE, 'YYYY-MM'), 1, 4) || '年' || 24 substr(to_char(SYSDATE, 'YYYY-MM'), 6, 2) || '月' start_date 25 FROM dual 26 WHERE 1 = 1; 27 28 l_encoding_data VARCHAR2(32767); 29 l_blob BLOB; 30 l_clob CLOB; 31 l_width NUMBER := 0; 32 33 BEGIN 34 35 -- l_width := 100; 36 output('<?xml version="1.0" encoding="UTF-8"?>'); 37 output('<DUALS>'); 38 39 FOR rec_dual IN cur_dual LOOP 40 --l_encoding_data := NULL; 41 l_encoding_data := l_encoding_data || rec_dual.company || chr(10) || chr(13); 42 43 l_encoding_data := l_encoding_data || rec_dual.start_date; 44 -- Dbms_Output.put_line(l_encoding_data); 45 IF length(l_encoding_data) > 32 THEN 46 l_width := 114; 47 ELSIF length(l_encoding_data) < 25 THEN 48 l_width := 100; 49 ELSE 50 l_width := 110; 51 END IF; 52 53 l_clob := NULL; 54 l_blob := cux_qrcode_util_pkg.encode2(p_contents => l_encoding_data, 55 p_width => l_width, 56 p_height => l_width, 57 p_padding => 0, 58 p_imageformat => 'png'); 59 l_clob := cux_qrcode_util_pkg.blob_to_clob(l_blob); 60 61 output('<DUAL>'); 62 output('<COMPANY>' || rec_dual.company || '</COMPANY>'); 63 output('<START_DATE>' || rec_dual.start_date || '</START_DATE>'); 64 output('<QRCODE>' || l_clob || '</QRCODE>'); 65 --INSERT INTO cux_test_clob (clob_txt) VALUES (l_clob); 66 output('</DUAL>'); 67 68 END LOOP; 69 70 output('</DUALS>'); 71 72 EXCEPTION 73 WHEN OTHERS THEN 74 x_return_status := fnd_api.g_ret_sts_error; 75 x_return_msg := substr(SQLERRM, 1, 1000); 76 END process_request; 77 78 PROCEDURE main(errbuf OUT VARCHAR2, retcode OUT VARCHAR2) IS 79 l_return_status VARCHAR2(1) := fnd_api.g_ret_sts_success; 80 l_return_msg VARCHAR2(4000) := ''; 81 BEGIN 82 errbuf := ''; 83 retcode := 0; 84 85 process_request(x_return_status => l_return_status, x_return_msg => l_return_msg); 86 87 EXCEPTION 88 WHEN fnd_api.g_exc_error THEN 89 retcode := 1; 90 errbuf := REPLACE(l_return_msg, chr(0), ' '); 91 WHEN fnd_api.g_exc_unexpected_error THEN 92 retcode := 2; 93 errbuf := REPLACE(l_return_msg, chr(0), ' '); 94 WHEN OTHERS THEN 95 retcode := 2; 96 errbuf := SQLERRM; 97 END main; 98 99 END cux_fa_dual_pkg;
获取定义RTF模板时需加载的XML:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <?xml version="1.0" encoding="GBK"?> 2 <DUALS> 3 <DUAL> 4 <COMPANY>www.hand-china.com</COMPANY> 5 <START_DATE>启用时间: 2015年08月</START_DATE> 6 <QRCODE>iVBORw0KGgoAAAANSUhEUgAAAHIAAAByCAYAAACP3YV9AAADA0lEQVR42u2dQW7k 7 MAwE/f9P794DBLHIalkUqoEcBshkbJXBkC1S8/xTV+hxCQSpBKkEqQQpSCVIJUgl 8 SEEqQSpBKkEqQQpSXQjyeZ7Sz8/3//b6t8/76zr+uj76/qj1EKQgGZDV368uyNsF 9 f/sA0NedWj9BCrJ3g29D6WqoeRuCq2Cr4LrrIUhBfguyGuJWk45T/nUIUpCzQFYB 10 VRcodX2CFORd5Qd9o29DNWVAWEcK8k6LbuprvVZBfgNyV7K0qwzoGgefraMgBYlY 11 Y1VznC4j0lZc15IUpCB76XsVTHdbi7bsuuVS2ggQpCBrBfOukEaHYMryG9vqIchh 12 IGnrqhrKUoU51WJyfGgV5CUg6XKj+wBU2ym7yU/XiBCkINnQWr3QapnQNQyoB4P+ 13 XEEKco/pnU6GKAuMGilYLccEKchM+dFdoK55TpUNdBIVb1kRpCBbyUzVDKeTr9To 14 QfdBFaQgM+VHuvBOjQqkzXFBCnJvaKU3hruhMDVsdEqzliAFWQsp1dCYLrRpM3t8 15 85Ugh4OshhZqG6hbBtGbAKmySpCC7BXeVHKSLn9WP59uHhOkILOhlW796JrkdHJW 16 NSzGmOaCHApytWBPGw1UWbB6P1+P5QlSkGsLnjabq0CpVpRukiVIQe6x6Og0nLa4 17 0gcgbdtWE6QgkRvtlgWroZIeQaCsO0EKMmvRUaY73QCcGljt/j1BCvKM8qO7wFTI 18 pg6hSCdnghQka03Rlhy90ZwyCsYmO4IcCpK24KhkIfUFLV+NQghSkNk0PZ1M7Bqo 19 pd8vSEHuDa3V8oE28amDlnaHUkEKMrT9Apna6Y1dqt3z2NAqyEtA7v56BCpE0yPz 20 q8nVmEMFBTkUZMpk71pqVaBU8jK201yQl4CkmrDopq7UEdVUWSJIQZ4Nkgq53W0l 21 usy5totOkJfOR1LJUup4T2q0/trQKshLyo+qFZcajKWP/RyX7AhyOEj6i1JoEHSD 22 MQVckIJkQaozJEhBKkEqQSpBClIJUglSCVKQSpBKkEqQSpCCVAfqPyGDaYroYl8y 23 AAAAAElFTkSuQmCC</QRCODE> 24 </DUAL> 25 </DUALS>
通过TEST main,得到输出的xml
---解析出来的encoding为UTF-8,在定义rtf模板加载xml时,需改为GBK,否则会报错
--通过output(p_text IN VARCHAR2)输出得到的XML,可能存在的一种情况:当需要输出的二维码数据太长,超过VARCHAR2的最大长度时,QRCODE将获取不到全部内容,内容被截断,导致最后会得不到二维码png,可使用lob对象来获取输出。
RTF模板:
QRCODE:<fo:instream-foreign-object content-type="image/png"><xsl:value-of select="QRCODE"/></fo:instream-foreign-object>
作用:固定形式,无需更改,大概是将QRCODE输出类型设置成图片png
在EBS系统中上传RTF模板
Xml Publish Administrator 责任à主页à 数据定义
创建数据定义
创建模板
4.4跑请求,打印二维码
(定义请求略)
结果: