diff --git a/src/common/decoder_rxing_result.rs b/src/common/decoder_rxing_result.rs
index a8e2a4e..e688a76 100644
--- a/src/common/decoder_rxing_result.rs
+++ b/src/common/decoder_rxing_result.rs
@@ -18,7 +18,7 @@
// import java.util.List;
-use std::any::Any;
+use std::{any::Any, rc::Rc};
/**
*
Encapsulates the result of decoding a matrix of bits. This typically
@@ -35,7 +35,7 @@ pub struct DecoderRXingResult {
ecLevel: String,
errorsCorrected: u64,
erasures: u64,
- other: Box,
+ other: Rc,
structuredAppendParity: i32,
structuredAppendSequenceNumber: i32,
symbologyModifier: u32,
@@ -106,7 +106,7 @@ impl DecoderRXingResult {
ecLevel,
errorsCorrected: 0,
erasures: 0,
- other: Box::new(false),
+ other: Rc::new(false),
structuredAppendParity: saParity,
structuredAppendSequenceNumber: saSequence,
symbologyModifier,
@@ -182,11 +182,11 @@ impl DecoderRXingResult {
/**
* @return arbitrary additional metadata
*/
- pub fn getOther(&self) -> &Box {
- &self.other
+ pub fn getOther(&self) -> Rc {
+ self.other.clone()
}
- pub fn setOther(&mut self, other: Box) {
+ pub fn setOther(&mut self, other: Rc) {
self.other = other
}
diff --git a/src/multi/mod.rs b/src/multi/mod.rs
index b0d645a..a55c715 100644
--- a/src/multi/mod.rs
+++ b/src/multi/mod.rs
@@ -1 +1,3 @@
+mod multiple_barcode_reader;
pub mod qrcode;
+pub use multiple_barcode_reader::*;
diff --git a/src/multi/MultipleBarcodeReader.java b/src/multi/multiple_barcode_reader.rs
similarity index 61%
rename from src/multi/MultipleBarcodeReader.java
rename to src/multi/multiple_barcode_reader.rs
index 65c89a8..441e930 100644
--- a/src/multi/MultipleBarcodeReader.java
+++ b/src/multi/multiple_barcode_reader.rs
@@ -14,14 +14,7 @@
* limitations under the License.
*/
-package com.google.zxing.multi;
-
-import com.google.zxing.BinaryBitmap;
-import com.google.zxing.DecodeHintType;
-import com.google.zxing.NotFoundException;
-import com.google.zxing.RXingResult;
-
-import java.util.Map;
+use crate::{BinaryBitmap, DecodingHintDictionary, Exceptions, RXingResult};
/**
* Implementation of this interface attempt to read several barcodes from one image.
@@ -29,11 +22,12 @@ import java.util.Map;
* @see com.google.zxing.Reader
* @author Sean Owen
*/
-public interface MultipleBarcodeReader {
-
- RXingResult[] decodeMultiple(BinaryBitmap image) throws NotFoundException;
-
- RXingResult[] decodeMultiple(BinaryBitmap image,
- Map hints) throws NotFoundException;
+pub trait MultipleBarcodeReader {
+ fn decodeMultiple(&self, image: &BinaryBitmap) -> Result, Exceptions>;
+ fn decodeMultipleWithHints(
+ &self,
+ image: &BinaryBitmap,
+ hints: &DecodingHintDictionary,
+ ) -> Result, Exceptions>;
}
diff --git a/src/multi/qrcode/QRCodeMultiReader.java b/src/multi/qrcode/QRCodeMultiReader.java
deleted file mode 100644
index cd760fe..0000000
--- a/src/multi/qrcode/QRCodeMultiReader.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright 2009 ZXing authors
- *
- * 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 com.google.zxing.multi.qrcode;
-
-import com.google.zxing.BarcodeFormat;
-import com.google.zxing.BinaryBitmap;
-import com.google.zxing.DecodeHintType;
-import com.google.zxing.NotFoundException;
-import com.google.zxing.ReaderException;
-import com.google.zxing.RXingResult;
-import com.google.zxing.RXingResultMetadataType;
-import com.google.zxing.RXingResultPoint;
-import com.google.zxing.common.DecoderRXingResult;
-import com.google.zxing.common.DetectorRXingResult;
-import com.google.zxing.multi.MultipleBarcodeReader;
-import com.google.zxing.multi.qrcode.detector.MultiDetector;
-import com.google.zxing.qrcode.QRCodeReader;
-import com.google.zxing.qrcode.decoder.QRCodeDecoderMetaData;
-
-import java.io.ByteArrayOutputStream;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Collections;
-import java.util.Comparator;
-
-/**
- * This implementation can detect and decode multiple QR Codes in an image.
- *
- * @author Sean Owen
- * @author Hannes Erven
- */
-public final class QRCodeMultiReader extends QRCodeReader implements MultipleBarcodeReader {
-
- private static final RXingResult[] EMPTY_RESULT_ARRAY = new RXingResult[0];
- private static final RXingResultPoint[] NO_POINTS = new RXingResultPoint[0];
-
- @Override
- public RXingResult[] decodeMultiple(BinaryBitmap image) throws NotFoundException {
- return decodeMultiple(image, null);
- }
-
- @Override
- public RXingResult[] decodeMultiple(BinaryBitmap image, Map hints) throws NotFoundException {
- List results = new ArrayList<>();
- DetectorRXingResult[] detectorRXingResults = new MultiDetector(image.getBlackMatrix()).detectMulti(hints);
- for (DetectorRXingResult detectorRXingResult : detectorRXingResults) {
- try {
- DecoderRXingResult decoderRXingResult = getDecoder().decode(detectorRXingResult.getBits(), hints);
- RXingResultPoint[] points = detectorRXingResult.getPoints();
- // If the code was mirrored: swap the bottom-left and the top-right points.
- if (decoderRXingResult.getOther() instanceof QRCodeDecoderMetaData) {
- ((QRCodeDecoderMetaData) decoderRXingResult.getOther()).applyMirroredCorrection(points);
- }
- RXingResult result = new RXingResult(decoderRXingResult.getText(), decoderRXingResult.getRawBytes(), points,
- BarcodeFormat.QR_CODE);
- List byteSegments = decoderRXingResult.getByteSegments();
- if (byteSegments != null) {
- result.putMetadata(RXingResultMetadataType.BYTE_SEGMENTS, byteSegments);
- }
- String ecLevel = decoderRXingResult.getECLevel();
- if (ecLevel != null) {
- result.putMetadata(RXingResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel);
- }
- if (decoderRXingResult.hasStructuredAppend()) {
- result.putMetadata(RXingResultMetadataType.STRUCTURED_APPEND_SEQUENCE,
- decoderRXingResult.getStructuredAppendSequenceNumber());
- result.putMetadata(RXingResultMetadataType.STRUCTURED_APPEND_PARITY,
- decoderRXingResult.getStructuredAppendParity());
- }
- results.add(result);
- } catch (ReaderException re) {
- // ignore and continue
- }
- }
- if (results.isEmpty()) {
- return EMPTY_RESULT_ARRAY;
- } else {
- results = processStructuredAppend(results);
- return results.toArray(EMPTY_RESULT_ARRAY);
- }
- }
-
- static List processStructuredAppend(List results) {
- List newRXingResults = new ArrayList<>();
- List saRXingResults = new ArrayList<>();
- for (RXingResult result : results) {
- if (result.getRXingResultMetadata().containsKey(RXingResultMetadataType.STRUCTURED_APPEND_SEQUENCE)) {
- saRXingResults.add(result);
- } else {
- newRXingResults.add(result);
- }
- }
- if (saRXingResults.isEmpty()) {
- return results;
- }
-
- // sort and concatenate the SA list items
- Collections.sort(saRXingResults, new SAComparator());
- StringBuilder newText = new StringBuilder();
- ByteArrayOutputStream newRawBytes = new ByteArrayOutputStream();
- ByteArrayOutputStream newByteSegment = new ByteArrayOutputStream();
- for (RXingResult saRXingResult : saRXingResults) {
- newText.append(saRXingResult.getText());
- byte[] saBytes = saRXingResult.getRawBytes();
- newRawBytes.write(saBytes, 0, saBytes.length);
- @SuppressWarnings("unchecked")
- Iterable byteSegments =
- (Iterable) saRXingResult.getRXingResultMetadata().get(RXingResultMetadataType.BYTE_SEGMENTS);
- if (byteSegments != null) {
- for (byte[] segment : byteSegments) {
- newByteSegment.write(segment, 0, segment.length);
- }
- }
- }
-
- RXingResult newRXingResult = new RXingResult(newText.toString(), newRawBytes.toByteArray(), NO_POINTS, BarcodeFormat.QR_CODE);
- if (newByteSegment.size() > 0) {
- newRXingResult.putMetadata(RXingResultMetadataType.BYTE_SEGMENTS, Collections.singletonList(newByteSegment.toByteArray()));
- }
- newRXingResults.add(newRXingResult);
- return newRXingResults;
- }
-
- private static final class SAComparator implements Comparator, Serializable {
- @Override
- public int compare(RXingResult a, RXingResult b) {
- int aNumber = (int) a.getRXingResultMetadata().get(RXingResultMetadataType.STRUCTURED_APPEND_SEQUENCE);
- int bNumber = (int) b.getRXingResultMetadata().get(RXingResultMetadataType.STRUCTURED_APPEND_SEQUENCE);
- return Integer.compare(aNumber, bNumber);
- }
- }
-
-}
diff --git a/src/multi/qrcode/mod.rs b/src/multi/qrcode/mod.rs
index 4c98ab1..fba716a 100644
--- a/src/multi/qrcode/mod.rs
+++ b/src/multi/qrcode/mod.rs
@@ -1 +1,3 @@
pub mod detector;
+mod qr_code_multi_reader;
+pub use qr_code_multi_reader::*;
diff --git a/src/multi/qrcode/qr_code_multi_reader.rs b/src/multi/qrcode/qr_code_multi_reader.rs
new file mode 100644
index 0000000..9230baf
--- /dev/null
+++ b/src/multi/qrcode/qr_code_multi_reader.rs
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2009 ZXing authors
+ *
+ * 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.
+ */
+
+use std::{cmp::Ordering, collections::HashMap};
+
+use crate::{
+ common::DetectorRXingResult,
+ multi::MultipleBarcodeReader,
+ qrcode::{
+ decoder::{self, QRCodeDecoderMetaData},
+ QRCodeReader,
+ },
+ BarcodeFormat, Exceptions, RXingResult, RXingResultMetadataType, RXingResultMetadataValue,
+};
+
+use super::detector::MultiDetector;
+
+/**
+ * This implementation can detect and decode multiple QR Codes in an image.
+ *
+ * @author Sean Owen
+ * @author Hannes Erven
+ */
+pub struct QRCodeMultiReader(QRCodeReader);
+impl MultipleBarcodeReader for QRCodeMultiReader {
+ fn decodeMultiple(
+ &self,
+ image: &crate::BinaryBitmap,
+ ) -> Result, crate::Exceptions> {
+ self.decodeMultipleWithHints(image, &HashMap::new())
+ }
+
+ fn decodeMultipleWithHints(
+ &self,
+ image: &crate::BinaryBitmap,
+ hints: &crate::DecodingHintDictionary,
+ ) -> Result, crate::Exceptions> {
+ let mut results = Vec::new();
+ let detectorRXingResults =
+ MultiDetector::new(image.getBlackMatrix().clone()).detectMulti(hints)?;
+ for detectorRXingResult in detectorRXingResults {
+ let mut proc = || -> Result<(), Exceptions> {
+ let decoderRXingResult = decoder::decoder::decode_bitmatrix_with_hints(
+ detectorRXingResult.getBits(),
+ hints,
+ )?;
+ let mut points = detectorRXingResult.getPoints().clone();
+ // If the code was mirrored: swap the bottom-left and the top-right points.
+ let other = decoderRXingResult.getOther();
+ if other.is::() {
+ (other
+ .downcast::()
+ .expect("must downcast to QRCodeDecoderMetaData"))
+ .applyMirroredCorrection(&mut points);
+ }
+ // if (decoderRXingResult.getOther() instanceof QRCodeDecoderMetaData) {
+ // ((QRCodeDecoderMetaData) decoderRXingResult.getOther()).applyMirroredCorrection(points);
+ // }
+ let mut result = RXingResult::new(
+ decoderRXingResult.getText(),
+ decoderRXingResult.getRawBytes().clone(),
+ points,
+ BarcodeFormat::QR_CODE,
+ );
+ let byteSegments = decoderRXingResult.getByteSegments();
+ // if (byteSegments != null) {
+ result.putMetadata(
+ RXingResultMetadataType::BYTE_SEGMENTS,
+ RXingResultMetadataValue::ByteSegments(byteSegments.clone()),
+ );
+ // }
+ let ecLevel = decoderRXingResult.getECLevel();
+ // if (ecLevel != null) {
+ result.putMetadata(
+ RXingResultMetadataType::ERROR_CORRECTION_LEVEL,
+ RXingResultMetadataValue::ErrorCorrectionLevel(ecLevel.to_owned()),
+ );
+ // }
+ if decoderRXingResult.hasStructuredAppend() {
+ result.putMetadata(
+ RXingResultMetadataType::STRUCTURED_APPEND_SEQUENCE,
+ RXingResultMetadataValue::StructuredAppendSequence(
+ decoderRXingResult.getStructuredAppendSequenceNumber(),
+ ),
+ );
+ result.putMetadata(
+ RXingResultMetadataType::STRUCTURED_APPEND_PARITY,
+ RXingResultMetadataValue::StructuredAppendParity(
+ decoderRXingResult.getStructuredAppendParity(),
+ ),
+ );
+ }
+ results.push(result);
+
+ Ok(())
+ };
+ let output = proc();
+ if output.is_ok() {
+ continue;
+ } else if let Err(Exceptions::ReaderException(_)) = output {
+ // ignore and continue
+ continue;
+ } else {
+ return Err(output.err().unwrap());
+ }
+ }
+
+ results = Self::processStructuredAppend(results)?;
+
+ Ok(results)
+ }
+}
+
+impl QRCodeMultiReader {
+ fn processStructuredAppend(results: Vec) -> Result, Exceptions> {
+ let mut newRXingResults = Vec::new();
+ let mut saRXingResults = Vec::new();
+ for result in &results {
+ if result
+ .getRXingResultMetadata()
+ .contains_key(&RXingResultMetadataType::STRUCTURED_APPEND_SEQUENCE)
+ {
+ saRXingResults.push(result.clone());
+ } else {
+ newRXingResults.push(result.clone());
+ }
+ }
+ if saRXingResults.is_empty() {
+ return Ok(results);
+ }
+
+ // sort and concatenate the SA list items
+ saRXingResults.sort_by(compareRXingResult);
+ let mut newText = String::new();
+ let mut newRawBytes = Vec::new(); //new ByteArrayOutputStream();
+ let mut newByteSegment = Vec::new(); //new ByteArrayOutputStream();
+ for saRXingResult in saRXingResults {
+ newText.push_str(saRXingResult.getText());
+ let saBytes = saRXingResult.getRawBytes();
+ newRawBytes.extend_from_slice(saBytes);
+ // newRawBytes.write(saBytes, 0, saBytes.len());
+
+ if let Some(RXingResultMetadataValue::ByteSegments(byteSegments)) = saRXingResult
+ .getRXingResultMetadata()
+ .get(&RXingResultMetadataType::BYTE_SEGMENTS)
+ {
+ for segment in byteSegments {
+ // newByteSegment.write(segment, 0, segment.len());
+ newByteSegment.extend_from_slice(segment);
+ }
+ };
+ }
+
+ let mut newRXingResult =
+ RXingResult::new(&newText, newRawBytes, Vec::new(), BarcodeFormat::QR_CODE);
+ if newByteSegment.len() > 0 {
+ newRXingResult.putMetadata(
+ RXingResultMetadataType::BYTE_SEGMENTS,
+ RXingResultMetadataValue::ByteSegments(vec![newByteSegment]),
+ );
+ }
+ newRXingResults.push(newRXingResult);
+
+ Ok(newRXingResults)
+ }
+}
+
+fn compareRXingResult(a: &RXingResult, b: &RXingResult) -> Ordering {
+ let aNumber = if let Some(RXingResultMetadataValue::StructuredAppendSequence(v)) = a
+ .getRXingResultMetadata()
+ .get(&RXingResultMetadataType::STRUCTURED_APPEND_SEQUENCE)
+ {
+ v
+ } else {
+ &-1
+ };
+ let bNumber = if let Some(RXingResultMetadataValue::StructuredAppendSequence(v)) = b
+ .getRXingResultMetadata()
+ .get(&RXingResultMetadataType::STRUCTURED_APPEND_SEQUENCE)
+ {
+ v
+ } else {
+ &-1
+ };
+
+ aNumber.cmp(bNumber)
+}
diff --git a/src/qrcode/decoder/decoder.rs b/src/qrcode/decoder/decoder.rs
index 61eec89..2e4691f 100644
--- a/src/qrcode/decoder/decoder.rs
+++ b/src/qrcode/decoder/decoder.rs
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-use std::collections::HashMap;
+use std::{collections::HashMap, rc::Rc};
/**
* The main class which implements QR Code decoding -- as opposed to locating and extracting
@@ -115,7 +115,7 @@ pub fn decode_bitmatrix_with_hints(
let mut result = decode_bitmatrix_parser_with_hints(&mut parser, hints)?;
// Success! Notify the caller that the code was mirrored.
- result.setOther(Box::new(QRCodeDecoderMetaData::new(true)));
+ result.setOther(Rc::new(QRCodeDecoderMetaData::new(true)));
Ok(result)
};
diff --git a/src/rxing_result.rs b/src/rxing_result.rs
index 16e6e61..f611397 100644
--- a/src/rxing_result.rs
+++ b/src/rxing_result.rs
@@ -32,6 +32,7 @@ use crate::{BarcodeFormat, RXingResultMetadataType, RXingResultMetadataValue, RX
*
* @author Sean Owen
*/
+#[derive(Clone)]
pub struct RXingResult {
text: String,
rawBytes: Vec,
diff --git a/src/rxing_result_metadata.rs b/src/rxing_result_metadata.rs
index 6d7dc7b..5c36a3f 100644
--- a/src/rxing_result_metadata.rs
+++ b/src/rxing_result_metadata.rs
@@ -22,7 +22,7 @@
*
* @author Sean Owen
*/
-#[derive(Eq, PartialEq, Hash, Debug)]
+#[derive(Eq, PartialEq, Hash, Debug, Clone)]
pub enum RXingResultMetadataType {
/**
* Unspecified, application-specific metadata. Maps to an unspecified {@link Object}.
@@ -122,7 +122,7 @@ impl From for RXingResultMetadataType {
}
}
-#[derive(Debug, PartialEq, Eq)]
+#[derive(Debug, PartialEq, Eq, Clone)]
pub enum RXingResultMetadataValue {
/**
* Unspecified, application-specific metadata. Maps to an unspecified {@link Object}.