Событие [21] |
Шоу бизнес [5] |
Мода и Стиль [2] |
Девушка дня [5] |
Авто / Мото [4] |
Игры [15] |
Кино [15] |
Музыка [14] |
Hi-Tech [35] |
Мобильные новости [27] |
Файлы (Софт) [2] |
Приколы / Анекдоты [0] |
Видео [6] |
Необъяснимое [40] |
Разное [35] |
17:29 Генерируем цветной QR-код с логотипом на Java | |
Некоторое время назад мне потребовалось генерировать несколько QR-кодов для каждого пользователя системы. А чтобы было интересно сканировать этот код, было решено добавить в него логотип. Как это сделать читайте дальше. Предисловие QR-коды можно встретить везде, но как их отличить друг от друга? QR-коды всё время завоёвывают популярность, и нет-нет, да и встретятся несколько штук рядом. Зрелище это не из приятных — какой сканировать первым? И вообще, зачем сканировать то, от чего начинает рябить в глазах? Решением такой задачи может служить персонализирование QR-кода: нестандартные цвета, логотип, или пояснительная надпись немного ниже самого кода, по которой можно понять — интересно ли зрителю это или нет. Навеняка многие видели красивые QR-коды (а кто не видел, может посмотреть тут), но мне стоит оговориться — чтобы создать такой, нужно вложить либо большие ресурсы в алгоритм генерации картинки, либо нарисовать такой код в фотошопе, но это будет единичный экземпляр, и для большинства из нас он не годится (если, конечно, есть вообще необходимость генерировать их самому). Как это реализовано? Создатели QR-кодов не расчитывали, что мы будем вставлять свои картинки в закодированные сообщения, коими являются сами коды, но они предусмотрели возможность высокого объёма восстановительной информации — код может содержать до 30% последней. Чем её больше — тем гуще картинка, но больше шансов, что пользователь раскодирует испорченный код. А портить мы его будем логотипом. Для генерации кода использовалась библиотека ZXing — это open source библиотека для обработки различных 1D/2D штрихкодов, которая, кроме Java, имеет порты на другие языки. Особенностью этой библиотеки является то, что она разбита на модули и распространяется в исходных кодах, которые необходимо компилировать. Но, к счастью, она есть в мавен репозитории — модуль core использовался для генерации, и модуль java se использовался для валидации кодов. Для работы с графикой были использованы стандартные классы из пакета java.awt (JavaSE). За дело! Для экспериментов была сделана небольшаю консольная программа, которую можно найти на гитхабе — репозиторий опытного образца, которую я и разберу в этом разделе. Тот, кому просто нужен QR-код, может написать следующее: Code BitMatrix matrix = new MultiFormatWriter().encode("text to encode", BarcodeFormat.QR_CODE, width, height); MatrixToImageWriter.writeToFile(matrix, filename.substring(filename.lastIndexOf('.')+1), new File(filename)); В противном случае так делать не стоит — по умолчанию библиотека добавит мало восстановительной информации, и даже если после вставки логотипа картинка расшифруется у нас на компьютере, то с фотокамеры она уже может считаться неправильно. По-этому будет хорошим тоном добавить максимум восстановительной информации, а раз мы будем изменять цвета и картинку, то не нужно спешить сохранять результат: Code 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); Создание картинки из матрицы кода делается в цикле — создаём картинку соответствующего размера и, проходя матрицу кода, отображаем наличие бита в матрице на картинку как информативный пиксель. Во время этого действа можно задать цвет фона и цвет кода: Code 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); } } } Ну вот, теперь, когда мы оперируем картинкой, а не матрицей единиц и нулей, нам очень даже удобно и логотип в центр поместить, предварительно поправив его разрешение, чтобы не перекрывать весь код в случае слишком большого размера: Code 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())); } После нашего надругательства над кодом, обязательно стоит его проверить на правильность — хватит ли восстановительной информации для идеальной фотокамеры? И если хватит, то пора сохранить катинку и отдать её пользователю: Code 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; } } Поставленная цель достигнута — QR код сгенерирован. Спасибо за внимание! | |
|
Всего комментариев: 0 | |