From 6f233e90e111f983e0ef401697859bb677bc0929 Mon Sep 17 00:00:00 2001 From: gotty Date: Mon, 29 Apr 2019 17:40:20 +0300 Subject: [PATCH] [LIB-9] Add ability to archive qrcode content --- .../java/org/hedgecode/chess/ChessQRCodeApp.java | 31 ------- .../org/hedgecode/chess/qrcode/ChessQRCodeApp.java | 50 ++++++++++ .../chess/{ => qrcode}/ChessQRCodeConstants.java | 4 +- .../chess/qrcode/ChessQRCodeException.java | 2 - .../hedgecode/chess/qrcode/ChessQRCodeMode.java | 15 ++- .../hedgecode/chess/qrcode/ChessQRCodeReader.java | 37 ++++---- .../hedgecode/chess/qrcode/ChessQRCodeWriter.java | 18 ++-- .../hedgecode/chess/qrcode/ChessQRMatrixUtils.java | 4 +- .../org/hedgecode/chess/qrcode/ChessQRResult.java | 16 +++- .../chess/qrcode/zip/ZipChessQRCodeReader.java | 84 +++++++++++++++++ .../chess/qrcode/zip/ZipChessQRCodeWriter.java | 91 ++++++++++++++++++ .../chess/qrcode/zip/ZipContentDeflater.java | 82 +++++++++++++++++ .../chess/qrcode/zip/ZipContentInflater.java | 63 +++++++++++++ .../chess/qrcode/zip/ZipContentUtils.java | 102 +++++++++++++++++++++ .../hedgecode/chess/qrcode/LocalStrings.properties | 3 + .../chess/qrcode/LocalStrings_ru.properties | 3 + 16 files changed, 534 insertions(+), 71 deletions(-) delete mode 100644 chesshog-qrcode/src/main/java/org/hedgecode/chess/ChessQRCodeApp.java create mode 100644 chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeApp.java rename chesshog-qrcode/src/main/java/org/hedgecode/chess/{ => qrcode}/ChessQRCodeConstants.java (90%) create mode 100644 chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipChessQRCodeReader.java create mode 100644 chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipChessQRCodeWriter.java create mode 100644 chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipContentDeflater.java create mode 100644 chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipContentInflater.java create mode 100644 chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipContentUtils.java diff --git a/chesshog-qrcode/src/main/java/org/hedgecode/chess/ChessQRCodeApp.java b/chesshog-qrcode/src/main/java/org/hedgecode/chess/ChessQRCodeApp.java deleted file mode 100644 index 59f64e3..0000000 --- a/chesshog-qrcode/src/main/java/org/hedgecode/chess/ChessQRCodeApp.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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/qrcode/ChessQRCodeApp.java b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeApp.java new file mode 100644 index 0000000..fe5d50e --- /dev/null +++ b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeApp.java @@ -0,0 +1,50 @@ +/* + * 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.ChessHogConsole; + +/** + * Chess QR Code Main Application. + * + * @author Dmitry Samoshin aka gotty + */ +public final class ChessQRCodeApp { + + private static final String[] QRCODE_MODULE_LOGO = { + " _ ", + " ___ ___ ___ ___ _| |___ ", + " | . | _| _| . | . | -_| ", + " |_ |_| |___|___|___|___| ", + " |_| _ _ ", + " _____ ___ _| |_ _| |___ ", + " | | . | . | | | | -_| ", + " |_|_|_|___|___|___|_|___| ", + " " + }; + + public static void main(String[] args) { + + ChessHogConsole.init(); + + for (String line : QRCODE_MODULE_LOGO) { + ChessHogConsole.console(line); + } + + } + +} \ 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/qrcode/ChessQRCodeConstants.java similarity index 90% rename from chesshog-qrcode/src/main/java/org/hedgecode/chess/ChessQRCodeConstants.java rename to chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeConstants.java index 396f0a5..b208100 100644 --- a/chesshog-qrcode/src/main/java/org/hedgecode/chess/ChessQRCodeConstants.java +++ b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeConstants.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.hedgecode.chess; +package org.hedgecode.chess.qrcode; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; @@ -32,7 +32,7 @@ public final class ChessQRCodeConstants { private ChessQRCodeConstants() { throw new AssertionError( - "No org.hedgecode.chess.ChessQRCodeConstants instances!" + "No org.hedgecode.chess.qrcode.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 index 5c490de..9844bc2 100644 --- a/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeException.java +++ b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeException.java @@ -18,8 +18,6 @@ package org.hedgecode.chess.qrcode; import java.util.ResourceBundle; -import org.hedgecode.chess.ChessQRCodeConstants; - /** * Chess-specific QR Codes read/write/parse Exception. * 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 index 1c8f373..e0c1f98 100644 --- a/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeMode.java +++ b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeMode.java @@ -17,7 +17,7 @@ package org.hedgecode.chess.qrcode; /** - * Mode of data type stored in Chess-specific QR Codes. + * Mode of data type stored in chess-specific QR Codes. * * @author Dmitry Samoshin aka gotty */ @@ -26,18 +26,23 @@ public enum ChessQRCodeMode { FEN, TCD, PGN, - TCG; + TCG, + ZIP; - static final int CODE_LENGTH = 3; + public static final int CODE_LENGTH = 3; - boolean isPosition() { + public boolean isPosition() { return FEN.equals(this) || TCD.equals(this); } - boolean isGame() { + public boolean isGame() { return PGN.equals(this) || TCG.equals(this); } + public boolean isArchive() { + return ZIP.equals(this); + } + public static ChessQRCodeMode byCode(String code) { for (ChessQRCodeMode mode : ChessQRCodeMode.values()) { if (mode.name().equals(code)) 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 index a0bbbdf..b10caa4 100644 --- a/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeReader.java +++ b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeReader.java @@ -36,8 +36,6 @@ 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). * @@ -49,7 +47,7 @@ public class ChessQRCodeReader { private static ChessQRCodeReader _instance = new ChessQRCodeReader(); - private ChessQRCodeReader() { + protected ChessQRCodeReader() { decoder = new Decoder(); } @@ -126,20 +124,7 @@ public class ChessQRCodeReader { ); } - 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) - ); + return createResult(decoderResult); /* ResultPoint[] points = detectorResult.getPoints(); @@ -176,6 +161,22 @@ public class ChessQRCodeReader { */ } + protected ChessQRResult createResult(DecoderResult decoderResult) throws ChessQRCodeException { + String result = decoderResult.getText(); + + ChessQRCodeMode mode = ChessQRResult.getMode(result); + if (mode == null) { + throw new ChessQRCodeException( + ChessQRCodeException.Type.READ, "read.unknown.chess.mode" + ); + } + + return new ChessQRResult( + mode, + ChessQRResult.getContents(result) + ); + } + public static ChessQRCodeReader getInstance() { return _instance; } @@ -184,7 +185,7 @@ public class ChessQRCodeReader { public static void main(String[] args) { try { ChessQRResult qrCodeResult = ChessQRCodeReader.getInstance().read( - new File("MyQRCode.png") + new File("qrcode.png") ); System.out.println("Decoded format = " + qrCodeResult.getMode()); System.out.println("Decoded text = " + qrCodeResult.getContents()); 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 index bc11750..cb2d532 100644 --- a/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeWriter.java +++ b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeWriter.java @@ -35,7 +35,6 @@ 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; /** @@ -47,12 +46,13 @@ 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(); - private ChessQRCodeWriter() { + protected ChessQRCodeWriter() { } public void write(ChessQRCodeMode mode, String contents, ImageFormat format, String qrCodeFilePath) @@ -139,7 +139,7 @@ public class ChessQRCodeWriter { ); } - private BitMatrix encode( + protected BitMatrix encode( ChessQRCodeMode mode, String contents, int width, int height, @@ -158,12 +158,14 @@ public class ChessQRCodeWriter { ); } - if (width < 0 || height < 0) { // todo + 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; @@ -188,12 +190,10 @@ public class ChessQRCodeWriter { } hints = newHints; - String qrCodeText = mode.name().concat(contents); - QRCode qrCode; try { qrCode = Encoder.encode( - qrCodeText, errorCorrectionLevel, hints + qrCodeContents, errorCorrectionLevel, hints ); } catch (WriterException e) { throw new ChessQRCodeException( @@ -239,7 +239,7 @@ public class ChessQRCodeWriter { ChessQRCodeMode.FEN, "rnbqkbnr/ppp1pppp/8/3p4/4P3/8/PPPP1PPP/RNBQKBNR w KQkq d6 0 2", ImageFormat.PNG, - "./MyQRCode.png" + "./qrcode.png" ); /* ChessQRCodeWriter.getInstance().write( @@ -249,7 +249,7 @@ public class ChessQRCodeWriter { "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" + "./qrcode.png" ); */ } catch (ChessQRCodeException e) { 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 index 75df9f9..a0e80c5 100644 --- a/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRMatrixUtils.java +++ b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRMatrixUtils.java @@ -81,8 +81,8 @@ public final class ChessQRMatrixUtils { static ByteMatrix embedChessLogoPattern(ByteMatrix byteMatrix) { - if (byteMatrix == null || byteMatrix.getWidth() < CHESS_LOGO_PATTERN_LENGTH + 14) // todo: 14 - return null; + if (byteMatrix == null || byteMatrix.getWidth() < CHESS_LOGO_PATTERN_LENGTH + 14) + return null; // todo: check for Version int xStart = (byteMatrix.getWidth() - CHESS_LOGO_PATTERN_LENGTH) / 2; int yStart = (byteMatrix.getHeight() - CHESS_LOGO_PATTERN_LENGTH) / 2; 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 index 1b8bbf7..0e2f585 100644 --- a/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRResult.java +++ b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRResult.java @@ -23,7 +23,7 @@ import org.hedgecode.chess.position.Parser; import org.hedgecode.chess.position.Position; /** - * Result storage for Chess-specific QR Codes data. + * Result storage for chess-specific QR Codes data. * * @author Dmitry Samoshin aka gotty */ @@ -34,11 +34,12 @@ public class ChessQRResult { private Position position; private Game game; - ChessQRResult(ChessQRCodeMode mode, String contents) { + public ChessQRResult(ChessQRCodeMode mode, String contents) { this.mode = mode; this.contents = contents; } +/* ChessQRResult(Position position) { this.position = position; } @@ -46,6 +47,7 @@ public class ChessQRResult { ChessQRResult(Game game) { this.game = game; } +*/ public ChessQRCodeMode getMode() { return mode; @@ -88,4 +90,14 @@ public class ChessQRResult { } } + public static ChessQRCodeMode getMode(String contents) { + return ChessQRCodeMode.byCode( + contents.substring(0, ChessQRCodeMode.CODE_LENGTH) + ); + } + + public static String getContents(String contents) { + return contents.substring(ChessQRCodeMode.CODE_LENGTH); + } + } diff --git a/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipChessQRCodeReader.java b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipChessQRCodeReader.java new file mode 100644 index 0000000..8578a8b --- /dev/null +++ b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipChessQRCodeReader.java @@ -0,0 +1,84 @@ +/* + * 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.zip; + +import java.io.File; + +import com.google.zxing.common.DecoderResult; + +import org.hedgecode.chess.qrcode.ChessQRCodeException; +import org.hedgecode.chess.qrcode.ChessQRCodeMode; +import org.hedgecode.chess.qrcode.ChessQRCodeReader; +import org.hedgecode.chess.qrcode.ChessQRResult; + +/** + * + * + * @author Dmitry Samoshin aka gotty + */ +public class ZipChessQRCodeReader extends ChessQRCodeReader { + + private static ZipChessQRCodeReader _instance = new ZipChessQRCodeReader(); + + protected ZipChessQRCodeReader() { + super(); + } + + @Override + protected ChessQRResult createResult(DecoderResult decoderResult) throws ChessQRCodeException { + String result = decoderResult.getText(); + ChessQRCodeMode mode = ChessQRResult.getMode(result); + + if (ChessQRCodeMode.ZIP.equals(mode)) { + String contents = ZipContentInflater.inflate( + ChessQRResult.getContents(result) + ); + + mode = ChessQRResult.getMode(contents); + if (mode == null) { + throw new ChessQRCodeException( + ChessQRCodeException.Type.READ, "read.unknown.chess.mode" + ); + } + + return new ChessQRResult( + mode, + ChessQRResult.getContents(contents) + ); + } else { + return super.createResult(decoderResult); + } + } + + public static ZipChessQRCodeReader getInstance() { + return _instance; + } + + + public static void main(String[] args) { + try { + ChessQRResult qrCodeResult = ZipChessQRCodeReader.getInstance().read( + new File("qrcode.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/zip/ZipChessQRCodeWriter.java b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipChessQRCodeWriter.java new file mode 100644 index 0000000..6b92e76 --- /dev/null +++ b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipChessQRCodeWriter.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.zip; + +import java.util.Map; + +import com.google.zxing.EncodeHintType; +import com.google.zxing.common.BitMatrix; +import org.hedgecode.chess.img.ImageFormat; +import org.hedgecode.chess.qrcode.ChessQRCodeException; +import org.hedgecode.chess.qrcode.ChessQRCodeMode; +import org.hedgecode.chess.qrcode.ChessQRCodeWriter; + +/** + * + * + * @author Dmitry Samoshin aka gotty + */ +public class ZipChessQRCodeWriter extends ChessQRCodeWriter { + + private static ChessQRCodeWriter _instance = new ZipChessQRCodeWriter(); + + protected ZipChessQRCodeWriter() { + super(); + } + + @Override + protected BitMatrix encode( + ChessQRCodeMode mode, + String contents, + int width, int height, + Map hints) + throws ChessQRCodeException + { + if (mode == null || mode.isArchive()) { + 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" + ); + } + + String zipContents = ZipContentDeflater.deflate( + mode.name().concat(contents) + ); + + return super.encode( + ChessQRCodeMode.ZIP, zipContents, width, height, hints + ); + } + + public static ChessQRCodeWriter getInstance() { + return _instance; + } + + + public static void main(String[] args) { + try { + ZipChessQRCodeWriter.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()); + } + } + +} diff --git a/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipContentDeflater.java b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipContentDeflater.java new file mode 100644 index 0000000..5a2c4fe --- /dev/null +++ b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipContentDeflater.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.zip; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; + +import org.hedgecode.chess.qrcode.ChessQRCodeConstants; +import org.hedgecode.chess.qrcode.ChessQRCodeException; + +/** + * + * + * @author Dmitry Samoshin aka gotty + */ +public class ZipContentDeflater { + + public static String deflate(String contents) throws ChessQRCodeException { + byte[] bytes = contents.getBytes( + ChessQRCodeConstants.CHARSET + ); + if (bytes.length > ZipContentUtils.getMaxContentLength()) { + throw new ChessQRCodeException( + ChessQRCodeException.Type.PARSE, "zip.max.length.error" + ); + } + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + DeflaterOutputStream zout = new DeflaterOutputStream( + bout, new Deflater(Deflater.BEST_COMPRESSION) + ); + try { + try { + zout.write(bytes); + } finally { + zout.close(); + bout.close(); + } + } catch (IOException e) { + throw new ChessQRCodeException( + ChessQRCodeException.Type.PARSE, "zip.deflate.write.error", e.getMessage() + ); + } + return ZipContentUtils.base64Encode( + ZipContentUtils.concat( + bytes.length, + bout.toByteArray() + ) + ); + } + + + public static void main(String[] args) throws ChessQRCodeException { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 10; ++i) { + sb.append(i).append("bla-bla-bla").append(i); + } + String zipContents = ZipContentDeflater.deflate(sb.toString()); + System.out.println("Zip Contents: " + zipContents); + System.out.println("Zip length: " + zipContents.length()); + String contents = ZipContentInflater.inflate(zipContents); + System.out.println("Unzip Contents: " + contents); + System.out.println("Unziip length: " + contents.length()); + System.out.println("Equals: " + sb.toString().equals(contents)); + } + +} diff --git a/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipContentInflater.java b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipContentInflater.java new file mode 100644 index 0000000..0e44138 --- /dev/null +++ b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipContentInflater.java @@ -0,0 +1,63 @@ +/* + * 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.zip; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.zip.InflaterInputStream; + +import org.hedgecode.chess.qrcode.ChessQRCodeConstants; +import org.hedgecode.chess.qrcode.ChessQRCodeException; + +/** + * + * + * @author Dmitry Samoshin aka gotty + */ +public class ZipContentInflater { + + public static String inflate(String zipContents) throws ChessQRCodeException { + byte[] zipBytes = ZipContentUtils.base64Decode(zipContents); + int length = ZipContentUtils.getZipLength(zipBytes); + if (length > ZipContentUtils.getMaxContentLength()) { + throw new ChessQRCodeException( + ChessQRCodeException.Type.PARSE, "zip.max.length.error" + ); + } + byte[] resultBytes = new byte[length]; + InflaterInputStream zin = new InflaterInputStream( + new ByteArrayInputStream( + ZipContentUtils.getZipBytes(zipBytes) + ) + ); + try { + try { + length = zin.read(resultBytes, 0, length); // todo : check length + } finally { + zin.close(); + } + } catch (IOException e) { + throw new ChessQRCodeException( + ChessQRCodeException.Type.PARSE, "zip.inflate.read.error", e.getMessage() + ); + } + return new String( + resultBytes, 0, length, ChessQRCodeConstants.CHARSET + ); + } + +} diff --git a/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipContentUtils.java b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipContentUtils.java new file mode 100644 index 0000000..dfd7901 --- /dev/null +++ b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/zip/ZipContentUtils.java @@ -0,0 +1,102 @@ +/* + * 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.zip; + +import java.util.Arrays; +import java.util.Base64; + +/** + * + * + * @author Dmitry Samoshin aka gotty + */ +public class ZipContentUtils { + + private static final int LENGTH_BYTE_COUNT = 4; + + private static final int MAX_CONTENT_LENGTH = 65536; + + public static int getMaxContentLength() { + return MAX_CONTENT_LENGTH; + } + + public static int getZipLength(byte[] zipBytes) { + return fromByteArray( + Arrays.copyOf(zipBytes, LENGTH_BYTE_COUNT) + ); + } + + public static byte[] getZipBytes(byte[] zipBytes) { + return Arrays.copyOfRange( + zipBytes, LENGTH_BYTE_COUNT, zipBytes.length + ); + } + + public static byte[] concat(int length, byte[] bytes) { + return concat( + toByteArray(length), + bytes + ); + } + + public static String base64Encode(byte[] bytes) { + return Base64.getEncoder().encodeToString(bytes); + } + + public static byte[] base64Decode(String value) { + return Base64.getDecoder().decode(value); + } + + private static byte[] toByteArray(int value) { + return new byte[] { + (byte) (value >> 24), + (byte) (value >> 16), + (byte) (value >> 8), + (byte) (value) + }; + } + + private static int fromByteArray(byte[] bytes) { + return bytes[0] << 24 + | (bytes[1] & 0xFF) << 16 + | (bytes[2] & 0xFF) << 8 + | (bytes[3] & 0xFF); + } + + private static byte[] concat(byte[]... byteArrays) { + int length = 0; + for (byte[] bytes : byteArrays) { + length += bytes.length; + } + byte[] concatBytes = new byte[length]; + length = 0; + for (byte[] bytes : byteArrays) { + if (bytes.length == 0) + continue; + System.arraycopy(bytes, 0, concatBytes, length, bytes.length); + length += bytes.length; + } + return concatBytes; + } + + private ZipContentUtils() { + throw new AssertionError( + "No org.hedgecode.chess.qrcode.zip.ZipContentUtils instances!" + ); + } + +} 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 index 5c7f3a1..39911c2 100644 --- a/chesshog-qrcode/src/main/resources/org/hedgecode/chess/qrcode/LocalStrings.properties +++ b/chesshog-qrcode/src/main/resources/org/hedgecode/chess/qrcode/LocalStrings.properties @@ -28,3 +28,6 @@ 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 +zip.max.length.error=Content length exceeds maximum size +zip.deflate.write.error=Error deflating of content +zip.inflate.read.error=Error inflating of content 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 index b396326..5036e83 100644 --- 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 @@ -27,3 +27,6 @@ write.qrcode.contents.empty=\u0412\u0445\u043E\u0434\u043D\u044B\u0435 \u0434\u0 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 +zip.max.length.error=\u0414\u043B\u0438\u043D\u0430 \u0441\u043E\u0434\u0435\u0440\u0436\u0438\u043C\u043E\u0433\u043E \u043F\u0440\u0435\u0432\u044B\u0448\u0430\u0435\u0442 \u043C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u043E \u0434\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u044B\u0439 \u0440\u0430\u0437\u043C\u0435\u0440 +zip.deflate.write.error=\u041E\u0448\u0438\u0431\u043A\u0430 \u0430\u0440\u0445\u0438\u0432\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u0441\u043E\u0434\u0435\u0440\u0436\u0438\u043C\u043E\u0433\u043E +zip.inflate.read.error=\u041E\u0448\u0438\u0431\u043A\u0430 \u0440\u0430\u0437\u0430\u0440\u0445\u0438\u0432\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u0441\u043E\u0434\u0435\u0440\u0436\u0438\u043C\u043E\u0433\u043E -- 2.10.0