[LIB-9] Add chesshog-qrcode module
[chesshog.git] / chesshog-qrcode / src / main / java / org / hedgecode / chess / qrcode / ChessQRCodeWriter.java
diff --git a/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeWriter.java b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeWriter.java
new file mode 100644 (file)
index 0000000..bc11750
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * 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.ChessQRCodeConstants;
+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 ErrorCorrectionLevel DEF_ERR_CORR_LEVEL = ErrorCorrectionLevel.H; // todo: Q
+
+    private static ChessQRCodeWriter _instance = new ChessQRCodeWriter();
+
+    private 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
+        );
+    }
+
+    private BitMatrix encode(
+            ChessQRCodeMode mode,
+            String contents,
+            int width, int height,
+            Map<EncodeHintType,?> 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 (width < 0 || height < 0) { // todo
+            throw new ChessQRCodeException(
+                    ChessQRCodeException.Type.WRITE, "write.qrcode.size.incorrect"
+            );
+        }
+
+        int quietZone = DEF_QUIET_ZONE_SIZE;
+        ErrorCorrectionLevel errorCorrectionLevel = DEF_ERR_CORR_LEVEL;
+
+        Map<EncodeHintType,Object> 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;
+
+        String qrCodeText = mode.name().concat(contents);
+
+        QRCode qrCode;
+        try {
+            qrCode = Encoder.encode(
+                    qrCodeText, 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,
+                    "./MyQRCode.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,
+                    "./MyQRCode.png"
+            );
+*/
+        } catch (ChessQRCodeException e) {
+            System.out.println("Could not generate QR Code, Exception: " + e.getMessage());
+        }
+    }
+
+}