File size: 5,356 Bytes
b43c3a8 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.LuminanceSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.ReaderException;
import com.google.zxing.Result;
import com.google.zxing.ResultMetadataType;
import com.google.zxing.common.HybridBinarizer;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.EnumMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* ZXing core 3.5.2 as Android's Settings app calls it (SettingsLib QrCamera): QR only, HybridBinarizer,
* decodeWithState on the frame, then on the inverted frame; no TRY_HARDER.
* stdin: "id<TAB>path" lines, each an 8-bit gray PNG. stdout: one JSON line per input, flushed, with every byte
* form of the content ZXing returns (its text as UTF-8 and, where it fits, ISO-8859-1, and its raw byte
* segments), base64-encoded: a read counts when one of them is the picture's content.
*/
public class ZxingServer {
/** A plain gray plane, as SettingsLib's QrYuvLuminanceSource hands ZXing the camera's Y plane. */
static final class GraySource extends LuminanceSource {
private final byte[] data;
GraySource(byte[] data, int width, int height) {
super(width, height);
this.data = data;
}
@Override
public byte[] getRow(int y, byte[] row) {
int w = getWidth();
if (row == null || row.length < w) row = new byte[w];
System.arraycopy(data, y * w, row, 0, w);
return row;
}
@Override
public byte[] getMatrix() {
return data;
}
}
static Result decode(MultiFormatReader reader, LuminanceSource source) {
try {
return reader.decodeWithState(new BinaryBitmap(new HybridBinarizer(source)));
} catch (ReaderException e) {
return null;
} finally {
reader.reset();
}
}
@SuppressWarnings("unchecked")
static Set<String> contents(Result r) throws Exception {
Base64.Encoder b64 = Base64.getEncoder();
Set<String> out = new LinkedHashSet<>();
String text = r.getText() == null ? "" : r.getText();
out.add(b64.encodeToString(text.getBytes(StandardCharsets.UTF_8)));
if (StandardCharsets.ISO_8859_1.newEncoder().canEncode(text)) {
out.add(b64.encodeToString(text.getBytes(StandardCharsets.ISO_8859_1)));
}
Map<ResultMetadataType, Object> md = r.getResultMetadata();
if (md != null && md.get(ResultMetadataType.BYTE_SEGMENTS) instanceof List) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
for (byte[] seg : (List<byte[]>) md.get(ResultMetadataType.BYTE_SEGMENTS)) bos.write(seg);
if (bos.size() > 0) out.add(b64.encodeToString(bos.toByteArray()));
}
return out;
}
public static void main(String[] args) throws Exception {
Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class);
hints.put(DecodeHintType.POSSIBLE_FORMATS, List.of(BarcodeFormat.QR_CODE));
MultiFormatReader reader = new MultiFormatReader();
reader.setHints(hints);
PrintStream out = new PrintStream(new FileOutputStream(FileDescriptor.out), true, StandardCharsets.UTF_8);
BufferedReader in = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8));
String line;
while ((line = in.readLine()) != null) {
String[] parts = line.split("\t", 2);
if (parts.length < 2 || !parts[0].matches("[0-9]+")) continue;
String id = parts[0];
BufferedImage img;
try {
img = ImageIO.read(new File(parts[1]));
} catch (Exception e) {
img = null;
}
if (img == null || img.getType() != BufferedImage.TYPE_BYTE_GRAY) {
out.println("{\"id\":" + id + ",\"error\":\"not an 8-bit gray PNG\"}");
continue;
}
int w = img.getWidth();
int h = img.getHeight();
byte[] y = (byte[]) img.getRaster().getDataElements(0, 0, w, h, null);
LuminanceSource src = new GraySource(y, w, h);
Result r = decode(reader, src);
boolean inverted = false;
if (r == null) { // QrCamera: "Check color inversion QR code"
r = decode(reader, src.invert());
inverted = r != null;
}
StringBuilder sb = new StringBuilder();
sb.append("{\"id\":").append(id).append(",\"inverted\":").append(inverted).append(",\"contents\":[");
if (r != null) {
int i = 0;
for (String c : contents(r)) sb.append(i++ > 0 ? "," : "").append('"').append(c).append('"');
}
sb.append("]}");
out.println(sb);
}
}
}
|