先來看看效果:
- 實現:
- 生成指定文字內容的二維碼
- 二維碼中間嵌入LOGO
- 二維碼做圓角和白色邊框處理
新需求不斷, 這不, 又來了個想生成帶用戶頭像的需求. 蠻簡單的… 在這裏造完輪子分享給大家
因爲公司後端主要是Kotlin寫的. 請大家自行翻譯.
Kotlin和Java本質是一種東西… 有關kotlin寫後端是否合適…自己看吧. 人家官網直接支持
不廢話了, 上代碼
需引入:
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.3.3</version>
</dependency>
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.8</version>
</dependency>
運行方法:
fun main() {
val resBase64 = QrCodeUtil.generateLogoQrCode("I Love U!", "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")
println(resBase64)
}
即可得到我們想要的二維碼Logo圖片!
因爲Base64字符串非常通用, 所以這裏的圖片入參都爲Base64字符串.
import com.google.zxing.BarcodeFormat
import com.google.zxing.EncodeHintType
import com.google.zxing.MultiFormatWriter
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
import net.coobird.thumbnailator.Thumbnails
import sun.misc.BASE64Decoder
import sun.misc.BASE64Encoder
import java.awt.AlphaComposite
import java.awt.BasicStroke
import java.awt.Color
import java.awt.RenderingHints
import java.awt.geom.RoundRectangle2D
import java.awt.image.BufferedImage
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import javax.imageio.ImageIO
class QrCodeUtil {
companion object {
/**
* 生成帶logo的二維碼
* @param content 內容
* @param logoBase64 內嵌logo圖片
* @param qrColor16bit 16位二維碼顏色 (默認黑色)
* @param bgColor16bit 16位背景顏色 (默認白色)
*/
fun generateLogoQrCode(content: String,
logoBase64: String,
qrColor16bit: Int = -0x1000000,
bgColor16bit: Int = -0x1,
width: Int = 258,
height: Int = 258): String {
// logo base64 轉 bufferedImg
val logoImg = base64ToBufferedImage(logoBase64)
// 二維碼圖片
val qrImg = generateBufferedQrImg(
content = content,
width = width,
height = height,
qrColor16bit = qrColor16bit,
bgColor16bit = bgColor16bit)
// 組合好的二維碼圖片
val finalImg = addLogoToQrCode(qrImg, logoImg)
// 圖片轉base64, 剔除換行
return bufferedImageToBase64(finalImg).replace("\r", "").replace("\n", "")
}
/**
* 生成二維碼bufferedImage圖片
*
* @param content 編碼內容
* @param barcodeFormat 編碼類型
* @param width 圖片寬度 (默認258)
* @param height 圖片高度 (默認258)
* @param hints 二維碼配置
* @return
*/
private fun generateBufferedQrImg(content: String,
barcodeFormat: BarcodeFormat = BarcodeFormat.QR_CODE,
width: Int,
height: Int,
qrColor16bit: Int,
bgColor16bit: Int,
hints: Map<EncodeHintType, Any> = defaultHint()): BufferedImage {
val multiFormatWriter = MultiFormatWriter()
val bm = multiFormatWriter.encode(content, barcodeFormat, width, height, hints)
// 創建bufferedImage對象
val image = BufferedImage(width, height, BufferedImage.TYPE_INT_RGB)
// 開始利用二維碼數據創建Bitmap圖片,分別設爲黑(0xFFFFFFFF)白(0xFF000000)兩色
for (x in 0 until width) {
for (y in 0 until height) {
image.setRGB(x, y, if (bm.get(x, y)) qrColor16bit else bgColor16bit)
}
}
return image
}
/**
* 默認二維碼配置
*/
private fun defaultHint(): Map<EncodeHintType, Any> {
val hints = HashMap<EncodeHintType, Any>()
// 二維碼糾錯級別(H爲最高)
hints[EncodeHintType.ERROR_CORRECTION] = ErrorCorrectionLevel.H
// 編碼
hints[EncodeHintType.CHARACTER_SET] = "utf-8"
hints[EncodeHintType.MARGIN] = 0
return hints
}
/**
* 給二維碼圖片添加LOGO
* @param img 二維碼原圖
* @param
*/
private fun addLogoToQrCode(qrImg: BufferedImage,
logoImg: BufferedImage): BufferedImage {
/**
* 讀取二維碼圖片, 構建繪圖對象
*/
val graphic = qrImg.createGraphics()
/**
* 爲logo進行壓縮處理
*/
val logoThumb =
Thumbnails.of(logoImg).scale(0.5).outputQuality(1f).asBufferedImage()
/**
* 設置logo寬高
*/
val logoWidth = if (logoThumb.width > qrImg.width * 3 / 10) qrImg.width * 3 / 10 else logoThumb.width
val logoHeight = if (logoThumb.height > qrImg.height * 3 / 10) qrImg.height * 3 / 10 else logoThumb.height
/**
* logo 放在中心
*/
val logoX = (qrImg.width - logoWidth) / 2
val logoY = (qrImg.height - logoHeight) / 2
/**
* 對LOGO進行裁剪並加白邊框
*/
val logoCut = imageCutRoundAndBackGround(logoThumb)
/**
* 繪製圖片
*/
graphic.drawImage(logoCut, logoX, logoY, logoWidth, logoHeight, null)
graphic.dispose()
qrImg.flush()
logoThumb.flush()
return qrImg
}
/**
* Base64字符串轉BufferedImage
*/
private fun base64ToBufferedImage(base64: String): BufferedImage {
val decoder = BASE64Decoder()
val logoByteData = decoder.decodeBuffer(base64)
val inputStream = ByteArrayInputStream(logoByteData)
return ImageIO.read(inputStream)
}
/**
* BufferedImage轉Base64字符串
*/
private fun bufferedImageToBase64(image: BufferedImage): String {
/**
* BufferedImage轉byte[]
*/
val bos = ByteArrayOutputStream()
ImageIO.write(image, "jpg", bos)
val byteArray = bos.toByteArray()
/**
* byte[]轉base64
*/
val encoder = BASE64Encoder()
return encoder.encode(byteArray)
}
/**
* 圖片切圓角且帶邊框
* 本方法待優化
*/
private fun imageCutRoundAndBackGround(img: BufferedImage,
border: Int = 0,
padding: Int = 5): BufferedImage {
val radius = (img.width + img.height) / 6
val width = img.width
val height = img.height
val canvasWidth = width + padding * 2
val canvasHeight = height + padding * 2
val resImage = BufferedImage(canvasWidth, canvasHeight, BufferedImage.TYPE_INT_ARGB)
val gs = resImage.createGraphics()
gs.composite = AlphaComposite.Src
gs.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
gs.color = Color.WHITE
gs.fill(RoundRectangle2D.Float(0f, 0f, canvasWidth.toFloat(), canvasHeight.toFloat(), radius.toFloat(), radius.toFloat()))
gs.composite = AlphaComposite.SrcAtop
gs.drawImage(setClip(img, radius), padding, padding, null)
if (border != 0) {
gs.color = Color.GRAY
gs.stroke = BasicStroke(border.toFloat())
gs.drawRoundRect(padding, padding, canvasWidth - 2 * padding, canvasHeight - 2 * padding, radius, radius)
}
gs.dispose()
return resImage
}
private fun setClip(img: BufferedImage, radius: Int): BufferedImage {
val width = img.width
val height = img.height
val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
val gs = image.createGraphics()
gs.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
gs.clip = RoundRectangle2D.Double(0.0, 0.0, width.toDouble(), height.toDouble(), radius.toDouble(), radius.toDouble())
gs.drawImage(img, 0, 0, null)
gs.dispose()
return image
}
}
}