--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ ~ 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.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.hedgecode.chess</groupId>
+ <artifactId>chesshog</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>chesshog-qrcode</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ <packaging>jar</packaging>
+
+ <name>Hedgecode ChessHog QR Code</name>
+ <description>
+ Hedgecode ChessHog QR Code Module.
+ </description>
+
+ <properties>
+ <chessHogVersion>0.1-SNAPSHOT</chessHogVersion>
+ <zxingVersion>3.3.3</zxingVersion>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.hedgecode.chess</groupId>
+ <artifactId>chesshog</artifactId>
+ <version>${chessHogVersion}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.hedgecode.chess</groupId>
+ <artifactId>chesshog-core</artifactId>
+ <version>${chessHogVersion}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.zxing</groupId>
+ <artifactId>core</artifactId>
+ <version>${zxingVersion}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.zxing</groupId>
+ <artifactId>javase</artifactId>
+ <version>${zxingVersion}</version>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
--- /dev/null
+/*
+ * 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;
+
+/**
+ * Chess QR Code Main Application.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class ChessQRCodeApp {
+
+ public static void main(String[] args) {
+
+
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Store of Chess QR Code module constants.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class ChessQRCodeConstants {
+
+ public static final Charset CHARSET = StandardCharsets.UTF_8;
+
+ public static final String LOCALE_BUNDLE_FILE = "org.hedgecode.chess.qrcode.LocalStrings";
+
+ private ChessQRCodeConstants() {
+ throw new AssertionError(
+ "No org.hedgecode.chess.ChessQRCodeConstants instances!"
+ );
+ }
+
+}
--- /dev/null
+/*
+ * 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.util.ResourceBundle;
+
+import org.hedgecode.chess.ChessQRCodeConstants;
+
+/**
+ * Chess-specific QR Codes read/write/parse Exception.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class ChessQRCodeException extends Exception {
+
+ public enum Type {
+
+ READ ("[Read Error] "),
+ WRITE ("[Write Error] "),
+ PARSE ("[Parse Error] ");
+
+ private String msg;
+
+ Type(String msg) {
+ this.msg = msg;
+ }
+ }
+
+ private static final ResourceBundle LOCALE_BUNDLE =
+ ResourceBundle.getBundle(ChessQRCodeConstants.LOCALE_BUNDLE_FILE);
+
+ private Type type;
+ private String localeKey;
+ private String message;
+
+ public ChessQRCodeException(Type type, String localeKey) {
+ this.type = type;
+ this.localeKey = localeKey;
+ this.message = null;
+ }
+
+ public ChessQRCodeException(Type type, String localeKey, String message) {
+ this.type = type;
+ this.localeKey = localeKey;
+ this.message = message;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ public String getLocaleKey() {
+ return localeKey;
+ }
+
+ public String getMessage() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(type.msg);
+ if (localeKey != null)
+ sb.append(
+ LOCALE_BUNDLE.getString(localeKey)
+ );
+ if (message != null)
+ sb.append(": ").append(message);
+ return sb.toString();
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+
+/**
+ * Mode of data type stored in Chess-specific QR Codes.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public enum ChessQRCodeMode {
+
+ FEN,
+ TCD,
+ PGN,
+ TCG;
+
+ static final int CODE_LENGTH = 3;
+
+ boolean isPosition() {
+ return FEN.equals(this) || TCD.equals(this);
+ }
+
+ boolean isGame() {
+ return PGN.equals(this) || TCG.equals(this);
+ }
+
+ public static ChessQRCodeMode byCode(String code) {
+ for (ChessQRCodeMode mode : ChessQRCodeMode.values()) {
+ if (mode.name().equals(code))
+ return mode;
+ }
+ return null;
+ }
+
+}
--- /dev/null
+/*
+ * 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.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.EnumMap;
+import java.util.Map;
+
+import javax.imageio.ImageIO;
+
+import com.google.zxing.BinaryBitmap;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.ReaderException;
+import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
+import com.google.zxing.common.DecoderResult;
+import com.google.zxing.common.DetectorResult;
+import com.google.zxing.common.HybridBinarizer;
+import com.google.zxing.qrcode.decoder.Decoder;
+import com.google.zxing.qrcode.detector.Detector;
+
+import org.hedgecode.chess.ChessQRCodeConstants;
+
+/**
+ * Chess-specific QR Codes reader (image decoder).
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class ChessQRCodeReader {
+
+ private final Decoder decoder;
+
+ private static ChessQRCodeReader _instance = new ChessQRCodeReader();
+
+ private ChessQRCodeReader() {
+ decoder = new Decoder();
+ }
+
+ public ChessQRResult read(File qrCodeImageFile) throws ChessQRCodeException {
+ BufferedImage qrCodeImage;
+ try {
+ qrCodeImage = ImageIO.read(qrCodeImageFile);
+ } catch (IOException e) {
+ throw new ChessQRCodeException(
+ ChessQRCodeException.Type.READ, "read.input.qrcode.file", qrCodeImageFile.getName()
+ );
+ }
+ return read(qrCodeImage);
+ }
+
+ public ChessQRResult read(InputStream qrCodeInputStream) throws ChessQRCodeException {
+ BufferedImage qrCodeImage;
+ try {
+ qrCodeImage = ImageIO.read(qrCodeInputStream);
+ } catch (IOException e) {
+ throw new ChessQRCodeException(
+ ChessQRCodeException.Type.READ, "read.input.qrcode.stream", e.getMessage()
+ );
+ }
+ return read(qrCodeImage);
+ }
+
+ public ChessQRResult read(URI qrCodeUri) throws ChessQRCodeException {
+ BufferedImage qrCodeImage;
+ try {
+ qrCodeImage = ImageIO.read(qrCodeUri.toURL());
+ } catch (IOException e) {
+ throw new ChessQRCodeException(
+ ChessQRCodeException.Type.READ, "read.input.qrcode.url", qrCodeUri.toASCIIString()
+ );
+ }
+ return read(qrCodeImage);
+ }
+
+ public ChessQRResult read(BufferedImage qrCodeImage) throws ChessQRCodeException {
+ BinaryBitmap bitmap = new BinaryBitmap(
+ new HybridBinarizer(
+ new BufferedImageLuminanceSource(qrCodeImage)
+ )
+ );
+ return decode(bitmap, null);
+ }
+
+ private ChessQRResult decode(BinaryBitmap image, Map<DecodeHintType,?> hints)
+ throws ChessQRCodeException
+ {
+ Map<DecodeHintType,Object> newHints = new EnumMap<>(DecodeHintType.class);
+ if (hints != null) {
+ newHints.putAll(hints);
+ if (!hints.containsKey(DecodeHintType.CHARACTER_SET))
+ newHints.put(DecodeHintType.CHARACTER_SET, ChessQRCodeConstants.CHARSET.name());
+ } else {
+ newHints.put(DecodeHintType.CHARACTER_SET, ChessQRCodeConstants.CHARSET.name());
+ }
+ hints = newHints;
+
+ DecoderResult decoderResult;
+ try {
+ DetectorResult detectorResult = new Detector(
+ image.getBlackMatrix()
+ ).detect(hints);
+
+ decoderResult = decoder.decode(
+ detectorResult.getBits(), hints
+ );
+ } catch (ReaderException e) {
+ throw new ChessQRCodeException(
+ ChessQRCodeException.Type.READ, "read.zxing.qrcode.error", e.getMessage()
+ );
+ }
+
+ ChessQRCodeMode mode = ChessQRCodeMode.byCode(
+ decoderResult.getText().substring(0, ChessQRCodeMode.CODE_LENGTH)
+ );
+
+ if (mode == null) {
+ throw new ChessQRCodeException(
+ ChessQRCodeException.Type.READ, "read.unknown.chess.mode"
+ );
+ }
+
+ return new ChessQRResult(
+ mode,
+ decoderResult.getText().substring(ChessQRCodeMode.CODE_LENGTH)
+ );
+/*
+ ResultPoint[] points = detectorResult.getPoints();
+
+ if (decoderResult.getOther() instanceof QRCodeDecoderMetaData) {
+ ((QRCodeDecoderMetaData) decoderResult.getOther()).applyMirroredCorrection(points);
+ }
+
+ Result result = new Result(
+ decoderResult.getText(),
+ decoderResult.getRawBytes(),
+ points,
+ BarcodeFormat.QR_CODE
+ );
+
+ List<byte[]> byteSegments = decoderResult.getByteSegments();
+ if (byteSegments != null) {
+ result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, byteSegments);
+ }
+ String ecLevel = decoderResult.getECLevel();
+ if (ecLevel != null) {
+ result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel);
+ }
+ if (decoderResult.hasStructuredAppend()) {
+ result.putMetadata(
+ ResultMetadataType.STRUCTURED_APPEND_SEQUENCE,
+ decoderResult.getStructuredAppendSequenceNumber()
+ );
+ result.putMetadata(
+ ResultMetadataType.STRUCTURED_APPEND_PARITY,
+ decoderResult.getStructuredAppendParity()
+ );
+ }
+ return result;
+*/
+ }
+
+ public static ChessQRCodeReader getInstance() {
+ return _instance;
+ }
+
+
+ public static void main(String[] args) {
+ try {
+ ChessQRResult qrCodeResult = ChessQRCodeReader.getInstance().read(
+ new File("MyQRCode.png")
+ );
+ System.out.println("Decoded format = " + qrCodeResult.getMode());
+ System.out.println("Decoded text = " + qrCodeResult.getContents());
+ } catch (ChessQRCodeException e) {
+ System.out.println("Could not decode QR Code. Exception: " + e.getMessage());
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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());
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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 com.google.zxing.common.BitMatrix;
+import com.google.zxing.qrcode.encoder.ByteMatrix;
+
+/**
+ * Chess-specific QR Codes binary matrix accessory utils.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public final class ChessQRMatrixUtils {
+
+/*
+ private static final int[][] CHESS_LOGO_PATTERN = {
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0},
+ {0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0},
+ {0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
+ {0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
+ {0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
+ {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1},
+ {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1},
+ {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1},
+ {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1},
+ {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
+ {0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1},
+ };
+*/
+
+
+ private static final int[][] CHESS_LOGO_PATTERN = {
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0},
+ {0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1},
+ {0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1},
+ {0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1},
+ {0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1},
+ {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1},
+ {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1},
+ {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1},
+ {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1},
+ {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1},
+ {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
+ {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
+ };
+
+/*
+ private static final int[][] CHESS_LOGO_PATTERN = {
+ {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
+ {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
+ {1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1},
+ {1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1},
+ {1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1},
+ {1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1},
+ {1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1},
+ {1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1},
+ {1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1},
+ {1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1},
+ {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
+ {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
+ };
+*/
+
+ private static final int CHESS_LOGO_PATTERN_LENGTH = CHESS_LOGO_PATTERN[0].length;
+
+
+ static ByteMatrix embedChessLogoPattern(ByteMatrix byteMatrix) {
+ if (byteMatrix == null || byteMatrix.getWidth() < CHESS_LOGO_PATTERN_LENGTH + 14) // todo: 14
+ return null;
+
+ int xStart = (byteMatrix.getWidth() - CHESS_LOGO_PATTERN_LENGTH) / 2;
+ int yStart = (byteMatrix.getHeight() - CHESS_LOGO_PATTERN_LENGTH) / 2;
+
+ for (int y = 0; y < CHESS_LOGO_PATTERN_LENGTH; ++y) {
+ int[] patternY = CHESS_LOGO_PATTERN[y];
+ for (int x = 0; x < CHESS_LOGO_PATTERN_LENGTH; ++x) {
+ byteMatrix.set(xStart + x, yStart + y, patternY[x]);
+ }
+ }
+ return byteMatrix;
+ }
+
+
+ static boolean checkChessLogoPattern(ByteMatrix byteMatrix) {
+
+
+ return false;
+ }
+
+ static boolean checkChessLogoPattern(BitMatrix bitMatrix) {
+
+
+ return false;
+ }
+
+
+ static BitMatrix renderMatrix(ByteMatrix byteMatrix, int width, int height, int quietZone) {
+ int inputWidth = byteMatrix.getWidth();
+ int inputHeight = byteMatrix.getHeight();
+ int qrWidth = inputWidth + (quietZone * 2);
+ int qrHeight = inputHeight + (quietZone * 2);
+ int outputWidth = Math.max(width, qrWidth);
+ int outputHeight = Math.max(height, qrHeight);
+
+ int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);
+ int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
+ int topPadding = (outputHeight - (inputHeight * multiple)) / 2;
+
+ BitMatrix bitMatrix = new BitMatrix(outputWidth, outputHeight);
+
+ for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {
+ for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
+ if (byteMatrix.get(inputX, inputY) == 1) {
+ bitMatrix.setRegion(outputX, outputY, multiple, multiple);
+ }
+ }
+ }
+ return bitMatrix;
+ }
+
+}
--- /dev/null
+/*
+ * 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 org.hedgecode.chess.Parsers;
+import org.hedgecode.chess.game.Game;
+import org.hedgecode.chess.position.ParseException;
+import org.hedgecode.chess.position.Parser;
+import org.hedgecode.chess.position.Position;
+
+/**
+ * Result storage for Chess-specific QR Codes data.
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class ChessQRResult {
+
+ private ChessQRCodeMode mode;
+ private String contents;
+ private Position position;
+ private Game game;
+
+ ChessQRResult(ChessQRCodeMode mode, String contents) {
+ this.mode = mode;
+ this.contents = contents;
+ }
+
+ ChessQRResult(Position position) {
+ this.position = position;
+ }
+
+ ChessQRResult(Game game) {
+ this.game = game;
+ }
+
+ public ChessQRCodeMode getMode() {
+ return mode;
+ }
+
+ public String getContents() {
+ return contents;
+ }
+
+ public Position getPosition() throws ChessQRCodeException {
+ if (position == null) {
+ if (mode != null && mode.isPosition()) {
+ try {
+ position = getPositionParser().parse(contents);
+ } catch (ParseException e) {
+ throw new ChessQRCodeException(
+ ChessQRCodeException.Type.PARSE, null, e.getMessage()
+ );
+ }
+ }
+ }
+ return position;
+ }
+
+ public Game getGame() {
+ // todo
+ return game;
+ }
+
+ private Parser getPositionParser() throws ChessQRCodeException {
+ switch (mode) {
+ case FEN:
+ return Parsers.FEN.parser();
+ case TCD:
+ return Parsers.TCD.parser();
+ default:
+ throw new ChessQRCodeException(
+ ChessQRCodeException.Type.PARSE, "parse.unknown.position.format"
+ );
+ }
+ }
+
+}
--- /dev/null
+# 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.
+
+# Default localized string information
+# Localized for Locale en_US
+
+parse.unknown.position.format=Unknown chess position format for parsing
+read.input.qrcode.file=Failed to read QR Code from file
+read.input.qrcode.stream=Failed to read QR Code from input stream
+read.input.qrcode.url=Failed to read QR Code from URL
+read.unknown.chess.mode=Unknown chess mode in the QR Code
+read.zxing.qrcode.error=Error decoding of QR code
+write.output.qrcode.file=Failed to write QR Code to file
+write.output.qrcode.stream=Failed to write QR Code to output stream
+write.qrcode.mode.null=Input chess mode not specified
+write.qrcode.contents.empty=Input data is empty
+write.qrcode.size.incorrect=Incorrect QR Code size specified
+write.qrcode.chess.incorrect=Specified size is not suitable for chess QR Code
+write.zxing.qrcode.error=Error encoding of QR code
--- /dev/null
+# 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.
+
+# Localized for Locale ru_RU
+
+parse.unknown.position.format=\u041D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u044B\u0439 \u0444\u043E\u0440\u043C\u0430\u0442 \u0448\u0430\u0445\u043C\u0430\u0442\u043D\u043E\u0439 \u043F\u043E\u0437\u0438\u0446\u0438\u0438
+read.input.qrcode.file=\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u043E\u043B\u0443\u0447\u0438\u0442\u044C QR \u043A\u043E\u0434 \u0438\u0437 \u0444\u0430\u0439\u043B\u0430
+read.input.qrcode.stream=\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u043E\u043B\u0443\u0447\u0438\u0442\u044C QR \u043A\u043E\u0434 \u0438\u0437 \u0432\u0445\u043E\u0434\u043D\u043E\u0433\u043E \u043F\u043E\u0442\u043E\u043A\u0430
+read.input.qrcode.url=\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u043E\u043B\u0443\u0447\u0438\u0442\u044C QR \u043A\u043E\u0434 \u0438\u0437 URL
+read.unknown.chess.mode=\u041D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u044B\u0439 \u0442\u0438\u043F \u0448\u0430\u0445\u043C\u0430\u0442\u043D\u043E\u0439 \u0437\u0430\u043F\u0438\u0441\u0438 \u0432 QR \u043A\u043E\u0434\u0435
+read.zxing.qrcode.error=\u041E\u0448\u0438\u0431\u043A\u0430 \u0434\u0435\u043A\u043E\u0434\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F QR \u043A\u043E\u0434\u0430
+write.output.qrcode.file=\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u043F\u0438\u0441\u0430\u0442\u044C QR \u043A\u043E\u0434 \u0432 \u0444\u0430\u0439\u043B
+write.output.qrcode.stream=\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u043F\u0438\u0441\u0430\u0442\u044C QR \u043A\u043E\u0434 \u0432 \u0432\u044B\u0445\u043E\u0434\u043D\u043E\u0439 \u043F\u043E\u0442\u043E\u043A
+write.qrcode.mode.null=\u041D\u0435 \u0437\u0430\u0434\u0430\u043D \u0442\u0438\u043F \u0432\u0445\u043E\u0434\u043D\u044B\u0445 \u0434\u0430\u043D\u043D\u044B\u0445
+write.qrcode.contents.empty=\u0412\u0445\u043E\u0434\u043D\u044B\u0435 \u0434\u0430\u043D\u043D\u044B\u0435 \u043F\u0443\u0441\u0442\u044B
+write.qrcode.size.incorrect=\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u043E \u0437\u0430\u0434\u0430\u043D \u0440\u0430\u0437\u043C\u0435\u0440 QR \u043A\u043E\u0434\u0430
+write.qrcode.chess.incorrect=\u0417\u0430\u0434\u0430\u043D\u043D\u044B\u0439 \u0440\u0430\u0437\u043C\u0435\u0440 \u043D\u0435 \u043F\u043E\u0434\u0445\u043E\u0434\u0438\u0442 \u0434\u043B\u044F \u0448\u0430\u0445\u043C\u0430\u0442\u043D\u043E\u0433\u043E QR \u043A\u043E\u0434\u0430
+write.zxing.qrcode.error=\u041E\u0448\u0438\u0431\u043A\u0430 \u043A\u043E\u0434\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F QR \u043A\u043E\u0434\u0430