[LIB-9] Add ability to archive qrcode content
[chesshog.git] / chesshog-qrcode / src / main / java / org / hedgecode / chess / qrcode / ChessQRCodeReader.java
1 /*
2  * Copyright (c) 2018-2019. Developed by Hedgecode.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package org.hedgecode.chess.qrcode;
18
19 import java.awt.image.BufferedImage;
20 import java.io.File;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.net.URI;
24 import java.util.EnumMap;
25 import java.util.Map;
26
27 import javax.imageio.ImageIO;
28
29 import com.google.zxing.BinaryBitmap;
30 import com.google.zxing.DecodeHintType;
31 import com.google.zxing.ReaderException;
32 import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
33 import com.google.zxing.common.DecoderResult;
34 import com.google.zxing.common.DetectorResult;
35 import com.google.zxing.common.HybridBinarizer;
36 import com.google.zxing.qrcode.decoder.Decoder;
37 import com.google.zxing.qrcode.detector.Detector;
38
39 /**
40  * Chess-specific QR Codes reader (image decoder).
41  *
42  * @author Dmitry Samoshin aka gotty
43  */
44 public class ChessQRCodeReader {
45
46     private final Decoder decoder;
47
48     private static ChessQRCodeReader _instance = new ChessQRCodeReader();
49
50     protected ChessQRCodeReader() {
51         decoder = new Decoder();
52     }
53
54     public ChessQRResult read(File qrCodeImageFile) throws ChessQRCodeException {
55         BufferedImage qrCodeImage;
56         try {
57             qrCodeImage = ImageIO.read(qrCodeImageFile);
58         } catch (IOException e) {
59             throw new ChessQRCodeException(
60                     ChessQRCodeException.Type.READ, "read.input.qrcode.file", qrCodeImageFile.getName()
61             );
62         }
63         return read(qrCodeImage);
64     }
65
66     public ChessQRResult read(InputStream qrCodeInputStream) throws ChessQRCodeException {
67         BufferedImage qrCodeImage;
68         try {
69             qrCodeImage = ImageIO.read(qrCodeInputStream);
70         } catch (IOException e) {
71             throw new ChessQRCodeException(
72                     ChessQRCodeException.Type.READ, "read.input.qrcode.stream", e.getMessage()
73             );
74         }
75         return read(qrCodeImage);
76     }
77
78     public ChessQRResult read(URI qrCodeUri) throws ChessQRCodeException {
79         BufferedImage qrCodeImage;
80         try {
81             qrCodeImage = ImageIO.read(qrCodeUri.toURL());
82         } catch (IOException e) {
83             throw new ChessQRCodeException(
84                     ChessQRCodeException.Type.READ, "read.input.qrcode.url", qrCodeUri.toASCIIString()
85             );
86         }
87         return read(qrCodeImage);
88     }
89
90     public ChessQRResult read(BufferedImage qrCodeImage) throws ChessQRCodeException {
91         BinaryBitmap bitmap = new BinaryBitmap(
92                 new HybridBinarizer(
93                         new BufferedImageLuminanceSource(qrCodeImage)
94                 )
95         );
96         return decode(bitmap, null);
97     }
98
99     private ChessQRResult decode(BinaryBitmap image, Map<DecodeHintType,?> hints)
100             throws ChessQRCodeException
101     {
102         Map<DecodeHintType,Object> newHints = new EnumMap<>(DecodeHintType.class);
103         if (hints != null) {
104             newHints.putAll(hints);
105             if (!hints.containsKey(DecodeHintType.CHARACTER_SET))
106                 newHints.put(DecodeHintType.CHARACTER_SET, ChessQRCodeConstants.CHARSET.name());
107         } else {
108             newHints.put(DecodeHintType.CHARACTER_SET, ChessQRCodeConstants.CHARSET.name());
109         }
110         hints = newHints;
111
112         DecoderResult decoderResult;
113         try {
114             DetectorResult detectorResult = new Detector(
115                     image.getBlackMatrix()
116             ).detect(hints);
117
118             decoderResult = decoder.decode(
119                     detectorResult.getBits(), hints
120             );
121         } catch (ReaderException e) {
122             throw new ChessQRCodeException(
123                     ChessQRCodeException.Type.READ, "read.zxing.qrcode.error", e.getMessage()
124             );
125         }
126
127         return createResult(decoderResult);
128 /*
129         ResultPoint[] points = detectorResult.getPoints();
130
131         if (decoderResult.getOther() instanceof QRCodeDecoderMetaData) {
132             ((QRCodeDecoderMetaData) decoderResult.getOther()).applyMirroredCorrection(points);
133         }
134
135         Result result = new Result(
136                 decoderResult.getText(),
137                 decoderResult.getRawBytes(),
138                 points,
139                 BarcodeFormat.QR_CODE
140         );
141
142         List<byte[]> byteSegments = decoderResult.getByteSegments();
143         if (byteSegments != null) {
144             result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, byteSegments);
145         }
146         String ecLevel = decoderResult.getECLevel();
147         if (ecLevel != null) {
148             result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel);
149         }
150         if (decoderResult.hasStructuredAppend()) {
151             result.putMetadata(
152                     ResultMetadataType.STRUCTURED_APPEND_SEQUENCE,
153                     decoderResult.getStructuredAppendSequenceNumber()
154             );
155             result.putMetadata(
156                     ResultMetadataType.STRUCTURED_APPEND_PARITY,
157                     decoderResult.getStructuredAppendParity()
158             );
159         }
160         return result;
161 */
162     }
163
164     protected ChessQRResult createResult(DecoderResult decoderResult) throws ChessQRCodeException {
165         String result = decoderResult.getText();
166
167         ChessQRCodeMode mode = ChessQRResult.getMode(result);
168         if (mode == null) {
169             throw new ChessQRCodeException(
170                     ChessQRCodeException.Type.READ, "read.unknown.chess.mode"
171             );
172         }
173
174         return new ChessQRResult(
175                 mode,
176                 ChessQRResult.getContents(result)
177         );
178     }
179
180     public static ChessQRCodeReader getInstance() {
181         return _instance;
182     }
183
184
185     public static void main(String[] args) {
186         try {
187             ChessQRResult qrCodeResult = ChessQRCodeReader.getInstance().read(
188                     new File("qrcode.png")
189             );
190             System.out.println("Decoded format = " + qrCodeResult.getMode());
191             System.out.println("Decoded text = " + qrCodeResult.getContents());
192         } catch (ChessQRCodeException e) {
193             System.out.println("Could not decode QR Code. Exception: " + e.getMessage());
194         }
195     }
196
197 }