From 1fbf14530416c16f1c6fa5ee3e589482dd722a5d Mon Sep 17 00:00:00 2001 From: gotty Date: Fri, 19 Apr 2019 04:04:13 +0300 Subject: [PATCH] [LIB-9] Add chesshog-qrcode module --- chesshog-qrcode/pom.xml | 67 ++++++ .../java/org/hedgecode/chess/ChessQRCodeApp.java | 31 +++ .../org/hedgecode/chess/ChessQRCodeConstants.java | 39 ++++ .../chess/qrcode/ChessQRCodeException.java | 82 +++++++ .../hedgecode/chess/qrcode/ChessQRCodeMode.java | 49 ++++ .../hedgecode/chess/qrcode/ChessQRCodeReader.java | 196 ++++++++++++++++ .../hedgecode/chess/qrcode/ChessQRCodeWriter.java | 260 +++++++++++++++++++++ .../hedgecode/chess/qrcode/ChessQRMatrixUtils.java | 137 +++++++++++ .../org/hedgecode/chess/qrcode/ChessQRResult.java | 91 ++++++++ .../hedgecode/chess/qrcode/LocalStrings.properties | 30 +++ .../chess/qrcode/LocalStrings_ru.properties | 29 +++ 11 files changed, 1011 insertions(+) create mode 100644 chesshog-qrcode/pom.xml create mode 100644 chesshog-qrcode/src/main/java/org/hedgecode/chess/ChessQRCodeApp.java create mode 100644 chesshog-qrcode/src/main/java/org/hedgecode/chess/ChessQRCodeConstants.java create mode 100644 chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeException.java create mode 100644 chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeMode.java create mode 100644 chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeReader.java create mode 100644 chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeWriter.java create mode 100644 chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRMatrixUtils.java create mode 100644 chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRResult.java create mode 100644 chesshog-qrcode/src/main/resources/org/hedgecode/chess/qrcode/LocalStrings.properties create mode 100644 chesshog-qrcode/src/main/resources/org/hedgecode/chess/qrcode/LocalStrings_ru.properties diff --git a/chesshog-qrcode/pom.xml b/chesshog-qrcode/pom.xml new file mode 100644 index 0000000..3892e5e --- /dev/null +++ b/chesshog-qrcode/pom.xml @@ -0,0 +1,67 @@ + + + + + + 4.0.0 + + + org.hedgecode.chess + chesshog + 0.1-SNAPSHOT + + + chesshog-qrcode + 0.1-SNAPSHOT + jar + + Hedgecode ChessHog QR Code + + Hedgecode ChessHog QR Code Module. + + + + 0.1-SNAPSHOT + 3.3.3 + + + + + org.hedgecode.chess + chesshog + ${chessHogVersion} + + + org.hedgecode.chess + chesshog-core + ${chessHogVersion} + + + com.google.zxing + core + ${zxingVersion} + + + com.google.zxing + javase + ${zxingVersion} + + + + \ No newline at end of file diff --git a/chesshog-qrcode/src/main/java/org/hedgecode/chess/ChessQRCodeApp.java b/chesshog-qrcode/src/main/java/org/hedgecode/chess/ChessQRCodeApp.java new file mode 100644 index 0000000..59f64e3 --- /dev/null +++ b/chesshog-qrcode/src/main/java/org/hedgecode/chess/ChessQRCodeApp.java @@ -0,0 +1,31 @@ +/* + * 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 diff --git a/chesshog-qrcode/src/main/java/org/hedgecode/chess/ChessQRCodeConstants.java b/chesshog-qrcode/src/main/java/org/hedgecode/chess/ChessQRCodeConstants.java new file mode 100644 index 0000000..396f0a5 --- /dev/null +++ b/chesshog-qrcode/src/main/java/org/hedgecode/chess/ChessQRCodeConstants.java @@ -0,0 +1,39 @@ +/* + * 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!" + ); + } + +} diff --git a/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeException.java b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeException.java new file mode 100644 index 0000000..5c490de --- /dev/null +++ b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeException.java @@ -0,0 +1,82 @@ +/* + * 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(); + } + +} diff --git a/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeMode.java b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeMode.java new file mode 100644 index 0000000..1c8f373 --- /dev/null +++ b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeMode.java @@ -0,0 +1,49 @@ +/* + * 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; + } + +} diff --git a/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeReader.java b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeReader.java new file mode 100644 index 0000000..a0bbbdf --- /dev/null +++ b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeReader.java @@ -0,0 +1,196 @@ +/* + * 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 hints) + throws ChessQRCodeException + { + Map 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 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()); + } + } + +} 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 index 0000000..bc11750 --- /dev/null +++ b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeWriter.java @@ -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 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 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()); + } + } + +} diff --git a/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRMatrixUtils.java b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRMatrixUtils.java new file mode 100644 index 0000000..75df9f9 --- /dev/null +++ b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRMatrixUtils.java @@ -0,0 +1,137 @@ +/* + * 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; + } + +} diff --git a/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRResult.java b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRResult.java new file mode 100644 index 0000000..1b8bbf7 --- /dev/null +++ b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRResult.java @@ -0,0 +1,91 @@ +/* + * 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" + ); + } + } + +} diff --git a/chesshog-qrcode/src/main/resources/org/hedgecode/chess/qrcode/LocalStrings.properties b/chesshog-qrcode/src/main/resources/org/hedgecode/chess/qrcode/LocalStrings.properties new file mode 100644 index 0000000..5c7f3a1 --- /dev/null +++ b/chesshog-qrcode/src/main/resources/org/hedgecode/chess/qrcode/LocalStrings.properties @@ -0,0 +1,30 @@ +# 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 diff --git a/chesshog-qrcode/src/main/resources/org/hedgecode/chess/qrcode/LocalStrings_ru.properties b/chesshog-qrcode/src/main/resources/org/hedgecode/chess/qrcode/LocalStrings_ru.properties new file mode 100644 index 0000000..b396326 --- /dev/null +++ b/chesshog-qrcode/src/main/resources/org/hedgecode/chess/qrcode/LocalStrings_ru.properties @@ -0,0 +1,29 @@ +# 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 -- 2.10.0