java?zxing合成復(fù)雜二維碼圖片示例詳解
說明:
最近接到需要將二維碼合成復(fù)雜圖片的需求,要求給二維碼上下或者左側(cè)添加相關(guān)文字描述,技術(shù)沒有難點(diǎn),整理本文主要記錄思路和踩過的坑。
整體思路:
引入zxing成熟的二維碼生成接口,生成標(biāo)準(zhǔn)二維碼文件,通過java圖形圖像處理API為二維碼添加相關(guān)文字描述,根據(jù)需要,可以為合成后的圖片添加相關(guān)背景。示例如下圖所示:

- 1.先拿點(diǎn)位圖來說,生成二維碼圖片核心代碼如下
/**
* 定義二維碼的參數(shù)
*/
HashMap<EncodeHintType, Object> hints = new HashMap();
//指定字符編碼為“utf-8”
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
//指定二維碼的糾錯(cuò)等級(jí)為中級(jí)
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
//設(shè)置圖片的邊距
hints.put(EncodeHintType.MARGIN, 1);
/**
* 生成二維碼
*/
try {
BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_WIDTH, QRCODE_HEIGHT, hints);
Path file = new File(filePath).toPath();
MatrixToImageWriter.writeToPath(bitMatrix, format, file);
} catch (Exception e) {
log.error("二維碼生成出錯(cuò):/permitDownload: error", e);
}
- 2.給二維碼添加文字
/**
* 給二維碼下方添加說明文字
*
* @param image 原二維碼
* @param topText 頂部說明文字
* @param downText 底部說明文字
* @return 帶說明文字的二維碼
*/
private static BufferedImage addNote(BufferedImage image, String topText, String downText) {
Image src = image.getScaledInstance(QRCODE_WIDTH, QRCODE_HEIGHT, Image.SCALE_DEFAULT);
BufferedImage tag = new BufferedImage(QRCODE_WIDTH, IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = tag.createGraphics();//設(shè)置文字
g2.setColor(Color.BLACK);
g2.setBackground(Color.WHITE);
g2.clearRect(0,0,QRCODE_WIDTH, IMAGE_HEIGHT);
//設(shè)置頂部文本并計(jì)算坐標(biāo)
// 保證操作系統(tǒng)包含“宋體”字體,如果沒有上傳字體至JAVA_HOME/jre/lib/fonts下
FontMetrics fm = getFontByWidth(new Font("宋體", Font.PLAIN, DEFAULT_FONT_SIZE), topText, g2);
//文字的寬度
int fontWidth = fm.stringWidth(topText);
//文字高度
int fontHeight = fm.getHeight();
/**
* 頂部添加文字并居中
*/
g2.drawString(topText, (QRCODE_WIDTH - fontWidth) / 2, (TEXT_DEFAULT_HEIGHT - fontHeight) / 2 + fm.getFont().getSize());
/**
* 繪制二維碼
*/
g2.drawImage(src, 0, TEXT_DEFAULT_HEIGHT, null);
// 設(shè)置底部文字字體并計(jì)算坐標(biāo)
// 保證操作系統(tǒng)包含“宋體”字體,如果沒有上傳字體至JAVA_HOME/jre/lib/fonts下
fm = getFontByWidth(new Font("宋體", Font.PLAIN, DEFAULT_FONT_SIZE), downText, g2);
//文字的寬度
fontWidth = fm.stringWidth(downText);
//文字高度
fontHeight = fm.getHeight();
/**
* 添加底部文字
*/
g2.drawString(downText, (QRCODE_WIDTH - fontWidth) / 2, QRCODE_HEIGHT + TEXT_DEFAULT_HEIGHT+(TEXT_DEFAULT_HEIGHT - fontHeight) / 2 + fm.getFont().getSize());
g2.dispose();
image = tag;
image.flush();
return image;
}
知識(shí)點(diǎn) : 底部文字長(zhǎng)度會(huì)變,目前設(shè)計(jì)只放一行文字,所以根據(jù)字?jǐn)?shù)多少會(huì)動(dòng)態(tài)改變文字大小在一個(gè)合理區(qū)間(不至于太小無法識(shí)別),使用FontMetrics 對(duì)象,這個(gè)點(diǎn)會(huì)對(duì)多數(shù)同學(xué)有幫助
- 3.動(dòng)態(tài)修改字體及大小
/**
* 根據(jù)文字長(zhǎng)度改變文字大小
*
* @param font 默認(rèn)字體
* @param note 文字內(nèi)容
* @param g2 圖像畫布
* @return 處理后的字體封裝
*/
private static FontMetrics getFontByWidth(Font font, String note, Graphics2D g2) {
FontMetrics fm = g2.getFontMetrics(font);
int textWidth = fm.stringWidth(note);//文字的寬度
if (textWidth > QRCODE_WIDTH) {
int fontSize = (int) ((TEMP_PARAM / textWidth) * font.getSize());
font = new Font(font.getName(), font.getStyle(), fontSize);
}
g2.setFont(font);
return g2.getFontMetrics(font);
}
- 4.最后一步將藍(lán)色底圖與二維碼圖片合成,就可以了。
圖片合成四部曲
第一步:創(chuàng)建畫布,需要設(shè)置畫布的寬、高,單位應(yīng)該是像素
BufferedImage tag = new BufferedImage(QRCODE_WIDTH, IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);
第二步: 在畫布上創(chuàng)建圖形對(duì)象Graphics2D,可以根據(jù)你對(duì)圖像知識(shí)點(diǎn)的了解設(shè)置如背景、前景、邊框、寬度等信息
Graphics2D g2 = tag.createGraphics();//設(shè)置文字
第三步: 合成圖片,過程中的圖片添加順序、圖片大小、坐標(biāo)位置會(huì)影響最終的呈現(xiàn)效果,如果最終效果沒有達(dá)到設(shè)計(jì)需求,調(diào)整這三個(gè)參數(shù)一定會(huì)有所幫助
Image src = image.getScaledInstance(QRCODE_WIDTH, QRCODE_HEIGHT, Image.SCALE_DEFAULT); ... /** * 繪制二維碼 */ g2.drawImage(src, 0, TEXT_DEFAULT_HEIGHT, null);
第四步: 生成最終的新圖片
/**
* 給二維碼圖片添加背景圖片
*
* @param qrPic 二維碼
* @param backImage 背景圖片
*/
private static void createNewPng(File qrPic, BufferedImage backImage) {
try {
if (!qrPic.isFile()) {
log.error("二維碼臨時(shí)路徑不存在!");
}
/**
* 讀取背景圖片,并構(gòu)建繪圖對(duì)象--畫布
*/
Graphics2D g = backImage.createGraphics();
/**
* 讀取二維碼圖片
*/
BufferedImage qrcodeImage = ImageIO.read(qrPic);
//開始繪制圖片
g.drawImage(qrcodeImage, 48, 120, qrcodeImage.getWidth(), qrcodeImage.getHeight(), null);
g.dispose();
ImageIO.write(backImage, "png", qrPic);
} catch (Exception e) {
log.error("繪制二維碼出錯(cuò)!");
}
}
第二張圖片和第一張圖片生成過程相同,只是將文中 【2. 給二維碼添加文字】中的順序由上、中、下變?yōu)?左、右即可
踩過的坑
- 背景圖片像素、大小需要和二維碼匹配,否則會(huì)出現(xiàn)二維碼與背景比例嚴(yán)重失調(diào)或二維碼顯示不完整
- 二維碼添加文字亂碼 開發(fā)環(huán)境(windows),測(cè)試環(huán)境(centos 服務(wù)器版)。本地開發(fā)測(cè)試沒有任何問題,打包部署至服務(wù)器后所有中文字符出現(xiàn)亂碼(各種轉(zhuǎn)碼,字體設(shè)置,調(diào)試整了很久),問題仍然沒有任何變化。最終靈光一現(xiàn)懷疑是系統(tǒng)字體問題,查了測(cè)試環(huán)境(centos)字體信息確實(shí)沒有“宋體”,設(shè)置成系統(tǒng)自有或默認(rèn)的字體,問題還在。最終從(c:\windows\fonts\simsun.ttc)開發(fā)系統(tǒng)中copy字體至測(cè)試系統(tǒng)(JAVA_HOME/jre/libs/fonts)中后重啟應(yīng)用,問題得到完美解決。系統(tǒng)安裝部署需要準(zhǔn)備字體有點(diǎn)麻煩,不知道還有沒有更好的辦法,字體有邏輯字體和物理字體,愛、先這樣吧。
- 背景圖片加載問題 項(xiàng)目為springboot項(xiàng)目,背景圖片存放在resources文件夾下,本地開發(fā)測(cè)試未見異常,打包部署至服務(wù)器后背景圖片無法找到,原始代碼如下
String backFilePath = "template/down.png";
ClassPathResource resource = new ClassPathResource(backFilePath);
File file = resource.getFile();
在網(wǎng)上找加載圖片的方法都使用過,沒有效果,最后修改為輸入流,圖片合成正常,代碼如下
/**
* 必須通過流方式,否則不同操作系統(tǒng)無法拿到背景圖片信息
*/
String backFilePath = "template/down.png";
ClassPathResource resource = new ClassPathResource(backFilePath);
BufferedImage bi = ImageIO.read(resource.getInputStream());以上就是java zxing合成復(fù)雜二維碼圖片示例詳解的詳細(xì)內(nèi)容,更多關(guān)于java zxing合成復(fù)雜二維碼的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java Eclipse進(jìn)行斷點(diǎn)調(diào)試的方法
本篇文章主要介紹了Java Eclipse進(jìn)行斷點(diǎn)調(diào)試的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-11
java獲取當(dāng)前時(shí)間的四種方法代碼實(shí)例
這篇文章主要介紹了java獲取當(dāng)前時(shí)間的四種方法代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09
徹底理解Spring注解@Autowired實(shí)現(xiàn)原理
這篇文章主要為大家詳細(xì)的介紹了Spring注解@Autowired實(shí)現(xiàn)的原理,縝密的邏輯分析,實(shí)踐應(yīng)用示例操作說明,讓大家徹底的理解Spring注解@Autowired背后實(shí)現(xiàn)原理2022-03-03
SpringBoot多租戶配置與實(shí)現(xiàn)示例
本文詳細(xì)介紹了在SpringBoot中實(shí)現(xiàn)多租戶架構(gòu)的方法和步驟,包括配置數(shù)據(jù)源、Hibernate攔截器、租戶解析器等,以共享數(shù)據(jù)庫(kù)、共享數(shù)據(jù)表的方式,確保數(shù)據(jù)隔離和安全性,感興趣的可以了解一下2024-09-09
Java畢業(yè)設(shè)計(jì)實(shí)戰(zhàn)之平行志愿管理系統(tǒng)的實(shí)現(xiàn)
這是一個(gè)使用了java+Springboot+Maven+mybatis+Vue+Mysql開發(fā)的圖片平行志愿管理系統(tǒng),是一個(gè)畢業(yè)設(shè)計(jì)的實(shí)戰(zhàn)練習(xí),具有志愿管理該有的所有功能,感興趣的朋友快來看看吧2022-02-02
Java設(shè)計(jì)模式以虹貓藍(lán)兔的故事講解代理模式
代理模式是Java常見的設(shè)計(jì)模式之一。所謂代理模式是指客戶端并不直接調(diào)用實(shí)際的對(duì)象,而是通過調(diào)用代理,來間接的調(diào)用實(shí)際的對(duì)象2022-04-04
用Java實(shí)現(xiàn)一個(gè)靜態(tài)鏈表的方法步驟
這篇文章主要介紹了用Java實(shí)現(xiàn)一個(gè)靜態(tài)鏈表的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02
Java判斷object對(duì)象為空(包括null ,““)的方法
這篇文章主要介紹了Java判斷對(duì)象是否為空(包括null ,“”)的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-12-12
JavaWeb開發(fā)之【Tomcat 環(huán)境配置】MyEclipse+IDEA配置教程
這篇文章主要介紹了JavaWeb開發(fā)之【Tomcat 環(huán)境配置】MyEclipse+IDEA配置教程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10

