📜 ⬆️ ⬇️

We generate color QR-code with Java logo

Some time ago I needed to generate several QR codes for each user of the system. And so that it was interesting to scan this code, it was decided to add a logo to it.
QR code example
How to do this, read on.


Foreword

QR codes can be found everywhere, but how to distinguish them from each other? QR codes are gaining popularity all the time, and no, no, yes, and there will be a few pieces nearby. The spectacle is not a pleasant one - which one to scan first? And in general, why scan something from which it begins to ruffle in the eyes?
The solution of such a task can be the personalization of a QR code: non-standard colors, a logo, or an explanatory inscription a little below the code itself, by which one can understand whether the viewer is interested in it or not.
Many people have seen beautiful QR codes (and who have not seen, can look at Habré or on a third-party resource ), but I should make a reservation - to create one, you need to invest more resources into the image generation algorithm, or draw such code in Photoshop, but it will be a single instance, and for most of us it will not work (unless, of course, there is a general need to generate them yourself).

How is this implemented?

The creators of QR codes did not expect that we would insert our pictures into the coded messages, which are the codes themselves, but they provided for the possibility of a high amount of recovery information - the code can contain up to 30% of the latter. The larger it is, the thicker the picture is, but the more likely it is that the user decodes the corrupted code. And we will spoil it with a logo.
To generate the code, the ZXing library was used - this is an open source library for processing various 1D / 2D barcodes, which, besides Java, has ports to other languages.
A feature of this library is that it is divided into modules and distributed in source codes that need to be compiled. But, fortunately, it is in the maven repository - the core module was used to generate, and the java se module was used to validate the codes.
The standard classes from the java.awt package (JavaSE) were used to work with graphics.
')
For the cause!

For experiments, a small console program was made, which can be found on a githaba - a prototype repository , which I will analyze in this section.

Anyone who just needs a QR code can write the following:
BitMatrix matrix = new MultiFormatWriter().encode("text to encode", BarcodeFormat.QR_CODE, width, height); MatrixToImageWriter.writeToFile(matrix, filename.substring(filename.lastIndexOf('.')+1), new File(filename)); 

Otherwise, you shouldn’t do that - by default, the library will add little recovery information, and even if after inserting a logo the image is decrypted on our computer, then it can already be considered wrong from the camera. Therefore, it will be good to add a maximum of recovery information, and since we will change colors and a picture, we don’t need to hurry to save the result:
 Hashtable<EncodeHintType, ErrorCorrectionLevel> hintMap = new Hashtable<EncodeHintType, ErrorCorrectionLevel>(); hintMap.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); QRCodeWriter qrCodeWriter = new QRCodeWriter(); BitMatrix bitMatrix = qrCodeWriter.encode(content, BarcodeFormat.QR_CODE, qrCodeSize, qrCodeSize, hintMap); 

Creating a picture from a matrix of code is done in a loop - we create a picture of the appropriate size and, passing the code matrix, we display the presence of a bit in the matrix onto the picture as an informative pixel. During this action, you can set the background color and code color:
 int matrixWidth = bitMatrix.getWidth(); BufferedImage image = new BufferedImage(matrixWidth, matrixWidth, BufferedImage.TYPE_INT_RGB); image.createGraphics(); Graphics2D graphics = (Graphics2D) image.getGraphics(); graphics.setColor(Color.white); graphics.fillRect(0, 0, matrixWidth, matrixWidth); Color mainColor = new Color(51, 102, 153); graphics.setColor(mainColor); //Write Bit Matrix as image for (int i = 0; i < matrixWidth; i++) { for (int j = 0; j < matrixWidth; j++) { if (bitMatrix.get(i, j)) { graphics.fillRect(i, j, 1, 1); } } } 

Well, now, when we operate with a picture, and not a matrix of ones and zeros, we are very comfortable with placing the logo in the center, having previously adjusted its resolution so as not to overlap the entire code if it is too large:
 BufferedImage logo = ImageIO.read( this.getLogoFile()); double scale = calcScaleRate(image, logo); logo = getScaledImage( logo, (int)( logo.getWidth() * scale), (int)( logo.getHeight() * scale) ); graphics.drawImage( logo, image.getWidth()/2 - logo.getWidth()/2, image.getHeight()/2 - logo.getHeight()/2, image.getWidth()/2 + logo.getWidth()/2, image.getHeight()/2 + logo.getHeight()/2, 0, 0, logo.getWidth(), logo.getHeight(), null); private BufferedImage getScaledImage(BufferedImage image, int width, int height) throws IOException { int imageWidth = image.getWidth(); int imageHeight = image.getHeight(); double scaleX = (double)width/imageWidth; double scaleY = (double)height/imageHeight; AffineTransform scaleTransform = AffineTransform.getScaleInstance(scaleX, scaleY); AffineTransformOp bilinearScaleOp = new AffineTransformOp( scaleTransform, AffineTransformOp.TYPE_BILINEAR); return bilinearScaleOp.filter( image, new BufferedImage(width, height, image.getType())); } 

After our abuse of the code, you should definitely check it out for correctness - is there enough recovery information for an ideal camera? And if that's enough, it's time to save the Katinka and give it to the user:
 if ( isQRCodeCorrect(content, image)) { ImageIO.write(image, imageFormat, this.getGeneratedFileStream()); } private boolean isQRCodeCorrect(String content, BufferedImage image){ boolean result = false; Result qrResult = decode(image); if (qrResult != null && content != null && content.equals(qrResult.getText())){ result = true; } return result; } private Result decode(BufferedImage image){ if (image == null) { return null; } try { LuminanceSource source = new BufferedImageLuminanceSource(image); BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); Result result = new MultiFormatReader().decode(bitmap, Collections.EMPTY_MAP); return result; } catch (NotFoundException nfe) { return null; } } 


The goal has been achieved - QR code generated. Thanks for attention!

Source: https://habr.com/ru/post/149172/


All Articles