/* * Copyright (c) 2018-2019. Developed by Hedgecode. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.hedgecode.chess.qrcode; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Path; import java.nio.file.Paths; import java.util.EnumMap; import java.util.Map; import javax.imageio.ImageIO; import com.google.zxing.EncodeHintType; import com.google.zxing.WriterException; import com.google.zxing.client.j2se.MatrixToImageWriter; import com.google.zxing.common.BitMatrix; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import com.google.zxing.qrcode.encoder.ByteMatrix; import com.google.zxing.qrcode.encoder.Encoder; import com.google.zxing.qrcode.encoder.QRCode; import org.hedgecode.chess.img.ImageFormat; /** * Chess-specific QR Codes writer (image generator). * * @author Dmitry Samoshin aka gotty */ public class ChessQRCodeWriter { private static final int DEF_QUIET_ZONE_SIZE = 4; private static final int DEF_QRCODE_BIT_SIZE = 250; private static final int MIN_QRCODE_BIT_SIZE = 50; private static final ErrorCorrectionLevel DEF_ERR_CORR_LEVEL = ErrorCorrectionLevel.H; // todo: Q private static ChessQRCodeWriter _instance = new ChessQRCodeWriter(); protected ChessQRCodeWriter() { } public void write(ChessQRCodeMode mode, String contents, ImageFormat format, String qrCodeFilePath) throws ChessQRCodeException { write( mode, contents, format, Paths.get(qrCodeFilePath) ); } public void write(ChessQRCodeMode mode, String contents, ImageFormat format, Path qrCodeFile) throws ChessQRCodeException { write( mode, contents, format, DEF_QRCODE_BIT_SIZE, DEF_QRCODE_BIT_SIZE, qrCodeFile ); } public void write( ChessQRCodeMode mode, String contents, ImageFormat format, int width, int height, Path qrCodeFile) throws ChessQRCodeException { try { BufferedImage image = generate(mode, contents, width, height); if (!ImageIO.write(image, format.name(), qrCodeFile.toFile())) { throw new ChessQRCodeException( ChessQRCodeException.Type.WRITE, "write.output.qrcode.file", qrCodeFile.toFile().getName() ); } } catch (IOException e) { throw new ChessQRCodeException( ChessQRCodeException.Type.WRITE, "write.output.qrcode.file", e.getMessage() ); } } public void write(ChessQRCodeMode mode, String contents, ImageFormat format, OutputStream stream) throws ChessQRCodeException { try { BufferedImage image = generate(mode, contents); if (!ImageIO.write(image, format.name(), stream)) { throw new ChessQRCodeException( ChessQRCodeException.Type.WRITE, "write.output.qrcode.stream" ); } } catch (IOException e) { throw new ChessQRCodeException( ChessQRCodeException.Type.WRITE, "write.output.qrcode.stream", e.getMessage() ); } } public BufferedImage generate(ChessQRCodeMode mode, String contents) throws ChessQRCodeException { return generate(mode, contents, DEF_QRCODE_BIT_SIZE, DEF_QRCODE_BIT_SIZE); } public BufferedImage generate(ChessQRCodeMode mode, String contents, int width, int height) throws ChessQRCodeException { BitMatrix bitMatrix = encode( mode, contents, width, height, null ); return MatrixToImageWriter.toBufferedImage( bitMatrix ); } protected BitMatrix encode( ChessQRCodeMode mode, String contents, int width, int height, Map hints) throws ChessQRCodeException { if (mode == null) { throw new ChessQRCodeException( ChessQRCodeException.Type.WRITE, "write.qrcode.mode.null" ); } if (contents == null || contents.isEmpty()) { throw new ChessQRCodeException( ChessQRCodeException.Type.WRITE, "write.qrcode.contents.empty" ); } if (Math.min(width, height) < MIN_QRCODE_BIT_SIZE) { // todo: check for Version throw new ChessQRCodeException( ChessQRCodeException.Type.WRITE, "write.qrcode.size.incorrect" ); } String qrCodeContents = mode.name().concat(contents); int quietZone = DEF_QUIET_ZONE_SIZE; ErrorCorrectionLevel errorCorrectionLevel = DEF_ERR_CORR_LEVEL; Map newHints = new EnumMap<>(EncodeHintType.class); if (hints != null) { newHints.putAll(hints); if (!hints.containsKey(EncodeHintType.CHARACTER_SET)) { newHints.put(EncodeHintType.CHARACTER_SET, ChessQRCodeConstants.CHARSET.name()); } if (hints.containsKey(EncodeHintType.ERROR_CORRECTION)) { errorCorrectionLevel = ErrorCorrectionLevel.valueOf( hints.get(EncodeHintType.ERROR_CORRECTION).toString() ); } if (hints.containsKey(EncodeHintType.MARGIN)) { quietZone = Integer.parseInt( hints.get(EncodeHintType.MARGIN).toString() ); } } else { newHints.put(EncodeHintType.CHARACTER_SET, ChessQRCodeConstants.CHARSET.name()); } hints = newHints; QRCode qrCode; try { qrCode = Encoder.encode( qrCodeContents, errorCorrectionLevel, hints ); } catch (WriterException e) { throw new ChessQRCodeException( ChessQRCodeException.Type.WRITE, "write.zxing.qrcode.error", e.getMessage() ); } return renderMatrix( qrCode, width, height, quietZone ); } private BitMatrix renderMatrix(QRCode qrCode, int width, int height, int quietZone) throws ChessQRCodeException { ByteMatrix byteMatrix = ChessQRMatrixUtils.embedChessLogoPattern( qrCode.getMatrix() ); if (byteMatrix == null) { throw new ChessQRCodeException( ChessQRCodeException.Type.WRITE, "write.qrcode.chess.incorrect" ); } return ChessQRMatrixUtils.renderMatrix( byteMatrix, width, height, quietZone ); } public static ChessQRCodeWriter getInstance() { return _instance; } public static void main(String[] args) { try { ChessQRCodeWriter.getInstance().write( ChessQRCodeMode.FEN, "rnbqkbnr/ppp1pppp/8/3p4/4P3/8/PPPP1PPP/RNBQKBNR w KQkq d6 0 2", ImageFormat.PNG, "./qrcode.png" ); /* ChessQRCodeWriter.getInstance().write( ChessQRCodeMode.PGN, "1.e4 c6 2.d4 d5 3.Nc3 dxe4 4.Nxe4 Nd7 5.Ng5 Ngf6 6.Bd3 e6 7.N1f3 h6\n" + "8.Nxe6 Qe7 9.O-O fxe6 10.Bg6+ Kd8 {Каспаров встряхнул головой} \n" + "11.Bf4 b5 12.a4 Bb7 13.Re1 Nd5 14.Bg3 Kc8 15.axb5 cxb5 16.Qd3 Bc6 \n" + "17.Bf5 exf5 18.Rxe7 Bxe7 19.c4 1-0", ImageFormat.PNG, "./qrcode.png" ); */ } catch (ChessQRCodeException e) { System.out.println("Could not generate QR Code, Exception: " + e.getMessage()); } } }